
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                     Tool Library                      //
//                     Grid_Gridding                     //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                    Shapes2Grid.cpp                    //
//                                                       //
//                 Copyright (C) 2003 by                 //
//                      Olaf Conrad                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
// 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:     oconrad@saga-gis.org                   //
//                                                       //
//    contact:    Olaf Conrad                            //
//                Institute of Geography                 //
//                University of Goettingen               //
//                Germany                                //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "Shapes2Grid.h"

//---------------------------------------------------------
#define X_WORLD_TO_GRID(X)	(((X) - m_pGrid->Get_XMin()) / m_pGrid->Get_Cellsize())
#define Y_WORLD_TO_GRID(Y)	(((Y) - m_pGrid->Get_YMin()) / m_pGrid->Get_Cellsize())

//---------------------------------------------------------
#define OUTPUT_NODATA	-2
#define OUTPUT_INDEX	-1


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

//---------------------------------------------------------
CShapes2Grid::CShapes2Grid(void)
{
	Set_Name		(_TL("Shapes to Grid"));

	Set_Author		("O.Conrad (c) 2003");

	Set_Description	(_TW(
		"Gridding of a shapes layer. If some shapes are selected, only these will be gridded."
	));

	//-----------------------------------------------------
	Parameters.Add_Shapes("",
		"INPUT"    , _TL("Shapes"),
		_TL(""),
		PARAMETER_INPUT
	);

	Parameters.Add_Table_Field("INPUT",
		"FIELD"    , _TL("Attribute"),
		_TL("")
	);

	Parameters.Add_Choice("",
		"OUTPUT"   , _TL("Output Values"),
		_TL(""),
		CSG_String::Format("%s|%s|%s",
			_TL("data / no-data"),
			_TL("index number"),
			_TL("attribute")
		), 2
	);

	Parameters.Add_Choice("",
		"MULTIPLE" , _TL("Method for Multiple Values"),
		_TL(""),
		CSG_String::Format("%s|%s|%s|%s|%s",
			_TL("first"),
			_TL("last"),
			_TL("minimum"),
			_TL("maximum"),
			_TL("mean")
		), 1
	);

	Parameters.Add_Choice("",
		"LINE_TYPE", _TL("Lines"),
		_TL(""),
		CSG_String::Format("%s|%s",
			_TL("thin"),
			_TL("thick")
		), 1
	);

	Parameters.Add_Choice("",
		"POLY_TYPE", _TL("Polygon"),
		_TL(""),
		CSG_String::Format("%s|%s",
			_TL("node"),
			_TL("cell")
		), 1
	);

	Parameters.Add_Data_Type("",
		"GRID_TYPE"	, _TL("Data Type"),
		_TL(""),
		SG_DATATYPES_Numeric|SG_DATATYPES_Bit, SG_DATATYPE_Undefined, _TL("same as attribute")
	);

	//-----------------------------------------------------
	m_Grid_Target.Create(&Parameters, false, "", "TARGET_");

	m_Grid_Target.Add_Grid("GRID" , _TL("Grid"            ), false);
	m_Grid_Target.Add_Grid("COUNT", _TL("Number of Values"),  true);
}


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

//---------------------------------------------------------
int CShapes2Grid::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if(	pParameter->Cmp_Identifier("INPUT") )
	{
		m_Grid_Target.Set_User_Defined(pParameters, pParameter->asShapes());
	}

	m_Grid_Target.On_Parameter_Changed(pParameters, pParameter);

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

//---------------------------------------------------------
int CShapes2Grid::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if(	pParameter->Cmp_Identifier("INPUT") )
	{
		pParameters->Set_Enabled("LINE_TYPE", pParameter->asShapes() && pParameter->asShapes()->Get_Type() == SHAPE_TYPE_Line);
		pParameters->Set_Enabled("POLY_TYPE", pParameter->asShapes() && pParameter->asShapes()->Get_Type() == SHAPE_TYPE_Polygon);
	}

	if(	pParameter->Cmp_Identifier("OUTPUT") )
	{
		pParameters->Set_Enabled("FIELD"    , pParameter->asInt() == 2);
		pParameters->Set_Enabled("MULTIPLE" , pParameter->asInt() != 0);
		pParameters->Set_Enabled("GRID_TYPE", pParameter->asInt() == 2);
	}

	m_Grid_Target.On_Parameters_Enable(pParameters, pParameter);

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


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

