Beispiel #1
0
/*!
  \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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
/*!
  \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;
}
Beispiel #4
0
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 */
Beispiel #5
0
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;
}
Beispiel #6
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;
}
Beispiel #7
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);
}