/******************************************************************************
 *
 * Project:  OpenGIS Simple Features Reference Implementation
 * Purpose:  OGRSpatialReference translation to/from USGS georeferencing
 *           information (used in GCTP package).
 * Author:   Andrey Kiselev, dron@ak4719.spb.edu
 *
 ******************************************************************************
 * Copyright (c) 2004, Andrey Kiselev <dron@ak4719.spb.edu>
 * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "cpl_port.h"
#include "ogr_srs_api.h"

#include <cmath>
#include <cstddef>

#include "cpl_conv.h"
#include "cpl_csv.h"
#include "cpl_error.h"
#include "cpl_string.h"
#include "ogr_core.h"
#include "ogr_p.h"
#include "ogr_spatialref.h"

CPL_CVSID("$Id: ogr_srs_usgs.cpp fa752ad6eabafaf630a704e1892a9d837d683cb3 2021-03-06 17:04:38 +0100 Even Rouault $")

/************************************************************************/
/*  GCTP projection codes.                                              */
/************************************************************************/

constexpr long GEO    = 0L;   // Geographic
constexpr long UTM    = 1L;   // Universal Transverse Mercator (UTM)
constexpr long SPCS   = 2L;   // State Plane Coordinates
constexpr long ALBERS = 3L;   // Albers Conical Equal Area
constexpr long LAMCC  = 4L;   // Lambert Conformal Conic
constexpr long MERCAT = 5L;   // Mercator
constexpr long PS     = 6L;   // Polar Stereographic
constexpr long POLYC  = 7L;   // Polyconic
constexpr long EQUIDC = 8L;   // Equidistant Conic
constexpr long TM     = 9L;   // Transverse Mercator
constexpr long STEREO = 10L;  // Stereographic
constexpr long LAMAZ  = 11L;  // Lambert Azimuthal Equal Area
constexpr long AZMEQD = 12L;  // Azimuthal Equidistant
constexpr long GNOMON = 13L;  // Gnomonic
constexpr long ORTHO  = 14L;  // Orthographic
// constexpr long GVNSP  = 15L;  // General Vertical Near-Side Perspective
constexpr long SNSOID = 16L;  // Sinusiodal
constexpr long EQRECT = 17L;  // Equirectangular
constexpr long MILLER = 18L;  // Miller Cylindrical
constexpr long VGRINT = 19L;  // Van der Grinten
constexpr long HOM    = 20L;  // (Hotine) Oblique Mercator
constexpr long ROBIN  = 21L;  // Robinson
// constexpr long SOM    = 22L;  // Space Oblique Mercator (SOM)
// constexpr long ALASKA = 23L;  // Alaska Conformal
// constexpr long GOODE  = 24L;  // Interrupted Goode Homolosine
constexpr long MOLL   = 25L;  // Mollweide
// constexpr long IMOLL  = 26L;  // Interrupted Mollweide
// constexpr long HAMMER = 27L;  // Hammer
constexpr long WAGIV  = 28L;  // Wagner IV
constexpr long WAGVII = 29L;  // Wagner VII
// constexpr long OBEQA  = 30L;  // Oblated Equal Area
// constexpr long ISINUS1 = 31L; // Integerized Sinusoidal Grid (the same as 99)
// constexpr long CEA    = 97L;  // Cylindrical Equal Area (Grid corners set
                                 // in meters for EASE grid)
// constexpr long BCEA   = 98L;  // Cylindrical Equal Area (Grid corners set
                                 // in DMS degs for EASE grid)
// constexpr long ISINUS = 99L;  // Integerized Sinusoidal Grid
                                 // (added by Raj Gejjagaraguppe ARC for MODIS)

/************************************************************************/
/*  GCTP ellipsoid codes.                                               */
/************************************************************************/

constexpr long CLARKE1866         = 0L;
// constexpr long CLARKE1880         = 1L;
// constexpr long BESSEL             = 2L;
// constexpr long INTERNATIONAL1967  = 3L;
// constexpr long INTERNATIONAL1909  = 4L;
// constexpr long WGS72              = 5L;
// constexpr long EVEREST            = 6L;
// constexpr long WGS66              = 7L;
constexpr long GRS1980            = 8L;
// constexpr long AIRY               = 9L;
// constexpr long MODIFIED_EVEREST   = 10L;
// constexpr long MODIFIED_AIRY      = 11L;
constexpr long WGS84              = 12L;
// constexpr long SOUTHEAST_ASIA     = 13L;
// constexpr long AUSTRALIAN_NATIONAL= 14L;
// constexpr long KRASSOVSKY         = 15L;
// constexpr long HOUGH              = 16L;
// constexpr long MERCURY1960        = 17L;
// constexpr long MODIFIED_MERCURY   = 18L;
// constexpr long SPHERE             = 19L;

/************************************************************************/
/*  Correspondence between GCTP and EPSG ellipsoid codes.               */
/************************************************************************/

