|
@@ -21,6 +21,7 @@
|
|
|
#include <math.h>
|
|
|
#include <grass/vector.h>
|
|
|
#include <grass/glocale.h>
|
|
|
+#include <grass/kdtree.h>
|
|
|
|
|
|
/* translate segment to box and back */
|
|
|
#define X1W 0x01 /* x1 is West, x2 East */
|
|
@@ -121,6 +122,14 @@ static int add_item_box(int id, const struct RTree_Rect *rect, void *list)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+Vect_snap_lines_list_rtree(struct Map_info *, const struct ilist *,
|
|
|
+ double, struct Map_info *);
|
|
|
+
|
|
|
+static void
|
|
|
+Vect_snap_lines_list_kdtree(struct Map_info *, const struct ilist *,
|
|
|
+ double, struct Map_info *);
|
|
|
+
|
|
|
/*!
|
|
|
\brief Snap selected lines to existing vertex in threshold.
|
|
|
|
|
@@ -161,6 +170,366 @@ void
|
|
|
Vect_snap_lines_list(struct Map_info *Map, const struct ilist *List_lines,
|
|
|
double thresh, struct Map_info *Err)
|
|
|
{
|
|
|
+ if (getenv("GRASS_VECTOR_LOWMEM"))
|
|
|
+ Vect_snap_lines_list_rtree(Map, List_lines, thresh, Err);
|
|
|
+ else
|
|
|
+ Vect_snap_lines_list_rtree(Map, List_lines, thresh, Err);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+Vect_snap_lines_list_kdtree(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 kdtree *KDTree;
|
|
|
+ double c[2];
|
|
|
+ double *kdd;
|
|
|
+ int *kduid, kd_found;
|
|
|
+
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+ KDTree = kdtree_create(2, NULL);
|
|
|
+
|
|
|
+ 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_important_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++;
|
|
|
+
|
|
|
+ /* coords */
|
|
|
+ c[0] = Points->x[v];
|
|
|
+ c[1] = Points->y[v];
|
|
|
+
|
|
|
+ if (kdtree_insert(KDTree, c, point, 0)) {
|
|
|
+ /* Add to structure */
|
|
|
+ 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 */
|
|
|
+
|
|
|
+ G_debug(0, "KD Tree depth: %d", (int)KDTree->root->depth);
|
|
|
+ kdtree_optimize(KDTree, 2);
|
|
|
+ G_debug(0, "KD Tree depth: %d", (int)KDTree->root->depth);
|
|
|
+
|
|
|
+ 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_important_message(_("Snap vertices Pass 2: assign anchor vertices"));
|
|
|
+
|
|
|
+ nanchors = ntosnap = 0;
|
|
|
+ for (point = 1; point <= npoints; point++) {
|
|
|
+ int i;
|
|
|
+
|
|
|
+ G_percent(point, npoints, 4);
|
|
|
+
|
|
|
+ G_debug(3, " point = %d", point);
|
|
|
+
|
|
|
+ if (XPnts[point].anchor >= 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ XPnts[point].anchor = 0; /* make it anchor */
|
|
|
+ nanchors++;
|
|
|
+
|
|
|
+ /* Find points in threshold */
|
|
|
+ c[0] = XPnts[point].x;
|
|
|
+ c[1] = XPnts[point].y;
|
|
|
+
|
|
|
+ Vect_reset_list(List);
|
|
|
+ kd_found = kdtree_dnn(KDTree, c, &kduid, &kdd, thresh, &point);
|
|
|
+ G_debug(4, " %d points in threshold box", kd_found);
|
|
|
+
|
|
|
+ for (i = 0; i < kd_found; i++) {
|
|
|
+ int pointb;
|
|
|
+ double dx, dy, dist2;
|
|
|
+
|
|
|
+ pointb = kduid[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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (kd_found) {
|
|
|
+ G_free(kdd);
|
|
|
+ G_free(kduid);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 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_important_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;
|
|
|
+ double kddist;
|
|
|
+
|
|
|
+ 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 */
|
|
|
+ c[0] = Points->x[v];
|
|
|
+ c[1] = Points->y[v];
|
|
|
+
|
|
|
+ /* Find point ( should always find one point ) */
|
|
|
+ Vect_reset_list(List);
|
|
|
+
|
|
|
+ spoint = -1;
|
|
|
+ kdtree_knn(KDTree, c, &spoint, &kddist, 1, NULL);
|
|
|
+ if (spoint == -1)
|
|
|
+ G_fatal_error("Point not in KD Tree");
|
|
|
+
|
|
|
+ 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;
|
|
|
+ double dx, dy;
|
|
|
+ double kdthresh;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ c[0] = (xmin + xmax) / 2;
|
|
|
+ c[1] = (ymin + ymax) / 2;
|
|
|
+
|
|
|
+ dx = xmax - xmin;
|
|
|
+ dy = ymax - ymin;
|
|
|
+ kdthresh = sqrt(dx * dx + dy * dy) + thresh;
|
|
|
+
|
|
|
+ /* Find points */
|
|
|
+ Vect_reset_list(List);
|
|
|
+ kd_found = kdtree_dnn(KDTree, c, &kduid, &kdd, kdthresh, NULL);
|
|
|
+
|
|
|
+ G_debug(3, " %d points in box", kd_found);
|
|
|
+
|
|
|
+ /* Snap to anchor in threshold different from end points */
|
|
|
+ nnew = 0;
|
|
|
+ for (i = 0; i < kd_found; i++) {
|
|
|
+ double dist2, along;
|
|
|
+
|
|
|
+ spoint = kduid[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++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (kd_found) {
|
|
|
+ G_free(kduid);
|
|
|
+ G_free(kdd);
|
|
|
+ }
|
|
|
+ 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);
|
|
|
+ kdtree_destroy(KDTree);
|
|
|
+
|
|
|
+ G_verbose_message(_("Snapped vertices: %d"), nsnapped);
|
|
|
+ G_verbose_message(_("New vertices: %d"), ncreated);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+Vect_snap_lines_list_rtree(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;
|
|
@@ -270,7 +639,7 @@ Vect_snap_lines_list(struct Map_info *Map, const struct ilist *List_lines,
|
|
|
for (point = 1; point <= npoints; point++) {
|
|
|
int i;
|
|
|
|
|
|
- G_percent(point, npoints, 2);
|
|
|
+ G_percent(point, npoints, 4);
|
|
|
|
|
|
G_debug(3, " point = %d", point);
|
|
|
|
|
@@ -581,7 +950,7 @@ Vect_snap_lines(struct Map_info *Map, int type, double thresh,
|
|
|
\param[in,out] Points line points to snap
|
|
|
\param[in] thresh threshold in which to snap vertices
|
|
|
\param[in] with_z 2D or 3D snapping
|
|
|
- \param[in,out] nsnapped number of snapped verices
|
|
|
+ \param[in,out] nsnapped number of snapped vertices
|
|
|
\param[in,out] ncreated number of new vertices (on segments)
|
|
|
|
|
|
\return 1 if line was changed, otherwise 0
|