123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507 |
- /*!
- * \file lib/vector/Vlib/snap.c
- *
- * \brief Vector library - Clean vector map (snap lines)
- *
- * Higher level functions for reading/writing/manipulating vectors.
- *
- * (C) 2001-2009 by the GRASS Development Team
- *
- * This program is free software under the GNU General Public License
- * (>=v2). Read the file COPYING that comes with GRASS for details.
- *
- * \author Radim Blazek
- * \author update to GRASS 7 Markus Metz
- */
- #include <stdlib.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <math.h>
- #include <grass/vector.h>
- #include <grass/glocale.h>
- /* function prototypes */
- static int sort_new(const void *pa, const void *pb);
- /* Vertex */
- typedef struct
- {
- double x, y;
- int anchor; /* 0 - anchor, do not snap this point, that means snap others to this */
- /* >0 - index of anchor to which snap this point */
- /* -1 - init value */
- } XPNT;
- typedef struct
- {
- int anchor;
- double along;
- } NEW;
- /* This function is called by RTreeSearch() to add selected node/line/area/isle to the list */
- static int add_item(int id, const struct RTree_Rect *rect, struct ilist *list)
- {
- G_ilist_add(list, id);
- return 1;
- }
- /*!
- \brief Snap selected lines to existing vertex in threshold.
-
- Snap selected lines to existing vertices.
-
- \warning Lines are not necessarily snapped to nearest vertex, but to vertex in threshold!
-
- Lines showing how vertices were snapped may be optionally written to error map.
- Input map must be opened on level 2 for update at least on GV_BUILD_BASE.
-
- As mentioned above, lines are not necessarily snapped to nearest vertex! For example:
- <pre>
- |
- | 1 line 3 is snapped to line 1,
- | then line 2 is not snapped to common node at lines 1 and 3,
- because it is already outside of threshold
- ----------- 3
- |
- | 2
- |
- </pre>
-
- The algorithm selects anchor vertices and snaps non-anchor vertices
- to these anchors.
- The distance between anchor vertices is always > threshold.
- If there is more than one anchor vertex within threshold around a
- non-anchor vertex, this vertex is snapped to the nearest anchor
- vertex within threshold.
- \param Map input map where vertices will be snapped
- \param List_lines list of lines to snap
- \param thresh threshold in which snap vertices
- \param[out] Err vector map where lines representing snap are written or NULL
-
- \return void
- */
- void
- Vect_snap_lines_list(struct Map_info *Map, const struct ilist *List_lines,
- double thresh, struct Map_info *Err)
- {
- struct line_pnts *Points, *NPoints;
- struct line_cats *Cats;
- int line, ltype, line_idx;
- double thresh2;
- int point; /* index in points array */
- int nanchors, ntosnap; /* number of anchors and number of points to be snapped */
- int nsnapped, ncreated; /* number of snapped verices, number of new vertices (on segments) */
- int apoints, npoints, nvertices; /* number of allocated points, registered points, vertices */
- XPNT *XPnts; /* Array of points */
- NEW *New = NULL; /* Array of new points */
- int anew = 0, nnew; /* allocated new points , number of new points */
- struct ilist *List;
- int *Index = NULL; /* indexes of anchors for vertices */
- int aindex = 0; /* allocated Index */
- struct RTree *RTree;
- int rtreefd = -1;
- static struct RTree_Rect rect;
- static int rect_init = 0;
- if (!rect_init) {
- rect.boundary = G_malloc(6 * sizeof(RectReal));
- rect_init = 6;
- }
- if (List_lines->n_values < 1)
- return;
- Points = Vect_new_line_struct();
- NPoints = Vect_new_line_struct();
- Cats = Vect_new_cats_struct();
- List = Vect_new_list();
- if (getenv("GRASS_VECTOR_LOWMEM")) {
- char *filename = G_tempfile();
- rtreefd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
- remove(filename);
- }
- RTree = RTreeNewIndex(rtreefd, 0, 2);
- thresh2 = thresh * thresh;
- /* Go through all lines in vector, and add each point to structure of points */
- apoints = 0;
- point = 1; /* index starts from 1 ! */
- nvertices = 0;
- XPnts = NULL;
- G_verbose_message(_("Snap vertices Pass 1: select points"));
- for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
- int v;
- G_percent(line_idx, List_lines->n_values, 2);
- line = List_lines->value[line_idx];
- G_debug(3, "line = %d", line);
- if (!Vect_line_alive(Map, line))
- continue;
- ltype = Vect_read_line(Map, Points, Cats, line);
- for (v = 0; v < Points->n_points; v++) {
- G_debug(3, " vertex v = %d", v);
- nvertices++;
- /* Box */
- rect.boundary[0] = Points->x[v];
- rect.boundary[3] = Points->x[v];
- rect.boundary[1] = Points->y[v];
- rect.boundary[4] = Points->y[v];
- rect.boundary[2] = 0;
- rect.boundary[5] = 0;
- /* Already registered ? */
- Vect_reset_list(List);
- RTreeSearch(RTree, &rect, (void *)add_item, List);
- G_debug(3, "List : nvalues = %d", List->n_values);
- if (List->n_values == 0) { /* Not found */
- /* Add to tree and to structure */
- RTreeInsertRect(&rect, point, RTree);
- if ((point - 1) == apoints) {
- apoints += 10000;
- XPnts =
- (XPNT *) G_realloc(XPnts,
- (apoints + 1) * sizeof(XPNT));
- }
- XPnts[point].x = Points->x[v];
- XPnts[point].y = Points->y[v];
- XPnts[point].anchor = -1;
- point++;
- }
- }
- }
- G_percent(line_idx, List_lines->n_values, 2); /* finish it */
- npoints = point - 1;
- /* Go through all registered points and if not yet marked mark it as anchor and assign this anchor
- * to all not yet marked points in threshold */
- G_verbose_message(_("Snap vertices Pass 2: assign anchor vertices"));
- nanchors = ntosnap = 0;
- for (point = 1; point <= npoints; point++) {
- int i;
- G_percent(point, npoints, 2);
- G_debug(3, " point = %d", point);
- if (XPnts[point].anchor >= 0)
- continue;
- XPnts[point].anchor = 0; /* make it anchor */
- nanchors++;
- /* Find points in threshold */
- rect.boundary[0] = XPnts[point].x - thresh;
- rect.boundary[3] = XPnts[point].x + thresh;
- rect.boundary[1] = XPnts[point].y - thresh;
- rect.boundary[4] = XPnts[point].y + thresh;
- rect.boundary[2] = 0;
- rect.boundary[5] = 0;
- Vect_reset_list(List);
- RTreeSearch(RTree, &rect, (void *)add_item, List);
- G_debug(4, " %d points in threshold box", List->n_values);
- for (i = 0; i < List->n_values; i++) {
- int pointb;
- double dx, dy, dist2;
- pointb = List->value[i];
- if (pointb == point)
- continue;
- dx = XPnts[pointb].x - XPnts[point].x;
- dy = XPnts[pointb].y - XPnts[point].y;
- dist2 = dx * dx + dy * dy;
- if (dist2 > thresh2) /* outside threshold */
- continue;
-
- /* doesn't have an anchor yet */
- if (XPnts[pointb].anchor == -1) {
- XPnts[pointb].anchor = point;
- ntosnap++;
- }
- else if (XPnts[pointb].anchor > 0) { /* check distance to previously assigned anchor */
- double dist2_a;
- dx = XPnts[XPnts[pointb].anchor].x - XPnts[pointb].x;
- dy = XPnts[XPnts[pointb].anchor].y - XPnts[pointb].y;
- dist2_a = dx * dx + dy * dy;
- /* replace old anchor */
- if (dist2 < dist2_a) {
- XPnts[pointb].anchor = point;
- }
- }
- }
- }
- /* Go through all lines and:
- * 1) for all vertices: if not anchor snap it to its anchor
- * 2) for all segments: snap it to all anchors in threshold (except anchors of vertices of course) */
- nsnapped = ncreated = 0;
- G_verbose_message(_("Snap vertices Pass 3: snap to assigned points"));
- for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
- int v, spoint, anchor;
- int changed = 0;
- G_percent(line_idx, List_lines->n_values, 2);
- line = List_lines->value[line_idx];
- G_debug(3, "line = %d", line);
- if (!Vect_line_alive(Map, line))
- continue;
- ltype = Vect_read_line(Map, Points, Cats, line);
- if (Points->n_points >= aindex) {
- aindex = Points->n_points;
- Index = (int *)G_realloc(Index, aindex * sizeof(int));
- }
- /* Snap all vertices */
- for (v = 0; v < Points->n_points; v++) {
- /* Box */
- rect.boundary[0] = Points->x[v];
- rect.boundary[3] = Points->x[v];
- rect.boundary[1] = Points->y[v];
- rect.boundary[4] = Points->y[v];
- rect.boundary[2] = 0;
- rect.boundary[5] = 0;
- /* Find point ( should always find one point ) */
- Vect_reset_list(List);
- RTreeSearch(RTree, &rect, (void *)add_item, List);
- spoint = List->value[0];
- anchor = XPnts[spoint].anchor;
- if (anchor > 0) { /* to be snapped */
- Points->x[v] = XPnts[anchor].x;
- Points->y[v] = XPnts[anchor].y;
- nsnapped++;
- changed = 1;
- Index[v] = anchor; /* point on new location */
- }
- else {
- Index[v] = spoint; /* old point */
- }
- }
- /* New points */
- Vect_reset_line(NPoints);
- /* Snap all segments to anchors in threshold */
- for (v = 0; v < Points->n_points - 1; v++) {
- int i;
- double x1, x2, y1, y2, xmin, xmax, ymin, ymax;
- G_debug(3, " segment = %d end anchors : %d %d", v, Index[v],
- Index[v + 1]);
- x1 = Points->x[v];
- x2 = Points->x[v + 1];
- y1 = Points->y[v];
- y2 = Points->y[v + 1];
- Vect_append_point(NPoints, Points->x[v], Points->y[v],
- Points->z[v]);
- /* Box */
- if (x1 <= x2) {
- xmin = x1;
- xmax = x2;
- }
- else {
- xmin = x2;
- xmax = x1;
- }
- if (y1 <= y2) {
- ymin = y1;
- ymax = y2;
- }
- else {
- ymin = y2;
- ymax = y1;
- }
- rect.boundary[0] = xmin - thresh;
- rect.boundary[3] = xmax + thresh;
- rect.boundary[1] = ymin - thresh;
- rect.boundary[4] = ymax + thresh;
- rect.boundary[2] = 0;
- rect.boundary[5] = 0;
- /* Find points */
- Vect_reset_list(List);
- RTreeSearch(RTree, &rect, (void *)add_item, List);
- G_debug(3, " %d points in box", List->n_values);
- /* Snap to anchor in threshold different from end points */
- nnew = 0;
- for (i = 0; i < List->n_values; i++) {
- double dist2, along;
- spoint = List->value[i];
- G_debug(4, " spoint = %d anchor = %d", spoint,
- XPnts[spoint].anchor);
- if (spoint == Index[v] || spoint == Index[v + 1])
- continue; /* end point */
- if (XPnts[spoint].anchor > 0)
- continue; /* point is not anchor */
- /* Check the distance */
- dist2 =
- dig_distance2_point_to_line(XPnts[spoint].x,
- XPnts[spoint].y, 0, x1, y1, 0,
- x2, y2, 0, 0, NULL, NULL,
- NULL, &along, NULL);
- G_debug(4, " distance = %lf", sqrt(dist2));
- if (dist2 <= thresh2) {
- G_debug(4, " anchor in thresh, along = %lf", along);
- if (nnew == anew) {
- anew += 100;
- New = (NEW *) G_realloc(New, anew * sizeof(NEW));
- }
- New[nnew].anchor = spoint;
- New[nnew].along = along;
- nnew++;
- }
- }
- G_debug(3, " nnew = %d", nnew);
- /* insert new vertices */
- if (nnew > 0) {
- /* sort by distance along the segment */
- qsort(New, sizeof(char) * nnew, sizeof(NEW), sort_new);
- for (i = 0; i < nnew; i++) {
- anchor = New[i].anchor;
- /* Vect_line_insert_point ( Points, ++v, XPnts[anchor].x, XPnts[anchor].y, 0); */
- Vect_append_point(NPoints, XPnts[anchor].x,
- XPnts[anchor].y, 0);
- ncreated++;
- }
- changed = 1;
- }
- }
- /* append end point */
- v = Points->n_points - 1;
- Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
- if (changed) { /* rewrite the line */
- Vect_line_prune(NPoints); /* remove duplicates */
- if (NPoints->n_points > 1 || ltype & GV_LINES) {
- Vect_rewrite_line(Map, line, ltype, NPoints, Cats);
- }
- else {
- Vect_delete_line(Map, line);
- }
- if (Err) {
- Vect_write_line(Err, ltype, Points, Cats);
- }
- }
- } /* for each line */
- G_percent(line_idx, List_lines->n_values, 2); /* finish it */
- Vect_destroy_line_struct(Points);
- Vect_destroy_line_struct(NPoints);
- Vect_destroy_cats_struct(Cats);
- G_free(XPnts);
- G_free(Index);
- G_free(New);
- RTreeFreeIndex(RTree);
- if (rtreefd >= 0)
- close(rtreefd);
- G_verbose_message(_("Snapped vertices: %d"), nsnapped);
- G_verbose_message(_("New vertices: %d"), ncreated);
- }
- /* for qsort */
- static int sort_new(const void *pa, const void *pb)
- {
- NEW *p1 = (NEW *) pa;
- NEW *p2 = (NEW *) pb;
- if (p1->along < p2->along)
- return -1;
- if (p1->along > p2->along)
- return 1;
- return 1;
- }
- /*!
- * \brief Snap lines in vector map to existing vertex in threshold.
- *
- * For details see Vect_snap_lines_list()
- *
- * \param[in] Map input map where vertices will be snapped
- * \param[in] type type of lines to snap
- * \param[in] thresh threshold in which snap vertices
- * \param[out] Err vector map where lines representing snap are written or NULL
- *
- * \return void
- */
- void
- Vect_snap_lines(struct Map_info *Map, int type, double thresh,
- struct Map_info *Err)
- {
- int line, nlines, ltype;
- struct ilist *List;
- List = Vect_new_list();
- nlines = Vect_get_num_lines(Map);
- for (line = 1; line <= nlines; line++) {
- G_debug(3, "line = %d", line);
- if (!Vect_line_alive(Map, line))
- continue;
- ltype = Vect_read_line(Map, NULL, NULL, line);
- if (!(ltype & type))
- continue;
- /* no need to check for duplicates:
- * use G_ilist_add() instead of Vect_list_append() */
- G_ilist_add(List, line);
- }
- Vect_snap_lines_list(Map, List, thresh, Err);
- Vect_destroy_list(List);
- return;
- }
|