/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "PmGeneralProjection.h"

//=================================================================
//
//  class PmGeneralProjection
//  ( a wrapper around Polar, Mercator and Cylindrical projections)
PmGeneralProjection ::PmGeneralProjection(const MvRequest& reqst) :
    PmProjection(reqst),
    GPhemis(NORTH),
    GPlat0(0.),
    GPlon0(0.),
    GPstlat1(0.),
    GPstlat2(0.)
{
    // Empty
}

bool PmGeneralProjection::CheckGeodeticCoordinates(Location& area)
{
    area = geodCoord_;
    return true;
}

// =============================================================
// class PmMercatorProjectionFactory - builds PmMercator projections
class PmMercatorProjectionFactory : public PmProjectionFactory
{
    PmProjection* Build(const MvRequest& reqst) override
    {
        return new PmMercator(reqst);
    }

public:
    PmMercatorProjectionFactory() :
        PmProjectionFactory("MERCATOR") {}
};

// Instance which is loaded in the "factory"
static PmMercatorProjectionFactory mercatorProjectionFactoryInstance;

// ====================================================================
//
//  PmMercator projection
PmMercator::PmMercator(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)  // PmGeneralProjection ( "MERCATOR" )
{
    // Initialize vertical longitude
    GPlon0 = viewRequest("MAP_VERTICAL_LONGITUDE");
    //	GPlon0 *= DEG_TO_RAD;

    // Initialize standard parallel ( the other values are the default ones)
    GPstlat1 = ((geodCoord_.Top() + geodCoord_.Bottom()) / 2.) * DEG_TO_RAD;

    // Calculate the projection coordinates
    this->CalculateProjectionCoord();
}

// 		GEODETIC TO MERCATOR COORDINATES
Point PmMercator ::LL2PC(Point& ptll)
{
    double equad,  // Squared eccentricity
        aux1,      // Ancillary variables
        aux2, aux3, aux4, aux5, aux6, ptllx, ptlly;

    ptllx = ptll.X();
    ptlly = ptll.Y();

    equad = 2. * Pflt;
    equad -= Pflt * Pflt;
    double a1, a2, a3;

    a1 = tan(ptlly / (double)2);
    a2 = 1. + a1;
    a3 = 1. - a1;
    aux1 = a2 / a3;

    a1 = equad * equad / 4.;
    a1 += equad;
    a2 = equad * equad * equad / 8.;
    a3 = sin(ptlly);
    aux2 = (a1 + a2) * a3;

    a1 = equad * equad / 12.;
    a2 = equad * equad * equad / 16.;
    a3 = sin((double)3 * ptlly);
    aux3 = (a1 + a2) * a3;

    a1 = equad * equad * equad / 80.;
    a2 = sin((double)5 * ptlly);
    aux4 = a1 * a2;
    aux5 = cos(GPstlat1);

    a1 = sin(GPstlat1);
    a1 *= a1;
    a1 *= equad;
    a2 = sqrt((double)1 - a1);
    aux6 = 1. / a2;

    ptllx = Prd * (ptllx - GPlon0) * aux5 * aux6;
    if (aux1 <= 0.0)
        ptlly = BIGFLOAT;
    else
        ptlly = Prd * (log(aux1) - aux2 + aux3 - aux4) * aux5 * aux6;

    return (Point(ptllx, ptlly));
}

