
///////////////////////////////////////////////////////////
//                                                       //
//                         SAGA                          //
//                                                       //
//      System for Automated Geoscientific Analyses      //
//                                                       //
//                     Tool Library                      //
//                      ta_lighting                      //
//                                                       //
//-------------------------------------------------------//
//                                                       //
//               topographic_correction.cpp              //
//                                                       //
//                 Copyright (C) 2008 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               //
//                Goldschmidtstr. 5                      //
//                37077 Goettingen                       //
//                Germany                                //
//                                                       //
///////////////////////////////////////////////////////////

//---------------------------------------------------------
#include "topographic_correction.h"


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

//---------------------------------------------------------
CTopographic_Correction::CTopographic_Correction(void)
{
	Set_Name		(_TL("Topographic Correction"));

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

	Set_Description	(_TW(
		"Topographic correction for satellite imagery's bands using "
		"a Digital Elevation Model to estimate and remove the shading "
		"effect for the given position of the Sun at acquisition time. "
	));

	Add_Reference(
		"Civco, D. L.", "1989",
		"Topographic Normalization of Landsat Thematic Mapper Digital Imagery",
		"Photogrammetric Engineering and Remote Sensing, 55(9), pp.1303-1309."
	);

	Add_Reference(
		"Law, K.H., Nichol, J.", "2004",
		"Topographic Correction for Differential Illumination Effects on Ikonos Satellite Imagery",
		"ISPRS 2004 International Society for Photogrammetry and Remote Sensing.",
		SG_T("http://www.cartesia.org/geodoc/isprs2004/comm3/papers/347.pdf")
	);

	Add_Reference(
		"Phua, M.-H., Saito, H.", "2003",
		"Estimation of biomass of a mountainous tropical forest using Landsat TM data",
		"Canadian Journal of Remote Sensing, 29(4), pp.429-440."
	);

	Add_Reference(
		"Riano, D., Chuvieco, E. Salas, J., Aguado, I.", "2003",
		"Assessment of Different Topographic Corrections in Landsat-TM Data for Mapping Vegetation Types",
		"IEEE Transactions on Geoscience and Remote Sensing, 41(5), pp.1056-1061.",
		SG_T("http://www.geogra.uah.es/~emilio/pdf/Riano2003b.pdf")
	);

	Add_Reference(
		"Teillet, P.M., Guindon, B., Goodenough, D.G.", "1982",
		"On the slope-aspect correction of multispectral scanner data",
		"Canadian Journal of Remote Sensing, 8(2), pp.1537-1540."
	);

	//-----------------------------------------------------
	Parameters.Add_Grid("",
		"DEM"		, _TL("Elevation"),
		_TL(""),
		PARAMETER_INPUT, false
	);

	Parameters.Add_Grid("",
		"ORIGINAL"	, _TL("Original Image"),
		_TL(""),
		PARAMETER_INPUT
	);

	Parameters.Add_Grid("",
		"CORRECTED"	, _TL("Corrected Image"),
		_TL(""),
		PARAMETER_OUTPUT
	);

	Parameters.Add_Node("", "NODE_SOLAR", _TL("Solar Position"), _TL(""));

	Parameters.Add_Double("NODE_SOLAR",
		"AZI"		, _TL("Azimuth"),
		_TL("direction of sun (degree, clockwise from North)"),
		180., 0., true, 360., true
	);

	Parameters.Add_Double("NODE_SOLAR",
		"HGT"		, _TL("Height"),
		_TL("height of sun above horizon (degree)"),
		 45., 0., true,  90., true
	);

	Parameters.Add_Choice("",
		"METHOD"	, _TL("Method"),
		_TL(""),
		CSG_String::Format("%s|%s|%s|%s|%s|%s|%s",
			_TL("Cosine Correction (Teillet et al. 1982)"),
			_TL("Cosine Correction (Civco 1989)"),
			_TL("Minnaert Correction"),
			_TL("Minnaert Correction with Slope (Riano et al. 2003)"),
			_TL("Minnaert Correction with Slope (Law & Nichol 2004)"),
			_TL("C Correction"),
			_TL("Normalization (after Civco, modified by Law & Nichol)")
		), 4
	);

	Parameters.Add_Double("",
		"MINNAERT"	, _TL("Minnaert Correction"),
		_TL(""),
		0.5, 0., true, 1., true
	);

	Parameters.Add_Int("",
		"MAXCELLS"	, _TL("Maximum Cells (C Correction Analysis)"),
		_TL("Maximum number of grid cells used for trend analysis as required by C correction."),
		1000, 10, true
	);

	Parameters.Add_Choice("",
		"MAXVALUE"	, _TL("Value Range"),
		_TL(""),
		CSG_String::Format("%s|%s",
			_TL("1 byte (0-255)"),
			_TL("2 byte (0-65535)")
		), 0
	);
}


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

//---------------------------------------------------------
bool CTopographic_Correction::On_Execute(void)
{
	if( !Get_Illumination() )
	{
		m_Slope			.Destroy();
		m_Illumination	.Destroy();

		return( false );
	}

	if( !Get_Model() )
	{
		m_Slope			.Destroy();
		m_Illumination	.Destroy();

		return( false );
	}

	//-----------------------------------------------------
	Process_Set_Text(_TL("Topographic Correction"));

	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		for(int x=0; x<Get_NX(); x++)
		{
			if( m_pOriginal->is_NoData(x, y) )
			{
				m_pCorrected->Set_NoData(x, y);
			}
			else
			{
				m_pCorrected->Set_Value(x, y, Get_Correction(
					m_Slope       .asDouble(x, y),
					m_Illumination.asDouble(x, y),
					m_pOriginal  ->asDouble(x, y)
				));
			}
		}
	}

	//-----------------------------------------------------
	m_Slope			.Destroy();
	m_Illumination	.Destroy();

	return( true );
}


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