constexpr int aoEllips[] =
{
    7008,   // Clarke, 1866 (NAD1927)
    7034,   // Clarke, 1880
    7004,   // Bessel, 1841
    0,      // FIXME: New International, 1967 --- skipped
    7022,   // International, 1924 (Hayford, 1909) XXX?
    7043,   // WGS, 1972
    7042,   // Everest, 1830
    7025,   // FIXME: WGS, 1966
    7019,   // GRS, 1980 (NAD1983)
    7001,   // Airy, 1830
    7018,   // Modified Everest
    7002,   // Modified Airy
    7030,   // WGS, 1984 (GPS)
    0,      // FIXME: Southeast Asia --- skipped
    7003,   // Australian National, 1965
    7024,   // Krassovsky, 1940
    7053,   // Hough
    0,      // FIXME: Mercury, 1960 --- skipped
    0,      // FIXME: Modified Mercury, 1968 --- skipped
    7047,   // Sphere, rad 6370997 m (normal sphere)
    7006,   // Bessel, 1841 (Namibia)
    7016,   // Everest (Sabah & Sarawak)
    7044,   // Everest, 1956
    7056,   // Everest, Malaysia 1969
    7018,   // Everest, Malay & Singapr 1948
    0,      // FIXME: Everest, Pakistan --- skipped
    7022,   // Hayford (International 1924) XXX?
    7020,   // Helmert 1906
    7021,   // Indonesian, 1974
    7036,   // South American, 1969
    0       // FIXME: WGS 60 --- skipped
};

#define NUMBER_OF_ELLIPSOIDS    static_cast<int>(CPL_ARRAYSIZE(aoEllips))

/************************************************************************/
/*                         OSRImportFromUSGS()                          */
/************************************************************************/

/**
 * \brief Import coordinate system from USGS projection definition.
 *
 * This function is the same as OGRSpatialReference::importFromUSGS().
 */
OGRErr OSRImportFromUSGS( OGRSpatialReferenceH hSRS, long iProjsys,
                          long iZone, double *padfPrjParams, long iDatum )

{
    VALIDATE_POINTER1( hSRS, "OSRImportFromUSGS", OGRERR_FAILURE );

    return OGRSpatialReference::FromHandle(hSRS)->importFromUSGS( iProjsys, iZone,
                                                           padfPrjParams,
                                                           iDatum );
}

static double OGRSpatialReferenceUSGSUnpackNoOp( double dfVal )
{
    return dfVal;
}

static double OGRSpatialReferenceUSGSUnpackRadian( double dfVal )
{
    return dfVal * 180.0 / M_PI;
}

/************************************************************************/
/*                          importFromUSGS()                            */
/************************************************************************/