//		MERCATOR TO GEODETIC COORDINATES
Point PmMercator ::PC2LL(Point& ptpc)
{
    double equad,  // Squared eccentricity
        pi,        // PII value
        t,         // Ancillary variables
        xx, aux1, aux2, aux3, aux4, aux5, ptpcx, ptpcy;

    ptpcx = ptpc.X();
    ptpcy = ptpc.Y();

    equad = 2. * Pflt;
    equad -= Pflt * Pflt;
    pi = 4. * atan((double)1);
    double a1, a2;
    aux1 = cos(GPstlat1);

    a1 = sin(GPstlat1);
    a1 *= a1;
    a1 *= equad;
    a2 = sqrt((double)1 - a1);
    aux2 = 1. / a2;
    ptpcx = ptpcx / (aux1 * aux2);
    ptpcy = ptpcy / (aux1 * aux2);
    t = exp(-ptpcy / Prd);
    xx = pi / 2. - 2. * atan(t);

    a1 = equad / 2.;
    a1 += 5. * equad * equad / 24.;
    a1 += equad * equad * equad / 12.;
    a2 = sin((double)4 * atan(t));
    aux3 = a1 * a2;

    a1 = 7. * equad * equad / 48.;
    a1 += 29. * equad * equad * equad / 240.;
    a2 = sin((double)8 * atan(t));
    aux4 = -a1 * a2;

    a1 = 7. * equad * equad * equad / 120.;
    a2 = sin((double)12 * atan(t));
    aux5 = a1 * a2;

    ptpcy = xx + aux3 + aux4 + aux5;
    ptpcx = ptpcx / Prd + GPlon0;

    return {ptpcx, ptpcy};
}

double
PmMercator::CheckOriginLongitude()
{
    double x0 = GPlon0 - 180.;
    if ((geodCoord_.Left() < x0) || (geodCoord_.Right() > x0 + 360.))
        GPlon0 = geodCoord_.Left() +
                 ((geodCoord_.Right() - geodCoord_.Left()) / 2.);

    return GPlon0;
}

bool PmMercator::CheckGeodeticCoordinates(Location& area)
{
    bool flag = true;

    // Check if individual values are within the range
    // Magics now allows longitude coordinates to be greater than 360.
    double x1 = geodCoord_.Left();
    double x2 = geodCoord_.Right();
    double y2 = (geodCoord_.Top() > 89.) ? 89 : geodCoord_.Top();
    double y1 = (geodCoord_.Bottom() < -89.) ? -89 : geodCoord_.Bottom();

    // Check if direction is ok (S -> N)
    if (y2 <= y1) {
        y1 = -90.;
        y2 = +90.;
    }

    // Special cases
    if (x2 == x1) {
        if (x2 == 0.)
            x2 = 360.;
        else if (x1 == 180.)
            x1 = -180.;
        else if (x2 == -180.)
            x2 = 180.;
    }

    // Check if some values have been changed
    if ((geodCoord_.Left() != x1) ||
        (geodCoord_.Right() != x2) ||
        (geodCoord_.Bottom() != y1) ||
        (geodCoord_.Top() != y2)) {
        flag = false;
        geodCoord_ = Location(y2, x1, y1, x2);
    }

    area = geodCoord_;

    return flag;
}

// =============================================================
// class PmCylindProjectionFactory - builds PmCylindrical projections
class PmCylindProjectionFactory : public PmProjectionFactory
{
    PmProjection* Build(const MvRequest& reqst) override
    {
        return new PmCylindEquid(reqst);
    }

public:
    PmCylindProjectionFactory() :
        PmProjectionFactory("CYLINDRICAL") {}
};

// Instance which is loaded in the "factory"
static PmCylindProjectionFactory cylindProjectionFactoryInstance;


//==============================================================
// CYLINDRICAL Projection
//
PmCylindEquid::PmCylindEquid(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)
{
    GPlon0 = viewRequest("MAP_VERTICAL_LONGITUDE");
    //	GPlon0 *= DEG_TO_RAD;

    // Calculate the projection coordinates
    this->CalculateProjectionCoord();
}

//	GEODETIC TO EQUIDISTANT CYLINDRICAL COORDINATES
Point PmCylindEquid ::LL2PC(Point& ptll)
{
    auto ptllx = (double)ptll.X();
    auto ptlly = (double)ptll.Y();

    ptllx = Prd * (ptllx - GPlon0) * cos(GPstlat1);
    ptlly = Prd * ptlly;

    return {ptllx, ptlly};
}

