
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                     Tool Library                      //
//                   CreateGridSystem                    //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                  CreateGridSystem.cpp                 //
//                                                       //
//                 Copyright (C) 2007 by                 //
//                    Volker Wichmann                    //
//                                                       //
//-------------------------------------------------------//
//                                                       //
// This file is part of 'SAGA - System for Automated     //
// Geoscientific Analyses'. SAGA is free software; you   //
// can redistribute it and/or modify it under the terms  //
// of the GNU General Public License as published by the //
// Free Software Foundation, either version 2 of the     //
// License, or (at your option) any later version.       //
//                                                       //
// SAGA is distributed in the hope that it will be       //
// useful, but WITHOUT ANY WARRANTY; without even the    //
// implied warranty of MERCHANTABILITY or FITNESS FOR A  //
// PARTICULAR PURPOSE. See the GNU General Public        //
// License for more details.                             //
//                                                       //
// You should have received a copy of the GNU General    //
// Public License along with this program; if not, see   //
// <http://www.gnu.org/licenses/>.                       //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//    e-mail:     reklovw@web.de                         //
//                                                       //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "CreateGridSystem.h"


///////////////////////////////////////////////////////////
//                                                       //
//                                                       //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
CCreateGridSystem::CCreateGridSystem(void)
{
	Set_Name		(_TL("Create Grid System"));

	Set_Author		("Volker Wichmann (c) 2007");
	
	Set_Description(_TW(
		"This tool creates a new user specified Grid System for use with other tools.\n\n"
		"First of all, please consider the following issues before using the tool:\n"
		"(a) all calculations of the tool refer to the lower left corner of the grid system, i.e. "
		"the xMin and yMin values. This coordinate is fixed unless you specify an offset.\n"
		"(b) the tool follows the philosophy of SAGA in that the values describing the extent refer to the "
		"cell centers. If you like to match the extent with the border of a grid, use an offset.\n\n"
		"The tool provides four possibilities to set/determine the extent of the grid system:\n"
		"(1) by specifying the coordinate of the lower left cell (xMin, yMin) and the number of cells in W-E (NX) and S-N (NY) direction\n"
		"(2) by specifying the coordinates the of lower left (xMin, yMin) and the upper right (xMax, yMax) cell\n"
		"(3) by the extent of the shape(s) provided in the Data Objects section\n"
		"(4) by the extent of the grid(s) provided in the Data Objects section\n\n"
		"After selecting the appropriate method to determine the extent, the next step is to specify the "
		"Cellsize of the new grid system.\n"
		"For all methods supplied to determine the extent but number (1), three possibilities are provided to "
		"adjust Cellsize and grid system extent (please remember, the lower left corner is fixed!):\n"
		"(I) adjust the extent to match the Cellsize\n"
		"(II) adjust the Cellsize to match the extent in E-W direction\n"
		"(III) adjust the Cellsize to match the extent in S-N direction\n\n"
		"Finally it is possible to apply an offset to the lower left corner of the grid system. "
		"In this case check the Use Offset option and specify the offset in W-E and S-N direction. Positive values "
		"result in a shift in E/N, negative in W/S direction.\n"
		"In order to create the grid system the tool needs to create a dummy grid."
	));

	//-----------------------------------------------------
	Parameters.Add_Grid_Output("",
		"GRID"    , _TL("Grid"),
		_TL("")
	);

	Parameters.Add_Data_Type("",
		"DATATYPE", _TL("Data Type"),
		_TL(""),
		SG_DATATYPES_Bit|SG_DATATYPES_Numeric
	);

	Parameters.Add_Double("DATATYPE",
		"INIT"    , _TL("Initialization Value"),
		_TL("Value which is assigned to the dummy grid.")
	);

	Parameters.Add_Double("",
		"CELLSIZE", _TL("Cellsize"),
		_TL(""),
		10., 0., true
	);

	Parameters.Add_Choice("",
		"M_EXTENT", _TL("Extent Definition"),
		_TL(""),
		CSG_String::Format("%s|%s|%s|%s",
			_TL("lower left coordinate and number of rows and columns"),
			_TL("lower left and upper right coordinates"),
			_TL("one or more shapes layers"),
			_TL("one or more grids")
		), 0
	);

	Parameters.Add_Choice("",
		"ADJUST"  , _TL("Adjust"),
		_TL(""),
		CSG_String::Format("%s|%s|%s|",
			_TL("extent to cell size"),
			_TL("cell size to left-right extent"),
			_TL("cell size to bottom-top extent")
		), 0
	);

	Parameters.Add_Bool  ("ADJUST",
		"ROUND"   , _TL("Round Coordinates"),
		_TL("Rounding of extent coordinates to multiples of cellsize."),
		false
	);

	//-----------------------------------------------------
	Parameters.Add_Node  (""     , "XNODE", _TL("Left-Right"), _TL(""));
	Parameters.Add_Double("XNODE", "XMIN" , _TL("Left"      ), _TL(""),   0.);
	Parameters.Add_Double("XNODE", "XMAX" , _TL("Right"     ), _TL(""), 100.);
	Parameters.Add_Int   ("XNODE", "NX"   , _TL("Columns"   ), _TL(""), 10, 1, true);

	Parameters.Add_Node  (""     , "YNODE", _TL("Bottom-Top"), _TL(""));
	Parameters.Add_Double("YNODE", "YMIN" , _TL("Bottom"    ), _TL(""),   0.);
	Parameters.Add_Double("YNODE", "YMAX" , _TL("Top"       ), _TL(""), 100.);
	Parameters.Add_Int   ("YNODE", "NY"   , _TL("Rows"      ), _TL(""), 10, 1, true);

	//-----------------------------------------------------
	Parameters.Add_Shapes_List("", "SHAPESLIST", _TL("Layers"), _TL(""), PARAMETER_INPUT);
	Parameters.Add_Grid_List  ("", "GRIDLIST"  , _TL("Layers"), _TL(""), PARAMETER_INPUT);
	Parameters.Add_Double     ("", "BUFFER"    , _TL("Buffer"), _TL(""), 0.);

	//-----------------------------------------------------
	Parameters.Add_Bool  (""      , "USEOFF" , _TL("Use Offset"), _TL(""), false);
	Parameters.Add_Double("USEOFF", "XOFFSET", _TL("X Offset"  ), _TL("Positive values result in a shift in E direction."));
	Parameters.Add_Double("USEOFF", "YOFFSET", _TL("Y Offset"  ), _TL("Positive values result in a shift in N direction."));
}