/**
 * \brief Import coordinate system from USGS projection definition.
 *
 * This method will import projection definition in style, used by USGS GCTP
 * software. GCTP operates on angles in packed DMS format (see
 * CPLDecToPackedDMS() function for details), so all angle values (latitudes,
 * longitudes, azimuths, etc.) specified in the padfPrjParams array should
 * be in the packed DMS format, unless bAnglesInPackedDMSFormat is set to FALSE.
 *
 * This function is the equivalent of the C function OSRImportFromUSGS().
 * Note that the bAnglesInPackedDMSFormat parameter is only present in the C++
 * method. The C function assumes bAnglesInPackedFormat = TRUE.
 *
 * @param iProjSys Input projection system code, used in GCTP.
 *
 * @param iZone Input zone for UTM and State Plane projection systems. For
 * Southern Hemisphere UTM use a negative zone code. iZone ignored for all
 * other projections.
 *
 * @param padfPrjParams Array of 15 coordinate system parameters. These
 * parameters differs for different projections.
 *
 * <h4>Projection Transformation Package Projection Parameters</h4>
 * \code{.unparsed}
 * ----------------------------------------------------------------------------
 *                         |                    Array Element
 *  Code & Projection Id   |---------------------------------------------------
 *                         |   0  |   1  |  2   |  3   |   4   |    5    |6 | 7
 * ----------------------------------------------------------------------------
 *  0 Geographic           |      |      |      |      |       |         |  |
 *  1 U T M                |Lon/Z |Lat/Z |      |      |       |         |  |
 *  2 State Plane          |      |      |      |      |       |         |  |
 *  3 Albers Equal Area    |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
 *  4 Lambert Conformal C  |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
 *  5 Mercator             |SMajor|SMinor|      |      |CentMer|TrueScale|FE|FN
 *  6 Polar Stereographic  |SMajor|SMinor|      |      |LongPol|TrueScale|FE|FN
 *  7 Polyconic            |SMajor|SMinor|      |      |CentMer|OriginLat|FE|FN
 *  8 Equid. Conic A       |SMajor|SMinor|STDPAR|      |CentMer|OriginLat|FE|FN
 *    Equid. Conic B       |SMajor|SMinor|STDPR1|STDPR2|CentMer|OriginLat|FE|FN
 *  9 Transverse Mercator  |SMajor|SMinor|Factor|      |CentMer|OriginLat|FE|FN
 * 10 Stereographic        |Sphere|      |      |      |CentLon|CenterLat|FE|FN
 * 11 Lambert Azimuthal    |Sphere|      |      |      |CentLon|CenterLat|FE|FN
 * 12 Azimuthal            |Sphere|      |      |      |CentLon|CenterLat|FE|FN
 * 13 Gnomonic             |Sphere|      |      |      |CentLon|CenterLat|FE|FN
 * 14 Orthographic         |Sphere|      |      |      |CentLon|CenterLat|FE|FN
 * 15 Gen. Vert. Near Per  |Sphere|      |Height|      |CentLon|CenterLat|FE|FN
 * 16 Sinusoidal           |Sphere|      |      |      |CentMer|         |FE|FN
 * 17 Equirectangular      |Sphere|      |      |      |CentMer|TrueScale|FE|FN
 * 18 Miller Cylindrical   |Sphere|      |      |      |CentMer|         |FE|FN
 * 19 Van der Grinten      |Sphere|      |      |      |CentMer|OriginLat|FE|FN
 * 20 Hotin Oblique Merc A |SMajor|SMinor|Factor|      |       |OriginLat|FE|FN
 *    Hotin Oblique Merc B |SMajor|SMinor|Factor|AziAng|AzmthPt|OriginLat|FE|FN
 * 21 Robinson             |Sphere|      |      |      |CentMer|         |FE|FN
 * 22 Space Oblique Merc A |SMajor|SMinor|      |IncAng|AscLong|         |FE|FN
 *    Space Oblique Merc B |SMajor|SMinor|Satnum|Path  |       |         |FE|FN
 * 23 Alaska Conformal     |SMajor|SMinor|      |      |       |         |FE|FN
 * 24 Interrupted Goode    |Sphere|      |      |      |       |         |  |
 * 25 Mollweide            |Sphere|      |      |      |CentMer|         |FE|FN
 * 26 Interrupt Mollweide  |Sphere|      |      |      |       |         |  |
 * 27 Hammer               |Sphere|      |      |      |CentMer|         |FE|FN
 * 28 Wagner IV            |Sphere|      |      |      |CentMer|         |FE|FN
 * 29 Wagner VII           |Sphere|      |      |      |CentMer|         |FE|FN
 * 30 Oblated Equal Area   |Sphere|      |Shapem|Shapen|CentLon|CenterLat|FE|FN
 * ----------------------------------------------------------------------------
 *
 *       ----------------------------------------------------
 *                               |      Array Element       |
 *         Code & Projection Id  |---------------------------
 *                               |  8  |  9 |  10 | 11 | 12 |
 *       ----------------------------------------------------
 *        0 Geographic           |     |    |     |    |    |
 *        1 U T M                |     |    |     |    |    |
 *        2 State Plane          |     |    |     |    |    |
 *        3 Albers Equal Area    |     |    |     |    |    |
 *        4 Lambert Conformal C  |     |    |     |    |    |
 *        5 Mercator             |     |    |     |    |    |
 *        6 Polar Stereographic  |     |    |     |    |    |
 *        7 Polyconic            |     |    |     |    |    |
 *        8 Equid. Conic A       |zero |    |     |    |    |
 *          Equid. Conic B       |one  |    |     |    |    |
 *        9 Transverse Mercator  |     |    |     |    |    |
 *       10 Stereographic        |     |    |     |    |    |
 *       11 Lambert Azimuthal    |     |    |     |    |    |
 *       12 Azimuthal            |     |    |     |    |    |
 *       13 Gnomonic             |     |    |     |    |    |
 *       14 Orthographic         |     |    |     |    |    |
 *       15 Gen. Vert. Near Per  |     |    |     |    |    |
 *       16 Sinusoidal           |     |    |     |    |    |
 *       17 Equirectangular      |     |    |     |    |    |
 *       18 Miller Cylindrical   |     |    |     |    |    |
 *       19 Van der Grinten      |     |    |     |    |    |
 *       20 Hotin Oblique Merc A |Long1|Lat1|Long2|Lat2|zero|
 *          Hotin Oblique Merc B |     |    |     |    |one |
 *       21 Robinson             |     |    |     |    |    |
 *       22 Space Oblique Merc A |PSRev|LRat|PFlag|    |zero|
 *          Space Oblique Merc B |     |    |     |    |one |
 *       23 Alaska Conformal     |     |    |     |    |    |
 *       24 Interrupted Goode    |     |    |     |    |    |
 *       25 Mollweide            |     |    |     |    |    |
 *       26 Interrupt Mollweide  |     |    |     |    |    |
 *       27 Hammer               |     |    |     |    |    |
 *       28 Wagner IV            |     |    |     |    |    |
 *       29 Wagner VII           |     |    |     |    |    |
 *       30 Oblated Equal Area   |Angle|    |     |    |    |
 *       ----------------------------------------------------
 *
 *   where
 *
 *    Lon/Z     Longitude of any point in the UTM zone or zero.  If zero,
 *              a zone code must be specified.
 *    Lat/Z     Latitude of any point in the UTM zone or zero.  If zero, a
 *              zone code must be specified.
 *    SMajor    Semi-major axis of ellipsoid.  If zero, Clarke 1866 in meters
 *              is assumed.
 *    SMinor    Eccentricity squared of the ellipsoid if less than zero,
 *              if zero, a spherical form is assumed, or if greater than
 *              zero, the semi-minor axis of ellipsoid.
 *    Sphere    Radius of reference sphere.  If zero, 6370997 meters is used.
 *    STDPAR    Latitude of the standard parallel
 *    STDPR1    Latitude of the first standard parallel
 *    STDPR2    Latitude of the second standard parallel
 *    CentMer   Longitude of the central meridian
 *    OriginLat Latitude of the projection origin
 *    FE        False easting in the same units as the semi-major axis
 *    FN        False northing in the same units as the semi-major axis
 *    TrueScale Latitude of true scale
 *    LongPol   Longitude down below pole of map
 *    Factor    Scale factor at central meridian (Transverse Mercator) or
 *              center of projection (Hotine Oblique Mercator)
 *    CentLon   Longitude of center of projection
 *    CenterLat Latitude of center of projection
 *    Height    Height of perspective point
 *    Long1     Longitude of first point on center line (Hotine Oblique
 *              Mercator, format A)
 *    Long2     Longitude of second point on center line (Hotine Oblique
 *              Mercator, format A)
 *    Lat1      Latitude of first point on center line (Hotine Oblique
 *              Mercator, format A)
 *    Lat2      Latitude of second point on center line (Hotine Oblique
 *              Mercator, format A)
 *    AziAng    Azimuth angle east of north of center line (Hotine Oblique
 *              Mercator, format B)
 *    AzmthPt   Longitude of point on central meridian where azimuth occurs
 *              (Hotine Oblique Mercator, format B)
 *    IncAng    Inclination of orbit at ascending node, counter-clockwise
 *              from equator (SOM, format A)
 *    AscLong   Longitude of ascending orbit at equator (SOM, format A)
 *    PSRev     Period of satellite revolution in minutes (SOM, format A)
 *    LRat      Landsat ratio to compensate for confusion at northern end
 *              of orbit (SOM, format A -- use 0.5201613)
 *    PFlag     End of path flag for Landsat:  0 = start of path,
 *              1 = end of path (SOM, format A)
 *    Satnum    Landsat Satellite Number (SOM, format B)
 *    Path      Landsat Path Number (Use WRS-1 for Landsat 1, 2 and 3 and
 *              WRS-2 for Landsat 4, 5 and 6.)  (SOM, format B)
 *    Shapem    Oblated Equal Area oval shape parameter m
 *    Shapen    Oblated Equal Area oval shape parameter n
 *    Angle     Oblated Equal Area oval rotation angle
 *
 * Array elements 13 and 14 are set to zero. All array elements with blank
 * fields are set to zero too.
 * \endcode
 *
 * @param iDatum Input spheroid.<p>
 *
 * If the datum code is negative, the first two values in the parameter array
 * (param) are used to define the values as follows:
 *
 * <ul>
 *
 * <li> If padfPrjParams[0] is a non-zero value and padfPrjParams[1] is
 * greater than one, the semimajor axis is set to padfPrjParams[0] and
 * the semiminor axis is set to padfPrjParams[1].
 *
 * <li> If padfPrjParams[0] is nonzero and padfPrjParams[1] is greater than
 * zero but less than or equal to one, the semimajor axis is set to
 * padfPrjParams[0] and the semiminor axis is computed from the eccentricity
 * squared value padfPrjParams[1]:<p>
 *
 * semiminor = sqrt(1.0 - ES) * semimajor<p>
 *
 * where<p>
 *
 * ES = eccentricity squared
 *
 * <li> If padfPrjParams[0] is nonzero and padfPrjParams[1] is equal to zero,
 * the semimajor axis and semiminor axis are set to padfPrjParams[0].
 *
 * <li> If padfPrjParams[0] equals zero and padfPrjParams[1] is greater than
 * zero, the default Clarke 1866 is used to assign values to the semimajor
 * axis and semiminor axis.
 *
 * <li> If padfPrjParams[0] and padfPrjParams[1] equals zero, the semimajor
 * axis is set to 6370997.0 and the semiminor axis is set to zero.
 *
 * </ul>
 *
 * If a datum code is zero or greater, the semimajor and semiminor axis are
 * defined by the datum code as found in the following table:
 *
 * <h4>Supported Datums</h4>
 * \code{.unparsed}
 *       0: Clarke 1866 (default)
 *       1: Clarke 1880
 *       2: Bessel
 *       3: International 1967
 *       4: International 1909
 *       5: WGS 72
 *       6: Everest
 *       7: WGS 66
 *       8: GRS 1980/WGS 84
 *       9: Airy
 *      10: Modified Everest
 *      11: Modified Airy
 *      12: WGS 84
 *      13: Southeast Asia
 *      14: Australian National
 *      15: Krassovsky
 *      16: Hough
 *      17: Mercury 1960
 *      18: Modified Mercury 1968
 *      19: Sphere of Radius 6370997 meters
 * \endcode
 *
 * @param nUSGSAngleFormat one of USGS_ANGLE_DECIMALDEGREES,
 *    USGS_ANGLE_PACKEDDMS, or USGS_ANGLE_RADIANS (default is
 *    USGS_ANGLE_PACKEDDMS).
 *
 * @return OGRERR_NONE on success or an error code in case of failure.
 */

