/*! \brief Finds node Find the node corresponding to each point in the point_list \param In pointer to Map_info structure \param point_list list of points (their ids) */ void NetA_points_to_nodes(struct Map_info *In, struct ilist *point_list) { int i, node; struct line_pnts *Points = Vect_new_line_struct(); for (i = 0; i < point_list->n_values; i++) { /* Vect_get_line_nodes(In, point_list->value[i], &node, NULL); */ node = Vect_find_node(In, Points->x[0], Points->y[0], Points->z[0], 0, 0); point_list->value[i] = node; } Vect_destroy_line_struct(Points); }
/* Snap to node */ int snap(double *x, double *y) { int node; double thresh; G_debug(2, "snap(): x = %f, y = %f", *x, *y); thresh = get_thresh(); node = Vect_find_node(&Map, *x, *y, 0, thresh, 0); if (node > 0) Vect_get_node_coor(&Map, node, x, y, NULL); G_debug(2, "node = %d x = %f, y = %f", node, *x, *y); return node; }
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); }
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); }
/* Same as path() but get start/stop from the command line (for non-interactive use) Hamish Bowman March 2007 */ int coor_path(struct Map_info *Map, const struct color_rgb *hcolor, int be_bold, double start_x, double start_y, double end_x, double end_y) { int ret; double nx, ny, fx, fy, tx, ty, msize, maxdist; struct line_pnts *Points; int start_node, end_node; double fdist, tdist, cost; Points = Vect_new_line_struct(); msize = 10 * (D_d_to_u_col(2.0) - D_d_to_u_col(1.0)); /* do it better */ G_debug(1, "msize = %f\n", msize); /* x1 = D_d_to_u_col ((double)(screen_x-WDTH)); y1 = D_d_to_u_row ((double)(screen_y-WDTH)); x2 = D_d_to_u_col ((double)(screen_x+WDTH)); y2 = D_d_to_u_row ((double)(screen_y+WDTH)); x1 = fabs ( x2 - x1 ); y1 = fabs ( y2 - y1 ); if ( x1 > y1 ) maxdist = x1; else maxdist = y1; */ /** maxdist = 10 pixels on the display (WDTH*2); ? ** ie related to zoom level ?? just use msize ?? **/ maxdist = msize; G_debug(1, "Maximum distance in map units = %f\n", maxdist); /* Vect_find_node(): find number of nearest node, 0 if not found */ start_node = Vect_find_node(Map, start_x, start_y, 0.0, maxdist, 0); if (start_node > 0) { Vect_get_node_coor(Map, start_node, &nx, &ny, NULL); fprintf(stderr, _("Node %d: %f %f\n"), start_node, nx, ny); } if (start_node > 0) { fx = nx; fy = ny; } else { fx = start_x; fy = start_y; } D_RGB_color(hcolor->r, hcolor->g, hcolor->b); D_plot_icon(fx, fy, G_ICON_BOX, 0.0, msize); end_node = Vect_find_node(Map, end_x, end_y, 0.0, maxdist, 0); if (end_node > 0) { Vect_get_node_coor(Map, end_node, &nx, &ny, NULL); fprintf(stderr, _("Node %d: %f %f\n"), end_node, nx, ny); } if (end_node > 0) { tx = nx; ty = ny; } else { tx = end_x; ty = end_y; } D_RGB_color(hcolor->r, hcolor->g, hcolor->b); D_plot_icon(tx, ty, G_ICON_CROSS, 0.0, msize); G_debug(2, "find path %f %f -> %f %f", fx, fy, tx, ty); ret = Vect_net_shortest_path_coor(Map, fx, fy, 0.0, tx, ty, 0.0, 5 * maxdist, 5 * maxdist, &cost, Points, NULL, NULL, NULL, &fdist, &tdist); if (ret == 0) { fprintf(stdout, _("Destination unreachable\n")); } else { fprintf(stdout, _("Costs on the network = %f\n"), cost); fprintf(stdout, _(" Distance to the network = %f, " "distance from the network = %f\n\n"), fdist, tdist); display(Map, Points, hcolor, 1, 1, be_bold); } return 0; }
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); }
int main(int argc, char **argv) { int i, j, k, ret; int nlines, type, ltype, afield, tfield, geo, cat; int sp, nsp, nspused, node, line; struct Option *map, *output, *afield_opt, *tfield_opt, *afcol, *type_opt, *term_opt, *nsp_opt; struct Flag *geo_f; struct GModule *module; struct Map_info Map, Out; int *testnode; /* array all nodes: 1 - should be tested as Steiner, * 0 - no need to test (unreachable or terminal) */ struct ilist *TList; /* list of terminal nodes */ struct ilist *StArcs; /* list of arcs on Steiner tree */ struct ilist *StNodes; /* list of nodes on Steiner tree */ struct boxlist *pointlist; double cost, tmpcost; struct cat_list *Clist; struct line_cats *Cats; struct line_pnts *Points; /* Initialize the GIS calls */ G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("network")); G_add_keyword(_("steiner tree")); module->label = _("Creates Steiner tree for the network and given terminals."); module->description = _("Note that 'Minimum Steiner Tree' problem is NP-hard " "and heuristic algorithm is used in this module so " "the result 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->key = "arc_type"; type_opt->options = "line,boundary"; type_opt->answer = "line,boundary"; type_opt->label = _("Arc type"); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "arc_layer"; afield_opt->answer = "1"; afield_opt->label = _("Arc layer"); tfield_opt = G_define_standard_option(G_OPT_V_FIELD); tfield_opt->key = "node_layer"; tfield_opt->answer = "2"; tfield_opt->label = _("Node layer (used for terminals)"); 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 = "terminal_cats"; term_opt->required = YES; term_opt->description = _("Categories of points on terminals (layer is specified by nlayer)"); nsp_opt = G_define_option(); nsp_opt->key = "npoints"; nsp_opt->type = TYPE_INTEGER; nsp_opt->required = NO; nsp_opt->multiple = NO; nsp_opt->answer = "-1"; nsp_opt->description = _("Number of Steiner points (-1 for all possible)"); 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(); 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, G_FATAL_EXIT); Vect_set_open_level(2); if (Vect_open_old(&Map, map->answer, "") < 0) G_fatal_error(_("Unable to open vector map <%s>"), map->answer); nnodes = Vect_get_num_nodes(&Map); nlines = Vect_get_num_lines(&Map); /* Create list of terminals 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); if (!(Vect_cat_get(Cats, tfield, &cat))) continue; 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 (cat=%d)"), cat); continue; } if (Vect_cat_in_cat_list(cat, Clist)) { Vect_list_append(TList, node); } } nterms = TList->n_values; /* GTC Terminal refers to an Steiner tree endpoint */ G_message(_("Number of terminals: %d\n"), nterms); if (nterms < 2) { /* GTC Terminal refers to an Steiner tree endpoint */ G_fatal_error(_("Not enough terminals (< 2)")); } /* Number of steiner points */ nsp = atoi(nsp_opt->answer); if (nsp > nterms - 2) { nsp = nterms - 2; G_warning(_("Requested number of Steiner points > than possible")); } else if (nsp == -1) { nsp = nterms - 2; } G_message(_("Number of Steiner points set to %d\n"), nsp); testnode = (int *)G_malloc((nnodes + 1) * sizeof(int)); for (i = 1; i <= nnodes; i++) testnode[i] = 1; /* Alloc arrays of costs for nodes, first node at 1 (0 not used) */ nodes_costs = (double **)G_malloc((nnodes) * sizeof(double *)); for (i = 0; i < nnodes; i++) { nodes_costs[i] = (double *)G_malloc((nnodes - i) * sizeof(double)); for (j = 0; j < nnodes - i; j++) nodes_costs[i][j] = -1; /* init, i.e. cost was not calculated yet */ } /* alloc memory from each to each other (not directed) terminal */ i = nterms + nterms - 2; /* max number of terms + Steiner points */ comps = (int *)G_malloc(i * sizeof(int)); i = i * (i - 1) / 2; /* number of combinations */ term_costs = (COST *) G_malloc(i * sizeof(COST)); /* alloc memory for costs from Stp to each other terminal */ i = nterms + nterms - 2 - 1; /* max number of terms + Steiner points - 1 */ sp_costs = (COST *) G_malloc(i * sizeof(COST)); terms = (int *)G_malloc((nterms + nterms - 2) * sizeof(int)); /* i.e. +(nterms - 2) St Points */ /* Create initial parts from list of terminals */ G_debug(1, "List of terminal nodes (%d):\n", nterms); for (i = 0; i < nterms; i++) { G_debug(1, "%d\n", TList->value[i]); terms[i] = TList->value[i]; testnode[terms[i]] = 0; /* do not test as Steiner */ } /* Build graph */ Vect_net_build_graph(&Map, type, afield, 0, afcol->answer, NULL, NULL, geo, 0); /* Init costs for all terminals */ for (i = 0; i < nterms; i++) init_node_costs(&Map, terms[i]); /* Test if all terminal may be connected */ for (i = 1; i < nterms; i++) { ret = get_node_costs(terms[0], terms[i], &cost); if (ret == 0) { /* GTC Terminal refers to an Steiner tree endpoint */ G_fatal_error(_("Terminal at node [%d] cannot be connected " "to terminal at node [%d]"), terms[0], terms[i]); } } /* Remove not reachable from list of SP candidates */ j = 0; for (i = 1; i <= nnodes; i++) { ret = get_node_costs(terms[0], i, &cost); if (ret == 0) { testnode[i] = 0; G_debug(2, "node %d removed from list of Steiner point candidates\n", i ); j++; } } G_message(_("[%d] (not reachable) nodes removed from list " "of Steiner point candidates"), j); /* calc costs for terminals MST */ ret = mst(&Map, terms, nterms, &cost, PORT_DOUBLE_MAX, NULL, NULL, 0, 1); /* no StP, rebuild */ G_message(_("MST costs = %f"), cost); /* Go through all nodes and try to use as steiner points -> find that which saves most costs */ nspused = 0; for (j = 0; j < nsp; j++) { sp = 0; G_verbose_message(_("Search for [%d]. Steiner point"), j + 1); for (i = 1; i <= nnodes; i++) { G_percent(i, nnodes, 1); if (testnode[i] == 0) { G_debug(3, "skip test for %d\n", i); continue; } ret = mst(&Map, terms, nterms + j, &tmpcost, cost, NULL, NULL, i, 0); G_debug(2, "cost = %f x %f\n", tmpcost, cost); if (tmpcost < cost) { /* sp candidate */ G_debug(3, " steiner candidate node = %d mst = %f (x last = %f)\n", i, tmpcost, cost); sp = i; cost = tmpcost; } } if (sp > 0) { G_message(_("Steiner point at node [%d] was added " "to terminals (MST costs = %f)"), sp, cost); terms[nterms + j] = sp; init_node_costs(&Map, sp); testnode[sp] = 0; nspused++; /* rebuild for nex cycle */ ret = mst(&Map, terms, nterms + nspused, &tmpcost, PORT_DOUBLE_MAX, NULL, NULL, 0, 1); } else { /* no steiner found */ G_message(_("No Steiner point found -> leaving cycle")); break; } } G_message(_("Number of added Steiner points: %d " "(theoretic max is %d).\n"), nspused, nterms - 2); /* Build lists of arcs and nodes for final version */ ret = mst(&Map, terms, nterms + nspused, &cost, PORT_DOUBLE_MAX, StArcs, StNodes, 0, 0); /* Calculate true costs, which may be lower than MST if steiner points were not used */ if (nsp < nterms - 2) { G_message(_("Spanning tree costs on complet graph = %f\n" "(may be higher than resulting Steiner tree costs!!!)"), cost); } else G_message(_("Steiner tree costs = %f"), cost); /* 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_debug(1, "Steiner tree:"); G_debug(1, "Arcs' categories (layer %d, %d arcs):", 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); G_debug(1, "arc cat = %d", cat); } G_debug(1, "Nodes' categories (layer %d, %d nodes):", tfield, StNodes->n_values); k = 0; pointlist = Vect_new_boxlist(0); for (i = 0; i < StNodes->n_values; i++) { double x, y, z; struct bound_box box; node = StNodes->value[i]; Vect_get_node_coor(&Map, node, &x, &y, &z); box.E = box.W = x; box.N = box.S = y; box.T = box.B = z; Vect_select_lines_by_box(&Map, &box, GV_POINT, pointlist); nlines = Vect_get_node_n_lines(&Map, node); for (j = 0; j < pointlist->n_values; j++) { line = pointlist->id[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); G_debug(1, "node cat = %d", cat); k++; } } Vect_build(&Out); G_message(n_("A Steiner tree with %d arc has been built", "A Steiner tree with %d arcs has been built", StArcs->n_values), StArcs->n_values); /* Free, ... */ Vect_destroy_list(StArcs); Vect_destroy_list(StNodes); Vect_close(&Map); Vect_close(&Out); exit(EXIT_SUCCESS); }
int new_line_update(void *closure, int sxn, int syn, int button) { struct new_line *nl = closure; double x = D_d_to_u_col(sxn); double y = D_d_to_u_row(syn); double dist; G_debug(3, "button = %d x = %d = %f y = %d = %f", button, sxn, x, syn, y); if (nl->first && button == 3) { /* Quit tool ( points & lines ), first is always for points */ Tool_next = TOOL_NOTHING; return 1; } if (button > 3) /* Do nothing on mouse scroll */ return 0; if (nl->type & GV_POINTS && (button == 1 || button == 2)) { snap(&x, &y); Vect_append_point(nl->Points, x, y, 0); write_line(&Map, nl->type, nl->Points); updated_lines_and_nodes_erase_refresh_display(); return 1; } else { /* GV_LINES */ /* Button may be 1,2,3 */ if (button == 1) { /* New point */ if (snap(&x, &y) == 0) { /* If not snapping to other features, try to snap to lines start. * Allows to create area of single boundary. */ if (nl->Points->n_points > 2) { dist = Vect_points_distance(nl->Points->x[0], nl->Points->y[0], 0, x, y, 0, WITHOUT_Z); if (dist < get_thresh()) { x = nl->Points->x[0]; y = nl->Points->y[0]; } } } if (Vect_append_point(nl->Points, x, y, 0) == -1) { G_warning(_("Out of memory! Point not added.")); return 0; } if (nl->type == GV_LINE) symb_set_driver_color(SYMB_LINE); else symb_set_driver_color(SYMB_BOUNDARY_0); display_points(nl->Points, 1); set_location(D_u_to_d_col(x), D_u_to_d_row(y)); nl->first = 0; set_mode(MOUSE_LINE); } else if (button == 2) { /* Undo last point */ if (nl->Points->n_points >= 1) { symb_set_driver_color(SYMB_BACKGROUND); display_points(nl->Points, 1); nl->Points->n_points--; if (nl->type == GV_LINE) symb_set_driver_color(SYMB_LINE); else symb_set_driver_color(SYMB_BOUNDARY_0); display_points(nl->Points, 1); set_location(D_u_to_d_col (nl->Points->x[nl->Points->n_points - 1]), D_u_to_d_row(nl->Points-> y[nl->Points->n_points - 1]) ); } if (nl->Points->n_points == 0) { i_prompt_buttons(_("New point"), "", _("Quit tool")); nl->first = 1; set_mode(MOUSE_POINT); } } else if (button == 3) { /* write the line and quit */ if (nl->Points->n_points > 1) { /* Before the line is written, we must check if connected to existing nodes, if yes, * such nodes must be add to update list before! the line is written (areas/isles */ int node1 = Vect_find_node(&Map, nl->Points->x[0], nl->Points->y[0], nl->Points->z[0], 0, Vect_is_3d(&Map)); int i = nl->Points->n_points - 1; int node2 = Vect_find_node(&Map, nl->Points->x[i], nl->Points->y[i], nl->Points->z[i], 0, Vect_is_3d(&Map)); G_debug(2, " old node1 = %d old node2 = %d", node1, node2); write_line(&Map, nl->type, nl->Points); updated_lines_and_nodes_erase_refresh_display(); } else G_warning(_("Less than 2 points for line -> nothing written")); return 1; } G_debug(2, "n_points = %d", nl->Points->n_points); } i_prompt_buttons(_("New point"), _("Undo last point"), _("Close line")); return 0; }
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; }
/*! \brief Overlay 2 vector maps with AND. AND supports:point line area point + - + line - - - area + - - \param AMap vector map A \param atype feature type for A \param AList unused ? \param AAList unused ? \param BMap vector map B \param btype feature type for B \param BList unused ? \param BAList unused ? \param operator operator code \return 1 on success \return 0 on error */ int Vect_overlay_and(struct Map_info *AMap, int atype, struct ilist *AList, struct ilist *AAList, struct Map_info *BMap, int btype, struct ilist *BList, struct ilist *BAList, struct Map_info *OMap) { int i, j, k, node, line, altype, bltype, oltype, area, centr; struct line_pnts *Points; struct line_cats *ACats, *BCats, *OCats; struct ilist *AOList, *BOList; /* TODO: support Lists */ Points = Vect_new_line_struct(); ACats = Vect_new_cats_struct(); BCats = Vect_new_cats_struct(); OCats = Vect_new_cats_struct(); AOList = Vect_new_list(); BOList = Vect_new_list(); /* TODO: support all types; at present only point x point, area x point and point x area supported */ if ((atype & GV_LINES) || (btype & GV_LINES)) G_warning(_("Overlay: line/boundary types not supported by AND operator")); if ((atype & GV_AREA) && (btype & GV_AREA)) G_warning(_("Overlay: area x area types not supported by AND operator")); /* TODO: more points in one node in one map */ /* point x point: select all points with identical coordinates in both maps */ if ((atype & GV_POINTS) && (btype & GV_POINTS)) { /* both points and centroids */ G_debug(3, "overlay: AND: point x point"); for (i = 1; i <= Vect_get_num_lines(AMap); i++) { altype = Vect_read_line(AMap, Points, ACats, i); if (!(altype & GV_POINTS)) continue; node = Vect_find_node(BMap, Points->x[0], Points->y[0], Points->z[0], 0, 1); G_debug(3, "overlay: node = %d", node); if (node == 0) continue; Vect_reset_cats(OCats); for (j = 0; j < Vect_get_node_n_lines(BMap, node); j++) { line = Vect_get_node_line(BMap, node, j); bltype = Vect_read_line(BMap, NULL, BCats, line); if (!(bltype & GV_POINTS)) continue; /* Identical points found -> write out */ /* TODO: do something if fields in ACats and BCats are identical */ for (k = 0; k < ACats->n_cats; k++) Vect_cat_set(OCats, ACats->field[k], ACats->cat[k]); for (k = 0; k < BCats->n_cats; k++) Vect_cat_set(OCats, BCats->field[k], BCats->cat[k]); /* TODO: what to do if one type is GV_POINT and second GV_CENTROID */ oltype = altype; Vect_write_line(OMap, oltype, Points, OCats); Vect_list_append(AOList, i); /* add to list of written lines */ Vect_list_append(BOList, line); break; } } } /* TODO: check only labeled areas */ /* point x area: select points from A in areas in B */ if ((atype & GV_POINTS) && (btype & GV_AREA)) { /* both points and centroids */ G_debug(3, "overlay: AND: point x area"); for (i = 1; i <= Vect_get_num_lines(AMap); i++) { altype = Vect_read_line(AMap, Points, ACats, i); if (!(altype & GV_POINTS)) continue; area = Vect_find_area(BMap, Points->x[0], Points->y[0]); if (area == 0) continue; Vect_reset_cats(OCats); /* TODO: do something if fields in ACats and BCats are identical */ for (k = 0; k < ACats->n_cats; k++) Vect_cat_set(OCats, ACats->field[k], ACats->cat[k]); centr = Vect_get_area_centroid(BMap, area); if (centr > 0) { bltype = Vect_read_line(BMap, NULL, BCats, centr); for (k = 0; k < BCats->n_cats; k++) Vect_cat_set(OCats, BCats->field[k], BCats->cat[k]); } /* Check if not yet written */ if (!(Vect_val_in_list(AOList, i))) { Vect_write_line(OMap, altype, Points, OCats); Vect_list_append(AOList, i); } } } /* area x point: select points from B in areas in A */ if ((btype & GV_POINTS) && (atype & GV_AREA)) { /* both points and centroids */ G_debug(3, "overlay: AND: area x point"); for (i = 1; i <= Vect_get_num_lines(BMap); i++) { bltype = Vect_read_line(BMap, Points, BCats, i); if (!(bltype & GV_POINTS)) continue; area = Vect_find_area(AMap, Points->x[0], Points->y[0]); if (area == 0) continue; Vect_reset_cats(OCats); /* TODO: do something if fields in ACats and BCats are identical */ for (k = 0; k < BCats->n_cats; k++) Vect_cat_set(OCats, BCats->field[k], BCats->cat[k]); centr = Vect_get_area_centroid(AMap, area); if (centr > 0) { altype = Vect_read_line(AMap, NULL, ACats, centr); for (k = 0; k < ACats->n_cats; k++) Vect_cat_set(OCats, ACats->field[k], ACats->cat[k]); } /* Check if not yet written */ if (!(Vect_val_in_list(BOList, i))) { Vect_write_line(OMap, bltype, Points, OCats); Vect_list_append(BOList, i); } } } return 0; }