GRASS 8 Programmer's Manual 8.6.0dev(2026)-1d1e47ad9d
Loading...
Searching...
No Matches
break_lines.c
Go to the documentation of this file.
1/*!
2 * \file lib/vector/Vlib/break_lines.c
3 *
4 * \brief Vector library - Clean vector map (break lines)
5 *
6 * (C) 2001-2009 by the GRASS Development Team
7 *
8 * This program is free software under the
9 * GNU General Public License (>=v2).
10 * Read the file COPYING that comes with GRASS
11 * for details.
12 *
13 * \author Radim Blazek
14 */
15
16#include <stdlib.h>
17#include <grass/vector.h>
18#include <grass/glocale.h>
19
20static int break_lines(struct Map_info *, struct ilist *, struct ilist *, int,
21 struct Map_info *, int);
22
23/*!
24 \brief Break lines in vector map at each intersection.
25
26 For details see Vect_break_lines_list().
27
28 \param Map input vector map
29 \param type feature type
30 \param[out] Err vector map where points at intersections will be written or
31 NULL
32 */
33void Vect_break_lines(struct Map_info *Map, int type, struct Map_info *Err)
34{
35 break_lines(Map, NULL, NULL, type, Err, 0);
36
37 return;
38}
39
40/*!
41 \brief Break selected lines in vector map at each intersection.
42
43 Breaks selected lines specified by type in vector map. Points at
44 intersections may be optionally written to error map. Input vector map
45 must be opened on level 2 for update at least on GV_BUILD_BASE.
46
47 The function also breaks lines forming collapsed loop, for example
48 0,0;1,0;0,0 is broken at 1,0.
49
50 If reference lines are given (<i>List_ref</i>) break only lines
51 which intersect reference lines.
52
53 \param Map input vector map
54 \param List_break list of lines (NULL for all lines in vector map)
55 \param List_ref list of reference lines or NULL
56 \param type feature type
57 \param[out] Err vector map where points at intersections will be written or
58 NULL
59
60 \return number of intersections
61 */
63 struct ilist *List_ref, int type,
64 struct Map_info *Err)
65{
66 return break_lines(Map, List_break, List_ref, type, Err, 0);
67}
68
69/*!
70 \brief Check for and count intersecting lines, do not break.
71
72 For details see Vect_check_line_breaks_list().
73
74 \param Map input vector map
75 \param type feature type
76 \param[out] Err vector map where points at intersections will be written or
77 NULL
78
79 \return number for intersections
80 */
81int Vect_check_line_breaks(struct Map_info *Map, int type, struct Map_info *Err)
82{
83 return break_lines(Map, NULL, NULL, type, Err, 1);
84}
85
86/*!
87 \brief Check for and count intersecting lines, do not break.
88
89 If <i>List_break</i> is given, only lines in the list are checked for
90 intersections.
91
92 If reference lines are given (<i>List_ref</i>) break only lines
93 which intersect reference lines.
94
95 \param Map input vector map
96 \param List_break list of lines (NULL for all lines in vector map)
97 \param List_ref list of reference lines or NULL
98 \param type feature type
99 \param[out] Err vector map where points at intersections will be written or
100 NULL
101
102 \return number of intersections
103 */
105 struct ilist *List_ref, int type,
106 struct Map_info *Err)
107{
108 return break_lines(Map, List_break, List_ref, type, Err, 1);
109}
110
111static int cmp(const void *a, const void *b)
112{
113 int ai = *(int *)a;
114 int bi = *(int *)b;
115
116 /* ai - bi is ok because ai and bi are positive integers
117 * -> no integer overflow */
118 return (ai - bi);
119}
120
121static void sort_ilist(struct ilist *List)
122{
123 int i, j, is_sorted = 1;
124
125 for (i = 1; i < List->n_values; i++) {
126 if (List->value[i - 1] > List->value[i]) {
127 is_sorted = 0;
128 break;
129 }
130 }
131
132 if (!is_sorted)
133 qsort(List->value, List->n_values, sizeof(int), cmp);
134
135 if (List->n_values > 1) {
136 j = 1;
137 for (i = 1; i < List->n_values; i++) {
138 if (List->value[j - 1] != List->value[i]) {
139 List->value[j] = List->value[i];
140 j++;
141 }
142 }
143 List->n_values = j;
144 }
145}
146
147int break_lines(struct Map_info *Map, struct ilist *List_break,
148 struct ilist *List_ref, int type, struct Map_info *Err,
149 int check)
150{
151 struct line_pnts *APoints, *BPoints, *Points;
152 struct line_pnts **AXLines, **BXLines;
153 struct line_cats *ACats, *BCats, *Cats;
154 int i, j, k, l, ret, atype, btype, aline, bline, found, iline;
155 int nlines, nlines_org;
156 int naxlines, nbxlines, nx;
157 double *xx = NULL, *yx = NULL, *zx = NULL;
158 struct bound_box ABox, *BBox;
159 struct boxlist *List;
160 int nbreaks;
161 int touch1_n = 0, touch1_s = 0, touch1_e = 0,
162 touch1_w = 0; /* other vertices except node1 touching box */
163 int touch2_n = 0, touch2_s = 0, touch2_e = 0,
164 touch2_w = 0; /* other vertices except node2 touching box */
165 int is3d;
166 int node, anode1, anode2, bnode1, bnode2;
167 double nodex, nodey;
169
170 type &= GV_LINES;
171 if (!type)
172 return 0;
173
176 Points = Vect_new_line_struct();
181
183
184 if (List_ref)
185 sort_ilist(List_ref);
186 if (List_break)
187 sort_ilist(List_break);
188
189 if (List_ref) {
190 nlines = List_ref->n_values;
191 nlines_org = List_ref->value[List_ref->n_values - 1];
192 }
193 else if (List_break) {
194 nlines = List_break->n_values;
195 nlines_org = List_break->value[List_break->n_values - 1];
196 }
197 else {
198 nlines = Vect_get_num_lines(Map);
199 nlines_org = nlines;
200 }
201 G_debug(3, "nlines = %d", nlines);
202
203 /* TODO:
204 * 1. It seems that lines/boundaries are not broken at intersections
205 * with points/centroids. Check if true, if yes, skip GV_POINTS
206 * 2. list of lines to break and list of reference lines
207 * aline: reference line, if List_ref == NULL, use all
208 * break aline only if it is in the list of lines to break
209 * bline: line to break, if List_break == NULL, break all
210 */
211
212 /* To find intersection of two lines (Vect_line_intersection) is quite slow.
213 * Fortunately usual lines/boundaries in GIS often forms a network where
214 * lines are connected by end points, and touch by MBR. This function checks
215 * and occasionally skips such cases. This is currently done for 2D only
216 */
217
218 /* Go through all lines in vector, for each select lines which overlap MBR
219 * of this line exclude those connected by one endpoint (see above) and try
220 * to intersect, if lines intersect write new lines at the end of the file,
221 * and process next line (remaining lines overlapping box are skipped)
222 */
223 nbreaks = 0;
224
225 for (iline = 0; iline < nlines; iline++) {
226 G_percent(iline, nlines, 1);
227
228 /* aline: reference line */
229 if (List_ref) {
230 aline = List_ref->value[iline];
231 }
232 else if (List_break) {
233 aline = List_break->value[iline];
234 }
235 else {
236 aline = iline + 1;
237 }
238
239 G_debug(3, "aline = %d", aline);
241 continue;
242
243 a_is_ref = 0;
244 break_a = 1;
245 if (List_ref) {
246 a_is_ref = 1;
247 }
248
249 if (List_break) {
250 break_a = 0;
251 if (bsearch(&aline, List_break->value, List_break->n_values,
252 sizeof(int), cmp)) {
253 break_a = 1;
254 }
255 }
256
258 if (!(atype & type))
259 continue;
260
263
264 /* Find which sides of the box are touched by intermediate (non-end)
265 * points of line */
266 if (!is3d) {
268 for (j = 1; j < APoints->n_points; j++) {
269 if (APoints->y[j] == ABox.N)
270 touch1_n = 1;
271 if (APoints->y[j] == ABox.S)
272 touch1_s = 1;
273 if (APoints->x[j] == ABox.E)
274 touch1_e = 1;
275 if (APoints->x[j] == ABox.W)
276 touch1_w = 1;
277 }
278 G_debug(3, "touch1: n = %d s = %d e = %d w = %d", touch1_n,
281 for (j = 0; j < APoints->n_points - 1; j++) {
282 if (APoints->y[j] == ABox.N)
283 touch2_n = 1;
284 if (APoints->y[j] == ABox.S)
285 touch2_s = 1;
286 if (APoints->x[j] == ABox.E)
287 touch2_e = 1;
288 if (APoints->x[j] == ABox.W)
289 touch2_w = 1;
290 }
291 G_debug(3, "touch2: n = %d s = %d e = %d w = %d", touch2_n,
293 }
294
296 G_debug(3, " %d lines selected by box", List->n_values);
297
298 for (j = -1; j < List->n_values; j++) {
299
300 /* bline: line to break */
301
302 if (j == -1) {
303 /* check first for self-intersections */
304 if (aline <= nlines_org)
305 bline = aline;
306 else
307 continue;
308 }
309 else {
310 bline = List->id[j];
311 if (bline == aline)
312 continue;
313 }
314
315 b_is_ref = 0;
316 break_b = 1;
317 if (List_ref && bsearch(&bline, List_ref->value, List_ref->n_values,
318 sizeof(int), cmp)) {
319 b_is_ref = 1;
320 /* reference bline will be broken when it is aline */
321 break_b = 0;
322 }
323
324 if (List_break) {
325 break_b = 0;
326 if (bsearch(&bline, List_break->value, List_break->n_values,
327 sizeof(int), cmp)) {
328 break_b = 1;
329 }
330 }
331
332 if (!break_a && !break_b)
333 continue;
334
335 /* check intersection of aline with bline only once
336 * if possible */
337 if (break_a && break_b && aline > bline &&
338 (!List_ref || b_is_ref)) {
339 continue;
340 }
341
342 G_debug(3, " j = %d bline = %d", j, bline);
343
346
347 if (j == -1)
348 BBox = &ABox;
349 else
350 BBox = &List->box[j];
351
352 /* Check if touch by end node only */
353 if (!is3d) {
356
357 node = 0;
358 if (anode1 == bnode1 || anode1 == bnode2)
359 node = anode1;
360 else if (anode2 == bnode1 || anode2 == bnode2)
361 node = anode2;
362
363 if (node) {
365 if ((node == anode1 && nodey == ABox.N && !touch1_n &&
366 nodey == BBox->S) ||
367 (node == anode2 && nodey == ABox.N && !touch2_n &&
368 nodey == BBox->S) ||
369 (node == anode1 && nodey == ABox.S && !touch1_s &&
370 nodey == BBox->N) ||
371 (node == anode2 && nodey == ABox.S && !touch2_s &&
372 nodey == BBox->N) ||
373 (node == anode1 && nodex == ABox.E && !touch1_e &&
374 nodex == BBox->W) ||
375 (node == anode2 && nodex == ABox.E && !touch2_e &&
376 nodex == BBox->W) ||
377 (node == anode1 && nodex == ABox.W && !touch1_w &&
378 nodex == BBox->E) ||
379 (node == anode2 && nodex == ABox.W && !touch2_w &&
380 nodex == BBox->E)) {
381
382 G_debug(3,
383 "lines %d and %d touching by end nodes only -> "
384 "no intersection",
385 aline, bline);
386 continue;
387 }
388 }
389 }
390
391 AXLines = NULL;
392 BXLines = NULL;
393
394 if (aline != bline) {
396 &BXLines, &naxlines, &nbxlines, 0);
397 }
398 else {
400 &BXLines, &naxlines, &nbxlines, 0);
401 }
402
403 G_debug(3, " naxlines = %d nbxlines = %d", naxlines, nbxlines);
404
405 /* This part handles a special case when aline == bline, no other
406 * intersection was found and the line is forming collapsed loop,
407 * for example 0,0;1,0;0,0 should be broken at 1,0.
408 * ---> */
409 if (aline == bline && naxlines == 0 && nbxlines == 0 &&
410 APoints->n_points >= 3 && break_a) {
411 int centre;
412
413 G_debug(3, " Check collapsed loop");
414 if (APoints->n_points % 2) { /* odd number of vertices */
415 centre = APoints->n_points / 2; /* index of centre */
416 if (APoints->x[centre - 1] == APoints->x[centre + 1] &&
417 APoints->y[centre - 1] == APoints->y[centre + 1] &&
418 APoints->z[centre - 1] ==
419 APoints->z[centre + 1]) { /* -> break */
420 AXLines = (struct line_pnts **)G_malloc(
421 2 * sizeof(struct line_pnts *));
424
425 for (i = 0; i <= centre; i++)
427 APoints->y[i], APoints->z[i]);
428
429 for (i = centre; i < APoints->n_points; i++)
431 APoints->y[i], APoints->z[i]);
432
433 naxlines = 2;
434 }
435 }
436 }
437 /* <--- */
438
439 if (Err) { /* array for intersections (more than needed */
440 xx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
441 yx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
442 zx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double));
443 }
444 nx = 0; /* number of intersections to be written to Err */
445 if (naxlines > 0) { /* intersection -> write out */
446
447 G_debug(3, " aline = %d, bline = %d, naxlines = %d", aline,
448 bline, naxlines);
449
450 if (!check && break_a)
452 for (k = 0; k < naxlines; k++) {
453 /* Write new line segments */
454 /* line may collapse, don't write zero length lines */
456 if ((atype & GV_POINTS) || AXLines[k]->n_points > 1) {
457 if (!check && break_a) {
458 ret =
460 G_debug(3, "Line %d written, npoints = %d", ret,
461 AXLines[k]->n_points);
462 if (List_ref && a_is_ref) {
464 }
465 if (List_break && break_a) {
467 }
468 }
469 }
470 else
471 G_debug(3, "axline %d has zero length", k);
472
473 /* Write intersection points */
474 if (Err) {
475 if (k > 0) {
476 xx[nx] = AXLines[k]->x[0];
477 yx[nx] = AXLines[k]->y[0];
478 zx[nx] = AXLines[k]->z[0];
479 nx++;
480 }
481 }
483 }
484 nbreaks += naxlines - 1;
485 }
486 if (AXLines)
488
489 if (nbxlines > 0) {
490 if (aline != bline) { /* Self intersection, do not write twice,
491 TODO: is it OK? */
492
493 G_debug(3, " aline = %d, bline = %d, nbxlines = %d",
495
496 if (!check && break_b)
498 for (k = 0; k < nbxlines; k++) {
499 /* Write new line segments */
500 /* line may collapse, don't write zero length lines */
502 if ((btype & GV_POINTS) || BXLines[k]->n_points > 1) {
503 if (!check && break_b) {
505 BCats);
506 G_debug(5, "Line %d written", ret);
507 if (List_ref && b_is_ref) {
509 }
510 if (List_break) {
512 }
513 }
514 }
515 else
516 G_debug(3, "bxline %d has zero length", k);
517
518 /* Write intersection points */
519 if (Err) {
520 if (k > 0) {
521 found = 0;
522 for (l = 0; l < nx; l++) {
523 if (xx[l] == BXLines[k]->x[0] &&
524 yx[l] == BXLines[k]->y[0] &&
525 zx[l] == BXLines[k]->z[0]) {
526 found = 1;
527 break;
528 }
529 }
530 if (!found) {
531 xx[nx] = BXLines[k]->x[0];
532 yx[nx] = BXLines[k]->y[0];
533 zx[nx] = BXLines[k]->z[0];
534 nx++;
535 }
536 }
537 }
538 }
539 nbreaks += nbxlines - 1;
540 }
541 for (k = 0; k < nbxlines; k++)
543 }
544 if (BXLines)
546 if (Err) {
547 for (l = 0; l < nx; l++) { /* Write out errors */
548 Vect_reset_line(Points);
549 Vect_append_point(Points, xx[l], yx[l], zx[l]);
550 ret = Vect_write_line(Err, GV_POINT, Points, Cats);
551 }
552
553 G_free(xx);
554 G_free(yx);
555 G_free(zx);
556 }
557 if (naxlines > 0 && !check && break_a) {
558 G_debug(3, "aline was broken, use next one");
559 break; /* first line was broken and deleted -> take the next one
560 */
561 }
562 }
563
564 if (List_ref) {
565 nlines = List_ref->n_values;
566 }
567 else if (List_break) {
568 nlines = List_break->n_values;
569 }
570 else {
571 nlines = Vect_get_num_lines(Map);
572 }
573 G_debug(3, "nlines = %d", nlines);
574 } /* for each line */
575 G_percent(nlines, nlines, 1); /* finish it */
576
577 G_verbose_message(_("Intersections: %d"), nbreaks);
578
586
587 return nbreaks;
588}
void Vect_break_lines(struct Map_info *Map, int type, struct Map_info *Err)
Break lines in vector map at each intersection.
Definition break_lines.c:33
int Vect_break_lines_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err)
Break selected lines in vector map at each intersection.
Definition break_lines.c:62
int Vect_check_line_breaks(struct Map_info *Map, int type, struct Map_info *Err)
Check for and count intersecting lines, do not break.
Definition break_lines.c:81
int Vect_check_line_breaks_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err)
Check for and count intersecting lines, do not break.
#define NULL
Definition ccmath.h:32
void G_percent(long, long, int)
Print percent complete messages.
Definition percent.c:61
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:147
#define G_malloc(n)
Definition defs/gis.h:139
void void G_verbose_message(const char *,...) __attribute__((format(printf
void G_ilist_add(struct ilist *, int)
Add item to ilist.
Definition ilist.c:78
int G_debug(int, const char *,...) __attribute__((format(printf
void Vect_destroy_line_struct(struct line_pnts *)
Frees all memory associated with a line_pnts structure, including the structure itself.
Definition line.c:77
int Vect_get_line_nodes(struct Map_info *, int, int *, int *)
Get line nodes.
Definition level_two.c:304
int Vect_get_node_coor(struct Map_info *, int, double *, double *, double *)
Get node coordinates.
Definition level_two.c:274
plus_t Vect_get_num_lines(struct Map_info *)
Fetch number of features (points, lines, boundaries, centroids) in vector map.
Definition level_two.c:75
void Vect_line_box(const struct line_pnts *, struct bound_box *)
Get bounding box of line.
Definition line.c:888
struct boxlist * Vect_new_boxlist(int)
Creates and initializes a struct boxlist.
int Vect_line_intersection2(struct line_pnts *, struct line_pnts *, struct bound_box *, struct bound_box *, struct line_pnts ***, struct line_pnts ***, int *, int *, int)
Intersect 2 lines.
Definition intersect2.c:677
void Vect_destroy_boxlist(struct boxlist *)
Frees all memory associated with a struct boxlist, including the struct itself.
void Vect_destroy_cats_struct(struct line_cats *)
Frees all memory associated with line_cats structure, including the struct itself.
int Vect_read_line(struct Map_info *, struct line_pnts *, struct line_cats *, int)
Read vector feature (topological level required)
int Vect_line_alive(struct Map_info *, int)
Check if feature is alive or dead (topological level required)
int Vect_delete_line(struct Map_info *, off_t)
Delete existing feature (topological level required)
struct line_cats * Vect_new_cats_struct(void)
Creates and initializes line_cats structure.
off_t Vect_write_line(struct Map_info *, int, const struct line_pnts *, const struct line_cats *)
Writes a new feature.
int Vect_select_lines_by_box(struct Map_info *, const struct bound_box *, int, struct boxlist *)
Select lines with bounding boxes by box.
Definition sindex.c:32
void Vect_reset_line(struct line_pnts *)
Reset line.
Definition line.c:129
int Vect_line_prune(struct line_pnts *)
Remove duplicate points, i.e. zero length segments.
Definition line.c:279
struct line_pnts * Vect_new_line_struct(void)
Creates and initializes a line_pnts structure.
Definition line.c:45
int Vect_is_3d(struct Map_info *)
Check if vector map is 3D.
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition line.c:148
#define GV_POINT
Feature types used in memory on run time (may change)
#define GV_LINES
#define GV_POINTS
#define _(str)
Definition glocale.h:10
double b
Definition r_raster.c:39
double l
Definition r_raster.c:39
Vector map info.
Bounding box.
Definition dig_structs.h:62
List of bounding boxes with id.
List of integers.
Definition gis.h:715
Feature category info.
Feature geometry info - coordinates.
double * y
Array of Y coordinates.
double * x
Array of X coordinates.
int n_points
Number of points.
double * z
Array of Z coordinates.