/*! \brief Get list of nodes from varray Returns the list of all nodes on features selected by varray. nodes_to_features conains the index of a feature adjecent to each node or -1 if no such feature specified by varray exists. Nodes_to_features might be NULL, in which case it is left unitialised. \param map pointer to Map_info structure \param varray pointer to varray structure \param[out] nodes list of node ids \param node_to_features ? */ void NetA_varray_to_nodes(struct Map_info *map, struct varray *varray, struct ilist *nodes, int *nodes_to_features) { int nlines, nnodes, i; nlines = Vect_get_num_lines(map); nnodes = Vect_get_num_nodes(map); if (nodes_to_features) for (i = 1; i <= nnodes; i++) nodes_to_features[i] = -1; for (i = 1; i <= nlines; i++) if (varray->c[i]) { int type = Vect_read_line(map, NULL, NULL, i); if (type == GV_POINT) { int node; Vect_get_line_nodes(map, i, &node, NULL); Vect_list_append(nodes, node); if (nodes_to_features) nodes_to_features[node] = i; } else { int node1, node2; Vect_get_line_nodes(map, i, &node1, &node2); Vect_list_append(nodes, node1); Vect_list_append(nodes, node2); if (nodes_to_features) nodes_to_features[node1] = nodes_to_features[node2] = i; } } }
/* Start from some arbitrary line on a polyline and walk back to find the first node (i.e. for which the number of connected lines <> 2) This line must not be a dead line (note that the arbitrary line cannot be a dead line because this has already been checked in main.c. */ int walk_back(struct Map_info *map, int start_line, int type) { int start_node, n1, n2; int line; int next_line; G_debug(2, "walk_back() start = %d", start_line); line = start_line; /* Otherwise find the start (i.e. travel in the negative direction) */ Vect_get_line_nodes(map, line, &start_node, NULL); while (1) { /* Find next line at start node */ next_line = find_next_line(map, line, start_node, type); G_debug(2, " next = %d", next_line); /* Keep going so long as not returned to start_line, i.e. if not a closed set of lines */ if (next_line == 0 || next_line == start_line) break; line = next_line; /* In a heavily edited binary vector map the relationship between the direction of a line (in terms of whether it is positive or negative in a node's line array) and the order of the line's nodes N1 and N2 is not constant. So here we flip the direction of travel if the initial direction of travel points back to the same line. */ Vect_get_line_nodes(map, next_line, &n1, &n2); if (n2 == start_node) start_node = n1; else start_node = n2; } return (line); }
/*! \brief Get node cost For each node in the map, finds the category of the point on it (if there is any) and stores the value associated with this category in the array node_costs. If there is no point with a category, node_costs=0. node_costs are multiplied by 1000000 and truncated to integers (as is done in Vect_net_build_graph) \param In pointer to Map_info structure \param layer layer number \param column name of column \param[out] node_costs list of node costs \returns 1 on success \return 0 on failure */ int NetA_get_node_costs(struct Map_info *In, int layer, char *column, int *node_costs) { int i, nlines, nnodes; dbCatValArray vals; struct line_cats *Cats; struct line_pnts *Points; dbDriver *driver; struct field_info *Fi; Fi = Vect_get_field(In, layer); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); nlines = Vect_get_num_lines(In); nnodes = Vect_get_num_nodes(In); Cats = Vect_new_cats_struct(); Points = Vect_new_line_struct(); for (i = 1; i <= nnodes; i++) node_costs[i] = 0; db_CatValArray_init(&vals); if (db_select_CatValArray(driver, Fi->table, Fi->key, column, NULL, &vals) == -1) return 0; for (i = 1; i <= nlines; i++) { int type = Vect_read_line(In, Points, Cats, i); if (type == GV_POINT) { int node, cat; double value; if (!Vect_cat_get(Cats, layer, &cat)) continue; Vect_get_line_nodes(In, i, &node, NULL); if (db_CatValArray_get_value_double(&vals, cat, &value) == DB_OK) node_costs[node] = value * 1000000.0; } } Vect_destroy_cats_struct(Cats); db_CatValArray_free(&vals); db_close_database_shutdown_driver(driver); return 1; }
/*! \brief Draw line nodes */ void draw_line_nodes(struct Map_info *Map, int line, int draw_flag, struct robject_list *list) { unsigned int i; int type, nodes[2]; int x, y; double east, north; struct robject *robj; Vect_get_line_nodes(Map, line, &(nodes[0]), &(nodes[1])); for (i = 0; i < sizeof(nodes) / sizeof(int); i++) { type = 0; if (Vect_get_node_n_lines(Map, nodes[i]) == 1) { if (draw_flag & DRAW_NODEONE) { type = TYPE_NODEONE; } } else { if (draw_flag & DRAW_NODETWO) { type = TYPE_NODETWO; } } if (type == 0) continue; Vect_get_node_coor(Map, nodes[i], &east, &north, NULL); robj = robj_alloc(type, 1); en_to_xy(east, north, &x, &y); robj->fid = line; robj->point->x = x; robj->point->y = y; list_append(list, robj); } }
void edit_line_phase2(struct edit_line *el, double x, double y) { int node1, node2; double nodex, nodey, nodez, dist; el->phase = 2; el->Points = Vect_new_line_struct(); el->Cats = Vect_new_cats_struct(); el->line_type = Vect_read_line(&Map, el->Points, el->Cats, el->line); el->reversed = 0; /* Find out the node nearest to the line */ Vect_get_line_nodes(&Map, el->line, &node1, &node2); Vect_get_node_coor(&Map, node2, &nodex, &nodey, &nodez); dist = (x - nodex) * (x - nodex) + (y - nodey) * (y - nodey); Vect_get_node_coor(&Map, node1, &nodex, &nodey, &nodez); if ((x - nodex) * (x - nodex) + (y - nodey) * (y - nodey) < dist) { /* The first node is the nearest => reverse the line and remember * doing so. */ Vect_line_reverse(el->Points); el->reversed = 1; } display_node(node1, SYMB_BACKGROUND, 1); display_node(node2, SYMB_BACKGROUND, 1); i_prompt_buttons(_("New Point"), _("Undo Last Point"), _("Close line")); set_location(D_u_to_d_col(el->Points->x[el->Points->n_points - 1]), D_u_to_d_row(el->Points->y[el->Points->n_points - 1]) ); set_mode(MOUSE_LINE); }
int main(int argc, char **argv) { int i, j, ret, centre, line, centre1, centre2, tfield, tucfield; int nlines, nnodes, type, ltype, afield, nfield, geo, cat; int node, node1, node2; double cost, e1cost, e2cost, n1cost, n2cost, s1cost, s2cost, l, l1; struct Option *map, *output; struct Option *afield_opt, *nfield_opt, *afcol, *abcol, *ncol, *type_opt, *term_opt, *cost_opt, *tfield_opt, *tucfield_opt; struct Flag *geo_f, *turntable_f; struct GModule *module; struct Map_info Map, Out; struct cat_list *catlist; CENTER *Centers = NULL; int acentres = 0, ncentres = 0; NODE *Nodes; struct line_cats *Cats; struct line_pnts *Points, *SPoints; int niso, aiso; double *iso; int npnts1, apnts1 = 0, npnts2, apnts2 = 0; ISOPOINT *pnts1 = NULL, *pnts2 = NULL; int next_iso; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("network")); G_add_keyword(_("isolines")); module->label = _("Splits net by cost isolines."); module->description = _ ("Splits net to bands between cost isolines (direction from center). " "Center node must be opened (costs >= 0). " "Costs of center node are used in calculation."); map = G_define_standard_option(G_OPT_V_INPUT); output = G_define_standard_option(G_OPT_V_OUTPUT); term_opt = G_define_standard_option(G_OPT_V_CATS); term_opt->key = "ccats"; term_opt->required = YES; term_opt->description = _("Categories of centers (points on nodes) to which net " "will be allocated, " "layer for this categories is given by nlayer option"); cost_opt = G_define_option(); cost_opt->key = "costs"; cost_opt->type = TYPE_INTEGER; cost_opt->multiple = YES; cost_opt->required = YES; cost_opt->description = _("Costs for isolines"); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "alayer"; afield_opt->answer = "1"; afield_opt->required = YES; afield_opt->label = _("Arc layer"); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary"; type_opt->answer = "line,boundary"; type_opt->required = YES; type_opt->label = _("Arc type"); nfield_opt = G_define_standard_option(G_OPT_V_FIELD); nfield_opt->key = "nlayer"; nfield_opt->answer = "2"; nfield_opt->required = YES; nfield_opt->label = _("Node layer"); afcol = G_define_standard_option(G_OPT_DB_COLUMN); afcol->key = "afcolumn"; afcol->description = _("Arc forward/both direction(s) cost column (number)"); afcol->guisection = _("Cost"); abcol = G_define_standard_option(G_OPT_DB_COLUMN); abcol->key = "abcolumn"; abcol->description = _("Arc backward direction cost column (number)"); abcol->guisection = _("Cost"); ncol = G_define_standard_option(G_OPT_DB_COLUMN); ncol->key = "ncolumn"; ncol->description = _("Node cost column (number)"); ncol->guisection = _("Cost"); turntable_f = G_define_flag(); turntable_f->key = 't'; turntable_f->description = _("Use turntable"); turntable_f->guisection = _("Turntable"); tfield_opt = G_define_standard_option(G_OPT_V_FIELD); tfield_opt->key = "tlayer"; tfield_opt->answer = "3"; tfield_opt->label = _("Layer with turntable"); tfield_opt->description = _("Relevant only with -t flag"); tfield_opt->guisection = _("Turntable"); tucfield_opt = G_define_standard_option(G_OPT_V_FIELD); tucfield_opt->key = "tuclayer"; tucfield_opt->answer = "4"; tucfield_opt->label = _("Layer with unique categories used in turntable"); tucfield_opt->description = _("Relevant only with -t flag"); tucfield_opt->guisection = _("Turntable"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); Vect_check_input_output_name(map->answer, output->answer, G_FATAL_EXIT); Cats = Vect_new_cats_struct(); Points = Vect_new_line_struct(); SPoints = Vect_new_line_struct(); type = Vect_option_to_types(type_opt); catlist = Vect_new_cat_list(); Vect_str_to_cat_list(term_opt->answer, catlist); /* Iso costs */ aiso = 1; iso = (double *)G_malloc(aiso * sizeof(double)); /* Set first iso to 0 */ iso[0] = 0; niso = 1; i = 0; while (cost_opt->answers[i]) { if (niso == aiso) { aiso += 1; iso = (double *)G_realloc(iso, aiso * sizeof(double)); } iso[niso] = atof(cost_opt->answers[i]); if (iso[niso] <= 0) G_fatal_error(_("Wrong iso cost: %f"), iso[niso]); if (iso[niso] <= iso[niso - 1]) G_fatal_error(_("Iso cost: %f less than previous"), iso[niso]); G_verbose_message(_("Iso cost %d: %f"), niso, iso[niso]); niso++; i++; } /* Should not happen: */ if (niso < 2) G_warning(_ ("Not enough costs, everything reachable falls to first band")); if (geo_f->answer) geo = 1; else geo = 0; Vect_set_open_level(2); if (Vect_open_old(&Map, map->answer, "") < 0) G_fatal_error(_("Unable to open vector map <%s>"), map->answer); afield = Vect_get_field_number(&Map, afield_opt->answer); nfield = Vect_get_field_number(&Map, nfield_opt->answer); tfield = Vect_get_field_number(&Map, tfield_opt->answer); tucfield = Vect_get_field_number(&Map, tucfield_opt->answer); /* Build graph */ if (turntable_f->answer) Vect_net_ttb_build_graph(&Map, type, afield, nfield, tfield, tucfield, afcol->answer, abcol->answer, ncol->answer, geo, 0); else Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer, abcol->answer, ncol->answer, geo, 0); nnodes = Vect_get_num_nodes(&Map); nlines = Vect_get_num_lines(&Map); /* Create list of centres based on list of categories */ for (i = 1; i <= nlines; i++) { ltype = Vect_get_line_type(&Map, i); if (!(ltype & GV_POINT)) continue; Vect_read_line(&Map, Points, Cats, i); node = Vect_find_node(&Map, Points->x[0], Points->y[0], Points->z[0], 0, 0); if (!node) { G_warning(_("Point is not connected to the network")); continue; } if (!(Vect_cat_get(Cats, nfield, &cat))) continue; if (Vect_cat_in_cat_list(cat, catlist)) { Vect_net_get_node_cost(&Map, node, &n1cost); if (n1cost == -1) { /* closed */ G_warning(_("Centre at closed node (costs = -1) ignored")); } else { if (acentres == ncentres) { acentres += 1; Centers = (CENTER *) G_realloc(Centers, acentres * sizeof(CENTER)); } Centers[ncentres].cat = cat; Centers[ncentres].node = node; G_debug(2, "centre = %d node = %d cat = %d", ncentres, node, cat); ncentres++; } } } G_message(_("Number of centres: %d (nlayer %d)"), ncentres, nfield); if (ncentres == 0) G_warning(_ ("Not enough centres for selected nlayer. Nothing will be allocated.")); /* alloc and reset space for all nodes */ if (turntable_f->answer) { /* if turntable is used we are looking for lines as destinations, instead of the intersections (nodes) */ Nodes = (NODE *) G_calloc((nlines * 2 + 2), sizeof(NODE)); for (i = 2; i <= (nlines * 2 + 2); i++) { Nodes[i].centre = -1;/* NOTE: first two items of Nodes are not used */ } } else { Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE)); for (i = 1; i <= nnodes; i++) { Nodes[i].centre = -1; } } apnts1 = 1; pnts1 = (ISOPOINT *) G_malloc(apnts1 * sizeof(ISOPOINT)); apnts2 = 1; pnts2 = (ISOPOINT *) G_malloc(apnts2 * sizeof(ISOPOINT)); /* Fill Nodes by neares centre and costs from that centre */ for (centre = 0; centre < ncentres; centre++) { node1 = Centers[centre].node; Vect_net_get_node_cost(&Map, node1, &n1cost); G_debug(2, "centre = %d node = %d cat = %d", centre, node1, Centers[centre].cat); G_message(_("Calculating costs from centre %d..."), centre + 1); if (turntable_f->answer) for (line = 1; line <= nlines; line++) { G_debug(5, " node1 = %d line = %d", node1, line); Vect_net_get_node_cost(&Map, line, &n2cost); /* closed, left it as not attached */ if (Vect_read_line(&Map, Points, Cats, line) < 0) continue; if (Vect_get_line_type(&Map, line) != GV_LINE) continue; if (!Vect_cat_get(Cats, tucfield, &cat)) continue; for (j = 0; j < 2; j++) { if (j == 1) cat *= -1; ret = Vect_net_ttb_shortest_path(&Map, node1, 0, cat, 1, tucfield, NULL, &cost); if (ret == -1) { continue; } /* node unreachable */ /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but * only if centre and node are not identical, because at the end node cost is add later */ if (ret != 1) cost += n1cost; G_debug(5, "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f", node1, line, cost, Nodes[line * 2 + j].centre, Nodes[line * 2 + j].cost); if (Nodes[line * 2 + j].centre == -1 || cost < Nodes[line * 2 + j].cost) { Nodes[line * 2 + j].cost = cost; Nodes[line * 2 + j].centre = centre; } } } else for (node2 = 1; node2 <= nnodes; node2++) { G_percent(node2, nnodes, 1); G_debug(5, " node1 = %d node2 = %d", node1, node2); Vect_net_get_node_cost(&Map, node2, &n2cost); if (n2cost == -1) { continue; } /* closed, left it as not attached */ ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost); if (ret == -1) { continue; } /* node unreachable */ /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but * only if centre and node are not identical, because at the end node cost is add later */ if (node1 != node2) cost += n1cost; G_debug(5, "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f", node1, node2, cost, Nodes[node2].centre, Nodes[node2].cost); if (Nodes[node2].centre == -1 || cost < Nodes[node2].cost) { Nodes[node2].cost = cost; Nodes[node2].centre = centre; } } } /* Write arcs to new map */ if (Vect_open_new(&Out, output->answer, Vect_is_3d(&Map)) < 0) G_fatal_error(_("Unable to create vector map <%s>"), output->answer); Vect_hist_command(&Out); G_message("Generating isolines..."); nlines = Vect_get_num_lines(&Map); for (line = 1; line <= nlines; line++) { G_percent(line, nlines, 2); ltype = Vect_read_line(&Map, Points, NULL, line); if (!(ltype & type)) { continue; } l = Vect_line_length(Points); if (l == 0) continue; if (turntable_f->answer) { centre1 = Nodes[line * 2].centre; centre2 = Nodes[line * 2 + 1].centre; s1cost = Nodes[line * 2].cost; s2cost = Nodes[line * 2 + 1].cost; n1cost = n2cost = 0; } else { Vect_get_line_nodes(&Map, line, &node1, &node2); centre1 = Nodes[node1].centre; centre2 = Nodes[node2].centre; s1cost = Nodes[node1].cost; s2cost = Nodes[node2].cost; Vect_net_get_node_cost(&Map, node1, &n1cost); Vect_net_get_node_cost(&Map, node2, &n2cost); } Vect_net_get_line_cost(&Map, line, GV_FORWARD, &e1cost); Vect_net_get_line_cost(&Map, line, GV_BACKWARD, &e2cost); G_debug(3, "Line %d : length = %f", line, l); G_debug(3, "Arc centres: %d %d (nodes: %d %d)", centre1, centre2, node1, node2); G_debug(3, " s1cost = %f n1cost = %f e1cost = %f", s1cost, n1cost, e1cost); G_debug(3, " s2cost = %f n2cost = %f e2cost = %f", s2cost, n2cost, e2cost); /* First check if arc is reachable from at least one side */ if ((centre1 != -1 && n1cost != -1 && e1cost != -1) || (centre2 != -1 && n2cost != -1 && e2cost != -1)) { /* Line is reachable at least from one side */ G_debug(3, " -> arc is reachable"); /* Add costs of node to starting costs */ s1cost += n1cost; s2cost += n2cost; e1cost /= l; e2cost /= l; /* Find points on isolines along the line in both directions, add them to array, * first point is placed at the beginning/end of line */ /* Forward */ npnts1 = 0; /* in case this direction is closed */ if (centre1 != -1 && n1cost != -1 && e1cost != -1) { /* Find iso for beginning of the line */ next_iso = 0; for (i = niso - 1; i >= 0; i--) { if (iso[i] <= s1cost) { next_iso = i; break; } } /* Add first */ pnts1[0].iso = next_iso; pnts1[0].distance = 0; npnts1++; next_iso++; /* Calculate distances for points along line */ while (next_iso < niso) { if (e1cost == 0) break; /* Outside line */ l1 = (iso[next_iso] - s1cost) / e1cost; if (l1 >= l) break; /* Outside line */ if (npnts1 == apnts1) { apnts1 += 1; pnts1 = (ISOPOINT *) G_realloc(pnts1, apnts1 * sizeof(ISOPOINT)); } pnts1[npnts1].iso = next_iso; pnts1[npnts1].distance = l1; G_debug(3, " forward %d : iso %d : distance %f : cost %f", npnts1, next_iso, l1, iso[next_iso]); npnts1++; next_iso++; } } G_debug(3, " npnts1 = %d", npnts1); /* Backward */ npnts2 = 0; if (centre2 != -1 && n2cost != -1 && e2cost != -1) { /* Find iso for beginning of the line */ next_iso = 0; for (i = niso - 1; i >= 0; i--) { if (iso[i] <= s2cost) { next_iso = i; break; } } /* Add first */ pnts2[0].iso = next_iso; pnts2[0].distance = l; npnts2++; next_iso++; /* Calculate distances for points along line */ while (next_iso < niso) { if (e2cost == 0) break; /* Outside line */ l1 = (iso[next_iso] - s2cost) / e2cost; if (l1 >= l) break; /* Outside line */ if (npnts2 == apnts2) { apnts2 += 1; pnts2 = (ISOPOINT *) G_realloc(pnts2, apnts2 * sizeof(ISOPOINT)); } pnts2[npnts2].iso = next_iso; pnts2[npnts2].distance = l - l1; G_debug(3, " backward %d : iso %d : distance %f : cost %f", npnts2, next_iso, l - l1, iso[next_iso]); npnts2++; next_iso++; } } G_debug(3, " npnts2 = %d", npnts2); /* Limit number of points by maximum costs in reverse direction, this may remove * also the first point in one direction, but not in both */ /* Forward */ if (npnts2 > 0) { for (i = 0; i < npnts1; i++) { G_debug(3, " pnt1 = %d dist1 = %f iso1 = %d max iso2 = %d", i, pnts1[i].distance, pnts1[i].iso, pnts2[npnts2 - 1].iso); if (pnts2[npnts2 - 1].iso < pnts1[i].iso) { G_debug(3, " -> cut here"); npnts1 = i; break; } } } G_debug(3, " npnts1 cut = %d", npnts1); /* Backward */ if (npnts1 > 0) { for (i = 0; i < npnts2; i++) { G_debug(3, " pnt2 = %d dist2 = %f iso2 = %d max iso1 = %d", i, pnts2[i].distance, pnts2[i].iso, pnts1[npnts1 - 1].iso); if (pnts1[npnts1 - 1].iso < pnts2[i].iso) { G_debug(3, " -> cut here"); npnts2 = i; break; } } } G_debug(3, " npnts2 cut = %d", npnts2); /* Biggest cost shoud be equal if exist (npnts > 0). Cut out overlapping segments, * this can cut only points on line but not first points */ if (npnts1 > 1 && npnts2 > 1) { while (npnts1 > 1 && npnts2 > 1) { if (pnts1[npnts1 - 1].distance >= pnts2[npnts2 - 1].distance) { /* overlap */ npnts1--; npnts2--; } else { break; } } } G_debug(3, " npnts1 2. cut = %d", npnts1); G_debug(3, " npnts2 2. cut = %d", npnts2); /* Now we have points in both directions which may not overlap, npoints in one * direction may be 0 but not both */ /* Join both arrays, iso of point is for next segment (point is at the beginning) */ /* In case npnts1 == 0 add point at distance 0 */ if (npnts1 == 0) { G_debug(3, " npnts1 = 0 -> add first at distance 0, cat = %d", pnts2[npnts2 - 1].iso); pnts1[0].iso = pnts2[npnts2 - 1].iso; /* use last point iso in reverse direction */ pnts1[0].distance = 0; npnts1++; } for (i = npnts2 - 1; i >= 0; i--) { /* Check if identical */ if (pnts1[npnts1 - 1].distance == pnts2[i].distance) continue; if (npnts1 == apnts1) { apnts1 += 1; pnts1 = (ISOPOINT *) G_realloc(pnts1, apnts1 * sizeof(ISOPOINT)); } pnts1[npnts1].iso = pnts2[i].iso - 1; /* last may be -1, but it is not used */ pnts1[npnts1].distance = pnts2[i].distance; npnts1++; } /* In case npnts2 == 0 add point at the end */ if (npnts2 == 0) { pnts1[npnts1].iso = 0; /* not used */ pnts1[npnts1].distance = l; npnts1++; } /* Create line segments. */ for (i = 1; i < npnts1; i++) { cat = pnts1[i - 1].iso + 1; G_debug(3, " segment %f - %f cat %d", pnts1[i - 1].distance, pnts1[i].distance, cat); ret = Vect_line_segment(Points, pnts1[i - 1].distance, pnts1[i].distance, SPoints); if (ret == 0) { G_warning(_ ("Cannot get line segment, segment out of line")); } else { Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, SPoints, Cats); } } } else { /* arc is not reachable */ G_debug(3, " -> arc is not reachable"); Vect_reset_cats(Cats); Vect_write_line(&Out, ltype, Points, Cats); } } Vect_build(&Out); /* Free, ... */ G_free(Nodes); G_free(Centers); Vect_close(&Map); Vect_close(&Out); exit(EXIT_SUCCESS); }
int report(struct Map_info *In, int afield, int nfield, int action) { int i, j, line, nlines, ltype, node, nnodes; int cat_line, cat_node[2]; struct line_cats *Cats, *Cats2; struct line_pnts *Points; struct bound_box box; double x, y, z; Cats = Vect_new_cats_struct(); Cats2 = Vect_new_cats_struct(); Points = Vect_new_line_struct(); nlines = Vect_get_num_lines(In); if (action == TOOL_REPORT) { struct boxlist *List; List = Vect_new_boxlist(0); /* For all lines find categories for points on nodes */ for (i = 1; i <= nlines; i++) { ltype = Vect_read_line(In, NULL, Cats, i); if (!(ltype & GV_LINES)) continue; cat_line = 0; if (!Vect_cat_get(Cats, afield, &cat_line)) G_warning(_("Line %d has no category"), i); cat_node[0] = cat_node[1] = -1; for (j = 0; j < 2; j++) { if (j == 0) Vect_get_line_nodes(In, i, &node, NULL); else Vect_get_line_nodes(In, i, NULL, &node); Vect_get_node_coor(In, node, &x, &y, &z); box.E = box.W = x; box.N = box.S = y; box.T = box.B = z; Vect_select_lines_by_box(In, &box, GV_POINT, List); nnodes = List->n_values; if (nnodes > 0) { line = List->id[nnodes - 1]; /* last in list */ Vect_read_line(In, NULL, Cats, line); Vect_cat_get(Cats, nfield, &(cat_node[j])); } if (nnodes == 0) { /* this is ok, not every node needs to be * represented by a point */ G_debug(4, "No point here: %g %g %.g line category: %d", x, y, z, cat_line); } else if (nnodes > 1) G_warning(_("%d points found: %g %g %g line category: %d"), nnodes, x, y, z, cat_line); } fprintf(stdout, "%d %d %d\n", cat_line, cat_node[0], cat_node[1]); } } else { /* node report */ int elem, nelem, type, k, l; struct ilist *List; List = Vect_new_list(); for (i = 1; i <= nlines; i++) { if (Vect_get_line_type(In, i) != GV_POINT) continue; Vect_read_line(In, Points, Cats, i); box.E = box.W = Points->x[0]; box.N = box.S = Points->y[0]; box.T = box.B = Points->z[0]; nnodes = Vect_select_nodes_by_box(In, &box, List); if (nnodes > 1) { G_warning(_("Duplicate nodes at x=%g y=%g z=%g "), Points->x[0], Points->y[0], Points->z[0]); } if (nnodes > 0) { node = List->value[0]; nelem = Vect_get_node_n_lines(In, node); /* Loop through all cats of point */ for (j = 0; j < Cats->n_cats; j++) { if (Cats->field[j] == nfield) { int count = 0; fprintf(stdout, "%d ", Cats->cat[j]); /* Loop through all lines */ for (k = 0; k < nelem; k++) { elem = abs(Vect_get_node_line(In, node, k)); type = Vect_read_line(In, NULL, Cats2, elem); if (!(type & GV_LINES)) continue; /* Loop through all cats of line */ for (l = 0; l < Cats2->n_cats; l++) { if (Cats2->field[l] == afield) { if (count > 0) fprintf(stdout, ","); fprintf(stdout, "%d", Cats2->cat[l]); count++; } } } fprintf(stdout, "\n"); } } } } } return 0; }
/*! \brief Connect lines in given threshold \code \ \ id1 \ -> \ \ id2 --------- -----+--- \endcode If two lines are selected and <i>thresh</i> is -1, no limit is applied. \param Map pointer to Map_info \param List list of selected lines \param thresh threshold value \return number of modified lines \return -1 on error */ int Vedit_connect_lines(struct Map_info *Map, struct ilist *List, double thresh) { int nlines_modified, connected; int i, j, node[2], n_nodes; int line, found; double x, y, z; struct ilist *List_exclude, *List_found; nlines_modified = 0; List_exclude = Vect_new_list(); List_found = Vect_new_list(); n_nodes = 2; /* collect lines to be modified */ for (i = 0; i < List->n_values; i++) { line = List->value[i]; if (!Vect_line_alive(Map, line)) continue; if (Vect_get_line_type(Map, line) & GV_POINTS) continue; node[0] = node[1] = -1; Vect_get_line_nodes(Map, line, &(node[0]), &(node[1])); if (node[0] < 0 || node[1] < 0) continue; connected = 0; Vect_reset_list(List_exclude); Vect_list_append(List_exclude, line); for (j = 0; j < n_nodes && !connected; j++) { /* for each line node find lines in threshold */ Vect_get_node_coor(Map, node[j], &x, &y, &z); do { /* find first nearest line */ found = Vect_find_line_list(Map, x, y, z, GV_LINES, thresh, WITHOUT_Z, List_exclude, List_found); if (found > 0 && Vect_line_alive(Map, found)) { /* try to connect lines (given node) */ G_debug(3, "Vedit_connect_lines(): lines=%d,%d", line, found); if (connect_lines(Map, !j, line, found, thresh, List)) { G_debug(3, "Vedit_connect_lines(): lines=%d,%d -> connected", line, found); nlines_modified += 2; connected = 1; } } Vect_list_append(List_exclude, found); } while(List_found->n_values > 0 && !connected); } } Vect_destroy_list(List_exclude); Vect_destroy_list(List_found); return nlines_modified; }
int write_triple(struct Site *s1, struct Site *s2, struct Site *s3) { int i; int node; static struct line_pnts *Points = NULL; static struct line_cats *Cats = NULL; struct Site *sa, *sb; if (!Points) { Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); } if (triangulate) { for (i = 0; i < 3; i++) { switch (i) { case 0: sa = s1; sb = s2; break; case 1: sa = s2; sb = s3; break; case 2: sa = s3; sb = s1; break; } /* Look if the line already exists */ node = Vect_find_node(&Out, sa->coord.x, sa->coord.y, 0.0, 0.0, 0); if (node > 0) { /* node found */ int j, nlines; int found = 0; double x, y, z; nlines = Vect_get_node_n_lines(&Out, node); for (j = 0; j < nlines; j++) { int line, node2; line = Vect_get_node_line(&Out, node, j); if (line > 0) Vect_get_line_nodes(&Out, line, NULL, &node2); else Vect_get_line_nodes(&Out, abs(line), &node2, NULL); Vect_get_node_coor(&Out, node2, &x, &y, &z); if (x == sb->coord.x && y == sb->coord.y) { found = 1; break; } } if (found) continue; /* next segment */ } /* Not found, write it */ Vect_reset_line(Points); if (mode3d) { G_debug(3, "sa->coord.z: %f", sa->coord.z); Vect_append_point(Points, sa->coord.x, sa->coord.y, sa->coord.z); Vect_append_point(Points, sb->coord.x, sb->coord.y, sb->coord.z); } else { Vect_append_point(Points, sa->coord.x, sa->coord.y, 0.0); Vect_append_point(Points, sb->coord.x, sb->coord.y, 0.0); } Vect_write_line(&Out, Type, Points, Cats); } } return 0; }
int main(int argc, char *argv[]) { struct Map_info In, Out; static struct line_pnts *Points; struct line_cats *Cats; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out; struct Option *cat_opt, *field_opt, *where_opt, *abcol, *afcol; struct Option *iter_opt, *error_opt; struct Flag *geo_f, *add_f; int chcat, with_z; int layer, mask_type; struct varray *varray; dglGraph_s *graph; int i, geo, nnodes, nlines, j, max_cat; char buf[2000], *covered; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); module->keywords = _("vector, network, centrality measures"); module->description = _("Computes degree, centrality, betweeness, closeness and eigenvector " "centrality measures in the network."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); field_opt = G_define_standard_option(G_OPT_V_FIELD); map_out = G_define_standard_option(G_OPT_V_OUTPUT); cat_opt = G_define_standard_option(G_OPT_V_CATS); cat_opt->guisection = _("Selection"); where_opt = G_define_standard_option(G_OPT_WHERE); where_opt->guisection = _("Selection"); afcol = G_define_standard_option(G_OPT_COLUMN); afcol->key = "afcolumn"; afcol->required = NO; afcol->description = _("Name of arc forward/both direction(s) cost column"); afcol->guisection = _("Cost"); abcol = G_define_standard_option(G_OPT_COLUMN); abcol->key = "abcolumn"; abcol->required = NO; abcol->description = _("Name of arc backward direction cost column"); abcol->guisection = _("Cost"); deg_opt = G_define_standard_option(G_OPT_COLUMN); deg_opt->key = "degree"; deg_opt->required = NO; deg_opt->description = _("Name of degree centrality column"); deg_opt->guisection = _("Columns"); close_opt = G_define_standard_option(G_OPT_COLUMN); close_opt->key = "closeness"; close_opt->required = NO; close_opt->description = _("Name of closeness centrality column"); close_opt->guisection = _("Columns"); betw_opt = G_define_standard_option(G_OPT_COLUMN); betw_opt->key = "betweenness"; betw_opt->required = NO; betw_opt->description = _("Name of betweenness centrality column"); betw_opt->guisection = _("Columns"); eigen_opt = G_define_standard_option(G_OPT_COLUMN); eigen_opt->key = "eigenvector"; eigen_opt->required = NO; eigen_opt->description = _("Name of eigenvector centrality column"); eigen_opt->guisection = _("Columns"); iter_opt = G_define_option(); iter_opt->key = "iterations"; iter_opt->answer = "1000"; iter_opt->type = TYPE_INTEGER; iter_opt->required = NO; iter_opt->description = _("Maximum number of iterations to compute eigenvector centrality"); error_opt = G_define_option(); error_opt->key = "error"; error_opt->answer = "0.1"; error_opt->type = TYPE_DOUBLE; error_opt->required = NO; error_opt->description = _("Cummulative error tolerance for eigenvector centrality"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); add_f = G_define_flag(); add_f->key = 'a'; add_f->description = _("Add points on nodes"); /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); /* TODO: make an option for this */ mask_type = GV_LINE | GV_BOUNDARY; Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_check_input_output_name(map_in->answer, map_out->answer, GV_FATAL_EXIT); Vect_set_open_level(2); if (1 > Vect_open_old(&In, map_in->answer, "")) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (geo_f->answer) { geo = 1; if (G_projection() != PROJECTION_LL) G_warning(_("The current projection is not longitude-latitude")); } else geo = 0; /* parse filter option and select appropriate lines */ layer = atoi(field_opt->answer); chcat = (NetA_initialise_varray (&In, layer, mask_type, where_opt->answer, cat_opt->answer, &varray) == 1); /* Create table */ Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE); Vect_map_add_dblink(&Out, 1, NULL, Fi->table, "cat", Fi->database, Fi->driver); db_init_string(&sql); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_init_string(&tmp); if (deg_opt->answer) append_string(&tmp, deg_opt->answer); if (close_opt->answer) append_string(&tmp, close_opt->answer); if (betw_opt->answer) append_string(&tmp, betw_opt->answer); if (eigen_opt->answer) append_string(&tmp, eigen_opt->answer); sprintf(buf, "create table %s(cat integer%s)", Fi->table, db_get_string(&tmp)); db_set_string(&sql, buf); G_debug(2, db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { db_close_database_shutdown_driver(driver); G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&sql)); } if (db_create_index2(driver, Fi->table, "cat") != DB_OK) G_warning(_("Cannot create index")); if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) G_fatal_error(_("Cannot grant privileges on table <%s>"), Fi->table); db_begin_transaction(driver); Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); Vect_net_build_graph(&In, mask_type, atoi(field_opt->answer), 0, afcol->answer, abcol->answer, NULL, geo, 0); graph = &(In.graph); nnodes = dglGet_NodeCount(graph); deg = closeness = betw = eigen = NULL; covered = (char *)G_calloc(nnodes + 1, sizeof(char)); if (!covered) G_fatal_error(_("Out of memory")); if (deg_opt->answer) { deg = (double *)G_calloc(nnodes + 1, sizeof(double)); if (!deg) G_fatal_error(_("Out of memory")); } if (close_opt->answer) { closeness = (double *)G_calloc(nnodes + 1, sizeof(double)); if (!closeness) G_fatal_error(_("Out of memory")); } if (betw_opt->answer) { betw = (double *)G_calloc(nnodes + 1, sizeof(double)); if (!betw) G_fatal_error(_("Out of memory")); } if (eigen_opt->answer) { eigen = (double *)G_calloc(nnodes + 1, sizeof(double)); if (!eigen) G_fatal_error(_("Out of memory")); } if (deg_opt->answer) { G_message(_("Computing degree centrality measure")); NetA_degree_centrality(graph, deg); } if (betw_opt->answer || close_opt->answer) { G_message(_("Computing betweenness and/or closeness centrality measure")); NetA_betweenness_closeness(graph, betw, closeness); if (closeness) for (i = 1; i <= nnodes; i++) closeness[i] /= (double)In.cost_multip; } if (eigen_opt->answer) { G_message(_("Computing eigenvector centrality measure")); NetA_eigenvector_centrality(graph, atoi(iter_opt->answer), atof(error_opt->answer), eigen); } nlines = Vect_get_num_lines(&In); G_message(_("Writing data into the table...")); G_percent_reset(); for (i = 1; i <= nlines; i++) { G_percent(i, nlines, 1); int type = Vect_read_line(&In, Points, Cats, i); if (type == GV_POINT && (!chcat || varray->c[i])) { int cat, node; if (!Vect_cat_get(Cats, layer, &cat)) continue; Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, type, Points, Cats); Vect_get_line_nodes(&In, i, &node, NULL); process_node(node, cat); covered[node] = 1; } } if (add_f->answer && !chcat) { max_cat = 0; for (i = 1; i <= nlines; i++) { Vect_read_line(&In, NULL, Cats, i); for (j = 0; j < Cats->n_cats; j++) if (Cats->cat[j] > max_cat) max_cat = Cats->cat[j]; } max_cat++; for (i = 1; i <= nnodes; i++) if (!covered[i]) { Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, max_cat); NetA_add_point_on_node(&In, &Out, i, Cats); process_node(i, max_cat); max_cat++; } } db_commit_transaction(driver); db_close_database_shutdown_driver(driver); G_free(covered); if (deg) G_free(deg); if (closeness) G_free(closeness); if (betw) G_free(betw); if (eigen) G_free(eigen); Vect_build(&Out); Vect_close(&In); Vect_close(&Out); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { struct Map_info In, Out, Error; struct line_pnts *Points; struct line_cats *Cats; int i, type, iter; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out, *error_out, *thresh_opt, *method_opt, *look_ahead_opt; struct Option *iterations_opt, *cat_opt, *alpha_opt, *beta_opt, *type_opt; struct Option *field_opt, *where_opt, *reduction_opt, *slide_opt; struct Option *angle_thresh_opt, *degree_thresh_opt, *closeness_thresh_opt; struct Option *betweeness_thresh_opt; struct Flag *notab_flag, *loop_support_flag; int with_z; int total_input, total_output; /* Number of points in the input/output map respectively */ double thresh, alpha, beta, reduction, slide, angle_thresh; double degree_thresh, closeness_thresh, betweeness_thresh; int method; int look_ahead, iterations; int loop_support; int layer; int n_lines; int simplification, mask_type; struct cat_list *cat_list = NULL; char *s, *descriptions; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("generalization")); G_add_keyword(_("simplification")); G_add_keyword(_("smoothing")); G_add_keyword(_("displacement")); G_add_keyword(_("network generalization")); module->description = _("Performs vector based generalization."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary,area"; type_opt->answer = "line,boundary,area"; type_opt->guisection = _("Selection"); map_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out->key = "error"; error_out->required = NO; error_out->description = _("Error map of all lines and boundaries not being generalized due to topology issues or over-simplification"); method_opt = G_define_option(); method_opt->key = "method"; method_opt->type = TYPE_STRING; method_opt->required = YES; method_opt->multiple = NO; method_opt->options = "douglas,douglas_reduction,lang,reduction,reumann,boyle,sliding_averaging,distance_weighting,chaiken,hermite,snakes,network,displacement"; descriptions = NULL; G_asprintf(&descriptions, "douglas;%s;" "douglas_reduction;%s;" "lang;%s;" "reduction;%s;" "reumann;%s;" "boyle;%s;" "sliding_averaging;%s;" "distance_weighting;%s;" "chaiken;%s;" "hermite;%s;" "snakes;%s;" "network;%s;" "displacement;%s;", _("Douglas-Peucker Algorithm"), _("Douglas-Peucker Algorithm with reduction parameter"), _("Lang Simplification Algorithm"), _("Vertex Reduction Algorithm eliminates points close to each other"), _("Reumann-Witkam Algorithm"), _("Boyle's Forward-Looking Algorithm"), _("McMaster's Sliding Averaging Algorithm"), _("McMaster's Distance-Weighting Algorithm"), _("Chaiken's Algorithm"), _("Interpolation by Cubic Hermite Splines"), _("Snakes method for line smoothing"), _("Network generalization"), _("Displacement of lines close to each other")); method_opt->descriptions = G_store(descriptions); method_opt->description = _("Generalization algorithm"); thresh_opt = G_define_option(); thresh_opt->key = "threshold"; thresh_opt->type = TYPE_DOUBLE; thresh_opt->required = YES; thresh_opt->options = "0-1000000000"; thresh_opt->description = _("Maximal tolerance value"); look_ahead_opt = G_define_option(); look_ahead_opt->key = "look_ahead"; look_ahead_opt->type = TYPE_INTEGER; look_ahead_opt->required = NO; look_ahead_opt->answer = "7"; look_ahead_opt->description = _("Look-ahead parameter"); reduction_opt = G_define_option(); reduction_opt->key = "reduction"; reduction_opt->type = TYPE_DOUBLE; reduction_opt->required = NO; reduction_opt->answer = "50"; reduction_opt->options = "0-100"; reduction_opt->description = _("Percentage of the points in the output of 'douglas_reduction' algorithm"); slide_opt = G_define_option(); slide_opt->key = "slide"; slide_opt->type = TYPE_DOUBLE; slide_opt->required = NO; slide_opt->answer = "0.5"; slide_opt->options = "0-1"; slide_opt->description = _("Slide of computed point toward the original point"); angle_thresh_opt = G_define_option(); angle_thresh_opt->key = "angle_thresh"; angle_thresh_opt->type = TYPE_DOUBLE; angle_thresh_opt->required = NO; angle_thresh_opt->answer = "3"; angle_thresh_opt->options = "0-180"; angle_thresh_opt->description = _("Minimum angle between two consecutive segments in Hermite method"); degree_thresh_opt = G_define_option(); degree_thresh_opt->key = "degree_thresh"; degree_thresh_opt->type = TYPE_INTEGER; degree_thresh_opt->required = NO; degree_thresh_opt->answer = "0"; degree_thresh_opt->description = _("Degree threshold in network generalization"); closeness_thresh_opt = G_define_option(); closeness_thresh_opt->key = "closeness_thresh"; closeness_thresh_opt->type = TYPE_DOUBLE; closeness_thresh_opt->required = NO; closeness_thresh_opt->answer = "0"; closeness_thresh_opt->options = "0-1"; closeness_thresh_opt->description = _("Closeness threshold in network generalization"); betweeness_thresh_opt = G_define_option(); betweeness_thresh_opt->key = "betweeness_thresh"; betweeness_thresh_opt->type = TYPE_DOUBLE; betweeness_thresh_opt->required = NO; betweeness_thresh_opt->answer = "0"; betweeness_thresh_opt->description = _("Betweeness threshold in network generalization"); alpha_opt = G_define_option(); alpha_opt->key = "alpha"; alpha_opt->type = TYPE_DOUBLE; alpha_opt->required = NO; alpha_opt->answer = "1.0"; alpha_opt->description = _("Snakes alpha parameter"); beta_opt = G_define_option(); beta_opt->key = "beta"; beta_opt->type = TYPE_DOUBLE; beta_opt->required = NO; beta_opt->answer = "1.0"; beta_opt->description = _("Snakes beta parameter"); iterations_opt = G_define_option(); iterations_opt->key = "iterations"; iterations_opt->type = TYPE_INTEGER; iterations_opt->required = NO; iterations_opt->answer = "1"; iterations_opt->description = _("Number of iterations"); cat_opt = G_define_standard_option(G_OPT_V_CATS); cat_opt->guisection = _("Selection"); where_opt = G_define_standard_option(G_OPT_DB_WHERE); where_opt->guisection = _("Selection"); loop_support_flag = G_define_flag(); loop_support_flag->key = 'l'; loop_support_flag->label = _("Disable loop support"); loop_support_flag->description = _("Do not modify end points of lines forming a closed loop"); notab_flag = G_define_standard_flag(G_FLG_V_TABLE); notab_flag->description = _("Do not copy attributes"); notab_flag->guisection = _("Attributes"); /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); thresh = atof(thresh_opt->answer); look_ahead = atoi(look_ahead_opt->answer); alpha = atof(alpha_opt->answer); beta = atof(beta_opt->answer); reduction = atof(reduction_opt->answer); iterations = atoi(iterations_opt->answer); slide = atof(slide_opt->answer); angle_thresh = atof(angle_thresh_opt->answer); degree_thresh = atof(degree_thresh_opt->answer); closeness_thresh = atof(closeness_thresh_opt->answer); betweeness_thresh = atof(betweeness_thresh_opt->answer); mask_type = type_mask(type_opt); G_debug(3, "Method: %s", method_opt->answer); s = method_opt->answer; if (strcmp(s, "douglas") == 0) method = DOUGLAS; else if (strcmp(s, "lang") == 0) method = LANG; else if (strcmp(s, "reduction") == 0) method = VERTEX_REDUCTION; else if (strcmp(s, "reumann") == 0) method = REUMANN; else if (strcmp(s, "boyle") == 0) method = BOYLE; else if (strcmp(s, "distance_weighting") == 0) method = DISTANCE_WEIGHTING; else if (strcmp(s, "chaiken") == 0) method = CHAIKEN; else if (strcmp(s, "hermite") == 0) method = HERMITE; else if (strcmp(s, "snakes") == 0) method = SNAKES; else if (strcmp(s, "douglas_reduction") == 0) method = DOUGLAS_REDUCTION; else if (strcmp(s, "sliding_averaging") == 0) method = SLIDING_AVERAGING; else if (strcmp(s, "network") == 0) method = NETWORK; else if (strcmp(s, "displacement") == 0) { method = DISPLACEMENT; /* we can displace only the lines */ mask_type = GV_LINE; } else { G_fatal_error(_("Unknown method")); exit(EXIT_FAILURE); } /* simplification or smoothing? */ switch (method) { case DOUGLAS: case DOUGLAS_REDUCTION: case LANG: case VERTEX_REDUCTION: case REUMANN: simplification = 1; break; default: simplification = 0; break; } Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (Vect_open_old2(&In, map_in->answer, "", field_opt->answer) < 1) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); if (Vect_get_num_primitives(&In, mask_type) == 0) { G_warning(_("No lines found in input map <%s>"), map_in->answer); Vect_close(&In); exit(EXIT_SUCCESS); } with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (error_out->answer) { if (0 > Vect_open_new(&Error, error_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create error vector map <%s>"), error_out->answer); } } Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); total_input = total_output = 0; layer = Vect_get_field_number(&In, field_opt->answer); /* parse filter options */ if (layer > 0) cat_list = Vect_cats_set_constraint(&In, layer, where_opt->answer, cat_opt->answer); if (method == DISPLACEMENT) { /* modifies only lines, all other features including boundaries are preserved */ /* options where, cats, and layer are respected */ G_message(_("Displacement...")); snakes_displacement(&In, &Out, thresh, alpha, beta, 1.0, 10.0, iterations, cat_list, layer); } /* TODO: rearrange code below. It's really messy */ if (method == NETWORK) { /* extracts lines of selected type, all other features are discarded */ /* options where, cats, and layer are ignored */ G_message(_("Network generalization...")); total_output = graph_generalization(&In, &Out, mask_type, degree_thresh, closeness_thresh, betweeness_thresh); } /* copy tables here because method == NETWORK is complete and * tables for Out may be needed for parse_filter_options() below */ if (!notab_flag->answer) { if (method == NETWORK) copy_tables_by_cats(&In, &Out); else Vect_copy_tables(&In, &Out, -1); } else if (where_opt->answer && method < NETWORK) { G_warning(_("Attributes are needed for 'where' option, copying table")); Vect_copy_tables(&In, &Out, -1); } /* smoothing/simplification */ if (method < NETWORK) { /* modifies only lines of selected type, all other features are preserved */ int not_modified_boundaries = 0, n_oversimplified = 0; struct line_pnts *APoints; /* original Points */ set_topo_debug(); Vect_copy_map_lines(&In, &Out); Vect_build_partial(&Out, GV_BUILD_CENTROIDS); G_message("-----------------------------------------------------"); G_message(_("Generalization (%s)..."), method_opt->answer); G_message(_("Using threshold: %g %s"), thresh, G_database_unit_name(1)); G_percent_reset(); APoints = Vect_new_line_struct(); n_lines = Vect_get_num_lines(&Out); for (i = 1; i <= n_lines; i++) { int after = 0; G_percent(i, n_lines, 1); type = Vect_read_line(&Out, APoints, Cats, i); if (!(type & GV_LINES) || !(mask_type & type)) continue; if (layer > 0) { if ((type & GV_LINE) && !Vect_cats_in_constraint(Cats, layer, cat_list)) continue; else if ((type & GV_BOUNDARY)) { int do_line = 0; int left, right; do_line = Vect_cats_in_constraint(Cats, layer, cat_list); if (!do_line) { /* check if any of the centroids is selected */ Vect_get_line_areas(&Out, i, &left, &right); if (left < 0) left = Vect_get_isle_area(&Out, abs(left)); if (right < 0) right = Vect_get_isle_area(&Out, abs(right)); if (left > 0) { Vect_get_area_cats(&Out, left, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } if (!do_line && right > 0) { Vect_get_area_cats(&Out, right, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } } if (!do_line) continue; } } Vect_line_prune(APoints); if (APoints->n_points < 2) /* Line of length zero, delete if boundary ? */ continue; total_input += APoints->n_points; /* copy points */ Vect_reset_line(Points); Vect_append_points(Points, APoints, GV_FORWARD); loop_support = 0; if (!loop_support_flag->answer) { int n1, n2; Vect_get_line_nodes(&Out, i, &n1, &n2); if (n1 == n2) { if (Vect_get_node_n_lines(&Out, n1) == 2) { if (abs(Vect_get_node_line(&Out, n1, 0)) == i && abs(Vect_get_node_line(&Out, n1, 1)) == i) loop_support = 1; } } } for (iter = 0; iter < iterations; iter++) { switch (method) { case DOUGLAS: douglas_peucker(Points, thresh, with_z); break; case DOUGLAS_REDUCTION: douglas_peucker_reduction(Points, thresh, reduction, with_z); break; case LANG: lang(Points, thresh, look_ahead, with_z); break; case VERTEX_REDUCTION: vertex_reduction(Points, thresh, with_z); break; case REUMANN: reumann_witkam(Points, thresh, with_z); break; case BOYLE: boyle(Points, look_ahead, loop_support, with_z); break; case SLIDING_AVERAGING: sliding_averaging(Points, slide, look_ahead, loop_support, with_z); break; case DISTANCE_WEIGHTING: distance_weighting(Points, slide, look_ahead, loop_support, with_z); break; case CHAIKEN: chaiken(Points, thresh, loop_support, with_z); break; case HERMITE: hermite(Points, thresh, angle_thresh, loop_support, with_z); break; case SNAKES: snakes(Points, alpha, beta, loop_support, with_z); break; } } if (loop_support == 0) { /* safety check, BUG in method if not passed */ if (APoints->x[0] != Points->x[0] || APoints->y[0] != Points->y[0] || APoints->z[0] != Points->z[0]) G_fatal_error(_("Method '%s' did not preserve first point"), method_opt->answer); if (APoints->x[APoints->n_points - 1] != Points->x[Points->n_points - 1] || APoints->y[APoints->n_points - 1] != Points->y[Points->n_points - 1] || APoints->z[APoints->n_points - 1] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve last point"), method_opt->answer); } else { /* safety check, BUG in method if not passed */ if (Points->x[0] != Points->x[Points->n_points - 1] || Points->y[0] != Points->y[Points->n_points - 1] || Points->z[0] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve loop"), method_opt->answer); } Vect_line_prune(Points); /* oversimplified line */ if (Points->n_points < 2) { after = APoints->n_points; n_oversimplified++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } /* check for topology corruption */ else if (type == GV_BOUNDARY) { if (!check_topo(&Out, i, APoints, Points, Cats)) { after = APoints->n_points; not_modified_boundaries++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } else after = Points->n_points; } else { /* type == GV_LINE */ Vect_rewrite_line(&Out, i, type, Points, Cats); after = Points->n_points; } total_output += after; } if (not_modified_boundaries > 0) G_warning(_("%d boundaries were not modified because modification would damage topology"), not_modified_boundaries); if (n_oversimplified > 0) G_warning(_("%d lines/boundaries were not modified due to over-simplification"), n_oversimplified); G_message("-----------------------------------------------------"); /* make sure that clean topo is built at the end */ Vect_build_partial(&Out, GV_BUILD_NONE); if (error_out->answer) Vect_build_partial(&Error, GV_BUILD_NONE); } Vect_build(&Out); if (error_out->answer) Vect_build(&Error); Vect_close(&In); Vect_close(&Out); if (error_out->answer) Vect_close(&Error); G_message("-----------------------------------------------------"); if (total_input != 0 && total_input != total_output) G_done_msg(_("Number of vertices for selected features %s from %d to %d (%d%% remaining)"), simplification ? _("reduced") : _("changed"), total_input, total_output, (total_output * 100) / total_input); else G_done_msg(" "); exit(EXIT_SUCCESS); }
int report(struct Map_info *In, int afield, int nfield, int action) { int i, j, k, line, ltype, nnodes; int cat_line, cat_node[2]; struct line_cats *Cats, *Cats2; int node; double x, y, z; Cats = Vect_new_cats_struct(); Cats2 = Vect_new_cats_struct(); if (action == TOOL_REPORT) { /* For all lines find categories for points on nodes */ for (i = 1; i <= Vect_get_num_lines(In); i++) { ltype = Vect_read_line(In, NULL, Cats, i); if (ltype != GV_LINE) continue; cat_line = 0; if (!Vect_cat_get(Cats, afield, &cat_line)) G_warning(_("Line %d has no category"), i); cat_node[0] = cat_node[1] = 0; for (j = 0; j < 2; j++) { if (j == 0) Vect_get_line_nodes(In, i, &node, NULL); else Vect_get_line_nodes(In, i, NULL, &node); Vect_get_node_coor(In, node, &x, &y, &z); nnodes = 0; for (k = 0; k < Vect_get_node_n_lines(In, node); k++) { line = abs(Vect_get_node_line(In, node, k)); ltype = Vect_read_line(In, NULL, Cats, line); if (ltype != GV_POINT) continue; Vect_cat_get(Cats, nfield, &(cat_node[j])); nnodes++; } if (nnodes == 0) G_warning(_("Point not found: %.3lf %.3lf %.3lf line category: %d"), x, y, z, cat_line); else if (nnodes > 1) G_warning(_("%d points found: %.3lf %.3lf %.3lf line category: %d"), nnodes, x, y, z, cat_line); } fprintf(stdout, "%d %d %d\n", cat_line, cat_node[0], cat_node[1]); } } else { /* node report */ int nnodes, node; nnodes = Vect_get_num_nodes(In); for (node = 1; node <= nnodes; node++) { int nelem, elem, type, i, j, k, l; nelem = Vect_get_node_n_lines(In, node); /* Loop through all points */ for (i = 0; i < nelem; i++) { elem = abs(Vect_get_node_line(In, node, i)); type = Vect_read_line(In, NULL, Cats, elem); if (type != GV_POINT) continue; /* Loop through all cats of point */ for (j = 0; j < Cats->n_cats; j++) { if (Cats->field[j] == nfield) { int count = 0; fprintf(stdout, "%d ", Cats->cat[j]); /* Loop through all lines */ for (k = 0; k < nelem; k++) { elem = abs(Vect_get_node_line(In, node, k)); type = Vect_read_line(In, NULL, Cats2, elem); if (!(type & GV_LINES)) continue; /* Loop through all cats of line */ for (l = 0; l < Cats2->n_cats; l++) { if (Cats2->field[l] == afield) { if (count > 0) fprintf(stdout, ","); fprintf(stdout, "%d", Cats2->cat[l]); count++; } } } fprintf(stdout, "\n"); } } } } } return 0; }
/* Called by Vect_remove_bridges() and Vect_chtype_bridges(): chtype = 0 -> works like Vect_remove_bridges() chtype = 1 -> works like Vect_chtype_bridges() Algorithm: Go thorough all lines, if both sides of the line have left and side 0 (candidate) do this check: follow adjacent lines in one direction (nearest to the right at the end node), if we reach this line again without dangle in the way, but with this line traversed from other side it is a bridge. List of all lines in chain is created during the cycle. */ void remove_bridges(struct Map_info *Map, int chtype, struct Map_info *Err) { int i, type, nlines, line; int left, right, node1, node2, current_line, next_line; int bridges_removed = 0; /* number of removed bridges */ int lines_removed = 0; /* number of lines removed */ char *lmsg; struct Plus_head *Plus; struct line_pnts *Points; struct line_cats *Cats; struct ilist *CycleList; struct ilist *BridgeList; int dangle, other_side; if (chtype) lmsg = "changed lines"; else lmsg = "removed lines"; Plus = &(Map->plus); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); CycleList = Vect_new_list(); BridgeList = Vect_new_list(); nlines = Vect_get_num_lines(Map); G_debug(1, "nlines = %d", nlines); for (line = 1; line <= nlines; line++) { G_percent(line, nlines, 1); if (!Vect_line_alive(Map, line)) continue; type = Vect_read_line(Map, NULL, NULL, line); if (!(type & GV_BOUNDARY)) continue; Vect_get_line_areas(Map, line, &left, &right); if (left != 0 || right != 0) continue; /* Cannot be bridge */ G_debug(2, "line %d - bridge candidate", line); Vect_get_line_nodes(Map, line, &node1, &node2); if (abs(node1) == abs(node2)) continue; /* either zero length or loop -> cannot be a bridge */ current_line = -line; /* we start with negative (go forward, node2 ) */ dangle = 0; other_side = 0; Vect_reset_list(CycleList); Vect_reset_list(BridgeList); while (1) { next_line = dig_angle_next_line(Plus, current_line, GV_RIGHT, GV_BOUNDARY); /* Add this line to the list */ /* TODO: Vect_val_in_list() and Vect_list_append() behave O(n) * change to O(log n) */ if (Vect_val_in_list(CycleList, abs(next_line))) /* other side -> bridge chain */ Vect_list_append(BridgeList, abs(next_line)); else dig_list_add(CycleList, abs(next_line)); /* not in list, can add new line fast */ if (abs(next_line) == abs(current_line)) { G_debug(4, " dangle -> no bridge"); dangle = 1; break; } if (abs(next_line) == line) { /* start line reached */ /* which side */ if (next_line < 0) { /* other side (connected by node 2) */ G_debug(5, " other side reached"); other_side = 1; } else { /* start side */ break; } } current_line = -next_line; /* change the sign to look at the next node in following cycle */ } if (!dangle && other_side) { G_debug(3, " line %d is part of bridge chain", line); for (i = 0; i < BridgeList->n_values; i++) { Vect_read_line(Map, Points, Cats, BridgeList->value[i]); if (Err) { Vect_write_line(Err, GV_BOUNDARY, Points, Cats); } if (!chtype) Vect_delete_line(Map, BridgeList->value[i]); else Vect_rewrite_line(Map, BridgeList->value[i], GV_LINE, Points, Cats); lines_removed++; } bridges_removed++; } } G_verbose_message("Removed lines: %d", lines_removed); G_verbose_message("Removed bridges: %d", bridges_removed); }
/* Calculate costs for MST on given set of terminals. * If AList / NList is not NULL, list of arcs / nodes in MST is created * Note: qsort() from more (say >30) terminals, takes long time (most of mst()). * To improve the speed, there are used two sorted queues of costs: * 1. for all combinations of in trms * 2. from 'sp' to all other terminals * Because 1. is sorted only if new sp as added to list of terminals, * and 2. is much shorter than 1., a lot of time is saved. */ int mst(struct Map_info *Map, int *trms, int ntrms, /* array of terminal, number of terminals */ double *cst, double max_cst, /* cost, maximum cost */ struct ilist *AList, struct ilist *NList, /* list of arcs/nodes in ST */ int sp, /* Steiner point (node) to be tested with terminals, (0 = ignore) */ int rebuild) { /* rebuild the sorted list of costs for terminals */ int i, j, node1, node2, com1, com2, t1, t2, line; static int k; int tcpos, scpos; /* current position in the term_costs / sp_costs */ double tcst; struct ilist *List; int nsteps, quse; int nall; /* number of terminals + sp ( if used ) */ if (AList != NULL) { Vect_reset_list(AList); } List = Vect_new_list(); /* Create sorted array for all combinations of terms */ if (rebuild) { k = 0; for (i = 0; i < ntrms; i++) { for (j = i + 1; j < ntrms; j++) { term_costs[k].term1 = i; term_costs[k].term2 = j; get_node_costs(trms[i], trms[j], &tcst); term_costs[k].cost = tcst; k++; } } qsort((void *)term_costs, k, sizeof(COST), cmp); /* this takes most of a time in mst() */ for (i = 0; i < k; i++) { G_debug(3, " %d - %d cost = %f\n", term_costs[i].term1, term_costs[i].term2, term_costs[i].cost); } } /* Create sorted array for all combinations of sp -> terms */ if (sp > 0) { for (i = 0; i < ntrms; i++) { sp_costs[i].term1 = -1; /* not needed */ sp_costs[i].term2 = i; get_node_costs(sp, trms[i], &tcst); sp_costs[i].cost = tcst; } qsort((void *)sp_costs, ntrms, sizeof(COST), cmp); for (i = 0; i < ntrms; i++) { G_debug(3, " %d - %d cost = %f\n", sp_costs[i].term1, sp_costs[i].term2, sp_costs[i].cost); } } tcst = 0; /* MST has number_of_terminals-1 arcs */ if (sp > 0) { nall = ntrms + 1; nsteps = ntrms; /* i.e. + one StP */ } else { nall = ntrms; nsteps = ntrms - 1; } G_debug(1, "nall = %d\n", nall); for (i = 0; i < nall; i++) comps[i] = 0; tcpos = 0; scpos = 0; G_debug(2, "nsteps = %d\n", nsteps); for (i = 0; i < nsteps; i++) { G_debug(2, "step = %d\n", i); /* Take the best (lowest costs, no cycle) from both queues */ /* For both queues go to next lowest costs without cycle */ /* treminal costs */ for (j = tcpos; j < k; j++) { t1 = term_costs[j].term1; t2 = term_costs[j].term2; com1 = comps[t1]; com2 = comps[t2]; if (com1 != com2 || com1 == 0) { /* will not create cycle -> candidate */ tcpos = j; break; } } if (j == k) { /* arc without cycle not found */ tcpos = -1; } /* StP costs */ if (sp > 0) { for (j = scpos; j < ntrms; j++) { t1 = ntrms; /* StP is on first fre position */ t2 = sp_costs[j].term2; com1 = comps[t1]; com2 = comps[t2]; G_debug(3, "scpos: j = %d comps(%d) = %d coms(%d) = %d\n", j, t1, com1, t2, com2); if (com1 != com2 || com1 == 0) { /* will not create cycle -> candidate */ scpos = j; G_debug(3, " ok -> scpos = %d\n", scpos); break; } } if (j == ntrms) { /* arc without cycle not found */ scpos = -1; } } else { scpos = -1; } /* Do not access invalid items even for debugging */ if (tcpos != -1 && scpos != -1) G_debug(3, "tcost = %f, scost = %f\n", term_costs[tcpos].cost, sp_costs[scpos].cost); /* Now we have positions set on lowest costs in each queue or -1 if no more/not used */ if (tcpos >= 0 && scpos >= 0) { if (term_costs[tcpos].cost < sp_costs[scpos].cost) quse = 1; /* use terms queue */ else quse = 2; /* use sp queue */ } else if (tcpos >= 0) { quse = 1; /* use terms queue */ } else { quse = 2; /* use sp queue */ } /* Now we know from which queue take next arc -> add arc to components */ if (quse == 1) { t1 = term_costs[tcpos].term1; t2 = term_costs[tcpos].term2; tcst += term_costs[tcpos].cost; tcpos++; } else { t1 = ntrms; t2 = sp_costs[scpos].term2; tcst += sp_costs[scpos].cost; scpos++; } G_debug(3, "quse = %d t1 = %d t2 = %d\n", quse, t1, t2); G_debug(3, "tcst = %f (max = %f)\n", tcst, max_cst); com1 = comps[t1]; com2 = comps[t2]; comps[t1] = i + 1; comps[t2] = i + 1; G_debug(3, "comps(%d) = %d coms(%d) = %d\n", t1, i + 1, t2, i + 1); /* reset connected branches */ for (j = 0; j < nall; j++) { if (comps[j] == com1 && com1 != 0) comps[j] = i + 1; if (comps[j] == com2 && com2 != 0) comps[j] = i + 1; } if (tcst > max_cst) { G_debug(3, "cost > max -> return\n"); *cst = PORT_DOUBLE_MAX; return 1; } /* add to list of arcs */ if (AList != NULL) { node1 = trms[t1]; node2 = trms[t2]; Vect_net_shortest_path(Map, node1, node2, List, NULL); for (j = 0; j < List->n_values; j++) { Vect_list_append(AList, abs(List->value[j])); } } } /* create list of nodes */ if (NList != NULL) { Vect_reset_list(NList); for (i = 0; i < AList->n_values; i++) { line = AList->value[i]; Vect_get_line_nodes(Map, line, &node1, &node2); Vect_list_append(NList, node1); Vect_list_append(NList, node2); } } *cst = tcst; Vect_destroy_list(List); return 1; }
int main(int argc, char **argv) { int i, j, ret, centre, line, centre1, centre2; int nlines, nnodes, type, ltype, afield, nfield, geo, cat; int node, node1, node2; double cost, e1cost, e2cost, n1cost, n2cost, s1cost, s2cost, l, l1, l2; struct Option *map, *output; struct Option *afield_opt, *nfield_opt, *afcol, *abcol, *ncol, *type_opt, *term_opt; struct Flag *geo_f; struct GModule *module; char *mapset; struct Map_info Map, Out; struct cat_list *catlist; CENTER *Centers = NULL; int acentres = 0, ncentres = 0; NODE *Nodes; struct line_cats *Cats; struct line_pnts *Points, *SPoints; G_gisinit(argv[0]); module = G_define_module(); module->keywords = _("vector, network, allocation"); module->label = _("Allocate subnets for nearest centres (direction from centre)."); module->description = _("Centre node must be opened (costs >= 0). " "Costs of centre node are used in calculation"); map = G_define_standard_option(G_OPT_V_INPUT); output = G_define_standard_option(G_OPT_V_OUTPUT); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary"; type_opt->answer = "line,boundary"; type_opt->description = _("Arc type"); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "alayer"; afield_opt->answer = "1"; afield_opt->description = _("Arc layer"); nfield_opt = G_define_standard_option(G_OPT_V_FIELD); nfield_opt->key = "nlayer"; nfield_opt->answer = "2"; nfield_opt->description = _("Node layer"); afcol = G_define_option(); afcol->key = "afcolumn"; afcol->type = TYPE_STRING; afcol->required = NO; afcol->description = _("Arc forward/both direction(s) cost column (number)"); abcol = G_define_option(); abcol->key = "abcolumn"; abcol->type = TYPE_STRING; abcol->required = NO; abcol->description = _("Arc backward direction cost column (number)"); ncol = G_define_option(); ncol->key = "ncolumn"; ncol->type = TYPE_STRING; ncol->required = NO; ncol->description = _("Node cost column (number)"); term_opt = G_define_standard_option(G_OPT_V_CATS); term_opt->key = "ccats"; term_opt->required = YES; term_opt->description = _("Categories of centres (points on nodes) to which net " "will be allocated, " "layer for this categories is given by nlayer option"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); Vect_check_input_output_name(map->answer, output->answer, GV_FATAL_EXIT); Cats = Vect_new_cats_struct(); Points = Vect_new_line_struct(); SPoints = Vect_new_line_struct(); type = Vect_option_to_types(type_opt); afield = atoi(afield_opt->answer); nfield = atoi(nfield_opt->answer); catlist = Vect_new_cat_list(); Vect_str_to_cat_list(term_opt->answer, catlist); if (geo_f->answer) geo = 1; else geo = 0; mapset = G_find_vector2(map->answer, NULL); if (mapset == NULL) G_fatal_error(_("Vector map <%s> not found"), map->answer); Vect_set_open_level(2); Vect_open_old(&Map, map->answer, mapset); /* Build graph */ Vect_net_build_graph(&Map, type, afield, nfield, afcol->answer, abcol->answer, ncol->answer, geo, 0); nnodes = Vect_get_num_nodes(&Map); /* Create list of centres based on list of categories */ for (node = 1; node <= nnodes; node++) { nlines = Vect_get_node_n_lines(&Map, node); for (j = 0; j < nlines; j++) { line = abs(Vect_get_node_line(&Map, node, j)); ltype = Vect_read_line(&Map, NULL, Cats, line); if (!(ltype & GV_POINT)) continue; if (!(Vect_cat_get(Cats, nfield, &cat))) continue; if (Vect_cat_in_cat_list(cat, catlist)) { Vect_net_get_node_cost(&Map, node, &n1cost); if (n1cost == -1) { /* closed */ G_warning("Centre at closed node (costs = -1) ignored"); } else { if (acentres == ncentres) { acentres += 1; Centers = (CENTER *) G_realloc(Centers, acentres * sizeof(CENTER)); } Centers[ncentres].cat = cat; Centers[ncentres].node = node; G_debug(2, "centre = %d node = %d cat = %d", ncentres, node, cat); ncentres++; } } } } G_message(_("Number of centres: [%d] (nlayer: [%d])"), ncentres, nfield); if (ncentres == 0) G_warning(_("Not enough centres for selected nlayer. " "Nothing will be allocated.")); /* alloc and reset space for all nodes */ Nodes = (NODE *) G_calloc((nnodes + 1), sizeof(NODE)); for (i = 1; i <= nnodes; i++) { Nodes[i].centre = -1; } /* Fill Nodes by neares centre and costs from that centre */ G_message(_("Calculating costs from centres ...")); for (centre = 0; centre < ncentres; centre++) { G_percent(centre, ncentres, 1); node1 = Centers[centre].node; Vect_net_get_node_cost(&Map, node1, &n1cost); G_debug(2, "centre = %d node = %d cat = %d", centre, node1, Centers[centre].cat); for (node2 = 1; node2 <= nnodes; node2++) { G_debug(5, " node1 = %d node2 = %d", node1, node2); Vect_net_get_node_cost(&Map, node2, &n2cost); if (n2cost == -1) { continue; } /* closed, left it as not attached */ ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &cost); if (ret == -1) { continue; } /* node unreachable */ /* We must add centre node costs (not calculated by Vect_net_shortest_path() ), but * only if centre and node are not identical, because at the end node cost is add later */ if (node1 != node2) cost += n1cost; G_debug(5, "Arc nodes: %d %d cost: %f (x old cent: %d old cost %f", node1, node2, cost, Nodes[node2].centre, Nodes[node2].cost); if (Nodes[node2].centre == -1 || cost < Nodes[node2].cost) { Nodes[node2].cost = cost; Nodes[node2].centre = centre; } } } G_percent(1, 1, 1); /* Write arcs to new map */ Vect_open_new(&Out, output->answer, Vect_is_3d(&Map)); Vect_hist_command(&Out); nlines = Vect_get_num_lines(&Map); for (line = 1; line <= nlines; line++) { ltype = Vect_read_line(&Map, Points, NULL, line); if (!(ltype & type)) { continue; } Vect_get_line_nodes(&Map, line, &node1, &node2); centre1 = Nodes[node1].centre; centre2 = Nodes[node2].centre; s1cost = Nodes[node1].cost; s2cost = Nodes[node2].cost; G_debug(3, "Line %d:", line); G_debug(3, "Arc centres: %d %d (nodes: %d %d)", centre1, centre2, node1, node2); Vect_net_get_node_cost(&Map, node1, &n1cost); Vect_net_get_node_cost(&Map, node2, &n2cost); Vect_net_get_line_cost(&Map, line, GV_FORWARD, &e1cost); Vect_net_get_line_cost(&Map, line, GV_BACKWARD, &e2cost); G_debug(3, " s1cost = %f n1cost = %f e1cost = %f", s1cost, n1cost, e1cost); G_debug(3, " s2cost = %f n2cost = %f e2cost = %f", s2cost, n2cost, e2cost); Vect_reset_cats(Cats); /* First check if arc is reachable from at least one side */ if ((centre1 != -1 && n1cost != -1 && e1cost != -1) || (centre2 != -1 && n2cost != -1 && e2cost != -1)) { /* Line is reachable at least from one side */ G_debug(3, " -> arc is reachable"); if (centre1 == centre2) { /* both nodes in one area -> whole arc in one area */ if (centre1 != -1) cat = Centers[centre1].cat; /* line reachable */ else cat = Centers[centre2].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, Points, Cats); } else { /* each node in different area */ /* Check if direction is reachable */ if (centre1 == -1 || n1cost == -1 || e1cost == -1) { /* closed from first node */ G_debug(3, " -> arc is not reachable from 1. node -> alloc to 2. node"); cat = Centers[centre2].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, Points, Cats); continue; } else if (centre2 == -1 || n2cost == -1 || e2cost == -1) { /* closed from second node */ G_debug(3, " -> arc is not reachable from 2. node -> alloc to 1. node"); cat = Centers[centre1].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, Points, Cats); continue; } /* Now we know that arc is reachable from both sides */ /* Add costs of node to starting costs */ s1cost += n1cost; s2cost += n2cost; /* Check if s1cost + e1cost <= s2cost or s2cost + e2cost <= s1cost ! * Note this check also possibility of (e1cost + e2cost) = 0 */ if (s1cost + e1cost <= s2cost) { /* whole arc reachable from node1 */ cat = Centers[centre1].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, Points, Cats); } else if (s2cost + e2cost <= s1cost) { /* whole arc reachable from node2 */ cat = Centers[centre2].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, Points, Cats); } else { /* split */ /* Calculate relative costs - we expect that costs along the line do not change */ l = Vect_line_length(Points); e1cost /= l; e2cost /= l; G_debug(3, " -> s1cost = %f e1cost = %f", s1cost, e1cost); G_debug(3, " -> s2cost = %f e2cost = %f", s2cost, e2cost); /* Costs from both centres to the splitting point must be equal: * s1cost + l1 * e1cost = s2cost + l2 * e2cost */ l1 = (l * e2cost - s1cost + s2cost) / (e1cost + e2cost); l2 = l - l1; G_debug(3, "l = %f l1 = %f l2 = %f", l, l1, l2); /* First segment */ ret = Vect_line_segment(Points, 0, l1, SPoints); if (ret == 0) { G_warning(_("Cannot get line segment, segment out of line")); } else { cat = Centers[centre1].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, SPoints, Cats); } /* Second segment */ ret = Vect_line_segment(Points, l1, l, SPoints); if (ret == 0) { G_warning(_("Cannot get line segment, segment out of line")); } else { Vect_reset_cats(Cats); cat = Centers[centre2].cat; Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, ltype, SPoints, Cats); } } } } else { /* arc is not reachable */ G_debug(3, " -> arc is not reachable"); Vect_write_line(&Out, ltype, Points, Cats); } } Vect_build(&Out); /* Free, ... */ G_free(Nodes); G_free(Centers); Vect_close(&Map); Vect_close(&Out); exit(EXIT_SUCCESS); }
int main(int argc, char **argv) { int i, j, k, ret, city, city1; int nlines, type, ltype, afield, tfield, geo, cat; int node, node1, node2, line; struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *type_opt, *term_opt; struct Flag *geo_f; struct GModule *module; char *mapset; struct Map_info Map, Out; struct ilist *TList; /* list of terminal nodes */ struct ilist *List; struct ilist *StArcs; /* list of arcs on Steiner tree */ struct ilist *StNodes; /* list of nodes on Steiner tree */ double cost, tmpcost, tcost; struct cat_list *Clist; struct line_cats *Cats; struct line_pnts *Points; /* Initialize the GIS calls */ G_gisinit(argv[0]); module = G_define_module(); module->keywords = _("vector, network, salesman"); module->label = _("Creates a cycle connecting given nodes (Traveling salesman problem)."); module->description = _("Note that TSP is NP-hard, heuristic algorithm is used by " "this module and created cycle may be sub optimal"); map = G_define_standard_option(G_OPT_V_INPUT); output = G_define_standard_option(G_OPT_V_OUTPUT); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary"; type_opt->answer = "line,boundary"; type_opt->description = _("Arc type"); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "alayer"; afield_opt->description = _("Arc layer"); tfield_opt = G_define_standard_option(G_OPT_V_FIELD); tfield_opt->key = "nlayer"; tfield_opt->answer = "2"; tfield_opt->description = _("Node layer (used for cities)"); afcol = G_define_option(); afcol->key = "acolumn"; afcol->type = TYPE_STRING; afcol->required = NO; afcol->description = _("Arcs' cost column (for both directions)"); term_opt = G_define_standard_option(G_OPT_V_CATS); term_opt->key = "ccats"; term_opt->required = YES; term_opt->description = _("Categories of points ('cities') on nodes " "(layer is specified by nlayer)"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); Cats = Vect_new_cats_struct(); Points = Vect_new_line_struct(); type = Vect_option_to_types(type_opt); afield = atoi(afield_opt->answer); TList = Vect_new_list(); List = Vect_new_list(); StArcs = Vect_new_list(); StNodes = Vect_new_list(); Clist = Vect_new_cat_list(); tfield = atoi(tfield_opt->answer); Vect_str_to_cat_list(term_opt->answer, Clist); G_debug(1, "Imput categories:\n"); for (i = 0; i < Clist->n_ranges; i++) { G_debug(1, "%d - %d\n", Clist->min[i], Clist->max[i]); } if (geo_f->answer) geo = 1; else geo = 0; Vect_check_input_output_name(map->answer, output->answer, GV_FATAL_EXIT); mapset = G_find_vector2(map->answer, NULL); if (mapset == NULL) G_fatal_error(_("Vector map <%s> not found"), map->answer); Vect_set_open_level(2); Vect_open_old(&Map, map->answer, mapset); nnodes = Vect_get_num_nodes(&Map); /* Create list of terminals based on list of categories */ for (i = 1; i <= nnodes; i++) { nlines = Vect_get_node_n_lines(&Map, i); for (j = 0; j < nlines; j++) { line = abs(Vect_get_node_line(&Map, i, j)); ltype = Vect_read_line(&Map, NULL, Cats, line); if (!(ltype & GV_POINT)) continue; if (!(Vect_cat_get(Cats, tfield, &cat))) continue; if (Vect_cat_in_cat_list(cat, Clist)) { Vect_list_append(TList, i); } } } ncities = TList->n_values; G_message(_("Number of cities: [%d]"), ncities); if (ncities < 2) G_fatal_error(_("Not enough cities (< 2)")); /* Alloc memory */ cities = (int *)G_malloc(ncities * sizeof(int)); cused = (int *)G_malloc(ncities * sizeof(int)); for (i = 0; i < ncities; i++) { G_debug(1, "%d\n", TList->value[i]); cities[i] = TList->value[i]; cused[i] = 0; /* not in cycle */ } costs = (COST **) G_malloc(ncities * sizeof(COST *)); for (i = 0; i < ncities; i++) { costs[i] = (COST *) G_malloc(ncities * sizeof(COST)); } cycle = (int *)G_malloc((ncities + 1) * sizeof(int)); /* + 1 is for output cycle */ /* Build graph */ Vect_net_build_graph(&Map, type, afield, 0, afcol->answer, NULL, NULL, geo, 0); /* Create sorted lists of costs */ for (i = 0; i < ncities; i++) { k = 0; for (j = 0; j < ncities; j++) { if (i == j) continue; ret = Vect_net_shortest_path(&Map, cities[i], cities[j], NULL, &cost); if (ret == -1) G_fatal_error(_("Destination node [%d] is unreachable " "from node [%d]"), cities[i], cities[j]); costs[i][k].city = j; costs[i][k].cost = cost; k++; } qsort((void *)costs[i], k, sizeof(COST), cmp); } /* debug: print sorted */ for (i = 0; i < ncities; i++) { for (j = 0; j < ncities - 1; j++) { city = costs[i][j].city; G_debug(2, "%d -> %d = %f\n", cities[i], cities[city], costs[i][j].cost); } } /* find 2 cities with largest distance */ cost = -1; for (i = 0; i < ncities; i++) { tmpcost = costs[i][ncities - 2].cost; if (tmpcost > cost) { cost = tmpcost; city = i; } } G_debug(2, "biggest costs %d - %d\n", city, costs[city][ncities - 2].city); /* add this 2 cities to array */ add_city(city, -1); add_city(costs[city][ncities - 2].city, 0); /* In each step, find not used city, with biggest cost to any used city, and insert * into cycle between 2 nearest nodes */ for (i = 0; i < ncities - 2; i++) { cost = -1; G_debug(2, "---- %d ----\n", i); for (j = 0; j < ncities; j++) { if (cused[j] == 1) continue; tmpcost = 0; for (k = 0; k < ncities - 1; k++) { G_debug(2, "? %d (%d) - %d (%d) \n", j, cnode(j), costs[j][k].city, cnode(costs[j][k].city)); if (!cused[costs[j][k].city]) continue; /* only used */ tmpcost += costs[j][k].cost; break; /* first nearest */ } G_debug(2, " cost = %f x %f\n", tmpcost, cost); if (tmpcost > cost) { cost = tmpcost; city = j; } } G_debug(2, "add %d\n", city); /* add to cycle on lovest costs */ cycle[ncyc] = cycle[0]; /* tmp for cycle */ cost = PORT_DOUBLE_MAX; for (j = 0; j < ncyc; j++) { node1 = cities[cycle[j]]; node2 = cities[cycle[j + 1]]; ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &tcost); tmpcost = -tcost; node1 = cities[cycle[j]]; node2 = cities[city]; ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &tcost); tmpcost += tcost; node1 = cities[cycle[j + 1]]; node2 = cities[city]; ret = Vect_net_shortest_path(&Map, node1, node2, NULL, &tcost); tmpcost += tcost; G_debug(2, "? %d - %d cost = %f x %f\n", node1, node2, tmpcost, cost); if (tmpcost < cost) { city1 = j; cost = tmpcost; } } add_city(city, city1); } /* Print */ G_debug(2, "Cycle:\n"); for (i = 0; i < ncities; i++) { G_debug(2, "%d: %d: %d\n", i, cycle[i], cities[cycle[i]]); } /* Create list of arcs */ cycle[ncities] = cycle[0]; for (i = 0; i < ncities; i++) { node1 = cities[cycle[i]]; node2 = cities[cycle[i + 1]]; G_debug(2, " %d -> %d\n", node1, node2); ret = Vect_net_shortest_path(&Map, node1, node2, List, NULL); for (j = 0; j < List->n_values; j++) { line = abs(List->value[j]); Vect_list_append(StArcs, line); Vect_get_line_nodes(&Map, line, &node1, &node2); Vect_list_append(StNodes, node1); Vect_list_append(StNodes, node2); } } /* Write arcs to new map */ Vect_open_new(&Out, output->answer, Vect_is_3d(&Map)); Vect_hist_command(&Out); fprintf(stdout, "\nCycle:\n"); fprintf(stdout, "Arcs' categories (layer %d, %d arcs):\n", afield, StArcs->n_values); for (i = 0; i < StArcs->n_values; i++) { line = StArcs->value[i]; ltype = Vect_read_line(&Map, Points, Cats, line); Vect_write_line(&Out, ltype, Points, Cats); Vect_cat_get(Cats, afield, &cat); if (i > 0) printf(","); fprintf(stdout, "%d", cat); } fprintf(stdout, "\n\n"); fprintf(stdout, "Nodes' categories (layer %d, %d nodes):\n", tfield, StNodes->n_values); k = 0; for (i = 0; i < StNodes->n_values; i++) { node = StNodes->value[i]; nlines = Vect_get_node_n_lines(&Map, node); for (j = 0; j < nlines; j++) { line = abs(Vect_get_node_line(&Map, node, j)); ltype = Vect_read_line(&Map, Points, Cats, line); if (!(ltype & GV_POINT)) continue; if (!(Vect_cat_get(Cats, tfield, &cat))) continue; Vect_write_line(&Out, ltype, Points, Cats); if (k > 0) fprintf(stdout, ","); fprintf(stdout, "%d", cat); k++; } } fprintf(stdout, "\n\n"); Vect_build(&Out); /* Free, ... */ Vect_destroy_list(StArcs); Vect_destroy_list(StNodes); Vect_close(&Map); Vect_close(&Out); exit(EXIT_SUCCESS); }
int move_line_update(void *closure, int sxn, int syn, int button) { struct move_line *ml = closure; double x = D_d_to_u_col(sxn); double y = D_d_to_u_row(syn); G_debug(3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y); if (ml->last_line > 0) { display_line(ml->last_line, SYMB_DEFAULT, 1); } if (button == 3) return 1; if (button == 1) { /* Select / new location */ int type; if (ml->last_line == 0) { /* Select line */ ml->line = Vect_find_line(&Map, x, y, 0, GV_POINT | GV_CENTROID, ml->thresh, 0, 0); G_debug(2, "point found = %d", ml->line); if (ml->line == 0) ml->line = Vect_find_line(&Map, x, y, 0, GV_LINE | GV_BOUNDARY, ml->thresh, 0, 0); G_debug(2, "line found = %d", ml->line); /* Display new selected line if any */ if (ml->line > 0) { display_line(ml->line, SYMB_HIGHLIGHT, 1); /* Find the nearest point on the line */ type = Vect_read_line(&Map, ml->Points, NULL, ml->line); Vect_line_distance(ml->Points, x, y, 0, 0, &ml->xo, &ml->yo, NULL, NULL, NULL, NULL); set_location(D_u_to_d_col(ml->xo), D_u_to_d_row(ml->yo)); i_prompt_buttons(_("New location"), _("Unselect"), _("Quit tool")); } ml->last_line = ml->line; } else { /* Line is already selected */ int node1, node2; int i; display_line(ml->last_line, SYMB_BACKGROUND, 1); Vect_get_line_nodes(&Map, ml->last_line, &node1, &node2); display_node(node1, SYMB_BACKGROUND, 1); display_node(node2, SYMB_BACKGROUND, 1); type = Vect_read_line(&Map, ml->Points, ml->Cats, ml->last_line); for (i = 0; i < ml->Points->n_points; i++) { ml->Points->x[i] = ml->Points->x[i] + x - ml->xo; ml->Points->y[i] = ml->Points->y[i] + y - ml->yo; } Vect_rewrite_line(&Map, ml->last_line, type, ml->Points, ml->Cats); updated_lines_and_nodes_erase_refresh_display(); ml->last_line = 0; } } if (button == 2) { /* Unselect */ if (ml->last_line > 0) { ml->last_line = 0; } } if (ml->last_line == 0) { i_prompt_buttons(_("Select"), "", _("Quit tool")); set_mode(MOUSE_POINT); } else set_mode(MOUSE_LINE); return 0; }
int delete_line_update(void *closure, int sxn, int syn, int button) { struct delete_line *dl = closure; double x = D_d_to_u_col(sxn); double y = D_d_to_u_row(syn); G_debug(3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y); /* Display last highlighted in normal color */ if (dl->last_line > 0) { display_line(dl->last_line, SYMB_DEFAULT, 1); } if (button == 3) /* Quit tool */ return 1; if (button == 1) { /* Confirm / select */ /* Delete last if any */ if (dl->last_line > 0) { int node1, node2; int i; /* Erase line and nodes !!! (because if the line is not connected to any other, nodes will die */ display_line(dl->last_line, SYMB_BACKGROUND, 1); Vect_get_line_nodes(&Map, dl->line, &node1, &node2); G_debug(2, "delete line = %d node1 = %d node2 = %d", dl->last_line, node1, node2); display_node(node1, SYMB_BACKGROUND, 1); display_node(node2, SYMB_BACKGROUND, 1); Vect_read_line(&Map, NULL, dl->Cats, dl->last_line); Vect_delete_line(&Map, dl->last_line); for (i = 0; i < dl->Cats->n_cats; i++) { check_record(dl->Cats->field[i], dl->Cats->cat[i]); } for (i = 0; i < Vect_get_num_updated_lines(&Map); i++) G_debug(2, "Updated line: %d", Vect_get_updated_line(&Map, i)); for (i = 0; i < Vect_get_num_updated_nodes(&Map); i++) G_debug(2, "Updated node: %d", Vect_get_updated_node(&Map, i)); updated_lines_and_nodes_erase_refresh_display(); } /* Find neares point or line */ dl->line = Vect_find_line(&Map, x, y, 0, GV_POINT | GV_CENTROID, dl->thresh, 0, 0); G_debug(2, "point found = %d", dl->line); if (dl->line == 0) dl->line = Vect_find_line(&Map, x, y, 0, GV_LINE | GV_BOUNDARY, dl->thresh, 0, 0); G_debug(2, "line found = %d", dl->line); /* Display new selected line if any */ if (dl->line > 0) { display_line(dl->line, SYMB_HIGHLIGHT, 1); } } else { /* button == 2 -> unselect */ dl->line = 0; } if (dl->line > 0) i_prompt_buttons(_("Confirm and select next"), _("Unselect"), _("Quit tool")); else i_prompt_buttons(_("Select"), _("Unselect"), _("Quit tool")); dl->last_line = dl->line; dl->first = 0; return 0; }
int main(int argc, char *argv[]) { struct Map_info In, Out; static struct line_pnts *Points; struct line_cats *Cats; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out; struct Option *method_opt, *afield_opt, *nfield_opt, *abcol, *afcol, *ncol; struct Flag *add_f; int with_z; int afield, nfield, mask_type; dglGraph_s *graph; int *component, nnodes, type, i, nlines, components, max_cat; char buf[2000], *covered; char *desc; /* Attribute table */ dbString sql; dbDriver *driver; struct field_info *Fi; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("network")); G_add_keyword(_("components")); module->description = _("Computes strongly and weakly connected components in the network."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "arc_layer"; afield_opt->answer = "1"; afield_opt->label = _("Arc layer"); afield_opt->guisection = _("Cost"); nfield_opt = G_define_standard_option(G_OPT_V_FIELD); nfield_opt->key = "node_layer"; nfield_opt->answer = "2"; nfield_opt->label = _("Node layer"); nfield_opt->guisection = _("Cost"); afcol = G_define_standard_option(G_OPT_DB_COLUMN); afcol->key = "arc_column"; afcol->required = NO; afcol->description = _("Arc forward/both direction(s) cost column (number)"); afcol->guisection = _("Cost"); abcol = G_define_standard_option(G_OPT_DB_COLUMN); abcol->key = "arc_backward_column"; abcol->required = NO; abcol->description = _("Arc backward direction cost column (number)"); abcol->guisection = _("Cost"); ncol = G_define_option(); ncol->key = "node_column"; ncol->type = TYPE_STRING; ncol->required = NO; ncol->description = _("Node cost column (number)"); ncol->guisection = _("Cost"); map_out = G_define_standard_option(G_OPT_V_OUTPUT); method_opt = G_define_option(); method_opt->key = "method"; method_opt->type = TYPE_STRING; method_opt->required = YES; method_opt->multiple = NO; method_opt->options = "weak,strong"; desc = NULL; G_asprintf(&desc, "weak;%s;strong;%s", _("Weakly connected components"), _("Strongly connected components")); method_opt->descriptions = desc; method_opt->description = _("Type of components"); add_f = G_define_flag(); add_f->key = 'a'; add_f->description = _("Add points on nodes"); /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); /* TODO: make an option for this */ mask_type = GV_LINE | GV_BOUNDARY; Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (1 > Vect_open_old(&In, map_in->answer, "")) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } /* parse filter option and select appropriate lines */ afield = Vect_get_field_number(&In, afield_opt->answer); nfield = Vect_get_field_number(&In, nfield_opt->answer); if (0 != Vect_net_build_graph(&In, mask_type, afield, nfield, afcol->answer, abcol->answer, ncol->answer, 0, 2)) G_fatal_error(_("Unable to build graph for vector map <%s>"), Vect_get_full_name(&In)); graph = Vect_net_get_graph(&In); nnodes = Vect_get_num_nodes(&In); component = (int *)G_calloc(nnodes + 1, sizeof(int)); covered = (char *)G_calloc(nnodes + 1, sizeof(char)); if (!component || !covered) { G_fatal_error(_("Out of memory")); exit(EXIT_FAILURE); } /* Create table */ Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE); Vect_map_add_dblink(&Out, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database, Fi->driver); db_init_string(&sql); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); sprintf(buf, "create table %s ( cat integer, comp integer)", Fi->table); db_set_string(&sql, buf); G_debug(2, "%s", db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { db_close_database_shutdown_driver(driver); G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&sql)); } if (db_create_index2(driver, Fi->table, GV_KEY_COLUMN) != DB_OK) G_warning(_("Cannot create index")); if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) G_fatal_error(_("Cannot grant privileges on table <%s>"), Fi->table); db_begin_transaction(driver); if (method_opt->answer[0] == 'w') { G_message(_("Computing weakly connected components...")); components = NetA_weakly_connected_components(graph, component); } else { G_message(_("Computing strongly connected components...")); components = NetA_strongly_connected_components(graph, component); } G_debug(3, "Components: %d", components); G_message(_("Writing output...")); Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); nlines = Vect_get_num_lines(&In); max_cat = 1; G_percent(0, nlines, 4); for (i = 1; i <= nlines; i++) { int comp, cat; G_percent(i, nlines, 4); type = Vect_read_line(&In, Points, Cats, i); if (!Vect_cat_get(Cats, afield, &cat)) continue; if (type == GV_LINE || type == GV_BOUNDARY) { int node1, node2; Vect_get_line_nodes(&In, i, &node1, &node2); if (component[node1] == component[node2]) { comp = component[node1]; } else { continue; } } else if (type == GV_POINT) { int node; /* Vect_get_line_nodes(&In, i, &node, NULL); */ node = Vect_find_node(&In, Points->x[0], Points->y[0], Points->z[0], 0, 0); if (!node) continue; comp = component[node]; covered[node] = 1; } else continue; cat = max_cat++; Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, type, Points, Cats); insert_new_record(driver, Fi, &sql, cat, comp); } /*add points on nodes not covered by any point in the network */ if (add_f->answer) { for (i = 1; i <= nnodes; i++) if (!covered[i]) { Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, max_cat); NetA_add_point_on_node(&In, &Out, i, Cats); insert_new_record(driver, Fi, &sql, max_cat++, component[i]); } } db_commit_transaction(driver); db_close_database_shutdown_driver(driver); Vect_close(&In); Vect_build(&Out); Vect_close(&Out); G_done_msg(_("Found %d components."), components); exit(EXIT_SUCCESS); }
/* Start from the first node on a polyline and walk to the other end, collecting the coordinates of each node en route. */ int walk_forward_and_pick_up_coords(struct Map_info *map, int start_line, int ltype, struct line_pnts *points, int *lines_visited, struct line_cats *Cats, int write_cats) { int cat_idx; int line, next_line, n1, n2; int type, node, next_node; struct line_pnts *pnts; struct line_cats *cats_tmp; G_debug(2, " walk_forward() start = %d", start_line); line = start_line; pnts = Vect_new_line_struct(); if (write_cats != NO_CATS) { cats_tmp = Vect_new_cats_struct(); } else { cats_tmp = NULL; } Vect_reset_line(points); /* Pick up first set of coordinates */ lines_visited[line] = 1; if (cats_tmp) type = Vect_read_line(map, pnts, Cats, line); else type = Vect_read_line(map, pnts, NULL, line); Vect_get_line_nodes(map, line, &n1, &n2); next_line = find_next_line(map, line, n1, ltype); if (next_line > 0) { /* continue at start node */ Vect_append_points(points, pnts, GV_BACKWARD); next_node = n1; } else { Vect_append_points(points, pnts, GV_FORWARD); next_line = find_next_line(map, line, n2, ltype); /* check end node */ if (next_line > 0) { next_node = n2; /* continue at end node */ } else { return 1; /* no other line */ } } /* While next line exist append coordinates */ line = next_line; node = next_node; while (line != 0 && line != start_line) { G_debug(2, " line = %d", line); type = Vect_read_line(map, pnts, cats_tmp, line); if (cats_tmp && write_cats == MULTI_CATS) { for (cat_idx = 0; cat_idx < cats_tmp->n_cats; cat_idx++) { Vect_cat_set(Cats, cats_tmp->field[cat_idx], cats_tmp->cat[cat_idx]); } } Vect_get_line_nodes(map, line, &n1, &n2); if (node == n1) { Vect_line_delete_point(pnts, 0); /* delete duplicate nodes */ Vect_append_points(points, pnts, GV_FORWARD); next_node = n2; } else { Vect_line_delete_point(pnts, pnts->n_points - 1); Vect_append_points(points, pnts, GV_BACKWARD); next_node = n1; } lines_visited[line] = 1; /* Find next one */ next_line = find_next_line(map, line, next_node, ltype); line = next_line; node = next_node; } if (cats_tmp) Vect_destroy_cats_struct(cats_tmp); return 1; }
bool QgsGrassFeatureIterator::fetchFeature( QgsFeature& feature ) { if ( mClosed ) return false; feature.setValid( false ); int cat = -1, type = -1, id = -1; QgsFeatureId featureId = -1; QgsDebugMsgLevel( "entered.", 3 ); /* TODO: handle editing if ( P->isEdited() || P->isFrozen() || !P->mValid ) { close(); return false; } */ // TODO: is this necessary? the same is checked below if ( !QgsGrassProvider::isTopoType( mSource->mLayerType ) && ( mSource->mCidxFieldIndex < 0 || mNextCidx >= mSource->mCidxFieldNumCats ) ) { close(); return false; // No features, no features in this layer } bool filterById = mRequest.filterType() == QgsFeatureRequest::FilterFid; // Get next line/area id int found = 0; while ( true ) { QgsDebugMsgLevel( QString( "mNextTopoId = %1" ).arg( mNextTopoId ), 3 ); if ( mSource->mLayerType == QgsGrassProvider::TOPO_POINT || mSource->mLayerType == QgsGrassProvider::TOPO_LINE ) { if ( mNextTopoId > Vect_get_num_lines( mSource->mMap ) ) break; id = mNextTopoId; type = Vect_read_line( mSource->mMap, 0, 0, mNextTopoId++ ); if ( !( type & mSource->mGrassType ) ) continue; featureId = id; } else if ( mSource->mLayerType == QgsGrassProvider::TOPO_NODE ) { if ( mNextTopoId > Vect_get_num_nodes( mSource->mMap ) ) break; id = mNextTopoId; type = 0; mNextTopoId++; featureId = id; } else { if ( mNextCidx >= mSource->mCidxFieldNumCats ) break; Vect_cidx_get_cat_by_index( mSource->mMap, mSource->mCidxFieldIndex, mNextCidx++, &cat, &type, &id ); // Warning: selection array is only of type line/area of current layer -> check type first if ( !( type & mSource->mGrassType ) ) continue; // The 'id' is a unique id of a GRASS geometry object (point, line, area) // but it cannot be used as QgsFeatureId because one geometry object may // represent more features because it may have more categories. featureId = makeFeatureId( id, cat ); } if ( filterById && featureId != mRequest.filterFid() ) continue; // it is correct to use id with mSelection because mSelection is only used // for geometry selection if ( !mSelection[id] ) continue; found = 1; break; } if ( !found ) { close(); return false; // No more features } QgsDebugMsgLevel( QString( "cat = %1 type = %2 id = %3 fatureId = %4" ).arg( cat ).arg( type ).arg( id ).arg( featureId ), 3 ); feature.setFeatureId( featureId ); feature.initAttributes( mSource->mFields.count() ); feature.setFields( &mSource->mFields ); // allow name-based attribute lookups if ( mRequest.flags() & QgsFeatureRequest::NoGeometry ) feature.setGeometry( 0 ); else setFeatureGeometry( feature, id, type ); if ( ! QgsGrassProvider::isTopoType( mSource->mLayerType ) ) { if ( mRequest.flags() & QgsFeatureRequest::SubsetOfAttributes ) setFeatureAttributes( cat, &feature, mRequest.subsetOfAttributes() ); else setFeatureAttributes( cat, &feature ); } else { feature.setAttribute( 0, id ); #if GRASS_VERSION_MAJOR < 7 if ( mSource->mLayerType == QgsGrassProvider::TOPO_POINT || mSource->mLayerType == QgsGrassProvider::TOPO_LINE ) #else /* No more topo points in GRASS 7 */ if ( mSource->mLayerType == QgsGrassProvider::TOPO_LINE ) #endif { feature.setAttribute( 1, QgsGrassProvider::primitiveTypeName( type ) ); int node1, node2; Vect_get_line_nodes( mSource->mMap, id, &node1, &node2 ); feature.setAttribute( 2, node1 ); if ( mSource->mLayerType == QgsGrassProvider::TOPO_LINE ) { feature.setAttribute( 3, node2 ); } } if ( mSource->mLayerType == QgsGrassProvider::TOPO_LINE ) { if ( type == GV_BOUNDARY ) { int left, right; Vect_get_line_areas( mSource->mMap, id, &left, &right ); feature.setAttribute( 4, left ); feature.setAttribute( 5, right ); } } else if ( mSource->mLayerType == QgsGrassProvider::TOPO_NODE ) { QString lines; int nlines = Vect_get_node_n_lines( mSource->mMap, id ); for ( int i = 0; i < nlines; i++ ) { int line = Vect_get_node_line( mSource->mMap, id, i ); if ( i > 0 ) lines += ","; lines += QString::number( line ); } feature.setAttribute( 1, lines ); } } feature.setValid( true ); return true; }
int main(int argc, char *argv[]) { struct Map_info In, Out; static struct line_pnts *Points, *PPoints; struct line_cats *Cats, *TCats; struct ilist *slist; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out; struct Option *catf_opt, *fieldf_opt, *wheref_opt; struct Option *catt_opt, *fieldt_opt, *wheret_opt, *typet_opt; struct Option *afield_opt, *nfield_opt, *abcol, *afcol, *ncol, *atype_opt; struct Flag *geo_f, *segments_f; int with_z, geo, segments; int atype, ttype; struct varray *varrayf, *varrayt; int flayer, tlayer; int afield, nfield; dglGraph_s *graph; struct ilist *nodest; int i, j, nnodes, nlines; int *dst, *nodes_to_features; int from_nr; /* 'from' features not reachable */ dglInt32_t **nxt; struct line_cats **on_path; char *segdir; char buf[2000]; /* Attribute table */ dbString sql; dbDriver *driver; struct field_info *Fi; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("network")); G_add_keyword(_("shortest path")); module->label = _("Computes shortest distance via the network between " "the given sets of features."); module->description = _("Finds the shortest paths from each 'from' point to the nearest 'to' feature " "and various information about this relation are uploaded to the attribute table."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); map_out = G_define_standard_option(G_OPT_V_OUTPUT); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "arc_layer"; afield_opt->answer = "1"; afield_opt->label = _("Arc layer"); afield_opt->guisection = _("Cost"); atype_opt = G_define_standard_option(G_OPT_V_TYPE); atype_opt->key = "arc_type"; atype_opt->options = "line,boundary"; atype_opt->answer = "line,boundary"; atype_opt->label = _("Arc type"); atype_opt->guisection = _("Cost"); nfield_opt = G_define_standard_option(G_OPT_V_FIELD); nfield_opt->key = "node_layer"; nfield_opt->answer = "2"; nfield_opt->label = _("Node layer"); nfield_opt->guisection = _("Cost"); fieldf_opt = G_define_standard_option(G_OPT_V_FIELD); fieldf_opt->key = "from_layer"; fieldf_opt->label = _("From layer number or name"); fieldf_opt->guisection = _("From"); catf_opt = G_define_standard_option(G_OPT_V_CATS); catf_opt->key = "from_cats"; catf_opt->label = _("From category values"); catf_opt->guisection = _("From"); wheref_opt = G_define_standard_option(G_OPT_DB_WHERE); wheref_opt->key = "from_where"; wheref_opt->label = _("From WHERE conditions of SQL statement without 'where' keyword"); wheref_opt->guisection = _("From"); fieldt_opt = G_define_standard_option(G_OPT_V_FIELD); fieldt_opt->key = "to_layer"; fieldt_opt->description = _("To layer number or name"); fieldt_opt->guisection = _("To"); typet_opt = G_define_standard_option(G_OPT_V_TYPE); typet_opt->key = "to_type"; typet_opt->options = "point,line,boundary"; typet_opt->answer = "point"; typet_opt->description = _("To feature type"); typet_opt->guisection = _("To"); catt_opt = G_define_standard_option(G_OPT_V_CATS); catt_opt->key = "to_cats"; catt_opt->label = _("To category values"); catt_opt->guisection = _("To"); wheret_opt = G_define_standard_option(G_OPT_DB_WHERE); wheret_opt->key = "to_where"; wheret_opt->label = _("To WHERE conditions of SQL statement without 'where' keyword"); wheret_opt->guisection = _("To"); afcol = G_define_standard_option(G_OPT_DB_COLUMN); afcol->key = "arc_column"; afcol->required = NO; afcol->description = _("Arc forward/both direction(s) cost column (number)"); afcol->guisection = _("Cost"); abcol = G_define_standard_option(G_OPT_DB_COLUMN); abcol->key = "arc_backward_column"; abcol->required = NO; abcol->description = _("Arc backward direction cost column (number)"); abcol->guisection = _("Cost"); ncol = G_define_standard_option(G_OPT_DB_COLUMN); ncol->key = "node_column"; ncol->required = NO; ncol->description = _("Node cost column (number)"); ncol->guisection = _("Cost"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); segments_f = G_define_flag(); #if 0 /* use this to sync with v.net.path */ segments_f->key = 's'; segments_f->description = _("Write output as original input segments, " "not each path as one line."); #else segments_f->key = 'l'; segments_f->description = _("Write each output path as one line, " "not as original input segments."); #endif /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); atype = Vect_option_to_types(atype_opt); ttype = Vect_option_to_types(typet_opt); Points = Vect_new_line_struct(); PPoints = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); TCats = Vect_new_cats_struct(); slist = G_new_ilist(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (1 > Vect_open_old(&In, map_in->answer, "")) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (geo_f->answer) { geo = 1; if (G_projection() != PROJECTION_LL) G_warning(_("The current projection is not longitude-latitude")); } else geo = 0; #if 0 /* use this to sync with v.net.path */ segments = segments_f->answer; #else segments = !segments_f->answer; #endif nnodes = Vect_get_num_nodes(&In); nlines = Vect_get_num_lines(&In); dst = (int *)G_calloc(nnodes + 1, sizeof(int)); nxt = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); nodes_to_features = (int *)G_calloc(nnodes + 1, sizeof(int)); on_path = (struct line_cats **)G_calloc(nlines + 1, sizeof(struct line_cats *)); segdir = (char *)G_calloc(nlines + 1, sizeof(char)); if (!dst || !nxt || !nodes_to_features || !on_path || !segdir) G_fatal_error(_("Out of memory")); for (i = 1; i <= nlines; i++) { on_path[i] = Vect_new_cats_struct(); segdir[i] = 0; } /*initialise varrays and nodes list appropriatelly */ afield = Vect_get_field_number(&In, afield_opt->answer); nfield = Vect_get_field_number(&In, nfield_opt->answer); flayer = atoi(fieldf_opt->answer); tlayer = atoi(fieldt_opt->answer); if (NetA_initialise_varray(&In, flayer, GV_POINT, wheref_opt->answer, catf_opt->answer, &varrayf) <= 0) { G_fatal_error(_("No 'from' features selected. " "Please check options '%s', '%s', '%s'."), fieldf_opt->key, wheref_opt->key, catf_opt->key); } if (NetA_initialise_varray(&In, tlayer, ttype, wheret_opt->answer, catt_opt->answer, &varrayt) <= 0) { G_fatal_error(_("No 'to' features selected. " "Please check options '%s', '%s', '%s'."), fieldt_opt->key, wheret_opt->key, catt_opt->key); } nodest = Vect_new_list(); NetA_varray_to_nodes(&In, varrayt, nodest, nodes_to_features); if (nodest->n_values == 0) G_fatal_error(_("No 'to' features")); if (0 != Vect_net_build_graph(&In, atype, afield, nfield, afcol->answer, abcol->answer, ncol->answer, geo, 2)) G_fatal_error(_("Unable to build graph for vector map <%s>"), Vect_get_full_name(&In)); graph = Vect_net_get_graph(&In); G_message(_("Distances to 'to' features ...")); NetA_distance_to_points(graph, nodest, dst, nxt); /* Create table */ Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE); Vect_map_add_dblink(&Out, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database, Fi->driver); db_init_string(&sql); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(driver); sprintf(buf, "create table %s ( cat integer, tcat integer, dist double precision)", Fi->table); db_set_string(&sql, buf); G_debug(2, "%s", db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&sql)); } if (db_create_index2(driver, Fi->table, GV_KEY_COLUMN) != DB_OK) G_warning(_("Cannot create index")); if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) G_fatal_error(_("Cannot grant privileges on table <%s>"), Fi->table); db_begin_transaction(driver); Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); G_message(_("Tracing paths from 'from' features ...")); from_nr = 0; for (i = 1; i <= nlines; i++) { if (varrayf->c[i]) { int type = Vect_read_line(&In, Points, Cats, i); int node, tcat, cat; double cost; dglInt32_t *vertex, vertex_id; if (!Vect_cat_get(Cats, flayer, &cat)) continue; if (type & GV_POINTS) { node = Vect_find_node(&In, Points->x[0], Points->y[0], Points->z[0], 0, 0); } else { Vect_get_line_nodes(&In, i, &node, NULL); } if (node < 1) continue; if (dst[node] < 0) { /* unreachable */ from_nr++; continue; } cost = dst[node] / (double)In.dgraph.cost_multip; vertex = dglGetNode(graph, node); vertex_id = node; slist->n_values = 0; while (nxt[vertex_id] != NULL) { int edge_id; edge_id = (int) dglEdgeGet_Id(graph, nxt[vertex_id]); if (segments) { Vect_cat_set(on_path[abs(edge_id)], 1, cat); if (edge_id < 0) { segdir[abs(edge_id)] = 1; } } else G_ilist_add(slist, edge_id); vertex = dglEdgeGet_Tail(graph, nxt[vertex_id]); vertex_id = dglNodeGet_Id(graph, vertex); } G_debug(3, "read line %d, vertex id %d", nodes_to_features[vertex_id], (int)vertex_id); Vect_read_line(&In, NULL, TCats, nodes_to_features[vertex_id]); if (!Vect_cat_get(TCats, tlayer, &tcat)) continue; Vect_write_line(&Out, type, Points, Cats); sprintf(buf, "insert into %s values (%d, %d, %f)", Fi->table, cat, tcat, cost); db_set_string(&sql, buf); G_debug(3, "%s", db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Cannot insert new record: %s"), db_get_string(&sql)); }; if (!segments) { Vect_reset_line(PPoints); for (j = 0; j < slist->n_values; j++) { Vect_read_line(&In, Points, NULL, abs(slist->value[j])); if (slist->value[j] > 0) Vect_append_points(PPoints, Points, GV_FORWARD); else Vect_append_points(PPoints, Points, GV_BACKWARD); PPoints->n_points--; } PPoints->n_points++; Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, GV_LINE, PPoints, Cats); } } } if (segments) { for (i = 1; i <= nlines; i++) { if (on_path[i]->n_cats > 0) { int type; if (segdir[i]) { type = Vect_read_line(&In, PPoints, NULL, i); Vect_reset_line(Points); Vect_append_points(Points, PPoints, GV_BACKWARD); } else type = Vect_read_line(&In, Points, NULL, i); Vect_write_line(&Out, type, Points, on_path[i]); } } } db_commit_transaction(driver); db_close_database_shutdown_driver(driver); Vect_build(&Out); Vect_close(&In); Vect_close(&Out); for (i = 1; i <= nlines; i++) Vect_destroy_cats_struct(on_path[i]); G_free(on_path); G_free(nodes_to_features); G_free(dst); G_free(nxt); G_free(segdir); if (from_nr) G_warning(n_("%d 'from' feature was not reachable", "%d 'from' features were not reachable", from_nr), from_nr); exit(EXIT_SUCCESS); }
/* merge a given line with all other lines of the same type and * with the same categories */ static int merge_line(struct Map_info *Map, int line, struct line_pnts *MPoints, struct line_cats *MCats) { int nlines, i, first, last, next_line, curr_line; int merged = 0, newl = 0; int next_node, direction, node_n_lines, type, ltype, lines_type; static struct ilist *List = NULL; static struct line_pnts *Points = NULL; static struct line_cats *Cats = NULL; type = GV_LINE; nlines = Vect_get_num_lines(Map); if (!Points) Points = Vect_new_line_struct(); if (!Cats) Cats = Vect_new_cats_struct(); if (!List) List = Vect_new_list(); Vect_reset_line(Points); Vect_reset_cats(Cats); Vect_reset_cats(MCats); Vect_reset_list(List); if (!Vect_line_alive(Map, line)) return 0; ltype = Vect_get_line_type(Map, line); if (!(ltype & type)) return 0; Vect_read_line(Map, MPoints, MCats, line); /* special cases: * - loop back to start boundary via several other boundaries * - one boundary forming closed loop * - node with 3 entries but only 2 boundaries, one of them connecting twice, * the other one must then be topologically incorrect in case of boundary */ /* go backward as long as there is only one other line/boundary at the current node */ G_debug(3, "go backward"); Vect_get_line_nodes(Map, line, &next_node, NULL); first = -line; while (1) { node_n_lines = Vect_get_node_n_lines(Map, next_node); /* count lines/boundaries at this node */ lines_type = 0; next_line = first; for (i = 0; i < node_n_lines; i++) { curr_line = Vect_get_node_line(Map, next_node, i); if ((Vect_get_line_type(Map, abs(curr_line)) & GV_LINES)) lines_type++; if ((Vect_get_line_type(Map, abs(curr_line)) == ltype)) { if (abs(curr_line) != abs(first)) { Vect_read_line(Map, NULL, Cats, abs(curr_line)); /* catgories must be identical */ if (compare_cats(MCats, Cats) == 0) next_line = curr_line; } } } if (lines_type == 2 && abs(next_line) != abs(first) && abs(next_line) != line) { first = next_line; if (first < 0) { Vect_get_line_nodes(Map, -first, &next_node, NULL); } else { Vect_get_line_nodes(Map, first, NULL, &next_node); } } else break; } /* go forward as long as there is only one other line/boundary at the current node */ G_debug(3, "go forward"); /* reverse direction */ last = -first; if (last < 0) { Vect_get_line_nodes(Map, -last, &next_node, NULL); } else { Vect_get_line_nodes(Map, last, NULL, &next_node); } Vect_reset_list(List); while (1) { G_ilist_add(List, last); node_n_lines = Vect_get_node_n_lines(Map, next_node); lines_type = 0; next_line = last; for (i = 0; i < node_n_lines; i++) { curr_line = Vect_get_node_line(Map, next_node, i); if ((Vect_get_line_type(Map, abs(curr_line)) & GV_LINES)) lines_type++; if ((Vect_get_line_type(Map, abs(curr_line)) == ltype)) { if (abs(curr_line) != abs(last)) { Vect_read_line(Map, NULL, Cats, abs(curr_line)); if (compare_cats(MCats, Cats) == 0) next_line = curr_line; } } } if (lines_type == 2 && abs(next_line) != abs(last) && abs(next_line) != abs(first)) { last = next_line; if (last < 0) { Vect_get_line_nodes(Map, -last, &next_node, NULL); } else { Vect_get_line_nodes(Map, last, NULL, &next_node); } } else break; } /* merge lines */ G_debug(3, "merge %d lines", List->n_values); Vect_reset_line(MPoints); for (i = 0; i < List->n_values; i++) { Vect_reset_line(Points); Vect_read_line(Map, Points, Cats, abs(List->value[i])); direction = (List->value[i] < 0 ? GV_BACKWARD : GV_FORWARD); Vect_append_points(MPoints, Points, direction); MPoints->n_points--; Vect_delete_line(Map, abs(List->value[i])); } MPoints->n_points++; merged += List->n_values; newl++; return merged; }
int Vect_break_lines_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err) { struct line_pnts *APoints, *BPoints, *Points; struct line_pnts **AXLines, **BXLines; struct line_cats *ACats, *BCats, *Cats; int j, k, l, ret, atype, btype, aline, bline, found, iline, nlines; int naxlines, nbxlines, nx; double *xx = NULL, *yx = NULL, *zx = NULL; struct bound_box ABox, BBox; struct boxlist *List; int nbreaks; int touch1_n = 0, touch1_s = 0, touch1_e = 0, touch1_w = 0; /* other vertices except node1 touching box */ int touch2_n = 0, touch2_s = 0, touch2_e = 0, touch2_w = 0; /* other vertices except node2 touching box */ int is3d; int node, anode1, anode2, bnode1, bnode2; double nodex, nodey; APoints = Vect_new_line_struct(); BPoints = Vect_new_line_struct(); Points = Vect_new_line_struct(); ACats = Vect_new_cats_struct(); BCats = Vect_new_cats_struct(); Cats = Vect_new_cats_struct(); List = Vect_new_boxlist(1); is3d = Vect_is_3d(Map); if (List_break) { nlines = List_break->n_values; } else { nlines = Vect_get_num_lines(Map); } G_debug(3, "nlines = %d", nlines); /* TODO: It seems that lines/boundaries are not broken at intersections * with points/centroids. Check if true, if yes, skip GV_POINTS */ /* To find intersection of two lines (Vect_line_intersection) is quite slow. * Fortunately usual lines/boundaries in GIS often forms a network where lines * are connected by end points, and touch by MBR. This function checks and occasionaly * skips such cases. This is currently done for 2D only */ /* Go through all lines in vector, for each select lines which overlap MBR of * this line exclude those connected by one endpoint (see above) * and try to intersect, if lines intersect write new lines at the end of * the file, and process next line (remaining lines overlapping box are skipped) */ nbreaks = 0; for (iline = 0; iline < nlines; iline++) { G_percent(iline, nlines, 1); if (List_break) { aline = List_break->value[iline]; } else { aline = iline + 1; } if (List_ref && !Vect_val_in_list(List_ref, aline)) continue; G_debug(3, "aline = %d", aline); if (!Vect_line_alive(Map, aline)) continue; atype = Vect_read_line(Map, APoints, ACats, aline); if (!(atype & type)) continue; Vect_line_box(APoints, &ABox); /* Find which sides of the box are touched by intermediate (non-end) points of line */ if (!is3d) { touch1_n = touch1_s = touch1_e = touch1_w = 0; for (j = 1; j < APoints->n_points; j++) { if (APoints->y[j] == ABox.N) touch1_n = 1; if (APoints->y[j] == ABox.S) touch1_s = 1; if (APoints->x[j] == ABox.E) touch1_e = 1; if (APoints->x[j] == ABox.W) touch1_w = 1; } G_debug(3, "touch1: n = %d s = %d e = %d w = %d", touch1_n, touch1_s, touch1_e, touch1_w); touch2_n = touch2_s = touch2_e = touch2_w = 0; for (j = 0; j < APoints->n_points - 1; j++) { if (APoints->y[j] == ABox.N) touch2_n = 1; if (APoints->y[j] == ABox.S) touch2_s = 1; if (APoints->x[j] == ABox.E) touch2_e = 1; if (APoints->x[j] == ABox.W) touch2_w = 1; } G_debug(3, "touch2: n = %d s = %d e = %d w = %d", touch2_n, touch2_s, touch2_e, touch2_w); } Vect_select_lines_by_box(Map, &ABox, type, List); G_debug(3, " %d lines selected by box", List->n_values); for (j = 0; j < List->n_values; j++) { bline = List->id[j]; if (List_break && !Vect_val_in_list(List_break, bline)) { continue; } G_debug(3, " j = %d bline = %d", j, bline); btype = Vect_read_line(Map, BPoints, BCats, bline); /* Check if thouch by end node only */ if (!is3d) { Vect_get_line_nodes(Map, aline, &anode1, &anode2); Vect_get_line_nodes(Map, bline, &bnode1, &bnode2); BBox = List->box[j]; if (anode1 == bnode1 || anode1 == bnode2) node = anode1; else if (anode2 == bnode1 || anode2 == bnode2) node = anode2; else node = 0; if (node) { Vect_get_node_coor(Map, node, &nodex, &nodey, NULL); if ((node == anode1 && nodey == ABox.N && !touch1_n && nodey == BBox.S) || (node == anode2 && nodey == ABox.N && !touch2_n && nodey == BBox.S) || (node == anode1 && nodey == ABox.S && !touch1_s && nodey == BBox.N) || (node == anode2 && nodey == ABox.S && !touch2_s && nodey == BBox.N) || (node == anode1 && nodex == ABox.E && !touch1_e && nodex == BBox.W) || (node == anode2 && nodex == ABox.E && !touch2_e && nodex == BBox.W) || (node == anode1 && nodex == ABox.W && !touch1_w && nodex == BBox.E) || (node == anode2 && nodex == ABox.W && !touch2_w && nodex == BBox.E)) { G_debug(3, "lines %d and %d touching by end nodes only -> no intersection", aline, bline); continue; } } } AXLines = NULL; BXLines = NULL; Vect_line_intersection(APoints, BPoints, &AXLines, &BXLines, &naxlines, &nbxlines, 0); G_debug(3, " naxlines = %d nbxlines = %d", naxlines, nbxlines); /* This part handles a special case when aline == bline, no other intersection was found * and the line is forming collapsed loop, for example 0,0;1,0;0,0 should be broken at 1,0. * ---> */ if (aline == bline && naxlines == 0 && nbxlines == 0 && APoints->n_points >= 3) { int centre; int i; G_debug(3, " Check collapsed loop"); if (APoints->n_points % 2) { /* odd number of vertices */ centre = APoints->n_points / 2; /* index of centre */ if (APoints->x[centre - 1] == APoints->x[centre + 1] && APoints->y[centre - 1] == APoints->y[centre + 1] && APoints->z[centre - 1] == APoints->z[centre + 1]) { /* -> break */ AXLines = (struct line_pnts **)G_malloc(2 * sizeof(struct line_pnts *)); AXLines[0] = Vect_new_line_struct(); AXLines[1] = Vect_new_line_struct(); for (i = 0; i <= centre; i++) Vect_append_point(AXLines[0], APoints->x[i], APoints->y[i], APoints->z[i]); for (i = centre; i < APoints->n_points; i++) Vect_append_point(AXLines[1], APoints->x[i], APoints->y[i], APoints->z[i]); naxlines = 2; } } } /* <--- */ if (Err) { /* array for intersections (more than needed */ xx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); yx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); zx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); } nx = 0; /* number of intersections to be written to Err */ if (naxlines > 0) { /* intersection -> write out */ Vect_delete_line(Map, aline); for (k = 0; k < naxlines; k++) { /* Write new line segments */ /* line may collapse, don't write zero length lines */ Vect_line_prune(AXLines[k]); if ((atype & GV_POINTS) || AXLines[k]->n_points > 1) { ret = Vect_write_line(Map, atype, AXLines[k], ACats); if (List_ref) { Vect_list_append(List_ref, ret); } G_debug(3, "Line %d written, npoints = %d", ret, AXLines[k]->n_points); if (List_break) { Vect_list_append(List_break, ret); } } /* Write intersection points */ if (Err) { if (k > 0) { xx[nx] = AXLines[k]->x[0]; yx[nx] = AXLines[k]->y[0]; zx[nx] = AXLines[k]->z[0]; nx++; } } Vect_destroy_line_struct(AXLines[k]); } nbreaks += naxlines - 1; } if (AXLines) G_free(AXLines); if (nbxlines > 0) { if (aline != bline) { /* Self intersection, do not write twice, TODO: is it OK? */ Vect_delete_line(Map, bline); for (k = 0; k < nbxlines; k++) { /* Write new line segments */ /* line may collapse, don't write zero length lines */ Vect_line_prune(BXLines[k]); if ((btype & GV_POINTS) || BXLines[k]->n_points > 1) { ret = Vect_write_line(Map, btype, BXLines[k], BCats); G_debug(5, "Line %d written", ret); if (List_break) { Vect_list_append(List_break, ret); } } /* Write intersection points */ if (Err) { if (k > 0) { found = 0; for (l = 0; l < nx; l++) { if (xx[l] == BXLines[k]->x[0] && yx[l] == BXLines[k]->y[0] && zx[l] == BXLines[k]->z[0]) { found = 1; break; } } if (!found) { xx[nx] = BXLines[k]->x[0]; yx[nx] = BXLines[k]->y[0]; zx[nx] = BXLines[k]->z[0]; nx++; } } } Vect_destroy_line_struct(BXLines[k]); } nbreaks += nbxlines - 1; } else { for (k = 0; k < nbxlines; k++) Vect_destroy_line_struct(BXLines[k]); } } if (BXLines) G_free(BXLines); if (Err) { for (l = 0; l < nx; l++) { /* Write out errors */ Vect_reset_line(Points); Vect_append_point(Points, xx[l], yx[l], zx[l]); ret = Vect_write_line(Err, GV_POINT, Points, Cats); } G_free(xx); G_free(yx); G_free(zx); } if (naxlines > 0) break; /* first line was broken and deleted -> take the next one */ } if (List_break) { nlines = List_break->n_values; } else { nlines = Vect_get_num_lines(Map); } G_debug(3, "nlines = %d", nlines); } /* for each line */ G_percent(nlines, nlines, 1); /* finish it */ G_verbose_message(_("Intersections: %d"), nbreaks); Vect_destroy_line_struct(APoints); Vect_destroy_line_struct(BPoints); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(ACats); Vect_destroy_cats_struct(BCats); Vect_destroy_cats_struct(Cats); Vect_destroy_boxlist(List); return nbreaks; }