static int merge_lines(struct line_pnts *Points1, struct line_cats *Cats1, struct line_pnts *Points2, struct line_cats *Cats2, double thresh, struct line_pnts **Points) { struct line_pnts *ps = *Points; struct line_cats *cs = Cats1; int i, mindistidx; double mindist; /* find mininal distance and its index */ mindist = Vedit_get_min_distance(Points1, Points2, 0, /* TODO 3D */ &mindistidx); G_debug(3, " merge line ? index: %d, mindist: %g, thresh: %g", mindistidx, mindist, thresh); if (thresh > 0 && mindist > thresh) { return 0; } /* set index and other things */ switch (mindistidx) { /* for each mindistidx create new line */ case 0: Vect_append_points(ps, Points2, GV_BACKWARD); if (ps->n_points == Points2->n_points) Vect_append_points(ps, Points1, GV_FORWARD); break; case 1: Vect_append_points(ps, Points2, GV_FORWARD); if (ps->n_points == Points2->n_points) Vect_append_points(ps, Points1, GV_FORWARD); break; case 2: if (ps->n_points == 0) Vect_append_points(ps, Points1, GV_FORWARD); Vect_append_points(ps, Points2, GV_FORWARD); break; case 3: if (ps->n_points == 0) Vect_append_points(ps, Points1, GV_FORWARD); Vect_append_points(ps, Points2, GV_BACKWARD); break; default: break; } /* remove duplicate points */ Vect_line_prune(ps); /* copy categories if needed */ for (i = 0; i < Cats2->n_cats; i++) { Vect_cat_set(cs, Cats2->field[i], Cats2->cat[i]); } return 1; }
/*! \brief Get area boundary points (PostGIS Topology) Used by Vect_build_line_area() and Vect_get_area_points(). \param Map pointer to Map_info struct \param lines array of boundary lines \param n_lines number of lines in array \param[out] APoints pointer to output line_pnts struct \return number of points \return -1 on error */ int Vect__get_area_points_pg(const struct Map_info *Map, const plus_t *lines, int n_lines, struct line_pnts *APoints) { int i, direction; struct Format_info_pg *pg_info; PGresult *res; pg_info = (struct Format_info_pg *)&(Map->fInfo.pg); Vect_reset_line(APoints); res = build_stmt(&(Map->plus), pg_info, lines, n_lines); if (!res) return -1; for (i = 0; i < n_lines; i++) { Vect__cache_feature_pg(PQgetvalue(res, i, 0), FALSE, FALSE, &(pg_info->cache), NULL); /* do caching in readable way */ direction = lines[i] > 0 ? GV_FORWARD : GV_BACKWARD; Vect_append_points(APoints, pg_info->cache.lines[0], direction); APoints->n_points--; /* skip last point, avoids duplicates */ } APoints->n_points++; /* close polygon */ PQclear(res); return APoints->n_points; }
/*! \brief Get area boundary points (native format) Used by Vect_build_line_area() and Vect_get_area_points(). \param Map pointer to Map_info struct \param lines array of boundary lines \param n_lines number of lines in array \param[out] APoints pointer to output line_pnts struct \return number of points \return -1 on error */ int Vect__get_area_points_nat(const struct Map_info *Map, const plus_t *lines, int n_lines, struct line_pnts *BPoints) { int i, line, aline, dir; static struct line_pnts *Points; if (!Points) Points = Vect_new_line_struct(); Vect_reset_line(BPoints); for (i = 0; i < n_lines; i++) { line = lines[i]; aline = abs(line); G_debug(5, " append line(%d) = %d", i, line); if (0 > Vect_read_line(Map, Points, NULL, aline)) return -1; dir = line > 0 ? GV_FORWARD : GV_BACKWARD; Vect_append_points(BPoints, Points, dir); BPoints->n_points--; /* skip last point, avoids duplicates */ } BPoints->n_points++; /* close polygon */ return BPoints->n_points; }
void QgsGrassEditNewLine::mouseMove( QgsPoint & newPoint ) { if ( e->mEditPoints->n_points > 0 ) { // Draw the line with new segment Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); Vect_append_point( e->mPoints, newPoint.x(), newPoint.y(), 0.0 ); e->displayDynamic( e->mPoints ); } }
/** * \brief Create network arcs (edge) based on given point vector map (nodes) * * \param file input file defining arcs * \param Points input vector point map * \param Out output vector map * \param afield arcs layer * \param nfield nodes layer * * \return number of new arcs */ int create_arcs(FILE * file, struct Map_info *Pnts, struct Map_info *Out, int afield, int nfield) { char buff[1024]; int lcat, fcat, tcat; int node1, node2; int narcs; struct line_pnts *points, *points2; struct line_cats *cats; points = Vect_new_line_struct(); points2 = Vect_new_line_struct(); points = Vect_new_line_struct(); cats = Vect_new_cats_struct(); narcs = 0; while (G_getl2(buff, sizeof(buff) - 1, file)) { if (sscanf(buff, "%d%d%d", &lcat, &fcat, &tcat) != 3) G_fatal_error(_("Error reading file: '%s'"), buff); node1 = find_node(Pnts, afield, fcat); node2 = find_node(Pnts, afield, tcat); if (node1 < 1 || node2 < 1) { G_warning(_("Skipping arc %d"), lcat); continue; } /* geometry */ Vect_read_line(Pnts, points, cats, node1); field2n(cats, nfield); Vect_write_line(Out, GV_POINT, points, cats); Vect_read_line(Pnts, points2, cats, node2); field2n(cats, nfield); Vect_write_line(Out, GV_POINT, points2, cats); Vect_append_points(points, points2, GV_FORWARD); /* category */ Vect_reset_cats(cats); Vect_cat_set(cats, afield, lcat); Vect_write_line(Out, GV_LINE, points, cats); narcs++; } Vect_destroy_line_struct(points); Vect_destroy_cats_struct(cats); return narcs; }
void QgsGrassEditNewLine::deactivate() { // Delete last segment if ( e->mEditPoints->n_points > 1 ) { Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); e->displayDynamic( e->mPoints ); } e->setCanvasPrompt( tr( "New vertex" ), "", "" ); QgsGrassEditTool::deactivate(); // call default bahivour }
/*! \brief Returns polygon array of points (outer ring) of given area \param Map pointer to Map_info structure \param area area id \param[out] BPoints points array \return number of points \return -1 on error */ int Vect_get_area_points(const struct Map_info *Map, int area, struct line_pnts *BPoints) { int i, line, aline, dir; const struct Plus_head *Plus; struct P_area *Area; static int first_time = 1; static struct line_pnts *Points; G_debug(3, "Vect_get_area_points(): area = %d", area); BPoints->n_points = 0; Plus = &(Map->plus); Area = Plus->Area[area]; if (Area == NULL) { /* dead area */ G_warning(_("Attempt to read points of nonexistent area")); return -1; /* error , because we should not read dead areas */ } if (first_time == 1) { Points = Vect_new_line_struct(); first_time = 0; } G_debug(3, " n_lines = %d", Area->n_lines); for (i = 0; i < Area->n_lines; i++) { line = Area->lines[i]; aline = abs(line); G_debug(3, " append line(%d) = %d", i, line); if (0 > Vect_read_line(Map, Points, NULL, aline)) { G_fatal_error(_("Unable to read read line %d"), aline); } G_debug(3, " line n_points = %d", Points->n_points); if (line > 0) dir = GV_FORWARD; else dir = GV_BACKWARD; Vect_append_points(BPoints, Points, dir); if (i != (Area->n_lines - 1)) /* all but not last */ BPoints->n_points--; G_debug(3, " area n_points = %d", BPoints->n_points); } return BPoints->n_points; }
void QgsGrassEditNewLine::activate() { QgsDebugMsg( "entered." ); // Display dynamic segment if ( e->mEditPoints->n_points > 0 ) { Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); QgsPoint point = toMapCoordinates( e->mCanvas->mouseLastXY() ); Vect_append_point( e->mPoints, point.x(), point.y(), 0.0 ); e->displayDynamic( e->mPoints ); } QgsGrassEditTool::activate(); // call default bahivour }
/*! \brief Returns polygon array of points for given isle \param Map vector map \param isle island id \param[out] BPoints points array \return number of points \return -1 on error */ int Vect_get_isle_points(const struct Map_info *Map, int isle, struct line_pnts *BPoints) { int i, line, aline, dir; const struct Plus_head *Plus; struct P_isle *Isle; static int first_time = 1; static struct line_pnts *Points; G_debug(3, "Vect_get_isle_points(): isle = %d", isle); BPoints->n_points = 0; Plus = &(Map->plus); Isle = Plus->Isle[isle]; if (first_time == 1) { Points = Vect_new_line_struct(); first_time = 0; } G_debug(3, " n_lines = %d", Isle->n_lines); for (i = 0; i < Isle->n_lines; i++) { line = Isle->lines[i]; aline = abs(line); G_debug(3, " append line(%d) = %d", i, line); if (0 > Vect_read_line(Map, Points, NULL, aline)) { G_fatal_error(_("Unable to read line %d"), aline); } G_debug(3, " line n_points = %d", Points->n_points); if (line > 0) dir = GV_FORWARD; else dir = GV_BACKWARD; Vect_append_points(BPoints, Points, dir); if (i != (Isle->n_lines - 1)) /* all but not last */ BPoints->n_points--; G_debug(3, " isle n_points = %d", BPoints->n_points); } return (BPoints->n_points); }
void QgsGrassEditMoveLine::mouseMove( QgsPoint & newPoint ) { // Move previously selected line if ( e->mSelectedLine > 0 ) { // Transform coordinates Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); for ( int i = 0; i < e->mPoints->n_points; i++ ) { e->mPoints->x[i] += newPoint.x() - e->mLastPoint.x(); e->mPoints->y[i] += newPoint.y() - e->mLastPoint.y(); } e->displayDynamic( e->mPoints ); } }
/* merge a given line with all other lines of the same type and * with the same categories */ static int merge_line(struct Map_info *Map, int line, struct line_pnts *MPoints, struct line_cats *MCats) { int nlines, i, first, last, next_line, curr_line; int merged = 0, newl = 0; int next_node, direction, node_n_lines, type, ltype, lines_type; static struct ilist *List = NULL; static struct line_pnts *Points = NULL; static struct line_cats *Cats = NULL; type = GV_LINE; nlines = Vect_get_num_lines(Map); if (!Points) Points = Vect_new_line_struct(); if (!Cats) Cats = Vect_new_cats_struct(); if (!List) List = Vect_new_list(); Vect_reset_line(Points); Vect_reset_cats(Cats); Vect_reset_cats(MCats); Vect_reset_list(List); if (!Vect_line_alive(Map, line)) return 0; ltype = Vect_get_line_type(Map, line); if (!(ltype & type)) return 0; Vect_read_line(Map, MPoints, MCats, line); /* special cases: * - loop back to start boundary via several other boundaries * - one boundary forming closed loop * - node with 3 entries but only 2 boundaries, one of them connecting twice, * the other one must then be topologically incorrect in case of boundary */ /* go backward as long as there is only one other line/boundary at the current node */ G_debug(3, "go backward"); Vect_get_line_nodes(Map, line, &next_node, NULL); first = -line; while (1) { node_n_lines = Vect_get_node_n_lines(Map, next_node); /* count lines/boundaries at this node */ lines_type = 0; next_line = first; for (i = 0; i < node_n_lines; i++) { curr_line = Vect_get_node_line(Map, next_node, i); if ((Vect_get_line_type(Map, abs(curr_line)) & GV_LINES)) lines_type++; if ((Vect_get_line_type(Map, abs(curr_line)) == ltype)) { if (abs(curr_line) != abs(first)) { Vect_read_line(Map, NULL, Cats, abs(curr_line)); /* catgories must be identical */ if (compare_cats(MCats, Cats) == 0) next_line = curr_line; } } } if (lines_type == 2 && abs(next_line) != abs(first) && abs(next_line) != line) { first = next_line; if (first < 0) { Vect_get_line_nodes(Map, -first, &next_node, NULL); } else { Vect_get_line_nodes(Map, first, NULL, &next_node); } } else break; } /* go forward as long as there is only one other line/boundary at the current node */ G_debug(3, "go forward"); /* reverse direction */ last = -first; if (last < 0) { Vect_get_line_nodes(Map, -last, &next_node, NULL); } else { Vect_get_line_nodes(Map, last, NULL, &next_node); } Vect_reset_list(List); while (1) { G_ilist_add(List, last); node_n_lines = Vect_get_node_n_lines(Map, next_node); lines_type = 0; next_line = last; for (i = 0; i < node_n_lines; i++) { curr_line = Vect_get_node_line(Map, next_node, i); if ((Vect_get_line_type(Map, abs(curr_line)) & GV_LINES)) lines_type++; if ((Vect_get_line_type(Map, abs(curr_line)) == ltype)) { if (abs(curr_line) != abs(last)) { Vect_read_line(Map, NULL, Cats, abs(curr_line)); if (compare_cats(MCats, Cats) == 0) next_line = curr_line; } } } if (lines_type == 2 && abs(next_line) != abs(last) && abs(next_line) != abs(first)) { last = next_line; if (last < 0) { Vect_get_line_nodes(Map, -last, &next_node, NULL); } else { Vect_get_line_nodes(Map, last, NULL, &next_node); } } else break; } /* merge lines */ G_debug(3, "merge %d lines", List->n_values); Vect_reset_line(MPoints); for (i = 0; i < List->n_values; i++) { Vect_reset_line(Points); Vect_read_line(Map, Points, Cats, abs(List->value[i])); direction = (List->value[i] < 0 ? GV_BACKWARD : GV_FORWARD); Vect_append_points(MPoints, Points, direction); MPoints->n_points--; Vect_delete_line(Map, abs(List->value[i])); } MPoints->n_points++; merged += List->n_values; newl++; return merged; }
/*! \brief Create buffer around the line line. Buffer is closed counter clockwise polygon. Warning: output line may contain loops! \param InPoints input line \param distance create buffer in distance \param tolerance maximum distance between theoretical arc and polygon segments \param[out] OutPoints output line */ void Vect_line_buffer(struct line_pnts *InPoints, double distance, double tolerance, struct line_pnts *OutPoints) { double dangle; int side, npoints; static struct line_pnts *Points = NULL; static struct line_pnts *PPoints = NULL; distance = fabs(distance); dangle = 2 * acos(1 - tolerance / fabs(distance)); /* angle step */ if (Points == NULL) Points = Vect_new_line_struct(); if (PPoints == NULL) PPoints = Vect_new_line_struct(); /* Copy and prune input */ Vect_reset_line(Points); Vect_append_points(Points, InPoints, GV_FORWARD); Vect_line_prune(Points); Vect_reset_line(OutPoints); npoints = Points->n_points; if (npoints <= 0) { return; } else if (npoints == 1) { /* make a circle */ double angle, x, y; for (angle = 0; angle < 2 * PI; angle += dangle) { x = Points->x[0] + distance * cos(angle); y = Points->y[0] + distance * sin(angle); Vect_append_point(OutPoints, x, y, 0); } /* Close polygon */ Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0); } else { /* 2 and more points */ for (side = 0; side < 2; side++) { double angle, sangle; double lx1, ly1, lx2, ly2; double x, y, nx, ny, sx, sy, ex, ey; /* Parallel on one side */ if (side == 0) { Vect_line_parallel(Points, distance, tolerance, 0, PPoints); Vect_append_points(OutPoints, PPoints, GV_FORWARD); } else { Vect_line_parallel(Points, -distance, tolerance, 0, PPoints); Vect_append_points(OutPoints, PPoints, GV_BACKWARD); } /* Arc at the end */ /* 2 points at theend of original line */ if (side == 0) { lx1 = Points->x[npoints - 2]; ly1 = Points->y[npoints - 2]; lx2 = Points->x[npoints - 1]; ly2 = Points->y[npoints - 1]; } else { lx1 = Points->x[1]; ly1 = Points->y[1]; lx2 = Points->x[0]; ly2 = Points->y[0]; } /* normalized vector */ vect(lx1, ly1, lx2, ly2, &nx, &ny); /* starting point */ sangle = atan2(-nx, ny); /* starting angle */ sx = lx2 + ny * distance; sy = ly2 - nx * distance; /* end point */ ex = lx2 - ny * distance; ey = ly2 + nx * distance; Vect_append_point(OutPoints, sx, sy, 0); /* arc */ for (angle = dangle; angle < PI; angle += dangle) { x = lx2 + distance * cos(sangle + angle); y = ly2 + distance * sin(sangle + angle); Vect_append_point(OutPoints, x, y, 0); } Vect_append_point(OutPoints, ex, ey, 0); } /* Close polygon */ Vect_append_point(OutPoints, OutPoints->x[0], OutPoints->y[0], 0); } Vect_line_prune(OutPoints); return; }
int display_area(struct Map_info *Map, struct cat_list *Clist, const struct Cell_head *window, const struct color_rgb *bcolor, const struct color_rgb *fcolor, int chcat, int id_flag, int cats_color_flag, int default_width, double width_scale, struct Colors *zcolors, dbCatValArray *cvarr_rgb, struct Colors *colors, dbCatValArray *cvarr_width, int nrec_width) { int num, area, isle, n_isles, n_points; double xl, yl; struct line_pnts *Points, * APoints, **IPoints; struct line_cats *Cats; int n_ipoints_alloc; int cat, centroid; int red, grn, blu; int i, custom_rgb, found; int width; struct bound_box box; if (Vect_level(Map) < 2) { G_warning(_("Unable to display areas, topology not available. " "Please try to rebuild topology using " "v.build or v.build.all.")); return 1; } G_debug(1, "display areas:"); centroid = 0; Points = Vect_new_line_struct(); APoints = Vect_new_line_struct(); n_ipoints_alloc = 10; IPoints = (struct line_pnts **)G_malloc(n_ipoints_alloc * sizeof(struct line_pnts *)); for (i = 0; i < n_ipoints_alloc; i++) { IPoints[i] = Vect_new_line_struct(); } Cats = Vect_new_cats_struct(); num = Vect_get_num_areas(Map); G_debug(2, "\tn_areas = %d", num); for (area = 1; area <= num; area++) { G_debug(3, "\tarea = %d", area); if (!Vect_area_alive(Map, area)) continue; centroid = Vect_get_area_centroid(Map, area); if (!centroid) { continue; } /* Check box */ Vect_get_area_box(Map, area, &box); if (box.N < window->south || box.S > window->north || box.E < window->west || box.W > window->east) { if (window->proj != PROJECTION_LL) continue; else { /* out of bounds for -180 to 180, try 0 to 360 as well */ if (box.N < window->south || box.S > window->north) continue; if (box.E + 360 < window->west || box.W + 360 > window->east) continue; } } custom_rgb = FALSE; found = FALSE; if (chcat) { if (id_flag) { if (!(Vect_cat_in_cat_list(area, Clist))) continue; } else { G_debug(3, "centroid = %d", centroid); if (centroid < 1) continue; Vect_read_line(Map, Points, Cats, centroid); for (i = 0; i < Cats->n_cats; i++) { G_debug(3, " centroid = %d, field = %d, cat = %d", centroid, Cats->field[i], Cats->cat[i]); if (Cats->field[i] == Clist->field && Vect_cat_in_cat_list(Cats->cat[i], Clist)) { found = TRUE; break; } } if (!found) continue; } } else if (Clist->field > 0) { found = FALSE; G_debug(3, "\tcentroid = %d", centroid); if (centroid < 1) continue; Vect_read_line(Map, NULL, Cats, centroid); for (i = 0; i < Cats->n_cats; i++) { G_debug(3, "\tcentroid = %d, field = %d, cat = %d", centroid, Cats->field[i], Cats->cat[i]); if (Cats->field[i] == Clist->field) { found = TRUE; break; } } /* lines with no category will be displayed */ if (Cats->n_cats > 0 && !found) continue; } /* fill */ Vect_get_area_points(Map, area, APoints); G_debug(3, "\tn_points = %d", APoints->n_points); if (APoints->n_points < 3) { G_warning(_("Invalid area %d skipped (not enough points)"), area); continue; } Vect_reset_line(Points); Vect_append_points(Points, APoints, GV_FORWARD); n_points = Points->n_points; xl = Points->x[n_points - 1]; yl = Points->y[n_points - 1]; n_isles = Vect_get_area_num_isles(Map, area); if (n_isles >= n_ipoints_alloc) { IPoints = (struct line_pnts **)G_realloc(IPoints, (n_isles + 10) * sizeof(struct line_pnts *)); for (i = n_ipoints_alloc; i < n_isles + 10; i++) { IPoints[i] = Vect_new_line_struct(); } n_ipoints_alloc = n_isles + 10; } for (i = 0; i < n_isles; i++) { isle = Vect_get_area_isle(Map, area, i); Vect_get_isle_points(Map, isle, IPoints[i]); Vect_append_points(Points, IPoints[i], GV_FORWARD); Vect_append_point(Points, xl, yl, 0.0); /* ??? */ } cat = Vect_get_area_cat(Map, area, (Clist->field > 0 ? Clist->field : (Cats->n_cats > 0 ? Cats->field[0] : 1))); if (!centroid && cat == -1) { continue; } /* z height colors */ if (zcolors) { if (Rast_get_d_color(&Points->z[0], &red, &grn, &blu, zcolors) == 1) custom_rgb = TRUE; else custom_rgb = FALSE; } /* custom colors */ if (colors || cvarr_rgb) { custom_rgb = get_table_color(cat, area, colors, cvarr_rgb, &red, &grn, &blu); } /* random colors */ if (cats_color_flag) { custom_rgb = get_cat_color(area, Cats, Clist, &red, &grn, &blu); } /* line width */ if (nrec_width) { width = (int) get_property(cat, area, cvarr_width, (double) width_scale, (double) default_width); D_line_width(width); } if (fcolor || zcolors) { if (!cvarr_rgb && !cats_color_flag && !zcolors && !colors) { D_RGB_color(fcolor->r, fcolor->g, fcolor->b); D_polygon_abs(Points->x, Points->y, Points->n_points); } else { if (custom_rgb) { D_RGB_color((unsigned char)red, (unsigned char)grn, (unsigned char)blu); } else { D_RGB_color(fcolor->r, fcolor->g, fcolor->b); } if (cat >= 0) { D_polygon_abs(Points->x, Points->y, Points->n_points); } } } /* boundary */ if (bcolor) { if (custom_rgb) { D_RGB_color((unsigned char)red, (unsigned char)grn, (unsigned char)blu); } else { D_RGB_color(bcolor->r, bcolor->g, bcolor->b); } /* use different user defined render methods */ D_polyline_abs(APoints->x, APoints->y, APoints->n_points); for (i = 0; i < n_isles; i++) { /* use different user defined render methods */ D_polyline_abs(IPoints[i]->x, IPoints[i]->y, IPoints[i]->n_points); } } } if ((colors || cvarr_rgb) && get_num_color_rules_skipped() > 0) G_warning(_n("%d invalid color rule for areas skipped", "%d invalid color rules for areas skipped", get_num_color_rules_skipped()), get_num_color_rules_skipped()); Vect_destroy_line_struct(Points); Vect_destroy_line_struct(APoints); for (i = 0; i < n_ipoints_alloc; i++) { Vect_destroy_line_struct(IPoints[i]); } G_free(IPoints); Vect_destroy_cats_struct(Cats); return 0; }
int main(int argc, char *argv[]) { struct Map_info In, Out, Error; struct line_pnts *Points; struct line_cats *Cats; int i, type, iter; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out, *error_out, *thresh_opt, *method_opt, *look_ahead_opt; struct Option *iterations_opt, *cat_opt, *alpha_opt, *beta_opt, *type_opt; struct Option *field_opt, *where_opt, *reduction_opt, *slide_opt; struct Option *angle_thresh_opt, *degree_thresh_opt, *closeness_thresh_opt; struct Option *betweeness_thresh_opt; struct Flag *notab_flag, *loop_support_flag; int with_z; int total_input, total_output; /* Number of points in the input/output map respectively */ double thresh, alpha, beta, reduction, slide, angle_thresh; double degree_thresh, closeness_thresh, betweeness_thresh; int method; int look_ahead, iterations; int loop_support; int layer; int n_lines; int simplification, mask_type; struct cat_list *cat_list = NULL; char *s, *descriptions; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("generalization")); G_add_keyword(_("simplification")); G_add_keyword(_("smoothing")); G_add_keyword(_("displacement")); G_add_keyword(_("network generalization")); module->description = _("Performs vector based generalization."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary,area"; type_opt->answer = "line,boundary,area"; type_opt->guisection = _("Selection"); map_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out->key = "error"; error_out->required = NO; error_out->description = _("Error map of all lines and boundaries not being generalized due to topology issues or over-simplification"); method_opt = G_define_option(); method_opt->key = "method"; method_opt->type = TYPE_STRING; method_opt->required = YES; method_opt->multiple = NO; method_opt->options = "douglas,douglas_reduction,lang,reduction,reumann,boyle,sliding_averaging,distance_weighting,chaiken,hermite,snakes,network,displacement"; descriptions = NULL; G_asprintf(&descriptions, "douglas;%s;" "douglas_reduction;%s;" "lang;%s;" "reduction;%s;" "reumann;%s;" "boyle;%s;" "sliding_averaging;%s;" "distance_weighting;%s;" "chaiken;%s;" "hermite;%s;" "snakes;%s;" "network;%s;" "displacement;%s;", _("Douglas-Peucker Algorithm"), _("Douglas-Peucker Algorithm with reduction parameter"), _("Lang Simplification Algorithm"), _("Vertex Reduction Algorithm eliminates points close to each other"), _("Reumann-Witkam Algorithm"), _("Boyle's Forward-Looking Algorithm"), _("McMaster's Sliding Averaging Algorithm"), _("McMaster's Distance-Weighting Algorithm"), _("Chaiken's Algorithm"), _("Interpolation by Cubic Hermite Splines"), _("Snakes method for line smoothing"), _("Network generalization"), _("Displacement of lines close to each other")); method_opt->descriptions = G_store(descriptions); method_opt->description = _("Generalization algorithm"); thresh_opt = G_define_option(); thresh_opt->key = "threshold"; thresh_opt->type = TYPE_DOUBLE; thresh_opt->required = YES; thresh_opt->options = "0-1000000000"; thresh_opt->description = _("Maximal tolerance value"); look_ahead_opt = G_define_option(); look_ahead_opt->key = "look_ahead"; look_ahead_opt->type = TYPE_INTEGER; look_ahead_opt->required = NO; look_ahead_opt->answer = "7"; look_ahead_opt->description = _("Look-ahead parameter"); reduction_opt = G_define_option(); reduction_opt->key = "reduction"; reduction_opt->type = TYPE_DOUBLE; reduction_opt->required = NO; reduction_opt->answer = "50"; reduction_opt->options = "0-100"; reduction_opt->description = _("Percentage of the points in the output of 'douglas_reduction' algorithm"); slide_opt = G_define_option(); slide_opt->key = "slide"; slide_opt->type = TYPE_DOUBLE; slide_opt->required = NO; slide_opt->answer = "0.5"; slide_opt->options = "0-1"; slide_opt->description = _("Slide of computed point toward the original point"); angle_thresh_opt = G_define_option(); angle_thresh_opt->key = "angle_thresh"; angle_thresh_opt->type = TYPE_DOUBLE; angle_thresh_opt->required = NO; angle_thresh_opt->answer = "3"; angle_thresh_opt->options = "0-180"; angle_thresh_opt->description = _("Minimum angle between two consecutive segments in Hermite method"); degree_thresh_opt = G_define_option(); degree_thresh_opt->key = "degree_thresh"; degree_thresh_opt->type = TYPE_INTEGER; degree_thresh_opt->required = NO; degree_thresh_opt->answer = "0"; degree_thresh_opt->description = _("Degree threshold in network generalization"); closeness_thresh_opt = G_define_option(); closeness_thresh_opt->key = "closeness_thresh"; closeness_thresh_opt->type = TYPE_DOUBLE; closeness_thresh_opt->required = NO; closeness_thresh_opt->answer = "0"; closeness_thresh_opt->options = "0-1"; closeness_thresh_opt->description = _("Closeness threshold in network generalization"); betweeness_thresh_opt = G_define_option(); betweeness_thresh_opt->key = "betweeness_thresh"; betweeness_thresh_opt->type = TYPE_DOUBLE; betweeness_thresh_opt->required = NO; betweeness_thresh_opt->answer = "0"; betweeness_thresh_opt->description = _("Betweeness threshold in network generalization"); alpha_opt = G_define_option(); alpha_opt->key = "alpha"; alpha_opt->type = TYPE_DOUBLE; alpha_opt->required = NO; alpha_opt->answer = "1.0"; alpha_opt->description = _("Snakes alpha parameter"); beta_opt = G_define_option(); beta_opt->key = "beta"; beta_opt->type = TYPE_DOUBLE; beta_opt->required = NO; beta_opt->answer = "1.0"; beta_opt->description = _("Snakes beta parameter"); iterations_opt = G_define_option(); iterations_opt->key = "iterations"; iterations_opt->type = TYPE_INTEGER; iterations_opt->required = NO; iterations_opt->answer = "1"; iterations_opt->description = _("Number of iterations"); cat_opt = G_define_standard_option(G_OPT_V_CATS); cat_opt->guisection = _("Selection"); where_opt = G_define_standard_option(G_OPT_DB_WHERE); where_opt->guisection = _("Selection"); loop_support_flag = G_define_flag(); loop_support_flag->key = 'l'; loop_support_flag->label = _("Disable loop support"); loop_support_flag->description = _("Do not modify end points of lines forming a closed loop"); notab_flag = G_define_standard_flag(G_FLG_V_TABLE); notab_flag->description = _("Do not copy attributes"); notab_flag->guisection = _("Attributes"); /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); thresh = atof(thresh_opt->answer); look_ahead = atoi(look_ahead_opt->answer); alpha = atof(alpha_opt->answer); beta = atof(beta_opt->answer); reduction = atof(reduction_opt->answer); iterations = atoi(iterations_opt->answer); slide = atof(slide_opt->answer); angle_thresh = atof(angle_thresh_opt->answer); degree_thresh = atof(degree_thresh_opt->answer); closeness_thresh = atof(closeness_thresh_opt->answer); betweeness_thresh = atof(betweeness_thresh_opt->answer); mask_type = type_mask(type_opt); G_debug(3, "Method: %s", method_opt->answer); s = method_opt->answer; if (strcmp(s, "douglas") == 0) method = DOUGLAS; else if (strcmp(s, "lang") == 0) method = LANG; else if (strcmp(s, "reduction") == 0) method = VERTEX_REDUCTION; else if (strcmp(s, "reumann") == 0) method = REUMANN; else if (strcmp(s, "boyle") == 0) method = BOYLE; else if (strcmp(s, "distance_weighting") == 0) method = DISTANCE_WEIGHTING; else if (strcmp(s, "chaiken") == 0) method = CHAIKEN; else if (strcmp(s, "hermite") == 0) method = HERMITE; else if (strcmp(s, "snakes") == 0) method = SNAKES; else if (strcmp(s, "douglas_reduction") == 0) method = DOUGLAS_REDUCTION; else if (strcmp(s, "sliding_averaging") == 0) method = SLIDING_AVERAGING; else if (strcmp(s, "network") == 0) method = NETWORK; else if (strcmp(s, "displacement") == 0) { method = DISPLACEMENT; /* we can displace only the lines */ mask_type = GV_LINE; } else { G_fatal_error(_("Unknown method")); exit(EXIT_FAILURE); } /* simplification or smoothing? */ switch (method) { case DOUGLAS: case DOUGLAS_REDUCTION: case LANG: case VERTEX_REDUCTION: case REUMANN: simplification = 1; break; default: simplification = 0; break; } Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (Vect_open_old2(&In, map_in->answer, "", field_opt->answer) < 1) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); if (Vect_get_num_primitives(&In, mask_type) == 0) { G_warning(_("No lines found in input map <%s>"), map_in->answer); Vect_close(&In); exit(EXIT_SUCCESS); } with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (error_out->answer) { if (0 > Vect_open_new(&Error, error_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create error vector map <%s>"), error_out->answer); } } Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); total_input = total_output = 0; layer = Vect_get_field_number(&In, field_opt->answer); /* parse filter options */ if (layer > 0) cat_list = Vect_cats_set_constraint(&In, layer, where_opt->answer, cat_opt->answer); if (method == DISPLACEMENT) { /* modifies only lines, all other features including boundaries are preserved */ /* options where, cats, and layer are respected */ G_message(_("Displacement...")); snakes_displacement(&In, &Out, thresh, alpha, beta, 1.0, 10.0, iterations, cat_list, layer); } /* TODO: rearrange code below. It's really messy */ if (method == NETWORK) { /* extracts lines of selected type, all other features are discarded */ /* options where, cats, and layer are ignored */ G_message(_("Network generalization...")); total_output = graph_generalization(&In, &Out, mask_type, degree_thresh, closeness_thresh, betweeness_thresh); } /* copy tables here because method == NETWORK is complete and * tables for Out may be needed for parse_filter_options() below */ if (!notab_flag->answer) { if (method == NETWORK) copy_tables_by_cats(&In, &Out); else Vect_copy_tables(&In, &Out, -1); } else if (where_opt->answer && method < NETWORK) { G_warning(_("Attributes are needed for 'where' option, copying table")); Vect_copy_tables(&In, &Out, -1); } /* smoothing/simplification */ if (method < NETWORK) { /* modifies only lines of selected type, all other features are preserved */ int not_modified_boundaries = 0, n_oversimplified = 0; struct line_pnts *APoints; /* original Points */ set_topo_debug(); Vect_copy_map_lines(&In, &Out); Vect_build_partial(&Out, GV_BUILD_CENTROIDS); G_message("-----------------------------------------------------"); G_message(_("Generalization (%s)..."), method_opt->answer); G_message(_("Using threshold: %g %s"), thresh, G_database_unit_name(1)); G_percent_reset(); APoints = Vect_new_line_struct(); n_lines = Vect_get_num_lines(&Out); for (i = 1; i <= n_lines; i++) { int after = 0; G_percent(i, n_lines, 1); type = Vect_read_line(&Out, APoints, Cats, i); if (!(type & GV_LINES) || !(mask_type & type)) continue; if (layer > 0) { if ((type & GV_LINE) && !Vect_cats_in_constraint(Cats, layer, cat_list)) continue; else if ((type & GV_BOUNDARY)) { int do_line = 0; int left, right; do_line = Vect_cats_in_constraint(Cats, layer, cat_list); if (!do_line) { /* check if any of the centroids is selected */ Vect_get_line_areas(&Out, i, &left, &right); if (left < 0) left = Vect_get_isle_area(&Out, abs(left)); if (right < 0) right = Vect_get_isle_area(&Out, abs(right)); if (left > 0) { Vect_get_area_cats(&Out, left, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } if (!do_line && right > 0) { Vect_get_area_cats(&Out, right, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } } if (!do_line) continue; } } Vect_line_prune(APoints); if (APoints->n_points < 2) /* Line of length zero, delete if boundary ? */ continue; total_input += APoints->n_points; /* copy points */ Vect_reset_line(Points); Vect_append_points(Points, APoints, GV_FORWARD); loop_support = 0; if (!loop_support_flag->answer) { int n1, n2; Vect_get_line_nodes(&Out, i, &n1, &n2); if (n1 == n2) { if (Vect_get_node_n_lines(&Out, n1) == 2) { if (abs(Vect_get_node_line(&Out, n1, 0)) == i && abs(Vect_get_node_line(&Out, n1, 1)) == i) loop_support = 1; } } } for (iter = 0; iter < iterations; iter++) { switch (method) { case DOUGLAS: douglas_peucker(Points, thresh, with_z); break; case DOUGLAS_REDUCTION: douglas_peucker_reduction(Points, thresh, reduction, with_z); break; case LANG: lang(Points, thresh, look_ahead, with_z); break; case VERTEX_REDUCTION: vertex_reduction(Points, thresh, with_z); break; case REUMANN: reumann_witkam(Points, thresh, with_z); break; case BOYLE: boyle(Points, look_ahead, loop_support, with_z); break; case SLIDING_AVERAGING: sliding_averaging(Points, slide, look_ahead, loop_support, with_z); break; case DISTANCE_WEIGHTING: distance_weighting(Points, slide, look_ahead, loop_support, with_z); break; case CHAIKEN: chaiken(Points, thresh, loop_support, with_z); break; case HERMITE: hermite(Points, thresh, angle_thresh, loop_support, with_z); break; case SNAKES: snakes(Points, alpha, beta, loop_support, with_z); break; } } if (loop_support == 0) { /* safety check, BUG in method if not passed */ if (APoints->x[0] != Points->x[0] || APoints->y[0] != Points->y[0] || APoints->z[0] != Points->z[0]) G_fatal_error(_("Method '%s' did not preserve first point"), method_opt->answer); if (APoints->x[APoints->n_points - 1] != Points->x[Points->n_points - 1] || APoints->y[APoints->n_points - 1] != Points->y[Points->n_points - 1] || APoints->z[APoints->n_points - 1] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve last point"), method_opt->answer); } else { /* safety check, BUG in method if not passed */ if (Points->x[0] != Points->x[Points->n_points - 1] || Points->y[0] != Points->y[Points->n_points - 1] || Points->z[0] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve loop"), method_opt->answer); } Vect_line_prune(Points); /* oversimplified line */ if (Points->n_points < 2) { after = APoints->n_points; n_oversimplified++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } /* check for topology corruption */ else if (type == GV_BOUNDARY) { if (!check_topo(&Out, i, APoints, Points, Cats)) { after = APoints->n_points; not_modified_boundaries++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } else after = Points->n_points; } else { /* type == GV_LINE */ Vect_rewrite_line(&Out, i, type, Points, Cats); after = Points->n_points; } total_output += after; } if (not_modified_boundaries > 0) G_warning(_("%d boundaries were not modified because modification would damage topology"), not_modified_boundaries); if (n_oversimplified > 0) G_warning(_("%d lines/boundaries were not modified due to over-simplification"), n_oversimplified); G_message("-----------------------------------------------------"); /* make sure that clean topo is built at the end */ Vect_build_partial(&Out, GV_BUILD_NONE); if (error_out->answer) Vect_build_partial(&Error, GV_BUILD_NONE); } Vect_build(&Out); if (error_out->answer) Vect_build(&Error); Vect_close(&In); Vect_close(&Out); if (error_out->answer) Vect_close(&Error); G_message("-----------------------------------------------------"); if (total_input != 0 && total_input != total_output) G_done_msg(_("Number of vertices for selected features %s from %d to %d (%d%% remaining)"), simplification ? _("reduced") : _("changed"), total_input, total_output, (total_output * 100) / total_input); else G_done_msg(" "); exit(EXIT_SUCCESS); }
/*! \brief Read next feature from OGR layer. Skip empty features (level 1) This function implements sequential access. The action of this routine can be modified by: - Vect_read_constraint_region() - Vect_read_constraint_type() - Vect_remove_constraints() \param Map pointer to Map_info structure \param[out] line_p container used to store line points within \param[out] line_c container used to store line categories within \return feature type \return -2 no more features (EOF) \return -1 out of memory */ int V1_read_next_line_ogr(struct Map_info *Map, struct line_pnts *line_p, struct line_cats *line_c) { int itype; struct bound_box lbox, mbox; OGRFeatureH hFeature; OGRGeometryH hGeom; G_debug(3, "V1_read_next_line_ogr()"); if (line_p != NULL) Vect_reset_line(line_p); if (line_c != NULL) Vect_reset_cats(line_c); if (Map->Constraint_region_flag) Vect_get_constraint_box(Map, &mbox); while (TRUE) { /* Read feature to cache if necessary */ while (Map->fInfo.ogr.lines_next == Map->fInfo.ogr.lines_num) { hFeature = OGR_L_GetNextFeature(Map->fInfo.ogr.layer); if (hFeature == NULL) { return -2; } /* no more features */ hGeom = OGR_F_GetGeometryRef(hFeature); if (hGeom == NULL) { /* feature without geometry */ OGR_F_Destroy(hFeature); continue; } Map->fInfo.ogr.feature_cache_id = (int)OGR_F_GetFID(hFeature); if (Map->fInfo.ogr.feature_cache_id == OGRNullFID) { G_warning(_("OGR feature without ID")); } /* Cache the feature */ Map->fInfo.ogr.lines_num = 0; cache_feature(Map, hGeom, -1); G_debug(4, "%d lines read to cache", Map->fInfo.ogr.lines_num); OGR_F_Destroy(hFeature); Map->fInfo.ogr.lines_next = 0; /* next to be read from cache */ } /* Read next part of the feature */ G_debug(4, "read next cached line %d", Map->fInfo.ogr.lines_next); itype = Map->fInfo.ogr.lines_types[Map->fInfo.ogr.lines_next]; /* Constraint on Type of line * Default is all of Point, Line, Area and whatever else comes along */ if (Map->Constraint_type_flag) { if (!(itype & Map->Constraint_type)) { Map->fInfo.ogr.lines_next++; continue; } } /* Constraint on specified region */ if (Map->Constraint_region_flag) { Vect_line_box(Map->fInfo.ogr.lines[Map->fInfo.ogr.lines_next], &lbox); if (!Vect_box_overlap(&lbox, &mbox)) { Map->fInfo.ogr.lines_next++; continue; } } if (line_p != NULL) Vect_append_points(line_p, Map->fInfo.ogr.lines[Map->fInfo.ogr. lines_next], GV_FORWARD); if (line_c != NULL && Map->fInfo.ogr.feature_cache_id != OGRNullFID) Vect_cat_set(line_c, 1, Map->fInfo.ogr.feature_cache_id); Map->fInfo.ogr.lines_next++; G_debug(4, "next line read, type = %d", itype); return itype; } return -2; /* not reached */ }
int main(int argc, char *argv[]) { struct GModule *module; struct Option *in_opt, *layer_opt, *out_opt, *length_opt, *units_opt, *vertices_opt; struct Flag *nosplit_flag, *fixedlength_flag; struct Map_info In, Out; struct line_pnts *Points, *Points2, *Points3; struct line_cats *Cats; int line, nlines, layer, nosplit, fixedlength; double length = -1; int vertices = 0; double (*line_length) (); int geodesic = 0; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("geometry")); G_add_keyword(_("densification")); G_add_keyword(_("node")); G_add_keyword(_("segment")); G_add_keyword(_("vertex")); module->description = _("Splits vector lines to shorter segments."); in_opt = G_define_standard_option(G_OPT_V_INPUT); layer_opt = G_define_standard_option(G_OPT_V_FIELD_ALL); out_opt = G_define_standard_option(G_OPT_V_OUTPUT); length_opt = G_define_option(); length_opt->key = "length"; length_opt->type = TYPE_DOUBLE; length_opt->required = NO; length_opt->multiple = NO; length_opt->description = _("Maximum segment length"); units_opt = G_define_option(); units_opt->key = "units"; units_opt->type = TYPE_STRING; units_opt->required = NO; units_opt->multiple = NO; units_opt->options = "map,meters,kilometers,feet,surveyfeet,miles,nautmiles"; units_opt->answer = "map"; units_opt->description = _("Length units"); vertices_opt = G_define_option(); vertices_opt->key = "vertices"; vertices_opt->type = TYPE_INTEGER; vertices_opt->required = NO; vertices_opt->multiple = NO; vertices_opt->description = _("Maximum number of vertices in segment"); nosplit_flag = G_define_flag(); nosplit_flag->key = 'n'; nosplit_flag->label = _("Add new vertices, but do not split"); nosplit_flag->description = _("Applies only to 'length' option"); fixedlength_flag = G_define_flag(); fixedlength_flag->key = 'f'; fixedlength_flag->label = _("Force segments to be exactly of given length, except for last one"); fixedlength_flag->description = _("Applies only to 'length' option"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); if ((length_opt->answer && vertices_opt->answer) || !(length_opt->answer || vertices_opt->answer)) G_fatal_error(_("Use either length or vertices")); line_length = NULL; if (length_opt->answer) { length = atof(length_opt->answer); if (length <= 0) G_fatal_error(_("Length must be positive but is %g"), length); /* convert length to meters */ if (strcmp(units_opt->answer, "meters") == 0) /* do nothing */ ; else if (strcmp(units_opt->answer, "kilometers") == 0) length *= FROM_KILOMETERS; else if (strcmp(units_opt->answer, "feet") == 0) length *= FROM_FEET; else if (strcmp(units_opt->answer, "surveyfeet") == 0) length *= FROM_SFEET; else if (strcmp(units_opt->answer, "miles") == 0) length *= FROM_MILES; else if (strcmp(units_opt->answer, "nautmiles") == 0) length *= FROM_NAUTMILES; else if (strcmp(units_opt->answer, "map") != 0) G_fatal_error(_("Unknown unit %s"), units_opt->answer); /* set line length function */ if (G_projection() == PROJECTION_LL) { if (strcmp(units_opt->answer, "map") == 0) line_length = Vect_line_length; else { line_length = Vect_line_geodesic_length; geodesic = 1; } } else { double factor; line_length = Vect_line_length; /* convert length to map units */ if ((factor = G_database_units_to_meters_factor()) == 0) G_fatal_error(_("Can not get projection units")); else if (strcmp(units_opt->answer, "map") != 0) { /* meters to units */ length = length / factor; } } if (strcmp(units_opt->answer, "map") == 0) G_verbose_message(_("Length in map units: %g"), length); else G_verbose_message(_("Length in meters: %g"), length); } if (vertices_opt->answer) { vertices = atoi(vertices_opt->answer); if (vertices < 2) G_fatal_error(_("Number of vertices must be at least 2")); } nosplit = nosplit_flag->answer; fixedlength = fixedlength_flag->answer; Vect_set_open_level(2); if (Vect_open_old2(&In, in_opt->answer, "", layer_opt->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), in_opt->answer); layer = Vect_get_field_number(&In, layer_opt->answer); if (Vect_open_new(&Out, out_opt->answer, Vect_is_3d(&In)) < 0) G_fatal_error(_("Unable to create vector map <%s>"), out_opt->answer); Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); Vect_copy_tables(&In, &Out, layer); Points = Vect_new_line_struct(); Points2 = Vect_new_line_struct(); Points3 = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); nlines = Vect_get_num_lines(&In); for (line = 1; line <= nlines; line++) { int ltype; G_percent(line, nlines, 1); if (!Vect_line_alive(&In, line)) continue; ltype = Vect_read_line(&In, Points, Cats, line); if (layer != -1 && !Vect_cat_get(Cats, layer, NULL)) continue; if (ltype & GV_LINES) { if (length > 0) { double l, from, to, step; l = line_length(Points); if (l <= length) { Vect_write_line(&Out, ltype, Points, Cats); } else { long n, i; G_debug(3, "l: %f, length: %f", l, length); n = ceil(l / length); if (geodesic) l = Vect_line_length(Points); if (fixedlength) { step = length; } else { step = l / n; } from = 0.; G_debug(3, "n: %ld, step: %f", n, step); if (nosplit) Vect_reset_line(Points3); for (i = 0; i < n; i++) { int ret; double x, y, z; if (i == n - 1) { to = l; /* to be sure that it goes to end */ } else { to = from + step; } ret = Vect_line_segment(Points, from, to, Points2); if (ret == 0) { G_warning(_("Unable to make line segment: %f - %f (line length = %f)"), from, to, l); continue; } /* To be sure that the coordinates are identical */ if (i > 0) { Points2->x[0] = x; Points2->y[0] = y; Points2->z[0] = z; } if (i == n - 1) { Points2->x[Points2->n_points - 1] = Points->x[Points->n_points - 1]; Points2->y[Points2->n_points - 1] = Points->y[Points->n_points - 1]; Points2->z[Points2->n_points - 1] = Points->z[Points->n_points - 1]; } if (nosplit) { if (Points3->n_points > 0) Points3->n_points--; Vect_append_points(Points3, Points2, GV_FORWARD); } else Vect_write_line(&Out, ltype, Points2, Cats); /* last point */ x = Points2->x[Points2->n_points - 1]; y = Points2->y[Points2->n_points - 1]; z = Points2->z[Points2->n_points - 1]; from += step; } if (nosplit) Vect_write_line(&Out, ltype, Points3, Cats); } } else { int start = 0; /* number of coordinates written */ while (start < Points->n_points - 1) { int i, v = 0; Vect_reset_line(Points2); for (i = 0; i < vertices; i++) { v = start + i; if (v == Points->n_points) break; Vect_append_point(Points2, Points->x[v], Points->y[v], Points->z[v]); } Vect_write_line(&Out, ltype, Points2, Cats); start = v; } } } else { Vect_write_line(&Out, ltype, Points, Cats); } } Vect_close(&In); Vect_build(&Out); Vect_close(&Out); exit(EXIT_SUCCESS); }
/* Start from the first node on a polyline and walk to the other end, collecting the coordinates of each node en route. */ int walk_forward_and_pick_up_coords(struct Map_info *map, int start_line, int ltype, struct line_pnts *points, int *lines_visited, struct line_cats *Cats, int write_cats) { int cat_idx; int line, next_line, n1, n2; int type, node, next_node; struct line_pnts *pnts; struct line_cats *cats_tmp; G_debug(2, " walk_forward() start = %d", start_line); line = start_line; pnts = Vect_new_line_struct(); if (write_cats != NO_CATS) { cats_tmp = Vect_new_cats_struct(); } else { cats_tmp = NULL; } Vect_reset_line(points); /* Pick up first set of coordinates */ lines_visited[line] = 1; if (cats_tmp) type = Vect_read_line(map, pnts, Cats, line); else type = Vect_read_line(map, pnts, NULL, line); Vect_get_line_nodes(map, line, &n1, &n2); next_line = find_next_line(map, line, n1, ltype); if (next_line > 0) { /* continue at start node */ Vect_append_points(points, pnts, GV_BACKWARD); next_node = n1; } else { Vect_append_points(points, pnts, GV_FORWARD); next_line = find_next_line(map, line, n2, ltype); /* check end node */ if (next_line > 0) { next_node = n2; /* continue at end node */ } else { return 1; /* no other line */ } } /* While next line exist append coordinates */ line = next_line; node = next_node; while (line != 0 && line != start_line) { G_debug(2, " line = %d", line); type = Vect_read_line(map, pnts, cats_tmp, line); if (cats_tmp && write_cats == MULTI_CATS) { for (cat_idx = 0; cat_idx < cats_tmp->n_cats; cat_idx++) { Vect_cat_set(Cats, cats_tmp->field[cat_idx], cats_tmp->cat[cat_idx]); } } Vect_get_line_nodes(map, line, &n1, &n2); if (node == n1) { Vect_line_delete_point(pnts, 0); /* delete duplicate nodes */ Vect_append_points(points, pnts, GV_FORWARD); next_node = n2; } else { Vect_line_delete_point(pnts, pnts->n_points - 1); Vect_append_points(points, pnts, GV_BACKWARD); next_node = n1; } lines_visited[line] = 1; /* Find next one */ next_line = find_next_line(map, line, next_node, ltype); line = next_line; node = next_node; } if (cats_tmp) Vect_destroy_cats_struct(cats_tmp); return 1; }
void QgsGrassEditNewLine::mouseClick( QgsPoint & point, Qt::MouseButton button ) { switch ( button ) { case Qt::LeftButton: if ( e->mEditPoints->n_points > 2 ) { e->snap( point, e->mEditPoints->x[0], e->mEditPoints->y[0] ); } else { e->snap( point ); } Vect_append_point( e->mEditPoints, point.x(), point.y(), 0.0 ); // Draw Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); e->displayDynamic( e->mPoints ); break; case Qt::MidButton: if ( e->mEditPoints->n_points > 0 ) { e->mEditPoints->n_points--; Vect_reset_line( e->mPoints ); Vect_append_points( e->mPoints, e->mEditPoints, GV_FORWARD ); QgsPoint point = toMapCoordinates( e->mCanvas->mouseLastXY() ); Vect_append_point( e->mPoints, point.x(), point.y(), 0.0 ); e->displayDynamic( e->mPoints ); } break; case Qt::RightButton: e->eraseDynamic(); if ( e->mEditPoints->n_points > 1 ) { int type; if ( mNewBoundary ) // boundary or line? type = GV_BOUNDARY; else type = GV_LINE; int line; line = e->writeLine( type, e->mEditPoints ); e->updateSymb(); e->displayUpdated(); if ( e->mAttributes ) { e->mAttributes->setLine( line ); e->mAttributes->clear(); } else { e->mAttributes = new QgsGrassAttributes( e, e->mProvider, line, e->mIface->mainWindow() ); } for ( int i = 0; i < e->mCats->n_cats; i++ ) { e->addAttributes( e->mCats->field[i], e->mCats->cat[i] ); } e->mAttributes->show(); e->mAttributes->raise(); } Vect_reset_line( e->mEditPoints ); break; default: // ignore others break; } if ( e->mEditPoints->n_points == 0 ) { e->setCanvasPrompt( tr( "New vertex" ), "", "" ); } else if ( e->mEditPoints->n_points == 1 ) { e->setCanvasPrompt( tr( "New vertex" ), tr( "Undo last vertex" ), "" ); } else if ( e->mEditPoints->n_points > 1 ) { e->setCanvasPrompt( tr( "New vertex" ), tr( "Undo last vertex" ), tr( "Close line" ) ); } }
int main(int argc, char *argv[]) { struct Map_info In, Out; static struct line_pnts *Points, *PPoints; struct line_cats *Cats, *TCats; struct ilist *slist; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out; struct Option *catf_opt, *fieldf_opt, *wheref_opt; struct Option *catt_opt, *fieldt_opt, *wheret_opt, *typet_opt; struct Option *afield_opt, *nfield_opt, *abcol, *afcol, *ncol, *atype_opt; struct Flag *geo_f, *segments_f; int with_z, geo, segments; int atype, ttype; struct varray *varrayf, *varrayt; int flayer, tlayer; int afield, nfield; dglGraph_s *graph; struct ilist *nodest; int i, j, nnodes, nlines; int *dst, *nodes_to_features; int from_nr; /* 'from' features not reachable */ dglInt32_t **nxt; struct line_cats **on_path; char *segdir; char buf[2000]; /* Attribute table */ dbString sql; dbDriver *driver; struct field_info *Fi; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("network")); G_add_keyword(_("shortest path")); module->label = _("Computes shortest distance via the network between " "the given sets of features."); module->description = _("Finds the shortest paths from each 'from' point to the nearest 'to' feature " "and various information about this relation are uploaded to the attribute table."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); map_out = G_define_standard_option(G_OPT_V_OUTPUT); afield_opt = G_define_standard_option(G_OPT_V_FIELD); afield_opt->key = "arc_layer"; afield_opt->answer = "1"; afield_opt->label = _("Arc layer"); afield_opt->guisection = _("Cost"); atype_opt = G_define_standard_option(G_OPT_V_TYPE); atype_opt->key = "arc_type"; atype_opt->options = "line,boundary"; atype_opt->answer = "line,boundary"; atype_opt->label = _("Arc type"); atype_opt->guisection = _("Cost"); nfield_opt = G_define_standard_option(G_OPT_V_FIELD); nfield_opt->key = "node_layer"; nfield_opt->answer = "2"; nfield_opt->label = _("Node layer"); nfield_opt->guisection = _("Cost"); fieldf_opt = G_define_standard_option(G_OPT_V_FIELD); fieldf_opt->key = "from_layer"; fieldf_opt->label = _("From layer number or name"); fieldf_opt->guisection = _("From"); catf_opt = G_define_standard_option(G_OPT_V_CATS); catf_opt->key = "from_cats"; catf_opt->label = _("From category values"); catf_opt->guisection = _("From"); wheref_opt = G_define_standard_option(G_OPT_DB_WHERE); wheref_opt->key = "from_where"; wheref_opt->label = _("From WHERE conditions of SQL statement without 'where' keyword"); wheref_opt->guisection = _("From"); fieldt_opt = G_define_standard_option(G_OPT_V_FIELD); fieldt_opt->key = "to_layer"; fieldt_opt->description = _("To layer number or name"); fieldt_opt->guisection = _("To"); typet_opt = G_define_standard_option(G_OPT_V_TYPE); typet_opt->key = "to_type"; typet_opt->options = "point,line,boundary"; typet_opt->answer = "point"; typet_opt->description = _("To feature type"); typet_opt->guisection = _("To"); catt_opt = G_define_standard_option(G_OPT_V_CATS); catt_opt->key = "to_cats"; catt_opt->label = _("To category values"); catt_opt->guisection = _("To"); wheret_opt = G_define_standard_option(G_OPT_DB_WHERE); wheret_opt->key = "to_where"; wheret_opt->label = _("To WHERE conditions of SQL statement without 'where' keyword"); wheret_opt->guisection = _("To"); afcol = G_define_standard_option(G_OPT_DB_COLUMN); afcol->key = "arc_column"; afcol->required = NO; afcol->description = _("Arc forward/both direction(s) cost column (number)"); afcol->guisection = _("Cost"); abcol = G_define_standard_option(G_OPT_DB_COLUMN); abcol->key = "arc_backward_column"; abcol->required = NO; abcol->description = _("Arc backward direction cost column (number)"); abcol->guisection = _("Cost"); ncol = G_define_standard_option(G_OPT_DB_COLUMN); ncol->key = "node_column"; ncol->required = NO; ncol->description = _("Node cost column (number)"); ncol->guisection = _("Cost"); geo_f = G_define_flag(); geo_f->key = 'g'; geo_f->description = _("Use geodesic calculation for longitude-latitude locations"); segments_f = G_define_flag(); #if 0 /* use this to sync with v.net.path */ segments_f->key = 's'; segments_f->description = _("Write output as original input segments, " "not each path as one line."); #else segments_f->key = 'l'; segments_f->description = _("Write each output path as one line, " "not as original input segments."); #endif /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); atype = Vect_option_to_types(atype_opt); ttype = Vect_option_to_types(typet_opt); Points = Vect_new_line_struct(); PPoints = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); TCats = Vect_new_cats_struct(); slist = G_new_ilist(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (1 > Vect_open_old(&In, map_in->answer, "")) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (geo_f->answer) { geo = 1; if (G_projection() != PROJECTION_LL) G_warning(_("The current projection is not longitude-latitude")); } else geo = 0; #if 0 /* use this to sync with v.net.path */ segments = segments_f->answer; #else segments = !segments_f->answer; #endif nnodes = Vect_get_num_nodes(&In); nlines = Vect_get_num_lines(&In); dst = (int *)G_calloc(nnodes + 1, sizeof(int)); nxt = (dglInt32_t **) G_calloc(nnodes + 1, sizeof(dglInt32_t *)); nodes_to_features = (int *)G_calloc(nnodes + 1, sizeof(int)); on_path = (struct line_cats **)G_calloc(nlines + 1, sizeof(struct line_cats *)); segdir = (char *)G_calloc(nlines + 1, sizeof(char)); if (!dst || !nxt || !nodes_to_features || !on_path || !segdir) G_fatal_error(_("Out of memory")); for (i = 1; i <= nlines; i++) { on_path[i] = Vect_new_cats_struct(); segdir[i] = 0; } /*initialise varrays and nodes list appropriatelly */ afield = Vect_get_field_number(&In, afield_opt->answer); nfield = Vect_get_field_number(&In, nfield_opt->answer); flayer = atoi(fieldf_opt->answer); tlayer = atoi(fieldt_opt->answer); if (NetA_initialise_varray(&In, flayer, GV_POINT, wheref_opt->answer, catf_opt->answer, &varrayf) <= 0) { G_fatal_error(_("No 'from' features selected. " "Please check options '%s', '%s', '%s'."), fieldf_opt->key, wheref_opt->key, catf_opt->key); } if (NetA_initialise_varray(&In, tlayer, ttype, wheret_opt->answer, catt_opt->answer, &varrayt) <= 0) { G_fatal_error(_("No 'to' features selected. " "Please check options '%s', '%s', '%s'."), fieldt_opt->key, wheret_opt->key, catt_opt->key); } nodest = Vect_new_list(); NetA_varray_to_nodes(&In, varrayt, nodest, nodes_to_features); if (nodest->n_values == 0) G_fatal_error(_("No 'to' features")); if (0 != Vect_net_build_graph(&In, atype, afield, nfield, afcol->answer, abcol->answer, ncol->answer, geo, 2)) G_fatal_error(_("Unable to build graph for vector map <%s>"), Vect_get_full_name(&In)); graph = Vect_net_get_graph(&In); G_message(_("Distances to 'to' features ...")); NetA_distance_to_points(graph, nodest, dst, nxt); /* Create table */ Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE); Vect_map_add_dblink(&Out, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database, Fi->driver); db_init_string(&sql); driver = db_start_driver_open_database(Fi->driver, Fi->database); if (driver == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(driver); sprintf(buf, "create table %s ( cat integer, tcat integer, dist double precision)", Fi->table); db_set_string(&sql, buf); G_debug(2, "%s", db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Unable to create table: '%s'"), db_get_string(&sql)); } if (db_create_index2(driver, Fi->table, GV_KEY_COLUMN) != DB_OK) G_warning(_("Cannot create index")); if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) G_fatal_error(_("Cannot grant privileges on table <%s>"), Fi->table); db_begin_transaction(driver); Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); G_message(_("Tracing paths from 'from' features ...")); from_nr = 0; for (i = 1; i <= nlines; i++) { if (varrayf->c[i]) { int type = Vect_read_line(&In, Points, Cats, i); int node, tcat, cat; double cost; dglInt32_t *vertex, vertex_id; if (!Vect_cat_get(Cats, flayer, &cat)) continue; if (type & GV_POINTS) { node = Vect_find_node(&In, Points->x[0], Points->y[0], Points->z[0], 0, 0); } else { Vect_get_line_nodes(&In, i, &node, NULL); } if (node < 1) continue; if (dst[node] < 0) { /* unreachable */ from_nr++; continue; } cost = dst[node] / (double)In.dgraph.cost_multip; vertex = dglGetNode(graph, node); vertex_id = node; slist->n_values = 0; while (nxt[vertex_id] != NULL) { int edge_id; edge_id = (int) dglEdgeGet_Id(graph, nxt[vertex_id]); if (segments) { Vect_cat_set(on_path[abs(edge_id)], 1, cat); if (edge_id < 0) { segdir[abs(edge_id)] = 1; } } else G_ilist_add(slist, edge_id); vertex = dglEdgeGet_Tail(graph, nxt[vertex_id]); vertex_id = dglNodeGet_Id(graph, vertex); } G_debug(3, "read line %d, vertex id %d", nodes_to_features[vertex_id], (int)vertex_id); Vect_read_line(&In, NULL, TCats, nodes_to_features[vertex_id]); if (!Vect_cat_get(TCats, tlayer, &tcat)) continue; Vect_write_line(&Out, type, Points, Cats); sprintf(buf, "insert into %s values (%d, %d, %f)", Fi->table, cat, tcat, cost); db_set_string(&sql, buf); G_debug(3, "%s", db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Cannot insert new record: %s"), db_get_string(&sql)); }; if (!segments) { Vect_reset_line(PPoints); for (j = 0; j < slist->n_values; j++) { Vect_read_line(&In, Points, NULL, abs(slist->value[j])); if (slist->value[j] > 0) Vect_append_points(PPoints, Points, GV_FORWARD); else Vect_append_points(PPoints, Points, GV_BACKWARD); PPoints->n_points--; } PPoints->n_points++; Vect_reset_cats(Cats); Vect_cat_set(Cats, 1, cat); Vect_write_line(&Out, GV_LINE, PPoints, Cats); } } } if (segments) { for (i = 1; i <= nlines; i++) { if (on_path[i]->n_cats > 0) { int type; if (segdir[i]) { type = Vect_read_line(&In, PPoints, NULL, i); Vect_reset_line(Points); Vect_append_points(Points, PPoints, GV_BACKWARD); } else type = Vect_read_line(&In, Points, NULL, i); Vect_write_line(&Out, type, Points, on_path[i]); } } } db_commit_transaction(driver); db_close_database_shutdown_driver(driver); Vect_build(&Out); Vect_close(&In); Vect_close(&Out); for (i = 1; i <= nlines; i++) Vect_destroy_cats_struct(on_path[i]); G_free(on_path); G_free(nodes_to_features); G_free(dst); G_free(nxt); G_free(segdir); if (from_nr) G_warning(n_("%d 'from' feature was not reachable", "%d 'from' features were not reachable", from_nr), from_nr); exit(EXIT_SUCCESS); }