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