/*! \brief Conversion to meters Returns a factor which converts the grid unit to meters (by multiplication). If the database is not metric (eg. imagery) then 0.0 is returned. \return value */ double G_database_units_to_meters_factor(void) { const char *unit; const char *buf; double factor; int n; /* TODO: sync with definitions in ../proj/units.table */ static const struct { char *unit; double factor; } table[] = { {"unit", 1.0}, {"meter", 1.0}, {"foot", .3048}, {"foot_us", 1200/3937.}, {"inch", .0254}, {NULL, 0.0} }; factor = 0.0; buf = lookup_units("meters"); if (buf) sscanf(buf, "%lf", &factor); if (factor <= 0.0) { unit = G_database_unit_name(0); for (n = 0; table[n].unit; n++) if (equal(unit, table[n].unit)) { factor = table[n].factor; break; } } return factor; }
double G_database_units_to_meters_factor() { char *unit; double factor; char buf[256]; int n; static struct { char *unit; double factor; } table[] = { {"unit", 1.0}, {"meter", 1.0}, {"foot", .3048}, {"inch", .0254}, {NULL, 0.0} }; factor = 0.0; if (lookup(UNIT_FILE, "meters", buf, sizeof(buf))) sscanf (buf, "%lf", &factor); if (factor <= 0.0) { unit = G_database_unit_name(0); for (n=0; table[n].unit; n++) if (equal(unit, table[n].unit)) { factor = table[n].factor; break; } } return factor; }
/*! \brief Get units code by name Units codes (gis.h): - U_METERS - U_KILOMETERS - U_ACRES - U_HECTARES - U_MILES - U_FEET - U_USFEET - ... - U_YEARS - ... \param units_name units name (singular or plural form) \return units code \return U_UNKNOWN if not found */ int G_units(const char *units_name) { if (units_name == NULL) { return G_units(G_database_unit_name(1)); } if (strcasecmp(units_name, "meter") == 0 || strcasecmp(units_name, "meters") == 0) return U_METERS; else if (strcasecmp(units_name, "kilometer") == 0 || strcasecmp(units_name, "kilometers") == 0) return U_KILOMETERS; else if (strcasecmp(units_name, "acre") == 0 || strcasecmp(units_name, "acres") == 0) return U_ACRES; else if (strcasecmp(units_name, "hectare") == 0 || strcasecmp(units_name, "hectares") == 0) return U_HECTARES; else if (strcasecmp(units_name, "mile") == 0 || strcasecmp(units_name, "miles") == 0) return U_MILES; else if (strcasecmp(units_name, "foot") == 0 || strcasecmp(units_name, "feet") == 0) return U_FEET; else if (strcasecmp(units_name, "foot_us") == 0 || strcasecmp(units_name, "foot_uss") == 0) return U_USFEET; else if (strcasecmp(units_name, "degree") == 0 || strcasecmp(units_name, "degrees") == 0) return U_DEGREES; else if (strcasecmp(units_name, "year") == 0 || strcasecmp(units_name, "years") == 0) return U_YEARS; else if (strcasecmp(units_name, "month") == 0 || strcasecmp(units_name, "months") == 0) return U_MONTHS; else if (strcasecmp(units_name, "day") == 0 || strcasecmp(units_name, "days") == 0) return U_DAYS; else if (strcasecmp(units_name, "hour") == 0 || strcasecmp(units_name, "hours") == 0) return U_HOURS; else if (strcasecmp(units_name, "minute") == 0 || strcasecmp(units_name, "minutes") == 0) return U_MINUTES; else if (strcasecmp(units_name, "secons") == 0 || strcasecmp(units_name, "seconds") == 0) return U_SECONDS; return U_UNKNOWN; }
int main(int argc, char *argv[]) { char *name, *outfile; const char *unit; int unit_id; double factor; int fd, projection; FILE *fp, *coor_fp; double res; char *null_string; char ebuf[256], nbuf[256], label[512], formatbuff[256]; char b1[100], b2[100]; int n; int havefirst = FALSE; int coords = 0, i, k = -1; double e1, e2, n1, n2; RASTER_MAP_TYPE data_type; struct Cell_head window; struct { struct Option *opt1, *profile, *res, *output, *null_str, *coord_file, *units; struct Flag *g, *c, *m; } parm; struct GModule *module; G_gisinit(argv[0]); /* Set description */ module = G_define_module(); G_add_keyword(_("raster")); G_add_keyword(_("profile")); module->description = _("Outputs the raster map layer values lying on user-defined line(s)."); parm.opt1 = G_define_standard_option(G_OPT_R_INPUT); parm.output = G_define_standard_option(G_OPT_F_OUTPUT); parm.output->required = NO; parm.output->answer = "-"; parm.output->description = _("Name of file for output (use output=- for stdout)"); parm.profile = G_define_standard_option(G_OPT_M_COORDS); parm.profile->required = NO; parm.profile->multiple = YES; parm.profile->description = _("Profile coordinate pairs"); parm.coord_file = G_define_standard_option(G_OPT_F_INPUT); parm.coord_file->key = "file"; parm.coord_file->required = NO; parm.coord_file->label = _("Name of input file containing coordinate pairs"); parm.coord_file->description = _("Use instead of the 'coordinates' option. " "\"-\" reads from stdin."); parm.res = G_define_option(); parm.res->key = "resolution"; parm.res->type = TYPE_DOUBLE; parm.res->required = NO; parm.res->description = _("Resolution along profile (default = current region resolution)"); parm.null_str = G_define_option(); parm.null_str->key = "null"; parm.null_str->type = TYPE_STRING; parm.null_str->required = NO; parm.null_str->answer = "*"; parm.null_str->description = _("Character to represent no data cell"); parm.g = G_define_flag(); parm.g->key = 'g'; parm.g->description = _("Output easting and northing in first two columns of four column output"); parm.c = G_define_flag(); parm.c->key = 'c'; parm.c->description = _("Output RRR:GGG:BBB color values for each profile point"); parm.units = G_define_standard_option(G_OPT_M_UNITS); parm.units->options = "meters,kilometers,feet,miles"; parm.units->label = parm.units->description; parm.units->description = _("If units are not specified, current location units are used. " "Meters are used by default in geographic (latlon) locations."); if (G_parser(argc, argv)) exit(EXIT_FAILURE); clr = 0; if (parm.c->answer) clr = 1; /* color output */ null_string = parm.null_str->answer; if ((parm.profile->answer && parm.coord_file->answer) || (!parm.profile->answer && !parm.coord_file->answer)) G_fatal_error(_("Either use profile option or coordinate_file " " option, but not both")); G_get_window(&window); projection = G_projection(); /* get conversion factor and units name */ if (parm.units->answer) { unit_id = G_units(parm.units->answer); factor = 1. / G_meters_to_units_factor(unit_id); unit = G_get_units_name(unit_id, 1, 0); } /* keep meters in case of latlon */ else if (projection == PROJECTION_LL) { factor = 1; unit = "meters"; } else { /* get conversion factor to current units */ unit = G_database_unit_name(1); factor = G_database_units_to_meters_factor(); } if (parm.res->answer) { res = atof(parm.res->answer); /* Catch bad resolution ? */ if (res <= 0) G_fatal_error(_("Illegal resolution %g [%s]"), res / factor, unit); } else { /* Do average of EW and NS res */ res = (window.ew_res + window.ns_res) / 2; } G_message(_("Using resolution: %g [%s]"), res / factor, unit); G_begin_distance_calculations(); /* Open Input File for reading */ /* Get Input Name */ name = parm.opt1->answer; if (parm.g->answer) coords = 1; /* Open Raster File */ fd = Rast_open_old(name, ""); /* initialize color structure */ if (clr) Rast_read_colors(name, "", &colors); /* Open ASCII file for output or stdout */ outfile = parm.output->answer; if ((strcmp("-", outfile)) == 0) { fp = stdout; } else if (NULL == (fp = fopen(outfile, "w"))) G_fatal_error(_("Unable to open file <%s>"), outfile); /* Get Raster Type */ data_type = Rast_get_map_type(fd); /* Done with file */ /* Show message giving output format */ G_message(_("Output columns:")); if (coords == 1) sprintf(formatbuff, _("Easting, Northing, Along track dist. [%s], Elevation"), unit); else sprintf(formatbuff, _("Along track dist. [%s], Elevation"), unit); if (clr) strcat(formatbuff, _(" RGB color")); G_message(formatbuff); /* Get Profile Start Coords */ if (parm.coord_file->answer) { if (strcmp("-", parm.coord_file->answer) == 0) coor_fp = stdin; else coor_fp = fopen(parm.coord_file->answer, "r"); if (coor_fp == NULL) G_fatal_error(_("Could not open <%s>"), parm.coord_file->answer); for (n = 1; input(b1, ebuf, b2, nbuf, label, coor_fp); n++) { G_debug(4, "stdin line %d: ebuf=[%s] nbuf=[%s]", n, ebuf, nbuf); if (!G_scan_easting(ebuf, &e2, G_projection()) || !G_scan_northing(nbuf, &n2, G_projection())) G_fatal_error(_("Invalid coordinates %s %s"), ebuf, nbuf); if (havefirst) do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp, null_string, unit, factor); e1 = e2; n1 = n2; havefirst = TRUE; } if (coor_fp != stdin) fclose(coor_fp); } else { /* Coords given on the Command Line using the profile= option */ for (i = 0; parm.profile->answers[i]; i += 2) { /* Test for number coordinate pairs */ k = i; } if (k == 0) { /* Only one coordinate pair supplied */ G_scan_easting(parm.profile->answers[0], &e1, G_projection()); G_scan_northing(parm.profile->answers[1], &n1, G_projection()); e2 = e1; n2 = n1; /* Get profile info */ do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp, null_string, unit, factor); } else { for (i = 0; i <= k - 2; i += 2) { G_scan_easting(parm.profile->answers[i], &e1, G_projection()); G_scan_northing(parm.profile->answers[i + 1], &n1, G_projection()); G_scan_easting(parm.profile->answers[i + 2], &e2, G_projection()); G_scan_northing(parm.profile->answers[i + 3], &n2, G_projection()); /* Get profile info */ do_profile(e1, e2, n1, n2, coords, res, fd, data_type, fp, null_string, unit, factor); } } } Rast_close(fd); fclose(fp); if (clr) Rast_free_colors(&colors); exit(EXIT_SUCCESS); } /* Done with main */
int do_scalebar(void) { double scale_size; double length, width; double x, x1, x2, y1, y2, y3; int seg, i, j, lab; int margin; char num[50]; /* get scale size */ scale_size = METERS_TO_INCHES * distance(PS.w.east, PS.w.west) / scale(PS.scaletext); /* convert scale size to map inches */ length = (sb.length / scale_size) * G_database_units_to_meters_factor() * METERS_TO_INCHES; /* if(sb.units == SB_UNITS_AUTO) { do nothing } */ if(sb.units == SB_UNITS_METERS) length /= G_database_units_to_meters_factor(); else if(sb.units == SB_UNITS_KM) length *= KILOMETERS_TO_METERS / G_database_units_to_meters_factor(); else if(sb.units == SB_UNITS_FEET) length *= FEET_TO_METERS / G_database_units_to_meters_factor(); else if(sb.units == SB_UNITS_MILES) length *= MILES_TO_METERS / G_database_units_to_meters_factor(); else if(sb.units == SB_UNITS_NMILES) length *= NAUT_MILES_TO_METERS / G_database_units_to_meters_factor(); width = sb.height; seg = sb.segment; j = 0; lab = 0; margin = (int)(0.2 * (double)sb.fontsize + 0.5); if (margin < 2) margin = 2; fprintf(PS.fp, "/mg %d def\n", margin); x = sb.x - (length / 2.); set_font_size(sb.fontsize); set_line_width(sb.width); if (strcmp(sb.type, "f") == 0) { /* draw fancy scale bar */ for (i = 0; i < seg; i++) { /* draw a filled rectangle */ x1 = 72.0 * (x + (length / seg) * i) + 0.5; y1 = 72.0 * (PS.page_height - sb.y); x2 = 72.0 * (x + (length / seg) * (i + 1)) + 0.5; y2 = (72.0 * (PS.page_height - sb.y)) + (width * 72.0); /* Alternate black and white */ if (j == 0) { fprintf(PS.fp, "0.0 0.0 0.0 C\n"); j = 1; } else { fprintf(PS.fp, "1.0 1.0 1.0 C\n"); j = 0; } fprintf(PS.fp, "%.1f %.1f %.1f %.1f B\n", x1, y1, x2, y2); /* set outline to black */ fprintf(PS.fp, "F 0.0 0.0 0.0 C\n"); fprintf(PS.fp, "D\n"); lab++; /* do text */ if (i == 0 || lab == sb.numbers) { sprintf(num, "%s", nice_number((sb.length / sb.segment) * i)); text_box_path(x1, y2 + margin, CENTER, LOWER, num, sb.fontsize, 0); if (sb.bgcolor) { /* TODO: take bg color, not just [white|none] */ set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); lab = 0; } if ((lab > 0 && i == seg - 1) || (sb.numbers == 1 && i == seg - 1)) { /* special case for last label */ sprintf(num, "%s", nice_number(sb.length)); text_box_path(x2, y2 + margin, CENTER, LOWER, num, sb.fontsize, 0); if (sb.bgcolor) { set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); } } } else { /* draw simple scalebar */ x1 = 72.0 * x + 0.5; y1 = (72.0 * (PS.page_height - sb.y)) + (width * 72.0); x2 = 72.0 * x + 0.5; y2 = 72.0 * (PS.page_height - sb.y); fprintf(PS.fp, "%.1f %.1f %.1f %.1f L D\n", x1, y1, x2, y2); /* draw label */ text_box_path(x1, y1 + margin, CENTER, LOWER, "0", sb.fontsize, 0); if (sb.bgcolor) { set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); x1 = 72.0 * x + 0.5; y1 = 72.0 * (PS.page_height - sb.y); x2 = 72.0 * (x + length) + 0.5; y2 = 72.0 * (PS.page_height - sb.y); fprintf(PS.fp, "%.1f %.1f %.1f %.1f L D\n", x1, y1, x2, y2); x1 = 72.0 * (x + length) + 0.5; y2 = (72.0 * (PS.page_height - sb.y)) + (width * 72.0); x2 = 72.0 * (x + length) + 0.5; y1 = 72.0 * (PS.page_height - sb.y); fprintf(PS.fp, "%.1f %.1f %.1f %.1f L D\n", x1, y1, x2, y2); /* draw label */ sprintf(num, "%s", nice_number(sb.length)); text_box_path(x1, y2 + margin, CENTER, LOWER, num, sb.fontsize, 0); if (sb.bgcolor) { set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); for (i = 1; i < seg; i++) { x1 = 72.0 * (x + (length / seg) * i) + 0.5; y1 = 72.0 * (PS.page_height - sb.y); x2 = 72.0 * (x + (length / seg) * i) + 0.5; y2 = (72.0 * (PS.page_height - sb.y)) + (width / 2. * 72.0); y3 = (72.0 * (PS.page_height - sb.y)) + (width * 72.0); fprintf(PS.fp, "%.1f %.1f %.1f %.1f L D\n", x1, y1, x2, y2); lab++; /* do text */ if (lab == sb.numbers) { sprintf(num, "%s", nice_number((sb.length / sb.segment) * i)); text_box_path(x1, y3 + margin, CENTER, LOWER, num, sb.fontsize, 0); if (sb.bgcolor) { set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); lab = 0; } } } /* draw units label */ if (sb.units == SB_UNITS_AUTO) strcpy(num, G_database_unit_name(TRUE)); else if(sb.units == SB_UNITS_METERS) strcpy(num, "meters"); else if(sb.units == SB_UNITS_KM) strcpy(num, "kilometers"); else if(sb.units == SB_UNITS_FEET) strcpy(num, "feet"); else if(sb.units == SB_UNITS_MILES) strcpy(num, "miles"); else if(sb.units == SB_UNITS_NMILES) strcpy(num, "nautical miles"); text_box_path(72.0 * (x + length/2), 72.0 * (PS.page_height - (sb.y + 0.075)), CENTER, UPPER, num, sb.fontsize, 0); if (sb.bgcolor) { set_rgb_color(WHITE); fprintf(PS.fp, "F "); } set_rgb_color(BLACK); fprintf(PS.fp, "TIB\n"); return 0; }
/*! \brief Get localized units name Units codes (gis.h): - U_METERS - U_KILOMETERS - U_ACRES - U_HECTARES - U_MILES - U_FEET - U_USFEET \param units units code \param plural plural form if true \param square area units if true \return units name \return NULL if units not found */ const char *G_get_units_name(int units, int plural, int square) { switch (units) { case U_UNKNOWN: if (square) return plural ? _("square units") : _("square unit"); else return plural ? _("units") : _("unit"); break; case U_METERS: if (square) return plural ? _("square meters") : _("square meter"); else return plural ? _("meters") : _("meter"); break; case U_KILOMETERS: if (square) return plural ? _("square kilometers") : _("square kilometer"); else return plural ? _("kilometers") : _("kilometer"); break; case U_ACRES: if (square) return plural ? _("acres") : _("acre"); else return G_get_units_name(G_units(G_database_unit_name(1)), plural, square); break; case U_HECTARES: if (square) return plural ? _("hectares") : _("hectare"); else return G_get_units_name(G_units(G_database_unit_name(1)), plural, square); break; case U_MILES: if (square) return plural ? _("square miles") : _("square mile"); else return plural ? _("miles") : _("mile"); break; case U_FEET: if (square) return plural ? _("square feet") : _("square foot"); else return plural ? _("feet") : _("foot"); break; case U_USFEET: if (square) return plural ? _("square US feet") : _("square US foot"); else return plural ? _("US feet") : _("US foot"); break; case U_DEGREES: if (square) return plural ? _("square degrees") : _("square degree"); else return plural ? _("degrees") : _("degree"); break; case U_YEARS: return plural ? _("years") : _("year"); break; case U_MONTHS: return plural ? _("months") : _("month"); break; case U_DAYS: return plural ? _("days") : _("day"); break; case U_HOURS: return plural ? _("hours") : _("hour"); break; case U_MINUTES: return plural ? _("minutes") : _("minute"); break; case U_SECONDS: return plural ? _("seconds") : _("second"); break; } return NULL; }
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); }