//---------------------------------------------------------
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 CPanSharp_PCA::On_Execute(void)
{
	//-----------------------------------------------------
	bool			bResult;
	CSG_Parameters	Tool_Parms;
	CSG_Table		Eigen;

	//-----------------------------------------------------
	// get the principal components for the low resolution bands

	SG_RUN_TOOL_KEEP_PARMS(bResult, "statistics_grid", 8, Tool_Parms,
			SG_TOOL_PARAMETER_SET("GRIDS"     , Parameters("GRIDS" ))
		&&	SG_TOOL_PARAMETER_SET("METHOD"    , Parameters("METHOD"))
		&&	SG_TOOL_PARAMETER_SET("EIGEN"     , &Eigen)
		&&	SG_TOOL_PARAMETER_SET("COMPONENTS", 0)	// get all components
	);

	if( !bResult )
	{
		return( false );
	}

	//-----------------------------------------------------
	CSG_Parameter_Grid_List	*pPCA	= Tool_Parms.Get_Parameter("PCA")->asGridList();

	int			i, n	= pPCA->Get_Grid_Count();

	CSG_Grid	*PCA	= new CSG_Grid[n];
	CSG_Grid	*pPan	= Parameters("PAN")->asGrid();

	//-----------------------------------------------------
	// replace first principal component with the high resolution panchromatic band

	Process_Set_Text(_TL("Replace first PC with PAN"));

	double	Offset_Pan, Offset, Scale;

	if( Parameters("PAN_MATCH")->asInt() == 0 )	// scale PAN band to fit first PC histogram
	{
		Offset_Pan	= pPan->Get_Min();
		Offset		= pPCA->Get_Grid(0)->Get_Min();
		Scale		= pPCA->Get_Grid(0)->Get_Range() / pPan->Get_Range();
	}
	else
	{
		Offset_Pan	= pPan->Get_Mean();
		Offset		= pPCA->Get_Grid(0)->Get_Mean();
		Scale		= pPCA->Get_Grid(0)->Get_StdDev() / pPan->Get_StdDev();
	}

	PCA[0].Create(Get_System());

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			if( pPan->is_NoData(x, y) )
			{
				PCA[0].Set_NoData(x, y);
			}
			else
			{
				PCA[0].Set_Value(x, y, Offset + Scale * (pPan->asDouble(x, y) - Offset_Pan));
			}
		}
	}

	//-----------------------------------------------------
	// resample all other PCs to match the high resolution of the PAN band

	TSG_Grid_Resampling	Resampling	= Get_Resampling(Parameters("RESAMPLING")->asInt());

	for(i=1; i<n; i++)
	{
		Process_Set_Text("%s: %s ...", _TL("Resampling"), pPCA->Get_Grid(i)->Get_Name());

		PCA[i].Create(Get_System());
		PCA[i].Assign(pPCA->Get_Grid(i), Resampling);

		delete(pPCA->Get_Grid(i));	// PCA tool was unmanaged, so we have to delete the output
	}

	delete(pPCA->Get_Grid(0));

	pPCA->Del_Items();

	for(i=0; i<n; i++)
	{
		pPCA->Add_Item(&PCA[i]);
	}

	//-----------------------------------------------------
	// inverse principal component rotation for the high resolution bands

	SG_RUN_TOOL_KEEP_PARMS(bResult, "statistics_grid", 10, Tool_Parms,
			SG_TOOL_PARAMETER_SET("PCA"  , Tool_Parms("PCA"))
		&&	SG_TOOL_PARAMETER_SET("GRIDS", Parameters("SHARPEN"))
		&&	SG_TOOL_PARAMETER_SET("EIGEN", &Eigen)
	);

	delete[](PCA);

	if( !bResult )
	{
		return( false );
	}

	CSG_Parameter_Grid_List	*pHiRes	= Parameters("SHARPEN")->asGridList();
	CSG_Parameter_Grid_List	*pLoRes	= Parameters("GRIDS"  )->asGridList();
	CSG_Parameter_Grid_List	*pGrids	= Tool_Parms("GRIDS"  )->asGridList();

	if( !Parameters("OVERWRITE")->asBool() )
	{
		pHiRes->Del_Items();
	}

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

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

		pHiRes->Get_Grid(i)->Set_Name(pLoRes->Get_Grid(i)->Get_Name());
	}

	return( true );
}
//---------------------------------------------------------
bool CPanSharp_IHS::On_Execute(void)
{
	//-----------------------------------------------------
	TSG_Grid_Resampling	Resampling	= Get_Resampling(Parameters("RESAMPLING")->asInt());

	//-----------------------------------------------------
	int			y;

	CSG_Grid	*pPan	= Parameters("PAN")->asGrid();

	//-----------------------------------------------------
	Process_Set_Text("%s: %s ...", _TL("Resampling"), Parameters("R")->asGrid()->Get_Name());
	CSG_Grid	*pR	= Parameters("R_SHARP")->asGrid();
	pR->Assign  (Parameters("R")->asGrid(), Resampling);
	pR->Set_Name(Parameters("R")->asGrid()->Get_Name());

	Process_Set_Text("%s: %s ...", _TL("Resampling"), Parameters("G")->asGrid()->Get_Name());
	CSG_Grid	*pG	= Parameters("G_SHARP")->asGrid();
	pG->Assign  (Parameters("G")->asGrid(), Resampling);
	pG->Set_Name(Parameters("G")->asGrid()->Get_Name());

	Process_Set_Text("%s: %s ...", _TL("Resampling"), Parameters("B")->asGrid()->Get_Name());
	CSG_Grid	*pB	= Parameters("B_SHARP")->asGrid();
	pB->Assign  (Parameters("B")->asGrid(), Resampling);
	pB->Set_Name(Parameters("B")->asGrid()->Get_Name());

	//-----------------------------------------------------
	Process_Set_Text(_TL("RGB to IHS"));

	double	rMin	= pR->Get_Min(),	rRange	= pR->Get_Range();
	double	gMin	= pG->Get_Min(),	gRange	= pG->Get_Range();
	double	bMin	= pB->Get_Min(),	bRange	= pB->Get_Range();

	for(y=0; y<pPan->Get_NY() && Set_Progress(y, pPan->Get_NY()); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<pPan->Get_NX(); x++)
		{
			bool	bNoData	= true;

			if( pPan->is_NoData(x, y) || pR->is_NoData(x, y) || pG->is_NoData(x, y) || pB->is_NoData(x, y) )
			{
				pR->Set_NoData(x, y);
				pG->Set_NoData(x, y);
				pB->Set_NoData(x, y);
			}
			else
			{
				double	r	= (pR->asDouble(x, y) - rMin) / rRange;	if( r < 0.0 ) r = 0.0; else if( r > 1.0 ) r = 1.0;
				double	g	= (pG->asDouble(x, y) - gMin) / gRange;	if( g < 0.0 ) g = 0.0; else if( g > 1.0 ) g = 1.0;
				double	b	= (pB->asDouble(x, y) - bMin) / bRange;	if( b < 0.0 ) b = 0.0; else if( b > 1.0 ) b = 1.0;

				double	h, s, i	= r + g + b;
				
				if( i <= 0.0 )
				{
					h	= 0.0;
					s	= 0.0;
				}
				else
				{
					if( r == g && g == b )			{	h	= 0.0;	}
					else if( b < r && b < g )		{	h	= (g - b) / (i - 3 * b)    ;	}
					else if( r < g && r < b )		{	h	= (b - r) / (i - 3 * r) + 1;	}
					else							{	h	= (r - g) / (i - 3 * g) + 2;	}

					if     ( 0.0 <= h && h < 1.0 )	{	s	= (i - 3 * b) / i;	}
					else if( 1.0 <= h && h < 2.0 )	{	s	= (i - 3 * r) / i;	}
					else							{	s	= (i - 3 * g) / i;	}
				}

				pR->Set_Value(x, y, i);
				pG->Set_Value(x, y, s);
				pB->Set_Value(x, y, h);
			}
		}
	}

	//-----------------------------------------------------
	double	Offset_Pan, Offset, Scale;

	if( Parameters("PAN_MATCH")->asInt() == 0 )
	{
		Offset_Pan	= pPan->Get_Min();
		Offset		= pR->Get_Min();
		Scale		= pR->Get_Range() / pPan->Get_Range();
	}
	else
	{
		Offset_Pan	= pPan->Get_Mean();
		Offset		= pR->Get_Mean();
		Scale		= pR->Get_StdDev() / pPan->Get_StdDev();
	}

	//-----------------------------------------------------
	Process_Set_Text(_TL("IHS to RGB"));

	for(y=0; y<pPan->Get_NY() && Set_Progress(y, pPan->Get_NY()); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<pPan->Get_NX(); x++)
		{
			if( !pR->is_NoData(x, y) )
			{
				double	i	= Offset + Scale * (pPan->asDouble(x, y) - Offset_Pan);
				double	s	= pG  ->asDouble(x, y);
				double	h	= pB  ->asDouble(x, y);

				double	r, g, b;

				if     ( 0.0 <= h && h < 1.0 )
				{
					r	= i * (1 + 2 * s - 3 * s * h) / 3;
					g	= i * (1 -     s + 3 * s * h) / 3;
					b	= i * (1 -     s            ) / 3;
				}
				else if( 1.0 <= h && h < 2.0 )
				{
					r	= i * (1 -     s                  ) / 3;
					g	= i * (1 + 2 * s - 3 * s * (h - 1)) / 3;
					b	= i * (1 -     s + 3 * s * (h - 1)) / 3;
				}
				else
				{
					r	= i * (1 -     s + 3 * s * (h - 2)) / 3;
					g	= i * (1 -     s                  ) / 3;
					b	= i * (1 + 2 * s - 3 * s * (h - 2)) / 3;
				}

				pR->Set_Value(x, y, rMin + r * rRange);
				pG->Set_Value(x, y, gMin + g * gRange);
				pB->Set_Value(x, y, bMin + b * bRange);
			}
		}
	}

	//-----------------------------------------------------
	return( true );
}
//---------------------------------------------------------
CSG_String CPresence_Prediction::Get_Feature(int x, int y, int i)
{
	CSG_Grid	*pFeature	= m_Features[i].pGrid;

	if( m_nNumClasses > 1 && m_Features[i].bNumeric )
	{
		return( CSG_String::Format("%d", (int)(m_nNumClasses * (pFeature->asDouble(x, y) - pFeature->Get_Min()) / pFeature->Get_Range())) );
	}

	return( SG_Get_String(pFeature->asDouble(x, y), -2) );
}
//---------------------------------------------------------
bool CPresence_Prediction::On_Execute(void)
{
	//-----------------------------------------------------
	EventSet	DL_Events ;	m_DL_Events  = &DL_Events ;
	GISTrainer	DL_Trainer;	m_DL_Trainer = &DL_Trainer;
	MaxEntModel	DL_Model  ;	m_DL_Model   = &DL_Model  ;

	m_YT_Model.clear();

	//-----------------------------------------------------
	CSG_Grid	*pPrediction	= Parameters("PREDICTION" )->asGrid();
	CSG_Grid	*pProbability	= Parameters("PROBABILITY")->asGrid();

	if( !pPrediction ->Get_Range() ) DataObject_Set_Colors(pPrediction , 11, SG_COLORS_YELLOW_GREEN);
	if( !pProbability->Get_Range() ) DataObject_Set_Colors(pProbability, 11, SG_COLORS_YELLOW_GREEN);

	m_Method		= Parameters("METHOD"      )->asInt ();
	m_nNumClasses	= Parameters("NUM_CLASSES" )->asInt ();
	m_bYT_Weights	= Parameters("YT_NUMASREAL")->asBool();

	//-----------------------------------------------------
	CSG_Array	Features;

	if( !Get_Features(Features) )
	{
		Error_Set(_TL("invalid features"));

		return( false );
	}

	//-----------------------------------------------------
	if( m_Method == 0 && SG_File_Exists(Parameters("YT_FILE_LOAD")->asString()) )
	{
		if( !Get_File(Parameters("YT_FILE_LOAD")->asString()) )
		{
			return( false );
		}
	}
	else if( !Get_Training() )
	{
		return( false );
	}

	//-----------------------------------------------------
	Process_Set_Text(_TL("prediction"));

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		#pragma omp parallel for
		for(int x=0; x<Get_NX(); x++)
		{
			int			i;
			CSG_Strings	Values;

			for(i=0; i<m_nFeatures; i++)
			{
				if( !m_Features[i].pGrid->is_NoData(x, y) )
				{
					Values.Add(Get_Feature(x, y, i));
				}
				else
				{
					break;
				}
			}

			if( Values.Get_Count() != m_nFeatures )
			{
				pPrediction ->Set_NoData(x, y);
				pProbability->Set_NoData(x, y);
			}
			else switch( m_Method )
			{
			//---------------------------------------------
			default:	// Kyoshida
				{
					ME_Sample	Sample;

					for(i=0; i<m_nFeatures; i++)
					{
						if( m_bYT_Weights && m_Features[i].bNumeric )
						{
							Sample.add_feature(m_Features[i].Name, m_Features[i].pGrid->asDouble(x, y));
						}
						else
						{
							Sample.add_feature(Values[i].b_str());
						}
					}

					vector<double> Probs	= m_YT_Model.classify(Sample);

					pPrediction ->Set_Value(x, y, m_YT_Model.get_class_id(Sample.label) == 0 ? 1 : 0);
					pProbability->Set_Value(x, y, Probs[0]);
				}
				break;

			//---------------------------------------------
			case  1:	// Dekang Lin
				{
					MaxEntEvent Event;	Event.count(1);

					for(i=0; i<m_nFeatures; i++)
					{
						Event.push_back(m_DL_Trainer->getId(Values[i].b_str()));
					}

					vector<double> Probs;

					pPrediction ->Set_Value(x, y, m_DL_Model->getProbs(Event, Probs) == 0 ? 1 : 0);
					pProbability->Set_Value(x, y, Probs[0]);
				}
				break;
			}
		}
	}

	return( true );
}