OGRErr OGRSpatialReference::importFromUSGS( long iProjSys, long iZone,
                                            double *padfPrjParams,
                                            long iDatum,
                                            int nUSGSAngleFormat )

{
    if( !padfPrjParams )
        return OGRERR_CORRUPT_DATA;

    double (*pfnUnpackAnglesFn)(double) = nullptr;

    if( nUSGSAngleFormat == USGS_ANGLE_DECIMALDEGREES )
        pfnUnpackAnglesFn = OGRSpatialReferenceUSGSUnpackNoOp;
    else if( nUSGSAngleFormat == USGS_ANGLE_RADIANS )
        pfnUnpackAnglesFn = OGRSpatialReferenceUSGSUnpackRadian;
    else
        pfnUnpackAnglesFn = CPLPackedDMSToDec;

/* -------------------------------------------------------------------- */
/*      Operate on the basis of the projection code.                    */
/* -------------------------------------------------------------------- */
    switch( iProjSys )
    {
        case GEO:
            break;

        case UTM:
            {
                int bNorth = TRUE;

                if( !iZone )
                {
                    if( padfPrjParams[2] != 0.0 )
                    {
                        iZone = static_cast<long>(padfPrjParams[2]);
                    }
                    else if( padfPrjParams[0] != 0.0 &&
                             padfPrjParams[1] != 0.0 )
                    {
                        const double dfUnpackedAngle =
                            pfnUnpackAnglesFn(padfPrjParams[0]);
                        iZone = static_cast<long>(
                            ((dfUnpackedAngle + 180.0) / 6.0) + 1.0);
                        if( dfUnpackedAngle < 0 )
                            bNorth = FALSE;
                    }
                }

                if( iZone < -60 || iZone > 60 )
                    return OGRERR_CORRUPT_DATA;

                if( iZone < 0 )
                {
                    iZone = -iZone;
                    bNorth = FALSE;
                }
                SetUTM( static_cast<int>(iZone), bNorth );
            }
            break;

        case SPCS:
            {
                int bNAD83 = TRUE;

                if( iDatum == 0 )
                    bNAD83 = FALSE;
                else if( iDatum != 8 )
                    CPLError( CE_Warning, CPLE_AppDefined,
                              "Wrong datum for State Plane projection %d. "
                              "Should be 0 or 8.", static_cast<int>(iDatum) );

                SetStatePlane( static_cast<int>(iZone), bNAD83 );
            }
            break;

        case ALBERS:
            SetACEA( pfnUnpackAnglesFn(padfPrjParams[2]),
                     pfnUnpackAnglesFn(padfPrjParams[3]),
                     pfnUnpackAnglesFn(padfPrjParams[5]),
                     pfnUnpackAnglesFn(padfPrjParams[4]),
                     padfPrjParams[6], padfPrjParams[7] );
            break;

        case LAMCC:
            SetLCC( pfnUnpackAnglesFn(padfPrjParams[2]),
                    pfnUnpackAnglesFn(padfPrjParams[3]),
                    pfnUnpackAnglesFn(padfPrjParams[5]),
                    pfnUnpackAnglesFn(padfPrjParams[4]),
                    padfPrjParams[6], padfPrjParams[7] );
            break;

        case MERCAT:
            SetMercator( pfnUnpackAnglesFn(padfPrjParams[5]),
                         pfnUnpackAnglesFn(padfPrjParams[4]),
                         1.0,
                         padfPrjParams[6], padfPrjParams[7] );
            break;

        case PS:
            SetPS( pfnUnpackAnglesFn(padfPrjParams[5]),
                   pfnUnpackAnglesFn(padfPrjParams[4]),
                   1.0,
                   padfPrjParams[6], padfPrjParams[7] );

            break;

        case POLYC:
            SetPolyconic( pfnUnpackAnglesFn(padfPrjParams[5]),
                          pfnUnpackAnglesFn(padfPrjParams[4]),
                          padfPrjParams[6], padfPrjParams[7] );
            break;

        case EQUIDC:
            if( padfPrjParams[8] != 0.0 )
            {
                SetEC( pfnUnpackAnglesFn(padfPrjParams[2]),
                       pfnUnpackAnglesFn(padfPrjParams[3]),
                       pfnUnpackAnglesFn(padfPrjParams[5]),
                       pfnUnpackAnglesFn(padfPrjParams[4]),
                       padfPrjParams[6], padfPrjParams[7] );
            }
            else
            {
                SetEC( pfnUnpackAnglesFn(padfPrjParams[2]),
                       pfnUnpackAnglesFn(padfPrjParams[2]),
                       pfnUnpackAnglesFn(padfPrjParams[5]),
                       pfnUnpackAnglesFn(padfPrjParams[4]),
                       padfPrjParams[6], padfPrjParams[7] );
            }
            break;

        case TM:
            SetTM( pfnUnpackAnglesFn(padfPrjParams[5]),
                   pfnUnpackAnglesFn(padfPrjParams[4]),
                   padfPrjParams[2],
                   padfPrjParams[6], padfPrjParams[7] );
            break;

        case STEREO:
            SetStereographic( pfnUnpackAnglesFn(padfPrjParams[5]),
                              pfnUnpackAnglesFn(padfPrjParams[4]),
                              1.0,
                              padfPrjParams[6], padfPrjParams[7] );
            break;

        case LAMAZ:
            SetLAEA( pfnUnpackAnglesFn(padfPrjParams[5]),
                     pfnUnpackAnglesFn(padfPrjParams[4]),
                     padfPrjParams[6], padfPrjParams[7] );
            break;

        case AZMEQD:
            SetAE( pfnUnpackAnglesFn(padfPrjParams[5]),
                   pfnUnpackAnglesFn(padfPrjParams[4]),
                   padfPrjParams[6], padfPrjParams[7] );
            break;

        case GNOMON:
            SetGnomonic( pfnUnpackAnglesFn(padfPrjParams[5]),
                         pfnUnpackAnglesFn(padfPrjParams[4]),
                         padfPrjParams[6], padfPrjParams[7] );
            break;

        case ORTHO:
            SetOrthographic( pfnUnpackAnglesFn(padfPrjParams[5]),
                             pfnUnpackAnglesFn(padfPrjParams[4]),
                             padfPrjParams[6], padfPrjParams[7] );
            break;

        // FIXME: GVNSP --- General Vertical Near-Side Perspective skipped.

        case SNSOID:
            SetSinusoidal( pfnUnpackAnglesFn(padfPrjParams[4]),
                           padfPrjParams[6], padfPrjParams[7] );
            break;

        case EQRECT:
            SetEquirectangular2( 0.0,
                                 pfnUnpackAnglesFn(padfPrjParams[4]),
                                 pfnUnpackAnglesFn(padfPrjParams[5]),
                                 padfPrjParams[6], padfPrjParams[7] );
            break;

        case MILLER:
            SetMC( pfnUnpackAnglesFn(padfPrjParams[5]),
                   pfnUnpackAnglesFn(padfPrjParams[4]),
                   padfPrjParams[6], padfPrjParams[7] );
            break;

        case VGRINT:
            SetVDG( pfnUnpackAnglesFn(padfPrjParams[4]),
                    padfPrjParams[6], padfPrjParams[7] );
            break;

        case HOM:
            if( padfPrjParams[12] != 0.0 )
            {
                SetHOM( pfnUnpackAnglesFn(padfPrjParams[5]),
                        pfnUnpackAnglesFn(padfPrjParams[4]),
                        pfnUnpackAnglesFn(padfPrjParams[3]),
                        0.0, padfPrjParams[2],
                        padfPrjParams[6], padfPrjParams[7] );
            }
            else
            {
                SetHOM2PNO( pfnUnpackAnglesFn(padfPrjParams[5]),
                            pfnUnpackAnglesFn(padfPrjParams[9]),
                            pfnUnpackAnglesFn(padfPrjParams[8]),
                            pfnUnpackAnglesFn(padfPrjParams[11]),
                            pfnUnpackAnglesFn(padfPrjParams[10]),
                            padfPrjParams[2],
                            padfPrjParams[6], padfPrjParams[7] );
            }
            break;

        case ROBIN:
            SetRobinson( pfnUnpackAnglesFn(padfPrjParams[4]),
                         padfPrjParams[6], padfPrjParams[7] );
            break;

        // FIXME: SOM --- Space Oblique Mercator skipped.
        // FIXME: ALASKA --- Alaska Conformal skipped.
        // FIXME: GOODE --- Interrupted Goode skipped.

        case MOLL:
            SetMollweide( pfnUnpackAnglesFn(padfPrjParams[4]),
                          padfPrjParams[6], padfPrjParams[7] );
            break;

        // FIXME: IMOLL --- Interrupted Mollweide skipped.
        // FIXME: HAMMER --- Hammer skipped.

        case WAGIV:
            SetWagner( 4, 0.0, padfPrjParams[6], padfPrjParams[7] );
            break;

        case WAGVII:
            SetWagner( 7, 0.0, padfPrjParams[6], padfPrjParams[7] );
            break;

        // FIXME: OBEQA --- Oblated Equal Area skipped.
        // FIXME: ISINUS1 --- Integerized Sinusoidal Grid (the same as 99).
        // FIXME: CEA --- Cylindrical Equal Area skipped (Grid corners set in
        // meters for EASE grid).
        // FIXME: BCEA --- Cylindrical Equal Area skipped (Grid corners set in
        // DMS degs for EASE grid).
        // FIXME: ISINUS --- Integrized Sinusoidal skipped.

        default:
            CPLDebug( "OSR_USGS", "Unsupported projection: %ld", iProjSys );
            SetLocalCS( CPLString().Printf("GCTP projection number %ld",
                                           iProjSys) );
            break;
    }

/* -------------------------------------------------------------------- */
/*      Try to translate the datum/spheroid.                            */
/* -------------------------------------------------------------------- */

    if( !IsLocal() )
    {
        char *pszName = nullptr;
        double dfSemiMajor = 0.0;
        double dfInvFlattening = 0.0;

        if( iDatum < 0 ) // Use specified ellipsoid parameters.
        {
            if( padfPrjParams[0] > 0.0 )
            {
                if( padfPrjParams[1] > 1.0 )
                {
                    dfInvFlattening = OSRCalcInvFlattening(padfPrjParams[0],
                                                           padfPrjParams[1]);
                }
                else if( padfPrjParams[1] > 0.0 )
                {
                    dfInvFlattening =
                        1.0 / ( 1.0 - sqrt(1.0 - padfPrjParams[1]) );
                }
                else
                {
                    dfInvFlattening = 0.0;
                }

                SetGeogCS( "Unknown datum based upon the custom spheroid",
                           "Not specified (based on custom spheroid)",
                           "Custom spheroid", padfPrjParams[0], dfInvFlattening,
                           nullptr, 0, nullptr, 0 );
            }
            else if( padfPrjParams[1] > 0.0 )  // Clarke 1866.
            {
                if( OSRGetEllipsoidInfo( 7008, &pszName, &dfSemiMajor,
                                          &dfInvFlattening ) == OGRERR_NONE )
                {
                    SetGeogCS( CPLString().Printf(
                                    "Unknown datum based upon the %s ellipsoid",
                                    pszName ),
                               CPLString().Printf(
                                    "Not specified (based on %s spheroid)",
                                    pszName ),
                               pszName, dfSemiMajor, dfInvFlattening,
                               nullptr, 0.0, nullptr, 0.0 );
                    SetAuthority( "SPHEROID", "EPSG", 7008 );
                }
            }
            else                              // Sphere, rad 6370997 m
            {
                if( OSRGetEllipsoidInfo( 7047, &pszName, &dfSemiMajor,
                                         &dfInvFlattening ) == OGRERR_NONE )
                {
                    SetGeogCS( CPLString().Printf(
                                    "Unknown datum based upon the %s ellipsoid",
                                    pszName ),
                               CPLString().Printf(
                                    "Not specified (based on %s spheroid)",
                                    pszName ),
                               pszName, dfSemiMajor, dfInvFlattening,
                               nullptr, 0.0, nullptr, 0.0 );
                    SetAuthority( "SPHEROID", "EPSG", 7047 );
                }
            }
        }
        else if( iDatum < NUMBER_OF_ELLIPSOIDS && aoEllips[iDatum] )
        {
            if( OSRGetEllipsoidInfo( aoEllips[iDatum], &pszName,
                                     &dfSemiMajor,
                                     &dfInvFlattening ) == OGRERR_NONE )
            {
                SetGeogCS( CPLString().Printf(
                               "Unknown datum based upon the %s ellipsoid",
                               pszName),
                           CPLString().Printf(
                               "Not specified (based on %s spheroid)",
                               pszName),
                           pszName, dfSemiMajor, dfInvFlattening,
                           nullptr, 0.0, nullptr, 0.0 );
                SetAuthority( "SPHEROID", "EPSG", aoEllips[iDatum] );
            }
            else
            {
                CPLError( CE_Warning, CPLE_AppDefined,
                          "Failed to lookup datum code %d. "
                          "Falling back to use WGS84.",
                          static_cast<int>(iDatum) );
                SetWellKnownGeogCS("WGS84");
            }
        }
        else
        {
            CPLError( CE_Warning, CPLE_AppDefined,
                      "Wrong datum code %d. Supported datums 0--%d only.  "
                      "Setting WGS84 as a fallback.",
                      static_cast<int>(iDatum), NUMBER_OF_ELLIPSOIDS );
            SetWellKnownGeogCS( "WGS84" );
        }

        CPLFree( pszName );
    }

/* -------------------------------------------------------------------- */
/*      Grid units translation                                          */
/* -------------------------------------------------------------------- */
    if( IsLocal() || IsProjected() )
        SetLinearUnits( SRS_UL_METER, 1.0 );

    return OGRERR_NONE;
}