//	EQUIDISTANT CYLINDRICAL TO GEODETIC COORDINATES
Point PmCylindEquid ::PC2LL(Point& ptpc)
{
    auto ptpcx = (double)ptpc.X();
    auto ptpcy = (double)ptpc.Y();

    ptpcy = ptpcy / Prd;
    ptpcx = GPlon0 + ptpcx / (Prd * cos(GPstlat1));

    return {ptpcx, ptpcy};
}

double
PmCylindEquid::CheckOriginLongitude()
{
    double x0 = GPlon0 - 180.;
    if ((geodCoord_.Left() < x0) || (geodCoord_.Right() > x0 + 360.))
        GPlon0 = geodCoord_.Left() +
                 ((geodCoord_.Right() - geodCoord_.Left()) / 2.);

    return GPlon0;
}

bool PmCylindEquid::CheckGeodeticCoordinates(Location& area)
{
    bool flag = true;

    // Check if individual values are within the range.
    // Magics now allows longitude coordinates to be greater than 360.
    double x1 = geodCoord_.Left();
    double x2 = geodCoord_.Right();
    double y2 = (geodCoord_.Top() > 90.) ? 90 : geodCoord_.Top();
    double y1 = (geodCoord_.Bottom() < -90.) ? -90 : geodCoord_.Bottom();

    // Check if direction is ok (S -> N)
    if (y2 < y1) {
        double aux = y2;
        y2 = y1;
        y1 = aux;
    }
    else if (y2 == y1) {
        y1 = -90.;
        y2 = +90.;
    }

    // Special cases
    if (x2 == x1) {
        if (x2 == 0.)
            x2 = 360.;
        else if (x1 == 180.)
            x1 = -180.;
        else if (x2 == -180.)
            x2 = 180.;
    }

    // Check if some values have been changed
    if ((geodCoord_.Left() != x1) ||
        (geodCoord_.Right() != x2) ||
        (geodCoord_.Bottom() != y1) ||
        (geodCoord_.Top() != y2)) {
        flag = false;
        geodCoord_ = Location(y2, x1, y1, x2);
    }

    area = geodCoord_;

    return flag;
}

// =============================================================
// class PmPolarProjectionFactory - builds Polar projections
//
class PmPolarProjectionFactory : public PmProjectionFactory
{
    PmProjection* Build(const MvRequest& reqst) override
    {
        return new PmPolarStereo(reqst);
    }

public:
    PmPolarProjectionFactory() :
        PmProjectionFactory("POLAR_STEREOGRAPHIC") {}
};

// Instance which is loaded in the "factory"
static PmPolarProjectionFactory polarProjectionFactoryInstance;

//===============================================================
//  PmPolar Stereo projection
//
PmPolarStereo::PmPolarStereo(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)
{
    if (viewRequest("MAP_HEMISPHERE") == Cached("SOUTH"))
        GPhemis = SOUTH;
    else
        GPhemis = NORTH;

    if (GPhemis == NORTH)
        GPlat0 = 90. * DEG_TO_RAD;
    else
        GPlat0 = -90. * DEG_TO_RAD;

    GPlon0 = viewRequest("MAP_VERTICAL_LONGITUDE");
    //	GPlon0 *= DEG_TO_RAD;

    // Calculate the projection coordinates
    this->CalculateProjectionCoord();
}

