Exemple #1
0
static void line(double m[2][3], double x0, double y0, double x1, double y1)
{
	double tx0 = m[0][0] * x0 + m[0][1] * y0 + m[0][2];
	double ty0 = m[1][0] * x0 + m[1][1] * y0 + m[1][2];
	double tx1 = m[0][0] * x1 + m[0][1] * y1 + m[0][2];
	double ty1 = m[1][0] * x1 + m[1][1] * y1 + m[1][2];

	D_line_abs(tx0, ty0, tx1, ty1);
}
Exemple #2
0
void plot(double lon1, double lat1, double lon2, double lat2,
	  int line_color, int text_color)
{
    int nsteps = 1000;
    int i;

    D_setup(0);

    D_use_color(line_color);

    if (lon1 == lon2) {
	D_line_abs(lon1, lat1, lon2, lat2);
	return;
    }

    if (lon1 > lon2) {
	double tmp = lon1;
	lon1 = lon2;
	lon2 = tmp;
    }

    G_shortest_way(&lon1, &lon2);

    G_begin_rhumbline_equation(lon1, lat1, lon2, lat2);

    D_begin();

    for (i = 0; i <= nsteps; i++) {
	double lon = lon1 + (lon2 - lon1) * i / nsteps;
	double lat = G_rhumbline_lat_from_lon(lon);
	if (i == 0)
	    D_move_abs(lon, lat);
	else
	    D_cont_abs(lon, lat);
    }

    D_end();
    D_stroke();
}
Exemple #3
0
void plot(double lon1, double lat1, double lon2, double lat2,
	  int line_color, int text_color, double factor, const char *unit)
{
    double distance;
    double text_x, text_y;
    double a, e2;
    int nsteps = 1000;
    int i;

    /* establish the current graphics window */
    D_setup(0);

    G_get_ellipsoid_parameters(&a, &e2);
    G_begin_geodesic_distance(a, e2);

    D_use_color(line_color);

    G_shortest_way(&lon1, &lon2);

    if (lon1 != lon2) {
	G_begin_geodesic_equation(lon1, lat1, lon2, lat2);

	D_begin();

	for (i = 0; i <= nsteps; i++) {
	    double lon = lon1 + (lon2 - lon1) * i / nsteps;
	    double lat = G_geodesic_lat_from_lon(lon);

	    if (i == 0)
		D_move_abs(lon, lat);
	    else
		D_cont_abs(lon, lat);
	}

	D_end();
	D_stroke();

	text_x = (lon1 + lon2) / 2;
	text_y = G_geodesic_lat_from_lon(text_x);
    }
    else {
	D_line_abs(lon1, lat1, lon2, lat2);
	text_x = (lon1 + lon2) / 2;
	text_y = (lat1 + lat2) / 2;
    }

    if (text_color != -1) {
	double t, b, l, r;
	char buf[100];

	D_text_size(10, 10);

	distance = G_geodesic_distance(lon1, lat1, lon2, lat2);
	sprintf(buf, "%.0f %s", distance / factor, unit);

	D_pos_abs(text_x, text_y);
	D_get_text_box(buf, &t, &b, &l, &r);

	if (t - D_get_u_north() > 0)
	    text_y -= t - D_get_u_north();
	if (b - D_get_u_south() < 0)
	    text_y -= b - D_get_u_south();
	if (r - D_get_u_east() > 0)
	    text_x -= r - D_get_u_east();
	if (l - D_get_u_west() < 0)
	    text_x -= l - D_get_u_west();

	D_use_color(text_color);

	D_pos_abs(text_x, text_y);
	D_text(buf);
    }
}
Exemple #4
0
int main(int argc, char **argv)
{
    struct Cell_head window;
    RASTER_MAP_TYPE raster_type, mag_raster_type = -1;
    int layer_fd;
    void *raster_row, *ptr;
    int nrows, ncols;
    int aspect_c = -1;
    float aspect_f = -1.0;

    double scale;
    int skip, no_arrow;
    char *mag_map = NULL;
    void *mag_raster_row = NULL, *mag_ptr = NULL;
    double length = -1;
    int mag_fd = -1;
    struct FPRange range;
    double mag_min, mag_max;

    struct GModule *module;
    struct Option *opt1, *opt2, *opt3, *opt4, *opt5,
	*opt6, *opt7, *opt8, *opt9;
    struct Flag *align;

    double t, b, l, r;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("display"));
    G_add_keyword(_("raster"));
    module->description =
	_("Draws arrows representing cell aspect direction "
	  "for a raster map containing aspect data.");

    opt1 = G_define_standard_option(G_OPT_R_MAP);
    opt1->description = _("Name of raster aspect map to be displayed");

    opt2 = G_define_option();
    opt2->key = "type";
    opt2->type = TYPE_STRING;
    opt2->required = NO;
    opt2->answer = "grass";
    opt2->options = "grass,compass,agnps,answers";
    opt2->description = _("Type of existing raster aspect map");

    opt3 = G_define_option();
    opt3->key = "arrow_color";
    opt3->type = TYPE_STRING;
    opt3->required = NO;
    opt3->answer = "green";
    opt3->gisprompt = "old_color,color,color";
    opt3->description = _("Color for drawing arrows");
    opt3->guisection = _("Colors");
    
    opt4 = G_define_option();
    opt4->key = "grid_color";
    opt4->type = TYPE_STRING;
    opt4->required = NO;
    opt4->answer = "gray";
    opt4->gisprompt = "old_color,color,color_none";
    opt4->description = _("Color for drawing grid or \"none\"");
    opt4->guisection = _("Colors");

    opt5 = G_define_option();
    opt5->key = "x_color";
    opt5->type = TYPE_STRING;
    opt5->required = NO;
    opt5->answer = DEFAULT_FG_COLOR;
    opt5->gisprompt = "old_color,color,color_none";
    opt5->description = _("Color for drawing X's (null values)");
    opt5->guisection = _("Colors");

    opt6 = G_define_option();
    opt6->key = "unknown_color";
    opt6->type = TYPE_STRING;
    opt6->required = NO;
    opt6->answer = "red";
    opt6->gisprompt = "old_color,color,color_none";
    opt6->description = _("Color for showing unknown information");
    opt6->guisection = _("Colors");

    opt9 = G_define_option();
    opt9->key = "skip";
    opt9->type = TYPE_INTEGER;
    opt9->required = NO;
    opt9->answer = "1";
    opt9->description = _("Draw arrow every Nth grid cell");

    opt7 = G_define_option();
    opt7->key = "magnitude_map";
    opt7->type = TYPE_STRING;
    opt7->required = NO;
    opt7->multiple = NO;
    opt7->gisprompt = "old,cell,raster";
    opt7->description =
	_("Raster map containing values used for arrow length");

    opt8 = G_define_option();
    opt8->key = "scale";
    opt8->type = TYPE_DOUBLE;
    opt8->required = NO;
    opt8->answer = "1.0";
    opt8->description = _("Scale factor for arrows (magnitude map)");

    align = G_define_flag();
    align->key = 'a';
    align->description = _("Align grids with raster cells");


    /* Check command line */
    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);


    layer_name = opt1->answer;

    arrow_color = D_translate_color(opt3->answer);
    x_color = D_translate_color(opt5->answer);
    unknown_color = D_translate_color(opt6->answer);

    if (strcmp("none", opt4->answer) == 0)
	grid_color = -1;
    else
	grid_color = D_translate_color(opt4->answer);


    if (strcmp("grass", opt2->answer) == 0)
	map_type = 1;
    else if (strcmp("agnps", opt2->answer) == 0)
	map_type = 2;
    else if (strcmp("answers", opt2->answer) == 0)
	map_type = 3;
    else if (strcmp("compass", opt2->answer) == 0)
	map_type = 4;


    scale = atof(opt8->answer);
    if (scale <= 0.0)
	G_fatal_error(_("Illegal value for scale factor"));

    skip = atoi(opt9->answer);
    if (skip <= 0)
	G_fatal_error(_("Illegal value for skip factor"));


    if (opt7->answer) {
	if (map_type != 1 && map_type != 4)
	    G_fatal_error(_("Magnitude is only supported for GRASS and compass aspect maps."));

	mag_map = opt7->answer;
    }
    else if (scale != 1.0)
	G_warning(_("Scale option requires magnitude_map"));


    /* Setup driver and check important information */
    if (D_open_driver() != 0)
      	G_fatal_error(_("No graphics device selected. "
			"Use d.mon to select graphics device."));
    
    D_setup(0);

    /* Read in the map window associated with window */
    G_get_window(&window);

    if (align->answer) {
	struct Cell_head wind;

	Rast_get_cellhd(layer_name, "", &wind);

	/* expand window extent by one wind resolution */
	wind.west += wind.ew_res * ((int)((window.west - wind.west) / wind.ew_res) - (window.west < wind.west));
	wind.east += wind.ew_res * ((int)((window.east - wind.east) / wind.ew_res) + (window.east > wind.east));
	wind.south += wind.ns_res * ((int)((window.south - wind.south) / wind.ns_res) - (window.south < wind.south));
	wind.north += wind.ns_res * ((int)((window.north - wind.north) / wind.ns_res) + (window.north > wind.north));

	wind.rows = (wind.north - wind.south) / wind.ns_res;
	wind.cols = (wind.east - wind.west) / wind.ew_res;

	Rast_set_window(&wind);

	nrows = wind.rows;
	ncols = wind.cols;

	t = (wind.north - window.north) * nrows / (wind.north - wind.south);
	b = t + (window.north - window.south) * nrows / (wind.north - wind.south);
	l = (window.west - wind.west) * ncols / (wind.east - wind.west);
	r = l + (window.east - window.west) * ncols / (wind.east - wind.west);
    } else {
        nrows = window.rows;
        ncols = window.cols;

	t = 0;
	b = nrows;
	l = 0;
	r = ncols;
    }

    D_set_src(t, b, l, r);
    D_update_conversions();

    /* figure out arrow scaling if using a magnitude map */
    if (opt7->answer) {
	Rast_init_fp_range(&range);	/* really needed? */
	if (Rast_read_fp_range(mag_map, "", &range) != 1)
	    G_fatal_error(_("Problem reading range file"));
	Rast_get_fp_range_min_max(&range, &mag_min, &mag_max);

	scale *= 1.5 / fabs(mag_max);
	G_debug(3, "scaling=%.2f  rast_max=%.2f", scale, mag_max);
    }

    if (grid_color > 0) {	/* ie not "none" */
	/* Set color */
	D_use_color(grid_color);

	/* Draw vertical grids */
	for (col = 0; col < ncols; col++)
	    D_line_abs(col, 0, col, nrows);

	/* Draw horizontal grids */
	for (row = 0; row < nrows; row++)
	    D_line_abs(0, row, ncols, row);
    }

    /* open the raster map */
    layer_fd = Rast_open_old(layer_name, "");

    raster_type = Rast_get_map_type(layer_fd);

    /* allocate the cell array */
    raster_row = Rast_allocate_buf(raster_type);


    if (opt7->answer) {
	/* open the magnitude raster map */
	mag_fd = Rast_open_old(mag_map, "");

	mag_raster_type = Rast_get_map_type(mag_fd);

	/* allocate the cell array */
	mag_raster_row = Rast_allocate_buf(mag_raster_type);
    }


    /* loop through cells, find value, determine direction (n,s,e,w,ne,se,sw,nw),
       and call appropriate function to draw an arrow on the cell */

    for (row = 0; row < nrows; row++) {
	Rast_get_row(layer_fd, raster_row, row, raster_type);
	ptr = raster_row;

	if (opt7->answer) {
	    Rast_get_row(mag_fd, mag_raster_row, row, mag_raster_type);
	    mag_ptr = mag_raster_row;
	}

	for (col = 0; col < ncols; col++) {

	    if (row % skip != 0)
		no_arrow = TRUE;
	    else
		no_arrow = FALSE;

	    if (col % skip != 0)
		no_arrow = TRUE;

	    /* find aspect direction based on cell value */
	    if (raster_type == CELL_TYPE)
		aspect_f = *((CELL *) ptr);
	    else if (raster_type == FCELL_TYPE)
		aspect_f = *((FCELL *) ptr);
	    else if (raster_type == DCELL_TYPE)
		aspect_f = *((DCELL *) ptr);


	    if (opt7->answer) {

		if (mag_raster_type == CELL_TYPE)
		    length = *((CELL *) mag_ptr);
		else if (mag_raster_type == FCELL_TYPE)
		    length = *((FCELL *) mag_ptr);
		else if (mag_raster_type == DCELL_TYPE)
		    length = *((DCELL *) mag_ptr);

		length *= scale;

		if (Rast_is_null_value(mag_ptr, mag_raster_type)) {
		    G_debug(5, "Invalid arrow length [NULL]. Skipping.");
		    no_arrow = TRUE;
		}
		else if (length <= 0.0) {	/* use fabs() or theta+=180? */
		    G_debug(5, "Illegal arrow length [%.3f]. Skipping.",
			    length);
		    no_arrow = TRUE;
		}
	    }

	    if (no_arrow) {
		ptr = G_incr_void_ptr(ptr, Rast_cell_size(raster_type));
		if (opt7->answer)
		    mag_ptr =
			G_incr_void_ptr(mag_ptr,
					Rast_cell_size(mag_raster_type));
		no_arrow = FALSE;
		continue;
	    }

	    /* treat AGNPS and ANSWERS data like old zero-as-null CELL */
	    /*   TODO: update models */
	    if (map_type == 2 || map_type == 3) {
		if (Rast_is_null_value(ptr, raster_type))
		    aspect_c = 0;
		else
		    aspect_c = (int)(aspect_f + 0.5);
	    }


	    /** Now draw the arrows **/

	    /* case switch for standard GRASS aspect map 
	       measured in degrees counter-clockwise from east */
	    if (map_type == 1) {
		D_use_color(arrow_color);

		if (Rast_is_null_value(ptr, raster_type)) {
		    D_use_color(x_color);
		    draw_x();
		    D_use_color(arrow_color);
		}
		else if (aspect_f >= 0.0 && aspect_f <= 360.0) {
		    if (opt7->answer)
			arrow_mag(aspect_f, length);
		    else
			arrow_360(aspect_f);
		}
		else {
		    D_use_color(unknown_color);
		    unknown_();
		    D_use_color(arrow_color);
		}
	    }


	    /* case switch for AGNPS type aspect map */
	    else if (map_type == 2) {
		D_use_color(arrow_color);
		switch (aspect_c) {
		case 0:
		    D_use_color(x_color);
		    draw_x();
		    D_use_color(arrow_color);
		    break;
		case 1:
		    arrow_n();
		    break;
		case 2:
		    arrow_ne();
		    break;
		case 3:
		    arrow_e();
		    break;
		case 4:
		    arrow_se();
		    break;
		case 5:
		    arrow_s();
		    break;
		case 6:
		    arrow_sw();
		    break;
		case 7:
		    arrow_w();
		    break;
		case 8:
		    arrow_nw();
		    break;
		default:
		    D_use_color(unknown_color);
		    unknown_();
		    D_use_color(arrow_color);
		    break;
		}
	    }


	    /* case switch for ANSWERS type aspect map */
	    else if (map_type == 3) {
		D_use_color(arrow_color);
		if (aspect_c >= 15 && aspect_c <= 360)	/* start at zero? */
		    arrow_360((double)aspect_c);
		else if (aspect_c == 400) {
		    D_use_color(unknown_color);
		    unknown_();
		    D_use_color(arrow_color);
		}
		else {
		    D_use_color(x_color);
		    draw_x();
		    D_use_color(arrow_color);
		}
	    }

	    /* case switch for compass type aspect map
	       measured in degrees clockwise from north */
	    else if (map_type == 4) {
		D_use_color(arrow_color);

		if (Rast_is_null_value(ptr, raster_type)) {
		    D_use_color(x_color);
		    draw_x();
		    D_use_color(arrow_color);
		}
		else if (aspect_f >= 0.0 && aspect_f <= 360.0) {
		    if (opt7->answer)
			arrow_mag(90 - aspect_f, length);
		    else
			arrow_360(90 - aspect_f);
		}
		else {
		    D_use_color(unknown_color);
		    unknown_();
		    D_use_color(arrow_color);
		}
	    }

	    ptr = G_incr_void_ptr(ptr, Rast_cell_size(raster_type));
	    if (opt7->answer)
		mag_ptr =
		    G_incr_void_ptr(mag_ptr, Rast_cell_size(mag_raster_type));
	}
    }

    Rast_close(layer_fd);
    if (opt7->answer)
	Rast_close(mag_fd);

    D_save_command(G_recreate_command());
    D_close_driver();

    exit(EXIT_SUCCESS);
}
Exemple #5
0
int bar(struct stat_list *dist_stats,	/* list of distribution statistics */
	struct Colors *colors)
{
    struct stat_node *ptr;
    int draw = YES;
    long int bar_height;	/* height, in pixels, of a histogram bar */
    CELL bar_color;		/* color/category number of a histogram bar */
    DCELL dmax, range_dmin, range_dmax, dmin, dval;
    long int max_tics;		/* maximum tics allowed on an axis */
    long int xoffset;		/* offset for x-axis */
    long int yoffset;		/* offset for y-axis */
    long int stat_start;
    long int stat_finis;
    int text_height;
    int text_width;
    long int i, j;
    long int num_cats = 0;
    long int num_stats = 0;
    long int tic_every;		/* spacing, in units of category value, of tics */

