
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                    User Interface                     //
//                                                       //
//                    Program: SAGA                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                 WKSP_Layer_Classify.h                 //
//                                                       //
//          Copyright (C) 2005 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/>.                       //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//    contact:    Olaf Conrad                            //
//                Institute of Geography                 //
//                University of Goettingen               //
//                Goldschmidtstr. 5                      //
//                37077 Goettingen                       //
//                Germany                                //
//                                                       //
//    e-mail:     oconrad@saga-gis.org                   //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#ifndef _HEADER_INCLUDED__SAGA_GUI__WKSP_Layer_Classify_H
#define _HEADER_INCLUDED__SAGA_GUI__WKSP_Layer_Classify_H


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

//---------------------------------------------------------
#include <wx/string.h>

#include <saga_api/saga_api.h>


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

//---------------------------------------------------------
enum
{
	CLASSIFY_SINGLE	= 0,
	CLASSIFY_LUT,
	CLASSIFY_DISCRETE,
	CLASSIFY_GRADUATED,
	CLASSIFY_OVERLAY,
	CLASSIFY_RGB
};

//---------------------------------------------------------
enum
{
	LUT_COLOR		= 0,
	LUT_TITLE,
	LUT_DESCRIPTION,
	LUT_MIN,
	LUT_MAX
};

//---------------------------------------------------------
enum
{
	SHADE_MODE_DSC_GREY	= 0,
	SHADE_MODE_ASC_GREY,
	SHADE_MODE_DSC_CYAN,
	SHADE_MODE_ASC_CYAN,
	SHADE_MODE_DSC_MAGENTA,
	SHADE_MODE_ASC_MAGENTA,
	SHADE_MODE_DSC_YELLOW,
	SHADE_MODE_ASC_YELLOW
};


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

//---------------------------------------------------------
class CSG_Scaler
{
public:
	typedef enum EInterval
	{
		LINEAR = 0, INCREASING, DECREASING, EQUALIZE
	}
	TMode;

	//-----------------------------------------------------
	CSG_Scaler(void);

								CSG_Scaler			(const CSG_Scaler &Scaler);
	bool						Create				(const CSG_Scaler &Scaler);

								CSG_Scaler			(double Minimum, double Maximum, int Mode = 0, double LogScale = 1.);
	bool						Create				(double Minimum, double Maximum, int Mode = 0, double LogScale = 1.);

	CSG_Scaler &				operator =			(const CSG_Scaler &Scaler);

	//-----------------------------------------------------
	bool						Set_Range			(double Minimum, double Maximum);
	double						Get_Range			(void)	const	{	return(             m_Range );	}
	double						Get_Minimum			(void)	const	{	return( m_Minimum           );	}
	double						Get_Maximum			(void)	const	{	return( m_Minimum + m_Range );	}

	bool						Set_Mode			(int Mode);
	TMode						Get_Mode			(void)	const	{	return( m_Mode              );	}

	bool						Set_LogScale		(double LogScale);
	double						Get_LogScale		(void)	const	{	return( m_LogScale[0]       );	}

	//-----------------------------------------------------
	bool						Set_Linear			(CSG_Table *pTable, int Field, double Minimum, double Maximum  , int Mode = 0, double LogScale = 1.);
	bool						Set_StdDev			(CSG_Table *pTable, int Field, double StdDev, bool bKeepInRange, int Mode = 0, double LogScale = 1.);
	bool						Set_Percentile		(CSG_Table *pTable, int Field, double Minimum, double Maximum  , int Mode = 0, double LogScale = 1.);

	bool						Set_Linear			(CSG_Data_Object *pObject    , double Minimum, double Maximum  , int Mode = 0, double LogScale = 1.);
	bool						Set_StdDev			(CSG_Data_Object *pObject    , double StdDev, bool bKeepInRange, int Mode = 0, double LogScale = 1.);
	bool						Set_Percentile		(CSG_Data_Object *pObject    , double Minimum, double Maximum  , int Mode = 0, double LogScale = 1.);

	bool						Set_Histogram		(CSG_Table *pTable, int Field, int Normalize = -1, double Scale = 1.);
	bool						Set_Histogram		(CSG_Data_Object *pObject    );

	//-----------------------------------------------------
	double						to_Relative			(double Value)	const
	{
		if( m_Range > 0. )
		{
			switch( m_Mode )
			{
			default        : return( (Value - m_Minimum) / m_Range );
			case INCREASING: Value = (Value - m_Minimum) / m_Range; return( Value <= 0. ? 0. :      (log(1. + m_LogScale[1] * (     Value)) / m_LogScale[2]) );
			case DECREASING: Value = (Value - m_Minimum) / m_Range; return( Value >= 1. ? 1. : 1. - (log(1. + m_LogScale[1] * (1. - Value)) / m_LogScale[2]) );
			case EQUALIZE  : return( m_Histogram.Get_Quantile_Value(Value) );
			}
		}

		return( 0. );
	}

