/***************************** 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 *************************************/

//
// .NAME:
// PmGeneralProjection
//
// .AUTHOR:
// Ubirajara Freitas, Julio d'Alge.
//
// .SUMMARY:
//  Provides methods that are required to handle all conventional map
//  projections.
//
// .DESCRIPTION:
//  Specifies earth and projection parameters that represent a common ground
//  in terms of defining the following conventional map projections: utm,
//  mercator, gauss-kruger, lambert conformal conic, polyconic, cylindrical
//  equidistant, polar stereographic, bipolar conformal conic, albers equal
//  area conic, and miller. Class PmGeneralProjection also encapsulates the
//  following important information:
//		Hemisphere;
//		Latitude of origin;

//		Longitude of origin;
//		Latitudes of standard parallels
//
// .DESCENDENT:
//  PmMercator, PmCylinEquid, PmPolarStereo
//
//
// .RELATED:
//  Point, GribData
//
// .ASCENDENT:
//  PmProjection
//// .COMMENTS:
//  None

#pragma once

#include "PmProjection.h"

class PmGeneralProjection : public PmProjection
{
protected:
    int GPhemis;      // Hemisphere: 1 (north) or -1 (south)
    double GPlat0;    // Latitude of origin (rad)
    double GPlon0;    // Longitude of origin (rad)
    double GPstlat1;  // First standard parallel (rad)
    double GPstlat2;  // Second standard parallel (rad)

public:
    PmGeneralProjection(const MvRequest& reqst);
    //		Normal constructor.
    //		Description:
    //			Activates object of type PmGeneralProjection with the
    //			current projection.

    ~PmGeneralProjection() override = default;
    //		Empty destructor.

    friend bool operator==(PmGeneralProjection& a, PmGeneralProjection& b);
    //		Equality operator. Objects a and b are considered equal if
    //		their common projection parameters are the same.
    //		Input:
    //			a:	Object of type PmGeneralProjection;
    //			b:	Object of type PmGeneralProjection.
    //		Return:
    //			TRUE:	Objects are equal;
    //			FALSE:	Objects are different.