//---------------------------------------------------------
double CTopographic_Correction::Get_Correction(double Slope, double Illumination, double Value)
{
	switch( m_Method )
	{
	case 0:	// Cosine Correction (Teillet et al. 1982)
		if( Illumination > 0.0 )
		{
			Value	= Value * m_cosTz / Illumination;
		}
		break;

	case 1:	// Cosine Correction (Civco 1989)
		Value	= Value + (Value * ((m_Illumination.Get_Mean() - Illumination) / m_Illumination.Get_Mean()));
		break;

	case 2:	// Minnaert Correction
		if( Illumination > 0.0 )
		{
			Value	= Value * pow(m_cosTz / Illumination, m_Minnaert);
		}
		break;

	case 3:	// Minnaert Correction with Slope (Riano et al. 2003)
		if( Illumination > 0.0 )
		{
			Value	= Value * cos(Slope) * pow(m_cosTz / (Illumination * cos(Slope)), m_Minnaert);
		}
		break;

	case 4:	// Minnaert Correction with Slope (Law & Nichol 2004)
		if( Illumination > 0.0 )
		{
			Value	= Value * cos(Slope) / pow(Illumination * cos(Slope), m_Minnaert);
		}
		break;

	case 5:	// C Correction
		Value	= Value * (m_cosTz + m_C) / (Illumination + m_C);
		break;

	case 6:	// Normalization
		Value	= Value + ((Value * ((m_Illumination.Get_Mean() - Illumination) / m_Illumination.Get_Mean())) * m_C);
		break;
	}

	return( Value < 0 ? 0 : Value > m_maxValue ? m_maxValue : Value );
}


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

//---------------------------------------------------------
bool CTopographic_Correction::Get_Model(void)
{
	m_pOriginal		= Parameters("ORIGINAL" )->asGrid();
	m_pCorrected	= Parameters("CORRECTED")->asGrid();

	m_pCorrected	->Fmt_Name("%s [%s]", m_pOriginal->Get_Name(), _TL("Topographic Correction"));

	m_Method		= Parameters("METHOD")		->asInt();

	m_Minnaert		= Parameters("MINNAERT")	->asDouble();

	switch( Parameters("MAXVALUE")->asInt() )
	{
	default:	m_maxValue	=   255;	break;
	case  1:	m_maxValue	= 65535;	break;
	}

	switch( m_Method )
	{
	//-----------------------------------------------------
	case 5:	// C Correction
		{
			Process_Set_Text(_TL("Regression Analysis"));

			CSG_Regression	R;

			sLong n		= Parameters("MAXCELLS")->asInt();
			int	nStep	= Get_NCells() < n ? 1 : (int)(Get_NCells() / n);

			for(n=0; n<Get_NCells() && Set_Progress_NCells(n); n+=nStep)
			{
				R.Add_Values(m_pOriginal->asDouble(n), m_Illumination.asDouble(n));
			}

			if( !R.Calculate() || !R.Get_Constant() )
			{
				return( false );
			}

			m_C	= R.Get_Coefficient() / R.Get_Constant();

			Message_Add(R.asString());
		}
		break;

	//-----------------------------------------------------
	case 6:	// Normalization (after Civco, modified by Law & Nichol)
		{
			m_C	= 1.0;
		}
		break;
	}

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


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

//---------------------------------------------------------
bool CTopographic_Correction::Get_Illumination(void)
{
	Process_Set_Text(_TL("Illumination calculation"));

	//-----------------------------------------------------
	CSG_Grid	DEM, *pDEM	= Parameters("DEM")->asGrid();

	if( !pDEM->Get_System().is_Equal(Get_System()) )
	{
		DEM.Create(Get_System());
		DEM.Assign(pDEM, pDEM->Get_Cellsize() > Get_Cellsize() ? GRID_RESAMPLING_BSpline : GRID_RESAMPLING_Mean_Cells);
		pDEM	= &DEM;
	}

	//-----------------------------------------------------
	double	Azi	= Parameters("AZI")->asDouble() * M_DEG_TO_RAD;
	double	Hgt	= Parameters("HGT")->asDouble() * M_DEG_TO_RAD;

	m_cosTz	= cos(M_PI_090 - Hgt);
	m_sinTz	= sin(M_PI_090 - Hgt);

	m_Slope			.Create(Get_System());
	m_Illumination	.Create(Get_System());

	//-----------------------------------------------------
	for(int y=0; y<Get_NY() && Set_Progress(y); y++)
	{
		for(int x=0; x<Get_NX(); x++)
		{
			double	Slope, Aspect;

			if( pDEM->Get_Gradient(x, y, Slope, Aspect) )
			{
				m_Slope			.Set_Value(x, y, Slope);
				m_Illumination	.Set_Value(x, y, cos(Slope) * m_cosTz + sin(Slope) * m_sinTz * cos(Azi - Aspect));
			}
			else
			{
				m_Slope			.Set_Value(x, y, 0.0);
				m_Illumination	.Set_Value(x, y, m_cosTz);
			}
		}
	}

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


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

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