snap.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /*!
  2. * \file 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. #include <grass/config.h>
  17. #include <stdlib.h>
  18. #include <math.h>
  19. #include <grass/gis.h>
  20. #include <grass/Vect.h>
  21. #include <grass/glocale.h>
  22. #include <grass/vect/rbtree.h>
  23. /* function prototypes */
  24. static int sort_new(const void *pa, const void *pb);
  25. /* Vertex */
  26. typedef struct
  27. {
  28. double x, y;
  29. double anchor_x, anchor_y;
  30. int anchor; /* 0 - anchor, do not snap this point, that means snap others to this */
  31. /* >0 - index of anchor to which snap this point */
  32. /* -1 - init value */
  33. } XPNT;
  34. typedef struct
  35. {
  36. double anchor_x, anchor_y;
  37. int anchor;
  38. double along;
  39. } NEW;
  40. /* This function is called by RTreeSearch() to add selected node/line/area/isle to thelist */
  41. int add_item(int id, struct ilist *list)
  42. {
  43. dig_list_add(list, id);
  44. return 1;
  45. }
  46. /* function used by binary tree to compare items */
  47. int compare_snappnts(const void *Xpnta, const void *Xpntb)
  48. {
  49. XPNT *a, *b;
  50. a = (XPNT *)Xpnta;
  51. b = (XPNT *)Xpntb;
  52. if (a->x > b->x)
  53. return 1;
  54. else if (a->x < b->x)
  55. return -1;
  56. else {
  57. if (a->y > b->y)
  58. return 1;
  59. else if (a->y < b->y)
  60. return -1;
  61. else
  62. return 0;
  63. }
  64. G_warning("Break polygons: Bug in binary tree!");
  65. return 1;
  66. }
  67. /*!
  68. \brief Snap selected lines to existing vertex in threshold.
  69. Snap selected lines to existing vertices.
  70. \warning Lines are not necessarily snapped to nearest vertex, but to vertex in threshold!
  71. Lines showing how vertices were snapped may be optionally written to error map.
  72. Input map must be opened on level 2 for update at least on GV_BUILD_BASE.
  73. As mentioned above, lines are not necessarily snapped to nearest vertex! For example:
  74. <pre>
  75. |
  76. | 1 line 3 is snapped to line 1,
  77. | then line 2 is not snapped to common node at lines 1 and 3,
  78. because it is already outside of threshold
  79. ----------- 3
  80. |
  81. | 2
  82. |
  83. </pre>
  84. The algorithm selects anchor vertices and snaps non-anchor vertices
  85. to these anchors.
  86. The distance between anchor vertices is always > threshold.
  87. If there is more than one anchor vertex within threshold around a
  88. non-anchor vertex, this vertex is snapped to the nearest anchor
  89. vertex within threshold.
  90. \param Map input map where vertices will be snapped
  91. \param List_lines list of lines to snap
  92. \param thresh threshold in which snap vertices
  93. \param[out] Err vector map where lines representing snap are written or NULL
  94. \return void
  95. */
  96. void
  97. Vect_snap_lines_list(struct Map_info *Map, const struct ilist *List_lines,
  98. double thresh, struct Map_info *Err)
  99. {
  100. struct line_pnts *Points, *NPoints;
  101. struct line_cats *Cats;
  102. int line, ltype, line_idx;
  103. double thresh2;
  104. double xmin, xmax, ymin, ymax;
  105. struct RB_TREE *RBTree;
  106. struct RB_TRAV RBTrav1, RBTrav2;
  107. int point; /* index in points array */
  108. int nanchors, ntosnap; /* number of anchors and number of points to be snapped */
  109. int nsnapped, ncreated; /* number of snapped verices, number of new vertices (on segments) */
  110. int apoints, npoints, nvertices; /* number of allocated points, registered points, vertices */
  111. XPNT *XPnt_found, *XPnt_found2, XPnt_search; /* snap points */
  112. NEW *New = NULL; /* Array of new points */
  113. int anew = 0, nnew; /* allocated new points , number of new points */
  114. if (List_lines->n_values < 1)
  115. return;
  116. Points = Vect_new_line_struct();
  117. NPoints = Vect_new_line_struct();
  118. Cats = Vect_new_cats_struct();
  119. RBTree = rbtree_create(compare_snappnts, sizeof(XPNT));
  120. thresh2 = thresh * thresh;
  121. /* Go through all lines in vector, and add each point to search tree
  122. * points are kept sorted in search tree first by x, then by y */
  123. apoints = 0;
  124. point = 1; /* index starts from 1 ! */
  125. nvertices = 0;
  126. G_verbose_message(_("Snap vertices Pass 1: select points"));
  127. for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
  128. int v;
  129. G_percent(line_idx, List_lines->n_values, 2);
  130. line = List_lines->value[line_idx];
  131. G_debug(3, "line = %d", line);
  132. if (!Vect_line_alive(Map, line))
  133. continue;
  134. ltype = Vect_read_line(Map, Points, Cats, line);
  135. for (v = 0; v < Points->n_points; v++) {
  136. G_debug(3, " vertex v = %d", v);
  137. nvertices++;
  138. XPnt_search.x = Points->x[v];
  139. XPnt_search.y = Points->y[v];
  140. /* Already registered ? */
  141. XPnt_found = rbtree_find(RBTree, &XPnt_search);
  142. if (XPnt_found == NULL) { /* Not found */
  143. /* Add to tree */
  144. XPnt_search.anchor = -1;
  145. XPnt_search.anchor_x = Points->x[v];
  146. XPnt_search.anchor_y = Points->y[v];
  147. rbtree_insert(RBTree, &XPnt_search);
  148. point++;
  149. }
  150. }
  151. } /* end of points selection */
  152. G_percent(line_idx, List_lines->n_values, 2); /* finish it */
  153. npoints = point - 1;
  154. /* Go through all registered points and if not yet marked,
  155. * mark it as anchor and assign this anchor to all not yet marked
  156. * points within threshold.
  157. * Update anchor for marked points if new anchor is closer. */
  158. G_verbose_message(_("Snap vertices Pass 2: assign anchor vertices"));
  159. nanchors = ntosnap = 0;
  160. rbtree_init_trav(&RBTrav1, RBTree);
  161. rbtree_init_trav(&RBTrav2, RBTree);
  162. point = 1;
  163. /* XPnts in the old version were not sorted, this causes subtle differences */
  164. while ((XPnt_found = rbtree_traverse(&RBTrav1)) != NULL) {
  165. double dx, dy, dist2;
  166. G_percent(point, npoints, 2);
  167. G_debug(3, " point = %d", point);
  168. point++;
  169. if (XPnt_found->anchor >= 0)
  170. continue;
  171. XPnt_found->anchor = 0; /* make it anchor */
  172. nanchors++;
  173. /* Find points in threshold */
  174. xmin = XPnt_found->x - thresh;
  175. xmax = XPnt_found->x + thresh;
  176. ymin = XPnt_found->y - thresh;
  177. ymax = XPnt_found->y + thresh;
  178. XPnt_search.x = xmin;
  179. XPnt_search.y = ymin;
  180. /* traverse search tree from start point onwards */
  181. rbtree_init_trav(&RBTrav2, RBTree);
  182. while ((XPnt_found2 = rbtree_traverse_start(&RBTrav2, &XPnt_search)) != NULL) {
  183. if (XPnt_found2->x > xmax)
  184. break; /* outside x search limit */
  185. /* not an anchor, and within y search limits */
  186. if (XPnt_found2->anchor != 0 &&
  187. XPnt_found2->y >= ymin && XPnt_found2->y <= ymax) {
  188. dx = XPnt_found2->x - XPnt_found->x;
  189. dy = XPnt_found2->y - XPnt_found->y;
  190. if (dx == 0 && dy == 0) /* XPnt_found2 == XPnt_found */
  191. continue;
  192. dist2 = dx * dx + dy * dy;
  193. if (dist2 > thresh2) /* outside threshold */
  194. continue;
  195. /* doesn't have an anchor yet */
  196. if (XPnt_found2->anchor == -1) {
  197. XPnt_found2->anchor = 1;
  198. XPnt_found2->anchor_x = XPnt_found->x;
  199. XPnt_found2->anchor_y = XPnt_found->y;
  200. ntosnap++;
  201. }
  202. else { /* check distance to previously assigned anchor */
  203. double dist2_a;
  204. dx = XPnt_found2->anchor_x - XPnt_found2->x;
  205. dy = XPnt_found2->anchor_y - XPnt_found2->y;
  206. dist2_a = dx * dx + dy * dy;
  207. /* replace old anchor */
  208. if (dist2 < dist2_a) {
  209. XPnt_found2->anchor_x = XPnt_found->x;
  210. XPnt_found2->anchor_y = XPnt_found->y;
  211. }
  212. }
  213. }
  214. }
  215. } /* end of anchor assignment */
  216. /* Go through all lines and:
  217. * 1) for all vertices: if not anchor snap it to its anchor
  218. * 2) for all segments: snap it to all anchors in threshold (except anchors of vertices of course) */
  219. nsnapped = ncreated = 0;
  220. G_verbose_message(_("Snap vertices Pass 3: snap to assigned points"));
  221. for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
  222. int v;
  223. int changed = 0;
  224. G_percent(line_idx, List_lines->n_values, 2);
  225. line = List_lines->value[line_idx];
  226. G_debug(3, "line = %d", line);
  227. if (!Vect_line_alive(Map, line))
  228. continue;
  229. ltype = Vect_read_line(Map, Points, Cats, line);
  230. /* Snap all vertices */
  231. G_debug(2, "snap vertices");
  232. for (v = 0; v < Points->n_points; v++) {
  233. /* Find point ( should always find one point ) */
  234. XPnt_search.x = Points->x[v];
  235. XPnt_search.y = Points->y[v];
  236. XPnt_found = rbtree_find(RBTree, &XPnt_search);
  237. if (XPnt_found == NULL)
  238. G_fatal_error("Snap lines: could not find vertex");
  239. if (XPnt_found->anchor > 0) { /* to be snapped */
  240. Points->x[v] = XPnt_found->anchor_x;
  241. Points->y[v] = XPnt_found->anchor_y;
  242. nsnapped++;
  243. changed = 1;
  244. }
  245. }
  246. /* New points */
  247. Vect_reset_line(NPoints);
  248. /* Snap all segments to anchors in threshold */
  249. G_debug(2, "snap segments");
  250. for (v = 0; v < Points->n_points - 1; v++) {
  251. int i;
  252. double x1, x2, y1, y2;
  253. double dist2, along;
  254. int status;
  255. x1 = Points->x[v];
  256. x2 = Points->x[v + 1];
  257. y1 = Points->y[v];
  258. y2 = Points->y[v + 1];
  259. Vect_append_point(NPoints, Points->x[v], Points->y[v],
  260. Points->z[v]);
  261. /* Search limits */
  262. xmin = (x1 < x2 ? x1 : x2) - thresh;
  263. xmax = (x1 > x2 ? x1 : x2) + thresh;
  264. ymin = (y1 < y2 ? y1 : y2) - thresh;
  265. ymax = (y1 > y2 ? y1 : y2) + thresh;
  266. XPnt_search.x = xmin;
  267. XPnt_search.y = ymin;
  268. /* Find points within search limits */
  269. nnew = 0;
  270. rbtree_init_trav(&RBTrav2, RBTree);
  271. G_debug(3, "snap segment");
  272. while ((XPnt_found2 = rbtree_traverse_start(&RBTrav2, &XPnt_search)) != NULL) {
  273. if (XPnt_found2->x > xmax)
  274. break; /* outside x search limit */
  275. /* found point must be within y search limits */
  276. if (XPnt_found2->y < ymin || XPnt_found2->y > ymax)
  277. continue;
  278. /* found point must be anchor */
  279. if (XPnt_found2->anchor > 0)
  280. continue; /* point is not anchor */
  281. /* found point must not be end point */
  282. if ((XPnt_found2->x == x1 && XPnt_found2->y == y1) ||
  283. (XPnt_found2->x == x2 && XPnt_found2->y == y2))
  284. continue; /* end point */
  285. /* Check the distance */
  286. dist2 =
  287. dig_distance2_point_to_line(XPnt_found2->x,
  288. XPnt_found2->y, 0, x1, y1, 0,
  289. x2, y2, 0, 0, NULL, NULL,
  290. NULL, &along, &status);
  291. G_debug(4, " distance = %lf", sqrt(dist2));
  292. /* status == 0 if point is w/in segment space
  293. * avoids messy lines and boundaries */
  294. if (status == 0 && dist2 <= thresh2) {
  295. G_debug(4, " anchor in thresh, along = %lf", along);
  296. if (nnew == anew) {
  297. anew += 100;
  298. New = (NEW *) G_realloc(New, anew * sizeof(NEW));
  299. }
  300. New[nnew].anchor_x = XPnt_found2->x;
  301. New[nnew].anchor_y = XPnt_found2->y;
  302. New[nnew].along = along;
  303. nnew++;
  304. }
  305. }
  306. G_debug(3, " nnew = %d", nnew);
  307. /* insert new vertices */
  308. if (nnew > 0) {
  309. /* sort by distance along the segment */
  310. qsort(New, sizeof(char) * nnew, sizeof(NEW), sort_new);
  311. for (i = 0; i < nnew; i++) {
  312. Vect_append_point(NPoints, New[i].anchor_x,
  313. New[i].anchor_y, 0);
  314. ncreated++;
  315. }
  316. changed = 1;
  317. }
  318. }
  319. /* append end point */
  320. v = Points->n_points - 1;
  321. Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
  322. if (changed) { /* rewrite the line */
  323. Vect_line_prune(NPoints); /* remove duplicates */
  324. if (NPoints->n_points > 1 || ltype & GV_LINES) {
  325. Vect_rewrite_line(Map, line, ltype, NPoints, Cats);
  326. }
  327. else {
  328. Vect_delete_line(Map, line);
  329. }
  330. if (Err) {
  331. Vect_write_line(Err, ltype, Points, Cats);
  332. }
  333. }
  334. } /* for each line */
  335. G_percent(line_idx, List_lines->n_values, 2); /* finish it */
  336. Vect_destroy_line_struct(Points);
  337. Vect_destroy_line_struct(NPoints);
  338. Vect_destroy_cats_struct(Cats);
  339. G_free(New);
  340. rbtree_destroy(RBTree);
  341. G_verbose_message(_("Snapped vertices: %d"), nsnapped);
  342. G_verbose_message(_("New vertices: %d"), ncreated);
  343. }
  344. /* for qsort */
  345. static int sort_new(const void *pa, const void *pb)
  346. {
  347. NEW *p1 = (NEW *) pa;
  348. NEW *p2 = (NEW *) pb;
  349. if (p1->along < p2->along)
  350. return -1;
  351. if (p1->along > p2->along)
  352. return 1;
  353. return 1;
  354. }
  355. /*!
  356. * \brief Snap lines in vector map to existing vertex in threshold.
  357. *
  358. * For details see Vect_snap_lines_list()
  359. *
  360. * \param[in] Map input map where vertices will be snapped
  361. * \param[in] type type of lines to snap
  362. * \param[in] thresh threshold in which snap vertices
  363. * \param[out] Err vector map where lines representing snap are written or NULL
  364. *
  365. * \return void
  366. */
  367. void
  368. Vect_snap_lines(struct Map_info *Map, int type, double thresh,
  369. struct Map_info *Err)
  370. {
  371. int line, nlines, ltype;
  372. struct ilist *List;
  373. List = Vect_new_list();
  374. nlines = Vect_get_num_lines(Map);
  375. for (line = 1; line <= nlines; line++) {
  376. G_debug(3, "line = %d", line);
  377. if (!Vect_line_alive(Map, line))
  378. continue;
  379. ltype = Vect_read_line(Map, NULL, NULL, line);
  380. if (!(ltype & type))
  381. continue;
  382. Vect_list_append(List, line);
  383. }
  384. Vect_snap_lines_list(Map, List, thresh, Err);
  385. Vect_destroy_list(List);
  386. return;
  387. }