/************************************************************************/
/*                          OSRExportToUSGS()                           */
/************************************************************************/
/**
 * \brief Export coordinate system in USGS GCTP projection definition.
 *
 * This function is the same as OGRSpatialReference::exportToUSGS().
 */

OGRErr OSRExportToUSGS( OGRSpatialReferenceH hSRS,
                        long *piProjSys, long *piZone,
                        double **ppadfPrjParams, long *piDatum )

{
    VALIDATE_POINTER1( hSRS, "OSRExportToUSGS", OGRERR_FAILURE );

    *ppadfPrjParams = nullptr;

    return OGRSpatialReference::FromHandle(hSRS)->exportToUSGS( piProjSys, piZone,
                                                         ppadfPrjParams,
                                                         piDatum );
}

/************************************************************************/
/*                           exportToUSGS()                             */
/************************************************************************/

/**
 * \brief Export coordinate system in USGS GCTP projection definition.
 *
 * This method is the equivalent of the C function OSRExportToUSGS().
 *
 * @param piProjSys Pointer to variable, where the projection system code will
 * be returned.
 *
 * @param piZone Pointer to variable, where the zone for UTM and State Plane
 * projection systems will be returned.
 *
 * @param ppadfPrjParams Pointer to which dynamically allocated array of
 * 15 projection parameters will be assigned. See importFromUSGS() for
 * the list of parameters. Caller responsible to free this array.
 *
 * @param piDatum Pointer to variable, where the datum code will
 * be returned.
 *
 * @return OGRERR_NONE on success or an error code on failure.
 */