///////////////////////////////////////////////////////////
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
int CCreateGridSystem::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	return( CSG_Tool::On_Parameter_Changed(pParameters, pParameter) );
}

//---------------------------------------------------------
int CCreateGridSystem::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if( pParameter->Cmp_Identifier("M_EXTENT") )
	{
		pParameters->Set_Enabled("NX"        , pParameter->asInt() == 0);
		pParameters->Set_Enabled("NY"        , pParameter->asInt() == 0);
		pParameters->Set_Enabled("XMAX"      , pParameter->asInt() == 1);
		pParameters->Set_Enabled("YMAX"      , pParameter->asInt() == 1);
		pParameters->Set_Enabled("XMIN"      , pParameter->asInt() <= 1);
		pParameters->Set_Enabled("YMIN"      , pParameter->asInt() <= 1);
		pParameters->Set_Enabled("XNODE"     , pParameter->asInt() <= 1);
		pParameters->Set_Enabled("YNODE"     , pParameter->asInt() <= 1);
		pParameters->Set_Enabled("ADJUST"    , pParameter->asInt() >= 1);
		pParameters->Set_Enabled("SHAPESLIST", pParameter->asInt() == 2);
		pParameters->Set_Enabled("GRIDLIST"  , pParameter->asInt() == 3);
		pParameters->Set_Enabled("BUFFER"    , pParameter->asInt() >= 2);
	}

	if( pParameter->Cmp_Identifier("ADJUST") )
	{
		pParameters->Set_Enabled("ROUND"     , pParameter->asInt() == 0); // extent to cell size
	}

	if( pParameter->Cmp_Identifier("USEOFF") )
	{
		pParameters->Set_Enabled("XOFFSET"   , pParameter->asBool());
		pParameters->Set_Enabled("YOFFSET"   , pParameter->asBool());
	}

	return( CSG_Tool::On_Parameters_Enable(pParameters, pParameter) );
}


