/*
 ****************************************************************************
 *
 * MODULE:       d.rast.arrow
 * AUTHOR(S):    Chris Rewerts, Agricultural Engineering, Purdue University
 * PURPOSE:      Draw arrows on slope/aspect maps. 
 * COPYRIGHT:    (C) 2000 by the GRASS Development Team
 *
 *              This program is free software under the GNU General Public
 *              License (>=v2). Read the file COPYING that comes with GRASS
 *              for details.
 *
 *****************************************************************************/

/* some minor cleanup done by Andreas Lange, andreas.lange@rhein-main.de
 * Update to handle NULLs and floating point aspect maps: Hamish Bowman, Aug 2004
 * Update for 360 degree arrows and magnitude scaling:  Hamish Bowman, Oct 2005
 */

/*
 *   Chris Rewerts, Agricultural Engineering, Purdue University
 *   rewerts@ecn.purdue.edu  March 1991
 *
 *   d.rast.arrow
 *
 *   Usage:  d.rast.arrow
 * 
 *   This program used Dgrid's sources as a beginning. Purpose of Darrow
 *   is to read an aspect layer produced by slope.aspect or by the 
 *   programs created for the ANSWERS or AGNPS Hydrology Toolbox
 *   endeavors.  d.rast.arrow draws an arrow on the graphic display
 *   of each cell, so that the flow pattern computed as an aspect
 *   layer can be easily seen. Other symbols ("?", "X") may be drawn
 *   as needed.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/display.h>
#include <grass/colors.h>
#include <grass/glocale.h>

# define RpD ((2 * M_PI) / 360.)	/* radians/degree */
# define D2R(d) (double)(d * RpD)	/* degrees->radians */

#define MAIN
static void arrow_mag(double, double);
static void arrow_360(double);
static void arrow_se(void);
static void arrow_ne(void);
static void arrow_nw(void);
static void arrow_sw(void);
static void arrow_e(void);
static void arrow_w(void);
static void arrow_s(void);
static void arrow_n(void);
static void draw_x(void);
static void unknown_(void);

int D_x, D_y;
double D_ew, D_ns;
char *mapset;
char layer_name[128];
int layer_set;
int map_type, arrow_color, grid_color, x_color, unknown_color;