//	GEODETIC TO POLAR STEREOGRAPHIC COORDINATES
//
Point PmPolarStereo ::LL2PC(Point& ptll)
{
    double k0,  // Scale factor
        e,      // Eccentricity
        pi,     // Ancillary variables
        lon0,   // auxilliary origin longitude
        t, ro, aux1, aux2, aux3, ptllx, ptlly;

    ptllx = (double)ptll.X();
    ptlly = (double)ptll.Y();

    //	k0 = 0.994;	// Standard parallel 80.1 degrees
    k0 = 0.933;  // Standard parallel 60 degrees
    e = sqrt((double)2 * Pflt - pow(Pflt, (double)2));
    pi = 4. * atan((double)1);

    ptlly *= GPhemis;
    ptllx *= GPhemis;
    lon0 = (GPhemis == 1) ? GPlon0 : -GPlon0;
    lon0 *= DEG_TO_RAD;

    aux1 = (1. - e * sin(ptlly)) / (1. + e * sin(ptlly));
    t = tan((pi / 4.) - (ptlly / 2.)) / pow(aux1, (e / (double)2));
    aux2 = pow(((double)1 + e), ((double)1 + e));
    aux3 = pow(((double)1 - e), ((double)1 - e));
    ro = 2. * Prd * k0 * t / sqrt(aux2 * aux3);

    aux1 = ro * sin(ptllx - lon0);
    ptlly = -ro * cos(ptllx - lon0);
    aux1 *= GPhemis;
    ptlly *= GPhemis;

    if (GPhemis == -1) {
        aux1 *= GPhemis;
        ptlly *= GPhemis;
    }

    return {aux1, ptlly};
}

//	POLAR STEREOGRAPHIC TO GEODETIC COORDINATES
//
Point PmPolarStereo ::PC2LL(Point& ptpc)
{
    double k0,  // Scale factor

        equad,  // Squared eccentricity
        e,      // Eccentricity
        pi,     // Ancillary variables
        lon0,   // auxilliary origin longitude
        ro, t, xx, aux1, aux2, aux3, aux4, aux5, ptpcx = 0., ptpcy, px, py;

    px = (double)ptpc.X();
    py = (double)ptpc.Y();

    //	k0 = 0.994;	// Standard parallel 80.1 degrees
    k0 = 0.933;  // Standard parallel 60 degrees
    pi = 4. * atan((double)1);
    equad = 2. * Pflt - pow(Pflt, (double)2);
    e = sqrt(equad);

    if (GPhemis == -1) {
        px *= GPhemis;
        py *= GPhemis;
    }

    lon0 = (GPhemis == 1) ? GPlon0 : -GPlon0;
    lon0 *= DEG_TO_RAD;

    ro = sqrt(px * px + py * py);
    aux1 = pow(((double)1 + e), ((double)1 + e));
    aux2 = pow(((double)1 - e), ((double)1 - e));
    t = (ro * sqrt(aux1 * aux2)) / (2. * Prd * k0);
    xx = pi / 2. - 2. * atan(t);
    aux3 = equad / 2. + 5. * equad * equad / 24. + equad * equad * equad / 12.;
    aux4 = 7. * equad * equad / 48. + 29. * equad * equad * equad / 240.;
    aux5 = 7. * equad * equad * equad / 120.;

    ptpcy = xx + aux3 * sin((double)2 * xx) + aux4 * sin((double)4 * xx) + aux5 * sin((double)6 * xx);

    if (py != 0.)
        ptpcx = lon0 + atan(px / (-py));

    if (GPhemis == 1) {
        if (px > 0. && py > 0.)
            ptpcx = ptpcx + pi;
        else if (px < 0. && py > 0.)
            ptpcx = ptpcx - pi;
        else if (px > 0. && py == 0.)
            ptpcx = lon0 + pi / 2.;
        else if (px < 0. && py == 0.)
            ptpcx = lon0 - pi / 2.;
        else if (px == 0. && py == 0.)
            ptpcx = lon0;
    }
    else {
        ptpcy *= GPhemis;
        ptpcx *= GPhemis;

        if (px > 0. && py < 0.)
            ptpcx = ptpcx + pi;
        else if (px < 0. && py < 0.)
            ptpcx = ptpcx - pi;
        else if (px > 0. && py == 0.)
            ptpcx = lon0 + pi / 2.;
        else if (px < 0. && py == 0.)
            ptpcx = lon0 - pi / 2.;
        else if (px == 0. && py == 0.)
            ptpcx = lon0;
    }

    if (ptpcx < (-pi))
        ptpcx += 2. * pi;
    else if (ptpcx > pi)
        ptpcx -= 2. * pi;

    return (Point(ptpcx, ptpcy));
}

