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