//---------------------------------------------------------
bool CFilter_Resample::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_Grid	*pGrid		= Parameters("GRID"  )->asGrid();
	CSG_Grid	*pLoPass	= Parameters("LOPASS")->asGrid();
	CSG_Grid	*pHiPass	= Parameters("HIPASS")->asGrid();

	double	Cellsize	= Parameters("SCALE" )->asDouble() * Get_Cellsize();

	//-----------------------------------------------------
	if( Cellsize > 0.5 * SG_Get_Length(Get_System().Get_XRange(), Get_System().Get_YRange()) )
	{
		Error_Set(_TL("resampling cell size is too large"));

		return( false );
	}

	//-----------------------------------------------------
	CSG_Grid	Grid(CSG_Grid_System(Cellsize, Get_XMin(), Get_YMin(), Get_XMax(), Get_YMax()), SG_DATATYPE_Float);

	Grid.Assign(pGrid, GRID_RESAMPLING_Mean_Cells);

	//-----------------------------------------------------
	pLoPass->Fmt_Name("%s [%s]", pGrid->Get_Name(), _TL("Low Pass" ));
	pHiPass->Fmt_Name("%s [%s]", pGrid->Get_Name(), _TL("High Pass"));

	CSG_Colors	Colors;

	DataObject_Get_Colors(pGrid  , Colors);
	DataObject_Set_Colors(pLoPass, Colors);
	DataObject_Set_Colors(pHiPass, 11, SG_COLORS_RED_GREY_BLUE);

	//-----------------------------------------------------
	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		double	py	= Get_YMin() + y * Get_Cellsize();

		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			double	z, px	= Get_XMin() + x * Get_Cellsize();

			if( !pGrid->is_NoData(x, y) && Grid.Get_Value(px, py, z) )
			{
				pLoPass->Set_Value(x, y, z);
				pHiPass->Set_Value(x, y, pGrid->asDouble(x, y) - z);
			}
			else
			{
				pLoPass->Set_NoData(x, y);
				pHiPass->Set_NoData(x, y);
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}
//---------------------------------------------------------
bool CViGrA_Watershed::On_Execute(void)
{
	CSG_Grid	*pInput  = Parameters("INPUT" )->asGrid();
	CSG_Grid	*pOutput = Parameters("OUTPUT")->asGrid();

	//-----------------------------------------------------
	if( !Parameters("RGB")->asBool() )
	{
		vigra::FImage	Input, Output(Get_NX(), Get_NY());

		Copy_Grid_SAGA_to_VIGRA(*pInput, Input, true);

		Segmentation(Input, Output, Parameters("SCALE")->asDouble(), Parameters("EDGES")->asBool());

		Copy_Grid_VIGRA_to_SAGA(*pOutput, Output, false);
	}

	//-----------------------------------------------------
	else	// perform watershed segmentation on color image
	{
		vigra::BRGBImage	Input, Output(Get_NX(), Get_NY());

		Copy_RGBGrid_SAGA_to_VIGRA(*pInput, Input, true);

		Segmentation(Input, Output, Parameters("SCALE")->asDouble(), Parameters("EDGES")->asBool());

		Copy_RGBGrid_VIGRA_to_SAGA(*pOutput, Output, false);
	}

	//-----------------------------------------------------
	pOutput->Fmt_Name("%s [%s]", pInput->Get_Name(), Get_Name().c_str());

	return( true );
}
//---------------------------------------------------------
bool CGrid_Mask::On_Execute(void)
{
	CSG_Grid	*pGrid	= Parameters("GRID")->asGrid();
	CSG_Grid	*pMask	= Parameters("MASK")->asGrid();

	if( !pGrid->is_Intersecting(pMask->Get_Extent()) )
	{
		Message_Add(_TL("no intersection with mask grid."));

		return( false );
	}

	//-----------------------------------------------------
	CSG_Grid	*pMasked	= Parameters("MASKED")->asGrid();

	if( pMasked && pMasked != pGrid )
	{
		pMasked->Create(*pGrid);
		pMasked->Fmt_Name("%s [%s]", pGrid->Get_Name(), _TL("masked"));

		pGrid	= pMasked;
	}

	//-----------------------------------------------------
	Process_Set_Text(_TL("masking..."));

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		double	py	= Get_YMin() + y * Get_Cellsize();

		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			if( !pGrid->is_NoData(x, y) )
			{
				double	px	= Get_XMin() + x * Get_Cellsize();

				if( !pMask->is_InGrid_byPos(px, py) )
				{
					pGrid->Set_NoData(x, y);
				}
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}
//---------------------------------------------------------
bool CGrid_Normalise::On_Execute(void)
{
	CSG_Grid	*pGrid	= Parameters("INPUT")->asGrid();

	if( pGrid->Get_StdDev() <= 0.0 )
	{
		return( false );
	}

	if( pGrid != Parameters("OUTPUT")->asGrid() )
	{
		pGrid	= Parameters("OUTPUT")->asGrid();
		pGrid	->Assign(Parameters("INPUT")->asGrid());
	}

	pGrid->Fmt_Name("%s (%s)", pGrid->Get_Name(), _TL("Normalized"));

	//-----------------------------------------------------
	double		Minimum, Maximum, Offset, Scale;

	Minimum	= Parameters("RANGE")->asRange()->Get_Min();
	Maximum	= Parameters("RANGE")->asRange()->Get_Max();
	Offset	= pGrid->Get_Min();
	Scale	= (Maximum - Minimum) / pGrid->Get_Range();

	for(int y=0; y<Get_NY() && SG_UI_Process_Set_Progress(y, Get_NY()); y++)
	{
		for(int x=0; x<Get_NX(); x++)
		{
			if( !pGrid->is_NoData(x, y) )
			{
				pGrid->Set_Value(x, y, Minimum + Scale * (pGrid->asDouble(x, y) - Offset));
			}
		}
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("INPUT")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CGrid_Standardise::On_Execute(void)
{
	CSG_Grid	*pGrid	= Parameters("INPUT")->asGrid();

	if( pGrid->Get_StdDev() <= 0.0 )
	{
		return( false );
	}

	if( pGrid != Parameters("OUTPUT")->asGrid() )
	{
		pGrid	= Parameters("OUTPUT")->asGrid();
		pGrid	->Assign(Parameters("INPUT")->asGrid());
	}

	pGrid->Fmt_Name("%s (%s)", pGrid->Get_Name(), _TL("Standard Score"));

	//-----------------------------------------------------
	double	Mean	= pGrid->Get_Mean();
	double	Stretch	= Parameters("STRETCH")->asDouble() / pGrid->Get_StdDev();

	for(int y=0; y<Get_NY() && SG_UI_Process_Set_Progress(y, Get_NY()); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			if( !pGrid->is_NoData(x, y) )
			{
				pGrid->Set_Value(x, y, Stretch * (pGrid->asDouble(x, y) - Mean));
			}
		}
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("INPUT")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CGrid_Invert::On_Execute(void)
{
	CSG_Grid	*pGrid	= Parameters("INVERSE")->asGrid();

	if( pGrid == NULL )
	{
		pGrid	= Parameters("GRID")->asGrid();
	}
	else if( pGrid != Parameters("GRID")->asGrid() )
	{
		pGrid->Create(*Parameters("GRID")->asGrid());

		pGrid->Fmt_Name("%s [%s]", pGrid->Get_Name(), _TL("Inverse"));
	}

	//-----------------------------------------------------
	double	zMin	= pGrid->Get_Min();
	double	zMax	= pGrid->Get_Max();

	for(int y=0; y<Get_NY() && SG_UI_Process_Set_Progress(y, Get_NY()); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			if( !pGrid->is_NoData(x, y) )
			{
				pGrid->Set_Value(x, y, zMax - (pGrid->asDouble(x, y) - zMin));
			}
		}
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("GRID")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CGrid_Mirror::On_Execute(void)
{
	CSG_Grid	*pGrid	= Parameters("MIRROR")->asGrid();

	if( pGrid == NULL )
	{
		pGrid	= Parameters("GRID")->asGrid();
	}
	else if( pGrid != Parameters("GRID")->asGrid() )
	{
		pGrid->Create(*Parameters("GRID")->asGrid());

		pGrid->Fmt_Name("%s [%s %s]", pGrid->Get_Name(), _TL("mirrored"), Parameters("METHOD")->asString());
	}

	//-----------------------------------------------------
	switch( Parameters("METHOD")->asInt() )
	{
	//-----------------------------------------------------
	case  0:	// vertically
		{
			for(int xa=0, xb=Get_NX()-1; xa<xb && SG_UI_Process_Set_Progress(xa, Get_NX()/2); xa++, xb--)
			{
				#pragma omp parallel for
				for(int y=0; y<Get_NY(); y++)
				{
					double	d             = pGrid->asDouble(xa, y);
					pGrid->Set_Value(xa, y, pGrid->asDouble(xb, y));
					pGrid->Set_Value(xb, y, d);
				}
			}
		}
		break;

	//-----------------------------------------------------
	case  1:	// horizontally
		{
			for(int ya=0, yb=Get_NY()-1; ya<yb && SG_UI_Process_Set_Progress(ya, Get_NY()/2); ya++, yb--)
			{
				#pragma omp parallel for
				for(int x=0; x<Get_NX(); x++)
				{
					double	d             = pGrid->asDouble(x, ya);
					pGrid->Set_Value(x, ya, pGrid->asDouble(x, yb));
					pGrid->Set_Value(x, yb, d);
				}
			}
		}
		break;

	//-----------------------------------------------------
	default:	// both
		{
			for(int ya=0, yb=Get_NY()-1; ya<=yb && SG_UI_Process_Set_Progress(ya, Get_NY()/2); ya++, yb--)
			{
				for(int xa=0, xb=Get_NX()-1; xa<=xb; xa++, xb--)
				{
					if( ya < yb && xa < xb )
					{
						double	d              = pGrid->asDouble(xa, ya);
						pGrid->Set_Value(xa, ya, pGrid->asDouble(xb, yb));
						pGrid->Set_Value(xb, yb, d);

						d                      = pGrid->asDouble(xa, yb);
						pGrid->Set_Value(xa, yb, pGrid->asDouble(xb, ya));
						pGrid->Set_Value(xb, ya, d);
					}
					else if( xa < xb )
					{
						double	d              = pGrid->asDouble(xa, ya);
						pGrid->Set_Value(xa, ya, pGrid->asDouble(xb, ya));
						pGrid->Set_Value(xb, ya, d);
					}
					else if( ya < yb )
					{
						double	d              = pGrid->asDouble(xa, ya);
						pGrid->Set_Value(xa, ya, pGrid->asDouble(xa, yb));
						pGrid->Set_Value(xa, yb, d);
					}
				}
			}
		}
		break;
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("GRID")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CGrid_Value_Replace::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_Grid	*pGrid	= Parameters("OUTPUT")->asGrid();

	if( !pGrid || pGrid == Parameters("INPUT")->asGrid() )
	{
		pGrid	= Parameters("INPUT")->asGrid();
	}
	else
	{
		pGrid->Assign(Parameters("INPUT")->asGrid());

		DataObject_Set_Parameters(pGrid, Parameters("INPUT")->asGrid());

		pGrid->Fmt_Name("%s [%s]", Parameters("INPUT")->asGrid()->Get_Name(), _TL("Changed"));
	}

	//-----------------------------------------------------
	int		Method	= Parameters("METHOD")->asInt();

	CSG_Table	LUT;

	switch( Method )
	{
	default:	LUT.Create(*Parameters("IDENTITY")->asTable());	break;
	case  1:	LUT.Create(*Parameters("RANGE"   )->asTable());	break;
	case  2:	LUT.Create( Parameters("RANGE"   )->asTable());
		if( SG_UI_Get_Window_Main()	// gui only
		&&  DataObject_Get_Parameter(Parameters("GRID" )->asGrid(), "LUT")
		&&  DataObject_Get_Parameter(Parameters("INPUT")->asGrid(), "LUT") )
		{
			CSG_Table	LUTs[2];

			LUTs[0].Create(*DataObject_Get_Parameter(Parameters("GRID" )->asGrid(), "LUT")->asTable());
			LUTs[1].Create(*DataObject_Get_Parameter(Parameters("INPUT")->asGrid(), "LUT")->asTable());

			for(int i=0; i<LUTs[0].Get_Count(); i++)
			{
				CSG_String	Name	= LUTs[0][i].asString(1);

				for(int j=LUTs[1].Get_Count()-1; j>=0; j--)
				{
					if( !Name.Cmp(LUTs[1][j].asString(1)) )
					{
						CSG_Table_Record	*pReplace	= LUT.Add_Record();

						pReplace->Set_Value(0, LUTs[0][i].asDouble(3));
						pReplace->Set_Value(1, LUTs[1][j].asDouble(3));
						pReplace->Set_Value(2, LUTs[1][j].asDouble(4));

						LUTs[1].Del_Record(j);

						break;
					}
				}
			}

			for(int j=0; j<LUTs[1].Get_Count(); j++)
			{
				LUTs[0].Add_Record(LUTs[1].Get_Record(j));
			}

			DataObject_Add(pGrid);
			CSG_Parameter	*pLUT	= DataObject_Get_Parameter(pGrid, "LUT");
			pLUT->asTable()->Assign_Values(&LUTs[0]);
			DataObject_Set_Parameter(pGrid, pLUT);
		}
		break;
	}

	//-----------------------------------------------------
	if( LUT.Get_Count() == 0 )
	{
		Error_Set(_TL("empty look-up table, nothing to replace"));

		return( false );
	}

	//-----------------------------------------------------
	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#ifndef _DEBUG
		#pragma omp parallel for
		#endif
		for(int x=0; x<Get_NX(); x++)
		{
			double	Value	= pGrid->asDouble(x, y);

			for(int i=0; i<LUT.Get_Count(); i++)
			{
				if( Method == 0 )
				{
					if( LUT[i].asDouble(1) == Value )
					{
						pGrid->Set_Value(x, y, LUT[i].asDouble(0));

						break;
					}
				}
				else
				{
					if( LUT[i].asDouble(1) <= Value && Value <= LUT[i].asDouble(2) )
					{
						pGrid->Set_Value(x, y, LUT[i].asDouble(0));

						break;
					}
				}
			}
		}
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("INPUT")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CCRU_Table_Import::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_File	File;

	if( !File.Open(Parameters("FILE")->asString(), SG_FILE_R, false) )
	{
		Error_Fmt("%s [%s]", _TL("could not open file"), Parameters("FILE")->asString());

		return( false );
	}

	//-----------------------------------------------------
	CSG_String	sLine;

	if( !File.Read_Line(sLine) )
	{
		Error_Fmt("%s [%s]", _TL("failed to read header"), Parameters("FILE")->asString());

		return( false );
	}

	//-----------------------------------------------------
	int		nx, ny, nMonths;
	double	Cellsize, xMin, yMin, xMax, yMax;

	if( !File.Scan(Cellsize)
	||  !File.Scan(xMin    ) || !File.Scan(yMin    )
	||  !File.Scan(xMax    ) || !File.Scan(yMax    )
	||  !File.Scan(nx      ) || !File.Scan(ny      )
	||  !File.Scan(nMonths ) )
	{
		Error_Fmt("%s [%s]", _TL("failed to read header"), Parameters("FILE")->asString());

		return( false );
	}

	//-----------------------------------------------------
	CSG_Grid_System	System(Cellsize, xMin, yMin, nx, ny);

	if( !System.is_Valid() || System.Get_XMax() != xMax || System.Get_YMax() != yMax )
	{
		Error_Fmt("%s [%s]", _TL("failed to read header"), Parameters("FILE")->asString());

		return( false );
	}

	//-----------------------------------------------------
	bool	bShift	= Parameters("SHIFT")->asBool();

	if( bShift )
	{
		System.Assign(Cellsize, xMin - 180.0, yMin, nx, ny);
	}

	//-----------------------------------------------------
	CSG_String	Name	= SG_File_Get_Name(Parameters("FILE")->asString(), false);

	Parameters("GRIDS")->asGridList()->Del_Items();

	for(int iMonth=0; iMonth<nMonths && !File.is_EOF() && Process_Get_Okay(); iMonth++)
	{
		Process_Set_Text("%s %d", _TL("Band"), 1 + iMonth);

		CSG_Grid	*pGrid	= SG_Create_Grid(System, SG_DATATYPE_Short);

		pGrid->Fmt_Name("%s_%02d", Name.c_str(), 1 + iMonth);
		pGrid->Set_NoData_Value(-9999);
		pGrid->Get_Projection().Set_GCS_WGS84();

		Parameters("GRIDS")->asGridList()->Add_Item(pGrid);

		//-------------------------------------------------
		for(int y=0; y<ny && !File.is_EOF() && Set_Progress(y, ny); y++)
		{
			if( File.Read_Line(sLine) && sLine.Length() >= 5. * nx )
			{
				for(int x=0, xx=bShift?nx/2:x, yy=ny-1-y; x<nx; x++, xx++)
				{
					double	z;

					CSG_String	s	= sLine.Mid(x * 5, 5);

					if( s.asDouble(z) )
					{
						pGrid->Set_Value(xx % nx, yy, z);
					}
					else
					{
						pGrid->Set_NoData(xx % nx, yy);
					}
				}
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}
//---------------------------------------------------------
bool CGrid_CVA::On_Execute(void)
{	
	//-----------------------------------------------------
	CSG_Parameter_Grid_List	*pA	= Parameters("A")->asGridList();
	CSG_Parameter_Grid_List	*pB	= Parameters("B")->asGridList();
	CSG_Parameter_Grid_List	*pC	= Parameters("C")->asGridList();

	if( pA->Get_Grid_Count() != pB->Get_Grid_Count() )
	{
		Error_Set(_TL("number of initial and final state grids differs"));

		return( false );
	}

	if( pA->Get_Grid_Count() == 0 )
	{
		Error_Set(_TL("no grids in list"));

		return( false );
	}

	//-----------------------------------------------------
	int	n	= pA->Get_Grid_Count();

	bool		bAngle	= Parameters("ANGLE")->asBool() && n == 2;
	bool		bC_Out	= Parameters("C_OUT")->asBool();

	CSG_Grid	*pDist	= Parameters("DIST")->asGrid();
	CSG_Grid	*pDir	= Parameters("DIR" )->asGrid();

	//-----------------------------------------------------
	pC->Del_Items();

	if( bC_Out )
	{
		for(int i=0; i<n; i++)
		{
			CSG_Grid	*pGrid	= SG_Create_Grid(Get_System());
			pGrid->Fmt_Name("%s %01d", _TL("Change Vector"), i + 1);
			pC->Add_Item(pGrid);
		}
	}

	//-----------------------------------------------------
	CSG_Parameter	*pLUT;
	CSG_Colors		Colors;

	Colors.Set_Count(100);
	Colors.Set_Ramp(SG_GET_RGB(255, 255, 255), SG_GET_RGB(  0, 127, 127), 0, Colors.Get_Count() / 2);
	Colors.Set_Ramp(SG_GET_RGB(  0, 127, 127), SG_GET_RGB(255,   0,   0),    Colors.Get_Count() / 2, Colors.Get_Count());
	DataObject_Set_Colors(pDist, Colors);

	if( (pLUT = DataObject_Get_Parameter(pDir, "LUT")) == NULL || pLUT->asTable() == NULL || bAngle )
	{
		Colors.Set_Default(100);
		Colors.Set_Ramp_Brighness(255,   0, 0, Colors.Get_Count() / 2);
		Colors.Set_Ramp_Brighness(  0, 255,    Colors.Get_Count() / 2, Colors.Get_Count());
		DataObject_Set_Colors(pDir, Colors);

		DataObject_Set_Parameter(pDir, "COLORS_TYPE", 2);
	}
	else
	{
		pLUT->asTable()->Del_Records();

		for(int i=0, nClasses=(int)pow(2.0, n); i<nClasses; i++)
		{
			CSG_String	s;

			for(int j=0; j<n; j++)
			{
				s	+= i & (int)pow(2.0, j) ? '+' : '-';
			}

			CSG_Table_Record	*pClass	= pLUT->asTable()->Add_Record();
			pClass->Set_Value(1, s);
			pClass->Set_Value(3, i);
			pClass->Set_Value(4, i);
		}

		Colors.Set_Count(pLUT->asTable()->Get_Count());
		Colors.Random();

		for(int c=0; c<pLUT->asTable()->Get_Count(); c++)
		{
			pLUT->asTable()->Get_Record(c)->Set_Value(0, Colors.Get_Color(c));
		}

		DataObject_Set_Parameter(pDir, pLUT);
		DataObject_Set_Parameter(pDir, "COLORS_TYPE", 1);	// Color Classification Type: Lookup Table
	}

	//-----------------------------------------------------
    for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			bool		bOkay;
			int			i, j;
			double		d;
			CSG_Vector	v(n);

			for(i=0, bOkay=true; i<n && bOkay; i++)
			{
				if( pA->Get_Grid(i)->is_NoData(x, y) || pB->Get_Grid(i)->is_NoData(x, y) )
				{
					bOkay	= false;
				}
				else
				{
					v[i]	= pB->Get_Grid(i)->asDouble(x, y) - pA->Get_Grid(i)->asDouble(x, y);
				}
			}

			//---------------------------------------------
			if( bOkay )
			{
				if( bAngle )
				{
					d	= atan2(v[0], v[1]);
				}
				else for(i=0, j=1, d=0.0; i<n; i++, j*=2)
				{
					if( v[i] >= 0.0 )
					{
						d	+= j;
					}
				}

				pDist->Set_Value(x, y, v.Get_Length());
				pDir ->Set_Value(x, y, d);

				for(i=0; i<n && bC_Out; i++)
				{
					pC->Get_Grid(i)->Set_Value(x, y, v[i]);
				}
			}

			//---------------------------------------------
			else
			{
				pDist->Set_NoData(x, y);
				pDir ->Set_NoData(x, y);

				for(i=0; i<n && bC_Out; i++)
				{
					pC->Get_Grid(i)->Set_NoData(x, y);
				}
			}
        }
    }

	return( true );
}
//---------------------------------------------------------
bool CFilter_3x3::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_Table	*pFilter	= Parameters("FILTER")->asTable()
		? Parameters("FILTER"    )->asTable()
		: Parameters("FILTER_3X3")->asTable();

	if( pFilter->Get_Count() < 1 || pFilter->Get_Field_Count() < 1 )
	{
		Error_Set(_TL("invalid filter matrix"));

		return( false );
	}

	//-----------------------------------------------------
	CSG_Matrix	Filter(pFilter->Get_Field_Count(), pFilter->Get_Count());

	{
		for(int iy=0; iy<Filter.Get_NY(); iy++)
		{
			CSG_Table_Record	*pRecord	= pFilter->Get_Record(iy);

			for(int ix=0; ix<Filter.Get_NX(); ix++)
			{
				Filter[iy][ix]	= pRecord->asDouble(ix);
			}
		}
	}

	int	nx	= (Filter.Get_NX() - 1) / 2;
	int	ny	= (Filter.Get_NY() - 1) / 2;

	//-----------------------------------------------------
	CSG_Grid	*pInput 	= Parameters("INPUT" )->asGrid();
	CSG_Grid	*pResult	= Parameters("RESULT")->asGrid();

	if( !pResult || pResult == pInput )
	{
		pResult	= SG_Create_Grid(pInput);
	}
	else
	{
		pResult->Fmt_Name("%s [%s]", pInput->Get_Name(), _TL("Filter"));

		pResult->Set_NoData_Value(pInput->Get_NoData_Value());
	}

	//-----------------------------------------------------
	bool	bAbsolute	= Parameters("ABSOLUTE")->asBool();

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			double	s	= 0.0;
			double	n	= 0.0;

			if( pInput->is_InGrid(x, y) )
			{
				for(int iy=0, jy=y-ny; iy<Filter.Get_NY(); iy++, jy++)
				{
					for(int ix=0, jx=x-nx; ix<Filter.Get_NX(); ix++, jx++)
					{
						if( pInput->is_InGrid(jx, jy) )
						{
							s	+=      Filter[iy][ix] * pInput->asDouble(jx, jy);
							n	+= fabs(Filter[iy][ix]);
						}
					}
				}
			}

			if( n > 0.0 )
			{
				pResult->Set_Value(x, y, bAbsolute ? s : s / n);
			}
			else
			{
				pResult->Set_NoData(x, y);
			}
		}
	}

	//-----------------------------------------------------
	if( !Parameters("RESULT")->asGrid() || Parameters("RESULT")->asGrid() == pInput )
	{
		pInput->Assign(pResult);

		delete(pResult);

		DataObject_Update(pInput);
	}

	return( true );
}
//---------------------------------------------------------
bool CKriging_Regression::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_Shapes	Points(SHAPE_TYPE_Point);

	CSG_Grid	*pPrediction	= Parameters("PREDICTION")->asGrid();
	CSG_Grid	*pRegression	= Parameters("REGRESSION")->asGrid();
	CSG_Grid	*pResiduals		= Parameters("RESIDUALS" )->asGrid();
	CSG_Grid	*pVariance		= Parameters("VARIANCE"  )->asGrid();

	//-----------------------------------------------------
	if( !pResiduals )
	{
		pResiduals	= pPrediction;
	}

	//-----------------------------------------------------
	SG_RUN_TOOL_ExitOnError("statistics_regression", 1,	// Multiple Regression Analysis (Points and Predictor Grids)
			SG_TOOL_PARAMETER_SET("PREDICTORS", Parameters("PREDICTORS"))
		&&	SG_TOOL_PARAMETER_SET("POINTS"    , Parameters("POINTS"    ))
		&&	SG_TOOL_PARAMETER_SET("ATTRIBUTE" , Parameters("FIELD"     ))
		&&	SG_TOOL_PARAMETER_SET("INFO_COEFF", Parameters("INFO_COEFF"))
		&&	SG_TOOL_PARAMETER_SET("INFO_MODEL", Parameters("INFO_MODEL"))
		&&	SG_TOOL_PARAMETER_SET("INFO_STEPS", Parameters("INFO_STEPS"))
		&&	SG_TOOL_PARAMETER_SET("RESAMPLING", Parameters("RESAMPLING"))
		&&	SG_TOOL_PARAMETER_SET("COORD_X"   , Parameters("COORD_X"   ))
		&&	SG_TOOL_PARAMETER_SET("COORD_Y"   , Parameters("COORD_Y"   ))
		&&	SG_TOOL_PARAMETER_SET("INTERCEPT" , Parameters("INTERCEPT" ))
		&&	SG_TOOL_PARAMETER_SET("METHOD"    , Parameters("METHOD"    ))
		&&	SG_TOOL_PARAMETER_SET("P_VALUE"   , Parameters("P_VALUE"   ))
		&&	SG_TOOL_PARAMETER_SET("REGRESSION", pRegression)
		&&	SG_TOOL_PARAMETER_SET("RESIDUALS" , &Points )
	);

	//-----------------------------------------------------
	CSG_Tool	*pK	= Parameters("KRIGING")->asInt() == 0 ? (CSG_Tool *)&m_SK : (CSG_Tool *)&m_OK;

	Process_Set_Text(pK->Get_Name());

	pK->Set_Manager(NULL);

	if( !pK->Set_Parameter("POINTS"           , &Points)
	||  !pK->Set_Parameter("FIELD"            , 2)	// residual
	||  !pK->Set_Parameter("LOG"              , Parameters("LOG"              ))
	||  !pK->Set_Parameter("BLOCK"            , Parameters("BLOCK"            ))
	||  !pK->Set_Parameter("DBLOCK"           , Parameters("DBLOCK"           ))
	||  !pK->Set_Parameter("SEARCH_RANGE"     , Parameters("SEARCH_RANGE"     ))
	||  !pK->Set_Parameter("SEARCH_RADIUS"    , Parameters("SEARCH_RADIUS"    ))
	||  !pK->Set_Parameter("SEARCH_POINTS_ALL", Parameters("SEARCH_POINTS_ALL"))
	||  !pK->Set_Parameter("SEARCH_POINTS_MIN", Parameters("SEARCH_POINTS_MIN"))
	||  !pK->Set_Parameter("SEARCH_POINTS_MAX", Parameters("SEARCH_POINTS_MAX"))
	||  !pK->Set_Parameter("SEARCH_DIRECTION" , Parameters("SEARCH_DIRECTION" ))
	||  !pK->Set_Parameter("TARGET_DEFINITION", 1)	// grid or grid system
	||  !pK->Set_Parameter("PREDICTION"       , pResiduals)
	||  !pK->Set_Parameter("VARIANCE"         , pVariance )

	|| (!SG_UI_Get_Window_Main() && (	// saga_cmd
	    !pK->Set_Parameter("VAR_MAXDIST"      , Parameters("VAR_MAXDIST"      ))
	||  !pK->Set_Parameter("VAR_NCLASSES"     , Parameters("VAR_NCLASSES"     ))
	||  !pK->Set_Parameter("VAR_NSKIP"        , Parameters("VAR_NSKIP"        ))
	||  !pK->Set_Parameter("VAR_MODEL"        , Parameters("VAR_MODEL"        )))) )
	{
		Error_Set(CSG_String::Format("%s [%s].[%s]", _TL("could not initialize tool"), _TL("statistics_regression"), pK->Get_Name().c_str()));

		return( false );
	}

	if( !pK->Execute() )
	{
		Error_Set(CSG_String::Format("%s [%s].[%s]", _TL("could not execute tool"   ), _TL("statistics_regression"), pK->Get_Name().c_str()));

		return( false );
	}

	//-----------------------------------------------------
	#pragma omp parallel for
	for(int y=0; y<Get_NY(); y++)
	{
		for(int x=0; x<Get_NX(); x++)
		{
			if( pRegression->is_NoData(x, y) || pResiduals->is_NoData(x, y) )
			{
				pPrediction->Set_NoData(x, y);
			}
			else
			{
				pPrediction->Set_Value(x, y, pRegression->asDouble(x, y) + pResiduals->asDouble(x, y));
			}
		}
	}

	//-----------------------------------------------------
	pRegression->Fmt_Name("%s.%s [%s]", Parameters("POINTS")->asShapes()->Get_Name(), Parameters("FIELD")->asString(), _TL("Regression"));
	pPrediction->Fmt_Name("%s.%s [%s]", Parameters("POINTS")->asShapes()->Get_Name(), Parameters("FIELD")->asString(), _TL("Prediction"));

	if( Parameters("RESIDUALS")->asGrid() )
	{
		pResiduals->Fmt_Name("%s.%s [%s]", Parameters("POINTS")->asShapes()->Get_Name(), Parameters("FIELD")->asString(), _TL("Residuals"));
	}

	if( pVariance )
	{
		pVariance ->Fmt_Name("%s.%s [%s]", Parameters("POINTS")->asShapes()->Get_Name(), Parameters("FIELD")->asString(), _TL("Quality"));
	}

	//-----------------------------------------------------
	return( true );
}
//---------------------------------------------------------
bool CGrid_Gaps_Resampling::On_Execute(void)
{
	//-----------------------------------------------------
	CSG_Grid	*pGrid	= Parameters("RESULT")->asGrid();
	CSG_Grid	*pMask	= Parameters("MASK"  )->asGrid();

	if( pGrid == NULL )
	{
		pGrid	= Parameters("INPUT")->asGrid();
	}
	else
	{
		pGrid->Assign(Parameters("INPUT")->asGrid());
		pGrid->Fmt_Name("%s [%s]", Parameters("INPUT")->asGrid()->Get_Name(), _TL("no gaps"));
	}

	//-----------------------------------------------------
	TSG_Grid_Resampling	Resampling;

	switch( Parameters("RESAMPLING")->asInt() )
	{
	default: Resampling = GRID_RESAMPLING_NearestNeighbour; break;
	case  1: Resampling = GRID_RESAMPLING_Bilinear        ; break;
	case  2: Resampling = GRID_RESAMPLING_BicubicSpline   ; break;
	case  3: Resampling = GRID_RESAMPLING_BSpline         ; break;
	}

	//-----------------------------------------------------
	CSG_Grid_Pyramid	Pyramid;

	if( !Pyramid.Create(pGrid, Parameters("GROW")->asDouble()) )
	{
		Error_Set(_TL("failed to create pyramid"));

		return( false );
	}

	//-----------------------------------------------------
	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		double	py	= Get_YMin() + y * Get_Cellsize();

		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			if( pGrid->is_NoData(x, y) && (!pMask || !pMask->is_NoData(x, y)) )
			{
				double	px	= Get_XMin() + x * Get_Cellsize();

				for(int i=0; i<Pyramid.Get_Count(); i++)
				{
					CSG_Grid	*pPatch	= Pyramid.Get_Grid(i);

					if( pPatch->is_InGrid_byPos(px, py) )
					{
						pGrid->Set_Value(x, y, pPatch->Get_Value(px, py, Resampling));

						break;
					}
				}
			}
		}
	}

	//-----------------------------------------------------
	if( pGrid == Parameters("INPUT")->asGrid() )
	{
		DataObject_Update(pGrid);
	}

	return( true );
}
//---------------------------------------------------------
bool CGrid_PCA_Focal::On_Execute(void)
{
	int		i;

	//-----------------------------------------------------
	CSG_Grid_Cell_Addressor	Kernel;

	Kernel.Set_Radius(
		Parameters("KERNEL_RADIUS")->asInt(),
		Parameters("KERNEL_TYPE"  )->asInt() == 0
	);

	CSG_Parameter_Grid_List	*pPCA, *pGrids	= Parameters("BASE")->asGridList();

	pGrids->Del_Items();

	for(i=0; i<Kernel.Get_Count()-1; i++)
	{
		CSG_Grid	*pGrid	= SG_Create_Grid(Get_System());

		if( !pGrid )
		{
			Error_Set(_TL("failed to allocate memory"));

			for(i=0; i<pGrids->Get_Grid_Count(); i++)
			{
				delete(pGrids->Get_Grid(i));
			}

			pGrids->Del_Items();

			return( false );
		}

		pGrid->Fmt_Name("x(%d)-y(%d)", Kernel.Get_X(i + 1), Kernel.Get_Y(i + 1));

		pGrids->Add_Item(pGrid);
	}

	//-----------------------------------------------------
	CSG_Grid	*pGrid	= Parameters("GRID")->asGrid();

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#pragma omp parallel for private(i)
		for(int x=0; x<Get_NX(); x++)
		{
			if( pGrid->is_NoData(x, y) )
			{
				for(i=0; i<pGrids->Get_Grid_Count(); i++)
				{
					pGrids->Get_Grid(i)->Set_NoData(x, y);
				}
			}
			else
			{
				double	z	= pGrid->asDouble(x, y);

				for(i=0; i<pGrids->Get_Grid_Count(); i++)
				{
					int	ix	= Kernel.Get_X(i + 1, x);
					int	iy	= Kernel.Get_Y(i + 1, y);

					pGrids->Get_Grid(i)->Set_Value(x, y, pGrid->is_InGrid(ix, iy) ? z - pGrid->asDouble(ix, iy) : 0.0);
				}
			}
		}
	}

	//-----------------------------------------------------
	bool	bResult;

	CSG_Parameters	PCA_Parms;

	SG_RUN_TOOL_KEEP_PARMS(bResult, "statistics_grid", 8, PCA_Parms,	// pca analysis for grids
			SG_TOOL_PARAMETER_SET("GRIDS"     , Parameters("BASE"      ))
		&&	SG_TOOL_PARAMETER_SET("METHOD"    , Parameters("METHOD"    ))
		&&	SG_TOOL_PARAMETER_SET("EIGEN"     , Parameters("EIGEN"     ))
		&&	SG_TOOL_PARAMETER_SET("COMPONENTS", Parameters("COMPONENTS")->asInt())
	);

	if( !Parameters("BASE_OUT")->asBool() )
	{
		for(i=0; i<pGrids->Get_Grid_Count(); i++)
		{
			delete(pGrids->Get_Grid(i));
		}

		pGrids->Del_Items();
	}

	//-----------------------------------------------------
	pGrids	= Parameters("PCA")->asGridList();
	pPCA	= PCA_Parms ("PCA")->asGridList();

	if( !Parameters("OVERWRITE")->asBool() || (pGrids->Get_Grid_Count() > 0 && !Get_System().is_Equal(pGrids->Get_Grid(0)->Get_System())) )
	{
		pGrids->Del_Items();
	}

	for(i=0; i<pPCA->Get_Grid_Count(); i++)
	{
		if( pGrids->Get_Grid(i) )
		{
			pGrids->Get_Grid(i)->Assign(pPCA->Get_Grid(i));

			delete(pPCA->Get_Grid(i));
		}
		else
		{
			pGrids->Add_Item(pPCA->Get_Grid(i));
		}

		pGrids->Get_Grid(i)->Fmt_Name("%s [PC%0*d]", pGrid->Get_Name(), pPCA->Get_Grid_Count() < 10 ? 1 : 2, i + 1);
	}

	//-----------------------------------------------------
	return( bResult );
}