OGRErr OGRSpatialReference::exportToUSGS( long *piProjSys, long *piZone,
                                          double **ppadfPrjParams,
                                          long *piDatum ) const

{
    const char *pszProjection = GetAttrValue("PROJECTION");

/* -------------------------------------------------------------------- */
/*      Fill all projection parameters with zero.                       */
/* -------------------------------------------------------------------- */
    *ppadfPrjParams = static_cast<double *>(CPLMalloc(15 * sizeof(double)));
    for( int i = 0; i < 15; i++ )
        (*ppadfPrjParams)[i] = 0.0;

    *piZone = 0L;

/* ==================================================================== */
/*      Handle the projection definition.                               */
/* ==================================================================== */
    if( IsLocal() )
        *piProjSys = GEO;

    else if( pszProjection == nullptr )
    {
#ifdef DEBUG
        CPLDebug( "OSR_USGS",
                  "Empty projection definition, considered as Geographic" );
#endif
        *piProjSys = GEO;
    }

    else if( EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
    {
        *piProjSys = ALBERS;
        (*ppadfPrjParams)[2] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
        (*ppadfPrjParams)[3] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) )
    {
        *piProjSys = LAMCC;
        (*ppadfPrjParams)[2] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
        (*ppadfPrjParams)[3] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) )
    {
        *piProjSys = MERCAT;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) )
    {
        *piProjSys = PS;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_POLYCONIC) )
    {
        *piProjSys = POLYC;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC) )
    {
        *piProjSys = EQUIDC;
        (*ppadfPrjParams)[2] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
        (*ppadfPrjParams)[3] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 ) );
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
        (*ppadfPrjParams)[8] = 1.0;
    }

    else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) )
    {
        int bNorth;

        *piZone = GetUTMZone( &bNorth );

        if( *piZone != 0 )
        {
            *piProjSys = UTM;
            if( !bNorth )
                *piZone = - *piZone;
        }
        else
        {
            *piProjSys = TM;
            (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
            (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
                GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
            (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
                GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
            (*ppadfPrjParams)[6] =
                GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
            (*ppadfPrjParams)[7] =
                GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
        }
    }

    else if( EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC) )
    {
        *piProjSys = STEREO;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) )
    {
        *piProjSys = LAMAZ;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT) )
    {
        *piProjSys = AZMEQD;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_GNOMONIC) )
    {
        *piProjSys = GNOMON;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC) )
    {
        *piProjSys = ORTHO;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_SINUSOIDAL) )
    {
        *piProjSys = SNSOID;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR) )
    {
        *piProjSys = EQRECT;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL) )
    {
        *piProjSys = MILLER;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_VANDERGRINTEN) )
    {
        *piProjSys = VGRINT;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR) )
    {
        *piProjSys = HOM;
        (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
        (*ppadfPrjParams)[3] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_AZIMUTH, 0.0 ) );
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
        (*ppadfPrjParams)[12] = 1.0;
    }

    else if( EQUAL(pszProjection,
                   SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) )
    {
        *piProjSys = HOM;
        (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
        (*ppadfPrjParams)[5] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
        (*ppadfPrjParams)[8] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_1, 0.0 ) );
        (*ppadfPrjParams)[9] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_POINT_1, 0.0 ) );
        (*ppadfPrjParams)[10] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_POINT_2, 0.0 ) );
        (*ppadfPrjParams)[11] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LATITUDE_OF_POINT_2, 0.0 ) );
        (*ppadfPrjParams)[12] = 0.0;
    }

    else if( EQUAL(pszProjection, SRS_PT_ROBINSON) )
    {
        *piProjSys = ROBIN;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_MOLLWEIDE) )
    {
        *piProjSys = MOLL;
        (*ppadfPrjParams)[4] = CPLDecToPackedDMS(
            GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 ) );
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_WAGNER_IV) )
    {
        *piProjSys = WAGIV;
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }

    else if( EQUAL(pszProjection, SRS_PT_WAGNER_VII) )
    {
        *piProjSys = WAGVII;
        (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
        (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
    }
    // Projection unsupported by GCTP.
    else
    {
        CPLDebug( "OSR_USGS",
                  "Projection \"%s\" unsupported by USGS GCTP. "
                  "Geographic system will be used.", pszProjection );
        *piProjSys = GEO;
    }

/* -------------------------------------------------------------------- */
/*      Translate the datum.                                            */
/* -------------------------------------------------------------------- */
    const char *pszDatum = GetAttrValue( "DATUM" );

    if( pszDatum )
    {
        if( EQUAL( pszDatum, SRS_DN_NAD27 ) )
        {
            *piDatum = CLARKE1866;
        }
        else if( EQUAL( pszDatum, SRS_DN_NAD83 ) )
        {
            *piDatum = GRS1980;
        }
        else if( EQUAL( pszDatum, SRS_DN_WGS84 ) )
        {
            *piDatum = WGS84;
        }
        // If not found well known datum, translate ellipsoid.
        else
        {
            const double dfSemiMajor = GetSemiMajor();
            const double dfInvFlattening = GetInvFlattening();

#ifdef DEBUG
            CPLDebug( "OSR_USGS",
                      "Datum \"%s\" unsupported by USGS GCTP. "
                      "Try to translate ellipsoid definition.", pszDatum );
#endif

            int i = 0;  // Used after for.
            for( ; i < NUMBER_OF_ELLIPSOIDS; i++ )
            {
                double dfSM = 0.0;
                double dfIF = 0.0;

                if( OSRGetEllipsoidInfo( aoEllips[i], nullptr,
                                         &dfSM, &dfIF ) == OGRERR_NONE
                    && CPLIsEqual( dfSemiMajor, dfSM )
                    && CPLIsEqual( dfInvFlattening, dfIF ) )
                {
                    *piDatum = i;
                    break;
                }
            }

            if( i == NUMBER_OF_ELLIPSOIDS )  // Didn't found matches; set
            {                                // custom ellipsoid parameters.
#ifdef DEBUG
                CPLDebug( "OSR_USGS",
                          "Ellipsoid \"%s\" unsupported by USGS GCTP. "
                          "Custom ellipsoid definition will be used.",
                          pszDatum );
#endif
                *piDatum = -1;
                (*ppadfPrjParams)[0] = dfSemiMajor;
                if( std::abs( dfInvFlattening ) < 0.000000000001 )
                {
                    (*ppadfPrjParams)[1] = dfSemiMajor;
                }
                else
                {
                    (*ppadfPrjParams)[1] =
                        dfSemiMajor * (1.0 - 1.0/dfInvFlattening);
                }
            }
        }
    }
    else
    {
        *piDatum = -1;
    }

    return OGRERR_NONE;
}