    long int tic_unit;
    double t, b, l, r;
    double tt, tb, tl, tr;
    double x_line[3];		/* for border of histogram */
    double y_line[3];
    double x_box[5];		/* for histogram bar coordinates */
    double y_box[5];
    double height, width;
    double xscale;		/* scaling factors */
    double yscale;
    char xlabel[1024];
    char ylabel[1024];
    char txt[1024];
    char tic_name[80];

    /* get coordinates of current screen window */
    D_get_src(&t, &b, &l, &r);

    /* create axis lines, to be drawn later */
    height = b - t;
    width = r - l;
    x_line[0] = x_line[1] = l + (ORIGIN_X * width);
    x_line[2] = l + (XAXIS_END * width);
    y_line[0] = b - (YAXIS_END * height);
    y_line[1] = y_line[2] = b - (ORIGIN_Y * height);

    /* figure scaling factors and offsets */
    num_cats = dist_stats->maxcat - dist_stats->mincat + 1;

    if (nodata) {
	num_cats++;
	dist_stats->mincat--;
    }
    xscale = ((x_line[2] - x_line[1]) / ((double)num_cats));
    yscale = ((y_line[1] - y_line[0])) / dist_stats->maxstat;
    if (num_cats >= x_line[2] - x_line[1])
	xoffset = (long int)x_line[1];
    else
	xoffset = (long int)x_line[0] + 0.5 * xscale;	/* boxes need extra space */
    yoffset = (double)(y_line[1]);