int main(int argc, char **argv)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    char window_name[128];
    struct Cell_head window;
    int t, b, l, r;
    char full_name[128];
    RASTER_MAP_TYPE raster_type, mag_raster_type = -1;
    int layer_fd;
    void *raster_row, *ptr;
    int nrows, ncols, row, col;
    int aspect_c = -1;
    float aspect_f = -1.0;
    double ew_res, ns_res;
    double D_south, D_west;
    double D_north, D_east;
    double U_to_D_xconv, U_to_D_yconv;
    double U_west, U_south;
    double U_east, U_north;
    double U_start;
    double U_x, U_y;

    double scale;
    int skip, no_arrow;
    char *mag_map = NULL, *mag_mapset = 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;

    G_gisinit(argv[0]);

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

    opt1 = G_define_option();
    opt1->key = "map";
    opt1->type = TYPE_STRING;
    opt1->required = NO;
    opt1->multiple = NO;
    opt1->gisprompt = "old,cell,raster";
    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->options = D_COLOR_LIST;
    opt3->description = _("Color for drawing arrows");

    opt4 = G_define_option();
    opt4->key = "grid_color";
    opt4->type = TYPE_STRING;
    opt4->required = NO;
    opt4->answer = "gray";
    opt4->options = D_COLOR_LIST ",none";
    opt4->description = _("Color for drawing grid or \"none\"");

    opt5 = G_define_option();
    opt5->key = "x_color";
    opt5->type = TYPE_STRING;
    opt5->required = NO;
    opt5->answer = DEFAULT_FG_COLOR;
    opt5->options = D_COLOR_LIST;
    opt5->description = _("Color for drawing X's (Null values)");

    opt6 = G_define_option();
    opt6->key = "unknown_color";
    opt6->type = TYPE_STRING;
    opt6->required = NO;
    opt6->answer = "red";
    opt6->options = D_COLOR_LIST;
    opt6->description = _("Color for showing unknown information");

    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)");


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


    if (opt1->answer) {
	G_strncpy(layer_name, opt1->answer, sizeof(layer_name) - 1);
	if ((mapset = G_find_cell2(layer_name, "")) == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), layer_name);
	layer_set = 1;
    }
    else
	layer_set = 0;


    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;
	if ((mag_mapset = G_find_cell2(mag_map, "")) == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), mag_map);
    }
    else if (scale != 1.0)
	G_warning(_("Scale option requires magnitude_map"));


    /* Setup driver and check important information */
    if (R_open_driver() != 0)
	G_fatal_error(_("No graphics device selected"));

    if (D_get_cur_wind(window_name))
	G_fatal_error(_("No current window"));

    if (D_set_cur_wind(window_name))
	G_fatal_error(_("Current window not available"));

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

    if (D_check_map_window(&window))
	G_fatal_error(_("Setting map window"));

    if (G_set_window(&window) == -1)
	G_fatal_error(_("Current window not settable"));

    /* Determine conversion factors */
    if (D_get_screen_window(&t, &b, &l, &r))
	G_fatal_error(_("Getting screen window"));
    if (D_do_conversions(&window, t, b, l, r))
	G_fatal_error(_("Error in calculating conversions"));

    /* where are we, both geographically and on the screen? */
    D_south = D_get_d_south();
    D_north = D_get_d_north();
    D_east = D_get_d_east();
    D_west = D_get_d_west();

    U_west = D_get_u_west();
    U_east = D_get_u_east();
    U_south = D_get_u_south();
    U_north = D_get_u_north();

    U_to_D_xconv = D_get_u_to_d_xconv();
    U_to_D_yconv = D_get_u_to_d_yconv();

    /* number of rows and cols in window */
    nrows = window.rows;
    ncols = window.cols;

    /*
       if ((nrows > 75) || (ncols > 75)){ 
       fprintf (stdout,"\n"); 
       fprintf (stdout,"Current window size:\n"); 
       fprintf (stdout,"rows:    %d\n", nrows);
       fprintf (stdout,"columns: %d\n", ncols);
       fprintf (stdout,"\n"); 
       fprintf (stdout,"Your current window setting may be too large.\n"); 
       fprintf (stdout,"Cells displayed on your graphics window may be too\n"); 
       fprintf (stdout,"small for arrows to be visible.\n\n"); 
       if (!G_yes("Do you wish to continue", 0))
       exit(0);
       }
     */

    /* resolutions */
    ew_res = window.ew_res;
    ns_res = window.ns_res;

    /* how many screen units of distance for each cell */
    D_ew = (D_east - D_west) / ncols;
    D_ns = (D_south - D_north) / nrows;

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

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