    virtual int Hemisphere() { return GPhemis; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the hemisphere for the polar
    //		stereographic projection.
    //		Return:
    //			GPhemis:	Hemisphere: 1 (north) or -1 (south)

    virtual void Hemisphere(int h) { GPhemis = h; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the hemisphere for the polar
    //		stereographic projection.
    //		Input:
    //			h: 		Hemisphere: 1 (north) or -1 (south).
    //		Precondition:
    //			h must be either 1 (north) or -1 (south).

    virtual double OriginLatitude() { return GPlat0; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the latitude of origin
    //		Return:
    //			GPlat0:		Latitude of origin (rad)

    virtual void OriginLatitude(double o) { GPlat0 = o; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the latitude of origin
    //		Input:
    //			o: 		Latitude of origin (rad)
    //		Precondition:
    //			o must be a valid latitude ([0,pi/2] or [0,-pi/2])

    virtual double OriginLongitude() { return GPlon0; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the longitude of origin
    //		Return:
    //			GPlon0:		Longitude of origin (rad)

    virtual void OriginLongitude(double o) { GPlon0 = o; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the longitude of origin.
    //		Input:
    //			o: 		Longitude of origin (rad)
    //		Precondition:
    //			o must be a valid longitude ([0,pi] or [0,-pi])

    virtual double StandardLatitudeOne() { return GPstlat1; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the latitude of the first standard
    //		parallel.
    //		Return:
    //			GPslat1:	Latitude of the first standard
    //					parallel (rad).

    virtual void StandardLatitudeOne(double s) { GPstlat1 = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the latitude of the first standard
    //		parallel.
    //		Input:
    //			s: 		Latitude of the first standard
    //					parallel (rad).
    //		Precondition:
    //			s must be a valid latitude ([0,pi/2] or [0,-pi/2]).

    virtual double StandardLatitudeTwo() { return GPstlat2; }
    //		This implementation of a pure virtual method defined in
    //		Projection returns the latitude of the second standard
    //		parallel.
    //		Return:
    //			GPslat2:	Latitude of the second standard
    //					parallel (rad)

    virtual void StandardLatitudeTwo(double s) { GPstlat2 = s; }
    //		This implementation of a pure virtual method defined in
    //		Projection updates the latitude of the second standard
    //		parallel.
    //		Input:
    //			s: 		Latitude of the second standard
    //					parallel (rad)
    //		Precondition:
    //			s must be a valid latitude ([0,pi/2] or [0,-pi/2])

    Point LL2PC(Point& p) override = 0;
    //		Pure virtual method that transforms geodetic into
    //		projection coordinates.
    //		Description:
    //			This method is implemented for each available
    //			projection class and represents the so-called
    //			direct formulas, which compute projection
    //			coordinates from geodetic coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad)
    //		Return:
    //			p:	Projection coordinates (m)
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi])

    Point PC2LL(Point& p) override = 0;
    //		Pure virtual method that transforms projection into
    //		geodetic coordinates.
    //		Description:
    //			This method is implemented for each available
    //			projection class and represents the so-called
    //			inverse formulas, which compute geodetic
    //			coordinates from projection coordinates.
    //		Input:
    //			p:	Projection coordinates (m)
    //		Return:
    //			p:	Geodetic coordinates (rad)
    //		Preconditions:
    //			X and Y projection coordinates must be both valid,
    //			within the typical range of each projection class.

    virtual double SensorResolutionX() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sensor angular resolution along X direction.
    //			Not implemented yet.

    virtual void SensorResolutionX(double) {}
    //		No code.
    //		Description:
    //			Updates sensor angular resolution along X direction.
    //			Not implemented yet.

    virtual double SensorResolutionY() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sensor angular resolution along Y direction.
    //			Not implemented yet.

    virtual void SensorResolutionY(double) {}
    //		No code.
    //		Description:
    //			Updates sensor angular resolution along Y direction.
    //			Not implemented yet.

    virtual double SubSatelliteX() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sub-satellite X coordinate. Not implemented
    //			yet.

    virtual void SubSatelliteX(double) {}
    //		No code.
    //		Description:
    //			Updates sub-satellite X coordinate. Not implemented
    //			yet.

    virtual double SubSatelliteY() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sub-satellite Y coordinate. Not implemented
    //			yet.

    virtual void SubSatelliteY(double) {}
    //		No code.
    //		Description:
    //			Updates sub-satellite Y coordinate. Not implemented
    //			yet.

    virtual double OriginX() { return 0.; }
    //		No code.
    //		Description:
    //			Returns image initial x coordinate. Not implemented
    //			yet.

    virtual double OriginY() { return 0.; }
    //		No code.
    //		Description:
    //			Returns image initial y coordinate. Not implemented
    //			yet.

    virtual double SubSatelliteLng() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sub-satellite longitude. Not implemented yet.

    virtual void SubSatelliteLng(double) {}
    //		No code.
    //		Description:
    //			Updates sub-satellite longitude. Not implemented yet.

    virtual double SubSatelliteLat() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sub-satellite latitude. Not implemented yet.

    virtual void SubSatelliteLat(double) {}
    //		No code.
    //		Description:
    //			Updates sub-satellite latitude. Not implemented yet.

    virtual double Radius() { return 0.; }
    //		No code.
    //		Description:
    //			Returns satellite orbit radius. Not implemented yet.

    virtual void Radius(double) {}
    //		No code.
    //		Description:
    //			Updates satellite orbit radius. Not implemented yet.

    virtual double ScanMode() { return 0.; }
    //		No code.
    //		Description:
    //			Returns sensor scanning mode. Not implemented yet.

    virtual double Yaw() { return 0.; }
    //		No code.
    //		Description:
    //			Returns orientation or yaw angle. Not implemented
    //			yet.

    virtual void Yaw(double) {}
    //		No code.
    //		Description:
    //			Updates orientation or yaw angle. Not implemented
    //			yet.

    void Print()
    //		Prints  projection parameters.
    //		Description:
    //			This method prints the projection parameters that
    //			are common to all descendent classes.
    {
        printf("\nHemisphere=%d OriginLat=%lf OriginLongi=%lf StdLatOne=%lf ERad=%lf EFlt=%lf", GPhemis, GPlat0, GPlon0, GPstlat1, Prd, Pflt);
    }

    bool CheckGeodeticCoordinates(Location&) override;

    double CheckOriginLongitude() override = 0;
};

//
// .NAME:
// PmMercator
//
// .AUTHOR:
// Ubirajara Freitas, Julio d'Alge
//
// .SUMMARY:
//  Provides methods that are required to handle the PmMercator map projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and PmMercator coordinates. Mercator is a conformal projection
//  system that uses the Hayford ellipsoid. Ellipsoid options can be redefined
//  by editing the file "Datum" under $SPRINGHOME/etc.
//
// .DESCENDENT:
//  None
//
// .RELATED:
//  Point
//
// .ASCENDENT:
//  PmGeneralProjection.
//
// .COMMENTS:
//  None
//

class PmMercator : public PmGeneralProjection
{
public:
    PmMercator(const MvRequest& reqst);
    // Constructor from a request

    ~PmMercator() override = default;
    //		Empty destructor


    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into PmMercator
    //		coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Mercator coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms Mercator into geodetic
    //		coordinates.
    //		Input:
    //			p:	Mercator coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Mercator coordinates must be both valid,
    //			within their typical range.

    bool CheckGeodeticCoordinates(Location&) override;

    virtual double CheckOriginLongitude();
};

//
// .NAME:
// PmCylinEquid
//
// .AUTHOR:
// Ubirajara Freitas, Julio d'Alge.
//
// .SUMMARY:
//  Provides methods that are required to handle the Equidistant Cylindrical
//  map projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and Equidistant Cylindrical coordinates. Equidistant Cylindrical
//  is a projection system that is neither conformal nor equal-area and uses
//  a spherical earth. Spheroid options can be redefined by editing the file
//  "Datum" under $SPRINGHOME/etc.
//
// .DESCENDENT:
//  None
//
// .RELATED:
//  Point, Datum
//
// .ASCENDENT:
//  PmGeneralProjection
//
// .COMMENTS:
//  None
//

class PmCylindEquid : public PmGeneralProjection
{
public:
    PmCylindEquid(const MvRequest& reqst);
    //	       Constructor from a request

    ~PmCylindEquid() override = default;
    //		Empty destructor

    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into Equidistant
    //		Cylindrical coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Equidistant Cylindrical coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms Equidistant Cylindrical into
    //		geodetic coordinates.
    //		Input:
    //			p:	Equidistant Cylindrical coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Equidistant Cylindrical coordinates must
    //			be both valid, within their typical range.

    bool CheckGeodeticCoordinates(Location&) override;

    double CheckOriginLongitude() override;
};

//
// .NAME:
// PmPolarStereo
//
// .AUTHOR:
// Ubirajara Freitas, Julio d'Alge.
//
// .SUMMARY:
//  Provides methods that are required to handle the Polar Stereographic map
//  projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and Polar Stereographic coordinates. Polar Stereographic is a
//  conformal projection system that uses the Hayford ellipsoid.
//  Ellipsoid options can be redefined by editing the file "Datum" under
//  $SPRINGHOME/etc.
//
// .DESCENDENT:
//  None.
//
// .RELATED:
//  Point, Datum.
//
// .ASCENDENT:
//  PmGeneralProjection.
//
// .COMMENTS:
//  Direct and inverse formulas require explicit knowledge of the hemisphere.
//  (see Snyder's 'Map Projections used by the U.S. Geological Survey').
//

class PmPolarStereo : public PmGeneralProjection
{
public:
    PmPolarStereo(const MvRequest& reqst);
    //		Constructor from a request

    ~PmPolarStereo() override = default;
    //		Empty destructor.

    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into Polar
    //		Stereographic coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Polar Stereographic coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms Polar Stereographic into
    //		geodetic coordinates.
    //		Input:
    //			p:	Polar Stereographic coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Polar Stereographic coordinates must
    //			be both valid, within their typical range.

    bool CheckGeodeticCoordinates(Location&) override;

    double CheckOriginLongitude() override
    {
        return OriginLongitude();
    }
};

//
// .NAME:
//  PmLambert
//
// .AUTHOR:
//  Ubirajara Freitas, Julio d'Alge
//
// .SUMMARY:
//  Provides methods that are required to handle the PmLambert map projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and PmLambert conformal conic coordinates. Lambert conformal
//  conic uses the planimetric datum Corrego Alegre (Hayford ellipsoid).
//  Ellipsoid options can be redefined by editing the file "Datum" under
//  $SPRINGHOME/etc.
//
// .DESCENDENT:
//  None
//
// .RELATED:
//  Point
//
// .ASCENDENT:
//  PmGeneralProjection.
//
// .COMMENTS:
//  None
//

class PmLambert : public PmGeneralProjection
{
protected:
    double LScale;  // Map scale

public:
    PmLambert(const MvRequest& reqst);
    //		Constructor from a request.

    ~PmLambert() override = default;
    //		Empty destructor.

    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into PmLambert
    //		coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Lambert coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms Lambert into geodetic
    //		coordinates.
    //		Input:
    //			p:	Lambert coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Lambert coordinates must be both valid,
    //			within their typical range.

    double CheckOriginLongitude() override;

    bool CanDoZoom() override
    {
        return true;
    }
};

//
// .NAME:
//  PmAitoff
//
// .AUTHOR:
//  Paddy O'Sullivan
//
// .SUMMARY:
//  Provides methods that are required to handle the PmAitoff map projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and PmAitoff coordinates.
//
// .DESCENDENT:
//  None.
//
// .RELATED:
//  Point
//
// .ASCENDENT:
//  PmGeneralProjection.
//
// .COMMENTS:
//  None.
//

class PmAitoff : public PmGeneralProjection
{
public:
    PmAitoff(const MvRequest& reqst);
    //		Constructor from a request

    ~PmAitoff() override = default;
    //		Empty destructor

    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into PmAitoff
    //		coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	Aitoff coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms Aitoff into geodetic
    //		coordinates.
    //		Input:
    //			p:	Aitoff coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y Aitoff coordinates must be both valid,
    //			within their typical range.

    double CheckOriginLongitude() override
    {
        return OriginLongitude();
    }

    bool CanDoZoom() override
    {
        return false;
    }
};

//
// .NAME:
//  PmOceanSection
//
// .AUTHOR:
//  Paddy O'Sullivan
//
// .SUMMARY:
//  Provides methods that are required to handle the PmOceanSection map projection.
//
// .DESCRIPTION:
//  Specifies methods that are necessary to establish the relation between
//  geodetic and PmOceanSection coordinates.
//
// .DESCENDENT:
//  None
//
// .RELATED:
//  Point
//
// .ASCENDENT:
//  PmGeneralProjection
//
// .COMMENTS:
//  None
//

class PmOceanSection : public PmGeneralProjection
{
public:
    PmOceanSection(const MvRequest& reqst);
    //		Constructor from a request.

    ~PmOceanSection() override = default;
    //		Empty destructor.


    Point LL2PC(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms geodetic into PmOceanSection
    //		coordinates.
    //		Input:
    //			p:	Geodetic coordinates (rad).
    //		Return:
    //			p:	OceanSection coordinates (m).
    //		Preconditions:
    //			Geodetic coordinates must be a valid latitude
    //			([0,pi/2] or [0,-pi/2]) and a valid longitude
    //			([0,pi] or [0,-pi]).

    Point PC2LL(Point& p) override;
    //		This implementation of a pure virtual method defined in
    //		PmGeneralProjection transforms OceanSection into geodetic
    //		coordinates.
    //		Input:
    //			p:	OceanSection coordinates (m).
    //		Return:
    //			p:	Geodetic coordinates (rad).
    //		Preconditions:
    //			X and Y OceanSection coordinates must be both valid,
    //			within their typical range.

    double CheckOriginLongitude() override;

    bool CanDoZoom() override
    {
        return false;
    }
};
