/*! \brief Get list of nodes from varray Returns the list of all nodes on features selected by varray. nodes_to_features conains the index of a feature adjecent to each node or -1 if no such feature specified by varray exists. Nodes_to_features might be NULL, in which case it is left unitialised. \param map pointer to Map_info structure \param varray pointer to varray structure \param[out] nodes list of node ids \param node_to_features ? */ void NetA_varray_to_nodes(struct Map_info *map, struct varray *varray, struct ilist *nodes, int *nodes_to_features) { int nlines, nnodes, i; nlines = Vect_get_num_lines(map); nnodes = Vect_get_num_nodes(map); if (nodes_to_features) for (i = 1; i <= nnodes; i++) nodes_to_features[i] = -1; for (i = 1; i <= nlines; i++) if (varray->c[i]) { int type = Vect_read_line(map, NULL, NULL, i); if (type == GV_POINT) { int node; Vect_get_line_nodes(map, i, &node, NULL); Vect_list_append(nodes, node); if (nodes_to_features) nodes_to_features[node] = i; } else { int node1, node2; Vect_get_line_nodes(map, i, &node1, &node2); Vect_list_append(nodes, node1); Vect_list_append(nodes, node2); if (nodes_to_features) nodes_to_features[node1] = nodes_to_features[node2] = i; } } }
/** \brief Reverse list selection \param[in] Map vector map \param[in] type feature type \param[in,out] reversed list \return 1 */ int reverse_selection(struct Map_info *Map, int type, struct ilist **List) { struct ilist *list_reverse; int line, nlines, ltype; list_reverse = Vect_new_list(); nlines = Vect_get_num_lines(Map); for (line = 1; line <= nlines; line++) { ltype = Vect_read_line(Map, NULL, NULL, line); if (!(ltype & type)) continue; if (!Vect_val_in_list(*List, line)) Vect_list_append(list_reverse, line); } Vect_destroy_list(*List); *List = list_reverse; return 1; }
/*! \brief Find all line/area id's for given category \param Map pointer to Map_info structure \param layer layer number \param type_mask feature type of objects to search for \param cat category number \param[out] lines array of ids of found lines/points */ void Vect_cidx_find_all(const struct Map_info *Map, int layer, int type_mask, int cat, struct ilist *lines) { int type, line; struct Cat_index *ci; int field_index, idx; Vect_reset_list(lines); field_index = Vect_cidx_get_field_index(Map, layer); if (field_index == -1) { /* not found */ return; } ci = &(Map->plus.cidx[field_index]); idx = Vect_cidx_find_next(Map, field_index, cat, type_mask, 0, &type, &line); if (idx == -1) { return; } do { if (ci->cat[idx][0] != cat) { break; } if (ci->cat[idx][1] & type_mask) { Vect_list_append(lines, ci->cat[idx][2]); } idx++; } while (idx < ci->n_cats); return; }
void copy_boxlist_and_destroy( struct boxlist *blist, struct ilist * list ) { Vect_reset_list( list ); for ( int i = 0; i < blist->n_values; i++ ) { Vect_list_append( list, blist->id[i] ); } Vect_destroy_boxlist( blist ); }
/*! \brief Get list of unique categories for given layer index \param Map pointer to Map_info structure \param field_index layer index \param[out] list output list of cats \return 1 on success \return 0 on error */ int Vect_cidx_get_unique_cats_by_index(struct Map_info *Map, int field_index, struct ilist *list) { int c; struct Cat_index *ci; check_status(Map); check_index(Map, field_index); ci = &(Map->plus.cidx[field_index]); /* force sorting index -- really needed? */ dig_cidx_sort(&(Map->plus)); Vect_reset_list(list); if (ci->n_cats > 0) Vect_list_append(list, ci->cat[0][0]); for (c = 1; c < ci->n_cats; c++) { if (ci->cat[c][0] != ci->cat[c - 1][0]) Vect_list_append(list, ci->cat[c][0]); } return list->n_values == ci->n_ucats ? 1 : 0; }
/** \brief merge two list, i.e. store only duplicate items \param[in] alist,blist list to be merged \return result number of items */ static int merge_lists(struct ilist *alist, struct ilist *blist) { int i; struct ilist *list_del; list_del = Vect_new_list(); for (i = 0; i < alist->n_values; i++) { if (!Vect_val_in_list(blist, alist->value[i])) Vect_list_append(list_del, alist->value[i]); } Vect_list_delete_list(alist, list_del); Vect_destroy_list(list_del); return alist->n_values; }
/*! \brief Get list of categories of given field. \param Cats line_cats structure \param field layer number \param[out] cats pointer to list where cats will be written \return number of found categories \return -1 on invalid field */ int Vect_field_cat_get(const struct line_cats *Cats, int field, struct ilist *cats) { int n; /* reset list of categories */ Vect_reset_list(cats); /* check input value */ if (field < 1 || field > GV_FIELD_MAX) return -1; /* go through cats and find if field exist */ for (n = 0; n < Cats->n_cats; n++) { if (Cats->field[n] == field) Vect_list_append(cats, Cats->cat[n]); } return cats->n_values; }
/** \brief Select features by id \param[in] Map vector map \param[in] type feature type \param[in] ids ids list \param[in,out] List list of selected features \return number of selected lines */ int sel_by_id(struct Map_info *Map, int type, char *ids, struct ilist *List) { int i; int num, id; struct cat_list *il; /* NOTE: this is not cat list, but list of id's */ struct ilist *List_tmp; if (first_selection) { List_tmp = List; first_selection = 0; } else { List_tmp = Vect_new_list(); } il = Vect_new_cat_list(); Vect_str_to_cat_list(ids, il); num = Vect_get_num_lines(Map); for (i = 0; i < il->n_ranges; i++) { for (id = 1; id <= num; id++) { if (!(Vect_read_line(Map, NULL, NULL, id) & type)) { continue; } if (id >= il->min[i] && id <= il->max[i]) { Vect_list_append(List_tmp, id); } } } G_debug(1, " %d lines selected (by id)", List_tmp->n_values); /* merge lists (only duplicate items) */ if (List_tmp != List) { merge_lists(List, List_tmp); Vect_destroy_list(List_tmp); } Vect_destroy_cat_list(il); return List->n_values; }
/*! \brief Creates list of boundaries for given area Note that ids in <b>List</b> can be negative. The sign indicates in which direction the boundary should be read (negative for backward). \param Map pointer to Map_info structure \param area area id \param[out] List pointer to list of boundaries \return number of boundaries */ int Vect_get_area_boundaries(const struct Map_info *Map, int area, struct ilist *List) { int i, line; const struct Plus_head *Plus; struct P_area *Area; G_debug(3, "Vect_get_area_boundaries(): area = %d", area); Vect_reset_list(List); Plus = &(Map->plus); Area = Plus->Area[area]; if (Area == NULL) G_fatal_error(_("Attempt to read topo for dead area (%d)"), area); for (i = 0; i < Area->n_lines; i++) { line = Area->lines[i]; Vect_list_append(List, line); } return (List->n_values); }
/*! \brief Creates list of boundaries for given isle Note that ids in <b>List</b> can be negative. The sign indicates in which direction the boundary should be read (negative for forward). \param Map pointer to Map_info structur \param isle island number \param[out] List pointer to list where boundaries are stored \return number of boundaries */ int Vect_get_isle_boundaries(const struct Map_info *Map, int isle, struct ilist *List) { int i, line; const struct Plus_head *Plus; struct P_isle *Isle; G_debug(3, "Vect_get_isle_boundaries(): isle = %d", isle); Vect_reset_list(List); Plus = &(Map->plus); Isle = Plus->Isle[isle]; if (Isle == NULL) G_fatal_error(_("Attempt to read topo for dead isle (%d)"), isle); for (i = 0; i < Isle->n_lines; i++) { line = Isle->lines[i]; Vect_list_append(List, line); } return (List->n_values); }
/*! \brief Connect lines in given threshold \code \ \ id1 \ -> \ \ id2 --------- -----+--- \endcode If two lines are selected and <i>thresh</i> is -1, no limit is applied. \param Map pointer to Map_info \param List list of selected lines \param thresh threshold value \return number of modified lines \return -1 on error */ int Vedit_connect_lines(struct Map_info *Map, struct ilist *List, double thresh) { int nlines_modified, connected; int i, j, node[2], n_nodes; int line, found; double x, y, z; struct ilist *List_exclude, *List_found; nlines_modified = 0; List_exclude = Vect_new_list(); List_found = Vect_new_list(); n_nodes = 2; /* collect lines to be modified */ for (i = 0; i < List->n_values; i++) { line = List->value[i]; if (!Vect_line_alive(Map, line)) continue; if (Vect_get_line_type(Map, line) & GV_POINTS) continue; node[0] = node[1] = -1; Vect_get_line_nodes(Map, line, &(node[0]), &(node[1])); if (node[0] < 0 || node[1] < 0) continue; connected = 0; Vect_reset_list(List_exclude); Vect_list_append(List_exclude, line); for (j = 0; j < n_nodes && !connected; j++) { /* for each line node find lines in threshold */ Vect_get_node_coor(Map, node[j], &x, &y, &z); do { /* find first nearest line */ found = Vect_find_line_list(Map, x, y, z, GV_LINES, thresh, WITHOUT_Z, List_exclude, List_found); if (found > 0 && Vect_line_alive(Map, found)) { /* try to connect lines (given node) */ G_debug(3, "Vedit_connect_lines(): lines=%d,%d", line, found); if (connect_lines(Map, !j, line, found, thresh, List)) { G_debug(3, "Vedit_connect_lines(): lines=%d,%d -> connected", line, found); nlines_modified += 2; connected = 1; } } Vect_list_append(List_exclude, found); } while(List_found->n_values > 0 && !connected); } } Vect_destroy_list(List_exclude); Vect_destroy_list(List_found); return nlines_modified; }
/*! \brief Find shortest path. Costs for 'from' and 'to' nodes are not considered (SP found even if 'from' or 'to' are 'closed' (costs = -1) and costs of these nodes are not added to SP costs result. \param graph pointer to graph structure \param from from node \param to to node \param List list of line ids \param cost costs value \return number of segments \return 0 is correct for from = to, or List == NULL ), ? sum of costs is better return value, \return -1 destination unreachable */ int Vect_graph_shortest_path(dglGraph_s * graph, int from, int to, struct ilist *List, double *cost) { int i, line, *pclip, cArc, nRet; dglSPReport_s *pSPReport; dglInt32_t nDistance; G_debug(3, "Vect_graph_shortest_path(): from = %d, to = %d", from, to); /* Note : if from == to dgl goes to nearest node and returns back (dgl feature) => * check here for from == to */ if (List != NULL) Vect_reset_list(List); /* Check if from and to are identical, otherwise dglib returns path to neares node and back! */ if (from == to) { if (cost != NULL) *cost = 0; return 0; } From_node = from; pclip = NULL; if (List != NULL) { nRet = dglShortestPath(graph, &pSPReport, (dglInt32_t) from, (dglInt32_t) to, clipper, pclip, NULL); } else { nRet = dglShortestDistance(graph, &nDistance, (dglInt32_t) from, (dglInt32_t) to, clipper, pclip, NULL); } if (nRet == 0) { if (cost != NULL) *cost = PORT_DOUBLE_MAX; return -1; } else if (nRet < 0) { G_warning(_("dglShortestPath error: %s"), dglStrerror(graph)); return -1; } if (List != NULL) { for (i = 0; i < pSPReport->cArc; i++) { line = dglEdgeGet_Id(graph, pSPReport->pArc[i].pnEdge); G_debug(2, "From %ld to %ld - cost %ld user %d distance %ld", pSPReport->pArc[i].nFrom, pSPReport->pArc[i].nTo, /* this is the cost from clip() */ dglEdgeGet_Cost(graph, pSPReport->pArc[i].pnEdge) / 1000, line, pSPReport->pArc[i].nDistance); Vect_list_append(List, line); } } if (cost != NULL) { if (List != NULL) *cost = (double)pSPReport->nDistance / 1000; else *cost = (double)nDistance / 1000; } if (List != NULL) { cArc = pSPReport->cArc; dglFreeSPReport(graph, pSPReport); } else cArc = 0; return (cArc); }
/*! \brief Get number of articulation points in the graph \param graph input graph \param[out] articulation_list list of articulation points \return number of points \return -1 on error */ int NetA_articulation_points(dglGraph_s * graph, struct ilist *articulation_list) { int nnodes; int points = 0; dglEdgesetTraverser_s *current; /*edge to be processed when the node is visited */ int *tin, *min_tin; /*time in, and smallest tin over all successors. 0 if not yet visited */ dglInt32_t **parent; /*parents of the nodes */ dglInt32_t **stack; /*stack of nodes */ dglInt32_t **current_edge; /*current edge for each node */ int *mark; /*marked articulation points */ dglNodeTraverser_s nt; dglInt32_t *current_node; int stack_size; int i, time; nnodes = dglGet_NodeCount(graph); current = (dglEdgesetTraverser_s *) G_calloc(nnodes + 1, sizeof(dglEdgesetTraverser_s)); tin = (int *)G_calloc(nnodes + 1, sizeof(int)); min_tin = (int *)G_calloc(nnodes + 1, sizeof(int)); parent = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); stack = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); current_edge = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); mark = (int *)G_calloc(nnodes + 1, sizeof(int)); if (!tin || !min_tin || !parent || !stack || !current || !mark) { G_fatal_error(_("Out of memory")); return -1; } for (i = 1; i <= nnodes; i++) { dglEdgeset_T_Initialize(¤t[i], graph, dglNodeGet_OutEdgeset(graph, dglGetNode(graph, i))); current_edge[i] = dglEdgeset_T_First(¤t[i]); tin[i] = mark[i] = 0; } dglNode_T_Initialize(&nt, graph); time = 0; for (current_node = dglNode_T_First(&nt); current_node; current_node = dglNode_T_Next(&nt)) { dglInt32_t current_id = dglNodeGet_Id(graph, current_node); if (tin[current_id] == 0) { int children = 0; /*number of subtrees rooted at the root/current_node */ stack[0] = current_node; stack_size = 1; parent[current_id] = NULL; while (stack_size) { dglInt32_t *node = stack[stack_size - 1]; dglInt32_t node_id = dglNodeGet_Id(graph, node); if (tin[node_id] == 0) /*vertex visited for the first time */ min_tin[node_id] = tin[node_id] = ++time; else { /*return from the recursion */ dglInt32_t to = dglNodeGet_Id(graph, dglEdgeGet_Tail(graph, current_edge [node_id])); if (min_tin[to] >= tin[node_id]) /*no path from the subtree above the current node */ mark[node_id] = 1; /*so the current node must be an articulation point */ if (min_tin[to] < min_tin[node_id]) min_tin[node_id] = min_tin[to]; current_edge[node_id] = dglEdgeset_T_Next(¤t[node_id]); /*proceed to the next edge */ } /*try next edges */ for (; current_edge[node_id]; current_edge[node_id] = dglEdgeset_T_Next(¤t[node_id])) { dglInt32_t *to = dglEdgeGet_Tail(graph, current_edge[node_id]); if (to == parent[node_id]) continue; /*skip parent */ int to_id = dglNodeGet_Id(graph, to); if (tin[to_id]) { /*back edge, cannot be a bridge/articualtion point */ if (tin[to_id] < min_tin[node_id]) min_tin[node_id] = tin[to_id]; } else { /*forward edge */ if (node_id == current_id) children++; /*if root, increase number of children */ parent[to_id] = node; stack[stack_size++] = to; break; } } if (!current_edge[node_id]) stack_size--; /*current node completely processed */ } if (children > 1) mark[current_id] = 1; /*if the root has more than 1 subtrees rooted at it, then it is an * articulation point */ } } for (i = 1; i <= nnodes; i++) if (mark[i]) { points++; Vect_list_append(articulation_list, i); } dglNode_T_Release(&nt); for (i = 1; i <= nnodes; i++) dglEdgeset_T_Release(¤t[i]); G_free(current); G_free(tin); G_free(min_tin); G_free(parent); G_free(stack); G_free(current_edge); return points; }
/*! \brief Merge lines/boundaries At least two lines need to be given. \param Map pointer to Map_info \param List list of selected lines \return number of merged lines \return -1 on error */ int Vedit_merge_lines(struct Map_info *Map, struct ilist *List) { struct ilist *List_in_box; struct line_pnts *Points1, *Points2, *Points; struct line_cats *Cats1, *Cats2; int line_i, i, j; int line, line1, type1, line2; int do_merge; /* number of lines (original, selected, merged) */ int nlines, nlines_merged; nlines_merged = 0; if (List->n_values < 2) { return 0; } Points1 = Vect_new_line_struct(); Cats1 = Vect_new_cats_struct(); Points2 = Vect_new_line_struct(); Cats2 = Vect_new_cats_struct(); Points = Vect_new_line_struct(); List_in_box = Vect_new_list(); nlines = Vect_get_num_lines(Map); /* merge lines */ for (line_i = 0; line_i < List->n_values; line_i++) { line1 = List->value[line_i]; if (!Vect_line_alive(Map, line1)) continue; type1 = Vect_read_line(Map, Points1, Cats1, line1); if (!(type1 & GV_LINES)) continue; Vect_reset_line(Points); for (i = 0; i < Points1->n_points; i += Points1->n_points - 1) { Vect_reset_list(List_in_box); /* define searching region */ Vect_reset_line(Points2); /* Vect_append_point (Points2, Points1 -> x[i] - thresh, Points1 -> y[i] + thresh, Points1 -> z[i]); Vect_append_point (Points2, Points1 -> x[i] + thresh, Points1 -> y[i] + thresh, Points1 -> z[i]); Vect_append_point (Points2, Points1 -> x[i] + thresh, Points1 -> y[i] - thresh, Points1 -> z[i]); Vect_append_point (Points2, Points1 -> x[i] - thresh, Points1 -> y[i] - thresh, Points1 -> z[i]); */ Vect_append_point(Points2, Points1->x[i], Points1->y[i], Points1->z[i]); /* * merge lines only if two lines found in the region * i.e. the current line and an adjacent line */ if (1 < Vect_select_lines_by_polygon(Map, Points2, 0, NULL, GV_LINES, List_in_box)) { do_merge = 1; line2 = -1; for (j = 0; do_merge && j < List->n_values; j++) { if (List->value[j] == line1 || !Vect_line_alive(Map, List->value[j])) continue; if (Vect_val_in_list(List_in_box, List->value[j])) { if (line2 > 0) { /* three lines found * selected lines will be not merged */ do_merge = 0; } else { line2 = List->value[j]; } } } if (!do_merge || line2 < 0) continue; Vect_read_line(Map, Points2, Cats2, line2); merge_lines(Points1, Cats1, Points2, Cats2, -1.0, &Points); /* do not use threshold value */ G_debug(3, "Vedit_merge_lines(): lines=%d,%d", line1, line2); if (Points->n_points > 0) { if (Vect_delete_line(Map, line2) == -1) { return -1; } if (line2 <= nlines) nlines_merged++; } } } /* for each node */ if (Points->n_points > 0) { line = Vect_rewrite_line(Map, line1, type1, Points, Cats1); if (line < 0) { return -1; } if (line1 <= nlines) nlines_merged++; /* update number of lines */ Vect_list_append(List, line); } } /* for each line */ /* destroy stuctures */ Vect_destroy_line_struct(Points1); Vect_destroy_line_struct(Points2); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats1); Vect_destroy_cats_struct(Cats2); return nlines_merged; }
/*! \brief Find a path (minimum number of edges) from 'from' to 'to' using only edges in 'edges'. Precisely, edge with id I is used if edges[abs(i)] == 1. List stores the indices of lines on the path. Method return number of edges or -1 if no path exist. \param graph input graph \param from 'from' position \param to 'to' position \param edges list of available edges \param[out] list list of edges \return number of edges \return -1 on failure */ int NetA_find_path(dglGraph_s * graph, int from, int to, int *edges, struct ilist *list) { dglInt32_t **prev, *queue; dglEdgesetTraverser_s et; char *vis; int begin, end, cur, nnodes; nnodes = dglGet_NodeCount(graph); prev = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); queue = (dglInt32_t *) G_calloc(nnodes + 1, sizeof(dglInt32_t)); vis = (char *)G_calloc(nnodes + 1, sizeof(char)); if (!prev || !queue || !vis) { G_fatal_error(_("Out of memory")); return -1; } Vect_reset_list(list); begin = 0; end = 1; vis[from] = 'y'; queue[0] = from; prev[from] = NULL; while (begin != end) { dglInt32_t vertex = queue[begin++]; if (vertex == to) break; dglInt32_t *edge, *node = dglGetNode(graph, vertex); dglEdgeset_T_Initialize(&et, graph, dglNodeGet_OutEdgeset(graph, node)); for (edge = dglEdgeset_T_First(&et); edge; edge = dglEdgeset_T_Next(&et)) { dglInt32_t id = abs(dglEdgeGet_Id(graph, edge)); dglInt32_t to = dglNodeGet_Id(graph, dglEdgeGet_Tail(graph, edge)); if (edges[id] && !vis[to]) { vis[to] = 'y'; prev[to] = edge; queue[end++] = to; } } dglEdgeset_T_Release(&et); } G_free(queue); if (!vis[to]) { G_free(prev); G_free(vis); return -1; } cur = to; while (prev[cur] != NULL) { Vect_list_append(list, abs(dglEdgeGet_Id(graph, prev[cur]))); cur = dglNodeGet_Id(graph, dglEdgeGet_Head(graph, prev[cur])); } G_free(prev); G_free(vis); return list->n_values; }
int main(int argc, char *argv[]) { int i, iopt; int operator; int aline, nalines, nskipped; int ltype, itype[2], ifield[2]; int **cats, *ncats, nfields, *fields; char *mapset[2], *pre[2]; struct GModule *module; struct GParm parm; struct GFlag flag; struct Map_info In[2], Out; struct field_info *IFi, *OFi; struct line_pnts *APoints, *BPoints; struct line_cats *ACats, *BCats; int *ALines; /* List of lines: 0 do not output, 1 - write to output */ struct ilist *List, *TmpList, *BoundList; G_gisinit(argv[0]); pre[0] = "a"; pre[1] = "b"; module = G_define_module(); module->keywords = _("vector, spatial query"); module->description = _("Selects features from vector map (A) by features from other vector map (B)."); parse_options(&parm, &flag); if (G_parser(argc, argv)) exit(EXIT_FAILURE); if (parm.operator->answer[0] == 'e') operator = OP_EQUALS; else if (parm.operator->answer[0] == 'd') { /* operator = OP_DISJOINT; */ operator = OP_INTERSECTS; flag.reverse->answer = YES; } else if (parm.operator->answer[0] == 'i') operator = OP_INTERSECTS; else if (parm.operator->answer[0] == 't') operator = OP_TOUCHES; else if (parm.operator->answer[0] == 'c' && parm.operator->answer[1] == 'r') operator = OP_CROSSES; else if (parm.operator->answer[0] == 'w') operator = OP_WITHIN; else if (parm.operator->answer[0] == 'c' && parm.operator->answer[1] == 'o') operator = OP_CONTAINS; else if (parm.operator->answer[0] == 'o') { if (strcmp(parm.operator->answer, "overlaps") == 0) operator = OP_OVERLAPS; else operator = OP_OVERLAP; } else if (parm.operator->answer[0] == 'r') operator = OP_RELATE; else G_fatal_error(_("Unknown operator")); if (operator == OP_RELATE && !parm.relate->answer) { G_fatal_error(_("Required parameter <%s> not set"), parm.relate->key); } for (iopt = 0; iopt < 2; iopt++) { itype[iopt] = Vect_option_to_types(parm.type[iopt]); ifield[iopt] = atoi(parm.field[iopt]->answer); Vect_check_input_output_name(parm.input[iopt]->answer, parm.output->answer, GV_FATAL_EXIT); if ((mapset[iopt] = G_find_vector2(parm.input[iopt]->answer, NULL)) == NULL) { G_fatal_error(_("Vector map <%s> not found"), parm.input[iopt]->answer); } Vect_set_open_level(2); Vect_open_old(&(In[iopt]), parm.input[iopt]->answer, mapset[iopt]); } /* Read field info */ IFi = Vect_get_field(&(In[0]), ifield[0]); APoints = Vect_new_line_struct(); BPoints = Vect_new_line_struct(); ACats = Vect_new_cats_struct(); BCats = Vect_new_cats_struct(); List = Vect_new_list(); TmpList = Vect_new_list(); BoundList = Vect_new_list(); /* Open output */ Vect_open_new(&Out, parm.output->answer, Vect_is_3d(&(In[0]))); Vect_set_map_name(&Out, _("Output from v.select")); Vect_set_person(&Out, G_whoami()); Vect_copy_head_data(&(In[0]), &Out); Vect_hist_copy(&(In[0]), &Out); Vect_hist_command(&Out); nskipped = 0; nalines = Vect_get_num_lines(&(In[0])); #ifdef HAVE_GEOS initGEOS(G_message, G_fatal_error); GEOSGeometry *AGeom = NULL; #else void *AGeom = NULL; #endif /* Alloc space for input lines array */ ALines = (int *)G_calloc(nalines + 1, sizeof(int)); G_message(_("Building spatial index...")); Vect_build_spatial_index(&In[0]); Vect_build_spatial_index(&In[1]); /* Lines in A. Go through all lines and mark those that meets condition */ if (itype[0] & (GV_POINTS | GV_LINES)) { G_message(_("Processing features...")); for (aline = 1; aline <= nalines; aline++) { BOUND_BOX abox; G_debug(3, "aline = %d", aline); G_percent(aline, nalines, 2); /* must be before any continue */ /* Check category */ if (!flag.cat->answer && Vect_get_line_cat(&(In[0]), aline, ifield[0]) < 0) { nskipped++; continue; } /* Read line and check type */ if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS AGeom = Vect_read_line_geos(&(In[0]), aline, <ype); #endif if (!(ltype & (GV_POINT | GV_LINE))) continue; if (!AGeom) G_fatal_error(_("Unable to read line id %d from vector map <%s>"), aline, Vect_get_full_name(&(In[0]))); } else { ltype = Vect_read_line(&(In[0]), APoints, NULL, aline); } if (!(ltype & itype[0])) continue; Vect_get_line_box(&(In[0]), aline, &abox); abox.T = PORT_DOUBLE_MAX; abox.B = -PORT_DOUBLE_MAX; /* Check if this line overlaps any feature in B */ /* x Lines in B */ if (itype[1] & (GV_POINTS | GV_LINES)) { int i; int found = 0; /* Lines */ Vect_select_lines_by_box(&(In[1]), &abox, itype[1], List); for (i = 0; i < List->n_values; i++) { int bline; bline = List->value[i]; G_debug(3, " bline = %d", bline); /* Check category */ if (!flag.cat->answer && Vect_get_line_cat(&(In[1]), bline, ifield[1]) < 0) { nskipped++; continue; } if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS if(line_relate_geos(&(In[1]), AGeom, bline, operator, parm.relate->answer)) { found = 1; break; } #endif } else { Vect_read_line(&(In[1]), BPoints, NULL, bline); if (Vect_line_check_intersection(APoints, BPoints, 0)) { found = 1; break; } } } if (found) { ALines[aline] = 1; continue; /* Go to next A line */ } } /* x Areas in B. */ if (itype[1] & GV_AREA) { int i; Vect_select_areas_by_box(&(In[1]), &abox, List); for (i = 0; i < List->n_values; i++) { int barea; barea = List->value[i]; G_debug(3, " barea = %d", barea); if (Vect_get_area_cat(&(In[1]), barea, ifield[1]) < 0) { nskipped++; continue; } if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS if(area_relate_geos(&(In[1]), AGeom, barea, operator, parm.relate->answer)) { ALines[aline] = 1; break; } #endif } else { if (line_overlap_area(&(In[0]), aline, &(In[1]), barea)) { ALines[aline] = 1; break; } } } } if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS GEOSGeom_destroy(AGeom); #endif AGeom = NULL; } } } /* Areas in A. */ if (itype[0] & GV_AREA) { int aarea, naareas; G_message(_("Processing areas...")); naareas = Vect_get_num_areas(&(In[0])); for (aarea = 1; aarea <= naareas; aarea++) { BOUND_BOX abox; G_percent(aarea, naareas, 2); /* must be before any continue */ if (Vect_get_area_cat(&(In[0]), aarea, ifield[0]) < 0) { nskipped++; continue; } Vect_get_area_box(&(In[0]), aarea, &abox); abox.T = PORT_DOUBLE_MAX; abox.B = -PORT_DOUBLE_MAX; if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS AGeom = Vect_read_area_geos(&(In[0]), aarea); #endif if (!AGeom) G_fatal_error(_("Unable to read area id %d from vector map <%s>"), aline, Vect_get_full_name(&(In[0]))); } /* x Lines in B */ if (itype[1] & (GV_POINTS | GV_LINES)) { Vect_select_lines_by_box(&(In[1]), &abox, itype[1], List); for (i = 0; i < List->n_values; i++) { int bline; bline = List->value[i]; if (!flag.cat->answer && Vect_get_line_cat(&(In[1]), bline, ifield[1]) < 0) { nskipped++; continue; } if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS if(line_relate_geos(&(In[1]), AGeom, bline, operator, parm.relate->answer)) { add_aarea(&(In[0]), aarea, ALines); break; } #endif } else { if (line_overlap_area(&(In[1]), bline, &(In[0]), aarea)) { add_aarea(&(In[0]), aarea, ALines); continue; } } } } /* x Areas in B */ if (itype[1] & GV_AREA) { int naisles; int found = 0; /* List of areas B */ /* Make a list of features forming area A */ Vect_reset_list(List); Vect_get_area_boundaries(&(In[0]), aarea, BoundList); for (i = 0; i < BoundList->n_values; i++) { Vect_list_append(List, abs(BoundList->value[i])); } naisles = Vect_get_area_num_isles(&(In[0]), aarea); for (i = 0; i < naisles; i++) { int j, aisle; aisle = Vect_get_area_isle(&(In[0]), aarea, i); Vect_get_isle_boundaries(&(In[0]), aisle, BoundList); for (j = 0; j < BoundList->n_values; j++) { Vect_list_append(List, BoundList->value[j]); } } Vect_select_areas_by_box(&(In[1]), &abox, TmpList); for (i = 0; i < List->n_values; i++) { int j, aline; aline = abs(List->value[i]); for (j = 0; j < TmpList->n_values; j++) { int barea, bcentroid; barea = TmpList->value[j]; G_debug(3, " barea = %d", barea); if (Vect_get_area_cat(&(In[1]), barea, ifield[1]) < 0) { nskipped++; continue; } /* Check if any centroid of area B is in area A. * This test is important in if area B is completely within area A */ bcentroid = Vect_get_area_centroid(&(In[1]), barea); Vect_read_line(&(In[1]), BPoints, NULL, bcentroid); if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS if(area_relate_geos(&(In[1]), AGeom, barea, operator, parm.relate->answer)) { found = 1; break; } #endif } else { if (Vect_point_in_area(&(In[0]), aarea, BPoints->x[0], BPoints->y[0])) { found = 1; break; } /* Check intersectin of lines from List with area B */ if (line_overlap_area(&(In[0]), aline, &(In[1]), barea)) { found = 1; break; } } } if (found) { add_aarea(&(In[0]), aarea, ALines); break; } } } if (operator != OP_OVERLAP) { #ifdef HAVE_GEOS GEOSGeom_destroy(AGeom); #endif AGeom = NULL; } } } Vect_close(&(In[1])); #ifdef HAVE_GEOS finishGEOS(); #endif /* Write lines */ nfields = Vect_cidx_get_num_fields(&(In[0])); cats = (int **)G_malloc(nfields * sizeof(int *)); ncats = (int *)G_malloc(nfields * sizeof(int)); fields = (int *)G_malloc(nfields * sizeof(int)); for (i = 0; i < nfields; i++) { ncats[i] = 0; cats[i] = (int *)G_malloc(Vect_cidx_get_num_cats_by_index(&(In[0]), i) * sizeof(int)); fields[i] = Vect_cidx_get_field_number(&(In[0]), i); } G_message(_("Writing selected features...")); for (aline = 1; aline <= nalines; aline++) { int atype; G_debug(4, "aline = %d ALines[aline] = %d", aline, ALines[aline]); G_percent(aline, nalines, 2); if ((!flag.reverse->answer && !(ALines[aline])) || (flag.reverse->answer && ALines[aline])) continue; atype = Vect_read_line(&(In[0]), APoints, ACats, aline); Vect_write_line(&Out, atype, APoints, ACats); if (!(flag.table->answer) && (IFi != NULL)) { for (i = 0; i < ACats->n_cats; i++) { int f, j; for (j = 0; j < nfields; j++) { /* find field */ if (fields[j] == ACats->field[i]) { f = j; break; } } cats[f][ncats[f]] = ACats->cat[i]; ncats[f]++; } } } /* Copy tables */ if (!(flag.table->answer)) { int ttype, ntabs = 0; G_message(_("Writing attributes...")); /* Number of output tabs */ for (i = 0; i < Vect_get_num_dblinks(&(In[0])); i++) { int f, j; IFi = Vect_get_dblink(&(In[0]), i); for (j = 0; j < nfields; j++) { /* find field */ if (fields[j] == IFi->number) { f = j; break; } } if (ncats[f] > 0) ntabs++; } if (ntabs > 1) ttype = GV_MTABLE; else ttype = GV_1TABLE; for (i = 0; i < nfields; i++) { int ret; if (fields[i] == 0) continue; /* Make a list of categories */ IFi = Vect_get_field(&(In[0]), fields[i]); if (!IFi) { /* no table */ G_warning(_("Layer %d - no table"), fields[i]); continue; } OFi = Vect_default_field_info(&Out, IFi->number, IFi->name, ttype); ret = db_copy_table_by_ints(IFi->driver, IFi->database, IFi->table, OFi->driver, Vect_subst_var(OFi->database, &Out), OFi->table, IFi->key, cats[i], ncats[i]); if (ret == DB_FAILED) { G_warning(_("Layer %d - unable to copy table"), fields[i]); } else { Vect_map_add_dblink(&Out, OFi->number, OFi->name, OFi->table, IFi->key, OFi->database, OFi->driver); } } } Vect_close(&(In[0])); Vect_build(&Out); Vect_close(&Out); if (nskipped > 0) { G_warning(_("%d features without category skipped"), nskipped); } G_done_msg(_("%d features written to output."), Vect_get_num_lines(&Out)); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { struct GModule *module; struct GParams params; struct Map_info Map; struct Map_info **BgMap; /* backgroud vector maps */ int nbgmaps; /* number of registrated background maps */ enum mode action_mode; FILE *ascii; int i; int move_first, snap; int ret, layer; double move_x, move_y, move_z, thresh[3]; struct line_pnts *coord; struct ilist *List; struct cat_list *Clist; ascii = NULL; List = NULL; BgMap = NULL; nbgmaps = 0; coord = NULL; Clist = NULL; G_gisinit(argv[0]); module = G_define_module(); module->overwrite = TRUE; G_add_keyword(_("vector")); G_add_keyword(_("editing")); G_add_keyword(_("geometry")); module->description = _("Edits a vector map, allows adding, deleting " "and modifying selected vector features."); if (!parser(argc, argv, ¶ms, &action_mode)) exit(EXIT_FAILURE); /* get list of categories */ Clist = Vect_new_cat_list(); if (params.cat->answer && Vect_str_to_cat_list(params.cat->answer, Clist)) { G_fatal_error(_("Unable to get category list <%s>"), params.cat->answer); } /* open input file */ if (params.in->answer) { if (strcmp(params.in->answer, "-") != 0) { ascii = fopen(params.in->answer, "r"); if (ascii == NULL) G_fatal_error(_("Unable to open file <%s>"), params.in->answer); } else { ascii = stdin; } } if (!ascii && action_mode == MODE_ADD) G_fatal_error(_("Required parameter <%s> not set"), params.in->key); if (action_mode == MODE_CREATE) { int overwrite; overwrite = G_check_overwrite(argc, argv); if (G_find_vector2(params.map->answer, G_mapset())) { if (!overwrite) G_fatal_error(_("Vector map <%s> already exists"), params.map->answer); } /* 3D vector maps? */ ret = Vect_open_new(&Map, params.map->answer, WITHOUT_Z); if (Vect_maptype(&Map) == GV_FORMAT_OGR_DIRECT) { int type; type = Vect_option_to_types(params.type); if (type != GV_POINT && type != GV_LINE && type != GV_BOUNDARY) G_fatal_error(_("Supported feature type for OGR layer: " "%s, %s or %s"), "point", "line", "boundary"); V2_open_new_ogr(&Map, type); } if (ret == -1) { G_fatal_error(_("Unable to create vector map <%s>"), params.map->answer); } G_debug(1, "Map created"); if (ascii) { /* also add new vector features */ action_mode = MODE_ADD; } } else { /* open selected vector file */ if (action_mode == MODE_ADD) /* write */ ret = Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer); else /* read-only -- select features */ ret = Vect_open_old2(&Map, params.map->answer, G_mapset(), params.fld->answer); if (ret < 2) G_fatal_error(_("Unable to open vector map <%s> at topological level %d"), params.map->answer, 2); } G_debug(1, "Map opened"); /* open backgroud maps */ if (params.bmaps->answer) { i = 0; while (params.bmaps->answers[i]) { const char *bmap = params.bmaps->answers[i]; const char *mapset = G_find_vector2(bmap, ""); if (!mapset) G_fatal_error(_("Vector map <%s> not found"), bmap); if (strcmp( G_fully_qualified_name(params.map->answer, G_mapset()), G_fully_qualified_name(bmap, mapset)) == 0) { G_fatal_error(_("Unable to open vector map <%s> as the background map. " "It is given as vector map to be edited."), bmap); } nbgmaps++; BgMap = (struct Map_info **)G_realloc( BgMap, nbgmaps * sizeof(struct Map_info *)); BgMap[nbgmaps - 1] = (struct Map_info *)G_malloc(sizeof(struct Map_info)); if (Vect_open_old(BgMap[nbgmaps - 1], bmap, "") == -1) G_fatal_error(_("Unable to open vector map <%s>"), bmap); G_verbose_message(_("Background vector map <%s> registered"), bmap); i++; } } layer = Vect_get_field_number(&Map, params.fld->answer); i = 0; while (params.maxdist->answers[i]) { switch (i) { case THRESH_COORDS: thresh[THRESH_COORDS] = max_distance(atof(params.maxdist->answers[THRESH_COORDS])); thresh[THRESH_SNAP] = thresh[THRESH_QUERY] = thresh[THRESH_COORDS]; break; case THRESH_SNAP: thresh[THRESH_SNAP] = max_distance(atof(params.maxdist->answers[THRESH_SNAP])); break; case THRESH_QUERY: thresh[THRESH_QUERY] = atof(params.maxdist->answers[THRESH_QUERY]); break; default: break; } i++; } move_first = params.move_first->answer ? 1 : 0; snap = NO_SNAP; if (strcmp(params.snap->answer, "node") == 0) snap = SNAP; else if (strcmp(params.snap->answer, "vertex") == 0) snap = SNAPVERTEX; if (snap != NO_SNAP && thresh[THRESH_SNAP] <= 0) { G_warning(_("Threshold for snapping must be > 0. No snapping applied.")); snap = NO_SNAP; } if (action_mode != MODE_CREATE && action_mode != MODE_ADD) { /* select lines */ List = Vect_new_list(); G_message(_("Selecting features...")); if (action_mode == MODE_COPY && BgMap && BgMap[0]) { List = select_lines(BgMap[0], action_mode, ¶ms, thresh, List); } else { List = select_lines(&Map, action_mode, ¶ms, thresh, List); } } if ((action_mode != MODE_CREATE && action_mode != MODE_ADD && action_mode != MODE_SELECT)) { if (List->n_values < 1) { G_warning(_("No features selected, nothing to edit")); action_mode = MODE_NONE; ret = 0; } else { /* reopen the map for updating */ if (action_mode == MODE_ZBULK && !Vect_is_3d(&Map)) { Vect_close(&Map); G_fatal_error(_("Vector map <%s> is not 3D. Tool '%s' requires 3D vector map. " "Please convert the vector map " "to 3D using e.g. %s."), params.map->answer, params.tool->answer, "v.extrude"); } Vect_close(&Map); Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer); } } /* coords option -> array */ if (params.coord->answers) { coord = Vect_new_line_struct(); int i = 0; double east, north; while (params.coord->answers[i]) { east = atof(params.coord->answers[i]); north = atof(params.coord->answers[i + 1]); Vect_append_point(coord, east, north, 0.0); i += 2; } } /* perform requested editation */ switch (action_mode) { case MODE_CREATE: break; case MODE_ADD: if (!params.header->answer) Vect_read_ascii_head(ascii, &Map); int num_lines; num_lines = Vect_get_num_lines(&Map); ret = Vect_read_ascii(ascii, &Map); G_message(_("%d features added"), ret); if (ret > 0) { int iline; struct ilist *List_added; List_added = Vect_new_list(); for (iline = num_lines + 1; iline <= Vect_get_num_lines(&Map); iline++) Vect_list_append(List_added, iline); G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); if (snap != NO_SNAP) { /* apply snapping */ /* snap to vertex ? */ Vedit_snap_lines(&Map, BgMap, nbgmaps, List_added, thresh[THRESH_SNAP], snap == SNAP ? FALSE : TRUE); } if (params.close->answer) { /* close boundaries */ int nclosed; nclosed = close_lines(&Map, GV_BOUNDARY, thresh[THRESH_SNAP]); G_message(_("%d boundaries closed"), nclosed); } Vect_destroy_list(List_added); } break; case MODE_DEL: ret = Vedit_delete_lines(&Map, List); G_message(_("%d features deleted"), ret); break; case MODE_MOVE: move_x = atof(params.move->answers[0]); move_y = atof(params.move->answers[1]); move_z = atof(params.move->answers[2]); G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); ret = Vedit_move_lines(&Map, BgMap, nbgmaps, List, move_x, move_y, move_z, snap, thresh[THRESH_SNAP]); G_message(_("%d features moved"), ret); break; case MODE_VERTEX_MOVE: move_x = atof(params.move->answers[0]); move_y = atof(params.move->answers[1]); move_z = atof(params.move->answers[2]); G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); ret = Vedit_move_vertex(&Map, BgMap, nbgmaps, List, coord, thresh[THRESH_COORDS], thresh[THRESH_SNAP], move_x, move_y, move_z, move_first, snap); G_message(_("%d vertices moved"), ret); break; case MODE_VERTEX_ADD: ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]); G_message(_("%d vertices added"), ret); break; case MODE_VERTEX_DELETE: ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]); G_message(_("%d vertices removed"), ret); break; case MODE_BREAK: if (params.coord->answer) { ret = Vedit_split_lines(&Map, List, coord, thresh[THRESH_COORDS], NULL); } else { ret = Vect_break_lines_list(&Map, List, NULL, GV_LINES, NULL); } G_message(_("%d lines broken"), ret); break; case MODE_CONNECT: G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); ret = Vedit_connect_lines(&Map, List, thresh[THRESH_SNAP]); G_message(_("%d lines connected"), ret); break; case MODE_MERGE: ret = Vedit_merge_lines(&Map, List); G_message(_("%d lines merged"), ret); break; case MODE_SELECT: ret = print_selected(List); break; case MODE_CATADD: ret = Vedit_modify_cats(&Map, List, layer, 0, Clist); G_message(_("%d features modified"), ret); break; case MODE_CATDEL: ret = Vedit_modify_cats(&Map, List, layer, 1, Clist); G_message(_("%d features modified"), ret); break; case MODE_COPY: if (BgMap && BgMap[0]) { if (nbgmaps > 1) G_warning(_("Multiple background maps were given. " "Selected features will be copied only from " "vector map <%s>."), Vect_get_full_name(BgMap[0])); ret = Vedit_copy_lines(&Map, BgMap[0], List); } else { ret = Vedit_copy_lines(&Map, NULL, List); } G_message(_("%d features copied"), ret); break; case MODE_SNAP: G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); ret = snap_lines(&Map, List, thresh[THRESH_SNAP]); break; case MODE_FLIP: ret = Vedit_flip_lines(&Map, List); G_message(_("%d lines flipped"), ret); break; case MODE_NONE: break; case MODE_ZBULK:{ double start, step; double x1, y1, x2, y2; start = atof(params.zbulk->answers[0]); step = atof(params.zbulk->answers[1]); x1 = atof(params.bbox->answers[0]); y1 = atof(params.bbox->answers[1]); x2 = atof(params.bbox->answers[2]); y2 = atof(params.bbox->answers[3]); ret = Vedit_bulk_labeling(&Map, List, x1, y1, x2, y2, start, step); G_message(_("%d lines labeled"), ret); break; } case MODE_CHTYPE:{ ret = Vedit_chtype_lines(&Map, List); if (ret > 0) { G_message(_("%d features converted"), ret); } else { G_message(_("No feature modified")); } break; } default: G_warning(_("Operation not implemented")); ret = -1; break; } Vect_hist_command(&Map); /* build topology only if requested or if tool!=select */ if (!(action_mode == MODE_SELECT || params.topo->answer == 1 || !MODE_NONE)) { Vect_build_partial(&Map, GV_BUILD_NONE); Vect_build(&Map); } if (List) Vect_destroy_list(List); Vect_close(&Map); G_debug(1, "Map closed"); /* close background maps */ for (i = 0; i < nbgmaps; i++) { Vect_close(BgMap[i]); G_free((void *)BgMap[i]); } G_free((void *)BgMap); if (coord) Vect_destroy_line_struct(coord); if (Clist) Vect_destroy_cat_list(Clist); G_done_msg(" "); if (ret > -1) { exit(EXIT_SUCCESS); } else { exit(EXIT_FAILURE); } }
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); }
/* 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; 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 Vect_break_lines_list(struct Map_info *Map, struct ilist *List_break, struct ilist *List_ref, int type, struct Map_info *Err) { struct line_pnts *APoints, *BPoints, *Points; struct line_pnts **AXLines, **BXLines; struct line_cats *ACats, *BCats, *Cats; int j, k, l, ret, atype, btype, aline, bline, found, iline, nlines; int naxlines, nbxlines, nx; double *xx = NULL, *yx = NULL, *zx = NULL; struct bound_box ABox, BBox; struct boxlist *List; int nbreaks; int touch1_n = 0, touch1_s = 0, touch1_e = 0, touch1_w = 0; /* other vertices except node1 touching box */ int touch2_n = 0, touch2_s = 0, touch2_e = 0, touch2_w = 0; /* other vertices except node2 touching box */ int is3d; int node, anode1, anode2, bnode1, bnode2; double nodex, nodey; APoints = Vect_new_line_struct(); BPoints = Vect_new_line_struct(); Points = Vect_new_line_struct(); ACats = Vect_new_cats_struct(); BCats = Vect_new_cats_struct(); Cats = Vect_new_cats_struct(); List = Vect_new_boxlist(1); is3d = Vect_is_3d(Map); if (List_break) { nlines = List_break->n_values; } else { nlines = Vect_get_num_lines(Map); } G_debug(3, "nlines = %d", nlines); /* TODO: It seems that lines/boundaries are not broken at intersections * with points/centroids. Check if true, if yes, skip GV_POINTS */ /* To find intersection of two lines (Vect_line_intersection) is quite slow. * Fortunately usual lines/boundaries in GIS often forms a network where lines * are connected by end points, and touch by MBR. This function checks and occasionaly * skips such cases. This is currently done for 2D only */ /* Go through all lines in vector, for each select lines which overlap MBR of * this line exclude those connected by one endpoint (see above) * and try to intersect, if lines intersect write new lines at the end of * the file, and process next line (remaining lines overlapping box are skipped) */ nbreaks = 0; for (iline = 0; iline < nlines; iline++) { G_percent(iline, nlines, 1); if (List_break) { aline = List_break->value[iline]; } else { aline = iline + 1; } if (List_ref && !Vect_val_in_list(List_ref, aline)) continue; G_debug(3, "aline = %d", aline); if (!Vect_line_alive(Map, aline)) continue; atype = Vect_read_line(Map, APoints, ACats, aline); if (!(atype & type)) continue; Vect_line_box(APoints, &ABox); /* Find which sides of the box are touched by intermediate (non-end) points of line */ if (!is3d) { touch1_n = touch1_s = touch1_e = touch1_w = 0; for (j = 1; j < APoints->n_points; j++) { if (APoints->y[j] == ABox.N) touch1_n = 1; if (APoints->y[j] == ABox.S) touch1_s = 1; if (APoints->x[j] == ABox.E) touch1_e = 1; if (APoints->x[j] == ABox.W) touch1_w = 1; } G_debug(3, "touch1: n = %d s = %d e = %d w = %d", touch1_n, touch1_s, touch1_e, touch1_w); touch2_n = touch2_s = touch2_e = touch2_w = 0; for (j = 0; j < APoints->n_points - 1; j++) { if (APoints->y[j] == ABox.N) touch2_n = 1; if (APoints->y[j] == ABox.S) touch2_s = 1; if (APoints->x[j] == ABox.E) touch2_e = 1; if (APoints->x[j] == ABox.W) touch2_w = 1; } G_debug(3, "touch2: n = %d s = %d e = %d w = %d", touch2_n, touch2_s, touch2_e, touch2_w); } Vect_select_lines_by_box(Map, &ABox, type, List); G_debug(3, " %d lines selected by box", List->n_values); for (j = 0; j < List->n_values; j++) { bline = List->id[j]; if (List_break && !Vect_val_in_list(List_break, bline)) { continue; } G_debug(3, " j = %d bline = %d", j, bline); btype = Vect_read_line(Map, BPoints, BCats, bline); /* Check if thouch by end node only */ if (!is3d) { Vect_get_line_nodes(Map, aline, &anode1, &anode2); Vect_get_line_nodes(Map, bline, &bnode1, &bnode2); BBox = List->box[j]; if (anode1 == bnode1 || anode1 == bnode2) node = anode1; else if (anode2 == bnode1 || anode2 == bnode2) node = anode2; else node = 0; if (node) { Vect_get_node_coor(Map, node, &nodex, &nodey, NULL); if ((node == anode1 && nodey == ABox.N && !touch1_n && nodey == BBox.S) || (node == anode2 && nodey == ABox.N && !touch2_n && nodey == BBox.S) || (node == anode1 && nodey == ABox.S && !touch1_s && nodey == BBox.N) || (node == anode2 && nodey == ABox.S && !touch2_s && nodey == BBox.N) || (node == anode1 && nodex == ABox.E && !touch1_e && nodex == BBox.W) || (node == anode2 && nodex == ABox.E && !touch2_e && nodex == BBox.W) || (node == anode1 && nodex == ABox.W && !touch1_w && nodex == BBox.E) || (node == anode2 && nodex == ABox.W && !touch2_w && nodex == BBox.E)) { G_debug(3, "lines %d and %d touching by end nodes only -> no intersection", aline, bline); continue; } } } AXLines = NULL; BXLines = NULL; Vect_line_intersection(APoints, BPoints, &AXLines, &BXLines, &naxlines, &nbxlines, 0); G_debug(3, " naxlines = %d nbxlines = %d", naxlines, nbxlines); /* This part handles a special case when aline == bline, no other intersection was found * and the line is forming collapsed loop, for example 0,0;1,0;0,0 should be broken at 1,0. * ---> */ if (aline == bline && naxlines == 0 && nbxlines == 0 && APoints->n_points >= 3) { int centre; int i; G_debug(3, " Check collapsed loop"); if (APoints->n_points % 2) { /* odd number of vertices */ centre = APoints->n_points / 2; /* index of centre */ if (APoints->x[centre - 1] == APoints->x[centre + 1] && APoints->y[centre - 1] == APoints->y[centre + 1] && APoints->z[centre - 1] == APoints->z[centre + 1]) { /* -> break */ AXLines = (struct line_pnts **)G_malloc(2 * sizeof(struct line_pnts *)); AXLines[0] = Vect_new_line_struct(); AXLines[1] = Vect_new_line_struct(); for (i = 0; i <= centre; i++) Vect_append_point(AXLines[0], APoints->x[i], APoints->y[i], APoints->z[i]); for (i = centre; i < APoints->n_points; i++) Vect_append_point(AXLines[1], APoints->x[i], APoints->y[i], APoints->z[i]); naxlines = 2; } } } /* <--- */ if (Err) { /* array for intersections (more than needed */ xx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); yx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); zx = (double *)G_malloc((naxlines + nbxlines) * sizeof(double)); } nx = 0; /* number of intersections to be written to Err */ if (naxlines > 0) { /* intersection -> write out */ Vect_delete_line(Map, aline); for (k = 0; k < naxlines; k++) { /* Write new line segments */ /* line may collapse, don't write zero length lines */ Vect_line_prune(AXLines[k]); if ((atype & GV_POINTS) || AXLines[k]->n_points > 1) { ret = Vect_write_line(Map, atype, AXLines[k], ACats); if (List_ref) { Vect_list_append(List_ref, ret); } G_debug(3, "Line %d written, npoints = %d", ret, AXLines[k]->n_points); if (List_break) { Vect_list_append(List_break, ret); } } /* Write intersection points */ if (Err) { if (k > 0) { xx[nx] = AXLines[k]->x[0]; yx[nx] = AXLines[k]->y[0]; zx[nx] = AXLines[k]->z[0]; nx++; } } Vect_destroy_line_struct(AXLines[k]); } nbreaks += naxlines - 1; } if (AXLines) G_free(AXLines); if (nbxlines > 0) { if (aline != bline) { /* Self intersection, do not write twice, TODO: is it OK? */ Vect_delete_line(Map, bline); for (k = 0; k < nbxlines; k++) { /* Write new line segments */ /* line may collapse, don't write zero length lines */ Vect_line_prune(BXLines[k]); if ((btype & GV_POINTS) || BXLines[k]->n_points > 1) { ret = Vect_write_line(Map, btype, BXLines[k], BCats); G_debug(5, "Line %d written", ret); if (List_break) { Vect_list_append(List_break, ret); } } /* Write intersection points */ if (Err) { if (k > 0) { found = 0; for (l = 0; l < nx; l++) { if (xx[l] == BXLines[k]->x[0] && yx[l] == BXLines[k]->y[0] && zx[l] == BXLines[k]->z[0]) { found = 1; break; } } if (!found) { xx[nx] = BXLines[k]->x[0]; yx[nx] = BXLines[k]->y[0]; zx[nx] = BXLines[k]->z[0]; nx++; } } } Vect_destroy_line_struct(BXLines[k]); } nbreaks += nbxlines - 1; } else { for (k = 0; k < nbxlines; k++) Vect_destroy_line_struct(BXLines[k]); } } if (BXLines) G_free(BXLines); if (Err) { for (l = 0; l < nx; l++) { /* Write out errors */ Vect_reset_line(Points); Vect_append_point(Points, xx[l], yx[l], zx[l]); ret = Vect_write_line(Err, GV_POINT, Points, Cats); } G_free(xx); G_free(yx); G_free(zx); } if (naxlines > 0) break; /* first line was broken and deleted -> take the next one */ } if (List_break) { nlines = List_break->n_values; } else { nlines = Vect_get_num_lines(Map); } G_debug(3, "nlines = %d", nlines); } /* for each line */ G_percent(nlines, nlines, 1); /* finish it */ G_verbose_message(_("Intersections: %d"), nbreaks); Vect_destroy_line_struct(APoints); Vect_destroy_line_struct(BPoints); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(ACats); Vect_destroy_cats_struct(BCats); Vect_destroy_cats_struct(Cats); Vect_destroy_boxlist(List); return nbreaks; }
/*! \brief Get number of bridges in the graph. Bridge is an array containing the indices of the bridges. \param graph input graph \param[out] bridge_list list of bridges \return number of bridges, -1 on error */ int NetA_compute_bridges(dglGraph_s * graph, struct ilist *bridge_list) { int nnodes; int bridges = 0; dglEdgesetTraverser_s *current; /*edge to be processed when the node is visited */ int *tin, *min_tin; /*time in, and smallest tin over all successors. 0 if not yet visited */ dglInt32_t *parent; /*edge from parent to the node */ dglInt32_t **stack; /*stack of nodes */ dglInt32_t **current_edge; /*current edge for each node */ dglNodeTraverser_s nt; dglInt32_t *current_node; int stack_size; int i, time; nnodes = dglGet_NodeCount(graph); current = (dglEdgesetTraverser_s *) G_calloc(nnodes + 1, sizeof(dglEdgesetTraverser_s)); tin = (int *)G_calloc(nnodes + 1, sizeof(int)); min_tin = (int *)G_calloc(nnodes + 1, sizeof(int)); parent = (dglInt32_t *) G_calloc(nnodes + 1, sizeof(dglInt32_t)); stack = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); current_edge = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); if (!tin || !min_tin || !parent || !stack || !current) { G_fatal_error(_("Out of memory")); return -1; } for (i = 1; i <= nnodes; i++) { dglEdgeset_T_Initialize(¤t[i], graph, dglNodeGet_OutEdgeset(graph, dglGetNode(graph, i))); current_edge[i] = dglEdgeset_T_First(¤t[i]); tin[i] = 0; } dglNode_T_Initialize(&nt, graph); time = 0; for (current_node = dglNode_T_First(&nt); current_node; current_node = dglNode_T_Next(&nt)) { dglInt32_t current_id = dglNodeGet_Id(graph, current_node); if (tin[current_id] == 0) { stack[0] = current_node; stack_size = 1; parent[current_id] = 0; while (stack_size) { dglInt32_t *node = stack[stack_size - 1]; dglInt32_t node_id = dglNodeGet_Id(graph, node); if (tin[node_id] == 0) /*vertex visited for the first time */ min_tin[node_id] = tin[node_id] = ++time; else { /*return from the recursion */ dglInt32_t to = dglNodeGet_Id(graph, dglEdgeGet_Tail(graph, current_edge [node_id])); if (min_tin[to] > tin[node_id]) { /*no path from the subtree above the current node */ Vect_list_append(bridge_list, dglEdgeGet_Id(graph, current_edge[node_id])); /*so it must be a bridge */ bridges++; } if (min_tin[to] < min_tin[node_id]) min_tin[node_id] = min_tin[to]; current_edge[node_id] = dglEdgeset_T_Next(¤t[node_id]); /*proceed to the next edge */ } for (; current_edge[node_id]; current_edge[node_id] = dglEdgeset_T_Next(¤t[node_id])) { /*try next edges */ dglInt32_t *to = dglEdgeGet_Tail(graph, current_edge[node_id]); dglInt32_t edge_id = dglEdgeGet_Id(graph, current_edge[node_id]); if (abs(edge_id) == parent[node_id]) continue; /*skip edge we used to travel to this node */ int to_id = dglNodeGet_Id(graph, to); if (tin[to_id]) { /*back edge, cannot be a bridge/articualtion point */ if (tin[to_id] < min_tin[node_id]) min_tin[node_id] = tin[to_id]; } else { /*forward edge */ parent[to_id] = abs(edge_id); stack[stack_size++] = to; break; } } if (!current_edge[node_id]) stack_size--; /*current node completely processed */ } } } dglNode_T_Release(&nt); for (i = 1; i <= nnodes; i++) dglEdgeset_T_Release(¤t[i]); G_free(current); G_free(tin); G_free(min_tin); G_free(parent); G_free(stack); G_free(current_edge); return bridges; }
/* Called by Vect_remove_bridges() and Vect_chtype_bridges(): chtype = 0 -> works like Vect_remove_bridges() chtype = 1 -> works like Vect_chtype_bridges() Algorithm: Go thorough all lines, if both sides of the line have left and side 0 (candidate) do this check: follow adjacent lines in one direction (nearest to the right at the end node), if we reach this line again without dangle in the way, but with this line traversed from other side it is a bridge. List of all lines in chain is created during the cycle. */ void remove_bridges(struct Map_info *Map, int chtype, struct Map_info *Err) { int i, type, nlines, line; int left, right, node1, node2, current_line, next_line; int bridges_removed = 0; /* number of removed bridges */ int lines_removed = 0; /* number of lines removed */ char *lmsg; struct Plus_head *Plus; struct line_pnts *Points; struct line_cats *Cats; struct ilist *CycleList; struct ilist *BridgeList; int dangle, other_side; if (chtype) lmsg = "changed lines"; else lmsg = "removed lines"; Plus = &(Map->plus); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); CycleList = Vect_new_list(); BridgeList = Vect_new_list(); nlines = Vect_get_num_lines(Map); G_debug(1, "nlines = %d", nlines); for (line = 1; line <= nlines; line++) { G_percent(line, nlines, 1); if (!Vect_line_alive(Map, line)) continue; type = Vect_read_line(Map, NULL, NULL, line); if (!(type & GV_BOUNDARY)) continue; Vect_get_line_areas(Map, line, &left, &right); if (left != 0 || right != 0) continue; /* Cannot be bridge */ G_debug(2, "line %d - bridge candidate", line); Vect_get_line_nodes(Map, line, &node1, &node2); if (abs(node1) == abs(node2)) continue; /* either zero length or loop -> cannot be a bridge */ current_line = -line; /* we start with negative (go forward, node2 ) */ dangle = 0; other_side = 0; Vect_reset_list(CycleList); Vect_reset_list(BridgeList); while (1) { next_line = dig_angle_next_line(Plus, current_line, GV_RIGHT, GV_BOUNDARY); /* Add this line to the list */ /* TODO: Vect_val_in_list() and Vect_list_append() behave O(n) * change to O(log n) */ if (Vect_val_in_list(CycleList, abs(next_line))) /* other side -> bridge chain */ Vect_list_append(BridgeList, abs(next_line)); else dig_list_add(CycleList, abs(next_line)); /* not in list, can add new line fast */ if (abs(next_line) == abs(current_line)) { G_debug(4, " dangle -> no bridge"); dangle = 1; break; } if (abs(next_line) == line) { /* start line reached */ /* which side */ if (next_line < 0) { /* other side (connected by node 2) */ G_debug(5, " other side reached"); other_side = 1; } else { /* start side */ break; } } current_line = -next_line; /* change the sign to look at the next node in following cycle */ } if (!dangle && other_side) { G_debug(3, " line %d is part of bridge chain", line); for (i = 0; i < BridgeList->n_values; i++) { Vect_read_line(Map, Points, Cats, BridgeList->value[i]); if (Err) { Vect_write_line(Err, GV_BOUNDARY, Points, Cats); } if (!chtype) Vect_delete_line(Map, BridgeList->value[i]); else Vect_rewrite_line(Map, BridgeList->value[i], GV_LINE, Points, Cats); lines_removed++; } bridges_removed++; } } G_verbose_message("Removed lines: %d", lines_removed); G_verbose_message("Removed bridges: %d", bridges_removed); }
/*! \brief Split selected lines on given position \param Map pointer to Map_info \param List list of selected lines \param coord points location \param thresh threshold \param[out] List_updated list of rewritten features (or NULL) \return number of modified lines \return -1 on error */ int Vedit_split_lines(struct Map_info *Map, struct ilist *List, struct line_pnts *coord, double thresh, struct ilist *List_updated) { int i, j, l; int type, line, seg, newline; int nlines_modified; double px, py, spdist, lpdist, dist; double *x, *y, *z; struct line_pnts *Points, *Points2; struct line_cats *Cats; struct ilist *List_in_box; nlines_modified = 0; Points = Vect_new_line_struct(); Points2 = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); List_in_box = Vect_new_list(); for (i = 0; i < List->n_values; i++) { line = List->value[i]; if (!Vect_line_alive(Map, line)) continue; type = Vect_read_line(Map, Points, Cats, line); if (!(type & GV_LINES)) continue; x = Points->x; y = Points->y; z = Points->z; for (j = 0; j < coord->n_points; j++) { seg = Vect_line_distance(Points, coord->x[j], coord->y[j], coord->z[j], WITHOUT_Z, &px, &py, NULL, &dist, &spdist, &lpdist); if (dist > thresh) { continue; } G_debug(3, "Vedit_split_lines(): line=%d, x=%f, y=%f, px=%f, py=%f, seg=%d, " "dist=%f, spdist=%f, lpdist=%f", line, coord->x[j], coord->y[j], px, py, seg, dist, spdist, lpdist); if (spdist <= 0.0 || spdist >= Vect_line_length(Points)) continue; G_debug(3, "Vedit_split_lines(): line=%d", line); /* copy first line part */ for (l = 0; l < seg; l++) { Vect_append_point(Points2, x[l], y[l], z[l]); } /* add last vertex */ Vect_append_point(Points2, px, py, 0.0); /* rewrite the line */ newline = Vect_rewrite_line(Map, line, type, Points2, Cats); if (newline < 0) { return -1; } if (List_updated) Vect_list_append(List_updated, newline); Vect_reset_line(Points2); /* add given vertex */ Vect_append_point(Points2, px, py, 0.0); /* copy second line part */ for (l = seg; l < Points->n_points; l++) { Vect_append_point(Points2, x[l], y[l], z[l]); } /* rewrite the line */ newline = Vect_write_line(Map, type, Points2, Cats); if (newline < 0) { return -1; } if (List_updated) Vect_list_append(List_updated, newline); nlines_modified++; } /* for each bounding box */ } /* for each selected line */ Vect_destroy_line_struct(Points); Vect_destroy_line_struct(Points2); Vect_destroy_cats_struct(Cats); Vect_destroy_list(List_in_box); return nlines_modified; }
/*! \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; }