bool PmPolarStereo::CheckGeodeticCoordinates(Location& area)
{
    bool flag = true;

    //	Remember: if GPhemis == NORTH, GPhemis =  1
    //	          if GPhemis == SOUTH, GPhemis = -1
    if (geodCoord_.Top() * GPhemis < -20.) {
        geodCoord_ = Location(-20. * GPhemis, geodCoord_.Left(),
                              geodCoord_.Bottom(), geodCoord_.Right());
        flag = false;
    }

    if (geodCoord_.Bottom() * GPhemis < -20.) {
        geodCoord_ = Location(geodCoord_.Top(), geodCoord_.Left(),
                              -20. * GPhemis, geodCoord_.Right());
        flag = false;
    }

    Point llPointLL(geodCoord_.Left() * DEG_TO_RAD, geodCoord_.Bottom() * DEG_TO_RAD);
    Point llPointPC = this->LL2PC(llPointLL);

    Point urPointLL(geodCoord_.Right() * DEG_TO_RAD, geodCoord_.Top() * DEG_TO_RAD);
    Point urPointPC = this->LL2PC(urPointLL);

    Point bmPointLL(GPlon0 * DEG_TO_RAD, -1. * GPhemis * DEG_TO_RAD);
    Point bmPointPC = this->LL2PC(bmPointLL);

    Point lmPointLL((GPlon0 - 90. * GPhemis) * DEG_TO_RAD, -1. * GPhemis * DEG_TO_RAD);
    Point lmPointPC = this->LL2PC(lmPointLL);

    Point tmPointLL((GPlon0 + 180.) * DEG_TO_RAD, -1. * GPhemis * DEG_TO_RAD);
    Point tmPointPC = this->LL2PC(tmPointLL);

    Point rmPointLL((GPlon0 + 90. * GPhemis) * DEG_TO_RAD, -1. * GPhemis * DEG_TO_RAD);
    Point rmPointPC = this->LL2PC(rmPointLL);

    if ((urPointPC.Y() <= llPointPC.Y()) ||
        (urPointPC.X() <= llPointPC.X()) ||
        (llPointPC.Y() < bmPointPC.Y()) ||
        (llPointPC.X() < lmPointPC.X()) ||
        (urPointPC.Y() > tmPointPC.Y()) ||
        (urPointPC.X() > rmPointPC.X())) {
        geodCoord_ = Location(-20. * GPhemis, -45. * GPhemis,
                              -20. * GPhemis, 135. * GPhemis);
        GPlon0 = 0;
        flag = false;
    }

    area = geodCoord_;
    return flag;
}

// =============================================================
// class PmLambertProjectionFactory - builds PmLambert projections
class PmLambertProjectionFactory : public PmProjectionFactory
{
    virtual PmProjection* Build(const MvRequest& reqst)
    {
        return new PmLambert(reqst);
    }

public:
    PmLambertProjectionFactory() :
        PmProjectionFactory("LAMBERT") {}
};

// Instance which is loaded in the "factory"
static PmLambertProjectionFactory lambertProjectionFactoryInstance;

// ====================================================================
//
//  PmLambert projection
PmLambert::PmLambert(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)  // PmGeneralProjection ( "LAMBERT" )
{
    // Initialize some parameters
    GPlon0 = viewRequest("SUBPAGE_MAP_CENTRE_LONGITUDE");
    GPlat0 = (double)viewRequest("SUBPAGE_MAP_CENTRE_LATITUDE") * DEG_TO_RAD;
    GPstlat1 = (double)viewRequest("SUBPAGE_MAP_STANDARD_LATITUDE_1") * DEG_TO_RAD;
    GPstlat2 = (double)viewRequest("SUBPAGE_MAP_STANDARD_LATITUDE_2") * DEG_TO_RAD;
    LScale = (double)viewRequest("SUBPAGE_MAP_SCALE");

    // Calculate the projection coordinates
    this->CalculateProjectionCoord();
}

