/*
 *  RSGISDEMTools.cpp
 *  RSGIS_LIB
 *
 *  Created by Pete Bunting on 01/08/2011.
 *  Copyright 2011 RSGISLib. All rights reserved.
 *  This file is part of RSGISLib.
 * 
 *  RSGISLib 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  RSGISLib 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 RSGISLib.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "RSGISDEMTools.h"


namespace rsgis{namespace calib{
    
    
    RSGISCalcSlope::RSGISCalcSlope(unsigned int band, float ewRes, float nsRes, int outType, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->outType = outType;
        this->noDataVal = noDataVal;
    }
    void RSGISCalcSlope::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }
        
        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }
        
        bool hasNoDataVal = false;
        double sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            double meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }
        
        if(nVals > 1)
        {
            const double radiansToDegrees = 180.0 / M_PI;

            double dx, dy, slopeRad;
            
            dx = ((dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]) - 
                  (dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]))/ewRes;
            
            dy = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                  (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;

            slopeRad = atan(sqrt((dx * dx) + (dy * dy))/8);

            if(outType == 0)
            {
                output[0] = (slopeRad * radiansToDegrees);
            }
            else
            {
                output[0] = slopeRad;
            }
        }
        else
        {
            output[0] = 0.0;
        }
    }



    RSGISCalcSlopePerPxlRes::RSGISCalcSlopePerPxlRes(unsigned int band, int outType, double noDataVal, unsigned int ew_res_band, unsigned int ns_res_band) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->outType = outType;
        this->noDataVal = noDataVal;
        this->ew_res_band = ew_res_band;
        this->ns_res_band = ns_res_band;
    }
    void RSGISCalcSlopePerPxlRes::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output)
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }

        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }

        bool hasNoDataVal = false;
        float sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            float meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }

        if(nVals > 1)
        {
            const double radiansToDegrees = 180.0 / M_PI;

            float ewRes = dataBlock[this->ew_res_band][1][1];
            float nsRes = dataBlock[this->ns_res_band][1][1];
            if(nsRes < 0)
            {
                nsRes = nsRes * (-1);
            }

            double dx, dy, slopeRad;

            dx = ((dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]) -
                  (dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]))/ewRes;

            dy = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) -
                  (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;

            slopeRad = atan(sqrt((dx * dx) + (dy * dy))/8);

            if(outType == 0)
            {
                output[0] = (slopeRad * radiansToDegrees);
            }
            else
            {
                output[0] = slopeRad;
            }
        }
        else
        {
            output[0] = 0.0;
        }
    }




    RSGISCalcAspect::RSGISCalcAspect(unsigned int band, float ewRes, float nsRes, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->noDataVal = noDataVal;
    }
		
    void RSGISCalcAspect::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }
        
        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }
        
        const double radiansToDegrees = 180.0 / M_PI;
        
        bool hasNoDataVal = false;
        float sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            float meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }
        
        if(nVals > 1)
        {
            double dx, dy, aspect;
            
            dx = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]) - 
                  (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/ewRes;
            
            dy = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                  (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
            
            aspect = atan2(-dx, dy)*radiansToDegrees;
                    
            if (dx == 0 && dy == 0)
            {
                // Flat area
                aspect = std::numeric_limits<double>::signaling_NaN();
            }
            else if(aspect < 0)
            {
                aspect += 360.0;
            }
            else if(aspect == 360.0)
            {
                aspect = 0.0;
            }
            else if(aspect > 360)
            {
                double num = aspect / 360.0;
                int num360s = floor(num);
                aspect = aspect - (360 * num360s);
            }
            
            output[0] = aspect;
        }
        else
        {
            // Input was no data region.
            output[0] = std::numeric_limits<double>::signaling_NaN();
        }
    }



    RSGISCalcAspectPerPxlRes::RSGISCalcAspectPerPxlRes(unsigned int band, double noDataVal, unsigned int ew_res_band, unsigned int ns_res_band) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->noDataVal = noDataVal;
        this->ew_res_band = ew_res_band;
        this->ns_res_band = ns_res_band;
    }
    void RSGISCalcAspectPerPxlRes::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output)
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }

        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }

        const double radiansToDegrees = 180.0 / M_PI;

        bool hasNoDataVal = false;
        float sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            float meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }

        if(nVals > 1)
        {
            float ewRes = dataBlock[this->ew_res_band][1][1];
            float nsRes = dataBlock[this->ns_res_band][1][1];
            if(nsRes < 0)
            {
                nsRes = nsRes * (-1);
            }

            double dx, dy, aspect;

            dx = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]) -
                  (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/ewRes;

            dy = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) -
                  (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;

            aspect = atan2(-dx, dy)*radiansToDegrees;

            if (dx == 0 && dy == 0)
            {
                // Flat area
                aspect = std::numeric_limits<double>::signaling_NaN();
            }
            else if(aspect < 0)
            {
                aspect += 360.0;
            }
            else if(aspect == 360.0)
            {
                aspect = 0.0;
            }
            else if(aspect > 360)
            {
                double num = aspect / 360.0;
                int num360s = floor(num);
                aspect = aspect - (360 * num360s);
            }

            output[0] = aspect;
        }
        else
        {
            // Input was no data region.
            output[0] = std::numeric_limits<double>::signaling_NaN();
        }


    }


    
    
    RSGISRecodeAspect::RSGISRecodeAspect():rsgis::img::RSGISCalcImageValue(1)
    {
        
    }

    void RSGISRecodeAspect::calcImageValue(float *bandValues, int numBands, double *output)
    {
        if (boost::math::isnan(bandValues[0]))
        {
            output[0] = 0;
        } else
        {
            if (bandValues[0] > 360)
            {
                double num = bandValues[0] / 360.0;
                int num360s = floor(num);
                bandValues[0] = bandValues[0] - (360 * num360s);
            }

            if ((bandValues[0] >= 0) & (bandValues[0] < 45))
            {
                output[0] = 1;
            } else if ((bandValues[0] >= 45) & (bandValues[0] < 90))
            {
                output[0] = 2;
            } else if ((bandValues[0] >= 90) & (bandValues[0] < 135))
            {
                output[0] = 3;
            } else if ((bandValues[0] >= 135) & (bandValues[0] < 180))
            {
                output[0] = 4;
            } else if ((bandValues[0] >= 180) & (bandValues[0] < 225))
            {
                output[0] = 5;
            } else if ((bandValues[0] >= 225) & (bandValues[0] < 270))
            {
                output[0] = 6;
            } else if ((bandValues[0] >= 270) & (bandValues[0] < 315))
            {
                output[0] = 7;
            } else if ((bandValues[0] >= 315) & (bandValues[0] <= 360))
            {
                output[0] = 8;
            } else if ((bandValues[0] >= 360) & (bandValues[0] <= 405))
            {
                output[0] = 1;
            } else
            {
                std::cerr << "Input Aspect Value = " << bandValues[0] << std::endl;
                throw rsgis::img::RSGISImageCalcException(
                        "The input image pixel values much be between 0 and 360 degrees.");
            }
        }
    }




    RSGISCalcHillShade::RSGISCalcHillShade(unsigned int band, float ewRes, float nsRes, float sunZenith, float sunAzimuth, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->sunZenith = sunZenith;
        this->sunAzimuth = sunAzimuth;

        this->sunAzimuth = 360 - this->sunAzimuth;
        this->sunAzimuth = this->sunAzimuth + 90;
        if(this->sunAzimuth > 360)
        {
            this->sunAzimuth = this->sunAzimuth - 360;
        }
        this->noDataVal = noDataVal;
    }

    void RSGISCalcHillShade::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output)
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }

        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }

        bool hasNoDataVal = false;
        double sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            double meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }

        if(nVals > 1)
        {
            const double degreesToRadians = M_PI / 180.0;

            double dx, dy, aspect;

            dx = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2])-
                  (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/(ewRes*8);

            dy = ((dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2])-
                  (dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]))/(nsRes*8);

            double xx_plus_yy = dx * dx + dy * dy;

            // aspect...
            aspect = atan2(dy,dx);

            // shade value
            double sunZenRad = sunZenith * degreesToRadians;
            double sunAzRad = sunAzimuth * degreesToRadians;

            double cang = (sin(sunZenRad) -
                           cos(sunZenRad) * sqrt(xx_plus_yy) *
                           sin(aspect - (sunAzRad-M_PI/2))) /
                          sqrt(1 + 1 * xx_plus_yy);

            if (cang <= 0.0)
            {
                cang = 1.0;
            }
            else
            {
                cang = 1.0 + (254.0 * cang);
            }

            output[0] = cang;
        }
        else
        {
            output[0] = 1.0;
        }
    }



    RSGISCalcHillShadePerPxlRes::RSGISCalcHillShadePerPxlRes(unsigned int band, float sunZenith, float sunAzimuth, double noDataVal, unsigned int ew_res_band, unsigned int ns_res_band) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->sunZenith = sunZenith;
        this->sunAzimuth = sunAzimuth;

        this->sunAzimuth = 360 - this->sunAzimuth;
        this->sunAzimuth = this->sunAzimuth + 90;
        if(this->sunAzimuth > 360)
        {
            this->sunAzimuth = this->sunAzimuth - 360;
        }
        this->noDataVal = noDataVal;
        this->ew_res_band = ew_res_band;
        this->ns_res_band = ns_res_band;
    }

    void RSGISCalcHillShadePerPxlRes::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output)
    {
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }

        if(band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }

        bool hasNoDataVal = false;
        double sumVals = 0.0;
        int nVals = 0;
        for(int i = 0; i < winSize; ++i)
        {
            for(int j = 0; j < winSize; ++j)
            {
                if(dataBlock[band][i][j] == noDataVal)
                {
                    hasNoDataVal = true;
                }
                else
                {
                    sumVals += dataBlock[band][i][j];
                    ++nVals;
                }
            }
        }
        if(hasNoDataVal && (nVals>1))
        {
            double meanVal = sumVals / nVals;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        dataBlock[band][i][j] = meanVal;
                    }
                }
            }
        }

        if(nVals > 1)
        {
            float ewRes = dataBlock[this->ew_res_band][1][1];
            float nsRes = dataBlock[this->ns_res_band][1][1];
            if(nsRes < 0)
            {
                nsRes = nsRes * (-1);
            }

            const double degreesToRadians = M_PI / 180.0;

            double dx, dy, aspect;

            dx = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2])-
                  (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/(ewRes*8);

            dy = ((dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2])-
                  (dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]))/(nsRes*8);

            double xx_plus_yy = dx * dx + dy * dy;

            // aspect...
            aspect = atan2(dy,dx);

            // shade value
            double sunZenRad = sunZenith * degreesToRadians;
            double sunAzRad = sunAzimuth * degreesToRadians;

            double cang = (sin(sunZenRad) -
                           cos(sunZenRad) * sqrt(xx_plus_yy) *
                           sin(aspect - (sunAzRad-M_PI/2))) /
                          sqrt(1 + 1 * xx_plus_yy);

            if (cang <= 0.0)
            {
                cang = 1.0;
            }
            else
            {
                cang = 1.0 + (254.0 * cang);
            }

            output[0] = cang;
        }
        else
        {
            output[0] = 1.0;
        }
    }
    
    
    
    RSGISCalcShadowBinaryMask::RSGISCalcShadowBinaryMask(GDALDataset *inputImage, unsigned int band, float ewRes, float nsRes, float sunZenith, float sunAzimuth, float maxElevHeight, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->sunZenith = sunZenith;
        this->sunAzimuth = sunAzimuth;
        this->inputImage = inputImage;
        this->maxElevHeight = maxElevHeight;
        
        this->demWidth = inputImage->GetRasterXSize() * ewRes;
        this->demHeight = inputImage->GetRasterYSize() * nsRes;
        
        this->sunRange = sqrt((demWidth * demWidth) + (demHeight * demHeight))*2;
        
        this->noDataVal = noDataVal;
        this->degreesToRadians = M_PI / 180.0;
        this->radiansToDegrees = 180.0 / M_PI;

        extractPixels = new rsgis::img::RSGISExtractImagePixelsOnLine();
    }
		
    void RSGISCalcShadowBinaryMask::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output, OGREnvelope extent)
    {
        
        if(winSize != 3)
        {
            throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
        }
        
        if(this->band >= numBands)
        {
            throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
        }
        
        output[0] = 0;
        try
        {
            if( dataBlock[band][1][1] != this->noDataVal)
            {
                bool flatGround = false;
                bool pxlAwayFromSun = false;
                
                double aspect = 0.0;
                double slopeRad = 0.0;
                
                bool hasNoDataVal = false;
                double sumVals = 0.0;
                int nVals = 0;
                for(int i = 0; i < winSize; ++i)
                {
                    for(int j = 0; j < winSize; ++j)
                    {
                        if(dataBlock[band][i][j] == noDataVal)
                        {
                            hasNoDataVal = true;
                        }
                        else
                        {
                            sumVals += dataBlock[band][i][j];
                            ++nVals;
                        }
                    }
                }
                if(hasNoDataVal && (nVals>1))
                {
                    double meanVal = sumVals / nVals;
                    for(int i = 0; i < winSize; ++i)
                    {
                        for(int j = 0; j < winSize; ++j)
                        {
                            if(dataBlock[band][i][j] == noDataVal)
                            {
                                dataBlock[band][i][j] = meanVal;
                            }
                        }
                    }
                }
                
                if(nVals > 1)
                {
                    const double radiansToDegrees = 180.0 / M_PI;
                    
                    double dxSlope, dySlope = 0.0;
                    
                    dxSlope = ((dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]) -
                               (dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]))/ewRes;
                    
                    dySlope = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) -
                               (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                    
                    slopeRad = atan(sqrt((dxSlope * dxSlope) + (dySlope * dySlope))/8);
                    
                    
                    double dxAspect, dyAspect = 0.0;
                    
                    dxAspect = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]) -
                                (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/ewRes;
                    
                    dyAspect = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) -
                                (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                    
                    aspect = atan2(-dxAspect, dyAspect)*radiansToDegrees;
                    
                    if (dxAspect == 0 && dyAspect == 0)
                    {
                        // Flat area
                        aspect = std::numeric_limits<double>::signaling_NaN();
                        flatGround = true;
                    }
                    else if(aspect < 0)
                    {
                        aspect += 360.0;
                    }
                    else if(aspect == 360.0)
                    {
                        aspect = 0.0;
                    }
                    else if(aspect > 360)
                    {
                        double num = aspect / 360.0;
                        int num360s = floor(num);
                        aspect = aspect - (360 * num360s);
                    }
                    
                    aspect = aspect * degreesToRadians;
                }
                else
                {
                    slopeRad = 0;
                    // No Data - call it flat ground...
                    aspect = std::numeric_limits<double>::signaling_NaN();
                    flatGround = true;
                }
                
                
                if(!flatGround)
                {
                    double sunZenRad = sunZenith * degreesToRadians;
                    double sunAzRad = sunAzimuth * degreesToRadians;
                    
                    double ic = (cos(sunZenRad) * cos(slopeRad)) + (sin(sunZenRad) * sin(slopeRad) * cos((sunAzRad) - aspect));
                    //output[0] = ic;
                    if(ic < 0)
                    {
                        pxlAwayFromSun = true;
                    }
                    
                    if(pxlAwayFromSun)
                    {
                        output[0] = 1;
                    }
                    else
                    {
                        // do ray tracing...
                        // Location of active point.
                        double x = extent.MinX + (extent.MaxX - extent.MinX)/2;
                        double y = extent.MinY + (extent.MaxY - extent.MinY)/2;
                        double z = dataBlock[band][1][1];
                        
                        
                        double sunAzTrans = 360-this->sunAzimuth;
                        sunAzTrans = sunAzTrans + 90;
                        if(sunAzTrans > 360)
                        {
                            sunAzTrans = sunAzTrans-360;
                        }
                        
                        double sunZenRad = sunZenith * degreesToRadians;
                        double sunAzRad = sunAzTrans * degreesToRadians;
                        
                        // Location of the sun.
                        //double sunX = x + (sunRange * sin(sunZenRad) * cos(sunAzRad));
                        //double sunY = y + (sunRange * sin(sunZenRad) * sin(sunAzRad));
                        //double sunZ = z + (sunRange * cos(sunZenRad));
                        
                        // Create Ray Line
                        OGRPoint *pxlPt = new OGRPoint(x, y, z);
                        std::vector<rsgis::img::ImagePixelValuePt*> *imagePxlPts = extractPixels->getImagePixelValues(inputImage, band+1, pxlPt, sunAzRad, sunZenRad, maxElevHeight);
                        delete pxlPt;
                        
                        // Check whether pixel intersects with ray.
                        for(std::vector<rsgis::img::ImagePixelValuePt*>::iterator iterPxls = imagePxlPts->begin(); iterPxls != imagePxlPts->end(); ++iterPxls)
                        {
                            if((*iterPxls)->pt->getZ() < (*iterPxls)->value)
                            {
                                output[0] = 1;
                                break;
                            }
                        }
                        
                        // Clean up memory..
                        for(std::vector<rsgis::img::ImagePixelValuePt*>::iterator iterPxls = imagePxlPts->begin(); iterPxls != imagePxlPts->end(); )
                        {
                            delete (*iterPxls)->pt;
                            delete (*iterPxls);
                            iterPxls = imagePxlPts->erase(iterPxls);
                        }
                        delete imagePxlPts;
                    }
                }
                else
                {
                    output[0] = 0;
                }
            }
            else
            {
                output[0] = 0;
            }
        }
        catch (rsgis::img::RSGISImageCalcException &e) 
        {
            throw e;
        }
    }
    
    RSGISCalcShadowBinaryMask::~RSGISCalcShadowBinaryMask()
    {
        delete extractPixels;
    }
    
    
    
    

    RSGISCalcRayIncidentAngle::RSGISCalcRayIncidentAngle(unsigned int band, float ewRes, float nsRes, float sunZenith, float sunAzimuth, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->sunZenith = sunZenith;
        this->sunAzimuth = sunAzimuth;
        this->noDataVal = noDataVal;
    }
		
    void RSGISCalcRayIncidentAngle::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        float outputValue = 0;
        
        const double degreesToRadians = M_PI / 180.0;
        const double radiansToDegrees = 180.0 / M_PI;
        
        try 
        {
            if(winSize != 3)
            {
                throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
            }
            
            if(band >= numBands)
            {
                throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
            }
            
            bool hasNoDataVal = false;
            double sumVals = 0.0;
            int nVals = 0;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        hasNoDataVal = true;
                    }
                    else
                    {
                        sumVals += dataBlock[band][i][j];
                        ++nVals;
                    }
                }
            }
            if(hasNoDataVal && (nVals>1))
            {
                double meanVal = sumVals / nVals;
                for(int i = 0; i < winSize; ++i)
                {
                    for(int j = 0; j < winSize; ++j)
                    {
                        if(dataBlock[band][i][j] == noDataVal)
                        {
                            dataBlock[band][i][j] = meanVal;
                        }
                    }
                }
            }
            
            if(nVals > 1)
            {
                double dxSlope, dySlope, slopeRad;
                
                dxSlope = ((dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]) - 
                           (dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]))/ewRes;
                
                dySlope = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                           (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                
                slopeRad = atan(sqrt((dxSlope * dxSlope) + (dySlope * dySlope))/8);
                
                
                double dxAspect, dyAspect, aspect;
                
                dxAspect = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]) - 
                            (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/ewRes;
                
                dyAspect = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                            (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                
                aspect = atan2(-dxAspect, dyAspect)*radiansToDegrees;
                
                if (dxAspect == 0 && dyAspect == 0)
                {
                    // Flat area
                    aspect = std::numeric_limits<double>::signaling_NaN();
                }
                
                if (aspect < 0)
                {
                    aspect += 360.0;
                }
                
                if (aspect == 360.0)
                {
                    aspect = 0.0;
                }
                double aspectRad = aspect*degreesToRadians;
                
                // UNIT VECTOR FOR SURFACE
                double pA = sin(slopeRad) * cos(aspectRad);
                double pB = sin(slopeRad) * sin(aspectRad);
                double pC = cos(slopeRad);

                double sunZenRad = sunZenith * degreesToRadians;
                double sunAzRad = sunAzimuth * degreesToRadians;
                
                // UNIT VECTOR FOR INCIDENT RAY
                double rA = sin(sunZenRad) * cos(sunAzRad);
                double rB = sin(sunZenRad) * sin(sunAzRad);
                double rC = cos(sunZenRad);
                
                outputValue = acos((pA*rA)+(pB*rB)+(pC*rC)) * radiansToDegrees;
                
                if(boost::math::isnan(outputValue))
                {
                    outputValue = sunZenith;
                }
            }
            else
            {
                outputValue = sunZenith;
            }
        }
        catch (rsgis::img::RSGISImageCalcException &e) 
        {
            throw e;
        }

        output[0] = outputValue;
    }
    
    
    
    RSGISCalcRayExitanceAngle::RSGISCalcRayExitanceAngle(unsigned int band, float ewRes, float nsRes, float viewZenith, float viewAzimuth, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->band = band;
        this->ewRes = ewRes;
        this->nsRes = nsRes;
        this->viewZenith = viewZenith;
        this->viewAzimuth = viewAzimuth;
        this->noDataVal = noDataVal;
    }
		
    void RSGISCalcRayExitanceAngle::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        float outputValue = 0;
        
        const double degreesToRadians = M_PI / 180.0;
        
        try 
        {
            if(winSize != 3)
            {
                throw rsgis::img::RSGISImageCalcException("Window size must be equal to 3 for the calculate of slope.");
            }
            
            if(band >= numBands)
            {
                throw rsgis::img::RSGISImageCalcException("Specified image band is not within the image.");
            }
            
            bool hasNoDataVal = false;
            double sumVals = 0.0;
            int nVals = 0;
            for(int i = 0; i < winSize; ++i)
            {
                for(int j = 0; j < winSize; ++j)
                {
                    if(dataBlock[band][i][j] == noDataVal)
                    {
                        hasNoDataVal = true;
                    }
                    else
                    {
                        sumVals += dataBlock[band][i][j];
                        ++nVals;
                    }
                }
            }
            if(hasNoDataVal && (nVals>1))
            {
                double meanVal = sumVals / nVals;
                for(int i = 0; i < winSize; ++i)
                {
                    for(int j = 0; j < winSize; ++j)
                    {
                        if(dataBlock[band][i][j] == noDataVal)
                        {
                            dataBlock[band][i][j] = meanVal;
                        }
                    }
                }
            }
            
            if(nVals > 1)
            {
                const double radiansToDegrees = 180.0 / M_PI;
                
                double dxSlope, dySlope, slopeRad = 0.0;
                
                dxSlope = ((dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]) - 
                           (dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]))/ewRes;
                
                dySlope = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                           (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                
                slopeRad = atan(sqrt((dxSlope * dxSlope) + (dySlope * dySlope))/8);
                
                double dxAspect, dyAspect, aspect, aspectRad = 0.0;
                
                dxAspect = ((dataBlock[band][0][2] + dataBlock[band][1][2] + dataBlock[band][1][2] + dataBlock[band][2][2]) - 
                            (dataBlock[band][0][0] + dataBlock[band][1][0] + dataBlock[band][1][0] + dataBlock[band][2][0]))/ewRes;
                
                dyAspect = ((dataBlock[band][2][0] + dataBlock[band][2][1] + dataBlock[band][2][1] + dataBlock[band][2][2]) - 
                            (dataBlock[band][0][0] + dataBlock[band][0][1] + dataBlock[band][0][1] + dataBlock[band][0][2]))/nsRes;
                
                aspect = atan2(-dxAspect, dyAspect)*radiansToDegrees;
                
                if (dxAspect == 0 && dyAspect == 0)
                {
                    // Flat area
                    aspect = 0;
                }
                
                if (aspect < 0)
                {
                    aspect += 360.0;
                }
                
                if (aspect == 360.0)
                {
                    aspect = 0.0;
                }
                aspectRad = aspect * degreesToRadians;
                
                // UNIT VECTOR FOR SURFACE
                double pA = sin(slopeRad) * cos(aspectRad);
                double pB = sin(slopeRad) * sin(aspectRad);
                double pC = cos(slopeRad);
                
                double viewZenRad = viewZenith * degreesToRadians;
                double viewAzRad = viewAzimuth * degreesToRadians;
                
                // UNIT VECTOR FOR EXITANCE RAY
                double rA = sin(viewZenRad) * cos(viewAzRad);
                double rB = sin(viewZenRad) * sin(viewAzRad);
                double rC = cos(viewZenRad);
                
                outputValue = acos((pA*rA)+(pB*rB)+(pC*rC)) * radiansToDegrees;
                
                if(boost::math::isnan(outputValue))
                {
                    outputValue = 0;
                }
            }
            else
            {
                outputValue = 0;
            }
        }
        catch (rsgis::img::RSGISImageCalcException &e) 
        {
            throw e;
        }
        
        output[0] = outputValue;
    }

    
    RSGISFilterDTMWithAspectMedianFilter::RSGISFilterDTMWithAspectMedianFilter(float aspectRange, double noDataVal) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->aspectRange = aspectRange;
        this->noDataVal = noDataVal;
    }
    
    void RSGISFilterDTMWithAspectMedianFilter::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        rsgis::math::RSGISMathsUtils mathUtils;
        int midPoint = floor(((float)winSize)/2.0);
        
        float aspectVal = dataBlock[1][midPoint][midPoint];
        float lowerAspThres = aspectVal - aspectRange;
        float upperAspThres = aspectVal + aspectRange;
        
        if(lowerAspThres < 0)
        {
            lowerAspThres = 360 + lowerAspThres;
        }
        
        if(upperAspThres > 360)
        {
            upperAspThres = upperAspThres - 360;
        }
        
        std::vector<float> vals;
        
        for(unsigned int i = 0; i < winSize; ++i)
        {
            for(unsigned int j = 0; j < winSize; ++j)
            {
                if(mathUtils.angleWithinRange(dataBlock[1][i][j], lowerAspThres, upperAspThres))
                {
                    if(!boost::math::isnan(dataBlock[0][i][j]) && (dataBlock[0][i][j] != noDataVal))
                    {
                        vals.push_back(dataBlock[0][i][j]);
                    }
                }
            }
        }
        if(vals.size() > 0)
        {
            std::sort(vals.begin(), vals.end());
            int midpt = floor(vals.size()/2.0);
            
            output[0] = vals.at(midpt);
        }
        else
        {
            for(unsigned int i = 0; i < winSize; ++i)
            {
                for(unsigned int j = 0; j < winSize; ++j)
                {
                    if(!boost::math::isnan(dataBlock[0][i][j]) && (dataBlock[0][i][j] != noDataVal))
                    {
                        vals.push_back(dataBlock[0][i][j]);
                    }
                }
            }
            if(vals.size() > 0)
            {
                std::sort(vals.begin(), vals.end());
                int midpt = floor(vals.size()/2.0);
                
                output[0] = vals.at(midpt);
            }
            else
            {
                output[0] = std::numeric_limits<float>::signaling_NaN();
            }
            
        }
        
    }
    
    
    
    
    RSGISDetreadDEMUsingPlaneFit::RSGISDetreadDEMUsingPlaneFit(double noDataVal, int winSize) : rsgis::img::RSGISCalcImageValue(1)
    {
        this->noDataVal = noDataVal;
        this->mathUtils = new rsgis::math::RSGISMathsUtils();
        this->xVals = new double[winSize*winSize];
        this->yVals = new double[winSize*winSize];
        this->zVals = new double[winSize*winSize];
        this->nVals = 0;
    }
    
    void RSGISDetreadDEMUsingPlaneFit::calcImageValue(float ***dataBlock, int numBands, int winSize, double *output) 
    {
        int midPoint = floor(((float)winSize)/2.0);
        
        for(unsigned int i = 0; i < (winSize*winSize); ++i)
        {
            this->xVals[i] = 0;
            this->yVals[i] = 0;
            this->zVals[i] = 0;
        }
        
        this->nVals = 0;
        for(unsigned int i = 0; i < winSize; ++i)
        {
            for(unsigned int j = 0; j < winSize; ++j)
            {
                if(!boost::math::isnan(dataBlock[0][i][j]) && (dataBlock[0][i][j] != noDataVal))
                {
                    this->xVals[this->nVals] = j-midPoint;
                    this->yVals[this->nVals] = i-midPoint;
                    this->zVals[this->nVals] = dataBlock[0][i][j];
                    this->nVals = this->nVals + 1;
                }
            }
        }
        if(this->nVals > 0)
        {
            double a = 0.0;
            double b = 0.0;
            double c = 0.0;
            mathUtils->fitPlane(this->xVals, this->yVals, this->zVals, this->nVals, &a, &b, &c);
            output[0] = c;
        }
        else
        {
            output[0] = 0;
        }
        
    }
    
    RSGISDetreadDEMUsingPlaneFit::~RSGISDetreadDEMUsingPlaneFit()
    {
        delete this->mathUtils;
        delete[] this->xVals;
        delete[] this->yVals;
        delete[] this->zVals;
    }
    
	
}}