    /* figure tic_every and tic_units for the x-axis of the bar-chart.
     * tic_every tells how often to place a tic-number.  tic_unit tells
     * the unit to use in expressing tic-numbers.
     */
    if (xscale < XTIC_DIST) {
	max_tics = (x_line[2] - x_line[1]) / XTIC_DIST;
	if (nodata)
	    max_tics--;
	i = 0;
	if (is_fp) {
	    Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
	    if (Rast_is_d_null_value(&range_dmin) ||
		Rast_is_d_null_value(&range_dmax))
		G_fatal_error("Floating point data range is empty");

	    if ((range_dmax - range_dmin) < 1.0)
		tics[i].every = 5;
	    if ((range_dmax - range_dmin) < 110)
		tics[i].every = 20;	/* dirrty hack */
	    while ((range_dmax - range_dmin) / tics[i].every > max_tics)
		i++;
	}
	else {
	    while ((num_cats / tics[i].every) > max_tics)
		i++;
	}
	tic_every = tics[i].every;
	tic_unit = tics[i].unit;
	strcpy(tic_name, tics[i].name);
    }
    else {
	if (is_fp && !cat_ranges) {
	    Rast_get_fp_range_min_max(&fp_range, &range_dmin, &range_dmax);
	    if (Rast_is_d_null_value(&range_dmin) ||
		Rast_is_d_null_value(&range_dmax))
		G_fatal_error("Floating point data range is empty");
	}
	tic_every = 1;
	tic_unit = 1;
    }

