snap.c 13 KB

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