/* -*- coding: utf-8 -*- */
/**
\ingroup geom
@{
\file arco.c
\brief Definición de funciones para la realización de cálculos con arcos de
       circunferencia.
\author José Luis García Pallero, jgpallero@gmail.com
\date 08 de agosto de 2013
\copyright
Copyright (c) 2013, 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/arco.h"
/******************************************************************************/
/******************************************************************************/
int ArcosCircMaxDisjuntos(const double tol,
                          const double lonMin1,
                          const double lonMax1,
                          const double lonMin2,
                          const double lonMax2)
{
    //comprobamos los casos de trabajo
    if(((lonMax1-lonMin1)>(GEOC_CONST_PI-tol))||
       ((lonMax2-lonMin2)>(GEOC_CONST_PI-tol)))
    {
        //indicamos que no son disjuntos, aunque en puridad no lo podemos decir,
        //pero estos casos son difíciles de determinar porque el arco cruza el
        //meridiano origen o su antimeridiano
        return 0;
    }
    else
    {
        //consideramos únicamente solapamientos en longitud
        return (!((lonMin1<(lonMax2+tol))&&(lonMax1>(lonMin2-tol))));
    }
}
/******************************************************************************/
/******************************************************************************/
double AcimutArcoCircMaxEsf(const double tol,
                            const double latA,
                            const double lonA,
                            const double latB,
                            const double lonB,
                            double mRot[][3])
{
    //variables auxiliares
    double sLat=0.0,cLat=0.0,sLon=0.0,cLon=0.0;
    double x=0.0,y=0.0,z=0.0,yR=0.0,zR=0.0;
    //variable de salida
    double aci=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos las razones trigonométricas de las coordenadas del vértice A
    sLat = sin(latA);
    cLat = cos(latA);
    sLon = sin(lonA);
    cLon = cos(lonA);
    //construimos la matriz de rotación para llevar el punto A a (lat=0,lon=0)
    //primero se rota en torno a Z el valor de la longitud, y luego en torno a Y
    //el valor -latitud
    //tal y como aquí se aplican, las rotaciones giran el sistema de coordenadas
    //en lugar del radio vector del punto de trabajo
    //     | cos(lon) sin(lon) 0|
    //Rz = |-sin(lon) cos(lon) 0|
    //     |        0        0 1|
    //
    //     |cos(-lat) 0 -sin(-lat)|   | cos(lat) 0 sin(lat)|
    //Ry = |        0 1          0| = |        0 1        1|
    //     |sin(-lat) 0  cos(-lat)|   |-sin(lat) 0 cos(lat)|
    //
    //la matriz de rotación es, entonces R = Ry*Rz, donde este orden de
    //multiplicación de las matrices indica que primero se rota en torno a Z y
    //luego en torno a Y
    mRot[0][0] = cLat*cLon;
    mRot[0][1] = cLat*sLon;
    mRot[0][2] = sLat;
    mRot[1][0] = -sLon;
    mRot[1][1] = cLon;
    mRot[1][2] = 0.0;
    mRot[2][0] = -sLat*cLon;
    mRot[2][1] = -sLat*sLon;
    mRot[2][2] = cLat;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si los puntos son coincidentes
    //aunque esta comprobación se podría haber hecho al principio, se hace aquí
    //para poder construir previamente la matriz de rotación que lleve al punto
    //A a (lat=0,lon=0)
    if(GEOC_ES_CERO(latA-latB,tol)&&GEOC_ES_CERO(lonA-lonB,tol))
    {
        //imponemos un acimut igual a 0.0
        aci = 0.0;
        //salimos de la función
        return aci;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //transformamos las coordenadas del extremo B a cartesianas
    cLat = cos(latB);
    x = cLat*cos(lonB);
    y = cLat*sin(lonB);
    z = sin(latB);
    //transformamos al nuevo sistema las coordenadas Y y Z de B
    AplicaMatrizRotacionCoorCart(1,x,y,z,mRot,NULL,&yR,&zR);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //esta variable es como si fuera una especie de longitud de B en un sistema
    //en el cual el eje de rotación fuese el X del nuevo sistema
    //si es >= 0.0, es ya directamente el acimut buscado
    aci = atan2(yR,zR);
    //metemos la variable auxiliar en el dominio [0,2*pi), si ha lugar
    if(aci<0.0)
    {
        aci += 2.0*GEOC_CONST_PI;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return aci;
}
/******************************************************************************/
/******************************************************************************/
void RotaArco00Ecuador(const double tol,
                       const double latA,
                       const double lonA,
                       const double latB,
                       const double lonB,
                       double mRot[][3],
                       double* lonBR)
{
    //coordenadas cartesianas
    double x=0.0,y=0.0,z=0.0,xR=0.0,yR=0.0;
    //matriz de rotación auxiliar
    double mRotAux[3][3];
    //acimut del arco AB
    double aci=0.0;
    //variables auxiliares
    double alfa=0.0,cLat=0.0,sAlfa=0.0,cAlfa=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //roto el sistema para llevar el punto A al punto (lat=0,lon=0)
    aci = AcimutArcoCircMaxEsf(tol,latA,lonA,latB,lonB,mRotAux);
    //el ángulo a rotar sobre el eje X será 90-aci
    alfa = GEOC_CONST_PI/2.0-aci;
    //calculo la matriz de rotación
    //tal y como aquí se aplica, las rotación gira el sistema de coordenadas en
    //lugar del radio vector del punto de trabajo
    //     |1          0         0|
    //Rx = |0  cos(alfa) sin(alfa)|
    //     |0 -sin(alfa) con(alfa)|
    //la matriz de rotación es, entonces R = Rx*mRotAux, donde este orden de
    //multiplicación de las matrices indica que primero se rota en torno a los
    //ejes que indique mRorAux y luego en torno a X
    sAlfa = sin(alfa);
    cAlfa = cos(alfa);
    mRot[0][0] = mRotAux[0][0];
    mRot[0][1] = mRotAux[0][1];
    mRot[0][2] = mRotAux[0][2];
    mRot[1][0] = cAlfa*mRotAux[1][0]+sAlfa*mRotAux[2][0];
    mRot[1][1] = cAlfa*mRotAux[1][1]+sAlfa*mRotAux[2][1];
    mRot[1][2] = sAlfa*mRotAux[2][2]; //+cAlfa*mRotAux[1][2], mRotAux[1][2]==0.0
    mRot[2][0] = -sAlfa*mRotAux[1][0]+cAlfa*mRotAux[2][0];
    mRot[2][1] = -sAlfa*mRotAux[1][1]+cAlfa*mRotAux[2][1];
    mRot[2][2] = cAlfa*mRotAux[2][2]; //-sAlfa*mRotAux[1][2], mRotAux[1][2]==0.0
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay que calcular la longitud de B en el nuevo sistema
    if(lonBR!=NULL)
    {
        //transformamos las coordenadas del extremo B a cartesianas
        cLat = cos(latB);
        x = cLat*cos(lonB);
        y = cLat*sin(lonB);
        z = sin(latB);
        //calculamos las coordenadas X e Y de B en el nuevo sistema
        AplicaMatrizRotacionCoorCart(1,x,y,z,mRot,&xR,&yR,NULL);
        //calculamos la longitud de B en el nuevo sistema
        *lonBR = atan2(yR,xR);
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
void AplicaMatrizRotacionCoorCart(const int sentido,
                                  const double x,
                                  const double y,
                                  const double z,
                                  double mRot[][3],
                                  double* xR,
                                  double* yR,
                                  double* zR)
{
    //comprobamos el sentido de la rotación
    if(sentido>=0)
    {
        //transformamos las coordenadas que se hayan pedido
        if(xR!=NULL)
        {
            *xR = mRot[0][0]*x+mRot[0][1]*y+mRot[0][2]*z;
        }
        if(yR!=NULL)
        {
            *yR = mRot[1][0]*x+mRot[1][1]*y+mRot[1][2]*z;
        }
        if(zR!=NULL)
        {
            *zR = mRot[2][0]*x+mRot[2][1]*y+mRot[2][2]*z;
        }
    }
    else
    {
        //transformamos las coordenadas que se hayan pedido
        if(xR!=NULL)
        {
            *xR = mRot[0][0]*x+mRot[1][0]*y+mRot[2][0]*z;
        }
        if(yR!=NULL)
        {
            *yR = mRot[0][1]*x+mRot[1][1]*y+mRot[2][1]*z;
        }
        if(zR!=NULL)
        {
            *zR = mRot[0][2]*x+mRot[1][2]*y+mRot[2][2]*z;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
void AplicaMatrizRotacionCoorGeod(const int sentido,
                                  const double lat,
                                  const double lon,
                                  double mRot[][3],
                                  double* latR,
                                  double* lonR)
{
    //variables auxiliares
    double x=0.0,y=0.0,z=0.0,xR=0.0,yR=0.0,zR=0.0,cLat=0.0;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si hay que hacer cálculos
    if((latR!=NULL)||(lonR!=NULL))
    {
        //razones trigonométricas auxiliares
        cLat = cos(lat);
        //trasformamos las coordenadas de entrada a cartesianas
        x = cLat*cos(lon);
        y = cLat*sin(lon);
        z = sin(lat);
        //aplicamos la rotación
        AplicaMatrizRotacionCoorCart(sentido,x,y,z,mRot,&xR,&yR,&zR);
        //convertimos de nuevo a geodésicas, si ha lugar
        if(latR!=NULL)
        {
            *latR = asin(zR);
        }
        if(lonR!=NULL)
        {
            *lonR = atan2(yR,xR);
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return;
}
/******************************************************************************/
/******************************************************************************/
int IntersecCircMaxEsfAux(const double tol,
                          const double xC,
                          const double yC,
                          const double zC,
                          const double xD,
                          const double yD,
                          const double zD,
                          double* xP,
                          double* yP,
                          double* zP)
{
    //vectores normales
    double NABx=0.0,NABy=0.0,NABz=0.0,NCDx=0.0,NCDy=0.0,NCDz=0.0;
    double dx=0.0,dy=0.0,dz=0.0;
    //variable auxiliar
    double t=0.0;
    //variable de salida
    int cod=GEOC_ARC_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //vector normal al plano del ecuador AOB
    NABx = 0.0;
    NABy = 0.0;
    NABz = 1.0;
    //vector normal al plano COD
    ProductoVectorial(xC,yC,zC,xD,yD,zD,&NCDx,&NCDy,&NCDz);
    //vector director de la línea intersección de los dos planos
    ProductoVectorial(NABx,NABy,NABz,NCDx,NCDy,NCDz,&dx,&dy,&dz);
    //sólo seguimos si no hay casos especiales
    if((!GEOC_ES_CERO(dx,tol))||
       (!GEOC_ES_CERO(dy,tol))||
       (!GEOC_ES_CERO(dz,tol)))
    {
        //hay intersección
        cod = GEOC_ARC_INTERSEC;
        //comprobamos si hay que devolver coordenadas
        if((xP!=NULL)&&(yP!=NULL)&&(zP!=NULL))
        {
            //calculamos el inverso del módulo del vector (dx,dy,dz)
            t = 1.0/sqrt(dx*dx+dy*dy+dz*dz);
            //coordenadas del punto de intersección en la esfera de radio unidad
            *xP = dx*t;
            *yP = dy*t;
            *zP = dz*t;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecArcCirMaxEsferaVertComunAux(const double tol,
                                        const double lonB,
                                        const double latC,
                                        const double lonC,
                                        const double latD,
                                        const double lonD,
                                        double* latP,
                                        double* lonP)
{
    //identificadores de igualdad entre puntos
    int AiC=0,AiD=0,BiC=0,BiD=0;
    //variables auxiliares
    double latA=0.0,lonA=0.0,latB=0.0;
    double lonAAux=0.0,lonCAux=0.0,lonDAux=0.0;
    //variable de salida
    int cod=GEOC_ARC_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //por defecto, las coordenadas de salida son 0.0
    if((latP!=NULL)&&(lonP!=NULL))
    {
        *latP = 0.0;
        *lonP = 0.0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //chequeamos la posible igualdad entre los vértices de los arcos
    AiC = (GEOC_ES_CERO(latA-latC,tol))&&(GEOC_ES_CERO(lonA-lonC,tol));
    AiD = (GEOC_ES_CERO(latA-latD,tol))&&(GEOC_ES_CERO(lonA-lonD,tol));
    BiC = (GEOC_ES_CERO(latB-latC,tol))&&(GEOC_ES_CERO(lonB-lonC,tol));
    BiD = (GEOC_ES_CERO(latB-latD,tol))&&(GEOC_ES_CERO(lonB-lonD,tol));
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos los posibles casos en que los arcos tengan algún extremo común
    if((AiC&&BiD)||(AiD&&BiC))
    {
        //los vértices coinciden, dos a dos: los arcos son el mismo
        cod = GEOC_ARC_INTERSEC_MISMO_ARC;
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice A
            *latP = latA;
            *lonP = lonA;
        }
    }
    else if(AiC)
    {
        //compruebo si D está en el ecuador
        if(GEOC_ES_CERO(latD,tol))
        {
            //comprobamos el tipo de colinelidad
            if((lonB*lonD)<=0.0)
            {
                //si el producto de las longitudes es negativo quiere decir que
                //los extremos B y D están cada uno a un lado del meridiano
                //origen, luego los arcos se tocan en un punto y son colineales
                cod = GEOC_ARC_INTERSEC_EXTREMOS_COLIN;
            }
            else
            {
                //los arcos se tocan en un punto y se solapan parcialmente
                cod = GEOC_ARC_INTERSEC_COLIN;
            }
        }
        else
        {
            //los arcos se tocan en un punto, pero no son colineales
            cod = GEOC_ARC_INTERSEC_EXTREMOS_NO_COLIN;
        }
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice A
            *latP = latA;
            *lonP = lonA;
        }
    }
    else if(AiD)
    {
        //compruebo si C está en el ecuador
        if(GEOC_ES_CERO(latC,tol))
        {
            //comprobamos el tipo de colinelidad
            if((lonB*lonC)<=0.0)
            {
                //si el producto de las longitudes es negativo quiere decir que
                //los extremos B y C están cada uno a un lado del meridiano
                //origen, luego los arcos se tocan en un punto y son colineales
                cod = GEOC_ARC_INTERSEC_EXTREMOS_COLIN;
            }
            else
            {
                //los arcos se tocan en un punto y se solapan parcialmente
                cod = GEOC_ARC_INTERSEC_COLIN;
            }
        }
        else
        {
            //los arcos se tocan en un punto, pero no son colineales
            cod = GEOC_ARC_INTERSEC_EXTREMOS_NO_COLIN;
        }
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice A
            *latP = latA;
            *lonP = lonA;
        }
    }
    else if(BiC)
    {
        //compruebo si D está en el ecuador
        if(GEOC_ES_CERO(latD,tol))
        {
            //ponemos el origen de longitudes en B
            lonAAux = -lonB;
            lonDAux = lonD-lonB;
            //comprobamos el tipo de colinelidad
            if((lonAAux*lonDAux)<=0.0)
            {
                //si el producto de las longitudes es negativo quiere decir que
                //los extremos A y D están cada uno a un lado de B, luego los
                //arcos se tocan en un punto y son colineales
                cod = GEOC_ARC_INTERSEC_EXTREMOS_COLIN;
            }
            else
            {
                //los arcos se tocan en un punto y se solapan parcialmente
                cod = GEOC_ARC_INTERSEC_COLIN;
            }
        }
        else
        {
            //los arcos se tocan en un punto, pero no son colineales
            cod = GEOC_ARC_INTERSEC_EXTREMOS_NO_COLIN;
        }
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice B
            *latP = latB;
            *lonP = lonB;
        }
    }
    else if(BiD)
    {
        //compruebo si C está en el ecuador
        if(GEOC_ES_CERO(latC,tol))
        {
            //ponemos el origen de longitudes en B
            lonAAux = -lonB;
            lonCAux = lonC-lonB;
            //comprobamos el tipo de colinelidad
            if((lonAAux*lonCAux)<=0.0)
            {
                //si el producto de las longitudes es negativo quiere decir que
                //los extremos A y C están cada uno a un lado de B, luego los
                //arcos se tocan en un punto y son colineales
                cod = GEOC_ARC_INTERSEC_EXTREMOS_COLIN;
            }
            else
            {
                //los arcos se tocan en un punto y se solapan parcialmente
                cod = GEOC_ARC_INTERSEC_COLIN;
            }
        }
        else
        {
            //los arcos se tocan en un punto, pero no son colineales
            cod = GEOC_ARC_INTERSEC_EXTREMOS_NO_COLIN;
        }
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice A
            *latP = latB;
            *lonP = lonB;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecArcCirMaxEsferaVertApoyadoAux(const double tol,
                                          const double lonB,
                                          const double latC,
                                          const double lonC,
                                          const double latD,
                                          const double lonD,
                                          double* latP,
                                          double* lonP)
{
    //coordenadas de trabajo
    double xC=0.0,yC=0.0,zC=0.0,xD=0.0,yD=0.0,zD=0.0,xP=0.0,yP=0.0,zP=0.0;
    double latA=0.0,lonA=0.0,latB=0.0,lonP1=0.0,lonP2=0.0;
    //variables auxiliares
    double cLat=0.0;
    //variable de salida
    int cod=GEOC_ARC_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //por defecto, las coordenadas de salida son 0.0
    if((latP!=NULL)&&(lonP!=NULL))
    {
        *latP = 0.0;
        *lonP = 0.0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si el arco CD está sobre el ecuador
    if(GEOC_ES_CERO(latC,tol)&&GEOC_ES_CERO(latD,tol))
    {
        //comprobamos qué punto está en qué segmento
        //consideramos también el caso de que los segmentos sólo se toquen en un
        //extremo
        if(((lonA>(lonC-tol))&&(lonA<(lonD+tol)))||
           ((lonA<(lonC+tol))&&(lonA>(lonD-tol))))
        {
            //el punto A está entre C y D, los arcos se solapan
            cod = GEOC_ARC_INTERSEC_COLIN;
            //comprobamos si hay que asignar coordenadas de salida
            if((latP!=NULL)&&(lonP!=NULL))
            {
                //el punto de corte es el vértice A
                *latP = latA;
                *lonP = lonA;
            }
            //salimos de la función
            return cod;
        }
        else if(((lonB>(lonC-tol))&&(lonB<(lonD+tol)))||
                ((lonB<(lonC+tol))&&(lonB>(lonD-tol))))
        {
            //el punto B está entre C y D, los arcos se solapan
            cod = GEOC_ARC_INTERSEC_COLIN;
            //comprobamos si hay que asignar coordenadas de salida
            if((latP!=NULL)&&(lonP!=NULL))
            {
                //el punto de corte es el vértice B
                *latP = latB;
                *lonP = lonB;
            }
            //salimos de la función
            return cod;
        }
        else
        {
            //salimos de la función
            return cod;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //coordenadas cartesianas tridimensionales geocéntricas de C
    cLat = cos(latC);
    xC = cLat*cos(lonC);
    yC = cLat*sin(lonC);
    zC = sin(latC);
    //coordenadas cartesianas tridimensionales geocéntricas de D
    cLat = cos(latD);
    xD = cLat*cos(lonD);
    yD = cLat*sin(lonD);
    zD = sin(latD);
    //calculamos la intersección de dos círculos máximos
    //aquí ya no cabe caso singular, ya que si los dos arcos estuviesen en el
    //ecuador habría sido detectado en el chequeo del principio de la función
    IntersecCircMaxEsfAux(tol,xC,yC,zC,xD,yD,zD,&xP,&yP,&zP);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos los posibles puntos de intersección
    lonP1 = atan2(yP,xP);
    lonP2 = lonP1+GEOC_CONST_PI;
    lonP2 = (lonP2>GEOC_CONST_PI) ? lonP2-2.0*GEOC_CONST_PI : lonP2;
    //vamos comprobando si algún punto de corte coincide con A, B, C o D
    if(GEOC_ES_CERO(lonP1,tol)||GEOC_ES_CERO(lonP2,tol))
    {
        //los arcos no son colineales
        cod = GEOC_ARC_INTERSEC_EXTREMO_NO_COLIN;
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice A
            *latP = latA;
            *lonP = lonA;
        }
    }
    else if(GEOC_ES_CERO(lonP1-lonB,tol)||GEOC_ES_CERO(lonP2-lonB,tol))
    {
        //los arcos no son colineales
        cod = GEOC_ARC_INTERSEC_EXTREMO_NO_COLIN;
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice B
            *latP = latB;
            *lonP = lonB;
        }
    }
    else if((GEOC_ES_CERO(latC,tol))&&
            (GEOC_ES_CERO(lonP1-lonC,tol)||GEOC_ES_CERO(lonP2-lonC,tol)))
    {
        //los arcos no son colineales
        cod = GEOC_ARC_INTERSEC_EXTREMO_NO_COLIN;
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice C
            *latP = 0.0;
            *lonP = lonC;
        }
    }
    else if((GEOC_ES_CERO(latD,tol))&&
            (GEOC_ES_CERO(lonP1-lonD,tol)||GEOC_ES_CERO(lonP2-lonD,tol)))
    {
        //los arcos no son colineales
        cod = GEOC_ARC_INTERSEC_EXTREMO_NO_COLIN;
        //comprobamos si hay que asignar coordenadas de salida
        if((latP!=NULL)&&(lonP!=NULL))
        {
            //el punto de corte es el vértice D
            *latP = 0.0;
            *lonP = lonD;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecArcCircMaxEsferaAux(const double tol,
                                const double lonB,
                                const double latC,
                                const double lonC,
                                const double latD,
                                const double lonD,
                                const double xGC,
                                const double yGC,
                                const double zGC,
                                const double xGD,
                                const double yGD,
                                const double zGD,
                                double* latP,
                                double* lonP)
{
    //coordenadas de trabajo
    double xC=0.0,yC=0.0,zC=0.0,xD=0.0,yD=0.0,zD=0.0;
    double xP=0.0,yP=0.0,zP=0.0,lonP1=0.0,lonP2=0.0,lonPI=0.0;
    //variables auxiliares
    double cLat=0.0;
    //variable de salida
    int cod=GEOC_ARC_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //por defecto, las coordenadas de salida son 0.0
    if((latP!=NULL)&&(lonP!=NULL))
    {
        *latP = 0.0;
        *lonP = 0.0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //comprobamos si CD cruza o no el ecuador
    if(((latC>tol)&&(latD>tol))||((latC<-tol)&&(latD<-tol)))
    {
        //salimos de la función
        return cod;
    }
    else
    {
        //casos en que los arcos puedan tener algún extremo común
        cod = IntersecArcCirMaxEsferaVertComunAux(tol,lonB,latC,lonC,latD,lonD,
                                                  latP,lonP);
        //si se ha encontrado intersección, salimos de la función
        if(cod!=GEOC_ARC_NO_INTERSEC)
        {
            //salimos de la función
            return cod;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //comprobamos si un extremo de un arco se apoya en el otro
        cod = IntersecArcCirMaxEsferaVertApoyadoAux(tol,lonB,latC,lonC,
                                                    latD,lonD,latP,lonP);
        //si se ha encontrado intersección, salimos de la función
        if(cod!=GEOC_ARC_NO_INTERSEC)
        {
            //salimos de la función
            return cod;
        }
        ////////////////////////////////////////////////////////////////////////
        ////////////////////////////////////////////////////////////////////////
        //comprobamos si no se han pasado coordenadas cartesianas para C
        if(EsGeocNan(xC)&&EsGeocNan(yC)&&EsGeocNan(zC))
        {
            //coordenadas cartesianas tridimensionales geocéntricas de C
            cLat = cos(latC);
            xC = cLat*cos(lonC);
            yC = cLat*sin(lonC);
            zC = sin(latC);
        }
        else
        {
            //copiamos las coordenadas pasadas
            xC = xGC;
            yC = yGC;
            zC = zGC;
        }
        //comprobamos si no se han pasado coordenadas cartesianas para D
        if(EsGeocNan(xD)&&EsGeocNan(yD)&&EsGeocNan(zD))
        {
            //coordenadas cartesianas tridimensionales geocéntricas de D
            cLat = cos(latD);
            xD = cLat*cos(lonD);
            yD = cLat*sin(lonD);
            zD = sin(latD);
        }
        else
        {
            //copiamos las coordenadas pasadas
            xD = xGD;
            yD = yGD;
            zD = zGD;
        }
        //calculamos la intersección de dos círculos máximos
        //aquí ya no cabe caso singular, ya que si los dos arcos estuviesen en
        //el ecuador habría sido detectado por la llamadas a
        //IntersecArcCirMaxEsferaVertComunAux() o
        //IntersecArcCirMaxEsferaVertApoyadoAux()
        IntersecCircMaxEsfAux(tol,xC,yC,zC,xD,yD,zD,&xP,&yP,&zP);
        //calculamos los posibles puntos de intersección
        lonP1 = atan2(yP,xP);
        lonP2 = lonP1+GEOC_CONST_PI;
        lonP2 = (lonP2>GEOC_CONST_PI) ? lonP2-2.0*GEOC_CONST_PI : lonP2;
        //compruebo si alguna de esas longitudes está en el segmento AB
        if(((lonP1>-tol)&&(lonP1<lonB+tol))||((lonP1<tol)&&(lonP1>lonB-tol)))
        {
            //asigno el código de salida
            cod = GEOC_ARC_INTERSEC;
            //longitud de la intersección
            lonPI = lonP1;
        }
        else if(((lonP2>-tol)&&(lonP2<lonB+tol))||
                ((lonP2<tol)&&(lonP2>lonB-tol)))
        {
            //asigno el código de salida
            cod = GEOC_ARC_INTERSEC;
            //longitud de la intersección
            lonPI = lonP2;
        }
        //compruebo si, habiendo corte, hay coordenadas de salida
        if((cod!=GEOC_ARC_NO_INTERSEC)&&(latP!=NULL)&&(lonP!=NULL))
        {
            //asigno las coordenadas a las variables de salida
            *latP = 0.0;
            *lonP = lonPI;
        }
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
int IntersecArcCircMaxEsfera(const double latA,
                             const double lonA,
                             const double latB,
                             const double lonB,
                             const double latC,
                             const double lonC,
                             const double latD,
                             const double lonD,
                             double* latP,
                             double* lonP)
{
    //tolerancia angular
    double tol=fabs(GEOC_ARC_RES_ANG);
    //coordenadas de trabajo
    double xC=0.0,yC=0.0,zC=0.0,xD=0.0,yD=0.0,zD=0.0;
    double xCR=0.0,yCR=0.0,zCR=0.0,xDR=0.0,yDR=0.0,zDR=0.0;
    double lonBR=0.0,latCR=0.0,lonCR=0.0,latDR=0.0,lonDR=0.0;
    double latPR=0.0,lonPR=0.0;
    //matriz de rotación
    double mRot[3][3];
    //variable auxiliar
    double cLat=0.0;
    //variable de salida
    int cod=GEOC_ARC_NO_INTERSEC;
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //por defecto, si no hay intersección, las coordenadas de salida son 0.0
    if((latP!=NULL)&&(lonP!=NULL))
    {
        *latP = 0.0;
        *lonP = 0.0;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //si los rectángulos son disjuntos, los segmentos no se tocan
    if(ArcosCircMaxDisjuntos(tol,
                             GEOC_MIN(lonA,lonB),GEOC_MAX(lonA,lonB),
                             GEOC_MIN(lonC,lonD),GEOC_MAX(lonC,lonD)))
    {
        //salimos de la función
        return cod;
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos la matriz de rotación para llevar AB al ecuador
    RotaArco00Ecuador(tol,latA,lonA,latB,lonB,mRot,&lonBR);
    //calculamos las coordenadas cartesianas de los puntos C y D
    cLat = cos(latC);
    xC = cLat*cos(lonC);
    yC = cLat*sin(lonC);
    zC = sin(latC);
    cLat = cos(latD);
    xD = cLat*cos(lonD);
    yD = cLat*sin(lonD);
    zD = sin(latD);
    //rotamos los puntos C y D
    AplicaMatrizRotacionCoorCart(1,xC,yC,zC,mRot,&xCR,&yCR,&zCR);
    AplicaMatrizRotacionCoorCart(1,xD,yD,zD,mRot,&xDR,&yDR,&zDR);
    //calculamos las coordenadas geodésicas rotadas
    latCR = asin(zCR);
    lonCR = atan2(yCR,xCR);
    latDR = asin(zDR);
    lonDR = atan2(yDR,xDR);
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //calculamos la intersección
    cod = IntersecArcCircMaxEsferaAux(tol,lonBR,latCR,lonCR,latDR,lonDR,xCR,yCR,
                                      zCR,xDR,yDR,zDR,&latPR,&lonPR);
    //transformamos el resultado al sistema original, si ha lugar
    if((cod!=GEOC_ARC_NO_INTERSEC)&&(latP!=NULL)&&(lonP!=NULL))
    {
        AplicaMatrizRotacionCoorGeod(-1,latPR,lonPR,mRot,latP,lonP);
    }
    ////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////////////////////
    //salimos de la función
    return cod;
}
/******************************************************************************/
/******************************************************************************/
/** @} */
/******************************************************************************/
/******************************************************************************/
/* 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; */