    /* X-AXIS LOOP
     *
     * loop through category range, drawing a pie-slice and a
     * legend bar on each iteration evenly divisible, a tic-mark
     * on those evenly divisible by tic_unit, and a tic_mark
     * number on those evenly divisible by tic_every
     *
     */
    ptr = dist_stats->ptr;
    for (i = dist_stats->mincat; i <= dist_stats->maxcat; i++) {
	if (!ptr)
	    break;
	draw = NO;
	/* figure bar color and height 
	 *
	 * the cat number determines the color, the corresponding stat,
	 * determines the bar height.  if a stat cannot be found for the
	 * cat, then it doesn't drow anything, before it used to draw the
	 * box of size 0 in black. Later when the option to provide the
	 * background color will be added , we might still draw a box in
	 * this color.
	 */
	if (nodata && i == dist_stats->mincat) {
	    if (dist_stats->null_stat == 0 && xscale > 1)
		draw = NO;
	    else {
		draw = YES;
		Rast_set_c_null_value(&bar_color, 1);
		bar_height =
		    (yoffset - yscale * (double)dist_stats->null_stat);
	    }
	}
	else if (ptr->cat == i) {	/* AH-HA!! found the stat */
	    if (ptr->stat == 0 && xscale > 1)
		draw = NO;
	    else {
		draw = YES;
		bar_color = ptr->cat;
		bar_height = (yoffset - yscale * (double)ptr->stat);
	    }
	    if (ptr->next != NULL)
		ptr = ptr->next;
	}
	else {			/* we have to look for the stat */

	    /* loop until we find it, or pass where it should be */
	    while (ptr->cat < i && ptr->next != NULL)
		ptr = ptr->next;
	    if (ptr->cat == i) {	/* AH-HA!! found the stat */
		if (ptr->stat == 0 && xscale > 1)
		    draw = NO;
		else {
		    draw = YES;
		    bar_color = ptr->cat;
		    bar_height = (yoffset - yscale * (double)ptr->stat);
		}
		if (ptr->next != NULL)
		    ptr = ptr->next;
	    }
	    else {		/* stat cannot be found */

		if (xscale > 1) {
		    draw = NO;

#ifdef notdef
		    draw = YES;
		    bar_color = D_translate_color("black");
		    bar_height = yoffset;	/* zero */
#endif
		}
		else
		    draw = NO;
	    }
	}

	/* draw the bar */
	if (draw == YES) {
	    if (xscale != 1) {
		/* draw the bar as a box */
		if (!Rast_is_c_null_value(&bar_color) && is_fp) {
		    if (cat_ranges)
			Rast_get_ith_d_cat(&cats, bar_color,
					       &dmin, &dmax);
		    else {
			dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
			dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
		    }
		    if (dmin != dmax) {
			for (j = 0; j < xscale; j++) {
			    dval = dmin + j * (dmax - dmin) / xscale;
			    D_d_color(dval, colors);
			    x_box[0] = x_box[1] =
				xoffset + ((i - dist_stats->mincat) * xscale -
					   0.5 * xscale + j);
			    x_box[2] = x_box[3] =
				xoffset + ((i - dist_stats->mincat) * xscale -
					   0.5 * xscale + j + 1);
			    y_box[0] = y_box[3] = yoffset;
			    y_box[1] = y_box[2] = bar_height;
			    D_polygon_abs(x_box, y_box, 4);
			}
		    }
		    else {	/* 1-color bar */

			D_d_color(dmin, colors);
			x_box[0] = x_box[1] =
			    xoffset + ((i - dist_stats->mincat) * xscale -
				       0.5 * xscale);
			x_box[2] = x_box[3] =
			    xoffset + ((i - dist_stats->mincat) * xscale +
				       0.5 * xscale);
			y_box[0] = y_box[3] = yoffset;
			y_box[1] = y_box[2] = bar_height;
			D_polygon_abs(x_box, y_box, 4);
		    }
		}		/* fp */
		else {		/* 1-color bar for int data or null */

		    D_color((CELL) bar_color, colors);
		    x_box[0] = x_box[1] =
			xoffset + ((i - dist_stats->mincat) * xscale -
				   0.5 * xscale);
		    x_box[2] = x_box[3] =
			xoffset + ((i - dist_stats->mincat) * xscale +
				   0.5 * xscale);
		    y_box[0] = y_box[3] = yoffset;
		    y_box[1] = y_box[2] = bar_height;
		    D_polygon_abs(x_box, y_box, 4);
		}
	    }
	    else {
		/* draw the bar as a line */
		if (is_fp) {
		    if (cat_ranges)
			Rast_get_ith_d_cat(&cats, bar_color,
					       &dmin, &dmax);
		    else {
			dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
			dmax = range_dmin + (i + 1) * (range_dmax - range_dmin) / nsteps;
		    }
		    D_d_color(dmin, colors);
		}
		else
		    D_color((CELL) bar_color, colors);
		x_box[0] = x_box[1] =
		    xoffset + (i - dist_stats->mincat) * xscale;
		y_box[0] = yoffset;
		y_box[1] = bar_height;
		D_line_abs(x_box[0], y_box[0], x_box[1], y_box[1]);
	    }
	}

	/* draw x-axis tic-marks and numbers */
	/* draw tick for null and for numbers at every tic step
	   except when there is null, don't draw tic for mincat+1 */

	if (((rem((long int)i, tic_every) == 0L) ||
	     ((i == dist_stats->mincat) && nodata))
	    && !(nodata && i == dist_stats->mincat + 1)) {

	    /* draw a numbered tic-mark */
	    D_use_color(color);
	    D_begin();
	    D_move_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale,
		       b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, BIG_TIC * (b - t));
	    D_end();
	    D_stroke();

	    if (nodata && i == dist_stats->mincat)
		sprintf(txt, "null");
	    else if (is_fp) {
		dmin = range_dmin + i * (range_dmax - range_dmin) / nsteps;
		if ((tic_every * (range_dmax - range_dmin) / nsteps) < 1.0)
		    sprintf(txt, "%.2f", dmin / (double)tic_unit);
		else
		    sprintf(txt, "%d", (int)(dmin / (double)tic_unit));
	    }
	    else
		sprintf(txt, "%d", (int)(i / tic_unit));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tr - tl) > XTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale - (tr - tl) / 2,
		      b - XNUMS_Y * (b - t));
	    D_text(txt);
	}
	else if (rem(i, tic_unit) == 0.0) {
	    /* draw a tic-mark */
	    D_use_color(color);
	    D_begin();
	    D_move_abs(xoffset + (i - dist_stats->mincat) * xscale - 0.5 * xscale,
		       b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, SMALL_TIC * (b - t));
	    D_end();
	    D_stroke();
	}
    }

    /* draw the x-axis label */
    if (tic_unit != 1)
	sprintf(xlabel, "X-AXIS: Cell Values %s", tic_name);
    else
	sprintf(xlabel, "X-AXIS: Cell Values");
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH;
    D_text_size(text_width, text_height);
    D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2,
	       b - LABEL_1 * (b - t));
    D_use_color(color);
    D_text(xlabel);

    /* DRAW Y-AXIS TIC-MARKS AND NUMBERS
     * 
     * first, figure tic_every and tic_units for the x-axis of the bar-chart.
     * tic_every tells how often to place a tic-number.  tic_unit tells
     * the unit to use in expressing tic-numbers.
     */

    max_tics = (long)((y_line[1] - y_line[0]) / YTIC_DIST);

    if (dist_stats->maxstat == dist_stats->minstat)
	dist_stats->minstat = 0;	/* LOOKS FUNNY TO ME */
    num_stats = dist_stats->maxstat - dist_stats->minstat;
    i = 0;
    while ((num_stats / tics[i].every) > max_tics)
	i++;
    tic_every = tics[i].every;
    tic_unit = tics[i].unit;
    strcpy(tic_name, tics[i].name);

    stat_start = tic_unit * ((long)(dist_stats->minstat / tic_unit));
    stat_finis = tic_unit * ((long)(dist_stats->maxstat / tic_unit));

    /* Y-AXIS LOOP
     *
     */
    for (i = stat_start; i <= stat_finis; i += tic_unit) {
	if (rem(i, tic_every) == (float)0) {
	    /* draw a tic-mark */
	    D_begin();
	    D_move_abs(x_line[0], yoffset - yscale * i);
	    D_cont_rel((-(r - l) * BIG_TIC), 0);
	    D_end();
	    D_stroke();

	    /* draw a tic-mark number */
	    sprintf(txt, "%d", (int)(i / tic_unit));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tt - tb) > YTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs(l + (r - l) * YNUMS_X - (tr - tl) / 2,
		      yoffset - (yscale * i + 0.5 * (tt - tb)));
	    D_text(txt);
	}
	else if (rem(i, tic_unit) == 0.0) {
	    /* draw a tic-mark */
	    D_begin();
	    D_move_abs(x_line[0], yoffset - yscale * i);
	    D_cont_rel(-(r - l) * SMALL_TIC, 0);
	    D_end();
	    D_stroke();
	}
    }

    /* draw the y-axis label */
    if (tic_unit != 1) {
	if (type == COUNT)
	    sprintf(ylabel, "Y-AXIS: Number of cells %s", tic_name);
	else
	    sprintf(ylabel, "Y-AXIS: Area %s sq. meters", tic_name);
    }
    else {
	if (type == COUNT)
	    sprintf(ylabel, "Y-AXIS: Number of cells");
	else
	    sprintf(ylabel, "Y-AXIS: Area");
    }

    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH;
    D_text_size(text_width, text_height);
    D_get_text_box(ylabel, &tt, &tb, &tl, &tr);
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2,
	      b - LABEL_2 * (b - t));
    D_use_color(color);
    D_text(ylabel);

    /* draw x and y axis lines */
    D_use_color(color);
    D_polyline_abs(x_line, y_line, 3);

    return 0;
}
Exemple #6
0
int main(int argc, char **argv)
{
    double xoffset;		/* offset for x-axis */
    double yoffset;		/* offset for y-axis */
    double text_height;
    double text_width;
    int i;
    int j;
    int c;
    int tic_every;
    int max_tics;
    int title_color;
    int num_y_files;
    int tic_unit;
    double t, b, l, r;
    double tt, tb, tl, tr;
    double prev_x, prev_y[11];
    double new_x, new_y[11];
    int line;
    double x_line[3];
    double y_line[3];
    int err;

    struct in_file
    {
	int num_pnts;		/* number of lines in file  */
	int color;		/* color to use for y lines */
	float max;		/* maximum value in file    */
	float min;		/* minimum value in file    */
	float value;		/* current value read in    */
	char name[1024];	/* name of file      */
	char full_name[1024];	/* path/name of file    */
	FILE *fp;		/* pointer to file        */
    };

    struct in_file in[12];
    struct GModule *module;

    float max_y;
    float min_y;
    float height, width;
    float xscale;
    float yscale;

    char txt[1024], xlabel[512];
    char tic_name[1024];
    char *name;
    char color_name[20];

    FILE *fopen();

    struct Option *dir_opt, *x_opt, *y_opt;
    struct Option *y_color_opt;
    struct Option *title[3];
    struct Option *t_color_opt;

    /* Initialize the GIS calls */
    G_gisinit(argv[0]);

    /* Set description */
    module = G_define_module();
    G_add_keyword(_("display"));
    G_add_keyword(_("cartography"));
    module->description =
	_("Generates and displays simple line graphs in the active graphics monitor display frame.");

    x_opt = G_define_option();
    x_opt->key = "x_file";
    x_opt->description = _("Name of data file for X axis of graph");
    x_opt->type = TYPE_STRING;
    x_opt->required = YES;

    y_opt = G_define_option();
    y_opt->key = "y_file";
    y_opt->description = _("Name of data file(s) for Y axis of graph");
    y_opt->type = TYPE_STRING;
    y_opt->required = YES;
    y_opt->multiple = YES;

    dir_opt = G_define_option();
    dir_opt->key = "directory";
    dir_opt->description = _("Path to file location");
    dir_opt->type = TYPE_STRING;
    dir_opt->required = NO;
    /* Remove answer because create problem with full path */
    /* dir_opt->answer = "."; */

    y_color_opt = G_define_option();
    y_color_opt->key = "y_color";
    y_color_opt->description = _("Color for Y data");
    y_color_opt->type = TYPE_STRING;
    y_color_opt->required = NO;
    y_color_opt->multiple = YES;
    y_color_opt->gisprompt = "old_color,color,color";
    y_color_opt->answers = NULL;

    t_color_opt = G_define_option();
    t_color_opt->key = "title_color";
    t_color_opt->description = _("Color for axis, tics, numbers, and title");
    t_color_opt->type = TYPE_STRING;
    t_color_opt->required = NO;
    t_color_opt->gisprompt = "old_color,color,color";
    t_color_opt->answer = DEFAULT_FG_COLOR;

    title[0] = G_define_option();
    title[0]->key = "x_title";
    title[0]->description = _("Title for X data");
    title[0]->type = TYPE_STRING;
    title[0]->required = NO;
    title[0]->answer = "";

    title[1] = G_define_option();
    title[1]->key = "y_title";
    title[1]->description = _("Title for Y data");
    title[1]->type = TYPE_STRING;
    title[1]->required = NO;
    title[1]->answer = "";

    title[2] = G_define_option();
    title[2]->key = "title";
    title[2]->description = _("Title for Graph");
    title[2]->type = TYPE_STRING;
    title[2]->required = NO;
    title[2]->answer = "";


    if (G_parser(argc, argv))
	exit(EXIT_FAILURE);

    for (i = 0; i < 3; i++) {
	for (j = 0; j < strlen(title[i]->answer); j++)
	    if (title[i]->answer[j] == '_')
		title[i]->answer[j] = ' ';
    }

    /* build path to X data file and open for reading
       notice that in[0] will be the X file, and in[1-10]
       will be the Y file(s) */

    if (dir_opt->answer != NULL) {
	sprintf(in[0].full_name, "%s/%s", dir_opt->answer, x_opt->answer);
    } else {
	sprintf(in[0].full_name, "%s", x_opt->answer);
    }
    sprintf(in[0].name, "%s", x_opt->answer);

    if ((in[0].fp = fopen(in[0].full_name, "r")) == NULL)
	G_fatal_error(_("Unable to open input file <%s>"), in[0].full_name);

    num_y_files = 0;

    /* open all Y data files */

    for (i = 0, j = 1; (name = y_opt->answers[i]); i++, j++) {
      
	if (dir_opt->answer != NULL) {
	    sprintf(in[j].full_name, "%s/%s", dir_opt->answer, name);
	} else {
	    sprintf(in[j].full_name, "%s", name);
	}
	sprintf(in[j].name, "%s", name);

	if ((in[j].fp = fopen(in[j].full_name, "r")) == NULL)
	    G_fatal_error(_("Unable to open input file <%s>"),
			  in[j].full_name);

	num_y_files++;
	if (num_y_files > 10)
	    G_fatal_error(_("Maximum of 10 Y data files exceeded"));
    }

    /* set colors  */

    title_color = D_translate_color(t_color_opt->answer);

    /* I had an argument with the parser, and couldn't get a neat list of
       the input colors as I thought I should. I did a quick hack to get
       my list from the answer var, which gives us the colors input
       separated by commas. at least we know that they have been checked against
       the list of possibles */
    c = 0;
    j = 1;
    if (y_color_opt->answer != NULL) {
	for (i = 0; i <= (strlen(y_color_opt->answer)); i++) {
	    if ((y_color_opt->answer[i] == ',') ||
		(i == (strlen(y_color_opt->answer)))) {
		color_name[c] = '\0';
		in[j].color = D_translate_color(color_name);
		j++;
		c = 0;
	    }
	    else {
		color_name[c++] = y_color_opt->answer[i];
	    }
	}
	/* this is lame. I could come up with a color or prompt for one or something */
	if (j < num_y_files)
	    G_fatal_error(_("Only <%d> colors given for <%d> lines"), j,
			  num_y_files);
    }
    else
	/* no colors given on command line, use default list */
    {
	for (i = 1; i <= num_y_files; i++) {
	    in[i].color = default_y_colors[i];
	}
    }

    /* get coordinates of current screen window, in pixels */
    D_open_driver();
    
    D_setup_unity(0);
    D_get_src(&t, &b, &l, &r);

    /* create axis lines, to be drawn later */
    height = b - t;
    width = r - l;
    x_line[0] = x_line[1] = l + (ORIGIN_X * width);
    x_line[2] = l + (XAXIS_END * width);
    y_line[0] = b - (YAXIS_END * height);
    y_line[1] = y_line[2] = b - (ORIGIN_Y * height);
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH;
    D_text_size(text_width, text_height);

    /* read thru each data file in turn, find max and min values for
       each, count lines, find x min and max, find overall y min and
       max */

    max_y = -99999.9;
    min_y = 99999.9;

    for (i = 0; i <= num_y_files; i++) {

	in[i].min = 99999.9;
	in[i].max = -99999.9;
	in[i].value = 0.0;
	in[i].num_pnts = 0;

	while ((err = fscanf(in[i].fp, "%f", &in[i].value)) != EOF) {
	    in[i].num_pnts++;
	    in[i].max = MAX(in[i].max, in[i].value);
	    in[i].min = MIN(in[i].min, in[i].value);
	    if (i > 0) {	/* if we have a y file */
		min_y = MIN(min_y, in[i].value);
		max_y = MAX(max_y, in[i].value);
	    }
	}
	if ((i > 0) && (in[0].num_pnts != in[i].num_pnts)) {
        if (in[i].num_pnts < in[0].num_pnts) {
            G_warning(_("Y input file <%s> contains fewer data points than the X input file"),
		      in[i].name);
        }
        else {
            G_warning(_("Y input file <%s> contains more data points than the X input file"),
		      in[i].name);
        }
        
	    if (in[i].num_pnts > in[0].num_pnts)
		G_message(n_("The last point will be ignored", 
                     "The last %d points will be ignored",
                     (in[i].num_pnts - in[0].num_pnts)),
			  (in[i].num_pnts - in[0].num_pnts));
	}
    }

    /* close all files */

    for (i = 0; i <= num_y_files; i++)
	fclose(in[i].fp);

    /* figure scaling factors and offsets */

    xscale = ((double)(x_line[2] - x_line[1]) / (double)(in[0].num_pnts));
    yscale = ((double)(y_line[1] - y_line[0]) / (max_y - min_y));
    yoffset = (double)(y_line[1]);
    xoffset = (double)(x_line[1]);

    /* figure tic_every and tic_units for the x-axis of the bar-chart.
       tic_every tells how often to place a tic-number.  tic_unit tells
       the unit to use in expressing tic-numbers. */

    if (xscale < XTIC_DIST) {
	max_tics = (x_line[2] - x_line[1]) / XTIC_DIST;
	i = 1;
	while (((in[0].max - in[0].min) / tics[i].every) > max_tics)
	    i++;
	tic_every = tics[i].every;
	tic_unit = tics[i].unit;
	strcpy(tic_name, tics[i].name);
    }
    else {
	tic_every = 1;
	tic_unit = 1;
	strcpy(tic_name, "");
    }


    /* open all the data files again */

    for (i = 0; i <= num_y_files; i++) {
	if ((in[i].fp = fopen(in[i].full_name, "r")) == NULL) {
	    D_close_driver();
	    G_fatal_error(_("Unable to open input file <%s>"), in[i].full_name);
	}
    }

    /* loop through number of lines in x data file, 
       then loop thru for each y file, drawing a piece of each line and a
       legend bar on each iteration evenly divisible, a tic-mark
       on those evenly divisible by tic_unit, and a tic_mark
       number on those evenly divisible by tic_every   */

    /* read the info from the inputs */

    for (line = 0; line < in[0].num_pnts; line++) {
	/* scan in an X value */
	err = fscanf(in[0].fp, "%f", &in[0].value);

	/* didn't find a number or hit EOF before our time */
	if ((err != 1) || (err == EOF)) {
	    D_close_driver();
	    G_fatal_error(_("Problem reading X data file at line %d"), line);
	}

	/* for each Y data file, get a value and compute where to draw it */
	for (i = 1; i <= num_y_files; i++) {
	    /* check to see that we do indeed have data for this point */
	    if (line < in[i].num_pnts) {
		err = fscanf(in[i].fp, "%f", &in[i].value);
		if ((in[i].num_pnts >= line) && (err != 1)) {
		    D_close_driver();
		    G_fatal_error(_("Problem reading <%s> data file at line %d"),
				  in[i].name, line);
		}

		/* in case the Y file has fewer lines than the X file, we will skip
		   trying to draw when we run out of data */

		/* draw increment of each Y file's data */

		D_use_color(in[i].color);

		/* find out position of where Y should be drawn. */
		/* if our minimum value of y is not negative, this is easy */

		if (min_y >= 0)
		    new_y[i] =
			(yoffset - yscale * (in[i].value - min_y));

		/* if our minimum value of y is negative, then we have two
		   cases:  our current value to plot is pos or neg */

		else {
		    if (in[i].value < 0)
			new_y[i] = (yoffset - yscale * (-1 *
							     (min_y -
							      in[i].value)));
		    else
			new_y[i] = (yoffset - yscale * (in[i].value +
							     (min_y * -1)));
		}

		new_x = xoffset + (line * xscale);
		if (line == 0) {
		    prev_x = xoffset;
		    prev_y[i] = yoffset;
		}
		D_line_abs(prev_x, prev_y[i], new_x, new_y[i]);
		prev_y[i] = new_y[i];
	    }
	}
	prev_x = new_x;

	/* draw x-axis tic-marks and numbers */

	if (rem((long int)in[0].value, tic_every) == 0.0) {

	    /* draw a numbered tic-mark */

	    D_use_color(title_color);
	    D_begin();
	    D_move_abs(xoffset + line * xscale, b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, BIG_TIC * (b - t));
	    D_end();
	    D_stroke();

	    if ((in[0].value >= 1) || (in[0].value <= -1) ||
		(in[0].value == 0))
		sprintf(txt, "%.0f", (in[0].value / tic_unit));
	    else
		sprintf(txt, "%.2f", (in[0].value));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tr - tl) > XTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs((xoffset + (line * xscale - (tr - tl) / 2)),
		       (b - XNUMS_Y * (b - t)));
	    D_text(txt);
	}
	else if (rem(line, tic_unit) == 0.0) {

	    /* draw a tic-mark */

	    D_use_color(title_color);
	    D_begin();
	    D_move_abs(xoffset + line * xscale,
		       b - ORIGIN_Y * (b - t));
	    D_cont_rel(0, SMALL_TIC * (b - t));
	    D_end();
	    D_stroke();
	}
    }

    /* close all input files */
    for (i = 0; i <= num_y_files; i++) {
	fclose(in[i].fp);
    }

    /* draw the x-axis label */
    if ((strcmp(title[0]->answer, "") == 0) && (strcmp(tic_name, "") == 0))
	*xlabel = '\0';
    else
	sprintf(xlabel, "X: %s %s", title[0]->answer, tic_name);
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH * 1.5;
    D_text_size(text_width, text_height);
    D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
    D_pos_abs((l + (r - l) / 2 - (tr - tl) / 2),
	      (b - LABEL_1 * (b - t)));
    D_use_color(title_color);
    D_text(xlabel);

    /* DRAW Y-AXIS TIC-MARKS AND NUMBERS
       first, figure tic_every and tic_units for the x-axis of the bar-chart.
       tic_every tells how often to place a tic-number.  tic_unit tells
       the unit to use in expressing tic-numbers. */

    if (yscale < YTIC_DIST) {
	max_tics = (y_line[1] - y_line[0]) / YTIC_DIST;
	i = 1;
	while (((max_y - min_y) / tics[i].every) > max_tics)
	    i++;
	tic_every = tics[i].every;
	tic_unit = tics[i].unit;
	strcpy(tic_name, tics[i].name);
    }
    else {
	tic_every = 1;
	tic_unit = 1;
	strcpy(tic_name, "");
    }

    /* Y-AXIS LOOP */

    for (i = (int)min_y; i <= (int)max_y; i += tic_unit) {
	if (rem(i, tic_every) == 0.0) {
	    /* draw a tic-mark */

	    D_begin();
	    D_move_abs(x_line[0], yoffset - yscale * (i - min_y));
	    D_cont_rel(-(r - l) * BIG_TIC, 0);
	    D_end();
	    D_stroke();

	    /* draw a tic-mark number */

	    sprintf(txt, "%d", (i / tic_unit));
	    text_height = (b - t) * TEXT_HEIGHT;
	    text_width = (r - l) * TEXT_WIDTH;
	    D_text_size(text_width, text_height);
	    D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    while ((tt - tb) > YTIC_DIST) {
		text_width *= 0.75;
		text_height *= 0.75;
		D_text_size(text_width, text_height);
		D_get_text_box(txt, &tt, &tb, &tl, &tr);
	    }
	    D_pos_abs(l + (r - l) * YNUMS_X - (tr - tl) / 2,
		      yoffset - (yscale * (i - min_y) + 0.5 * (tt - tb)));
	    D_text(txt);
	}
	else if (rem(i, tic_unit) == 0.0) {
	    /* draw a tic-mark */
	    D_begin();
	    D_move_abs(x_line[0], (yoffset - yscale * (i - min_y)));
	    D_cont_rel(-(r - l) * SMALL_TIC, 0);
	    D_end();
	    D_stroke();
	}
    }

    /* draw the y-axis label */
    if ((strcmp(title[1]->answer, "") == 0) && (strcmp(tic_name, "") == 0))
	*xlabel = '\0';
    else
	sprintf(xlabel, "Y: %s %s", title[1]->answer, tic_name);
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH * 1.5;
    D_text_size(text_width, text_height);
    D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2, b - LABEL_2 * (b - t));
    D_use_color(title_color);
    D_text(xlabel);

    /* top label */
    sprintf(xlabel, "%s", title[2]->answer);
    text_height = (b - t) * TEXT_HEIGHT;
    text_width = (r - l) * TEXT_WIDTH * 2.0;
    D_text_size(text_width, text_height);
    D_get_text_box(xlabel, &tt, &tb, &tl, &tr);
    /*
       D_move_abs((int)(((r-l)/2)-(tr-tl)/2),
       (int) (t+ (b-t)*.07) );
     */
    D_pos_abs(l + (r - l) / 2 - (tr - tl) / 2, t + (b - t) * .07);
    D_use_color(title_color);
    D_text(xlabel);

    /* draw x and y axis lines */
    D_use_color(title_color);
    D_polyline_abs(x_line, y_line, 3);

    D_save_command(G_recreate_command());
    D_close_driver();
    
    exit(EXIT_SUCCESS);
}