///////////////////////////////////////////////////////////
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CCreateGridSystem::On_Execute(void)
{
	CSG_Grid_System System;

	switch( Parameters("M_EXTENT")->asInt() )
	{
	//-----------------------------------------------------
	default: // lower left coordinate and number of rows and columns
		{
			System.Assign(Parameters("CELLSIZE")->asDouble(),
				Parameters("XMIN")->asDouble(), Parameters("YMIN")->asDouble(),
				Parameters("NX"  )->asInt   (), Parameters("NY"  )->asInt   ()
			);
		}
		break;

	//-----------------------------------------------------
	case  1: // lower left and upper right coordinates
		{
			CSG_Rect Extent(
				Parameters("XMIN")->asDouble(), Parameters("YMIN")->asDouble(),
				Parameters("XMAX")->asDouble(), Parameters("YMAX")->asDouble()
			);

			System = Get_Adjusted(Parameters("CELLSIZE")->asDouble(), Extent);
		}
		break;

	//-----------------------------------------------------
	case  2: // one or more shapes layers
		{
			CSG_Parameter_Shapes_List *pList = Parameters("SHAPESLIST")->asShapesList();

			if( pList->Get_Item_Count() > 0 )
			{
				CSG_Rect Extent(pList->Get_Shapes(0)->Get_Extent());

				for(int i=1; i<pList->Get_Item_Count(); i++)
				{
					Extent.Union(pList->Get_Shapes(i)->Get_Extent());
				}

				if( !Get_Buffered(Parameters["BUFFER"].asDouble(), Extent) )
				{
					return( false );
				}

				System = Get_Adjusted(Parameters("CELLSIZE")->asDouble(), Extent);
			}
		}
		break;

	//-----------------------------------------------------
	case  3: // one or more grids
		{
			CSG_Parameter_Grid_List *pList = Parameters("GRIDLIST")->asGridList();

			if( pList->Get_Grid_Count() > 0 )
			{
				CSG_Rect Extent(pList->Get_Grid(0)->Get_Extent());

				for(int i=1; i<pList->Get_Grid_Count(); i++)
				{
					Extent.Union(pList->Get_Grid(i)->Get_Extent());
				}

				if( !Get_Buffered(Parameters["BUFFER"].asDouble(), Extent) )
				{
					return( false );
				}

				System = Get_Adjusted(Parameters("CELLSIZE")->asDouble(), Extent);
			}
		}
		break;
	}

	//-----------------------------------------------------
	if( !System.is_Valid() )
	{
		Error_Set(_TL("invalid grid system"));

		return( false );
	}

	//-----------------------------------------------------
	if( Parameters("USEOFF")->asBool() )
	{
		CSG_Rect Extent = System.Get_Extent();

		Extent.Move(
			Parameters("XOFFSET")->asDouble(),
			Parameters("YOFFSET")->asDouble()
		);

		System.Assign(System.Get_Cellsize(), Extent.Get_XMin(), Extent.Get_YMin(), System.Get_NX(), System.Get_NY());
	}
		
	//-----------------------------------------------------
	CSG_Grid *pGrid = Parameters["GRID"].asGrid(); if( !pGrid ) { Parameters.Set_Parameter("GRID", pGrid = SG_Create_Grid()); }

	if( pGrid->Create(System, Parameters("DATATYPE")->asDataType()->Get_Data_Type()) )
	{
		pGrid->Fmt_Name("%s %s", _TL("Grid System"), System.asString());
		pGrid->Assign(Parameters("INIT")->asDouble());

		return( true );
	}

	return( false );
}


///////////////////////////////////////////////////////////
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
bool CCreateGridSystem::Get_Buffered(double Buffer, CSG_Rect &Extent)
{
	if( Buffer >= 0. || (Buffer < 0. && -Buffer < .5 * Extent.Get_XRange() && -Buffer < .5 * Extent.Get_YRange()) )
	{
		Extent.Inflate(Buffer, false);

		return( true );
	}

	Error_Fmt("%s\n %s = %f\n x-%s = %f\n y-%s = %f", _TL("Negative buffer does not fit into target extent!"),
		_TL("buffer"), -Buffer, _TL("extent"), .5 * Extent.Get_XRange(), _TL("extent"), .5 * Extent.Get_YRange()
	);

	return( false );
}

//---------------------------------------------------------
CSG_Grid_System CCreateGridSystem::Get_Adjusted(double Cellsize, CSG_Rect &Extent)
{
	CSG_Grid_System System;

	if( Cellsize > 0. )
	{
		double xRange = Extent.xMax - Extent.xMin;
		double yRange = Extent.yMax - Extent.yMin;
		double n;

		switch( Parameters("ADJUST")->asInt() )
		{
		case 0:	// extent to cell size
			if( modf(xRange / Cellsize, &n) != 0. )
			{
				Extent.xMax	= Extent.xMin + Cellsize * floor(0.5 + xRange / Cellsize);
			}
			
			if( modf(yRange / Cellsize, &n) != 0. )
			{
				Extent.yMax = Extent.yMin + Cellsize * floor(0.5 + yRange / Cellsize);
			}

			if( Parameters["ROUND"].asBool() )
			{
				Extent.xMin = floor(Extent.xMin / Cellsize) * Cellsize; Extent.xMax = ceil(Extent.xMax / Cellsize) * Cellsize;
				Extent.yMin = floor(Extent.yMin / Cellsize) * Cellsize; Extent.yMax = ceil(Extent.yMax / Cellsize) * Cellsize;
			}
			break;

		case 1:	// cell size to left-right extent
			if( modf(xRange / Cellsize, &n) != 0. )
			{
				Cellsize	= xRange / floor(xRange / Cellsize);
			}

			if( modf(yRange / Cellsize, &n) != 0. )
			{
				Extent.yMax = Extent.yMin + Cellsize * floor(0.5 + yRange / Cellsize);
			}
			break;

		case 2:	// cell size to bottom-top extent
			if( modf(yRange / Cellsize, &n) != 0. )
			{
				Cellsize	= yRange / floor(yRange / Cellsize);
			}

			if( modf(xRange / Cellsize, &n) != 0. )
			{
				Extent.xMax = Extent.xMin + Cellsize * floor(0.5 + xRange / Cellsize);
			}
			break;
		}

		System.Assign(Cellsize, Extent);
	}

	return( System );
}


///////////////////////////////////////////////////////////
//                                                       //
//                                                       //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