// 		GEODETIC TO LAMBERT COORDINATES
Point PmLambert ::LL2PC(Point&)  // ptll
{
#if 0
	double	equad,			// Squared eccentricity
		e,			// Ancillary variables
		m1,m2,aux1,aux2,aux0,t1,t2,t0,n,efe,ro0,aux,
		t,ro,teta,ptllx,ptlly;

	ptllx = (double)ptll.X();
	ptlly = (double)ptll.Y();

	equad = 2.*Pflt - pow(Pflt,(double)2);
	e = sqrt(equad);

	m1 = cos(GPstlat1)/sqrt((double)1-equad*pow(sin(GPstlat1),(double)2));
	m2 = cos(GPstlat2)/sqrt((double)1-equad*pow(sin(GPstlat2),(double)2));
	aux1 = sqrt(((double)1-e*sin(GPstlat1))/((double)1+e*sin(GPstlat1)));
	aux2 = sqrt(((double)1-e*sin(GPstlat2))/((double)1+e*sin(GPstlat2)));
	aux0 = sqrt(((double)1-e*sin(GPlat0))/((double)1+e*sin(GPlat0)));
	t1 = ((1.-tan(GPstlat1/(double)2))/(1.+tan(GPstlat1/(double)2)))/pow(aux1,e);
	t2 = ((1.-tan(GPstlat2/(double)2))/(1.+tan(GPstlat2/(double)2)))/pow(aux2,e);
	t0 = ((1.-tan(GPlat0/(double)2))/(1.+tan(GPlat0/(double)2)))/pow(aux0,e);

	if (GPstlat1 == GPstlat2)
		n = sin(GPstlat1);
	else
		n = (log(m1)-log(m2))/(log(t1)-log(t2));

	efe = m1/(n*pow(t1,n));
	ro0 = Prd*efe*pow(t0,n);

	aux = sqrt(((double)1-e*sin(ptlly))/((double)1+e*sin(ptlly)));
	t = ((1.-tan(ptlly/(double)2))/(1.+tan(ptlly/(double)2)))/pow(aux,e);
	ro = Prd*efe*pow(t,n);
	teta = n*(ptllx - GPlon0);
	ptllx = ro*sin(teta);
	ptlly = ro0 - ro*cos(teta);

	return(Point(ptllx,ptlly));
#endif
    return {0., 0.};
}

//		LAMBERT TO GEODETIC COORDINATES
Point PmLambert ::PC2LL(Point&)  // ptpc
{
#if 0
	double	equad,			// Squared eccentricity 
		pi,			// PI value
		e,m1,m2,aux1,aux2,aux0,t1,t2,t0,n,efe,ro0,ro,teta,
		t,xx,aux3,aux4,aux5,ptpcx,ptpcy;

	int	sinal;

	ptpcx = (double)ptpc.X();
	ptpcy = (double)ptpc.Y();

	equad = 2.*Pflt - pow(Pflt,(double)2);
	e = sqrt(equad);
	pi = 4.*atan((double)1);

	m1 = cos(GPstlat1)/sqrt((double)1-equad*pow(sin(GPstlat1),(double)2));
	m2 = cos(GPstlat2)/sqrt((double)1-equad*pow(sin(GPstlat2),(double)2));
	aux1 = sqrt(((double)1-e*sin(GPstlat1))/((double)1+e*sin(GPstlat1)));
	aux2 = sqrt(((double)1-e*sin(GPstlat2))/((double)1+e*sin(GPstlat2)));
	aux0 = sqrt(((double)1-e*sin(GPlat0))/((double)1+e*sin(GPlat0)));
	t1 = ((1.-tan(GPstlat1/(double)2))/(1.+tan(GPstlat1/(double)2)))/pow(aux1,e);
	t2 = ((1.-tan(GPstlat2/(double)2))/(1.+tan(GPstlat2/(double)2)))/pow(aux2,e);
	t0 = ((1.-tan(GPlat0/(double)2))/(1.+tan(GPlat0/(double)2)))/pow(aux0,e);

	if (GPstlat1 == GPstlat2)
		n = sin(GPstlat1);
	else
		n = (log(m1)-log(m2))/(log(t1)-log(t2));

	efe = m1/(n*pow(t1,n));
	ro0 = Prd*efe*pow(t0,n);

	sinal = (int)(n/fabs(n));
	ro = sqrt(ptpcx*ptpcx + (ro0-ptpcy)*(ro0-ptpcy));
	ro *= sinal;
	teta = atan(ptpcx/(ro0-ptpcy));
	t = pow((ro/(Prd*efe)),(double)1/n);
	xx = pi/2. - 2.*atan(t);
	aux3 = equad/2. + 5.*equad*equad/24. + equad*equad*equad/12.;
	aux4 = 7.*equad*equad/48. + 29.*equad*equad*equad/240.;
	aux5 = (7.*equad*equad*equad/120.)*sin(12.*atan(t));

	ptpcy = xx + aux3*sin(4.*atan(t)) - aux4*sin(8.*atan(t)) + aux5;
	ptpcx = teta/n + GPlon0;

	return (Point(ptpcx,ptpcy));
#endif
    return {0., 0.};
}

