GRASS 8 Programmer's Manual 8.6.0dev(2026)-55de52a352
Loading...
Searching...
No Matches
Vlib/snap.c
Go to the documentation of this file.
1/*!
2 * \file lib/vector/Vlib/snap.c
3 *
4 * \brief Vector library - Clean vector map (snap lines)
5 *
6 * Higher level functions for reading/writing/manipulating vectors.
7 *
8 * SPDX-FileCopyrightText: 2001-2009 GRASS Development Team
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 *
11 * \author Radim Blazek
12 * \author update to GRASS 7 Markus Metz
13 */
14
15#include <errno.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/stat.h>
19#include <fcntl.h>
20#include <unistd.h>
21#include <math.h>
22#include <grass/vector.h>
23#include <grass/gis.h>
24#include <grass/glocale.h>
25#include <grass/kdtree.h>
26
27/* translate segment to box and back */
28#define X1W 0x01 /* x1 is West, x2 East */
29#define Y1S 0x02 /* y1 is South, y2 North */
30#define Z1B 0x04 /* z1 is Bottom, z2 Top */
31
32/* Vertex */
33typedef struct {
34 double x, y, z;
35 int anchor; /* 0 - anchor, do not snap this point, that means snap others to
36 this */
37 /* >0 - index of anchor to which snap this point */
38 /* -1 - init value */
39} XPNT;
40
41typedef struct {
42 int anchor;
43 double along;
44} NEW;
45
46/* for qsort */
47static int sort_new(const void *pa, const void *pb)
48{
49 NEW *p1 = (NEW *)pa;
50 NEW *p2 = (NEW *)pb;
51
52 return (p1->along < p2->along ? -1 : (p1->along > p2->along));
53
54 /*
55 if (p1->along < p2->along)
56 return -1;
57 if (p1->along > p2->along)
58 return 1;
59 return 1;
60 */
61}
62
63typedef struct {
64 double x, y, z, along;
65} NEW2;
66
67/* for qsort */
68static int sort_new2(const void *pa, const void *pb)
69{
70 NEW2 *p1 = (NEW2 *)pa;
71 NEW2 *p2 = (NEW2 *)pb;
72
73 return (p1->along < p2->along ? -1 : (p1->along > p2->along));
74}
75
76/* This function is called by RTreeSearch() to find a vertex */
77static int find_item(int id, const struct RTree_Rect *rect G_UNUSED, void *list)
78{
79 G_ilist_add((struct ilist *)list, id);
80 return 0;
81}
82
83/* This function is called by RTreeSearch() to add selected node/line/area/isle
84 * to the list */
85static int add_item(int id, const struct RTree_Rect *rect G_UNUSED, void *list)
86{
87 G_ilist_add((struct ilist *)list, id);
88 return 1;
89}
90
91/* This function is called by RTreeSearch() to add selected node/line/area/isle
92 * to the list */
93static int find_item_box(int id, const struct RTree_Rect *rect, void *list)
94{
95 struct bound_box box;
96
97 box.W = rect->boundary[0];
98 box.S = rect->boundary[1];
99 box.B = rect->boundary[2];
100 box.E = rect->boundary[3];
101 box.N = rect->boundary[4];
102 box.T = rect->boundary[5];
103
104 dig_boxlist_add((struct boxlist *)list, id, &box);
105
106 return 0;
107}
108
109/* This function is called by RTreeSearch() to add selected node/line/area/isle
110 * to the list */
111static int add_item_box(int id, const struct RTree_Rect *rect, void *list)
112{
113 struct bound_box box;
114
115 box.W = rect->boundary[0];
116 box.S = rect->boundary[1];
117 box.B = rect->boundary[2];
118 box.E = rect->boundary[3];
119 box.N = rect->boundary[4];
120 box.T = rect->boundary[5];
121
122 dig_boxlist_add((struct boxlist *)list, id, &box);
123
124 return 1;
125}
126
127static void Vect_snap_lines_list_rtree(struct Map_info *, const struct ilist *,
128 double, struct Map_info *);
129
130static void Vect_snap_lines_list_kdtree(struct Map_info *, const struct ilist *,
131 double, struct Map_info *);
132
133/*!
134 \brief Snap selected lines to existing vertex in threshold.
135
136 Snap selected lines to existing vertices of other selected lines.
137 3D snapping is not supported.
138
139 Lines showing how vertices were snapped may be optionally written to error
140 map. Input map must be opened on level 2 for update at least on
141 GV_BUILD_BASE.
142
143 As mentioned above, lines are not necessarily snapped to nearest vertex! For
144 example: <pre>
145 |
146 | 1 line 3 is snapped to line 1,
147 | then line 2 is not snapped to common node at lines 1 and 3,
148 because it is already outside of threshold
149 ----------- 3
150
151 |
152 | 2
153 |
154 </pre>
155
156 The algorithm selects anchor vertices and snaps non-anchor vertices
157 to these anchors.
158 The distance between anchor vertices is always > threshold.
159 If there is more than one anchor vertex within threshold around a
160 non-anchor vertex, this vertex is snapped to the nearest anchor
161 vertex within threshold.
162
163 \param Map input map where vertices will be snapped
164 \param List_lines list of lines to snap
165 \param thresh threshold in which snap vertices
166 \param[out] Err vector map where lines representing snap are written or NULL
167
168 \return void
169 */
171 double thresh, struct Map_info *Err)
172{
173 if (getenv("GRASS_VECTOR_LOWMEM"))
174 Vect_snap_lines_list_rtree(Map, List_lines, thresh, Err);
175 else
176 Vect_snap_lines_list_kdtree(Map, List_lines, thresh, Err);
177}
178
179static void Vect_snap_lines_list_kdtree(struct Map_info *Map,
180 const struct ilist *List_lines,
181 double thresh, struct Map_info *Err)
182{
183 struct line_pnts *Points, *NPoints;
184 struct line_cats *Cats;
185 int line, ltype, line_idx;
186 double thresh2;
187
188 int point; /* index in points array */
189 int nsnapped, ncreated; /* number of snapped verices, number of new vertices
190 (on segments) */
191 int apoints, npoints; /* number of allocated points, registered points */
192 XPNT *XPnts; /* Array of points */
193 NEW *New = NULL; /* Array of new points */
194 int anew = 0, nnew; /* allocated new points, number of new points */
195 struct ilist *List;
196 int *Index = NULL; /* indexes of anchors for vertices */
197 int aindex = 0; /* allocated Index */
198
199 struct kdtree *KDTree;
200 double c[2];
201 double *kdd;
202 int *kduid, kd_found;
203
204 if (List_lines->n_values < 1)
205 return;
206
207 Points = Vect_new_line_struct();
211
213
215
216 /* Go through all lines in vector, and add each point to structure of points
217 */
218 apoints = 0;
219 point = 1; /* index starts from 1 ! */
220 XPnts = NULL;
221
222 G_important_message(_("Snap vertices Pass 1: select points"));
223 for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
224 int v;
225
226 G_percent(line_idx, List_lines->n_values, 2);
227
228 line = List_lines->value[line_idx];
229
230 G_debug(3, "line = %d", line);
231 if (!Vect_line_alive(Map, line))
232 continue;
233
234 ltype = Vect_read_line(Map, Points, Cats, line);
235
236 for (v = 0; v < Points->n_points; v++) {
237
238 G_debug(3, " vertex v = %d", v);
239
240 /* coords */
241 c[0] = Points->x[v];
242 c[1] = Points->y[v];
243
244 if (kdtree_insert(KDTree, c, point, 0)) {
245 /* Add to structure */
246 if ((point - 1) == apoints) {
247 apoints += 10000;
248 XPnts =
249 (XPNT *)G_realloc(XPnts, (apoints + 1) * sizeof(XPNT));
250 }
251 XPnts[point].x = Points->x[v];
252 XPnts[point].y = Points->y[v];
253 XPnts[point].anchor = -1;
254 point++;
255 }
256 }
257 }
258 G_percent(line_idx, List_lines->n_values, 2); /* finish it */
259
260 npoints = point - 1;
261
262 /* Go through all registered points and if not yet marked mark it as anchor
263 * and assign this anchor to all not yet marked points in threshold */
264
265 G_important_message(_("Snap vertices Pass 2: assign anchor vertices"));
266
267 for (point = 1; point <= npoints; point++) {
268 int i;
269
270 G_percent(point, npoints, 4);
271
272 G_debug(3, " point = %d", point);
273
274 if (XPnts[point].anchor >= 0)
275 continue;
276
277 XPnts[point].anchor = 0; /* make it anchor */
278
279 /* Find points in threshold */
280 c[0] = XPnts[point].x;
281 c[1] = XPnts[point].y;
282
284 kd_found = kdtree_dnn(KDTree, c, &kduid, &kdd, thresh, &point);
285 G_debug(4, " %d points in threshold box", kd_found);
286
287 for (i = 0; i < kd_found; i++) {
288 int pointb;
289 double dx, dy, dist2;
290
291 pointb = kduid[i];
292 if (pointb == point)
293 continue;
294
295 dx = XPnts[pointb].x - XPnts[point].x;
296 dy = XPnts[pointb].y - XPnts[point].y;
297 dist2 = dx * dx + dy * dy;
298
299 if (dist2 > thresh2) /* outside threshold */
300 continue;
301
302 /* doesn't have an anchor yet */
303 if (XPnts[pointb].anchor == -1) {
304 XPnts[pointb].anchor = point;
305 }
306 else if (XPnts[pointb].anchor >
307 0) { /* check distance to previously assigned anchor */
308 double dist2_a;
309
310 dx = XPnts[XPnts[pointb].anchor].x - XPnts[pointb].x;
311 dy = XPnts[XPnts[pointb].anchor].y - XPnts[pointb].y;
312 dist2_a = dx * dx + dy * dy;
313
314 /* replace old anchor */
315 if (dist2 < dist2_a) {
316 XPnts[pointb].anchor = point;
317 }
318 }
319 }
320 if (kd_found) {
321 G_free(kdd);
322 G_free(kduid);
323 }
324 }
325
326 /* Go through all lines and:
327 * 1) for all vertices: if not anchor snap it to its anchor
328 * 2) for all segments: snap it to all anchors in threshold (except
329 * anchors of vertices of course) */
330
331 nsnapped = ncreated = 0;
332
333 G_important_message(_("Snap vertices Pass 3: snap to assigned points"));
334
335 for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
336 int v, spoint, anchor;
337 int changed = 0;
338 double kddist;
339
340 G_percent(line_idx, List_lines->n_values, 2);
341
342 line = List_lines->value[line_idx];
343
344 G_debug(3, "line = %d", line);
345 if (!Vect_line_alive(Map, line))
346 continue;
347
348 ltype = Vect_read_line(Map, Points, Cats, line);
349
350 if (Points->n_points >= aindex) {
351 aindex = Points->n_points;
352 Index = (int *)G_realloc(Index, aindex * sizeof(int));
353 }
354
355 /* Snap all vertices */
356 G_debug(3, "Snap all vertices");
357 for (v = 0; v < Points->n_points; v++) {
358 /* Box */
359 c[0] = Points->x[v];
360 c[1] = Points->y[v];
361
362 /* Find point ( should always find one point ) */
364
365 spoint = -1;
366 kdtree_knn(KDTree, c, &spoint, &kddist, 1, NULL);
367 if (spoint == -1)
368 G_fatal_error("Point not in KD Tree");
369
370 anchor = XPnts[spoint].anchor;
371
372 if (anchor > 0) { /* to be snapped */
373 Points->x[v] = XPnts[anchor].x;
374 Points->y[v] = XPnts[anchor].y;
375 nsnapped++;
376 changed = 1;
377 Index[v] = anchor; /* point on new location */
378 }
379 else {
380 Index[v] = spoint; /* old point */
381 }
382 }
383
384 /* New points */
386
387 /* Snap all segments to anchors in threshold */
388 G_debug(3, "Snap all segments");
389 for (v = 0; v < Points->n_points - 1; v++) {
390 int i;
391 double x1, x2, y1, y2, xmin, xmax, ymin, ymax;
392 double rc[4];
393
394 G_debug(3, " segment = %d end anchors : %d %d", v, Index[v],
395 Index[v + 1]);
396
397 x1 = Points->x[v];
398 x2 = Points->x[v + 1];
399 y1 = Points->y[v];
400 y2 = Points->y[v + 1];
401
402 Vect_append_point(NPoints, Points->x[v], Points->y[v],
403 Points->z[v]);
404
405 /* Box */
406 if (x1 <= x2) {
407 xmin = x1;
408 xmax = x2;
409 }
410 else {
411 xmin = x2;
412 xmax = x1;
413 }
414 if (y1 <= y2) {
415 ymin = y1;
416 ymax = y2;
417 }
418 else {
419 ymin = y2;
420 ymax = y1;
421 }
422
423 /* Find points */
425 G_debug(3, " search anchors for segment %g,%g to %g,%g", x1, y1,
426 x2, y2);
427 /* distance search: circle around midpoint encompassing
428 * endpoints
429 * box search: box encompassing endpoints,
430 * smaller than corresponding circle */
431 rc[0] = xmin - thresh * 2;
432 rc[1] = ymin - thresh * 2;
433 rc[2] = xmax + thresh * 2;
434 rc[3] = ymax + thresh * 2;
435
437
438 G_debug(3, " %d points in box", kd_found);
439
440 /* Snap to anchor in threshold different from end points */
441 nnew = 0;
442 for (i = 0; i < kd_found; i++) {
443 double dist2, along;
444 int status;
445
446 spoint = kduid[i];
447 G_debug(4, " spoint = %d anchor = %d", spoint,
448 XPnts[spoint].anchor);
449
450 if (spoint == Index[v] || spoint == Index[v + 1])
451 continue; /* end point */
452 if (XPnts[spoint].anchor > 0)
453 continue; /* point is not anchor */
454
455 /* Check the distance */
457 XPnts[spoint].x, XPnts[spoint].y, 0, x1, y1, 0, x2, y2, 0,
458 0, NULL, NULL, NULL, &along, &status);
459
460 G_debug(4, " distance = %lf", sqrt(dist2));
461
462 if (status == 0 && dist2 <= thresh2) {
463 G_debug(4, " anchor in thresh, along = %lf", along);
464
465 if (nnew == anew) {
466 anew += 100;
467 New = (NEW *)G_realloc(New, anew * sizeof(NEW));
468 }
469 New[nnew].anchor = spoint;
470 New[nnew].along = along;
471 nnew++;
472 }
473 }
474 if (kd_found) {
475 G_free(kduid);
476 }
477 G_debug(3, " nnew = %d", nnew);
478 /* insert new vertices */
479 if (nnew > 0) {
480 /* sort by distance along the segment */
481 qsort(New, sizeof(char) * nnew, sizeof(NEW), sort_new);
482
483 for (i = 0; i < nnew; i++) {
484 anchor = New[i].anchor;
485 /* Vect_line_insert_point ( Points, ++v, XPnts[anchor].x,
486 * XPnts[anchor].y, 0); */
487 Vect_append_point(NPoints, XPnts[anchor].x, XPnts[anchor].y,
488 0);
489 ncreated++;
490 }
491 changed = 1;
492 }
493 }
494
495 /* append end point */
496 v = Points->n_points - 1;
497 Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
498
499 if (changed) { /* rewrite the line */
500 Vect_line_prune(NPoints); /* remove duplicates */
501 if (NPoints->n_points > 1 || !(ltype & GV_LINES)) {
503 }
504 else {
505 Vect_delete_line(Map, line);
506 }
507 if (Err) {
508 Vect_write_line(Err, ltype, Points, Cats);
509 }
510 }
511 } /* for each line */
512 G_percent(line_idx, List_lines->n_values, 2); /* finish it */
513
517 G_free(XPnts);
518 G_free(Index);
519 G_free(New);
521
522 G_verbose_message(_("Snapped vertices: %d"), nsnapped);
523 G_verbose_message(_("New vertices: %d"), ncreated);
525}
526
527static void Vect_snap_lines_list_rtree(struct Map_info *Map,
528 const struct ilist *List_lines,
529 double thresh, struct Map_info *Err)
530{
531 struct line_pnts *Points, *NPoints;
532 struct line_cats *Cats;
533 int line, ltype, line_idx;
534 double thresh2;
535
536 int point; /* index in points array */
537 int nsnapped, ncreated; /* number of snapped verices, number of new vertices
538 (on segments) */
539 int apoints, npoints; /* number of allocated points, registered points */
540 XPNT *XPnts; /* Array of points */
541 NEW *New = NULL; /* Array of new points */
542 int anew = 0, nnew; /* allocated new points , number of new points */
543 struct ilist *List;
544 int *Index = NULL; /* indexes of anchors for vertices */
545 int aindex = 0; /* allocated Index */
546
547 struct RTree *RTree;
548 int rtreefd = -1;
549 static struct RTree_Rect rect;
550 static int rect_init = 0;
551
552 if (!rect_init) {
553 rect.boundary = G_malloc(6 * sizeof(RectReal));
554 rect_init = 6;
555 }
556
557 if (List_lines->n_values < 1)
558 return;
559
560 Points = Vect_new_line_struct();
564 if (getenv("GRASS_VECTOR_LOWMEM")) {
565 char *filename = G_tempfile();
566
567 rtreefd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
568 if (rtreefd < 0) {
569 G_fatal_error(_("Unable to create temporary file <%s>: %s"),
570 filename, strerror(errno));
571 }
572 if (remove(filename) != 0) {
573 G_warning(_("Unable to remove temporary file <%s>: %s"), filename,
574 strerror(errno));
575 }
576 G_free(filename);
577 }
579
581
582 /* Go through all lines in vector, and add each point to structure of points
583 */
584 apoints = 0;
585 point = 1; /* index starts from 1 ! */
586 XPnts = NULL;
587
588 G_important_message(_("Snap vertices Pass 1: select points"));
589 for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
590 int v;
591
592 G_percent(line_idx, List_lines->n_values, 2);
593
594 line = List_lines->value[line_idx];
595
596 G_debug(3, "line = %d", line);
597 if (!Vect_line_alive(Map, line))
598 continue;
599
600 ltype = Vect_read_line(Map, Points, Cats, line);
601
602 for (v = 0; v < Points->n_points; v++) {
603 G_debug(3, " vertex v = %d", v);
604
605 /* Box */
606 rect.boundary[0] = Points->x[v];
607 rect.boundary[3] = Points->x[v];
608 rect.boundary[1] = Points->y[v];
609 rect.boundary[4] = Points->y[v];
610 rect.boundary[2] = 0;
611 rect.boundary[5] = 0;
612
613 /* Already registered ? */
615 RTreeSearch(RTree, &rect, find_item, List);
616 G_debug(3, "List : nvalues = %d", List->n_values);
617
618 if (List->n_values == 0) { /* Not found */
619 /* Add to tree and to structure */
620 RTreeInsertRect(&rect, point, RTree);
621 if ((point - 1) == apoints) {
622 apoints += 10000;
623 XPnts =
624 (XPNT *)G_realloc(XPnts, (apoints + 1) * sizeof(XPNT));
625 }
626 XPnts[point].x = Points->x[v];
627 XPnts[point].y = Points->y[v];
628 XPnts[point].anchor = -1;
629 point++;
630 }
631 }
632 }
633 G_percent(line_idx, List_lines->n_values, 2); /* finish it */
634
635 npoints = point - 1;
636
637 /* Go through all registered points and if not yet marked mark it as anchor
638 * and assign this anchor to all not yet marked points in threshold */
639
640 G_important_message(_("Snap vertices Pass 2: assign anchor vertices"));
641
642 for (point = 1; point <= npoints; point++) {
643 int i;
644
645 G_percent(point, npoints, 4);
646
647 G_debug(3, " point = %d", point);
648
649 if (XPnts[point].anchor >= 0)
650 continue;
651
652 XPnts[point].anchor = 0; /* make it anchor */
653
654 /* Find points in threshold */
655 rect.boundary[0] = XPnts[point].x - thresh;
656 rect.boundary[3] = XPnts[point].x + thresh;
657 rect.boundary[1] = XPnts[point].y - thresh;
658 rect.boundary[4] = XPnts[point].y + thresh;
659 rect.boundary[2] = 0;
660 rect.boundary[5] = 0;
661
663 RTreeSearch(RTree, &rect, add_item, List);
664 G_debug(4, " %d points in threshold box", List->n_values);
665
666 for (i = 0; i < List->n_values; i++) {
667 int pointb;
668 double dx, dy, dist2;
669
670 pointb = List->value[i];
671 if (pointb == point)
672 continue;
673
674 dx = XPnts[pointb].x - XPnts[point].x;
675 dy = XPnts[pointb].y - XPnts[point].y;
676 dist2 = dx * dx + dy * dy;
677
678 if (dist2 > thresh2) /* outside threshold */
679 continue;
680
681 /* doesn't have an anchor yet */
682 if (XPnts[pointb].anchor == -1) {
683 XPnts[pointb].anchor = point;
684 }
685 else if (XPnts[pointb].anchor >
686 0) { /* check distance to previously assigned anchor */
687 double dist2_a;
688
689 dx = XPnts[XPnts[pointb].anchor].x - XPnts[pointb].x;
690 dy = XPnts[XPnts[pointb].anchor].y - XPnts[pointb].y;
691 dist2_a = dx * dx + dy * dy;
692
693 /* replace old anchor */
694 if (dist2 < dist2_a) {
695 XPnts[pointb].anchor = point;
696 }
697 }
698 }
699 }
700
701 /* Go through all lines and:
702 * 1) for all vertices: if not anchor snap it to its anchor
703 * 2) for all segments: snap it to all anchors in threshold (except
704 * anchors of vertices of course) */
705
706 nsnapped = ncreated = 0;
707
708 G_important_message(_("Snap vertices Pass 3: snap to assigned points"));
709
710 for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
711 int v, spoint, anchor;
712 int changed = 0;
713
714 G_percent(line_idx, List_lines->n_values, 2);
715
716 line = List_lines->value[line_idx];
717
718 G_debug(3, "line = %d", line);
719 if (!Vect_line_alive(Map, line))
720 continue;
721
722 ltype = Vect_read_line(Map, Points, Cats, line);
723
724 if (Points->n_points >= aindex) {
725 aindex = Points->n_points;
726 Index = (int *)G_realloc(Index, aindex * sizeof(int));
727 }
728
729 /* Snap all vertices */
730 for (v = 0; v < Points->n_points; v++) {
731 /* Box */
732 rect.boundary[0] = Points->x[v];
733 rect.boundary[3] = Points->x[v];
734 rect.boundary[1] = Points->y[v];
735 rect.boundary[4] = Points->y[v];
736 rect.boundary[2] = 0;
737 rect.boundary[5] = 0;
738
739 /* Find point ( should always find one point ) */
741
742 RTreeSearch(RTree, &rect, add_item, List);
743
744 spoint = List->value[0];
745 anchor = XPnts[spoint].anchor;
746
747 if (anchor > 0) { /* to be snapped */
748 Points->x[v] = XPnts[anchor].x;
749 Points->y[v] = XPnts[anchor].y;
750 nsnapped++;
751 changed = 1;
752 Index[v] = anchor; /* point on new location */
753 }
754 else {
755 Index[v] = spoint; /* old point */
756 }
757 }
758
759 /* New points */
761
762 /* Snap all segments to anchors in threshold */
763 for (v = 0; v < Points->n_points - 1; v++) {
764 int i;
765 double x1, x2, y1, y2, xmin, xmax, ymin, ymax;
766
767 G_debug(3, " segment = %d end anchors : %d %d", v, Index[v],
768 Index[v + 1]);
769
770 x1 = Points->x[v];
771 x2 = Points->x[v + 1];
772 y1 = Points->y[v];
773 y2 = Points->y[v + 1];
774
775 Vect_append_point(NPoints, Points->x[v], Points->y[v],
776 Points->z[v]);
777
778 /* Box */
779 if (x1 <= x2) {
780 xmin = x1;
781 xmax = x2;
782 }
783 else {
784 xmin = x2;
785 xmax = x1;
786 }
787 if (y1 <= y2) {
788 ymin = y1;
789 ymax = y2;
790 }
791 else {
792 ymin = y2;
793 ymax = y1;
794 }
795
796 rect.boundary[0] = xmin - thresh;
797 rect.boundary[3] = xmax + thresh;
798 rect.boundary[1] = ymin - thresh;
799 rect.boundary[4] = ymax + thresh;
800 rect.boundary[2] = 0;
801 rect.boundary[5] = 0;
802
803 /* Find points */
805 RTreeSearch(RTree, &rect, add_item, List);
806
807 G_debug(3, " %d points in box", List->n_values);
808
809 /* Snap to anchor in threshold different from end points */
810 nnew = 0;
811 for (i = 0; i < List->n_values; i++) {
812 double dist2, along;
813 int status;
814
815 spoint = List->value[i];
816 G_debug(4, " spoint = %d anchor = %d", spoint,
817 XPnts[spoint].anchor);
818
819 if (spoint == Index[v] || spoint == Index[v + 1])
820 continue; /* end point */
821 if (XPnts[spoint].anchor > 0)
822 continue; /* point is not anchor */
823
824 /* Check the distance */
826 XPnts[spoint].x, XPnts[spoint].y, 0, x1, y1, 0, x2, y2, 0,
827 0, NULL, NULL, NULL, &along, &status);
828
829 G_debug(4, " distance = %lf", sqrt(dist2));
830
831 if (status == 0 && dist2 <= thresh2) {
832 G_debug(4, " anchor in thresh, along = %lf", along);
833
834 if (nnew == anew) {
835 anew += 100;
836 New = (NEW *)G_realloc(New, anew * sizeof(NEW));
837 }
838 New[nnew].anchor = spoint;
839 New[nnew].along = along;
840 nnew++;
841 }
842 }
843 G_debug(3, " nnew = %d", nnew);
844 /* insert new vertices */
845 if (nnew > 0) {
846 /* sort by distance along the segment */
847 qsort(New, sizeof(char) * nnew, sizeof(NEW), sort_new);
848
849 for (i = 0; i < nnew; i++) {
850 anchor = New[i].anchor;
851 /* Vect_line_insert_point ( Points, ++v, XPnts[anchor].x,
852 * XPnts[anchor].y, 0); */
853 Vect_append_point(NPoints, XPnts[anchor].x, XPnts[anchor].y,
854 0);
855 ncreated++;
856 }
857 changed = 1;
858 }
859 }
860
861 /* append end point */
862 v = Points->n_points - 1;
863 Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
864
865 if (changed) { /* rewrite the line */
866 Vect_line_prune(NPoints); /* remove duplicates */
867 if (NPoints->n_points > 1 || !(ltype & GV_LINES)) {
869 }
870 else {
871 Vect_delete_line(Map, line);
872 }
873 if (Err) {
874 Vect_write_line(Err, ltype, Points, Cats);
875 }
876 }
877 } /* for each line */
878 G_percent(line_idx, List_lines->n_values, 2); /* finish it */
879
883 G_free(XPnts);
884 G_free(Index);
885 G_free(New);
887 if (rtreefd >= 0)
888 close(rtreefd);
889
890 G_verbose_message(_("Snapped vertices: %d"), nsnapped);
891 G_verbose_message(_("New vertices: %d"), ncreated);
893}
894
895/*!
896 \brief Snap lines in vector map to existing vertex in threshold.
897
898 For details see Vect_snap_lines_list()
899
900 \param[in] Map input map where vertices will be snapped
901 \param[in] type type of lines to snap
902 \param[in] thresh threshold in which snap vertices
903 \param[out] Err vector map where lines representing snap are written or NULL
904
905 \return void
906 */
907void Vect_snap_lines(struct Map_info *Map, int type, double thresh,
908 struct Map_info *Err)
909{
910 int line, nlines, ltype;
911 struct ilist *List;
912
914
915 nlines = Vect_get_num_lines(Map);
916
917 G_important_message(_("Reading features..."));
918 for (line = 1; line <= nlines; line++) {
919 G_debug(3, "line = %d", line);
920
921 if (!Vect_line_alive(Map, line))
922 continue;
923
924 ltype = Vect_read_line(Map, NULL, NULL, line);
925
926 if (!(ltype & type))
927 continue;
928
929 G_ilist_add(List, line);
930 }
931
933
935
936 return;
937}
938
939/*!
940 \brief Snap a line to reference lines in Map with threshold.
941
942 3D snapping is supported. The line to snap and the reference lines
943 can but do not need to be in different vector maps.
944
945 Vect_snap_line() uses less memory, but is slower than
946 Vect_snap_lines_list()
947
948 For details on snapping, see Vect_snap_lines_list()
949
950 \param[in] Map input map with reference lines
951 \param[in] reflist list of reference lines
952 \param[in,out] Points line points to snap
953 \param[in] thresh threshold in which to snap vertices
954 \param[in] with_z 2D or 3D snapping
955 \param[in,out] nsnapped number of snapped vertices
956 \param[in,out] ncreated number of new vertices (on segments)
957
958 \return 1 if line was changed, otherwise 0
959 */
961 struct line_pnts *Points, double thresh, int with_z,
962 int *nsnapped, int *ncreated)
963{
964 struct line_pnts *LPoints, *NPoints;
965 struct line_cats *Cats;
966 int i, v, line, nlines;
967 int changed;
968 double thresh2;
969
970 int point; /* index in points array */
971 int segment; /* index in segments array */
972 int asegments; /* number of allocated segments */
973 char *XSegs = NULL; /* Array of segments */
974 NEW2 *New = NULL; /* Array of new points */
975 int anew = 0, nnew; /* allocated new points , number of new points */
976 struct boxlist *List;
977
978 struct RTree *pnt_tree, /* spatial index for reference points */
979 *seg_tree; /* spatial index for reference segments */
980 struct RTree_Rect rect;
981
982 rect.boundary = G_malloc(6 * sizeof(RectReal));
983 rect.boundary[0] = 0;
984 rect.boundary[1] = 0;
985 rect.boundary[2] = 0;
986 rect.boundary[3] = 0;
987 rect.boundary[4] = 0;
988 rect.boundary[5] = 0;
989
990 changed = 0;
991 if (nsnapped)
992 *nsnapped = 0;
993 if (ncreated)
994 *ncreated = 0;
995
996 point = Points->n_points;
997 Vect_line_prune(Points);
998 if (point != Points->n_points)
999 changed = 1;
1000
1001 nlines = reflist->n_values;
1002 if (nlines < 1) {
1003 G_free(rect.boundary);
1004 return changed;
1005 }
1006
1011 with_z = (with_z != 0);
1012 pnt_tree = RTreeCreateTree(-1, 0, 2 + with_z);
1014 seg_tree = RTreeCreateTree(-1, 0, 2 + with_z);
1016
1017 thresh2 = thresh * thresh;
1018
1019 point = segment = 1; /* index starts from 1 ! */
1020 asegments = 0;
1021
1022 /* Add all vertices and all segments of all reference lines
1023 * to spatial indices */
1024 nlines = reflist->n_values;
1025 for (i = 0; i < nlines; i++) {
1026
1027 line = reflist->value[i];
1028
1029 G_debug(3, "line = %d", line);
1030 if (!Vect_line_alive(Map, line))
1031 continue;
1032
1033 Vect_read_line(Map, LPoints, Cats, line);
1035
1036 for (v = 0; v < LPoints->n_points; v++) {
1037 G_debug(3, " vertex v = %d", v);
1038
1039 /* Box */
1040 rect.boundary[0] = LPoints->x[v];
1041 rect.boundary[3] = LPoints->x[v];
1042 rect.boundary[1] = LPoints->y[v];
1043 rect.boundary[4] = LPoints->y[v];
1044 if (with_z) {
1045 rect.boundary[2] = LPoints->z[v];
1046 rect.boundary[5] = LPoints->z[v];
1047 }
1048
1049 /* Already registered ? */
1051 RTreeSearch(pnt_tree, &rect, find_item_box, (void *)List);
1052 G_debug(3, "List : nvalues = %d", List->n_values);
1053
1054 if (List->n_values == 0) { /* Not found */
1055
1056 /* Add to points tree */
1057 RTreeInsertRect(&rect, point, pnt_tree);
1058
1059 point++;
1060 }
1061
1062 /* reference segments */
1063 if (v) {
1064 char sides = 0;
1065
1066 /* Box */
1067 if (LPoints->x[v - 1] < LPoints->x[v]) {
1068 rect.boundary[0] = LPoints->x[v - 1];
1069 rect.boundary[3] = LPoints->x[v];
1070 sides |= X1W;
1071 }
1072 else {
1073 rect.boundary[0] = LPoints->x[v];
1074 rect.boundary[3] = LPoints->x[v - 1];
1075 }
1076 if (LPoints->y[v - 1] < LPoints->y[v]) {
1077 rect.boundary[1] = LPoints->y[v - 1];
1078 rect.boundary[4] = LPoints->y[v];
1079 sides |= Y1S;
1080 }
1081 else {
1082 rect.boundary[1] = LPoints->y[v];
1083 rect.boundary[4] = LPoints->y[v - 1];
1084 }
1085 if (LPoints->z[v - 1] < LPoints->z[v]) {
1086 rect.boundary[2] = LPoints->z[v - 1];
1087 rect.boundary[5] = LPoints->z[v];
1088 sides |= Z1B;
1089 }
1090 else {
1091 rect.boundary[2] = LPoints->z[v];
1092 rect.boundary[5] = LPoints->z[v - 1];
1093 }
1094
1095 /* do not check for duplicates, too costly
1096 * because different segments can have identical boxes */
1097 RTreeInsertRect(&rect, segment, seg_tree);
1098
1099 if ((segment - 1) == asegments) {
1100 asegments += 1000;
1101 XSegs = (char *)G_realloc(XSegs,
1102 (asegments + 1) * sizeof(char));
1103 }
1104 XSegs[segment] = sides;
1105 segment++;
1106 }
1107 }
1108 }
1109
1110 /* go through all vertices of the line to snap */
1111 /* find nearest reference vertex */
1112 for (v = 0; v < Points->n_points; v++) {
1113 double dist2, tmpdist2;
1114 double x, y, z;
1115
1116 dist2 = thresh2 + thresh2;
1117 x = Points->x[v];
1118 y = Points->y[v];
1119 z = Points->z[v];
1120
1121 /* Box */
1122 rect.boundary[0] = Points->x[v] - thresh;
1123 rect.boundary[3] = Points->x[v] + thresh;
1124 rect.boundary[1] = Points->y[v] - thresh;
1125 rect.boundary[4] = Points->y[v] + thresh;
1126 if (with_z) {
1127 rect.boundary[2] = Points->z[v] - thresh;
1128 rect.boundary[5] = Points->z[v] + thresh;
1129 }
1130
1132
1133 RTreeSearch(pnt_tree, &rect, add_item_box, (void *)List);
1134
1135 for (i = 0; i < List->n_values; i++) {
1136 double dx = List->box[i].E - Points->x[v];
1137 double dy = List->box[i].N - Points->y[v];
1138 double dz = 0;
1139
1140 if (with_z)
1141 dz = List->box[i].T - Points->z[v];
1142
1143 tmpdist2 = dx * dx + dy * dy + dz * dz;
1144
1145 if (tmpdist2 < dist2) {
1146 dist2 = tmpdist2;
1147
1148 x = List->box[i].E;
1149 y = List->box[i].N;
1150 z = List->box[i].T;
1151 }
1152 }
1153
1155 Points->x[v] = x;
1156 Points->y[v] = y;
1157 Points->z[v] = z;
1158
1159 changed = 1;
1160 if (nsnapped)
1161 (*nsnapped)++;
1162 }
1163 }
1164
1165 /* go through all vertices of the line to snap */
1166 /* find nearest reference segment */
1167 for (v = 0; v < Points->n_points; v++) {
1168 double dist2, tmpdist2;
1169 double x, y, z;
1170
1171 dist2 = thresh2 + thresh2;
1172 x = Points->x[v];
1173 y = Points->y[v];
1174 z = Points->z[v];
1175
1176 /* Box */
1177 rect.boundary[0] = Points->x[v] - thresh;
1178 rect.boundary[3] = Points->x[v] + thresh;
1179 rect.boundary[1] = Points->y[v] - thresh;
1180 rect.boundary[4] = Points->y[v] + thresh;
1181 if (with_z) {
1182 rect.boundary[2] = Points->z[v] - thresh;
1183 rect.boundary[5] = Points->z[v] + thresh;
1184 }
1185
1187
1188 RTreeSearch(seg_tree, &rect, add_item_box, (void *)List);
1189
1190 for (i = 0; i < List->n_values; i++) {
1191 double x1, y1, z1, x2, y2, z2;
1192 double tmpx, tmpy, tmpz;
1193 int status;
1194
1195 segment = List->id[i];
1196
1197 if (XSegs[segment] & X1W) {
1198 x1 = List->box[i].W;
1199 x2 = List->box[i].E;
1200 }
1201 else {
1202 x1 = List->box[i].E;
1203 x2 = List->box[i].W;
1204 }
1205 if (XSegs[segment] & Y1S) {
1206 y1 = List->box[i].S;
1207 y2 = List->box[i].N;
1208 }
1209 else {
1210 y1 = List->box[i].N;
1211 y2 = List->box[i].S;
1212 }
1213 if (XSegs[segment] & Z1B) {
1214 z1 = List->box[i].B;
1215 z2 = List->box[i].T;
1216 }
1217 else {
1218 z1 = List->box[i].T;
1219 z2 = List->box[i].B;
1220 }
1221
1222 /* Check the distance */
1224 Points->x[v], Points->y[v], Points->z[v], x1, y1, z1, x2, y2,
1225 z2, with_z, &tmpx, &tmpy, &tmpz, NULL, &status);
1226
1227 if (tmpdist2 < dist2 && status == 0) {
1228 dist2 = tmpdist2;
1229
1230 x = tmpx;
1231 y = tmpy;
1232 z = tmpz;
1233 }
1234 }
1235
1237 Points->x[v] = x;
1238 Points->y[v] = y;
1239 Points->z[v] = z;
1240
1241 changed = 1;
1242 if (nsnapped)
1243 (*nsnapped)++;
1244 }
1245 }
1246
1248 G_free(XSegs);
1249
1250 /* go through all segments of the line to snap */
1251 /* find nearest reference vertex, add this vertex */
1252 for (v = 0; v < Points->n_points - 1; v++) {
1253 double x1, x2, y1, y2, z1, z2;
1254 double xmin, xmax, ymin, ymax, zmin, zmax;
1255
1256 x1 = Points->x[v];
1257 x2 = Points->x[v + 1];
1258 y1 = Points->y[v];
1259 y2 = Points->y[v + 1];
1260 if (with_z) {
1261 z1 = Points->z[v];
1262 z2 = Points->z[v + 1];
1263 }
1264 else {
1265 z1 = z2 = 0;
1266 }
1267
1268 Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
1269
1270 /* Box */
1271 if (x1 <= x2) {
1272 xmin = x1;
1273 xmax = x2;
1274 }
1275 else {
1276 xmin = x2;
1277 xmax = x1;
1278 }
1279 if (y1 <= y2) {
1280 ymin = y1;
1281 ymax = y2;
1282 }
1283 else {
1284 ymin = y2;
1285 ymax = y1;
1286 }
1287 if (z1 <= z2) {
1288 zmin = z1;
1289 zmax = z2;
1290 }
1291 else {
1292 zmin = z2;
1293 zmax = z1;
1294 }
1295
1296 rect.boundary[0] = xmin - thresh;
1297 rect.boundary[3] = xmax + thresh;
1298 rect.boundary[1] = ymin - thresh;
1299 rect.boundary[4] = ymax + thresh;
1300 rect.boundary[2] = zmin - thresh;
1301 rect.boundary[5] = zmax + thresh;
1302
1303 /* Find points */
1305 RTreeSearch(pnt_tree, &rect, add_item_box, (void *)List);
1306
1307 G_debug(3, " %d points in box", List->n_values);
1308
1309 /* Snap to vertex in threshold different from end points */
1310 nnew = 0;
1311 for (i = 0; i < List->n_values; i++) {
1312 double dist2, along;
1313 int status;
1314
1315 if (!with_z)
1316 List->box[i].T = 0;
1317
1318 if (Points->x[v] == List->box[i].E &&
1319 Points->y[v] == List->box[i].N &&
1320 Points->z[v] == List->box[i].T)
1321 continue; /* start point */
1322
1323 if (Points->x[v + 1] == List->box[i].E &&
1324 Points->y[v + 1] == List->box[i].N &&
1325 Points->z[v + 1] == List->box[i].T)
1326 continue; /* end point */
1327
1328 /* Check the distance */
1330 List->box[i].E, List->box[i].N, List->box[i].T, x1, y1, z1, x2,
1331 y2, z2, with_z, NULL, NULL, NULL, &along, &status);
1332
1333 if (dist2 <= thresh2 && status == 0) {
1334 G_debug(4, " anchor in thresh, along = %lf", along);
1335
1336 if (nnew == anew) {
1337 anew += 100;
1338 New = (NEW2 *)G_realloc(New, anew * sizeof(NEW2));
1339 }
1340 New[nnew].x = List->box[i].E;
1341 New[nnew].y = List->box[i].N;
1342 New[nnew].z = List->box[i].T;
1343 New[nnew].along = along;
1344 nnew++;
1345 }
1346 G_debug(3, "dist: %g, thresh: %g", dist2, thresh2);
1347 }
1348 G_debug(3, " nnew = %d", nnew);
1349 /* insert new vertices */
1350 if (nnew > 0) {
1351 /* sort by distance along the segment */
1352 qsort(New, nnew, sizeof(NEW2), sort_new2);
1353
1354 for (i = 0; i < nnew; i++) {
1355 Vect_append_point(NPoints, New[i].x, New[i].y, New[i].z);
1356 if (ncreated)
1357 (*ncreated)++;
1358 }
1359 changed = 1;
1360 }
1361 }
1362
1363 /* append end point */
1364 v = Points->n_points - 1;
1365 Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
1366
1367 if (Points->n_points != NPoints->n_points) {
1368 Vect_line_prune(NPoints); /* remove duplicates */
1369 Vect_reset_line(Points);
1371 }
1372
1377 G_free(New);
1379 G_free(rect.boundary);
1380
1381 return changed;
1382}
#define X1W
Definition Vlib/snap.c:28
void Vect_snap_lines_list(struct Map_info *Map, const struct ilist *List_lines, double thresh, struct Map_info *Err)
Snap selected lines to existing vertex in threshold.
Definition Vlib/snap.c:170
#define Y1S
Definition Vlib/snap.c:29
#define Z1B
Definition Vlib/snap.c:30
void Vect_snap_lines(struct Map_info *Map, int type, double thresh, struct Map_info *Err)
Snap lines in vector map to existing vertex in threshold.
Definition Vlib/snap.c:907
int Vect_snap_line(struct Map_info *Map, struct ilist *reflist, struct line_pnts *Points, double thresh, int with_z, int *nsnapped, int *ncreated)
Snap a line to reference lines in Map with threshold.
Definition Vlib/snap.c:960
#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_realloc(p, n)
Definition defs/gis.h:138
void void void void G_fatal_error(const char *,...) __attribute__((format(printf
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:136
void void G_verbose_message(const char *,...) __attribute__((format(printf
char * G_tempfile(void)
Returns a temporary file name.
Definition tempfile.c:60
void void void G_important_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
off_t Vect_rewrite_line(struct Map_info *, off_t, int, const struct line_pnts *, const struct line_cats *)
Rewrites existing feature (topological level required)
int Vect_reset_boxlist(struct boxlist *)
Reset boxlist structure.
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
struct boxlist * Vect_new_boxlist(int)
Creates and initializes a struct boxlist.
void Vect_destroy_boxlist(struct boxlist *)
Frees all memory associated with a struct boxlist, including the struct itself.
void Vect_destroy_list(struct ilist *)
Frees all memory associated with a struct ilist, 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.
struct ilist * Vect_new_list(void)
Creates and initializes a struct ilist.
off_t Vect_write_line(struct Map_info *, int, const struct line_pnts *, const struct line_cats *)
Writes a new feature.
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_reset_list(struct ilist *)
Reset ilist structure.
int Vect_append_point(struct line_pnts *, double, double, double)
Appends one point to the end of a line.
Definition line.c:146
int Vect_append_points(struct line_pnts *, const struct line_pnts *, int)
Appends points to the end of a line.
Definition line.c:333
#define GV_LINES
#define GV_FORWARD
Line direction indicator forward/backward.
double dig_distance2_point_to_line(double, double, double, double, double, double, double, double, double, int, double *, double *, double *, double *, int *)
int dig_boxlist_add(struct boxlist *, int, const struct bound_box *)
Header file for msvc/fcntl.c.
#define open
Definition fcntl.h:32
#define G_UNUSED
A macro for an attribute, if attached to a variable, indicating that the variable is not used.
Definition gis.h:43
#define _(str)
Definition glocale.h:10
int kdtree_rnn(struct kdtree *t, double *c, int **puid, int *skip)
Definition kdtree.c:749
struct kdtree * kdtree_create(char ndims, int *btol)
Definition kdtree.c:109
int kdtree_insert(struct kdtree *t, double *c, int uid, int dc)
Definition kdtree.c:177
int kdtree_knn(struct kdtree *t, double *c, int *uid, double *d, int k, int *skip)
Definition kdtree.c:510
void kdtree_destroy(struct kdtree *t)
Definition kdtree.c:165
int kdtree_dnn(struct kdtree *t, double *c, int **puid, double **pd, double maxdist, int *skip)
Definition kdtree.c:634
Vector map info.
RectReal * boundary
Definition rtree.h:52
Definition rtree.h:120
Bounding box.
Definition dig_structs.h:62
double W
West.
Definition dig_structs.h:78
List of bounding boxes with id.
List of integers.
Definition gis.h:712
k-d tree
Definition kdtree.h:78
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.
Definition manage.h:4
#define close
Definition unistd.h:8
void RTreeSetOverflow(struct RTree *t, char overflow)
Enable/disable R*-tree forced reinsertion (overflow)
struct RTree * RTreeCreateTree(int fd, off_t rootpos, int ndims)
Create new empty R*-Tree.
int RTreeInsertRect(struct RTree_Rect *r, int tid, struct RTree *t)
Insert an item into a R*-Tree.
void RTreeDestroyTree(struct RTree *t)
Destroy an R*-Tree.
int RTreeSearch(struct RTree *t, struct RTree_Rect *r, SearchHitCallback *shcb, void *cbarg)
Search an R*-Tree.
#define x