/* -*- coding: utf-8 -*- */
/**
\ingroup octproj
@{
\file projwrap.c
\brief Function definition for PROJ wrapper.
\author José Luis García Pallero, jgpallero@gmail.com
\date 05-12-2009
\date 22-02-2020
\section License License
This program is free software. You can redistribute it and/or modify it under
the terms of the GNU General Public License (GPL) as published by the Free
Software Foundation (FSF), either version 3 of the License, or (at your option)
any later version.
You can obtain a copy of the GPL or contact with the FSF in: http://www.fsf.org
or http://www.gnu.org
*/
/******************************************************************************/
/******************************************************************************/
#include"projwrap.h"
/******************************************************************************/
/******************************************************************************/
int proj_fwd(double* u,
             double* v,
             const size_t nElem,
             const size_t incElem,
             const char params[],
             char errorText[],
             int* projectionError)
{
    //error code
    int idErr=0;
    //index for loop
    size_t i=0;
    //byte increment between elements in arrays
    size_t ieb=incElem*sizeof(double);
    //safe context for multithread
    PJ_CONTEXT *C=NULL;
    //projection parameters
    PJ *proyec=NULL;
    //auxiliary variables for coordinate checking
    double auxU=0.0,auxV=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //error identifier
    *projectionError = -1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //multithread context
    C = proj_context_create();
    //projection init
    proyec = proj_create(C,params);
    //errors
    if(proyec==0)
    {
        //PROJ error code
        idErr = proj_context_errno(C);
        //memory free
        proj_context_destroy(C);
        //error text
        sprintf(errorText,"Error in projection parameters\n\t%s\n\t%s",
                proj_errno_string(idErr),params);
        //exit
        return idErr;
    }
    //check if params is a cartographic projection
    if((proj_get_type(proyec)!=PJ_TYPE_OTHER_COORDINATE_OPERATION)&&
       (proj_angular_input(proyec,PJ_FWD)==0)&&
       (proj_angular_output(proyec,PJ_FWD)!=0))
    {
        //free memory
        proj_destroy(proyec);
        proj_context_destroy(C);
        //error message
        sprintf(errorText,"The parameters does not correspond to a "
                          "cartographic projection\n\t%s",params);
        //salimos de la función
        return PROJWRAP_ERR_NOT_PROJECTION;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //projection
    proj_trans_generic(proyec,PJ_FWD,u,ieb,nElem,v,ieb,nElem,NULL,0,0,NULL,0,0);
    //check bad projection
    for(i=0;i<nElem;i++)
    {
        auxU = u[i*incElem];
        auxV = v[i*incElem];
        //error checking
        if((!isfinite(auxU))||(!isfinite(auxV))||
           (auxU==HUGE_VAL)||(auxV==HUGE_VAL)||
           (auxU==(-HUGE_VAL))||(auxV==(-HUGE_VAL)))
        {
            //first occurrence
            if((*projectionError)==-1)
            {
                //point position
                *projectionError = (int)i;
            }
            //HUGE_VAL assign to output
            u[i*incElem] = HUGE_VAL;
            v[i*incElem] = HUGE_VAL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //memory free
    proj_destroy(proyec);
    proj_context_destroy(C);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //exit
    return idErr;
}
/******************************************************************************/
/******************************************************************************/
int proj_inv(double* u,
             double* v,
             const size_t nElem,
             const size_t incElem,
             const char params[],
             char errorText[],
             int* projectionError)
{
    //error code
    int idErr=0;
    //index for loop
    size_t i=0;
    //byte increment between elements in arrays
    size_t ieb=incElem*sizeof(double);
    //safe context for multithread
    PJ_CONTEXT *C=NULL;
    //projection parameters
    PJ *proyec=NULL;
    //projection information
    PJ_PROJ_INFO infop;
    //auxiliary variables for coordinate checking
    double auxU=0.0,auxV=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //error identifier
    *projectionError = -1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //multithread context
    C = proj_context_create();
    //projection init
    proyec = proj_create(C,params);
    //errors
    if(proyec==0)
    {
        //PROJ error code
        idErr = proj_context_errno(C);
        //memory free
        proj_context_destroy(C);
        //error text
        sprintf(errorText,"Error in projection parameters\n\t%s\n\t%s",
                proj_errno_string(idErr),params);
        //exit
        return idErr;
    }
    //check if params is a cartographic projection
    if((proj_get_type(proyec)!=PJ_TYPE_OTHER_COORDINATE_OPERATION)&&
       (proj_angular_input(proyec,PJ_INV)!=0)&&
       (proj_angular_output(proyec,PJ_INV)==0))
    {
        //free memory
        proj_destroy(proyec);
        proj_context_destroy(C);
        //error message
        sprintf(errorText,"The parameters does not correspond to a "
                          "cartographic projection\n\t%s",params);
        //salimos de la función
        return PROJWRAP_ERR_NOT_PROJECTION;
    }
    //extract projection information
    infop = proj_pj_info(proyec);
    //check if inverse step exists
    if(infop.has_inverse==0)
    {
        //free memory
        proj_destroy(proyec);
        proj_context_destroy(C);
        //error message
        //error text
        sprintf(errorText,"Inverse step dooes not exists\n\t%s",params);
        //exit
        return PROJWRAP_ERR_NOT_INV_PROJ;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //projection
    proj_trans_generic(proyec,PJ_INV,u,ieb,nElem,v,ieb,nElem,NULL,0,0,NULL,0,0);
    //check bad projection
    for(i=0;i<nElem;i++)
    {
        auxU = u[i*incElem];
        auxV = v[i*incElem];
        //error checking
        if((!isfinite(auxU))||(!isfinite(auxV))||
           (auxU==HUGE_VAL)||(auxV==HUGE_VAL)||
           (auxU==(-HUGE_VAL))||(auxV==(-HUGE_VAL)))
        {
            //first occurrence
            if((*projectionError)==-1)
            {
                //point position
                *projectionError = (int)i;
            }
            //HUGE_VAL assign to output
            u[i*incElem] = HUGE_VAL;
            v[i*incElem] = HUGE_VAL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //memory free
    proj_destroy(proyec);
    proj_context_destroy(C);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //exit
    return idErr;
}
/******************************************************************************/
/******************************************************************************/
int proj_transform(double* u,
                   double* v,
                   double* z,
                   double* t,
                   const size_t nElem,
                   const size_t incElem,
                   const char paramsStart[],
                   const char paramsEnd[],
                   char errorText[],
                   int* transformationError)
{
    //error code
    int idErr=0;
    //index for loop
    size_t i=0;
    //byte increment between elements in arrays
    size_t ieb=incElem*sizeof(double);
    //safe context for multithread
    PJ_CONTEXT *C=NULL;
    //transformation parameters
    PJ *trans=NULL;
    //auxiliary variables for coordinate checking
    double auxU=0.0,auxV=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //error identifier
    *transformationError = -1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //multithread context
    C = proj_context_create();
    //transformation init
    trans = proj_create_crs_to_crs(C,paramsStart,paramsEnd,NULL);
    //error checking
    if(trans==0)
    {
        //PROJ error code
        idErr = proj_context_errno(C);
        //memory free
        proj_context_destroy(C);
        //error text
        sprintf(errorText,"Error in definition parameters\n\t%s\n\t%s\n\t%s",
                paramsStart,paramsEnd,proj_errno_string(idErr));
        //exit
        return idErr;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //transformation
    proj_trans_generic(trans,PJ_FWD,
                       u,ieb,nElem,v,ieb,nElem,z,ieb,nElem,t,ieb,nElem);
    //check bad projection
    for(i=0;i<nElem;i++)
    {
        auxU = u[i*incElem];
        auxV = v[i*incElem];
        //error checking
        if((!isfinite(auxU))||(!isfinite(auxV))||
           (auxU==HUGE_VAL)||(auxV==HUGE_VAL)||
           (auxU==(-HUGE_VAL))||(auxV==(-HUGE_VAL)))
        {
            //first occurrence
            if((*transformationError)==-1)
            {
                //point position
                *transformationError = (int)i;
            }
            //HUGE_VAL assign to output
            u[i*incElem] = HUGE_VAL;
            v[i*incElem] = HUGE_VAL;
            if(z!=NULL)
            {
                z[i*incElem] = HUGE_VAL;
            }
            if(t!=NULL)
            {
                t[i*incElem] = HUGE_VAL;
            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //memory free
    proj_destroy(trans);
    proj_context_destroy(C);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //exit
    return idErr;
}
/******************************************************************************/
/******************************************************************************/
/** @} */
