123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955 |
- /****************************************************************
- *
- * MODULE: v.buffer
- *
- * AUTHOR(S): Radim Blazek
- * Upgraded by Rosen Matev (Google Summer of Code 2008)
- * OGR support by Martin Landa <landa.martin gmail.com> (2009)
- * rewrite and GEOS added by Markus Metz
- *
- * PURPOSE: Vector buffer
- *
- * COPYRIGHT: (C) 2001-2014 by the GRASS Development Team
- *
- * This program is free software under the GNU General
- * Public License (>=v2). Read the file COPYING that
- * comes with GRASS for details.
- *
- **************************************************************/
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <math.h>
- #include <grass/gis.h>
- #include <grass/vector.h>
- #include <grass/dbmi.h>
- #include <grass/glocale.h>
- #include "local_proto.h"
- #define PI M_PI
- #ifndef MIN
- #define MIN(X,Y) ((X<Y)?X:Y)
- #endif
- #ifndef MAX
- #define MAX(X,Y) ((X>Y)?X:Y)
- #endif
- /* returns 1 if unit_tolerance is adjusted, 0 otherwise */
- int adjust_tolerance(double *tolerance)
- {
- double t = 0.999 * (1 - cos(PI / 8));
- G_debug(2, "Maximum tolerance = %f", t);
- if (*tolerance > t) {
- *tolerance = t;
- return 1;
- }
- return 0;
- }
- int db_CatValArray_get_value_di(dbCatValArray * cvarr, int cat, double *value)
- {
- int t;
- int ctype = cvarr->ctype;
- int ret;
- if (ctype == DB_C_TYPE_INT) {
- ret = db_CatValArray_get_value_int(cvarr, cat, &t);
- if (ret != DB_OK)
- return ret;
- *value = (double)t;
- return DB_OK;
- }
- if (ctype == DB_C_TYPE_DOUBLE) {
- ret = db_CatValArray_get_value_double(cvarr, cat, value);
- return ret;
- }
- return DB_FAILED;
- }
- int point_in_buffer(struct buf_contours *arr_bc, struct spatial_index *si,
- struct Map_info *Buf, double x, double y)
- {
- int i, j, ret, flag;
- struct bound_box bbox;
- static struct ilist *List = NULL;
- static struct line_pnts *Points = NULL;
- static struct line_cats *BCats = NULL;
- if (List == NULL)
- List = Vect_new_list();
- if (Points == NULL)
- Points = Vect_new_line_struct();
- if (BCats == NULL)
- BCats = Vect_new_cats_struct();
- /* select outer contours overlapping with centroid (x, y) */
- bbox.W = bbox.E = x;
- bbox.N = bbox.S = y;
- bbox.T = PORT_DOUBLE_MAX;
- bbox.B = -PORT_DOUBLE_MAX;
- Vect_spatial_index_select(si, &bbox, List);
-
- for (i = 0; i < List->n_values; i++) {
- Vect_read_line(Buf, Points, BCats, arr_bc[List->value[i]].outer);
- ret = Vect_point_in_poly(x, y, Points);
- if (ret == 0)
- continue;
- flag = 1;
- for (j = 0; j < arr_bc[List->value[i]].inner_count; j++) {
- if (arr_bc[List->value[i]].inner[j] < 1)
- continue;
- Vect_read_line(Buf, Points, NULL, arr_bc[List->value[i]].inner[j]);
- ret = Vect_point_in_poly(x, y, Points);
- if (ret != 0) { /* inside inner contour */
- flag = 0;
- break;
- }
- }
- if (flag) {
- /* (x,y) is inside outer contour and outside inner contours of arr_bc[i] */
- return 1;
- }
- }
- return 0;
- }
- int buffer_cats(struct buf_contours *arr_bc, struct spatial_index *si,
- struct Map_info *Buf, double x, double y, struct line_cats *Cats)
- {
- int i, j, ret, flag, inside;
- struct bound_box bbox;
- static struct ilist *List = NULL;
- static struct line_pnts *Points = NULL;
- static struct line_cats *BCats = NULL;
- if (List == NULL)
- List = Vect_new_list();
- if (Points == NULL)
- Points = Vect_new_line_struct();
- if (BCats == NULL)
- BCats = Vect_new_cats_struct();
- /* select outer contours overlapping with centroid (x, y) */
- bbox.W = bbox.E = x;
- bbox.N = bbox.S = y;
- bbox.T = PORT_DOUBLE_MAX;
- bbox.B = -PORT_DOUBLE_MAX;
- Vect_spatial_index_select(si, &bbox, List);
-
- Vect_reset_cats(Cats);
-
- inside = 0;
- for (i = 0; i < List->n_values; i++) {
- Vect_read_line(Buf, Points, BCats, arr_bc[List->value[i]].outer);
- ret = Vect_point_in_poly(x, y, Points);
- if (ret == 0)
- continue;
- flag = 1;
- for (j = 0; j < arr_bc[List->value[i]].inner_count; j++) {
- if (arr_bc[List->value[i]].inner[j] < 1)
- continue;
- Vect_read_line(Buf, Points, NULL, arr_bc[List->value[i]].inner[j]);
- ret = Vect_point_in_poly(x, y, Points);
- if (ret != 0) { /* inside inner contour */
- flag = 0;
- break;
- }
- }
- if (flag) {
- /* (x,y) is inside outer contour and outside inner contours of arr_bc[i] */
- inside = 1;
- for (j = 0; j < BCats->n_cats; j++)
- Vect_cat_set(Cats, BCats->field[j], BCats->cat[j]);
- }
- }
- return inside;
- }
- int main(int argc, char *argv[])
- {
- struct Map_info In, Out, Buf;
- struct line_pnts *Points;
- struct line_cats *Cats, *BCats, *CCats;
- struct GModule *module;
- struct Option *in_opt, *out_opt, *type_opt, *dista_opt, *distb_opt,
- *angle_opt;
- struct Flag *straight_flag, *nocaps_flag, *cats_flag;
- struct Option *tol_opt, *bufcol_opt, *scale_opt, *field_opt,
- *where_opt, *cats_opt;
- struct cat_list *cat_list = NULL;
- int verbose, use_geos;
- double da, db, dalpha, tolerance, unit_tolerance;
- int type;
- int i, ret, nareas, area, nlines, line;
- char *Areas, *Lines;
- int field;
- struct buf_contours *arr_bc;
- int arr_bc_alloc;
- struct buf_contours_pts arr_bc_pts;
- int line_id, buffers_count = 0;
- struct spatial_index si;
- struct bound_box bbox;
- /* Attributes if sizecol is used */
- int nrec, ctype;
- struct field_info *Fi = NULL;
- dbDriver *Driver;
- dbCatValArray cvarr;
- double size_val, scale;
- #ifdef HAVE_GEOS
- /* TODO: use GEOSBufferParams * */
- #endif
- module = G_define_module();
- G_add_keyword(_("vector"));
- G_add_keyword(_("buffer"));
- G_add_keyword(_("area"));
- G_add_keyword(_("circle"));
- G_add_keyword(_("geometry"));
- G_add_keyword(_("line"));
- G_add_keyword(_("grow"));
- G_add_keyword(_("shrink"));
- module->description =
- _("Creates a buffer around vector features of given type.");
- in_opt = G_define_standard_option(G_OPT_V_INPUT);
- field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL);
- field_opt->guisection = _("Selection");
- cats_opt = G_define_standard_option(G_OPT_V_CATS);
- cats_opt->guisection = _("Selection");
-
- where_opt = G_define_standard_option(G_OPT_DB_WHERE);
- where_opt->guisection = _("Selection");
- type_opt = G_define_standard_option(G_OPT_V_TYPE);
- type_opt->options = "point,line,boundary,centroid,area";
- type_opt->answer = "point,line,area";
- type_opt->guisection = _("Selection");
- out_opt = G_define_standard_option(G_OPT_V_OUTPUT);
-
- dista_opt = G_define_option();
- dista_opt->key = "distance";
- dista_opt->type = TYPE_DOUBLE;
- dista_opt->required = NO;
- dista_opt->description =
- _("Buffer distance along major axis in map units");
- dista_opt->guisection = _("Distance");
- distb_opt = G_define_option();
- distb_opt->key = "minordistance";
- distb_opt->type = TYPE_DOUBLE;
- distb_opt->required = NO;
- distb_opt->description =
- _("Buffer distance along minor axis in map units");
- distb_opt->guisection = _("Distance");
- angle_opt = G_define_option();
- angle_opt->key = "angle";
- angle_opt->type = TYPE_DOUBLE;
- angle_opt->required = NO;
- angle_opt->answer = "0";
- angle_opt->description = _("Angle of major axis in degrees");
- angle_opt->guisection = _("Distance");
- bufcol_opt = G_define_standard_option(G_OPT_DB_COLUMN);
- bufcol_opt->description =
- _("Name of column to use for buffer distances");
- bufcol_opt->guisection = _("Distance");
- scale_opt = G_define_option();
- scale_opt->key = "scale";
- scale_opt->type = TYPE_DOUBLE;
- scale_opt->required = NO;
- scale_opt->answer = "1.0";
- scale_opt->description = _("Scaling factor for attribute column values");
- scale_opt->guisection = _("Distance");
- tol_opt = G_define_option();
- tol_opt->key = "tolerance";
- tol_opt->type = TYPE_DOUBLE;
- tol_opt->required = NO;
- tol_opt->description =
- _("Maximum distance between theoretical arc and polygon segments as multiple of buffer (default 0.01)");
- tol_opt->guisection = _("Distance");
- straight_flag = G_define_flag();
- straight_flag->key = 's';
- straight_flag->description = _("Make outside corners straight");
- nocaps_flag = G_define_flag();
- nocaps_flag->key = 'c';
- nocaps_flag->description = _("Do not make caps at the ends of polylines");
- cats_flag = G_define_flag();
- cats_flag->key = 't';
- cats_flag->description = _("Transfer categories and attributes");
- cats_flag->guisection = _("Attributes");
-
- G_gisinit(argv[0]);
-
- if (G_parser(argc, argv))
- exit(EXIT_FAILURE);
- verbose = G_verbose();
- #if !defined HAVE_GEOS
- use_geos = FALSE;
- #else
- use_geos = getenv("GRASS_VECTOR_BUFFER") ? FALSE : TRUE;
- #endif
- G_debug(1, "use_geos = %d", use_geos);
-
- type = Vect_option_to_types(type_opt);
- /* TODO: no geodesic support yet in GEOS */
- if (G_projection() == PROJECTION_LL)
- G_important_message(_("Note: In latitude-longitude coordinate system specify distances in degree unit"));
- if ((dista_opt->answer && bufcol_opt->answer) ||
- (!(dista_opt->answer || bufcol_opt->answer)))
- G_fatal_error(_("Select a buffer distance/minordistance/angle "
- "or column, but not both."));
- Vect_check_input_output_name(in_opt->answer, out_opt->answer, G_FATAL_EXIT);
- Vect_set_open_level(2); /* topology required */
- if (Vect_open_old2(&In, in_opt->answer, "", field_opt->answer) < 0)
- G_fatal_error(_("Unable to open vector map <%s>"), in_opt->answer);
- Vect_set_error_handler_io(&In, &Out);
- if (field_opt->answer)
- field = Vect_get_field_number(&In, field_opt->answer);
- else
- field = -1;
- if ((cats_opt->answer || where_opt->answer) && field == -1) {
- G_warning(_("Invalid layer number (%d). Parameter '%s' or '%s' specified, assuming layer '1'."),
- field, cats_opt->key, where_opt->key);
- field = 1;
- }
- cat_list = NULL;
- if (field > 0)
- cat_list = Vect_cats_set_constraint(&In, field, where_opt->answer,
- cats_opt->answer);
-
- if (bufcol_opt->answer && field == -1)
- G_fatal_error(_("The bufcol option requires a valid layer."));
- tolerance = 0.01;
- if (tol_opt->answer) {
- tolerance = atof(tol_opt->answer);
- if (tolerance <= 0)
- G_fatal_error(_("The tolerance must be > 0."));
- }
- if (adjust_tolerance(&tolerance))
- G_warning(_("The tolerance was reset to %g"), tolerance);
- scale = atof(scale_opt->answer);
- if (scale <= 0.0)
- G_fatal_error(_("Illegal scale value"));
- da = db = dalpha = unit_tolerance = 0;
- if (dista_opt->answer) {
- da = atof(dista_opt->answer);
- if (distb_opt->answer)
- db = atof(distb_opt->answer);
- else
- db = da;
- if (angle_opt->answer)
- dalpha = atof(angle_opt->answer);
- else
- dalpha = 0;
- unit_tolerance = fabs(tolerance * MIN(da, db));
- G_verbose_message(_("The tolerance in map units = %g"), unit_tolerance);
- if (use_geos) {
- if (distb_opt->answer)
- G_warning(_("Option '%s' is not available with GEOS buffering"),
- distb_opt->key);
- if (dalpha != 0)
- G_warning(_("Option '%s' is not available with GEOS buffering"),
- angle_opt->key);
- if (tol_opt->answer)
- G_warning(_("Option '%s' is not available with GEOS buffering"),
- tol_opt->key);
- }
- }
- if (Vect_open_new(&Out, out_opt->answer, WITHOUT_Z) < 0)
- G_fatal_error(_("Unable to create vector map <%s>"), out_opt->answer);
-
- Points = Vect_new_line_struct();
- Cats = Vect_new_cats_struct();
- BCats = Vect_new_cats_struct();
- CCats = Vect_new_cats_struct();
-
- /* open tmp vector for buffers, needed for cleaning */
- if (0 > Vect_open_tmp_new(&Buf, NULL, WITHOUT_Z))
- G_fatal_error(_("Unable to create temporary vector map"));
- G_set_verbose(0);
- Vect_build_partial(&Buf, GV_BUILD_BASE); /* switch to level 2 */
- G_set_verbose(verbose);
- /* check and load attribute column data */
- if (bufcol_opt->answer) {
- db_CatValArray_init(&cvarr);
- Fi = Vect_get_field(&In, field);
- if (Fi == NULL)
- G_fatal_error(_("Database connection not defined for layer %d"),
- field);
- 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);
- /* Note do not check if the column exists in the table because it may be expression */
- /* TODO: only select values we need instead of all in column */
- nrec =
- db_select_CatValArray(Driver, Fi->table, Fi->key,
- bufcol_opt->answer, NULL, &cvarr);
- if (nrec < 0)
- G_fatal_error(_("Unable to select data from table <%s>"),
- Fi->table);
- G_debug(2, "%d records selected from table", nrec);
- ctype = cvarr.ctype;
- if (ctype != DB_C_TYPE_INT && ctype != DB_C_TYPE_DOUBLE)
- G_fatal_error(_("Column type not supported"));
- db_close_database_shutdown_driver(Driver);
- /* Output cats/values list */
- for (i = 0; i < cvarr.n_values; i++) {
- if (ctype == DB_C_TYPE_INT) {
- G_debug(4, "cat = %d val = %d", cvarr.value[i].cat,
- cvarr.value[i].val.i);
- }
- else if (ctype == DB_C_TYPE_DOUBLE) {
- G_debug(4, "cat = %d val = %f", cvarr.value[i].cat,
- cvarr.value[i].val.d);
- }
- }
- }
- Vect_copy_head_data(&In, &Out);
- Vect_hist_copy(&In, &Out);
- Vect_hist_command(&Out);
- /* Create buffers' boundaries */
- nlines = nareas = 0;
- if ((type & GV_POINTS) || (type & GV_LINES))
- nlines = Vect_get_num_primitives(&In, type);
- if (type & GV_AREA)
- nareas = Vect_get_num_areas(&In);
-
- if (nlines + nareas == 0) {
- G_warning(_("No features available for buffering. "
- "Check type option and features available in the input vector."));
- exit(EXIT_SUCCESS);
- }
- /* init arr_bc */
- buffers_count = 1;
- arr_bc_alloc = nlines + nareas + 1;
- arr_bc = G_calloc(arr_bc_alloc, sizeof(struct buf_contours));
- Vect_spatial_index_init(&si, 0);
- #ifdef HAVE_GEOS
- initGEOS(G_message, G_fatal_error);
- /* check required version for -s/-c flag */
- #ifndef GEOS_3_3
- G_warning(_("Flags -%c/%c ignored by this version, GEOS >= 3.3 is required"),
- 's', 'c');
- #endif
- #endif
- if(!use_geos && (da < 0. || db < 0.)) {
- G_warning(_("Negative distances for internal buffers are not supported "
- "and converted to positive values."));
- da = fabs(da);
- db = fabs(db);
- }
- /* Areas */
- if (nareas > 0) {
- int centroid;
- G_message(_("Buffering areas..."));
- for (area = 1; area <= nareas; area++) {
- int cat;
- G_percent(area, nareas, 2);
-
- if (!Vect_area_alive(&In, area))
- continue;
-
- centroid = Vect_get_area_centroid(&In, area);
- if (centroid == 0)
- continue;
- Vect_read_line(&In, NULL, Cats, centroid);
- if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list))
- continue;
- Vect_reset_cats(CCats);
- for (i = 0; i < Cats->n_cats; i++) {
- if (field < 0) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- else if (field > 0 && Cats->field[i] == field) {
- if (!cat_list) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- else if (Vect_cat_in_cat_list(Cats->cat[i], cat_list)) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- }
- }
- if (bufcol_opt->answer) {
- Vect_cat_get(Cats, field, &cat);
- ret = db_CatValArray_get_value_di(&cvarr, cat, &size_val);
- if (ret != DB_OK) {
- G_warning(_("No record for category %d in table <%s>"),
- cat, Fi->table);
- continue;
- }
- if (size_val < 0.0) {
- G_warning(_("Attribute is of invalid size (%.3f) for category %d"),
- size_val, cat);
- continue;
- }
- if (size_val == 0.0)
- continue;
- da = size_val * scale;
- db = da;
- dalpha = 0;
- unit_tolerance = fabs(tolerance * MIN(da, db));
- G_debug(2, " dynamic buffer size = %.2f", da);
- G_debug(2, _("The tolerance in map units: %g"),
- unit_tolerance);
- }
- #ifdef HAVE_GEOS
- if (use_geos)
- geos_buffer(&In, &Out, &Buf, area, GV_AREA, da,
- &si, CCats, &arr_bc, &buffers_count, &arr_bc_alloc,
- straight_flag->answer, nocaps_flag->answer);
- #endif
- if (!use_geos) {
- Vect_area_buffer2(&In, area, da, db, dalpha,
- !(straight_flag->answer),
- !(nocaps_flag->answer), unit_tolerance,
- &(arr_bc_pts.oPoints),
- &(arr_bc_pts.iPoints),
- &(arr_bc_pts.inner_count));
-
- Vect_write_line(&Out, GV_BOUNDARY, arr_bc_pts.oPoints, BCats);
- line_id = Vect_write_line(&Buf, GV_BOUNDARY, arr_bc_pts.oPoints, CCats);
- Vect_destroy_line_struct(arr_bc_pts.oPoints);
- /* add buffer to spatial index */
- Vect_get_line_box(&Buf, line_id, &bbox);
- Vect_spatial_index_add_item(&si, buffers_count, &bbox);
- arr_bc[buffers_count].outer = line_id;
-
- arr_bc[buffers_count].inner_count = arr_bc_pts.inner_count;
- if (arr_bc_pts.inner_count > 0) {
- arr_bc[buffers_count].inner = G_malloc(arr_bc_pts.inner_count * sizeof(int));
- for (i = 0; i < arr_bc_pts.inner_count; i++) {
- Vect_write_line(&Out, GV_BOUNDARY, arr_bc_pts.iPoints[i], BCats);
- line_id = Vect_write_line(&Buf, GV_BOUNDARY, arr_bc_pts.iPoints[i], BCats);
- Vect_destroy_line_struct(arr_bc_pts.iPoints[i]);
- arr_bc[buffers_count].inner[i] = line_id;
- }
- G_free(arr_bc_pts.iPoints);
- }
- buffers_count++;
- } /* native buffer end */
- }
- }
- /* Lines (and Points) */
- if (nlines > 0) {
- int ltype;
- G_message(_("Buffering features..."));
-
- if (da < 0 || db < 0) {
- G_warning(_("Negative distances are only supported for areas"));
- da = fabs(da);
- db = fabs(db);
- }
- nlines = Vect_get_num_lines(&In);
- for (line = 1; line <= nlines; line++) {
- int cat;
- G_debug(2, "line = %d", line);
- G_percent(line, nlines, 2);
-
- if (!Vect_line_alive(&In, line))
- continue;
- ltype = Vect_read_line(&In, Points, Cats, line);
- if (!(ltype & type))
- continue;
- if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list))
- continue;
- Vect_reset_cats(CCats);
- for (i = 0; i < Cats->n_cats; i++) {
- if (field < 0) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- else if (field > 0 && Cats->field[i] == field) {
- if (!cat_list) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- else if (Vect_cat_in_cat_list(Cats->cat[i], cat_list)) {
- Vect_cat_set(CCats, Cats->field[i], Cats->cat[i]);
- }
- }
- }
- if (bufcol_opt->answer) {
- Vect_cat_get(Cats, field, &cat);
- ret = db_CatValArray_get_value_di(&cvarr, cat, &size_val);
- if (ret != DB_OK) {
- G_warning(_("No record for category %d in table <%s>"),
- cat, Fi->table);
- continue;
- }
- if (size_val < 0.0) {
- G_warning(_("Attribute is of invalid size (%.3f) for category %d"),
- size_val, cat);
- continue;
- }
- if (size_val == 0.0)
- continue;
- da = size_val * scale;
- if (da < 0) {
- G_warning(_("Negative distances are only supported for areas"));
- da = fabs(da);
- }
- db = da;
- dalpha = 0;
- unit_tolerance = tolerance * MIN(da, db);
- G_debug(2, " dynamic buffer size = %.2f", da);
- G_debug(2, _("The tolerance in map units: %g"),
- unit_tolerance);
- }
- if (da <= 0) {
- G_warning(_("Distances must be positive, ignoring distance %g"), da);
- continue;
- }
-
- Vect_line_prune(Points);
- if (ltype & GV_POINTS || Points->n_points == 1) {
- if (straight_flag->answer) {
- arr_bc_pts.oPoints = Vect_new_line_struct();
- Vect_append_point(arr_bc_pts.oPoints, Points->x[0] + da, Points->y[0] + da, 0);
- Vect_append_point(arr_bc_pts.oPoints, Points->x[0] + da, Points->y[0] - da, 0);
- Vect_append_point(arr_bc_pts.oPoints, Points->x[0] - da, Points->y[0] - da, 0);
- Vect_append_point(arr_bc_pts.oPoints, Points->x[0] - da, Points->y[0] + da, 0);
- Vect_append_point(arr_bc_pts.oPoints, arr_bc_pts.oPoints->x[0], arr_bc_pts.oPoints->y[0], arr_bc_pts.oPoints->z[0]);
- } else {
- Vect_point_buffer2(Points->x[0], Points->y[0], da, db, dalpha,
- !(straight_flag->answer), unit_tolerance,
- &(arr_bc_pts.oPoints));
- }
- Vect_write_line(&Out, GV_BOUNDARY, arr_bc_pts.oPoints, BCats);
- line_id = Vect_write_line(&Buf, GV_BOUNDARY, arr_bc_pts.oPoints, CCats);
- Vect_destroy_line_struct(arr_bc_pts.oPoints);
- /* add buffer to spatial index */
- Vect_get_line_box(&Buf, line_id, &bbox);
- Vect_spatial_index_add_item(&si, buffers_count, &bbox);
- arr_bc[buffers_count].outer = line_id;
- arr_bc[buffers_count].inner_count = 0;
- arr_bc[buffers_count].inner = NULL;
- buffers_count++;
- }
- else {
- #ifdef HAVE_GEOS
- if (use_geos)
- geos_buffer(&In, &Out, &Buf, line, type, da,
- &si, CCats, &arr_bc, &buffers_count, &arr_bc_alloc,
- straight_flag->answer, nocaps_flag->answer);
- #endif
- if (!use_geos) {
- Vect_line_buffer2(Points, da, db, dalpha,
- !(straight_flag->answer),
- !(nocaps_flag->answer), unit_tolerance,
- &(arr_bc_pts.oPoints),
- &(arr_bc_pts.iPoints),
- &(arr_bc_pts.inner_count));
-
- Vect_write_line(&Out, GV_BOUNDARY, arr_bc_pts.oPoints, BCats);
- line_id = Vect_write_line(&Buf, GV_BOUNDARY, arr_bc_pts.oPoints, CCats);
- Vect_destroy_line_struct(arr_bc_pts.oPoints);
- /* add buffer to spatial index */
- Vect_get_line_box(&Buf, line_id, &bbox);
- Vect_spatial_index_add_item(&si, buffers_count, &bbox);
- arr_bc[buffers_count].outer = line_id;
-
- arr_bc[buffers_count].inner_count = arr_bc_pts.inner_count;
- if (arr_bc_pts.inner_count > 0) {
- arr_bc[buffers_count].inner = G_malloc(arr_bc_pts.inner_count * sizeof(int));
- for (i = 0; i < arr_bc_pts.inner_count; i++) {
- Vect_write_line(&Out, GV_BOUNDARY, arr_bc_pts.iPoints[i], BCats);
- line_id = Vect_write_line(&Buf, GV_BOUNDARY, arr_bc_pts.iPoints[i], BCats);
- Vect_destroy_line_struct(arr_bc_pts.iPoints[i]);
- arr_bc[buffers_count].inner[i] = line_id;
- }
- G_free(arr_bc_pts.iPoints);
- }
- buffers_count++;
- } /* native buffer end */
- }
- }
- }
- #ifdef HAVE_GEOS
- finishGEOS();
- #endif
- G_message(_("Cleaning buffers..."));
-
- /* Break lines */
- G_message(_("Building parts of topology..."));
- Vect_build_partial(&Out, GV_BUILD_BASE);
- /* Warning: snapping must be done, otherwise colinear boundaries are not broken and
- * topology cannot be built (the same angle). But snapping distance must be very, very
- * small, otherwise counterclockwise boundaries can appear in areas outside the buffer.
- * I have done some tests on real data (projected) and threshold 1e-8 was not enough,
- * Snapping threshold 1e-7 seems to work. Don't increase until we find example
- * where it is not sufficient. RB */
- /* TODO: look at snapping threshold better, calculate some theoretical value to avoid
- * the same angles of lines at nodes, don't forget about LongLat data, probably
- * calculate different threshold for each map, depending on map's bounding box
- * and/or distance and tolerance */
- G_message(_("Snapping boundaries..."));
- Vect_snap_lines(&Out, GV_BOUNDARY, 1e-7, NULL);
- G_message(_("Breaking polygons..."));
- Vect_break_polygons(&Out, GV_BOUNDARY, NULL);
- G_message(_("Removing duplicates..."));
- Vect_remove_duplicates(&Out, GV_BOUNDARY, NULL);
- do {
- G_message(_("Breaking boundaries..."));
- Vect_break_lines(&Out, GV_BOUNDARY, NULL);
- G_message(_("Removing duplicates..."));
- Vect_remove_duplicates(&Out, GV_BOUNDARY, NULL);
- G_message(_("Cleaning boundaries at nodes"));
- } while (Vect_clean_small_angles_at_nodes(&Out, GV_BOUNDARY, NULL) > 0);
- /* Dangles and bridges don't seem to be necessary if snapping is small enough. */
- /* Still needed for larger buffer distances ? */
- Vect_build_partial(&Out, GV_BUILD_AREAS);
- G_message(_("Removing dangles..."));
- Vect_remove_dangles(&Out, GV_BOUNDARY, -1, NULL);
- G_message (_("Removing bridges..."));
- Vect_remove_bridges(&Out, NULL, NULL, NULL);
- G_message(_("Attaching islands..."));
- Vect_build_partial(&Out, GV_BUILD_ATTACH_ISLES);
- if (!cats_flag->answer) {
- /* Calculate new centroids for all areas */
- nareas = Vect_get_num_areas(&Out);
- Areas = (char *)G_calloc(nareas + 1, sizeof(char));
- G_message(_("Calculating centroids for all areas..."));
- G_percent(0, nareas, 2);
- for (area = 1; area <= nareas; area++) {
- double x, y;
- G_percent(area, nareas, 2);
- G_debug(3, "area = %d", area);
- if (!Vect_area_alive(&Out, area))
- continue;
- ret = Vect_get_point_in_area(&Out, area, &x, &y);
- if (ret < 0) {
- G_warning(_("Cannot calculate area centroid"));
- continue;
- }
- ret = point_in_buffer(arr_bc, &si, &Buf, x, y);
- if (ret) {
- G_debug(3, " -> in buffer");
- Areas[area] = 1;
- }
- }
- /* Make a list of boundaries to be deleted (both sides inside) */
- nlines = Vect_get_num_lines(&Out);
- G_debug(3, "nlines = %d", nlines);
- Lines = (char *)G_calloc(nlines + 1, sizeof(char));
- G_message(_("Generating list of boundaries to be deleted..."));
- for (line = 1; line <= nlines; line++) {
- int j, side[2], areas[2];
- G_percent(line, nlines, 2);
- G_debug(3, "line = %d", line);
- if (!Vect_line_alive(&Out, line))
- continue;
- Vect_get_line_areas(&Out, line, &side[0], &side[1]);
- for (j = 0; j < 2; j++) {
- if (side[j] == 0) { /* area/isle not build */
- areas[j] = 0;
- }
- else if (side[j] > 0) { /* area */
- areas[j] = side[j];
- }
- else { /* < 0 -> island */
- areas[j] = Vect_get_isle_area(&Out, abs(side[j]));
- }
- }
- G_debug(3, " areas = %d , %d -> Areas = %d, %d", areas[0], areas[1],
- Areas[areas[0]], Areas[areas[1]]);
- if (Areas[areas[0]] && Areas[areas[1]])
- Lines[line] = 1;
- }
- G_free(Areas);
- /* Delete boundaries */
- G_message(_("Deleting boundaries..."));
- for (line = 1; line <= nlines; line++) {
- G_percent(line, nlines, 2);
-
- if (!Vect_line_alive(&Out, line))
- continue;
- if (Lines[line]) {
- G_debug(3, " delete line %d", line);
- Vect_delete_line(&Out, line);
- }
- else {
- /* delete incorrect boundaries */
- int side[2];
- Vect_get_line_areas(&Out, line, &side[0], &side[1]);
- if (!side[0] && !side[1]) {
- G_debug(3, " delete line %d", line);
- Vect_delete_line(&Out, line);
- }
- }
- }
- G_free(Lines);
- }
- /* Create new centroids */
- Vect_reset_cats(Cats);
- Vect_cat_set(Cats, 1, 1);
- nareas = Vect_get_num_areas(&Out);
- G_message(_("Calculating centroids for areas..."));
- for (area = 1; area <= nareas; area++) {
- double x, y;
- G_percent(area, nareas, 2);
- G_debug(3, "area = %d", area);
- if (!Vect_area_alive(&Out, area))
- continue;
- ret = Vect_get_point_in_area(&Out, area, &x, &y);
- if (ret < 0) {
- G_warning(_("Unable to calculate centroid for area %d"), area);
- continue;
- }
- if (cats_flag->answer)
- ret = buffer_cats(arr_bc, &si, &Buf, x, y, Cats);
- else
- ret = point_in_buffer(arr_bc, &si, &Buf, x, y);
- if (ret) {
- Vect_reset_line(Points);
- Vect_append_point(Points, x, y, 0.);
- Vect_write_line(&Out, GV_CENTROID, Points, Cats);
- }
- }
- Vect_spatial_index_destroy(&si);
- Vect_close(&Buf);
-
- if (cats_flag->answer)
- Vect_copy_tables(&In, &Out, field);
- Vect_close(&In);
- Vect_build_partial(&Out, GV_BUILD_NONE);
- Vect_build(&Out);
- Vect_close(&Out);
- exit(EXIT_SUCCESS);
- }
|