double
PmLambert::CheckOriginLongitude()
{
    // Add code later
    return 0.;
}

// =============================================================
// class PmOceanSectionProjectionFactory - builds PmOceanSection projections
class PmOceanSectionProjectionFactory : public PmProjectionFactory
{
    PmProjection* Build(const MvRequest& reqst) override
    {
        return new PmOceanSection(reqst);
    }

public:
    PmOceanSectionProjectionFactory() :
        PmProjectionFactory("OCEAN_SECTION") {}
};

// Instance which is loaded in the "factory"
static PmOceanSectionProjectionFactory oceansectionProjectionFactoryInstance;

// ====================================================================
//
//  PmOceanSection projection
PmOceanSection::PmOceanSection(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)  // PmGeneralProjection ( "OCEAN_SECTION" )
{
    this->CalculateProjectionCoord();
}

// 		GEODETIC TO OCEAN_SECTION COORDINATES
Point PmOceanSection ::LL2PC(Point&)
{
    // Add code later
    return {0., 0.};
}

//		OCEAN_SECTION TO GEODETIC COORDINATES
Point PmOceanSection ::PC2LL(Point&)
{
    // Add code later
    return {0., 0.};
}

double
PmOceanSection::CheckOriginLongitude()
{
    // Add code later
    return 0.;
}

// =============================================================
// class PmAitoffProjectionFactory - builds PmAitoff projections
class PmAitoffProjectionFactory : public PmProjectionFactory
{
    PmProjection* Build(const MvRequest& reqst) override
    {
        return new PmAitoff(reqst);
    }

public:
    PmAitoffProjectionFactory() :
        PmProjectionFactory("AITOFF") {}
};

// Instance which is loaded in the "factory"
static PmAitoffProjectionFactory aitoffProjectionFactoryInstance;

// ====================================================================
//
//  PmAitoff projection
PmAitoff::PmAitoff(const MvRequest& viewRequest) :
    PmGeneralProjection(viewRequest)  // PmGeneralProjection ( "AITOFF" )
{
    // Initialize vertical longitude
    GPlon0 = viewRequest("MAP_VERTICAL_LONGITUDE");

    // Calculate the projection coordinates
    this->CalculateProjectionCoord();
}

// 		GEODETIC TO AITOFF COORDINATES
Point PmAitoff ::LL2PC(Point&)
{
    // Add code later
    return {0., 0.};
}

//		AITOFF TO GEODETIC COORDINATES
Point PmAitoff ::PC2LL(Point&)
{
    // Add code later
    return {0., 0.};
}