	//-----------------------------------------------------
	double						from_Relative		(double Value)	const
	{
		if( m_Range > 0. )
		{
			switch( m_Mode )
			{
			default        :                                                                          break;
			case INCREASING: Value =      ((exp(m_LogScale[2] * (     Value)) - 1.) / m_LogScale[1]); break;
			case DECREASING: Value = 1. - ((exp(m_LogScale[2] * (1. - Value)) - 1.) / m_LogScale[1]); break;
			case EQUALIZE  : return( m_Histogram.Get_Quantile(Value) );
			}

			return( m_Minimum + (m_Range * Value) );
		}

		return( m_Minimum );
	}


protected:

	TMode						m_Mode = LINEAR;

	CSG_Histogram				m_Histogram;

	double						m_Minimum = 0., m_Range = 0., m_LogScale[3] = { 1., 10., 2.302585093 };


	bool						_Update				(CSG_Table *pTable, int Field, double Minimum, double Maximum, int Mode, double LogScale);
	bool						_Update				(CSG_Data_Object *pObject    , double Minimum, double Maximum, int Mode, double LogScale);

};


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

//---------------------------------------------------------
class CWKSP_Layer_Classify
{
public: ///////////////////////////////////////////////////
	CWKSP_Layer_Classify(class CWKSP_Layer *pLayer);
	virtual ~CWKSP_Layer_Classify(void);

	bool						Create					(CSG_Table *pLUT, CSG_Colors *pColors);

	bool						Update					(void);

	bool						Set_Mode				(int Mode);
	int							Get_Mode				(void) const { return( m_Mode         ); }

	bool						Set_NoData_Color		(int Color);
	int							Get_NoData_Color		(void) const { return( m_NoData_Color ); }

	bool						Set_Single_Color		(int Color);
	int							Get_Single_Color		(void) const { return( m_Single_Color ); }


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

	//-----------------------------------------------------
	bool						Set_Class_Count			(int Count);

	int							Get_Class_Count			(void)	const
	{
		switch( m_Mode )
		{
		case CLASSIFY_SINGLE   : default:
			return( 1 );

		case CLASSIFY_LUT      :
			return( m_pLUT->Get_Count() );

		case CLASSIFY_GRADUATED:
		case CLASSIFY_OVERLAY  :
			return( m_Count );

		case CLASSIFY_DISCRETE :
			return( m_pColors->Get_Count() );
		}
	}

	//-----------------------------------------------------
	int							Get_Class				(double Value)
	{
		switch( m_Mode )
		{
		case CLASSIFY_SINGLE   : default:
			return( 0 );

		case CLASSIFY_LUT      :
			return( _LUT_Get_Class(Value) );

		case CLASSIFY_GRADUATED:
		case CLASSIFY_DISCRETE :
		case CLASSIFY_OVERLAY  :
			return( _METRIC_Get_Class(Value) );
		}
	}

	int							Get_Class				(const CSG_String &Value)
	{
		if( m_Mode == CLASSIFY_LUT )
		{
			return( _LUT_Get_Class(Value) );
		}

		return( Get_Class(Value.asDouble()) );
	}

	//-----------------------------------------------------
	double						Get_Class_Value_Minimum	(int Class);
	double						Get_Class_Value_Center	(int Class);
	double						Get_Class_Value_Maximum	(int Class);
	wxString					Get_Class_Name			(int Class);
	wxString					Get_Class_Name_byValue	(double          Value);
	wxString					Get_Class_Name_byValue	(const wxString &Value);


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

	//-----------------------------------------------------
	CSG_Colors					Get_Class_Colors		(void)	const;

	//-----------------------------------------------------
	bool						Get_Class_Color			(int Class, int &Color)	const
	{
		switch( m_Mode )
		{
		case CLASSIFY_SINGLE   : default:
			Color = m_Single_Color;
			break;

		case CLASSIFY_LUT      :
			if( Class < 0 || Class >= m_pLUT->Get_Count() )
			{
				Color = m_NoData_Color;

				return( false );
			}

			Color = m_pLUT->Get_Record(Class)->asInt(LUT_COLOR);
			break;


		case CLASSIFY_DISCRETE :
			Color = m_pColors->Get_Color(Class < 0 ? 0 : Class >= m_pColors->Get_Count() ? m_pColors->Get_Count() - 1 : Class);
			break;

		case CLASSIFY_GRADUATED:
		case CLASSIFY_OVERLAY  :
			Get_Class_Color_byValue(m_Stretch.from_Relative(Class / (double)m_Count), Color);
			break;
		}

		return( true );
	}

	//-----------------------------------------------------
	int							Get_Class_Color			(int Class)	const
	{
		int Color; return( Get_Class_Color(Class, Color) ? Color : 0 );
	}

