/* Init all costs to/from given node */ int init_node_costs(struct Map_info *Map, int from) { int to, ret, row, col; double cost; G_verbose_message(_("Init costs from node %d"), from); for (to = 1; to <= nnodes; to++) { if (from == to) continue; ret = Vect_net_shortest_path(Map, from, to, NULL, &cost); if (ret == -1) { G_debug(1, "Destination node %d is unreachable from node %d\n", to, from); cost = -2; } if (from < to) { row = from - 1; col = to - from - 1; } else { row = to - 1; col = from - to - 1; } G_debug(3, "init costs %d - > %d = %f\n", from, to, cost); nodes_costs[row][col] = cost; } return 1; }
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 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); }
/* 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, 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); }