GRASS Programmer's Manual  6.5.svn(2012)-r51648
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
Vlib/snap.c
Go to the documentation of this file.
00001 
00020 #include <math.h>
00021 #include <grass/gis.h>
00022 #include <grass/Vect.h>
00023 #include <grass/glocale.h>
00024 
00025 /* function prototypes */
00026 static int sort_new(const void *pa, const void *pb);
00027 
00028 /* Vertex */
00029 typedef struct
00030 {
00031     double x, y;
00032     int anchor;                 /* 0 - anchor, do not snap this point, that means snap others to this */
00033     /* >0  - index of anchor to which snap this point */
00034     /* -1  - init value */
00035 } XPNT;
00036 
00037 typedef struct
00038 {
00039     int anchor;
00040     double along;
00041 } NEW;
00042 
00043 /* This function is called by  RTreeSearch() to add selected node/line/area/isle to thelist */
00044 int add_item(int id, struct ilist *list)
00045 {
00046     dig_list_add(list, id);
00047     return 1;
00048 }
00049 
00068 /* As mentioned above, lines are not necessarily snapped to nearest vertex! For example:
00069    |                    
00070    | 1         line 3 is snapped to line 1,
00071    |           then line 2 is not snapped to common node at lines 1 and 3,
00072    because it is already outside of threshold
00073    ----------- 3   
00074 
00075    |
00076    | 2
00077    |    
00078  */
00079 void
00080 Vect_snap_lines_list(struct Map_info *Map, struct ilist *List_lines,
00081                      double thresh, struct Map_info *Err)
00082 {
00083     struct line_pnts *Points, *NPoints;
00084     struct line_cats *Cats;
00085     int line, ltype, line_idx;
00086     double thresh2;
00087 
00088     struct Node *RTree;
00089     int point;                  /* index in points array */
00090     int nanchors, ntosnap;      /* number of anchors and number of points to be snapped */
00091     int nsnapped, ncreated;     /* number of snapped verices, number of new vertices (on segments) */
00092     int apoints, npoints, nvertices;    /* number of allocated points, registered points, vertices */
00093     XPNT *XPnts;                /* Array of points */
00094     NEW *New = NULL;            /* Array of new points */
00095     int anew = 0, nnew;         /* allocated new points , number of new points */
00096     struct Rect rect;
00097     struct ilist *List;
00098     int *Index = NULL;          /* indexes of anchors for vertices */
00099     int aindex = 0;             /* allocated Index */
00100 
00101     if (List_lines->n_values < 1)
00102         return;
00103 
00104     Points = Vect_new_line_struct();
00105     NPoints = Vect_new_line_struct();
00106     Cats = Vect_new_cats_struct();
00107     List = Vect_new_list();
00108     RTree = RTreeNewIndex();
00109 
00110     thresh2 = thresh * thresh;
00111 
00112     /* Go through all lines in vector, and add each point to structure of points */
00113     apoints = 0;
00114     point = 1;                  /* index starts from 1 ! */
00115     nvertices = 0;
00116     XPnts = NULL;
00117 
00118     G_verbose_message(_("Snap vertices Pass 1: select points"));
00119     for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
00120         int v;
00121 
00122         G_percent(line_idx, List_lines->n_values, 2);
00123 
00124         line = List_lines->value[line_idx];
00125 
00126         G_debug(3, "line =  %d", line);
00127         if (!Vect_line_alive(Map, line))
00128             continue;
00129 
00130         ltype = Vect_read_line(Map, Points, Cats, line);
00131 
00132         for (v = 0; v < Points->n_points; v++) {
00133             G_debug(3, "  vertex v = %d", v);
00134             nvertices++;
00135 
00136             /* Box */
00137             rect.boundary[0] = Points->x[v];
00138             rect.boundary[3] = Points->x[v];
00139             rect.boundary[1] = Points->y[v];
00140             rect.boundary[4] = Points->y[v];
00141             rect.boundary[2] = 0;
00142             rect.boundary[5] = 0;
00143 
00144             /* Already registered ? */
00145             Vect_reset_list(List);
00146             RTreeSearch(RTree, &rect, (void *)add_item, List);
00147             G_debug(3, "List : nvalues =  %d", List->n_values);
00148 
00149             if (List->n_values == 0) {  /* Not found */
00150                 /* Add to tree and to structure */
00151                 RTreeInsertRect(&rect, point, &RTree, 0);
00152                 if ((point - 1) == apoints) {
00153                     apoints += 10000;
00154                     XPnts =
00155                         (XPNT *) G_realloc(XPnts,
00156                                            (apoints + 1) * sizeof(XPNT));
00157                 }
00158                 XPnts[point].x = Points->x[v];
00159                 XPnts[point].y = Points->y[v];
00160                 XPnts[point].anchor = -1;
00161                 point++;
00162             }
00163         }
00164     }
00165     G_percent(line_idx, List_lines->n_values, 2); /* finish it */
00166 
00167     npoints = point - 1;
00168 
00169     /* Go through all registered points and if not yet marked mark it as anchor and assign this anchor
00170      * to all not yet marked points in threshold */
00171 
00172     G_verbose_message(_("Snap vertices Pass 2: assign anchor vertices"));
00173 
00174     nanchors = ntosnap = 0;
00175     for (point = 1; point <= npoints; point++) {
00176         int i;
00177 
00178         G_percent(point, npoints, 2);
00179 
00180         G_debug(3, "  point = %d", point);
00181 
00182         if (XPnts[point].anchor >= 0)
00183             continue;
00184 
00185         XPnts[point].anchor = 0;        /* make it anchor */
00186         nanchors++;
00187 
00188         /* Find points in threshold */
00189         rect.boundary[0] = XPnts[point].x - thresh;
00190         rect.boundary[3] = XPnts[point].x + thresh;
00191         rect.boundary[1] = XPnts[point].y - thresh;
00192         rect.boundary[4] = XPnts[point].y + thresh;
00193         rect.boundary[2] = 0;
00194         rect.boundary[5] = 0;
00195 
00196         Vect_reset_list(List);
00197         RTreeSearch(RTree, &rect, (void *)add_item, List);
00198         G_debug(4, "  %d points in threshold box", List->n_values);
00199 
00200         for (i = 0; i < List->n_values; i++) {
00201             int pointb;
00202             double dx, dy, dist2;
00203 
00204             pointb = List->value[i];
00205             if (pointb == point)
00206                 continue;
00207 
00208             dx = XPnts[pointb].x - XPnts[point].x;
00209             dy = XPnts[pointb].y - XPnts[point].y;
00210             dist2 = dx * dx + dy * dy;
00211 
00212             if (dist2 > thresh2) /* outside threshold */
00213                 continue;
00214                 
00215             /* doesn't have an anchor yet */
00216             if (XPnts[pointb].anchor == -1) {
00217                 XPnts[pointb].anchor = point;
00218                 ntosnap++;
00219             }
00220             else if (XPnts[pointb].anchor > 0) {   /* check distance to previously assigned anchor */
00221                 double dist2_a;
00222 
00223                 dx = XPnts[XPnts[pointb].anchor].x - XPnts[pointb].x;
00224                 dy = XPnts[XPnts[pointb].anchor].y - XPnts[pointb].y;
00225                 dist2_a = dx * dx + dy * dy;
00226 
00227                 /* replace old anchor */
00228                 if (dist2 < dist2_a) {
00229                     XPnts[pointb].anchor = point;
00230                 }
00231             }
00232         }
00233     }
00234 
00235     /* Go through all lines and: 
00236      *   1) for all vertices: if not anchor snap it to its anchor
00237      *   2) for all segments: snap it to all anchors in threshold (except anchors of vertices of course) */
00238 
00239     nsnapped = ncreated = 0;
00240 
00241     G_verbose_message(_("Snap vertices Pass 3: snap to assigned points"));
00242 
00243     for (line_idx = 0; line_idx < List_lines->n_values; line_idx++) {
00244         int v, spoint, anchor;
00245         int changed = 0;
00246 
00247         G_percent(line_idx, List_lines->n_values, 2);
00248 
00249         line = List_lines->value[line_idx];
00250 
00251         G_debug(3, "line =  %d", line);
00252         if (!Vect_line_alive(Map, line))
00253             continue;
00254 
00255         ltype = Vect_read_line(Map, Points, Cats, line);
00256 
00257         if (Points->n_points >= aindex) {
00258             aindex = Points->n_points;
00259             Index = (int *)G_realloc(Index, aindex * sizeof(int));
00260         }
00261 
00262         /* Snap all vertices */
00263         for (v = 0; v < Points->n_points; v++) {
00264             /* Box */
00265             rect.boundary[0] = Points->x[v];
00266             rect.boundary[3] = Points->x[v];
00267             rect.boundary[1] = Points->y[v];
00268             rect.boundary[4] = Points->y[v];
00269             rect.boundary[2] = 0;
00270             rect.boundary[5] = 0;
00271 
00272             /* Find point ( should always find one point ) */
00273             Vect_reset_list(List);
00274 
00275             RTreeSearch(RTree, &rect, (void *)add_item, List);
00276 
00277             spoint = List->value[0];
00278             anchor = XPnts[spoint].anchor;
00279 
00280             if (anchor > 0) {   /* to be snapped */
00281                 Points->x[v] = XPnts[anchor].x;
00282                 Points->y[v] = XPnts[anchor].y;
00283                 nsnapped++;
00284                 changed = 1;
00285                 Index[v] = anchor;      /* point on new location */
00286             }
00287             else {
00288                 Index[v] = spoint;      /* old point */
00289             }
00290         }
00291 
00292         /* New points */
00293         Vect_reset_line(NPoints);
00294 
00295         /* Snap all segments to anchors in threshold */
00296         for (v = 0; v < Points->n_points - 1; v++) {
00297             int i;
00298             double x1, x2, y1, y2, xmin, xmax, ymin, ymax;
00299 
00300             G_debug(3, "  segment = %d end anchors : %d  %d", v, Index[v],
00301                     Index[v + 1]);
00302 
00303             x1 = Points->x[v];
00304             x2 = Points->x[v + 1];
00305             y1 = Points->y[v];
00306             y2 = Points->y[v + 1];
00307 
00308             Vect_append_point(NPoints, Points->x[v], Points->y[v],
00309                               Points->z[v]);
00310 
00311             /* Box */
00312             if (x1 <= x2) {
00313                 xmin = x1;
00314                 xmax = x2;
00315             }
00316             else {
00317                 xmin = x2;
00318                 xmax = x1;
00319             }
00320             if (y1 <= y2) {
00321                 ymin = y1;
00322                 ymax = y2;
00323             }
00324             else {
00325                 ymin = y2;
00326                 ymax = y1;
00327             }
00328 
00329             rect.boundary[0] = xmin - thresh;
00330             rect.boundary[3] = xmax + thresh;
00331             rect.boundary[1] = ymin - thresh;
00332             rect.boundary[4] = ymax + thresh;
00333             rect.boundary[2] = 0;
00334             rect.boundary[5] = 0;
00335 
00336             /* Find points */
00337             Vect_reset_list(List);
00338             RTreeSearch(RTree, &rect, (void *)add_item, List);
00339 
00340             G_debug(3, "  %d points in box", List->n_values);
00341 
00342             /* Snap to anchor in threshold different from end points */
00343             nnew = 0;
00344             for (i = 0; i < List->n_values; i++) {
00345                 double dist2, along;
00346 
00347                 spoint = List->value[i];
00348                 G_debug(4, "    spoint = %d anchor = %d", spoint,
00349                         XPnts[spoint].anchor);
00350 
00351                 if (spoint == Index[v] || spoint == Index[v + 1])
00352                     continue;   /* end point */
00353                 if (XPnts[spoint].anchor > 0)
00354                     continue;   /* point is not anchor */
00355 
00356                 /* Check the distance */
00357                 dist2 =
00358                     dig_distance2_point_to_line(XPnts[spoint].x,
00359                                                 XPnts[spoint].y, 0, x1, y1, 0,
00360                                                 x2, y2, 0, 0, NULL, NULL,
00361                                                 NULL, &along, NULL);
00362 
00363                 G_debug(4, "      distance = %lf", sqrt(dist2));
00364 
00365                 if (dist2 <= thresh2) {
00366                     G_debug(4, "      anchor in thresh, along = %lf", along);
00367 
00368                     if (nnew == anew) {
00369                         anew += 100;
00370                         New = (NEW *) G_realloc(New, anew * sizeof(NEW));
00371                     }
00372                     New[nnew].anchor = spoint;
00373                     New[nnew].along = along;
00374                     nnew++;
00375                 }
00376             }
00377             G_debug(3, "  nnew = %d", nnew);
00378             /* insert new vertices */
00379             if (nnew > 0) {
00380                 /* sort by distance along the segment */
00381                 qsort(New, sizeof(char) * nnew, sizeof(NEW), sort_new);
00382 
00383                 for (i = 0; i < nnew; i++) {
00384                     anchor = New[i].anchor;
00385                     /* Vect_line_insert_point ( Points, ++v, XPnts[anchor].x, XPnts[anchor].y, 0); */
00386                     Vect_append_point(NPoints, XPnts[anchor].x,
00387                                       XPnts[anchor].y, 0);
00388                     ncreated++;
00389                 }
00390                 changed = 1;
00391             }
00392         }
00393 
00394         /* append end point */
00395         v = Points->n_points - 1;
00396         Vect_append_point(NPoints, Points->x[v], Points->y[v], Points->z[v]);
00397 
00398         if (changed) {          /* rewrite the line */
00399             Vect_line_prune(NPoints);   /* remove duplicates */
00400             if (NPoints->n_points > 1 || ltype & GV_LINES) {
00401                 Vect_rewrite_line(Map, line, ltype, NPoints, Cats);
00402             }
00403             else {
00404                 Vect_delete_line(Map, line);
00405             }
00406             if (Err) {
00407                 Vect_write_line(Err, ltype, Points, Cats);
00408             }
00409         }
00410     }                           /* for each line */
00411     G_percent(line_idx, List_lines->n_values, 2); /* finish it */
00412 
00413     Vect_destroy_line_struct(Points);
00414     Vect_destroy_line_struct(NPoints);
00415     Vect_destroy_cats_struct(Cats);
00416     G_free(XPnts);
00417     G_free(Index);
00418     G_free(New);
00419     RTreeDestroyNode(RTree);
00420 
00421     G_verbose_message(_("Snapped vertices: %d"), nsnapped);
00422     G_verbose_message(_("New vertices: %d"), ncreated);
00423 }
00424 
00425 /* for qsort */
00426 static int sort_new(const void *pa, const void *pb)
00427 {
00428     NEW *p1 = (NEW *) pa;
00429     NEW *p2 = (NEW *) pb;
00430 
00431     if (p1->along < p2->along)
00432         return -1;
00433     if (p1->along > p2->along)
00434         return 1;
00435     return 1;
00436 }
00437 
00450 void
00451 Vect_snap_lines(struct Map_info *Map, int type, double thresh,
00452                 struct Map_info *Err)
00453 {
00454     int line, nlines, ltype;
00455 
00456     struct ilist *List;
00457 
00458     List = Vect_new_list();
00459 
00460     nlines = Vect_get_num_lines(Map);
00461 
00462     for (line = 1; line <= nlines; line++) {
00463         G_debug(3, "line =  %d", line);
00464 
00465         if (!Vect_line_alive(Map, line))
00466             continue;
00467 
00468         ltype = Vect_read_line(Map, NULL, NULL, line);
00469 
00470         if (!(ltype & type))
00471             continue;
00472 
00473         /* Vect_list_append(List, line); */
00474         dig_list_add(List, line);
00475     }
00476 
00477     Vect_snap_lines_list(Map, List, thresh, Err);
00478 
00479     Vect_destroy_list(List);
00480 
00481     return;
00482 }