/* -*- coding: utf-8 -*- */
/* Copyright (C) 2009-2022  José Luis García Pallero, <jgpallero@gmail.com>
 *
 * This file is part of OctPROJ.
 *
 * OctPROJ 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.
 *
 * This program 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 software; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
/******************************************************************************/
/******************************************************************************/
#define HELPTEXT "\
-*- texinfo -*-\n\
@deftypefn {Function file}{[@var{X2},@var{Y2},@var{Z2},@var{t2}] =}_op_transform(@var{X1},@var{Y1},@var{Z1},@var{t1},@var{par1},@var{par2})\n\
\n\
@cindex Performs transformation between two coordinate systems.\n\
\n\
This function transforms X/Y/Z/t, lon/lat/h/t points between two coordinate\n\
systems 1 and 2 using the PROJ function proj_trans_generic().\n\
\n\
INPUT ARGUMENTS:\n\
\n\
@itemize @bullet\n\
@item\n\
@var{X1} is a column vector containing by default the first coordinates (X or\n\
geodetic longitude) in the source system.\n\
@item\n\
@var{Y1} is a column vector containing by default the second coordinates (Y\n\
or geodetic latitude) in the source system.\n\
@item\n\
@var{Z1} is a column vector containing by default the third coordinates (Z or\n\
height) in the source system.\n\
@item\n\
@var{t1} is a column vector containing by default the fourth coordinates\n\
(time) in the source system.\n\
@item\n\
@var{par1} is a text string containing the parameters for the source system,\n\
in PROJ '+' format, as EPSG code or as a WKT definition.\n\
@item\n\
@var{par2} is a text string containing the parameters for the destination\n\
system, in PROJ '+' format, as EPSG code or as WKT definition.\n\
@end itemize\n\
\n\
OUTPUT ARGUMENTS:\n\
\n\
@itemize @bullet\n\
@item\n\
@var{X2} is a column vector containing by default the first coordinates (X or\n\
geodetic longitude) in the destination system\n\
@item\n\
@var{Y2} is a column vector containing by default the second coordinates (Y\n\
or geodetic latitude) in the destination system.\n\
@item\n\
@var{Z2} is a column vector containing by default the third coordinates (Z or\n\
height) in the destination system.\n\
@item\n\
@var{t2} is a column vector containingby default the fourth coordinates\n\
(time) in the destination system.\n\
@end itemize\n\
\n\
The coordinate vectors @var{X1}, @var{Y1}, @var{Z1}, and @var{t1} must be all\n\
scalars or all column vectors (of the same size). @var{X2}, @var{Y2},\n\
@var{Z2}, and @var{t2} will be according to the input dimensions.\n\
\n\
Angular units are by default radians and linear meters, although other can be\n\
specified in @var{par1} and/or @var{par2}, so @var{X1}, @var{Y1}, @var{Z1},\n\
and @var{t1} must be congruent with them. The same applies to the coordinate\n\
order at input and output.\n\
\n\
If a transformation error occurs the resultant coordinates for the affected\n\
points have all Inf value and a warning message is emitted (one for each\n\
erroneous point).\n\
@seealso{_op_fwd, _op_inv}\n\
@end deftypefn"
/******************************************************************************/
/******************************************************************************/
#include<octave/oct.h>
#include"projwrap.h"
/******************************************************************************/
/******************************************************************************/
#define ERRORTEXT 1000
/******************************************************************************/
/******************************************************************************/
DEFUN_DLD(_op_transform,args,,HELPTEXT)
{
    //error message
    char errorText[ERRORTEXT]="_op_transform:\n\t";
    //output list
    octave_value_list outputList;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //checking input arguments
    if(args.length()!=6)
    {
        //error text
        sprintf(&errorText[strlen(errorText)],
                "Incorrect number of input arguments\n\t"
                "See help _op_transform");
        //error message
        error("%s",errorText);
    }
    else
    {
        //error code
        int idErr=0;
        //error in projection
        int projectionError=0;
        //loop index
        size_t i=0;
        //input coordinates from GNU Octave
        ColumnVector xIn=args(0).column_vector_value();
        ColumnVector yIn=args(1).column_vector_value();
        ColumnVector zIn=args(2).column_vector_value();
        ColumnVector tIn=args(3).column_vector_value();
        //parameters strings
        std::string paramsStart=args(4).string_value();
        std::string paramsEnd=args(5).string_value();
        //number of imput data
        size_t nElem=static_cast<size_t>(xIn.rows());
        size_t nElemZ=static_cast<size_t>(zIn.rows());
        size_t nElemt=static_cast<size_t>(tIn.rows());
        //output coordinates for GNU Octave
        ColumnVector xOut(nElem);
        ColumnVector yOut(nElem);
        ColumnVector zOut(nElemZ);
        ColumnVector tOut(nElemt);
        //pointers to output data
        double* x=NULL;
        double* y=NULL;
        double* z=NULL;
        double* t=NULL;
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //copy input data in working arrays
        for(i=0;i<nElem;i++)
        {
            xOut(i) = xIn(i);
            yOut(i) = yIn(i);
            if(nElemZ)
            {
                zOut(i) = zIn(i);
            }
            if(nElemt)
            {
                tOut(i) = tIn(i);
            }
        }
        //pointers to output data
        x = xOut.fortran_vec();
        y = yOut.fortran_vec();
        if(nElemZ)
        {
            z = zOut.fortran_vec();
        }
        if(nElemt)
        {
            t = tOut.fortran_vec();
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //transformation
        idErr = proj_transform(x,y,z,t,nElem,1,
                               paramsStart.c_str(),paramsEnd.c_str(),
                               &errorText[strlen(errorText)],&projectionError);
        //error checking
        if(idErr||(projectionError>-1))
        {
            //type of error
            if(projectionError>-1)
            {
                //warning message
                warning("%s",errorText);
                //positions of projection errors
                for(i=0;i<nElem;i++)
                {
                    //testing for HUGE_VAL
                    if((x[i]==HUGE_VAL)||(y[i]==HUGE_VAL))
                    {
                        //error text
                        warning("Transformation error in point %zu "
                                "(index starts at 1)",i+1);
                        //assign Inf values
                        x[i] = INFINITY;
                        y[i] = INFINITY;
                        if(nElemZ)
                        {
                            z[i] = INFINITY;
                        }
                        if(nElemt)
                        {
                            t[i] = INFINITY;
                        }
                    }
                }
            }
            else
            {
                //error message
                error("%s",errorText);
                //exit
                return outputList;
            }
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //adding results to output list
        outputList(0) = xOut;
        outputList(1) = yOut;
        outputList(2) = zOut;
        outputList(3) = tOut;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //exit
    return outputList;
}