//---------------------------------------------------------
TSG_Data_Type CShapes2Grid::Get_Data_Type(int Field)
{
	CSG_Shapes *pShapes = Parameters("INPUT")->asShapes();

	if( Field >= 0 && (Field >= pShapes->Get_Field_Count() || !SG_Data_Type_is_Numeric(pShapes->Get_Field_Type(Field))) )
	{
		Field = OUTPUT_INDEX;    // index number
	}

	if( Field == OUTPUT_NODATA ) // data / no-data
	{
		return( SG_DATATYPE_Byte );
	}

	if( Field <= OUTPUT_INDEX )  // index number
	{
		return( pShapes->Get_Count() < 65535 ? SG_DATATYPE_Word : SG_DATATYPE_DWord );
	}

//	if( Field >= 0 )             // attribute
	{
		return( Parameters("GRID_TYPE")->asDataType()->Get_Data_Type(pShapes->Get_Field_Type(Field)) );
	}
}


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

//---------------------------------------------------------
bool CShapes2Grid::On_Execute(void)
{
	CSG_Shapes *pShapes = Parameters("INPUT")->asShapes();

	m_Multiple = Parameters("MULTIPLE")->asInt();

	//-----------------------------------------------------
	bool bFat;

	switch( pShapes->Get_Type() )
	{
	default                : bFat = false;                                 break;
	case SHAPE_TYPE_Line   : bFat = Parameters("LINE_TYPE")->asInt() == 1; break;
	case SHAPE_TYPE_Polygon: bFat = Parameters("POLY_TYPE")->asInt() == 1; break;
	}

	//-----------------------------------------------------
	int Field; CSG_String Name(pShapes->Get_Name());

	switch( Parameters("OUTPUT")->asInt() )
	{
	case  0: // data / no-data
		Field = OUTPUT_NODATA;
		break;

	case  1: // index number
		Field = OUTPUT_INDEX;
		Name += " [ID]";
		break;

	default: // attribute
		Field = Parameters("FIELD")->asInt();

		if( Field < 0 )
		{
			Error_Set(_TL("Field needs to be specified!"));

			return( false );
		}

		if( !SG_Data_Type_is_Numeric(pShapes->Get_Field_Type(Field)) )
		{
			Message_Add(_TL("WARNING: selected attribute is not numeric."));
		}

		Name += CSG_String::Format(" [%s, %s]", pShapes->Get_Field_Name(Field), Parameters("MULTIPLE")->asString());
		break;
	}

	//-----------------------------------------------------
	if( (m_pGrid = m_Grid_Target.Get_Grid("GRID", Get_Data_Type(Field))) == NULL )
	{
		Error_Set(_TL("Failed to request target grid."));

		return( false );
	}

	if( !pShapes->Get_Extent().Intersects(m_pGrid->Get_Extent()) )
	{
		Error_Set(_TL("Polygons' and target grid's extent do not intersect."));

		return( false );
	}

	if( Field < 0 )
	{
		m_pGrid->Set_NoData_Value(0.);
	}

	m_pGrid->Set_Name(Name);
	m_pGrid->Assign_NoData();

	//-------------------------------------------------
	CSG_Grid Count;

	m_pCount = m_Grid_Target.Get_Grid("COUNT", pShapes->Get_Count() < 256 ? SG_DATATYPE_Byte : SG_DATATYPE_Word);

	if( m_pCount == NULL )
	{
		Count.Create(m_pGrid->Get_System(), SG_DATATYPE_Word);

		m_pCount = &Count;
	}

	m_pCount->Fmt_Name("%s [%s]", pShapes->Get_Name(), _TL("Count"));
	m_pCount->Set_NoData_Value(0.);
	m_pCount->Assign(0.);

	//-----------------------------------------------------
	for(sLong i=0; i<pShapes->Get_Count() && Set_Progress(i, pShapes->Get_Count()); i++)
	{
		CSG_Shape *pShape = pShapes->Get_Shape(i);

		m_Cells_On_Shape.clear();

		if( pShapes->Get_Selection_Count() <= 0 || pShape->is_Selected() )
		{
			if( Field < 0 || !pShape->is_NoData(Field) )
			{
				if( pShape->Intersects(m_pGrid->Get_Extent(true)) )
				{
					double Value = Field >= 0 ? pShape->asDouble(Field) : Field == OUTPUT_INDEX ? i + 1 : 1;

					switch( pShapes->Get_Type() )
					{
					case SHAPE_TYPE_Point  :
					case SHAPE_TYPE_Points : Set_Points (pShape      , Value); break;
					case SHAPE_TYPE_Line   : Set_Line   (pShape, bFat, Value); break;
					case SHAPE_TYPE_Polygon: Set_Polygon(pShape, bFat, Value); break;
					}
				}
			}
		}
	}

	//-----------------------------------------------------
	if( m_Multiple == 4 ) // mean
	{
		#pragma omp parallel for
		for(sLong i=0; i<m_pGrid->Get_NCells(); i++)
		{
			if( m_pCount->asInt(i) > 1 )
			{
				m_pGrid->Mul_Value(i, 1. / m_pCount->asDouble(i));
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}


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

//---------------------------------------------------------
inline void CShapes2Grid::Set_Value(int x, int y, double Value, bool bCheckDuplicates)
{
	if( m_pGrid->is_InGrid(x, y, false) )
	{
		if( bCheckDuplicates )
		{
			sLong n = (sLong)y * m_pGrid->Get_NX() + x;

			if( !m_Cells_On_Shape.insert(n).second )
			{
				return; // this cell has already been rendered for this shape
			}
		}

		if( m_pCount->asInt(x, y) == 0 )
		{
			m_pGrid->Set_Value(x, y, Value);
		}
		else switch( m_Multiple )
		{
		default : // first
			break;

		case  1: // last
			m_pGrid->Set_Value(x, y, Value);
			break;

		case  2: // minimum
			if( m_pGrid->asDouble(x, y) > Value )
			{
				m_pGrid->Set_Value(x, y, Value);
			}
			break;

		case  3: // maximum
			if( m_pGrid->asDouble(x, y) < Value )
			{
				m_pGrid->Set_Value(x, y, Value);
			}
			break;

		case  4: // mean
			m_pGrid->Add_Value(x, y, Value);
			break;
		}

		m_pCount->Add_Value(x, y, 1);
	}
}


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

//---------------------------------------------------------
void CShapes2Grid::Set_Points(CSG_Shape *pShape, double Value)
{
	for(int iPart=0; iPart<pShape->Get_Part_Count(); iPart++)
	{
		for(int iPoint=0; iPoint<pShape->Get_Point_Count(iPart); iPoint++)
		{
			TSG_Point p = pShape->Get_Point(iPoint, iPart);

			Set_Value(
				m_pGrid->Get_System().Get_xWorld_to_Grid(p.x),
				m_pGrid->Get_System().Get_yWorld_to_Grid(p.y), Value, false
			);
		}
	}
}


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

//---------------------------------------------------------
void CShapes2Grid::Set_Line(CSG_Shape *pShape, bool bFat, double Value)
{
	for(int iPart=0; iPart<pShape->Get_Part_Count(); iPart++)
	{
		if( !((CSG_Shape_Line *)pShape)->Get_Part(iPart)->Get_Extent().Intersects(m_pGrid->Get_Extent()) )
		{
			continue;
		}

		int iPoint = pShape->Get_Type() == SHAPE_TYPE_Polygon ? 0 : 1;

		TSG_Point A = pShape->Get_Point(0, iPart, iPoint != 0);

		A.x = X_WORLD_TO_GRID(A.x);
		A.y = Y_WORLD_TO_GRID(A.y);

		for( ; iPoint<pShape->Get_Point_Count(iPart); iPoint++)
		{
			TSG_Point B = A; A = pShape->Get_Point(iPoint, iPart);

			A.x	= X_WORLD_TO_GRID(A.x);
			A.y	= Y_WORLD_TO_GRID(A.y);

			if( bFat )
			{
				Set_Line_Fat(A, B, Value);
			}
			else
			{
				Set_Line_Thin(A, B, Value);
			}
		}
	}
}

//---------------------------------------------------------
void CShapes2Grid::Set_Line_Thin(TSG_Point a, TSG_Point b, double Value)
{
	TSG_Point_Int A; A.x = (int)(a.x += 0.5); A.y = (int)(a.y += 0.5);
	TSG_Point_Int B; B.x = (int)(b.x += 0.5); B.y = (int)(b.y += 0.5);

	//-----------------------------------------------------
	if( A.x != B.x || A.y != B.y )
	{
		double dx = b.x - a.x;
		double dy = b.y - a.y;

		if( fabs(dx) > fabs(dy) )
		{
			int sig = dx < 0 ? -1 : 1; dx = fabs(dx); dy /= dx;

			for(int ix=0; ix<=dx; ix++, a.x+=sig, a.y+=dy)
			{
				Set_Value((int)a.x, (int)a.y, Value);
			}
		}
		else if( fabs(dy) >= fabs(dx) && dy != 0 )
		{
			int	sig	= dy < 0 ? -1 : 1; dy = fabs(dy); dx /= dy;

			for(int iy=0; iy<=dy; iy++, a.x+=dx, a.y+=sig)
			{
				Set_Value((int)a.x, (int)a.y, Value);
			}
		}
	}
	else
	{
		Set_Value(A.x, A.y, Value);
	}
}

//---------------------------------------------------------
void CShapes2Grid::Set_Line_Fat(TSG_Point a, TSG_Point b, double Value)
{
	TSG_Point_Int A; A.x = (int)(a.x += 0.5); A.y = (int)(a.y += 0.5);
	TSG_Point_Int B; B.x = (int)(b.x += 0.5); B.y = (int)(b.y += 0.5);

	Set_Value(A.x, A.y, Value);

	//-----------------------------------------------------
	if( A.x != B.x || A.y != B.y )
	{
		double dx = b.x - a.x;
		double dy = b.y - a.y;

		a.x = a.x > 0. ? a.x - (int)a.x : 1. + (a.x - (int)a.x);
		a.y = a.y > 0. ? a.y - (int)a.y : 1. + (a.y - (int)a.y);

		//-------------------------------------------------
		if( fabs(dx) > fabs(dy) )
		{
			int ix = dx > 0. ? 1 : -1;
			int iy = dy > 0. ? 1 : -1;

			double d, e;

			d  = fabs(dy / dx);
			dx = ix < 0 ? a.x : 1. - a.x;
			e  = iy > 0 ? a.y : 1. - a.y;
			e += d * dx;

			while( e > 1. )
			{
				Set_Value(A.x, A.y += iy, Value); e--;
			}

			while( A.x != B.x )
			{
				Set_Value(A.x += ix, A.y, Value); e += d;

				if( A.x != B.x )
				{
					while( e > 1. )
					{
						Set_Value(A.x, A.y += iy, Value); e--;
					}
				}
			}

			if( A.y != B.y )
			{
				iy = A.y < B.y ? 1 : -1;

				while( A.y != B.y )
				{
					Set_Value(A.x, A.y += iy, Value);
				}
			}
		}

		//-------------------------------------------------
		else // if( fabs(dy) > fabs(dx) )
		{
			int ix = dx > 0. ? 1 : -1;
			int iy = dy > 0. ? 1 : -1;

			double d, e;

			d  = fabs(dx / dy);
			dy = iy < 0 ? a.y : 1. - a.y;
			e  = ix > 0 ? a.x : 1. - a.x;
			e += d * dy;

			while( e > 1. )
			{
				Set_Value(A.x += ix, A.y, Value); e--;
			}

			while( A.y != B.y )
			{
				Set_Value(A.x, A.y += iy, Value); e += d;

				if( A.y != B.y )
				{
					while( e > 1. )
					{
						Set_Value(A.x += ix, A.y, Value); e--;
					}
				}
			}

			if( A.x != B.x )
			{
				ix = A.x < B.x ? 1 : -1;

				while( A.x != B.x )
				{
					Set_Value(A.x += ix, A.y, Value);
				}
			}
		}
	}
}


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

//---------------------------------------------------------
void CShapes2Grid::Set_Polygon(CSG_Shape *pShape, bool bFat, double Value)
{
	Set_Polygon((CSG_Shape_Polygon *)pShape, Value);

	if( bFat )	// all cells intersected have to be marked
	{
		Set_Line(pShape, true, Value);	// thick, each cell crossed by polygon boundary will be marked additionally
	}
}

//---------------------------------------------------------
void CShapes2Grid::Set_Polygon(CSG_Shape_Polygon *pPolygon, double Value)
{
	bool *bCrossing = (bool *)SG_Malloc(m_pGrid->Get_NX() * sizeof(bool));

	CSG_Rect Extent(pPolygon->Get_Extent());

	int xStart = (int)((Extent.Get_XMin() - m_pGrid->Get_XMin()) / m_pGrid->Get_Cellsize()) - 1; if( xStart < 0                 ) { xStart = 0;                     }
	int xStop  = (int)((Extent.Get_XMax() - m_pGrid->Get_XMin()) / m_pGrid->Get_Cellsize()) + 1; if( xStop >= m_pGrid->Get_NX() ) { xStop  = m_pGrid->Get_NX() - 1; }

	TSG_Point A, B;

	A.y = m_pGrid->Get_YMin();
	A.x = m_pGrid->Get_XMin() - 1.;
	B.x = m_pGrid->Get_XMax() + 1.;

	//-----------------------------------------------------
	for(int y=0; y<m_pGrid->Get_NY(); y++, A.y+=m_pGrid->Get_Cellsize())
	{
		if( A.y >= Extent.yMin && A.y <= Extent.yMax )
		{
			B.y = A.y;

			memset(bCrossing, 0, m_pGrid->Get_NX() * sizeof(bool));

			for(int iPart=0; iPart<pPolygon->Get_Part_Count(); iPart++)
			{
				if( pPolygon->Get_Part(iPart)->Get_Extent().Intersects(m_pGrid->Get_Extent(true)) )
				{
					TSG_Point b = pPolygon->Get_Point(pPolygon->Get_Point_Count(iPart) - 1, iPart);

					for(int iPoint=0; iPoint<pPolygon->Get_Point_Count(iPart); iPoint++)
					{
						TSG_Point c, a = b; b = pPolygon->Get_Point(iPoint, iPart);

						if(	((a.y <= A.y && A.y  < b.y)
						||	 (a.y  > A.y && A.y >= b.y)) )
						{
							SG_Get_Crossing(c, a, b, A, B, false);

							int x = (int)(1. + X_WORLD_TO_GRID(c.x));

							if( x < 0 )
							{
								x = 0;
							}
							else if( x >= m_pGrid->Get_NX() )
							{
								continue;
							}

							bCrossing[x] = !bCrossing[x];
						}
					}
				}
			}

			//---------------------------------------------
			bool bFill = false;

			for(int x=xStart; x<=xStop; x++)
			{
				if( bCrossing[x] )
				{
					bFill = !bFill;
				}

				if( bFill )
				{
					Set_Value(x, y, Value);
				}
			}
		}
	}

	//-----------------------------------------------------
	SG_Free(bCrossing);
}


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

//---------------------------------------------------------
CPolygons2Grid::CPolygons2Grid(void)
{
	Set_Name		(_TL("Polygons to Grid"));

	Set_Author		("O.Conrad (c) 2018");

	Set_Description	(_TW(
		"Gridding of polygons. If any polygons are selected, only these will be gridded."
	));

	//-----------------------------------------------------
	Parameters.Add_Shapes("",
		"POLYGONS"	, _TL("Polygons"),
		_TL(""),
		PARAMETER_INPUT, SHAPE_TYPE_Polygon
	);

	Parameters.Add_Table_Field("POLYGONS",
		"FIELD"		, _TL("Attribute"),
		_TL("")
	);

	Parameters.Add_Choice("",
		"OUTPUT"	, _TL("Output Values"),
		_TL(""),
		CSG_String::Format("%s|%s",
			_TL("index number"),
			_TL("attribute")
		), 2
	);

	Parameters.Add_Choice("",
		"MULTIPLE"	, _TL("Multiple Polygons"),
		_TL("Output value for cells that intersect with more than one polygon."),
		CSG_String::Format("%s|%s|%s",
			_TL("minimum coverage"),
			_TL("maximum coverage"),
			_TL("average proportional to area coverage")
		), 1
	);

	Parameters.Add_Data_Type("",
		"GRID_TYPE"	, _TL("Data Type"),
		_TL(""),
		SG_DATATYPES_Numeric|SG_DATATYPES_Bit, SG_DATATYPE_Undefined, _TL("same as attribute")
	);

	//-----------------------------------------------------
	m_Grid_Target.Create(&Parameters, false, "", "TARGET_");

	m_Grid_Target.Add_Grid("GRID"    , _TL("Grid"    ), false);
	m_Grid_Target.Add_Grid("COVERAGE", _TL("Coverage"),  true);
}


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

//---------------------------------------------------------
int CPolygons2Grid::On_Parameter_Changed(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if(	pParameter->Cmp_Identifier("POLYGONS") )
	{
		m_Grid_Target.Set_User_Defined(pParameters, pParameter->asShapes());
	}

	m_Grid_Target.On_Parameter_Changed(pParameters, pParameter);

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

//---------------------------------------------------------
int CPolygons2Grid::On_Parameters_Enable(CSG_Parameters *pParameters, CSG_Parameter *pParameter)
{
	if(	pParameter->Cmp_Identifier("OUTPUT") )
	{
		pParameters->Set_Enabled("FIELD"    , pParameter->asInt() == 1);
		pParameters->Set_Enabled("GRID_TYPE", pParameter->asInt() == 1);
	}

	m_Grid_Target.On_Parameters_Enable(pParameters, pParameter);

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


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

//---------------------------------------------------------
TSG_Data_Type CPolygons2Grid::Get_Data_Type(int Field)
{
	CSG_Shapes *pShapes = Parameters("POLYGONS")->asShapes();

	if( Field >= 0 && (Field >= pShapes->Get_Field_Count() || !SG_Data_Type_is_Numeric(pShapes->Get_Field_Type(Field))) )
	{
		Field  = OUTPUT_INDEX;  // index number
	}

	if( Field <= OUTPUT_INDEX ) // index number
	{
		return( pShapes->Get_Count() < 65535 ? SG_DATATYPE_Word : SG_DATATYPE_DWord );
	}

//	if( Field >= 0 )            // attribute
	{
		return( Parameters("GRID_TYPE")->asDataType()->Get_Data_Type(pShapes->Get_Field_Type(Field)) );
	}
}


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

//---------------------------------------------------------
bool CPolygons2Grid::On_Execute(void)
{
	CSG_Shapes *pPolygons = Parameters("POLYGONS")->asShapes();

	m_Multiple = Parameters("MULTIPLE")->asInt();

	//-----------------------------------------------------
	int		Field;

	switch( Parameters("OUTPUT")->asInt() )
	{
	case  0: Field = OUTPUT_INDEX; break;          // index number
	default: Field = Parameters("FIELD")->asInt(); // attribute
		if( Field < 0 || !SG_Data_Type_is_Numeric(pPolygons->Get_Field_Type(Field)) )
		{
			Message_Add(_TL("WARNING: selected attribute is not numeric."));
		}
		break;
	}

	//-----------------------------------------------------
	if( (m_pGrid = m_Grid_Target.Get_Grid("GRID", Get_Data_Type(Field))) == NULL )
	{
		return( false );
	}

	if( !pPolygons->Get_Extent().Intersects(m_pGrid->Get_Extent()) )
	{
		Error_Set(_TL("Polygons' and target grid's extent do not intersect."));

		return( false );
	}

	if( Field < 0 )
	{
		m_pGrid->Set_NoData_Value(0.);
	}

	m_pGrid->Fmt_Name("%s [%s]", pPolygons->Get_Name(), Field < 0 ? _TL("ID") : pPolygons->Get_Field_Name(Field));
	m_pGrid->Assign_NoData();

	//-------------------------------------------------
	CSG_Grid Coverage;

	m_pCoverage = m_Grid_Target.Get_Grid("COVERAGE");

	if( m_pCoverage == NULL )
	{
		Coverage.Create(m_pGrid->Get_System());

		m_pCoverage = &Coverage;
	}

	m_pCoverage->Fmt_Name("%s [%s]", pPolygons->Get_Name(), _TL("Coverage"));
	m_pCoverage->Set_NoData_Value(0.);
	m_pCoverage->Assign(0.);

	//-----------------------------------------------------
	for(sLong i=0; i<pPolygons->Get_Count() && Set_Progress(i, pPolygons->Get_Count()); i++)
	{
		CSG_Shape_Polygon *pPolygon = (CSG_Shape_Polygon *)pPolygons->Get_Shape(i);

		if( pPolygons->Get_Selection_Count() <= 0 || pPolygon->is_Selected() )
		{
			if( Field < 0 || !pPolygon->is_NoData(Field) )
			{
				if( pPolygon->Intersects(m_pGrid->Get_Extent(true)) )
				{
					Set_Polygon(pPolygon, Field < 0 ? i + 1. : pPolygon->asDouble(Field));
				}
			}
		}
	}

	//-----------------------------------------------------
	if( m_Multiple == 2 )	// average
	{
		#pragma omp parallel for
		for(sLong i=0; i<m_pGrid->Get_NCells(); i++)
		{
			double Area = m_pCoverage->asDouble(i);

			if( Area > 0. )
			{
				m_pGrid->Mul_Value(i, 1. / Area);
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}


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

//---------------------------------------------------------
inline void CPolygons2Grid::Set_Value(int x, int y, double Value, double Coverage)
{
	if( m_pGrid->is_InGrid(x, y, false) )
	{
		if( m_pCoverage->asDouble(x, y) <= 0. )
		{
			m_pGrid    ->Set_Value(x, y, m_Multiple != 2 ? Value : Coverage * Value);
			m_pCoverage->Set_Value(x, y, Coverage);
		}
		else switch( m_Multiple )
		{
		case  0:	// minimum
			if( m_pCoverage->asDouble(x, y) > Coverage )
			{
				m_pGrid    ->Set_Value(x, y, Value   );
				m_pCoverage->Set_Value(x, y, Coverage);
			}
			break;

		default:	// maximum
			if( m_pCoverage->asDouble(x, y) < Coverage )
			{
				m_pGrid    ->Set_Value(x, y, Value   );
				m_pCoverage->Set_Value(x, y, Coverage);
			}
			break;

		case  2:	// average
			{
				m_pGrid    ->Add_Value(x, y, Coverage * Value);
				m_pCoverage->Add_Value(x, y, Coverage);
			}
			break;
		}
	}
}


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

//---------------------------------------------------------
void CPolygons2Grid::Set_Polygon(CSG_Shape_Polygon *pPolygon, double Value)
{
	//-----------------------------------------------------
	CSG_Grid_System	s(m_pGrid->Get_System());

	int	xA	= s.Get_xWorld_to_Grid(pPolygon->Get_Extent().Get_XMin());	if( xA <  0          )	xA	= 0;
	int	xB	= s.Get_xWorld_to_Grid(pPolygon->Get_Extent().Get_XMax());	if( xB >= s.Get_NX() )	xB	= s.Get_NX() - 1;
	int	yA	= s.Get_yWorld_to_Grid(pPolygon->Get_Extent().Get_YMin());	if( yA <  0          )	yA	= 0;
	int	yB	= s.Get_yWorld_to_Grid(pPolygon->Get_Extent().Get_YMax());	if( yB >= s.Get_NY() )	yB	= s.Get_NY() - 1;

	//-----------------------------------------------------
	CSG_Shapes	Intersect(SHAPE_TYPE_Polygon);

	CSG_Shape_Polygon	*pCell	= (CSG_Shape_Polygon *)Intersect.Add_Shape();

	TSG_Rect	r;

	r.yMax	= s.Get_yGrid_to_World(yA) - 0.5 * s.Get_Cellsize();

	for(int y=yA; y<=yB; y++)
	{
		r.yMin	= r.yMax;	r.yMax	+= s.Get_Cellsize();

		r.xMax	= s.Get_xGrid_to_World(xA) - 0.5 * s.Get_Cellsize();

		for(int x=xA; x<=xB; x++)
		{
			r.xMin	= r.xMax;	r.xMax	+= s.Get_Cellsize();

			pCell->Add_Point(r.xMin, r.yMin);
			pCell->Add_Point(r.xMin, r.yMax);
			pCell->Add_Point(r.xMax, r.yMax);
			pCell->Add_Point(r.xMax, r.yMin);

			if( SG_Shape_Get_Intersection(pCell, pPolygon) )
			{
				Set_Value(x, y, Value, pCell->Get_Area());
			}

			pCell->Del_Parts();
		}
	}
}


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

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