/* -*- coding: utf-8 -*- */
/**
\ingroup geom
@{
\file dpeuckere.c
\brief Definición de funciones auxiliares para el aligerado de polilíneas sobre
       la superficie de la esfera, basadas en el algoritmo de Douglas-Peucker.
\author José Luis García Pallero, jgpallero@gmail.com
\note Este fichero contiene funciones paralelizadas con OpenMP.
\date 15 de agosto de 2013
\copyright
Copyright (c) 2013-2020, José Luis García Pallero. All rights reserved.
\par
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
\par
- Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this
  list of conditions and the following disclaimer in the documentation and/or
  other materials provided with the distribution.
- Neither the name of the copyright holders nor the names of its contributors
  may be used to endorse or promote products derived from this software without
  specific prior written permission.
\par
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/******************************************************************************/
/******************************************************************************/
#include"libgeoc/dpeuckere.h"
/******************************************************************************/
/******************************************************************************/
int GeocParOmpDpeuckere(char version[])
{
    //comprobamos si hay paralelización
#if defined(_OPENMP)
    //comprobamos si hay que extraer versión
    if(version!=NULL)
    {
        //calculamos la versión
        VersionOpenMP(_OPENMP,version);
    }
    //salimos de la función
    return 1;
#else
    if(version!=NULL)
    {
        //utilizamos la variable version para que no dé warming al compilar
        strcpy(version,"");
    }
    //salimos de la función
    return 0;
#endif
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerOriginalEsfera(const double* x,
                                  const double* y,
                                  const size_t nPtos,
                                  const size_t incX,
                                  const size_t incY,
                                  const double tol,
                                  const size_t posIni,
                                  const size_t posFin,
                                  char* usados)
{
    //índice para recorrer bucles
    size_t i=0;
    //seno de la distancia angular de los puntos al segmento base
    double dist=0.0;
    //valor absoluto del seno de la tolerancia, excepto en el caso en que ésta
    //sea mayor que pi/2, que hacemos 1-cos(tol) para que quede un valor mayor
    //que 1
    double stol=(tol<=(2.0*GEOC_CONST_PI)) ? fabs(sin(tol)) : 1.0-cos(tol);
    //posición de la distancia máxima
    size_t pos=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos casos especiales
    if((nPtos<=2)||(stol==0.0))
    {
        //se usan todos los puntos
        for(i=0;i<nPtos;i++)
        {
            usados[i] = 1;
        }
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //sólo continuamos si los puntos extremos no están seguidos
    if(posIni!=(posFin-1))
    {
        //calculamos la distancia máxima de los puntos de trabajo al arco
        //formado por los extremos
        dist = DouglasPeuckerSenDistMaxEsfera(y,x,incY,incX,posIni,posFin,&pos);
        //comprobamos si esa distancia está fuera de tolerancia
        if(dist>stol)
        {
            //indicamos que el punto se usa
            usados[pos] = 1;
            //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel sections default(none) \
 shared(incY,y,incX,x,nPtos,tol,posIni,posFin,pos,usados)
#endif
{
#if defined(_OPENMP)
#pragma omp section
#endif
            //aplicamos el algoritmo a la parte anterior al punto de trabajo
            DouglasPeuckerOriginalEsfera(x,y,nPtos,incX,incY,tol,posIni,pos,
                                         usados);
#if defined(_OPENMP)
#pragma omp section
#endif
            //aplicamos el algoritmo a la parte posterior al punto de trabajo
            DouglasPeuckerOriginalEsfera(x,y,nPtos,incX,incY,tol,pos,posFin,
                                         usados);
} // --> fin del #pragma omp parallel sections
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
size_t* DouglasPeuckerRobustoEsfera(const double* x,
                                    const double* y,
                                    const size_t nPtos,
                                    const size_t incX,
                                    const size_t incY,
                                    const double tol,
                                    const int paralelizaTol,
                                    const enum GEOC_DPEUCKER_ROBUSTO robusto,
                                    const size_t nSegRobOrig,
                                    const size_t nSegRobAuto,
                                    size_t* nPtosSal)
{
    //índices para recorrer bucles
    size_t i=0,j=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //tolerancia angular
    double tolAng=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de trabajo
    double lonFinR=0.0;
    //matriz de rotación
    double mRot[3][3];
    //variable indicadora de punto en tolerancia
    int entol=0;
    //identificador de caso especial
    int hayCasoEspecial=0;
    //identificadores de utilización de algoritmos semi robustos
    int robOrig=0,robAuto=0;
    //número de elementos de trabajo internos del vector de salida
    size_t nElem=0;
    //identificador de paralelización
    int paraleliza=0;
    //vectores para almacenar coordenadas cartesianas geocéntricas
    double* xG=NULL;
    double* yG=NULL;
    double* zG=NULL;
    //variable auxiliar
    double cLat=0.0,latVert=0.0,lonVert=0.0;
    //vector de salida
    size_t* sal=NULL;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos casos especiales
    sal = CasosEspecialesAligeraPolilinea(x,y,nPtos,incX,incY,atol,nPtosSal,
                                          &hayCasoEspecial);
    //comprobamos si ha habido algún caso especial
    if(hayCasoEspecial)
    {
        //comprobamos si ha ocurrido algún error de asignación de memoria
        if(nPtos&&(sal==NULL))
        {
            //mensaje de error
            GEOC_ERROR("Error de asignación de memoria");
            //salimos de la función
            return NULL;
        }
        else
        {
            //salimos de la función
            return sal;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //asignamos memoria para las coordenadas cartesianas geocéntricas
    xG = (double*)malloc(nPtos*sizeof(double));
    yG = (double*)malloc(nPtos*sizeof(double));
    zG = (double*)malloc(nPtos*sizeof(double));
    //comprobamos los posibles errores
    if((xG==NULL)||(yG==NULL)||(zG==NULL))
    {
        //liberamos la posible memoeia asignada
        free(xG);
        free(yG);
        free(zG);
        //mensaje de error
        GEOC_ERROR("Error de asignación de memoria");
        //salimos de la función
        return NULL;
    }
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(nPtos,incY,y,incX,x,xG,yG,zG) \
 private(i,latVert,lonVert,cLat)
#endif
    //recorremos los puntos de trabajo
    for(i=0;i<nPtos;i++)
    {
        //extraemos las coordenadas geodésicas
        latVert = y[i*incY];
        lonVert = x[i*incX];
        //calculamos el coseno de la latitud
        cLat = cos(latVert);
        //calculamos las coordenadas cartesianas, considerando radio unidad
        xG[i] = cLat*cos(lonVert);
        yG[i] = cLat*sin(lonVert);
        zG[i] = sin(latVert);
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si se utiliza algoritmo de intersección con línea original
    if((robusto==GeocDPeuckerRobSi)||(robusto==GeocDPeuckerRobOrig))
    {
        robOrig = 1;
    }
    //comprobamos si se utiliza algoritmo de intersección con línea generada
    if((robusto==GeocDPeuckerRobSi)||(robusto==GeocDPeuckerRobAuto))
    {
        robAuto = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //inicializamos el indicador interno de tamaño del vector
    nElem = GEOC_DPEUCKER_BUFFER_PTOS;
    //asignamos memoria para el vector de salida
    sal = (size_t*)malloc(nElem*sizeof(size_t));
    //comprobamos los posibles errores
    if(sal==NULL)
    {
        //liberamos la posible memoria asignada
        free(xG);
        free(yG);
        free(zG);
        //mensaje de error
        GEOC_ERROR("Error de asignación de memoria");
        //salimos de la función
        return NULL;
    }
    //indicamos que el primer punto siempre se usa
    *nPtosSal = 1;
    sal[0] = 0;
    //puntos de trabajo para iniciar los cálculos
    i = 0;
    j = 2;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //entramos en un bucle mientras no hayamos llegado hasta el último punto
    while(j<nPtos)
    {
        //hacemos el cambio de sistema para qua la base AB quede en el ecuador,
        //con A en el punto (lat=0,lon=0)
        RotaArco00Ecuador(tolAng,y[i*incY],x[i*incX],y[j*incY],x[j*incX],mRot,
                          &lonFinR);
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //comprobamos si los puntos intermedios están en tolerancia
        //sólo paralelizamos si el número de puntos intermedios es mayor que uno
        if(paraleliza&&paralelizaTol&&((j-i-1)>1))
        {
            //aplicamos el algoritmo en paralelo
            entol = DouglasPeuckerPuntosEnTolEsferaOMP(x,y,incX,incY,xG,yG,zG,
                                                       atol,i,j,i+1,j-1,lonFinR,
                                                       mRot);
        }
        else
        {
            //aplicamos el algoritmo en serie
            entol = DouglasPeuckerPuntosEnTolEsferaSerie(x,y,incX,incY,xG,yG,zG,
                                                         atol,i,j,i+1,j-1,
                                                         lonFinR,mRot);
        }
        //comprobamos si todos los puntos están en tolerancia
        if(entol)
        {
            //pasamos al siguiente punto como extremo del segmento
            j++;
        }
        else
        {
            //el punto final será el anterior al actual, ya que con el actual
            //hay al menos un vértice fuera de tolerancia y con el anterior se
            //comprobó en el paso previo del bucle que no había ningún vértice
            //fuera
            j--;
            //aplicación del algoritmo de intersección con puntos originales
            if(robOrig)
            {
                //aplicamos el algoritmo
                DouglasPeuckerRobIntersecOrigEsfera(x,y,nPtos,incX,incY,xG,yG,
                                                    zG,nSegRobOrig,i,&j);
            }
            //aplicación del algoritmo de auto intersección
            if(robAuto)
            {
                //aplicamos el algoritmo
                DouglasPeuckerRobAutoIntersecEsfera(x,y,incX,incY,xG,yG,zG,i,&j,
                                                    sal,*nPtosSal,nSegRobAuto);
            }
            //añadimos al contador el nuevo punto
            (*nPtosSal)++;
            //comprobamos si hay que reasignar memoria
            if((*nPtosSal)>nElem)
            {
                //añadimos otro grupo de puntos
                nElem += GEOC_DPEUCKER_BUFFER_PTOS;
                //asignamos memoria para el vector de salida
                sal = (size_t*)realloc(sal,nElem*sizeof(size_t));
                //comprobamos los posibles errores
                if(sal==NULL)
                {
                    //liberamos la posible memoria asignada
                    free(xG);
                    free(yG);
                    free(zG);
                    //mensaje de error
                    GEOC_ERROR("Error de asignación de memoria");
                    //salimos de la función
                    return NULL;
                }
            }
            //añadimos el punto al vector de salida
            sal[(*nPtosSal)-1] = j;
            //actualizamos los índices de los puntos de trabajo
            i = j;
            j = i+2;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay que añadir el último punto
    if((sal[(*nPtosSal)-1]!=(nPtos-1))&&
       ((x[sal[(*nPtosSal)-1]*incX]!=x[(nPtos-1)*incX])||
        (y[sal[(*nPtosSal)-1]*incY]!=y[(nPtos-1)*incY])))
    {
        //añadimos al contador el último punto
        (*nPtosSal)++;
        //comprobamos si hay que reasignar memoria
        if((*nPtosSal)>nElem)
        {
            //añadimos otro grupo de puntos
            nElem += GEOC_DPEUCKER_BUFFER_PTOS;
            //asignamos memoria para el vector de salida
            sal = (size_t*)realloc(sal,nElem*sizeof(size_t));
            //comprobamos los posibles errores
            if(sal==NULL)
            {
                //liberamos la posible memoria asignada
                free(xG);
                free(yG);
                free(zG);
                //mensaje de error
                GEOC_ERROR("Error de asignación de memoria");
                //salimos de la función
                return NULL;
            }
        }
        //asignamos el último punto
        sal[(*nPtosSal)-1] = nPtos-1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si el vector de salida tiene demasiada memoria asignada
    if(nElem>(*nPtosSal))
    {
        //ajustamos el tamaño del vector de salida
        sal = (size_t*)realloc(sal,(*nPtosSal)*sizeof(size_t));
        //comprobamos los posibles errores
        if(sal==NULL)
        {
            //liberamos la posible memoria asignada
            free(xG);
            free(yG);
            free(zG);
            //mensaje de error
            GEOC_ERROR("Error de asignación de memoria");
            //salimos de la función
            return NULL;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //liberamos la posible memoria asignada
    free(xG);
    free(yG);
    free(zG);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return sal;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerPuntosEnTolEsferaOMP(const double* x,
                                       const double* y,
                                       const size_t incX,
                                       const size_t incY,
                                       const double* xG,
                                       const double* yG,
                                       const double* zG,
                                       const double tol,
                                       const size_t posBaseIni,
                                       const size_t posBaseFin,
                                       const size_t posPtoIni,
                                       const size_t posPtoFin,
                                       const double lonFinR,
                                       double mRot[][3])
{
    //índice para recorrer bucles
    size_t i=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //identificador de punto fuera de tolerancia
    int ftol=0;
    //coordenadas de los vértices de trabajo
    double xTrab=0.0,yTrab=0.0,xGTrab=0.0,yGTrab=0.0,zGTrab=0.0;
    //distancia calculada
    double dist=0.0;
    //identificador de existencia de coordenadas cartesianas geocéntricas
    int ccart=0;
    //variable de salida
    int entol=1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos salida rápida
    if((posBaseIni+1)>=posBaseFin)
    {
        //salimos de la función
        return entol;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //valor absoluto del seno de la tolerancia, excepto en el caso en que ésta
    //sea mayor que pi/2, que hacemos 1-cos(tol) para que quede un valor > 1
    atol = (atol<=(2.0*GEOC_CONST_PI)) ? sin(atol) : 1.0-cos(atol);
    //comprobamos si se han pasado coordenadas cartesianas geocénticas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //indicamos que sí existen
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posPtoIni,posPtoFin,incX,x,incY,y,ccart,xG,yG,zG,mRot,lonFinR,atol) \
 private(i,xTrab,yTrab,xGTrab,yGTrab,zGTrab,dist) \
 reduction(+:ftol)
#endif
    //recorremos los puntos de trabajo
    for(i=posPtoIni;i<=posPtoFin;i++)
    {
        //sólo calculo si no se ha encontrado ningún punto fuera de tolerancia
        //en este hilo
        if(!ftol)
        {
            //extraemos las coordenadas del vértice de trabajo
            xTrab = x[i*incX];
            yTrab = y[i*incY];
            //comprobamos si hay coordenadas cartesianas
            if(ccart)
            {
                //extraigo las coordenadas cartesianas del vértice de trabajo
                xGTrab = xG[i];
                yGTrab = yG[i];
                zGTrab = zG[i];
            }
            else
            {
                //como no hay coordenadas cartesianas, asigno NaN
                xGTrab = GeocNan();
                yGTrab = xGTrab;
                zGTrab = xGTrab;
            }
            //calculamos la distancia sobre la esfera de radio unidad
            dist = DouglasPeuckerSenDistMaxEsferaAux(yTrab,xTrab,xGTrab,yGTrab,
                                                     zGTrab,lonFinR,mRot);
            //comprobamos si está fuera de tolerancia
            if(dist>atol)
            {
                //aumentamos el indicador de fuera de tolerancia
                ftol++;
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay algún punto fuera de tolerancia
    if(ftol)
    {
        //indicamos que hay algún punto que no está en tolerancia
        entol = 0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return entol;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerPuntosEnTolEsferaSerie(const double* x,
                                         const double* y,
                                         const size_t incX,
                                         const size_t incY,
                                         const double* xG,
                                         const double* yG,
                                         const double* zG,
                                         const double tol,
                                         const size_t posBaseIni,
                                         const size_t posBaseFin,
                                         const size_t posPtoIni,
                                         const size_t posPtoFin,
                                         const double lonFinR,
                                         double mRot[][3])
{
    //índice para recorrer bucles
    size_t i=0;
    //valor absoluto de la tolerancia
    double atol=fabs(tol);
    //coordenadas de los vértices
    double xTrab=0.0,yTrab=0.0;
    double xGTrab=GeocNan(),yGTrab=GeocNan(),zGTrab=GeocNan();
    //distancia calculada
    double dist=0.0;
    //identificador de existencia de coordenadas cartesianas geocéntricas
    int ccart=0;
    //variable de salida
    int entol=1;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos salida rápida
    if((posBaseIni+1)>=posBaseFin)
    {
        //salimos de la función
        return entol;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //valor absoluto del seno de la tolerancia, excepto en el caso en que ésta
    //sea mayor que pi/2, que hacemos 1-cos(tol) para que quede un valor > 1
    atol = (atol<=(2.0*GEOC_CONST_PI)) ? sin(atol) : 1.0-cos(atol);
    //comprobamos si se han pasado coordenadas cartesianas geocénticas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //indicamos que sí existen
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos a chequear
    for(i=posPtoIni;i<=posPtoFin;i++)
    {
        //extraemos las coordenadas del vértice de trabajo
        xTrab = x[i*incX];
        yTrab = y[i*incY];
        //extraemos las coordenadas cartesianas del vértice de trabajo
        if(ccart)
        {
            xGTrab = xG[i];
            yGTrab = yG[i];
            zGTrab = zG[i];
        }
        //calculamos la distancia sobre la esfera de radio unidad
        dist = DouglasPeuckerSenDistMaxEsferaAux(yTrab,xTrab,xGTrab,yGTrab,
                                                 zGTrab,lonFinR,mRot);
        //comprobamos si está fuera de tolerancia
        if(dist>atol)
        {
            //indicamos que estamos fuera de tolerancia
            entol = 0;
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return entol;
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerRobIntersecOrigEsfera(const double* x,
                                         const double* y,
                                         const size_t nPtos,
                                         const size_t incX,
                                         const size_t incY,
                                         const double* xG,
                                         const double* yG,
                                         const double* zG,
                                         const size_t segAUsar,
                                         const size_t posIni,
                                         size_t* posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas del arco base
    double xA=0.0,yA=0.0,xB=0.0,yB=0.0;
    //posición de parada para comprobar la intersección de arcos
    size_t posParada=0;
    //identificación de paralelización
    int paraleliza=0;
    //variable identificadora de existencia de corte de segmentos
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los puntos de inicio y fin son contiguos, hay salida rápida
    if((posIni+1)>=(*posFin))
    {
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //posición de parada para el chequeo de segmentos/arcos
    posParada = ((segAUsar==0)||(segAUsar>=(nPtos-(*posFin))))
                ? nPtos-1
                : (*posFin)+segAUsar;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas del punto inicial del segmento/arco base (no cambian)
    xA = x[posIni*incX];
    yA = y[posIni*incY];
    //construimos todos los segmentos/arcos base posibles
    for(i=(*posFin);i>posIni;i--)
    {
        //comprobamos si estamos ante el punto posterior al inicial
        if(i==(posIni+1))
        {
            //este punto es el siguiente al punto inicial del segmento/arco base
            *posFin = i;
            //salimos del bucle
            break;
        }
        //coordenadas del punto final del segmento/arco base
        xB = x[i*incX];
        yB = y[i*incY];
        //comprobamos si hay que paralelizar
        if(paraleliza)
        {
            //calculamos en paralelo
            corte = DouglasPeuckerRobIntersecOrigEsferaOMP(xA,yA,xB,yB,x,y,incX,
                                                           incY,xG,yG,zG,i,
                                                           posParada);
        }
        else
        {
            //calculamos en serie
            corte = DouglasPeuckerRobIntersecOrigEsferaSerie(xA,yA,xB,yB,x,y,
                                                             incX,incY,xG,yG,zG,
                                                             i,posParada);
        }
        //comprobamos si no ha habido ninguna intersección
        if(!corte)
        {
            //indicamos el índice del vértice final
            *posFin = i;
            //salimos del bucle de segmentos base
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecOrigEsferaOMP(const double xA,
                                           const double yA,
                                           const double xB,
                                           const double yB,
                                           const double* x,
                                           const double* y,
                                           const size_t incX,
                                           const size_t incY,
                                           const double* xG,
                                           const double* yG,
                                           const double* zG,
                                           const size_t posIni,
                                           const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de los arcos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    double xBR=0.0,xCR=0.0,yCR=0.0,xDR=0.0,yDR=0.0;
    double xGC=0.0,yGC=0.0,zGC=0.0,xGD=0.0,yGD=0.0,zGD=0.0;
    double xGCR=0.0,yGCR=0.0,zGCR=0.0,xGDR=0.0,yGDR=0.0,zGDR=0.0;
    //matriz de rotación
    double mRot[3][3];
    //identificador de haber pasado coordenadas cartesianas
    int ccart=0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //llevamos AB al ecuador, con A en (lat=0,lon=0)
    RotaArco00Ecuador(tol,yA,xA,yB,xB,mRot,&xBR);
    //comprobamos si se han pasado coordenadas cartesianas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //se han pasado
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posIni,posFin,incX,x,incY,y,tol,xA,xB,ccart,xG,yG,zG,mRot,xBR) \
 private(i,xGC,yGC,zGC,xGD,yGD,zGD,xGCR,yGCR,zGCR,xGDR,yGDR,zGDR, \
         xC,yC,xD,yD,xCR,yCR,xDR,yDR) \
 reduction(+:corte)
#endif
    //recorremos los puntos de trabajo
    for(i=posIni;i<posFin;i++)
    {
        //sólo realizo cálculos si no se ha encontrado ningún corte en este hilo
        if(!corte)
        {
            //puntos inicial y final del siguiente segmento/arco de trabajo
            xC = x[i*incX];
            yC = y[i*incY];
            xD = x[(i+1)*incX];
            yD = y[(i+1)*incY];
            //sigo si los rectángulos que encierran a los segmentos se cortan
            if(!ArcosCircMaxDisjuntos(tol,
                                      GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                      GEOC_MIN(xC,xD),GEOC_MAX(xC,xD)))
            {
                //comprobamos si hay coordenadas cartesianas
                if(ccart)
                {
                    //extraigo las coordenadas cartesianas
                    xGC = xG[i];
                    yGC = yG[i];
                    zGC = zG[i];
                    xGD = xG[i+1];
                    yGD = yG[i+1];
                    zGD = zG[i+1];
                    //aplico la rotación a las coordenadas cartesianas
                    AplicaMatrizRotacionCoorCart(1,xGC,yGC,zGC,mRot,&xGCR,&yGCR,
                                                 &zGCR);
                    AplicaMatrizRotacionCoorCart(1,xGD,yGD,zGD,mRot,&xGDR,&yGDR,
                                                 &zGDR);
                    //coordenadas geodésicas de los puntos rotados
                    yCR = asin(zGCR);
                    xCR = atan2(yGCR,xGCR);
                    yDR = asin(zGDR);
                    xDR = atan2(yGDR,xGDR);
                }
                else
                {
                    //como no hay coordenadas cartesianas, asigno NaN
                    xGCR = GeocNan();
                    yGCR = xGCR;
                    zGCR = xGCR;
                    xGDR = xGCR;
                    yGDR = xGCR;
                    zGDR = xGCR;
                    //rotamos las coordenadas de C y D
                    AplicaMatrizRotacionCoorGeod(1,yC,xC,mRot,&yCR,&xCR);
                    AplicaMatrizRotacionCoorGeod(1,yD,xD,mRot,&yDR,&xDR);
                }
                //comprobamos si hay intersección
                corte += DouglasPeuckerRobIntersecEsfera(xBR,xCR,yCR,xDR,yDR,
                                                         xGCR,yGCR,zGCR,xGDR,
                                                         yGDR,zGDR,posIni,i);
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecOrigEsferaSerie(const double xA,
                                             const double yA,
                                             const double xB,
                                             const double yB,
                                             const double* x,
                                             const double* y,
                                             const size_t incX,
                                             const size_t incY,
                                             const double* xG,
                                             const double* yG,
                                             const double* zG,
                                             const size_t posIni,
                                             const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    double xBR=0.0,xCR=0.0,yCR=0.0,xDR=0.0,yDR=0.0;
    double xGC=0.0,yGC=0.0,zGC=0.0,xGD=0.0,yGD=0.0,zGD=0.0;
    double xGCR=GeocNan(),yGCR=GeocNan(),zGCR=GeocNan();
    double xGDR=GeocNan(),yGDR=GeocNan(),zGDR=GeocNan();
    //matriz de rotación
    double mRot[3][3];
    //identificador de haber pasado coordenadas cartesianas
    int ccart=0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //llevamos AB al ecuador, con A en (lat=0,lon=0)
    RotaArco00Ecuador(tol,yA,xA,yB,xB,mRot,&xBR);
    //comprobamos si se han pasado coordenadas cartesianas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //se han pasado
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos de trabajo
    for(i=posIni;i<posFin;i++)
    {
        //puntos inicial y final del siguiente segmento/arco de trabajo
        xC = x[i*incX];
        yC = y[i*incY];
        xD = x[(i+1)*incX];
        yD = y[(i+1)*incY];
        //seguimos si los rectángulos que encierran a los segmentos se cortan
        if(!ArcosCircMaxDisjuntos(tol,
                                  GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                  GEOC_MIN(xC,xD),GEOC_MAX(xC,xD)))
        {
            //comprobamos si hay coordenadas cartesianas
            if(ccart)
            {
                //extraigo las coordenadas cartesianas
                xGC = xG[i];
                yGC = yG[i];
                zGC = zG[i];
                xGD = xG[i+1];
                yGD = yG[i+1];
                zGD = zG[i+1];
                //aplico la rotación a las coordenadas cartesianas
                AplicaMatrizRotacionCoorCart(1,xGC,yGC,zGC,mRot,&xGCR,&yGCR,
                                             &zGCR);
                AplicaMatrizRotacionCoorCart(1,xGD,yGD,zGD,mRot,&xGDR,&yGDR,
                                             &zGDR);
                //coordenadas geodésicas de los puntos rotados
                yCR = asin(zGCR);
                xCR = atan2(yGCR,xGCR);
                yDR = asin(zGDR);
                xDR = atan2(yGDR,xGDR);
            }
            else
            {
                //rotamos las coordenadas de C y D
                AplicaMatrizRotacionCoorGeod(1,yC,xC,mRot,&yCR,&xCR);
                AplicaMatrizRotacionCoorGeod(1,yD,xD,mRot,&yDR,&xDR);
            }
            //comprobamos si hay intersección
            corte = DouglasPeuckerRobIntersecEsfera(xBR,xCR,yCR,xDR,yDR,xGCR,
                                                    yGCR,zGCR,xGDR,yGDR,zGDR,
                                                    posIni,i);
        }
        //si ha habido intersección de segmentos/arcos, salimos del bucle
        if(corte)
        {
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
void DouglasPeuckerRobAutoIntersecEsfera(const double* x,
                                         const double* y,
                                         const size_t incX,
                                         const size_t incY,
                                         const double* xG,
                                         const double* yG,
                                         const double* zG,
                                         const size_t posIni,
                                         size_t* posFin,
                                         const size_t* posAlig,
                                         const size_t nPosAlig,
                                         const size_t segAUsar)
{
    //índice para recorrer bucles
    size_t i=0;
    //coordenadas del arco base
    double xA=0.0,yA=0.0,xB=0.0,yB=0.0;
    //posición de parada para comprobar la intersección de arcos
    size_t posParada=0;
    //identificación de paralelización
    int paraleliza=0;
    //variable identificadora de existencia de corte de segmentos
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los puntos de inicio y fin son contiguos, hay salida rápida
    if((posIni+1)>=(*posFin))
    {
        //salimos de la función
        return;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
#if defined(_OPENMP)
    //comprobamos si hay más de un procesador
    if(omp_get_num_procs()>1)
    {
        //indicamos que hay paralelización
        paraleliza = 1;
    }
#endif
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //posición de parada en el vector posAlig para el chequeo de segmentos/arcos
    posParada = ((segAUsar==0)||(segAUsar>=(nPosAlig-1)))
                ? 0
                : nPosAlig-1-segAUsar;
    //coordenadas del punto inicial del arco base (no cambian)
    xA = x[posIni*incX];
    yA = y[posIni*incY];
    //construimos todos los arcos base posibles
    for(i=(*posFin);i>posIni;i--)
    {
        //comprobamos si estamos ante el punto posterior al inicial
        if(i==(posIni+1))
        {
            //este punto es el siguiente al punto inicial del segmento/arco base
            *posFin = i;
            //salimos del bucle
            break;
        }
        //coordenadas del punto final del segmento/arco base
        xB = x[i*incX];
        yB = y[i*incY];
        //comprobamos si hay que paralelizar
        if(paraleliza)
        {
            //calculamos en paralelo
            corte = DouglasPeuckerRobAutoIntersecEsferaOMP(xA,yA,xB,yB,x,y,incX,
                                                           incY,xG,yG,zG,
                                                           posAlig,nPosAlig,
                                                           nPosAlig-1,
                                                           posParada);
        }
        else
        {
            //calculamos en serie
            corte = DouglasPeuckerRobAutoIntersecEsferaSerie(xA,yA,xB,yB,x,y,
                                                             incX,incY,xG,yG,zG,
                                                             posAlig,nPosAlig,
                                                             nPosAlig-1,
                                                             posParada);
        }
        //comprobamos si no ha habido ninguna intersección
        if(!corte)
        {
            //indicamos el índice del vértice final
            *posFin = i;
            //salimos del bucle de segmentos base
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobAutoIntersecEsferaOMP(const double xA,
                                           const double yA,
                                           const double xB,
                                           const double yB,
                                           const double* x,
                                           const double* y,
                                           const size_t incX,
                                           const size_t incY,
                                           const double* xG,
                                           const double* yG,
                                           const double* zG,
                                           const size_t* posAlig,
                                           const size_t nPosAlig,
                                           const size_t posIni,
                                           const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    double xBR=0.0,xCR=0.0,yCR=0.0,xDR=0.0,yDR=0.0;
    double xGC=0.0,yGC=0.0,zGC=0.0,xGD=0.0,yGD=0.0,zGD=0.0;
    double xGCR=0.0,yGCR=0.0,zGCR=0.0,xGDR=0.0,yGDR=0.0,zGDR=0.0;
    //matriz de rotación
    double mRot[3][3];
    //identificador de haber pasado coordenadas cartesianas
    int ccart=0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //llevamos AB al ecuador, con A en (lat=0,lon=0)
    RotaArco00Ecuador(tol,yA,xA,yB,xB,mRot,&xBR);
    //comprobamos si se han pasado coordenadas cartesianas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //se han pasado
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //paralelización con OpenMP
#if defined(_OPENMP)
#pragma omp parallel for default(none) \
 shared(posIni,posFin,incX,x,posAlig,incY,y,tol,xA,xB,ccart,xG,yG,zG,mRot,xBR, \
        nPosAlig) \
 private(i,xGC,yGC,zGC,xGD,yGD,zGD,xGCR,yGCR,zGCR,xGDR,yGDR,zGDR, \
         xC,yC,xD,yD,xCR,yCR,xDR,yDR) \
 reduction(+:corte)
#endif
    //recorremos los puntos de trabajo
    for(i=posIni;i>posFin;i--)
    {
        //sólo realizo cálculos si no se ha encontrado ningún corte en este hilo
        if(!corte)
        {
            //puntos inicial y final del siguiente segmento/arco de trabajo
            xC = x[posAlig[i]*incX];
            yC = y[posAlig[i]*incY];
            xD = x[posAlig[i-1]*incX];
            yD = y[posAlig[i-1]*incY];
            //sigo si los rectángulos que encierran a los segmentos se cortan
            if(!ArcosCircMaxDisjuntos(tol,
                                      GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                      GEOC_MIN(xC,xD),GEOC_MAX(xC,xD)))
            {
                //comprobamos si hay coordenadas cartesianas
                if(ccart)
                {
                    //extraigo las coordenadas cartesianas
                    xGC = xG[posAlig[i]];
                    yGC = yG[posAlig[i]];
                    zGC = zG[posAlig[i]];
                    xGD = xG[posAlig[i-1]];
                    yGD = yG[posAlig[i-1]];
                    zGD = zG[posAlig[i-1]];
                    //aplico la rotación a las coordenadas cartesianas
                    AplicaMatrizRotacionCoorCart(1,xGC,yGC,zGC,mRot,&xGCR,&yGCR,
                                                 &zGCR);
                    AplicaMatrizRotacionCoorCart(1,xGD,yGD,zGD,mRot,&xGDR,&yGDR,
                                                 &zGDR);
                    //coordenadas geodésicas de los puntos rotados
                    yCR = asin(zGCR);
                    xCR = atan2(yGCR,xGCR);
                    yDR = asin(zGDR);
                    xDR = atan2(yGDR,xGDR);
                }
                else
                {
                    //como no hay coordenadas cartesianas, asigno NaN
                    xGCR = GeocNan();
                    yGCR = xGCR;
                    zGCR = xGCR;
                    xGDR = xGCR;
                    yGDR = xGCR;
                    zGDR = xGCR;
                    //rotamos las coordenadas de C y D
                    AplicaMatrizRotacionCoorGeod(1,yC,xC,mRot,&yCR,&xCR);
                    AplicaMatrizRotacionCoorGeod(1,yD,xD,mRot,&yDR,&xDR);
                }
                //comprobamos si hay intersección
                corte += DouglasPeuckerRobIntersecEsfera(xBR,xCR,yCR,xDR,yDR,
                                                         xGCR,yGCR,zGCR,xGDR,
                                                         yGDR,zGDR,
                                                         posAlig[nPosAlig-1],
                                                         posAlig[i]);
            }
        }
    } // --> fin del #pragma omp parallel for
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobAutoIntersecEsferaSerie(const double xA,
                                             const double yA,
                                             const double xB,
                                             const double yB,
                                             const double* x,
                                             const double* y,
                                             const size_t incX,
                                             const size_t incY,
                                             const double* xG,
                                             const double* yG,
                                             const double* zG,
                                             const size_t* posAlig,
                                             const size_t nPosAlig,
                                             const size_t posIni,
                                             const size_t posFin)
{
    //índice para recorrer bucles
    size_t i=0;
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de los segmentos de trabajo
    double xC=0.0,yC=0.0,xD=0.0,yD=0.0;
    double xBR=0.0,xCR=0.0,yCR=0.0,xDR=0.0,yDR=0.0;
    double xGC=0.0,yGC=0.0,zGC=0.0,xGD=0.0,yGD=0.0,zGD=0.0;
    double xGCR=GeocNan(),yGCR=GeocNan(),zGCR=GeocNan();
    double xGDR=GeocNan(),yGDR=GeocNan(),zGDR=GeocNan();
    //matriz de rotación
    double mRot[3][3];
    //identificador de haber pasado coordenadas cartesianas
    int ccart=0;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //llevamos AB al ecuador, con A en (lat=0,lon=0)
    RotaArco00Ecuador(tol,yA,xA,yB,xB,mRot,&xBR);
    //comprobamos si se han pasado coordenadas cartesianas
    if((xG!=NULL)&&(yG!=NULL)&&(zG!=NULL))
    {
        //se han pasado
        ccart = 1;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //recorremos los puntos de trabajo
    for(i=posIni;i>posFin;i--)
    {
        //puntos inicial y final del siguiente segmento/arco de trabajo
        xC = x[posAlig[i]*incX];
        yC = y[posAlig[i]*incY];
        xD = x[posAlig[i-1]*incX];
        yD = y[posAlig[i-1]*incY];
        //seguimos si los rectángulos que encierran a los segmentos se cortan
        if(!ArcosCircMaxDisjuntos(tol,
                                  GEOC_MIN(xA,xB),GEOC_MAX(xA,xB),
                                  GEOC_MIN(xC,xD),GEOC_MAX(xC,xD)))
        {
            //comprobamos si hay coordenadas cartesianas
            if(ccart)
            {
                //extraigo las coordenadas cartesianas
                xGC = xG[posAlig[i]];
                yGC = yG[posAlig[i]];
                zGC = zG[posAlig[i]];
                xGD = xG[posAlig[i-1]];
                yGD = yG[posAlig[i-1]];
                zGD = zG[posAlig[i-1]];
                //aplico la rotación a las coordenadas cartesianas
                AplicaMatrizRotacionCoorCart(1,xGC,yGC,zGC,mRot,&xGCR,&yGCR,
                                             &zGCR);
                AplicaMatrizRotacionCoorCart(1,xGD,yGD,zGD,mRot,&xGDR,&yGDR,
                                             &zGDR);
                //coordenadas geodésicas de los puntos rotados
                yCR = asin(zGCR);
                xCR = atan2(yGCR,xGCR);
                yDR = asin(zGDR);
                xDR = atan2(yGDR,xGDR);
            }
            else
            {
                //rotamos las coordenadas de C y D
                AplicaMatrizRotacionCoorGeod(1,yC,xC,mRot,&yCR,&xCR);
                AplicaMatrizRotacionCoorGeod(1,yD,xD,mRot,&yDR,&xDR);
            }
            //comprobamos si hay intersección
            corte = DouglasPeuckerRobIntersecEsfera(xBR,xCR,yCR,xDR,yDR,xGCR,
                                                    yGCR,zGCR,xGDR,yGDR,zGDR,
                                                    posAlig[nPosAlig-1],
                                                    posAlig[i]);
        }
        //si ha habido intersección de segmentos/arcos, salimos del bucle
        if(corte)
        {
            //salimos del bucle
            break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
int DouglasPeuckerRobIntersecEsfera(const double xB,
                                    const double xC,
                                    const double yC,
                                    const double xD,
                                    const double yD,
                                    const double xGC,
                                    const double yGC,
                                    const double zGC,
                                    const double xGD,
                                    const double yGD,
                                    const double zGD,
                                    const size_t posFinAB,
                                    const size_t posIniCD)
{
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //variables auxiliares
    double xAux=0.0,yAux=0.0;
    //identificador de intersección
    int inter=GEOC_DPEUCKER_NO_INTERSEC;
    //variable de salida
    int corte=0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //seguimos si los rectángulos que encierran a los arcos se cortan
    if(!ArcosCircMaxDisjuntos(tol,
                              GEOC_MIN(0.0,xB),GEOC_MAX(0.0,xB),
                              GEOC_MIN(xC,xD),GEOC_MAX(xC,xD)))
    {
        //comprobamos la posible intersección de los arcos
        inter = IntersecArcCircMaxEsferaAux(tol,xB,yC,xC,yD,xD,xGC,yGC,zGC,xGD,
                                            yGD,zGD,&yAux,&xAux);
        //compruebo si los dos arcos son contiguos
        if(posFinAB==posIniCD)
        {
            //compruebo si es la sucesión de arco inicial+final
            if((inter!=GEOC_ARC_INTERSEC_MISMO_ARC)&&
               (inter!=GEOC_ARC_INTERSEC_COLIN))
            {
                //en este caso, no hay intersección
                inter = GEOC_DPEUCKER_NO_INTERSEC;
            }
        }
        //unificamos los identificadores de intersección
        if(!((inter==GEOC_ARC_NO_INTERSEC)||(inter==GEOC_DPEUCKER_NO_INTERSEC)))
        {
            //hay intersección
            corte = 1;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return corte;
}
/******************************************************************************/
/******************************************************************************/
double DouglasPeuckerSenDistMaxEsfera(const double* lat,
                                      const double* lon,
                                      const size_t incLat,
                                      const size_t incLon,
                                      const size_t posIni,
                                      const size_t posFin,
                                      size_t* pos)
{
    //índice para recorrer bucles
    size_t i=0;
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas del punto de trabajo
    double latP=0.0,lonP=0.0;
    //matriz de rotación
    double mRot[3][3];
    //longitud del extremo final de la base en el sistema rotado
    double lonFinR=0.0;
    //variable auxiliar
    double distAux=0.0;
    //variable de salida
    double dist=-1.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si los dos puntos están seguidos
    if((posIni+1)==posFin)
    {
        //la posición que devolvemos es la del punto inicial
        *pos = posIni;
        //la distancia devuelta es -1.0
        return dist;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos la matriz de rotación para llevar la base al ecuador
    RotaArco00Ecuador(tol,
                      lat[posIni*incLat],lon[posIni*incLon],
                      lat[posFin*incLat],lon[posFin*incLon],mRot,&lonFinR);
    //recorremos los puntos entre los extremos
    for(i=posIni+1;i<posFin;i++)
    {
        //extraemos las coordenadas del punto de trabajo
        latP = lat[i*incLat];
        lonP = lon[i*incLon];
        //calculamos la distancia desde el punto a la base
        distAux = DouglasPeuckerSenDistMaxEsferaAux(latP,lonP,GeocNan(),
                                                    GeocNan(),GeocNan(),lonFinR,
                                                    mRot);
        //comprobamos si la distancia calculada es mayor que la anterior
        if(distAux>dist)
        {
            //actualizamos la distancia máxima
            dist = distAux;
            //guardamos la posición del punto
            *pos = i;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return dist;
}
/******************************************************************************/
/******************************************************************************/
double DouglasPeuckerSenDistMaxEsferaAux(const double latVert,
                                         const double lonVert,
                                         const double xVert,
                                         const double yVert,
                                         const double zVert,
                                         const double lonBase2R,
                                         double mRot[][3])
{
    //coordenadas cartesianas tridimensionales geocéntricas
    double x=0.0,y=0.0,z=0.0,xR=0.0,yR=0.0,zR=0.0,xB2R=0.0,yB2R=0.0;
    //longitud del vértice en el sistema rotado
    double lonVR=0.0;
    //variables auxiliares
    double cLat=0.0,alfa=0.0,cAlfa=0.0;
    //variable de salida
    double dist=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si se han pasado las coordenadas cartesianas del vértice
    if((!EsGeocNan(xVert))&&(!EsGeocNan(yVert))&&(!EsGeocNan(zVert)))
    {
        //asignamos las coordenadas a las variables de trabajo
        x = xVert;
        y = yVert;
        z = zVert;
    }
    else
    {
        //calculamos las coordenadas tridimensionales del vértice de trabajo
        cLat = cos(latVert);
        x = cLat*cos(lonVert);
        y = cLat*sin(lonVert);
        z = sin(latVert);
    }
    //aplicamos la rotación
    AplicaMatrizRotacionCoorCart(1,x,y,z,mRot,&xR,&yR,&zR);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //distinguimos todos los casos de trabajo
    if((xR==0.0)&&(yR==0.0))
    {
        //el vértice queda en el polo
        dist = 1.0;
    }
    else
    {
        //distinguimos casos especiales
        if(((lonBase2R>0.0)&&(yR<0.0))||((lonBase2R<0.0)&&(yR>0.0)))
        {
            //vértice fuera de la base: izquierda de AB o derecha de BA
            //coseno del ángulo en el espacio que forma el eje X con el vector
            //OV, es decir, entre los vectores (1,0,0) y (xR,yR,zR)
            cAlfa = xR/sqrt(xR*xR+yR*yR+zR*zR);
            //ángulo
            alfa = acos(cAlfa);
            //comprobamos si el ángulo es mayor o menor de pi/2
            if(alfa<=(2.0*GEOC_CONST_PI))
            {
                //la variable de salida es el seno del ángulo
                dist = sin(alfa);
            }
            else
            {
                //la variable de salida es 1 menos el coseno del ángulo, para
                //quede un valor mayor que 1
                dist = 1.0-cos(alfa);
            }
        }
        else
        {
            //longitud del vértice en el sistema rotado
            lonVR = atan2(yR,xR);
            //distinguimos casos especiales
            if(((lonBase2R>0.0)&&(lonVR>lonBase2R))||
               ((lonBase2R<0.0)&&(lonVR<lonBase2R)))
            {
                //vértice fuera de la base: derecha de AB o izquierda de BA
                //coordenadas cartesianas del punto de la base que no es (0,0,0)
                xB2R = cos(lonBase2R);
                yB2R = sin(lonBase2R);
                //coseno del ángulo en el espacio que forma el vector OBase2R
                //con el vector OV, es decir, entre los vectores
                //(xBase2R,yBase2R,0) y (xR,yR,zR)
                cAlfa = (xB2R*xR+yB2R*yR)/sqrt(xR*xR+yR*yR+zR*zR);
                //ángulo
                alfa = acos(cAlfa);
                //comprobamos si el ángulo es mayor o menor de pi/2
                if(alfa<=(2.0*GEOC_CONST_PI))
                {
                    //la variable de salida es el seno del ángulo
                    dist = sin(alfa);
                }
                else
                {
                    //la variable de salida es 1 menos el coseno del ángulo,
                    //para quede un valor mayor que 1
                    dist = 1.0-cos(alfa);
                }
            }
            else
            {
                //en el caso normal, la distancia es directamente el seno de la
                //latitud del vértice, que es igual a la coordenada Z
                dist = fabs(zR);
            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return dist;
}
/******************************************************************************/
/******************************************************************************/
/** @} */
/******************************************************************************/
/******************************************************************************/
/* kate: encoding utf-8; end-of-line unix; syntax c; indent-mode cstyle; */
/* kate: replace-tabs on; space-indent on; tab-indents off; indent-width 4; */
/* kate: line-numbers on; folding-markers on; remove-trailing-space on; */
/* kate: backspace-indents on; show-tabs on; */
/* kate: word-wrap-column 80; word-wrap-marker-color #D2D2D2; word-wrap off; */