/*------------------------------------------
    fprintf (stdout,"ew_res:  %.2f\n", window.ew_res);
    fprintf (stdout,"ns_res:  %.2f\n", window.ns_res);
    fprintf (stdout,"D_ew:  %f D_ns:  %f \n", D_ew, D_ns); 
    fprintf (stdout,"nrows:    %d\n", nrows);
    fprintf (stdout,"ncols:    %d\n", ncols);
    fprintf (stdout,"t:  %d\n", t);
    fprintf (stdout,"b:  %d\n", b);
    fprintf (stdout,"l:  %d\n", l);
    fprintf (stdout,"r:  %d\n", r);
    fprintf (stdout,"U_west:	%f\n", U_west);
    fprintf (stdout,"U_east:	%f\n", U_east);
    fprintf (stdout,"U_south:	%f\n", U_south);
    fprintf (stdout,"U_north:	%f\n", U_north);
    fprintf (stdout,"D_west:	%f\n", D_west);
    fprintf (stdout,"D_east:	%f\n", D_east);
    fprintf (stdout,"D_south:	%f\n", D_south);
    fprintf (stdout,"D_north:	%f\n", D_north);
    fprintf (stdout,"U_to_D_xconv:	%f\n", U_to_D_xconv);
    fprintf (stdout,"U_to_D_yconv:	%f\n", U_to_D_yconv);
--------------------------------------------------------*/

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

	/* Draw vertical grids */
	U_start = U_east;
	for (U_x = U_start; U_x >= U_west; U_x -= ew_res) {
	    D_x = (int)((U_x - U_west) * U_to_D_xconv + D_west);
	    R_move_abs(D_x, (int)D_south);
	    R_cont_abs(D_x, (int)D_north);
	}

	/* Draw horizontal grids */
	U_start = U_north;
	for (U_y = U_start; U_y >= U_south; U_y -= ns_res) {
	    D_y = (int)((U_south - U_y) * U_to_D_yconv + D_south);
	    R_move_abs((int)D_west, D_y);
	    R_cont_abs((int)D_east, D_y);
	}
    }

    /* if we didn't get a layer name from the arg options, then
       get name of layer that is on the screen */
    if (!layer_set) {
	if (D_get_cell_name(full_name))
	    G_fatal_error(_("No raster map exists in the current window"));

	mapset = G_find_cell(full_name, "");
	if (mapset == NULL)
	    G_fatal_error(_("Raster map <%s> not found"), full_name);

	sscanf(full_name, "%s", layer_name);
    }

    /* open the raster map */
    layer_fd = G_open_cell_old(layer_name, mapset);
    if (layer_fd < 0)
	G_fatal_error(_("Unable to open raster map <%s>"), layer_name);

    raster_type = G_get_raster_map_type(layer_fd);

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


    if (opt7->answer) {
	/* open the magnitude raster map */
	mag_fd = G_open_cell_old(mag_map, mag_mapset);
	if (mag_fd < 0)
	    G_fatal_error("Unable to open raster map <%s>", mag_map);

	mag_raster_type = G_get_raster_map_type(mag_fd);

	/* allocate the cell array */
	mag_raster_row = G_allocate_raster_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++) {
	G_get_raster_row(layer_fd, raster_row, row, raster_type);
	ptr = raster_row;

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

	/* determine screen y coordinate of top of current cell */
	D_y = (int)(row * D_ns + D_north);

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

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

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

	    /* determine screen x coordinate of west side of current cell */
	    D_x = (int)(col * D_ew + D_west);

	    /* 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 (G_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, G_raster_size(raster_type));
		if (opt7->answer)
		    mag_ptr =
			G_incr_void_ptr(mag_ptr,
					G_raster_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 (G_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) {
		R_standard_color(arrow_color);

		if (G_is_null_value(ptr, raster_type)) {
		    R_standard_color(x_color);
		    draw_x();
		    R_standard_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 {
		    R_standard_color(unknown_color);
		    unknown_();
		    R_standard_color(arrow_color);
		}
	    }


	    /* case switch for AGNPS type aspect map */
	    else if (map_type == 2) {
		R_standard_color(arrow_color);
		switch (aspect_c) {
		case 0:
		    R_standard_color(x_color);
		    draw_x();
		    R_standard_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:
		    R_standard_color(unknown_color);
		    unknown_();
		    R_standard_color(arrow_color);
		    break;
		}
	    }


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

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

		if (G_is_null_value(ptr, raster_type)) {
		    R_standard_color(x_color);
		    draw_x();
		    R_standard_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 {
		    R_standard_color(unknown_color);
		    unknown_();
		    R_standard_color(arrow_color);
		}
	    }

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

    G_close_cell(layer_fd);
    if (opt7->answer)
	G_close_cell(mag_fd);
    D_add_to_list(G_recreate_command());
    R_close_driver();

    exit(0);
}

/* --- end of main --- */

/*---------------------------------------------------------------*/


static void arrow_mag(double theta, double length)
{				/* angle is measured in degrees counter-clockwise from east */
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y, dx, dy, mid_x, mid_y;
    double theta_offset;

    theta *= -1;		/* display coords use inverse y */

    /* find the display coordinates of the middle of the cell */
    mid_x = D_x + (int)(D_ew * .5);
    mid_y = D_y + (int)(D_ns * .5);

    /* tail */
    R_move_abs(mid_x, mid_y);

    /* head */
    x = mid_x + (int)(length * cos(D2R(theta)));
    y = mid_y + (int)(length * sin(D2R(theta)));
    R_cont_abs(x, y);

    /* fin 1 */
    theta_offset = theta + 20;
    dx = mid_x + (int)(0.6 * length * cos(D2R(theta_offset)));
    dy = mid_y + (int)(0.6 * length * sin(D2R(theta_offset)));
    R_cont_abs(dx, dy);

    /* fin 2 */
    R_move_abs(x, y);
    theta_offset = theta - 20;
    dx = mid_x + (int)(0.6 * length * cos(D2R(theta_offset)));
    dy = mid_y + (int)(0.6 * length * sin(D2R(theta_offset)));
    R_cont_abs(dx, dy);
}


