
/***************************************************************************
 *
 * MODULE:	r.region (commandline)
 * AUTHOR(S):	Glynn Clements
 *		based upon g.region
 * PURPOSE:	Set the boundary definitions for a raster map.
 * 
 * COPYRIGHT:	(C) 2002-2020 by the GRASS Development Team
 *
 *		This program is free software under the GPL (>=v2)
 *		Read the file COPYING that comes with GRASS for details.
 ****************************************************************************
 */

#include <string.h>
#include <stdlib.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/vector.h>
#include <grass/glocale.h>

static int nsew(const char *, const char *, const char *, const char *);
static void die(struct Option *);


int main(int argc, char *argv[])
{
    int i;
    double x;
    struct Cell_head cellhd, window;
    const char *value;
    const char *name;

    struct GModule *module;
    struct
    {
	struct Flag *dflt, *cur, *llauto;
    } flag;
    struct
    {
	struct Option
	    *map,
	    *north, *south, *east, *west,
	    *raster, *vect, *region, *align;
    } parm;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("raster"));
    G_add_keyword(_("metadata"));
    G_add_keyword(_("geometry"));

    module->description =
	_("Sets the boundary definitions for a raster map.");

    /* flags */

    flag.cur = G_define_flag();
    flag.cur->key = 'c';
    flag.cur->description = _("Set from current region");
    flag.cur->guisection = _("Existing");

    flag.dflt = G_define_flag();
    flag.dflt->key = 'd';
    flag.dflt->description = _("Set from default region");
    flag.dflt->guisection = _("Existing");

    flag.llauto = G_define_flag();
    flag.llauto->key = 'a';
    flag.llauto->label = _("Auto-adjustment for lat/lon");
    flag.llauto->description = _("Attempt to fix small precision errors in resolution and extents");
    flag.llauto->guisection = _("Existing");

    /* parameters */

    parm.map = G_define_standard_option(G_OPT_R_MAP);
    parm.map->description = _("Name of raster map to change");

    parm.region = G_define_option();
    parm.region->key = "region";
    parm.region->key_desc = "name";
    parm.region->required = NO;
    parm.region->multiple = NO;
    parm.region->type = TYPE_STRING;
    parm.region->description = _("Set region from named region");
    parm.region->gisprompt = "old,windows,region";
    parm.region->guisection = _("Existing");
    
    parm.raster = G_define_standard_option(G_OPT_R_MAP);
    parm.raster->key = "raster";
    parm.raster->required = NO;
    parm.raster->multiple = NO;
    parm.raster->description = _("Set region to match this raster map");
    parm.raster->guisection = _("Existing");

    parm.vect = G_define_standard_option(G_OPT_V_MAP);
    parm.vect->key = "vector";
    parm.vect->required = NO;
    parm.vect->multiple = NO;
    parm.vect->description = _("Set region to match this vector map");
    parm.vect->guisection = _("Existing");

    parm.north = G_define_option();
    parm.north->key = "n";
    parm.north->key_desc = "value";
    parm.north->required = NO;
    parm.north->multiple = NO;
    parm.north->type = TYPE_STRING;
    parm.north->description = _("Value for the northern edge");
    parm.north->guisection = _("Bounds");

    parm.south = G_define_option();
    parm.south->key = "s";
    parm.south->key_desc = "value";
    parm.south->required = NO;
    parm.south->multiple = NO;
    parm.south->type = TYPE_STRING;
    parm.south->description = _("Value for the southern edge");
    parm.south->guisection = _("Bounds");

    parm.east = G_define_option();
    parm.east->key = "e";
    parm.east->key_desc = "value";
    parm.east->required = NO;
    parm.east->multiple = NO;
    parm.east->type = TYPE_STRING;
    parm.east->description = _("Value for the eastern edge");
    parm.east->guisection = _("Bounds");

    parm.west = G_define_option();
    parm.west->key = "w";
    parm.west->key_desc = "value";
    parm.west->required = NO;
    parm.west->multiple = NO;
    parm.west->type = TYPE_STRING;
    parm.west->description = _("Value for the western edge");
    parm.west->guisection = _("Bounds");
    
    parm.align = G_define_standard_option(G_OPT_R_MAP);
    parm.align->key = "align";
    parm.align->required = NO;
    parm.align->multiple = NO;
    parm.align->description = _("Raster map to align to");
    parm.align->guisection = _("Existing");

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

    G_get_window(&window);

    name = parm.map->answer;

    Rast_get_cellhd(name, G_mapset(), &cellhd);

    window = cellhd;

    if (flag.dflt->answer)
	G_get_default_window(&window);

    if (flag.cur->answer)
	G_get_window(&window);

    if ((name = parm.region->answer))	/* region= */
	G_get_element_window(&window, "windows", name, "");

    if ((name = parm.raster->answer)) {	/* raster= */
	Rast_get_cellhd(name, "", &window);
    }

    if ((name = parm.vect->answer)) {	/* vect= */
	struct Map_info Map;
	struct bound_box box;

	Vect_set_open_level(1);
	if (Vect_open_old(&Map, name, "") != 1)
	    G_fatal_error(_("Unable to open vector map <%s>"), name);

	Vect_get_map_box1(&Map, &box);
	window.north = box.N;
	window.south = box.S;
	window.west = box.W;
	window.east = box.E;

	Rast_align_window(&window, &cellhd);

	Vect_close(&Map);
    }

    if ((value = parm.north->answer)) {	/* n= */
	if ((i = nsew(value, "n+", "n-", "s+"))) {
	    if (!G_scan_resolution(value + 2, &x, window.proj))
		die(parm.north);
	    switch (i) {
	    case 1:
		window.north += x;
		break;
	    case 2:
		window.north -= x;
		break;
	    case 3:
		window.north = window.south + x;
		break;
	    }
	}
	else if (G_scan_northing(value, &x, window.proj))
	    window.north = x;
	else
	    die(parm.north);
    }

    if ((value = parm.south->answer)) {	/* s= */
	if ((i = nsew(value, "s+", "s-", "n-"))) {
	    if (!G_scan_resolution(value + 2, &x, window.proj))
		die(parm.south);
	    switch (i) {
	    case 1:
		window.south += x;
		break;
	    case 2:
		window.south -= x;
		break;
	    case 3:
		window.south = window.north - x;
		break;
	    }
	}
	else if (G_scan_northing(value, &x, window.proj))
	    window.south = x;
	else
	    die(parm.south);
    }

    if ((value = parm.east->answer)) {	/* e= */
	if ((i = nsew(value, "e+", "e-", "w+"))) {
	    if (!G_scan_resolution(value + 2, &x, window.proj))
		die(parm.east);
	    switch (i) {
	    case 1:
		window.east += x;
		break;
	    case 2:
		window.east -= x;
		break;
	    case 3:
		window.east = window.west + x;
		break;
	    }
	}
	else if (G_scan_easting(value, &x, window.proj))
	    window.east = x;
	else
	    die(parm.east);
    }

    if ((value = parm.west->answer)) {	/* w= */
	if ((i = nsew(value, "w+", "w-", "e-"))) {
	    if (!G_scan_resolution(value + 2, &x, window.proj))
		die(parm.west);
	    switch (i) {
	    case 1:
		window.west += x;
		break;
	    case 2:
		window.west -= x;
		break;
	    case 3:
		window.west = window.east - x;
		break;
	    }
	}
	else if (G_scan_easting(value, &x, window.proj))
	    window.west = x;
	else
	    die(parm.west);
    }

    if ((name = parm.align->answer)) {	/* align= */
	struct Cell_head temp_window;

	Rast_get_cellhd(name, "", &temp_window);

	Rast_align_window(&window, &temp_window);
    }

    window.rows = cellhd.rows;
    window.cols = cellhd.cols;

    G_adjust_Cell_head(&window, 1, 1);

    if (flag.llauto->answer && cellhd.proj == PROJECTION_LL) {
	G_adjust_window_ll(&window);
    }

    cellhd.north = window.north;
    cellhd.south = window.south;
    cellhd.east = window.east;
    cellhd.west = window.west;
    cellhd.ns_res = window.ns_res;
    cellhd.ew_res = window.ew_res;

    Rast_put_cellhd(parm.map->answer, &cellhd);

    G_done_msg(" ");

    return 0;
}

static void die(struct Option *parm)
{
    G_fatal_error("<%s=%s> ** illegal value **", parm->key, parm->answer);
}

static int nsew(const char *value, const char *a, const char *b,
		const char *c)
{
    if (strncmp(value, a, strlen(a)) == 0)
	return 1;
    if (strncmp(value, b, strlen(b)) == 0)
	return 2;
    if (strncmp(value, c, strlen(c)) == 0)
	return 3;
    return 0;
}