	//-----------------------------------------------------
	bool						Get_Class_Color_byValue	(double Value, int &Color)	const
	{
		switch( m_Mode )
		{
		case CLASSIFY_SINGLE   : default:
			{
				return( Get_Class_Color(0, Color) );
			}

		case CLASSIFY_LUT      :
			{
				return( Get_Class_Color(_LUT_Get_Class(Value), Color) );
			}

		case CLASSIFY_DISCRETE :
			{
				return( Get_Class_Color(_METRIC_Get_Class(Value), Color) );
			}

		case CLASSIFY_GRADUATED:
			{
				Color = m_pColors->Get_Interpolated(m_Stretch.to_Relative(Value) * (m_pColors->Get_Count() - 1));

				return( true );
			}

		case CLASSIFY_OVERLAY  :
			{
				int c = (int)(255. * m_Stretch.to_Relative(Value)); if( c < 0 ) { c = 0; } else if( c > 255 ) { c = 255; }

				Color = SG_GET_RGB(c, c, c);

				return( true );
			}

		case CLASSIFY_RGB      :
			{
				Color = (int)Value;
			
				return( true );
			}
		}
	}

	bool						Get_Class_Color_byValue	(const CSG_String &Value, int &Color)	const
	{
		if( m_Mode == CLASSIFY_LUT )
		{
			return( Get_Class_Color(_LUT_Get_Class(Value), Color) );
		}
		
		return( Get_Class_Color(Value.asDouble(), Color) );
	}

	//-----------------------------------------------------
	int							Get_Class_Color_byValue	(double Value)	const
	{
		int Color; return( Get_Class_Color_byValue(Value, Color) ? Color : 0 );
	}

	int							Get_Class_Color_byValue	(const CSG_String &Value)	const
	{
		int Color; return( Get_Class_Color_byValue(Value, Color) ? Color : 0 );
	}


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

	//-----------------------------------------------------
	CSG_Colors &					Get_Colors					(void)               { return( *m_pColors ); }
	const CSG_Colors &				Get_Colors					(void)         const { return( *m_pColors ); }

	CSG_Scaler &					Get_Stretch					(void)               { return( m_Stretch ); }
	const CSG_Scaler &				Get_Stretch					(void)         const { return( m_Stretch ); }

	bool							Set_Stretch					(double Minimum, double Maximum, int Mode = 0, double LogScale = 1.) { return( m_Stretch.Create(Minimum, Maximum, Mode, LogScale) ); }

	double							Get_Stretch_Minimum			(void)         const { return( m_Stretch.Get_Minimum () ); }
	double							Get_Stretch_Maximum			(void)         const { return( m_Stretch.Get_Maximum () ); }
	double							Get_Stretch_Range			(void)         const { return( m_Stretch.Get_Range   () ); }
	int								Get_Stretch_Mode			(void)         const { return( m_Stretch.Get_Mode    () ); }
	double							Get_Stretch_LogScale		(void)         const { return( m_Stretch.Get_LogScale() ); }

	double							Get_Stretch_to_Relative		(double Value) const { return( m_Stretch.  to_Relative(Value) ); }
	double							Get_Stretch_from_Relative	(double Value) const { return( m_Stretch.from_Relative(Value) ); }


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

	//-----------------------------------------------------
	bool							Histogram_Update		(void);

	const CSG_Histogram &			Histogram_Get			(void)	const	{	return( m_Histogram );	}

	const CSG_Simple_Statistics &	Statistics_Get			(void)	const	{	return( m_Statistics );	}


protected: ////////////////////////////////////////////////

	int								m_Mode, m_Count, m_Single_Color, m_NoData_Color;

	CSG_Scaler						m_Stretch;

	CSG_Histogram					m_Histogram;

	CSG_Simple_Statistics			m_Statistics;

	CSG_Colors						*m_pColors = NULL;

	CSG_Table						*m_pLUT    = NULL;

	class CWKSP_Layer				*m_pLayer  = NULL;


	//-----------------------------------------------------
	int								_LUT_Cmp_Class			(double            Value, int Class)	const;
	int								_LUT_Get_Class			(double            Value           )	const;

	int								_LUT_Cmp_Class			(const CSG_String &Value, int Class)	const;
	int								_LUT_Get_Class			(const CSG_String &Value           )	const;

	int								_METRIC_Get_Class		(double Value)	const
	{
		if( Value < m_Stretch.Get_Minimum() )
		{
			return( -1 );
		}

		if( Value > m_Stretch.Get_Maximum() )
		{
			return( Get_Class_Count() );
		}

		int Class = (int)(m_Stretch.to_Relative(Value) * Get_Class_Count());

		return( Class < 0 ? 0 : Class < Get_Class_Count() ? Class : Get_Class_Count() - 1 );
	}


	//-----------------------------------------------------
	bool							_Histogram_Update		(CSG_Data_Object *pObject);
	bool							_Histogram_Update		(CSG_Table *pTable, int Field, int Normalize = -1, double Scale = 1.);

};


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

//---------------------------------------------------------
#endif // #ifndef _HEADER_INCLUDED__SAGA_GUI__WKSP_Layer_Classify_H
