
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                    User Interface                     //
//                                                       //
//                    Program: SAGA                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//                WKSP_Layer_Classify.cpp                //
//                                                       //
//          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                   //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "helper.h"

#include "wksp_layer.h"
#include "wksp_layer_classify.h"
#include "wksp_grid.h"
#include "wksp_grids.h"
#include "wksp_shapes.h"
#include "wksp_pointcloud.h"
#include "wksp_tin.h"


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

//---------------------------------------------------------
CSG_Scaler::CSG_Scaler(void)
{
	Create(0., 0.);
}

//-----------------------------------------------------
CSG_Scaler::CSG_Scaler(const CSG_Scaler &Scaler)
{
	Create(Scaler);
}

//-----------------------------------------------------
CSG_Scaler::CSG_Scaler(double Minimum, double Maximum, int Mode, double LogScale)
{
	Create(Minimum, Maximum, Mode, LogScale);
}

//-----------------------------------------------------
bool CSG_Scaler::Create(double Minimum, double Maximum, int Mode, double LogScale)
{
	return( Set_Range(Minimum, Maximum) && Set_Mode(Mode) && Set_LogScale(LogScale) );
}

//-----------------------------------------------------
bool CSG_Scaler::Create(const CSG_Scaler &Scaler)
{
	return( Create(Scaler.Get_Minimum(), Scaler.Get_Maximum(), Scaler.Get_Mode(), Get_LogScale()) );
}