static void arrow_360(double theta)
{				/* angle is measured in degrees counter-clockwise from east */
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y, dx, dy, mid_x, mid_y;
    double max_radius, theta_offset;

    theta *= -1;		/* display coords use inverse y */
    max_radius = ((D_ew < D_ns) ? D_ew : D_ns) * 0.8 / 2;

    /* find the display coordinates of the middle of the cell */
    mid_x = D_x + (int)(D_ew * 0.5);
    mid_y = D_y + (int)(D_ns * 0.5);

    /* head */
    x = mid_x + (int)(max_radius * cos(D2R(theta)));
    y = mid_y + (int)(max_radius * sin(D2R(theta)));
    R_move_abs(x, y);

    /* tail */
    dx = -2 * (int)(max_radius * cos(D2R(theta)));
    dy = -2 * (int)(max_radius * sin(D2R(theta)));
    R_cont_rel(dx, dy);

    /* fin 1 */
    R_move_abs(x, y);
    theta_offset = theta + 90;
    dx = mid_x + (int)(0.5 * max_radius * cos(D2R(theta_offset)));
    dy = mid_y + (int)(0.5 * max_radius * sin(D2R(theta_offset)));
    R_cont_abs(dx, dy);

    /* fin 2 */
    R_move_abs(x, y);
    theta_offset = theta - 90;
    dx = mid_x + (int)(0.5 * max_radius * cos(D2R(theta_offset)));
    dy = mid_y + (int)(0.5 * max_radius * sin(D2R(theta_offset)));
    R_cont_abs(dx, dy);

}

static void arrow_se(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .8);
    y = D_y + (int)(D_ns * .8);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.6)), ((int)(D_ns * (-.6))));
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (-.4)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.4)), 0);

}

static void arrow_ne(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .8);
    y = D_y + (int)(D_ns * .2);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.6)), ((int)(D_ns * (.6))));
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (.4)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.4)), 0);

}

static void arrow_nw(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .2);
    y = D_y + (int)(D_ns * .2);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.6)), ((int)(D_ns * (.6))));
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (.4)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.4)), 0);

}

static void arrow_sw(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .2);
    y = D_y + (int)(D_ns * .8);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.6)), ((int)(D_ns * (-.6))));
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (-.4)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.4)), 0);

}
static void arrow_e(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .9);
    y = D_y + (int)(D_ns * .5);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.8)), 0);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.3)), (int)(D_ns * (-.3)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.3)), (int)(D_ns * (.3)));

}
static void arrow_w(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .1);
    y = D_y + (int)(D_ns * .5);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.8)), 0);
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.3)), (int)(D_ns * (-.3)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.3)), (int)(D_ns * (.3)));

}
static void arrow_s(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .5);
    y = D_y + (int)(D_ns * .9);
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (-.8)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.3)), (int)(D_ns * (-.3)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.3)), (int)(D_ns * (-.3)));

}
static void arrow_n(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .5);
    y = D_y + (int)(D_ns * .1);
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (.8)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (.3)), (int)(D_ns * (.3)));
    R_move_abs(x, y);
    R_cont_rel((int)(D_ew * (-.3)), (int)(D_ns * (.3)));

}
static void draw_x(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x;
    y = D_y;
    R_move_abs(x, y);
    R_cont_rel((int)D_ew, (int)D_ns);
    y = D_y + (int)D_ns;
    R_move_abs(x, y);
    R_cont_rel((int)D_ew, (int)(D_ns * -1));
}
static void unknown_(void)
{
    extern double D_ew, D_ns;
    extern int D_x, D_y;
    int x, y;

    x = D_x + (int)(D_ew * .3);
    y = D_y + (int)(D_ns * .4);
    R_move_abs(x, y);
    R_cont_rel(0, (int)(D_ns * (-.15)));
    R_cont_rel((int)(D_ew * (.1)), (int)(D_ns * (-.1)));
    R_cont_rel((int)(D_ew * (.2)), 0);
    R_cont_rel((int)(D_ew * (.1)), (int)(D_ns * (.1)));
    R_cont_rel(0, (int)(D_ns * (.2)));
    R_cont_rel((int)(D_ew * (-.1)), (int)(D_ns * (.1)));
    R_cont_rel((int)(D_ew * (-.1)), 0);
    R_cont_rel(0, (int)(D_ns * (.25)));
    R_move_rel(0, (int)(D_ns * (.1)));
    R_cont_rel(0, (int)(D_ns * (.1)));
}