//-----------------------------------------------------
CSG_Scaler & CSG_Scaler::operator =	(const CSG_Scaler &Scaler)
{
	Create(Scaler);

	return( *this );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_Range(double Minimum, double Maximum)
{
	m_Minimum = Minimum < Maximum ? Minimum : Maximum;
	m_Range   = Minimum < Maximum ? Maximum - Minimum : Minimum - Maximum;

	return( m_Range > 0. );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_LogScale(double LogScale)
{
	if( LogScale >= 0. )
	{
		m_LogScale[0] = LogScale;
		m_LogScale[1] = pow(10., m_LogScale[0]);
		m_LogScale[2] = log(1. + m_LogScale[1]);

		return( true );
	}

	return( false );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_Mode(int Mode)
{
	switch( Mode )
	{
	default: m_Mode = LINEAR    ; break;
	case  1: m_Mode = INCREASING; break;
	case  2: m_Mode = DECREASING; break;
	case  3: m_Mode = EQUALIZE  ; break;
	}

	return( true );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_Linear(CSG_Table *pTable, int Field, double Minimum, double Maximum, int Mode, double LogScale)
{
	Minimum = pTable->Get_Minimum(Field) + pTable->Get_Range(Field) * Minimum / 100.;
	Maximum = pTable->Get_Minimum(Field) + pTable->Get_Range(Field) * Maximum / 100.;

	return( _Update(pTable, Field, Minimum, Maximum, Mode, LogScale) );
}

bool CSG_Scaler::Set_StdDev(CSG_Table *pTable, int Field, double StdDev, bool bKeepInRange, int Mode, double LogScale)
{
	double Minimum = pTable->Get_Mean(Field) - StdDev * pTable->Get_StdDev(Field); if( bKeepInRange && Minimum < pTable->Get_Minimum(Field) ) { Minimum = pTable->Get_Minimum(Field); }
	double Maximum = pTable->Get_Mean(Field) + StdDev * pTable->Get_StdDev(Field); if( bKeepInRange && Maximum > pTable->Get_Maximum(Field) ) { Maximum = pTable->Get_Maximum(Field); }

	return( _Update(pTable, Field, Minimum, Maximum, Mode, LogScale) );
}

bool CSG_Scaler::Set_Percentile(CSG_Table *pTable, int Field, double Minimum, double Maximum, int Mode, double LogScale)
{
	Minimum = pTable->Get_Histogram(Field).Get_Percentile(Minimum);
	Maximum = pTable->Get_Histogram(Field).Get_Percentile(Maximum);

	return( _Update(pTable, Field, Minimum, Maximum, Mode, LogScale) );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_Linear(CSG_Data_Object *pObject, double Minimum, double Maximum, int Mode, double LogScale)
{
	if( pObject->asGrid () )
	{
		Minimum = pObject->asGrid ()->Get_Min() + pObject->asGrid ()->Get_Range() * Minimum / 100.;
		Maximum = pObject->asGrid ()->Get_Min() + pObject->asGrid ()->Get_Range() * Maximum / 100.;

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	if( pObject->asGrids() )
	{
		Minimum = pObject->asGrids()->Get_Min() + pObject->asGrids()->Get_Range() * Minimum / 100.;
		Maximum = pObject->asGrids()->Get_Min() + pObject->asGrids()->Get_Range() * Maximum / 100.;

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	return( false );
}

bool CSG_Scaler::Set_StdDev(CSG_Data_Object *pObject, double StdDev, bool bKeepInRange, int Mode, double LogScale)
{
	if( pObject->asGrid () )
	{
		double Minimum = pObject->asGrid ()->Get_Mean() - StdDev * pObject->asGrid ()->Get_StdDev(); if( bKeepInRange && Minimum < pObject->asGrid ()->Get_Min() ) { Minimum = pObject->asGrid ()->Get_Min(); }
		double Maximum = pObject->asGrid ()->Get_Mean() + StdDev * pObject->asGrid ()->Get_StdDev(); if( bKeepInRange && Maximum > pObject->asGrid ()->Get_Max() ) { Maximum = pObject->asGrid ()->Get_Max(); }

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	if( pObject->asGrids() )
	{
		double Minimum = pObject->asGrids()->Get_Mean() - StdDev * pObject->asGrids()->Get_StdDev(); if( bKeepInRange && Minimum < pObject->asGrids()->Get_Min() ) { Minimum = pObject->asGrids()->Get_Min(); }
		double Maximum = pObject->asGrids()->Get_Mean() + StdDev * pObject->asGrids()->Get_StdDev(); if( bKeepInRange && Maximum > pObject->asGrids()->Get_Max() ) { Maximum = pObject->asGrids()->Get_Max(); }

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	return( false );
}

bool CSG_Scaler::Set_Percentile(CSG_Data_Object *pObject, double Minimum, double Maximum, int Mode, double LogScale)
{
	if( pObject->asGrid () )
	{
		Minimum = pObject->asGrid ()->Get_Percentile(Minimum);
		Maximum = pObject->asGrid ()->Get_Percentile(Maximum);

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	if( pObject->asGrids() )
	{
		Minimum = pObject->asGrids()->Get_Percentile(Minimum);
		Maximum = pObject->asGrids()->Get_Percentile(Maximum);

		return( _Update(pObject, Minimum, Maximum, Mode, LogScale) );
	}

	return( false );
}

//-----------------------------------------------------
bool CSG_Scaler::Set_Histogram(CSG_Table *pTable, int Field, int Normalize, double Scale)
{
	return( m_Mode == EQUALIZE && m_Histogram.Create(256, pTable, Field, Get_Minimum(), Get_Maximum(), pTable->Get_Max_Samples(), Normalize, Scale) );
}

bool CSG_Scaler::Set_Histogram(CSG_Data_Object *pObject)
{
	if( pObject && pObject->asGrid () ) { return( m_Mode == EQUALIZE && m_Histogram.Create(256, pObject->asGrid (), Get_Minimum(), Get_Maximum(), pObject->Get_Max_Samples()) ); }
	if( pObject && pObject->asGrids() ) { return( m_Mode == EQUALIZE && m_Histogram.Create(256, pObject->asGrids(), Get_Minimum(), Get_Maximum(), pObject->Get_Max_Samples()) ); }

	return( false );
}

//-----------------------------------------------------
bool CSG_Scaler::_Update(CSG_Table *pTable, int Field, double Minimum, double Maximum, int Mode, double LogScale)
{
	if( Mode == EQUALIZE && !m_Histogram.Create(256, pTable, Field, Minimum, Maximum, pTable->Get_Max_Samples()) )
	{
		return( false );
	}

	return( Create(Minimum, Maximum, Mode, LogScale) );
}

//-----------------------------------------------------
bool CSG_Scaler::_Update(CSG_Data_Object *pObject, double Minimum, double Maximum, int Mode, double LogScale)
{
	if( Mode == EQUALIZE && pObject->asGrid () && !m_Histogram.Create(256, pObject->asGrid (), Minimum, Maximum, pObject->Get_Max_Samples()) )
	{
		return( false );
	}

	if( Mode == EQUALIZE && pObject->asGrids() && !m_Histogram.Create(256, pObject->asGrids(), Minimum, Maximum, pObject->Get_Max_Samples()) )
	{
		return( false );
	}

	return( Create(Minimum, Maximum, Mode, LogScale) );
}


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

//---------------------------------------------------------
CWKSP_Layer_Classify::CWKSP_Layer_Classify(CWKSP_Layer *pLayer)
{
	m_pLayer  = pLayer;

	m_pColors = NULL;
	m_pLUT    = NULL;

	m_Count   = m_pLayer->Get_Object()->asTable(true) && !m_pLayer->Get_Object()->asPointCloud() ? 16 : 64;

	m_Mode    = CLASSIFY_SINGLE;

	m_NoData_Color = SG_COLOR_GREY_LIGHT;
	m_Single_Color = SG_Color_Get_Random();
}

//---------------------------------------------------------
CWKSP_Layer_Classify::~CWKSP_Layer_Classify(void)
{}


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

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Create(CSG_Table *pLUT, CSG_Colors *pColors)
{
	m_pLUT = pLUT; m_pColors = pColors;

	if( m_pLUT && m_pLUT->Get_Count() < 1 )
	{
		CSG_Colors Colors(11, SG_COLORS_RAINBOW); int Color = (int)CSG_Random::Get_Uniform(0, Colors.Get_Count());

		#define ADD_LUT_CLASS(name, desc, min, max) { CSG_Table_Record &r = *m_pLUT->Add_Record();\
			r.Set_Value(LUT_TITLE, name); r.Set_Value(LUT_DESCRIPTION, desc); r.Set_Value(LUT_MIN, min); r.Set_Value(LUT_MAX, max);\
			r.Set_Value(LUT_COLOR, Colors[Color]); Color = (Color + Colors.Get_Count() / 2) % Colors.Get_Count();\
		}

		ADD_LUT_CLASS(_TL("Class 1"), _TL( "First Class"), 0., 1.);
		ADD_LUT_CLASS(_TL("Class 2"), _TL("Second Class"), 1., 2.);
	}

	return( Update() );
}

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Update(void)
{
	if( m_pLUT )
	{
		m_pLUT->Set_Index(LUT_MIN, TABLE_INDEX_Ascending);
	}

	switch( m_pLayer->Get_Type() )
	{
	case WKSP_ITEM_Grid      :
		m_Stretch.Set_Histogram(((CWKSP_Grid  *)m_pLayer)->Get_Grid ());
		break;

	case WKSP_ITEM_Grids     :
		if( m_Mode == CLASSIFY_OVERLAY )
		{
			m_Stretch.Set_Histogram(((CWKSP_Grids *)m_pLayer)->Get_Grids());
		}
		else
		{
			m_Stretch.Set_Histogram(((CWKSP_Grids *)m_pLayer)->Get_Grid ());
		}
		break;

	#define HISTOGRAM_UPDATE(pLayer) m_Stretch.Set_Histogram((pLayer)->Get_Object()->asTable(true), (pLayer)->Get_Field_Value (), (pLayer)->Get_Field_Normal(), (pLayer)->Get_Scale_Normal())

	case WKSP_ITEM_Shapes    : HISTOGRAM_UPDATE((CWKSP_Shapes     *)m_pLayer); break;
	case WKSP_ITEM_PointCloud: HISTOGRAM_UPDATE((CWKSP_PointCloud *)m_pLayer); break;
	case WKSP_ITEM_TIN       : HISTOGRAM_UPDATE((CWKSP_TIN        *)m_pLayer); break;

	#undef HISTOGRAM_UPDATE

	default: break;
	}

	return( true );
}


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

//---------------------------------------------------------
double CWKSP_Layer_Classify::Get_Class_Value_Minimum(int iClass)
{
	switch( m_Mode )
	{
	case CLASSIFY_LUT      :
		if( iClass >= 0 && iClass < m_pLUT->Get_Count() )
		{
			return( m_pLUT->Get_Record(iClass)->asDouble(LUT_MIN) );
		}
		break;

	case CLASSIFY_GRADUATED:
	case CLASSIFY_DISCRETE :
	case CLASSIFY_OVERLAY  :
		if( m_Stretch.Get_Range() > 0. )
		{
			return( m_Stretch.from_Relative(iClass / (double)Get_Class_Count()) );
		}
		break;

	default: break;
	}

	return( m_Stretch.Get_Minimum() );
}

double CWKSP_Layer_Classify::Get_Class_Value_Maximum(int iClass)
{
	switch( m_Mode )
	{
	case CLASSIFY_LUT      :
		if( iClass >= 0 && iClass < m_pLUT->Get_Count() )
		{
			return( m_pLUT->Get_Record(iClass)->asDouble(LUT_MAX) );
		}
		break;

	case CLASSIFY_GRADUATED:
	case CLASSIFY_DISCRETE :
	case CLASSIFY_OVERLAY  :
		if( m_Stretch.Get_Range() > 0. )
		{
			return( m_Stretch.from_Relative((1. + iClass) / (double)Get_Class_Count()) );
		}
		break;

	default: break;
	}

	return( m_Stretch.Get_Maximum() );
}

double CWKSP_Layer_Classify::Get_Class_Value_Center(int iClass)
{
	switch( m_Mode )
	{
	case CLASSIFY_LUT      :
		if( iClass >= 0 && iClass < m_pLUT->Get_Count() )
		{
			return( 0.5 * (m_pLUT->Get_Record(iClass)->asDouble(LUT_MIN) + m_pLUT->Get_Record(iClass)->asDouble(LUT_MAX)) );
		}
		break;

	case CLASSIFY_GRADUATED:
	case CLASSIFY_DISCRETE :
	case CLASSIFY_OVERLAY  :
		if( m_Stretch.Get_Range() > 0. )
		{
			return( m_Stretch.from_Relative((0.5 + iClass) / (double)Get_Class_Count()) );
		}
		break;

	default: break;
	}

	return( m_Stretch.Get_Minimum() + 0.5 * m_Stretch.Get_Range() );
}

//---------------------------------------------------------
wxString CWKSP_Layer_Classify::Get_Class_Name(int iClass)
{
	CSG_String s;

	switch( m_Mode )
	{
	case CLASSIFY_LUT      :
		if( iClass >= 0 && iClass < m_pLUT->Get_Count() )
		{
			s.Printf(SG_T("%s"), m_pLUT->Get_Record(iClass)->asString(LUT_TITLE));
		}
		break;

	case CLASSIFY_GRADUATED:
	case CLASSIFY_DISCRETE :
	case CLASSIFY_OVERLAY  :
		s = SG_Get_String(Get_Class_Value_Minimum(iClass), -2) + SG_T(" < ")
		  + SG_Get_String(Get_Class_Value_Maximum(iClass), -2);
		break;

	default: break;
	}

	return( s.c_str() );
}

//---------------------------------------------------------
wxString CWKSP_Layer_Classify::Get_Class_Name_byValue(double Value)
{
	return( Get_Class_Name(Get_Class(Value)) );
}

wxString CWKSP_Layer_Classify::Get_Class_Name_byValue(const wxString &Value)
{
	return( SG_Data_Type_is_Numeric(m_pLUT->Get_Field_Type(LUT_MIN))
		? Get_Class_Name(Get_Class(CSG_String(&Value).asDouble()))
		: Get_Class_Name(Get_Class(CSG_String(&Value)))
	);
}


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

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Set_Mode(int Mode)
{
	m_Mode = Mode;

	return( true );
}

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Set_NoData_Color(int Color)
{
	m_NoData_Color = Color;

	return( true );
}

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Set_Single_Color(int Color)
{
	m_Single_Color = Color;

	return( true );
}


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

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Set_Class_Count(int Count)
{
	if( Count > 0 && Count != m_Count )
	{
		m_Count	= Count;

		return( Histogram_Update() );
	}

	return( false );
}

//---------------------------------------------------------
CSG_Colors CWKSP_Layer_Classify::Get_Class_Colors(void)	const
{
	CSG_Colors Colors(Get_Class_Count());

	for(int i=0; i<Get_Class_Count(); i++)
	{
		Colors[i] = Get_Class_Color(i);
	}

	return( Colors );
}


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

//---------------------------------------------------------
inline int CWKSP_Layer_Classify::_LUT_Cmp_Class(double Value, int iClass)	const
{
	CSG_Table_Record *pClass = m_pLUT->Get_Record_byIndex(iClass);

	double min	= pClass->asDouble(LUT_MIN);

	if( Value == min ) { return(  0 ); }
	if( Value  < min ) { return(  1 ); }

	double	max	= pClass->asDouble(LUT_MAX);

	if( max    < min ) { return( -1 ); }
	if( Value  < max ) { return(  0 ); }

	return( iClass == m_pLUT->Get_Count() - 1 && Value == max ? 0 : -1 );
}

//---------------------------------------------------------
int CWKSP_Layer_Classify::_LUT_Get_Class(double Value)	const
{
	if( m_pLUT->Get_Count() > 0 )
	{
		if( m_pLUT->Get_Index_Field(0) != LUT_MIN || m_pLUT->Get_Index_Order(0) != TABLE_INDEX_Ascending )
		{
			m_pLUT->Set_Index(LUT_MIN, TABLE_INDEX_Ascending);
		}

		int a = 0, b = m_pLUT->Get_Count() - 1;

		while( a < b )
		{
			int i = a + (b - a) / 2; int c = _LUT_Cmp_Class(Value, i);

			if( c > 0 )
			{
				b = b > i ? i : b - 1;
			}
			else if( c < 0 )
			{
				a = a < i ? i : a + 1;
			}
			else
			{
				return( m_pLUT->Get_Record_byIndex(i)->Get_Index() );
			}
		}

		if( a != b && _LUT_Cmp_Class(Value, a) == 0 )
		{
			return( m_pLUT->Get_Record_byIndex(a)->Get_Index() );
		}

		if( Value >= m_pLUT->Get_Record_byIndex(b)->asDouble(LUT_MIN)
		&&  Value <= m_pLUT->Get_Record_byIndex(b)->asDouble(LUT_MAX) ) // less or equal comparison for last class
		{
			return( m_pLUT->Get_Record_byIndex(b)->Get_Index() );
		}
	}

	return( -1 );
}

//---------------------------------------------------------
inline int CWKSP_Layer_Classify::_LUT_Cmp_Class(const CSG_String &Value, int iClass)	const
{
	CSG_Table_Record *pClass = m_pLUT->Get_Record_byIndex(iClass);

	int c = Value.Cmp(pClass->asString(LUT_MIN));

	if( c < 0 )
	{
		return( 1 );
	}

	if( c > 0 && Value.Cmp(pClass->asString(LUT_MAX)) > 0 )
	{
		return( -1 );
	}

	return( 0 );
}

//---------------------------------------------------------
int CWKSP_Layer_Classify::_LUT_Get_Class(const CSG_String &Value)	const
{
	if( m_pLUT->Get_Count() > 0 )
	{
		if( m_pLUT->Get_Index_Field(0) != LUT_MIN || m_pLUT->Get_Index_Order(0) != TABLE_INDEX_Ascending )
		{
			m_pLUT->Set_Index(LUT_MIN, TABLE_INDEX_Ascending);
		}

		int a = 0, b = m_pLUT->Get_Count() - 1;

		while( a < b )
		{
			int i = a + (b - a) / 2; int c = _LUT_Cmp_Class(Value, i);

			if( c > 0 )
			{
				b = b > i ? i : b - 1;
			}
			else if( c < 0 )
			{
				a = a < i ? i : a + 1;
			}
			else
			{
				return( m_pLUT->Get_Record_byIndex(i)->Get_Index() );
			}
		}

		if( a != b && _LUT_Cmp_Class(Value, a) == 0 )
		{
			return( m_pLUT->Get_Record_byIndex(a)->Get_Index() );
		}

		if( _LUT_Cmp_Class(Value, b) == 0 )
		{
			return( m_pLUT->Get_Record_byIndex(b)->Get_Index() );
		}
	}

	return( -1 );
}


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

//---------------------------------------------------------
bool CWKSP_Layer_Classify::Histogram_Update(void)
{
	m_Statistics.Create();

	if( Get_Class_Count() < 1 )
	{
		m_Histogram.Destroy();

		return( false );
	}

	//-----------------------------------------------------
	STATUSBAR_Set_Text(_TL("Build Histogram..."));

	m_Histogram.Create(Get_Class_Count(), 0., Get_Class_Count() - 1.);

	switch( m_pLayer->Get_Type() )
	{
	case WKSP_ITEM_Grid      :
		_Histogram_Update(((CWKSP_Grid  *)m_pLayer)->Get_Grid());
		break;

	case WKSP_ITEM_Grids     :
		if( m_Mode == CLASSIFY_OVERLAY )
		{
			_Histogram_Update(((CWKSP_Grids *)m_pLayer)->Get_Grids());
		}
		else
		{
			_Histogram_Update(((CWKSP_Grids *)m_pLayer)->Get_Grid ());
		}
		break;

	#define HISTOGRAM_UPDATE(pLayer) _Histogram_Update((pLayer)->Get_Object()->asTable(true), (pLayer)->Get_Field_Value (), (pLayer)->Get_Field_Normal(), (pLayer)->Get_Scale_Normal())

	case WKSP_ITEM_Shapes    : HISTOGRAM_UPDATE((CWKSP_Shapes     *)m_pLayer); break;
	case WKSP_ITEM_PointCloud: HISTOGRAM_UPDATE((CWKSP_PointCloud *)m_pLayer); break;
	case WKSP_ITEM_TIN       : HISTOGRAM_UPDATE((CWKSP_TIN        *)m_pLayer); break;

	#undef HISTOGRAM_UPDATE

	default: break;
	}

	m_Histogram.Update();

	PROCESS_Set_Okay();

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

//---------------------------------------------------------
bool CWKSP_Layer_Classify::_Histogram_Update(CSG_Data_Object *pObject)
{
	#define isNoData(i) (pObject->asGrid() ? pObject->asGrid()->is_NoData((sLong)i) : pObject->asGrids()->is_NoData((sLong)i))
	#define asDouble(i) (pObject->asGrid() ? pObject->asGrid()-> asDouble((sLong)i) : pObject->asGrids()-> asDouble((sLong)i))

	sLong nCells = pObject->asGrid() ? pObject->asGrid()->Get_NCells() : pObject->asGrids()->Get_NCells();

	if( pObject->Get_Max_Samples() > 0 && pObject->Get_Max_Samples() < nCells )
	{
		double d = (double)nCells / (double)pObject->Get_Max_Samples();

		for(double i=0; i<(double)nCells && PROGRESSBAR_Set_Position(i, (double)nCells); i+=d)
		{
			if( !isNoData(i) )
			{
				m_Histogram	+= Get_Class(asDouble(i));
			}
		}

		if( m_Histogram.Update() && m_Histogram.Get_Element_Count() < (size_t)pObject->Get_Max_Samples() )	// any no-data cells ?
		{
			d *= (double)m_Histogram.Get_Element_Count() / (double)pObject->Get_Max_Samples();
		}

		m_Histogram.Scale_Element_Count(d);

		m_Statistics = pObject->asGrid() ? pObject->asGrid()->Get_Statistics() : pObject->asGrids()->Get_Statistics();

		return( true );
	}

	for(sLong i=0; i<nCells && PROGRESSBAR_Set_Position((double)i, (double)nCells); i++)
	{
		if( !isNoData(i) )
		{
			m_Histogram	+= Get_Class(asDouble(i));
		}
	}

	m_Statistics = pObject->asGrid() ? pObject->asGrid()->Get_Statistics() : pObject->asGrids()->Get_Statistics();

	#undef isNoData
	#undef asDouble

	return( true );
}

//---------------------------------------------------------
bool CWKSP_Layer_Classify::_Histogram_Update(CSG_Table *pTable, int Field, int Normalize, double Scale)
{
	if( !pTable || Field < 0 || Field >= pTable->Get_Field_Count() )
	{
		return( false );
	}

	//-----------------------------------------------------
	if( pTable->Get_Max_Samples() > 0 && pTable->Get_Max_Samples() < pTable->Get_Count() )
	{
		double d = (double)pTable->Get_Count() / (double)pTable->Get_Max_Samples();

		if( m_Mode == CLASSIFY_LUT && !SG_Data_Type_is_Numeric(m_pLUT->Get_Field_Type(LUT_MIN)) )
		{
			CSG_String Value;

			for(double i=0; i<(double)pTable->Get_Count() && PROGRESSBAR_Set_Position(i, (double)pTable->Get_Count()); i+=d)
			{
				if( pTable->Get_Value((sLong)i, Field, Value) )
				{
					m_Histogram += Get_Class(Value);
				}
			}
		}
		else
		{
			double Value, Dividend;

			for(double i=0; i<(double)pTable->Get_Count() && PROGRESSBAR_Set_Position(i, (double)pTable->Get_Count()); i+=d)
			{
				if( pTable->Get_Value((sLong)i, Field, Value) )
				{
					if( Normalize < 0 )
					{
						m_Histogram += Get_Class(Value); m_Statistics += Value;
					}
					else if( pTable->Get_Value((sLong)i, Normalize, Dividend) && Dividend != 0. )
					{
						Value *= Scale / Dividend;

						m_Histogram += Get_Class(Value); m_Statistics += Value;
					}
				}
			}
		}

		if( m_Histogram.Update() && m_Histogram.Get_Element_Count() < (size_t)pTable->Get_Max_Samples() ) // any no-data cells ?
		{
			d *= (double)m_Histogram.Get_Element_Count() / (double)pTable->Get_Max_Samples();
		}

		m_Histogram.Scale_Element_Count(d);

		return( true );
	}

	//-----------------------------------------------------
	if( m_Mode == CLASSIFY_LUT && !SG_Data_Type_is_Numeric(m_pLUT->Get_Field_Type(LUT_MIN)) )
	{
		CSG_String Value;

		for(sLong i=0; i<pTable->Get_Count() && PROGRESSBAR_Set_Position(i, pTable->Get_Count()); i++)
		{
			if( pTable->Get_Value((sLong)i, Field, Value) )
			{
				m_Histogram += Get_Class(Value);
			}
		}
	}
	else
	{
		double Value, Dividend;

		for(sLong i=0; i<pTable->Get_Count() && PROGRESSBAR_Set_Position(i, pTable->Get_Count()); i++)
		{
			if( pTable->Get_Value((sLong)i, Field, Value) )
			{
				if( Normalize < 0 )
				{
					m_Histogram += Get_Class(Value); m_Statistics += Value;
				}
				else if( pTable->Get_Value((sLong)i, Normalize, Dividend) && Dividend != 0. )
				{
					Value *= Scale / Dividend;

					m_Histogram += Get_Class(Value); m_Statistics += Value;
				}
			}
		}
	}

	return( true );
}


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

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