LCOV - code coverage report
Current view: top level - ogr - ogr_srs_pci.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 324 531 61.0 %
Date: 2024-05-04 12:52:34 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  OGRSpatialReference translation to/from PCI georeferencing
       5             :  *           information.
       6             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2003, Andrey Kiselev <dron@ak4719.spb.edu>
      10             :  * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include "cpl_port.h"
      32             : #include "ogr_srs_api.h"
      33             : 
      34             : #include <cctype>
      35             : #include <cstdio>
      36             : #include <cstdlib>
      37             : #include <cstring>
      38             : 
      39             : #include "cpl_conv.h"
      40             : #include "cpl_csv.h"
      41             : #include "cpl_error.h"
      42             : #include "cpl_string.h"
      43             : #include "cpl_vsi.h"
      44             : #include "ogr_core.h"
      45             : #include "ogr_p.h"
      46             : #include "ogr_spatialref.h"
      47             : 
      48             : // PCI uses a 16-character string for coordinate system and datum/ellipsoid.
      49             : constexpr int knProjSize = 16;
      50             : 
      51             : struct PCIDatums
      52             : {
      53             :     const char *pszPCIDatum;
      54             :     int nEPSGCode;
      55             : };
      56             : 
      57             : static const PCIDatums asDatums[] = {
      58             :     {"D-01", 4267},  // NAD27 (USA, NADCON)
      59             :     {"D-03", 4267},  // NAD27 (Canada, NTv1)
      60             :     {"D-02", 4269},  // NAD83 (USA, NADCON)
      61             :     {"D-04", 4269},  // NAD83 (Canada, NTv1)
      62             :     {"D000", 4326},  // WGS 1984
      63             :     {"D001", 4322},  // WGS 1972
      64             :     {"D008", 4296},  // Sudan
      65             :     {"D013", 4601},  // Antigua Island Astro 1943
      66             :     {"D029", 4202},  // Australian Geodetic 1966
      67             :     {"D030", 4203},  // Australian Geodetic 1984
      68             :     {"D033", 4216},  // Bermuda 1957
      69             :     {"D034", 4165},  // Bissau
      70             :     {"D036", 4219},  // Bukit Rimpah
      71             :     {"D038", 4221},  // Campo Inchauspe
      72             :     {"D040", 4222},  // Cape
      73             :     {"D042", 4223},  // Carthage
      74             :     {"D044", 4224},  // Chua Astro
      75             :     {"D045", 4225},  // Corrego Alegre
      76             :     {"D046", 4155},  // Dabola (Guinea)
      77             :     {"D066", 4272},  // Geodetic Datum 1949 (New Zealand)
      78             :     {"D071", 4255},  // Herat North (Afghanistan)
      79             :     {"D077", 4239},  // Indian 1954 (Thailand, Vietnam)
      80             :     {"D078", 4240},  // Indian 1975 (Thailand)
      81             :     {"D083", 4244},  // Kandawala (Sri Lanka)
      82             :     {"D085", 4245},  // Kertau 1948 (West Malaysia & Singapore)
      83             :     {"D088", 4250},  // Leigon (Ghana)
      84             :     {"D089", 4251},  // Liberia 1964 (Liberia)
      85             :     {"D092", 4256},  // Mahe 1971 (Mahe Island)
      86             :     {"D093", 4262},  // Massawa (Ethiopia (Eritrea))
      87             :     {"D094", 4261},  // Merchich (Morocco)
      88             :     {"D098",
      89             :      4604},  // Montserrat Island Astro 1958 (Montserrat (Leeward Islands))
      90             :     {"D110", 4267},  // NAD27 / Alaska
      91             :     {"D139", 4282},  // Pointe Noire 1948 (Congo)
      92             :     {"D140", 4615},  // Porto Santo 1936 (Porto Santo, Madeira Islands)
      93             :     {"D151", 4139},  // Puerto Rico (Puerto Rico, Virgin Islands)
      94             :     {"D153", 4287},  // Qornoq (Greenland (South))
      95             :     {"D158", 4292},  // Sapper Hill 1943 (East Falkland Island)
      96             :     {"D159", 4293},  // Schwarzeck (Namibia)
      97             :     {"D160", 4616},  // Selvagem Grande 1938 (Salvage Islands)
      98             :     {"D176", 4297},  // Tananarive Observatory 1925 (Madagascar)
      99             :     {"D177", 4298},  // Timbalai 1948 (Brunei, East Malaysia (Sabah, Sarawak))
     100             :     {"D187", 4309},  // Yacare (Uruguay)
     101             :     {"D188", 4311},  // Zanderij (Suriname)
     102             :     {"D401", 4124},  // RT90 (Sweden)
     103             :     {"D501", 4312},  // MGI (Hermannskogel, Austria)
     104             :     {nullptr, 0}};
     105             : 
     106             : static const PCIDatums asEllips[] = {
     107             :     {"E000", 7008},  // Clarke, 1866 (NAD1927)
     108             :     {"E001", 7034},  // Clarke, 1880
     109             :     {"E002", 7004},  // Bessel, 1841
     110             :     {"E004", 7022},  // International, 1924 (Hayford, 1909)
     111             :     {"E005", 7043},  // WGS, 1972
     112             :     {"E006", 7042},  // Everest, 1830
     113             :     {"E008", 7019},  // GRS, 1980 (NAD1983)
     114             :     {"E009", 7001},  // Airy, 1830
     115             :     {"E010", 7018},  // Modified Everest
     116             :     {"E011", 7002},  // Modified Airy
     117             :     {"E012", 7030},  // WGS, 1984 (GPS)
     118             :     {"E014", 7003},  // Australian National, 1965
     119             :     {"E015", 7024},  // Krassovsky, 1940
     120             :     {"E016", 7053},  // Hough
     121             :     {"E019", 7052},  // normal sphere
     122             :     {"E333", 7046},  // Bessel 1841 (Japan By Law)
     123             :     {"E900", 7006},  // Bessel, 1841 (Namibia)
     124             :     {"E901", 7044},  // Everest, 1956
     125             :     {"E902", 7056},  // Everest, 1969
     126             :     {"E903", 7016},  // Everest (Sabah & Sarawak)
     127             :     {"E904", 7020},  // Helmert, 1906
     128             :     {"E907", 7036},  // South American, 1969
     129             :     {"E910", 7041},  // ATS77
     130             :     {nullptr, 0}};
     131             : 
     132             : /************************************************************************/
     133             : /*                         OSRImportFromPCI()                           */
     134             : /************************************************************************/
     135             : 
     136             : /**
     137             :  * \brief Import coordinate system from PCI projection definition.
     138             :  *
     139             :  * This function is the same as OGRSpatialReference::importFromPCI().
     140             :  */
     141             : 
     142           8 : OGRErr OSRImportFromPCI(OGRSpatialReferenceH hSRS, const char *pszProj,
     143             :                         const char *pszUnits, double *padfPrjParams)
     144             : 
     145             : {
     146           8 :     VALIDATE_POINTER1(hSRS, "OSRImportFromPCI", OGRERR_FAILURE);
     147             : 
     148           8 :     return OGRSpatialReference::FromHandle(hSRS)->importFromPCI(
     149           8 :         pszProj, pszUnits, padfPrjParams);
     150             : }
     151             : 
     152             : /************************************************************************/
     153             : /*                          importFromPCI()                             */
     154             : /************************************************************************/
     155             : 
     156             : /**
     157             :  * \brief Import coordinate system from PCI projection definition.
     158             :  *
     159             :  * PCI software uses 16-character string to specify coordinate system
     160             :  * and datum/ellipsoid. You should supply at least this string to the
     161             :  * importFromPCI() function.
     162             :  *
     163             :  * This function is the equivalent of the C function OSRImportFromPCI().
     164             :  *
     165             :  * @param pszProj NULL terminated string containing the definition. Looks
     166             :  * like "pppppppppppp Ennn" or "pppppppppppp Dnnn", where "pppppppppppp" is
     167             :  * a projection code, "Ennn" is an ellipsoid code, "Dnnn" a datum code.
     168             :  *
     169             :  * @param pszUnits Grid units code ("DEGREE" or "METRE"). If NULL "METRE" will
     170             :  * be used.
     171             :  *
     172             :  * @param padfPrjParams Array of 17 coordinate system parameters:
     173             :  *
     174             :  * [0]  Spheroid semi major axis
     175             :  * [1]  Spheroid semi minor axis
     176             :  * [2]  Reference Longitude
     177             :  * [3]  Reference Latitude
     178             :  * [4]  First Standard Parallel
     179             :  * [5]  Second Standard Parallel
     180             :  * [6]  False Easting
     181             :  * [7]  False Northing
     182             :  * [8]  Scale Factor
     183             :  * [9]  Height above sphere surface
     184             :  * [10] Longitude of 1st point on center line
     185             :  * [11] Latitude of 1st point on center line
     186             :  * [12] Longitude of 2nd point on center line
     187             :  * [13] Latitude of 2nd point on center line
     188             :  * [14] Azimuth east of north for center line
     189             :  * [15] Landsat satellite number
     190             :  * [16] Landsat path number
     191             :  *
     192             :  * Particular projection uses different parameters, unused ones may be set to
     193             :  * zero. If NULL is supplied instead of an array pointer, default values will be
     194             :  * used (i.e., zeroes).
     195             :  *
     196             :  * @return OGRERR_NONE on success or an error code in case of failure.
     197             :  */
     198             : 
     199        1205 : OGRErr OGRSpatialReference::importFromPCI(const char *pszProj,
     200             :                                           const char *pszUnits,
     201             :                                           const double *padfPrjParams)
     202             : 
     203             : {
     204        1205 :     Clear();
     205             : 
     206        2410 :     if (pszProj == nullptr ||
     207        1205 :         CPLStrnlen(pszProj, knProjSize) < static_cast<size_t>(knProjSize))
     208             :     {
     209           3 :         return OGRERR_CORRUPT_DATA;
     210             :     }
     211             : 
     212        1202 :     CPLDebug("OSR_PCI", "Trying to import projection \"%s\"", pszProj);
     213             : 
     214             :     /* -------------------------------------------------------------------- */
     215             :     /*      Use safe defaults if projection parameters are not supplied.    */
     216             :     /* -------------------------------------------------------------------- */
     217             :     static const double adfZeroedPrjParams[17] = {0};
     218        1202 :     if (padfPrjParams == nullptr)
     219             :     {
     220           0 :         padfPrjParams = adfZeroedPrjParams;
     221             :     }
     222             : 
     223             :     /* -------------------------------------------------------------------- */
     224             :     /*      Extract and "normalize" the earthmodel to look like E001,       */
     225             :     /*      D-02 or D109.                                                   */
     226             :     /* -------------------------------------------------------------------- */
     227        1202 :     char szEarthModel[5] = {};
     228             : 
     229        1202 :     strcpy(szEarthModel, "");
     230        1202 :     const char *pszEM = pszProj + strlen(pszProj) - 1;
     231       14108 :     while (pszEM != pszProj)
     232             :     {
     233       14108 :         if (*pszEM == 'e' || *pszEM == 'E' || *pszEM == 'd' || *pszEM == 'D')
     234             :         {
     235        1202 :             int nCode = atoi(pszEM + 1);
     236             : 
     237        1202 :             if (nCode >= -99 && nCode <= 999)
     238        1202 :                 snprintf(szEarthModel, sizeof(szEarthModel), "%c%03d",
     239        1202 :                          CPLToupper(static_cast<unsigned char>(*pszEM)), nCode);
     240             : 
     241        1202 :             break;
     242             :         }
     243             : 
     244       12906 :         pszEM--;
     245             :     }
     246             : 
     247        1196 :     const bool bIsNAD27 = EQUAL(pszEM, "E000") || EQUAL(pszEM, "D-01") ||
     248        1196 :                           EQUAL(pszEM, "D-03") || EQUAL(pszEM, "D-07") ||
     249        1196 :                           EQUAL(pszEM, "D-09") || EQUAL(pszEM, "D-11") ||
     250        2398 :                           EQUAL(pszEM, "D-13") || EQUAL(pszEM, "D-17");
     251             : 
     252             :     /* -------------------------------------------------------------------- */
     253             :     /*      Operate on the basis of the projection name.                    */
     254             :     /* -------------------------------------------------------------------- */
     255        1202 :     if (STARTS_WITH_CI(pszProj, "LONG/LAT"))
     256             :     {
     257             :         // TODO(schwehr): A NOP is okay?
     258             :     }
     259        1193 :     else if (STARTS_WITH_CI(pszProj, "METER") ||
     260        1186 :              STARTS_WITH_CI(pszProj, "METRE"))
     261             :     {
     262        1171 :         SetLocalCS("METER");
     263        1171 :         SetLinearUnits("METER", 1.0);
     264             :     }
     265          22 :     else if (STARTS_WITH_CI(pszProj, "FEET") || STARTS_WITH_CI(pszProj, "FOOT"))
     266             :     {
     267           0 :         SetLocalCS("FEET");
     268           0 :         SetLinearUnits("FEET", CPLAtof(SRS_UL_FOOT_CONV));
     269             :     }
     270          22 :     else if (STARTS_WITH_CI(pszProj, "ACEA"))
     271             :     {
     272           0 :         SetACEA(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
     273           0 :                 padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     274             :     }
     275          22 :     else if (STARTS_WITH_CI(pszProj, "AE"))
     276             :     {
     277           0 :         SetAE(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     278           0 :               padfPrjParams[7]);
     279             :     }
     280          22 :     else if (STARTS_WITH_CI(pszProj, "CASS "))
     281             :     {
     282           0 :         SetCS(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     283           0 :               padfPrjParams[7]);
     284             :     }
     285          22 :     else if (STARTS_WITH_CI(pszProj, "EC"))
     286             :     {
     287           2 :         SetEC(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
     288           2 :               padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     289             :     }
     290          20 :     else if (STARTS_WITH_CI(pszProj, "ER"))
     291             :     {
     292             :         // PCI and GCTP don't support natural origin lat.
     293           0 :         SetEquirectangular2(0.0, padfPrjParams[2], padfPrjParams[3],
     294           0 :                             padfPrjParams[6], padfPrjParams[7]);
     295             :     }
     296          20 :     else if (STARTS_WITH_CI(pszProj, "GNO"))
     297             :     {
     298           0 :         SetGnomonic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     299           0 :                     padfPrjParams[7]);
     300             :     }
     301             :     // FIXME: GVNP --- General Vertical Near- Side Perspective skipped.
     302             :     // FIXME: GOOD -- Our Goode's is not the interrupted version from PCI.
     303          20 :     else if (STARTS_WITH_CI(pszProj, "LAEA"))
     304             :     {
     305           0 :         SetLAEA(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     306           0 :                 padfPrjParams[7]);
     307             :     }
     308          20 :     else if (STARTS_WITH_CI(pszProj, "LCC "))
     309             :     {
     310           4 :         SetLCC(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
     311           4 :                padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     312             :     }
     313          16 :     else if (STARTS_WITH_CI(pszProj, "LCC_1SP "))
     314             :     {
     315           0 :         SetLCC1SP(padfPrjParams[3], padfPrjParams[2], padfPrjParams[8],
     316           0 :                   padfPrjParams[6], padfPrjParams[7]);
     317             :     }
     318          16 :     else if (STARTS_WITH_CI(pszProj, "MC"))
     319             :     {
     320           0 :         SetMC(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     321           0 :               padfPrjParams[7]);
     322             :     }
     323          16 :     else if (STARTS_WITH_CI(pszProj, "MER"))
     324             :     {
     325           1 :         if (EQUAL(pszEM, "D894") && padfPrjParams[3] == 0 &&
     326           1 :             padfPrjParams[2] == 0 && padfPrjParams[8] == 1.0 &&
     327           1 :             padfPrjParams[6] == 0 && padfPrjParams[7] == 0)
     328             :         {
     329             :             // Special case for Web Mercator
     330           1 :             return importFromEPSG(3857);
     331             :         }
     332           0 :         SetMercator(padfPrjParams[3], padfPrjParams[2],
     333           0 :                     (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
     334           0 :                     padfPrjParams[6], padfPrjParams[7]);
     335             :     }
     336          15 :     else if (STARTS_WITH_CI(pszProj, "OG"))
     337             :     {
     338           0 :         SetOrthographic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     339           0 :                         padfPrjParams[7]);
     340             :     }
     341          15 :     else if (STARTS_WITH_CI(pszProj, "OM "))
     342             :     {
     343           0 :         if (padfPrjParams[10] == 0.0 && padfPrjParams[11] == 0.0 &&
     344           0 :             padfPrjParams[12] == 0.0 && padfPrjParams[13] == 0.0)
     345             :         {
     346           0 :             SetHOM(padfPrjParams[3], padfPrjParams[2], padfPrjParams[14],
     347           0 :                    padfPrjParams[14],  // Use azimuth for grid angle.
     348           0 :                    padfPrjParams[8], padfPrjParams[6], padfPrjParams[7]);
     349             :         }
     350             :         else
     351             :         {
     352           0 :             SetHOM2PNO(padfPrjParams[3], padfPrjParams[11], padfPrjParams[10],
     353           0 :                        padfPrjParams[13], padfPrjParams[12], padfPrjParams[8],
     354           0 :                        padfPrjParams[6], padfPrjParams[7]);
     355             :         }
     356             :     }
     357          15 :     else if (STARTS_WITH_CI(pszProj, "PC"))
     358             :     {
     359           0 :         SetPolyconic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
     360           0 :                      padfPrjParams[7]);
     361             :     }
     362          15 :     else if (STARTS_WITH_CI(pszProj, "PS"))
     363             :     {
     364           0 :         SetPS(padfPrjParams[3], padfPrjParams[2],
     365           0 :               (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
     366           0 :               padfPrjParams[6], padfPrjParams[7]);
     367             :     }
     368          15 :     else if (STARTS_WITH_CI(pszProj, "ROB"))
     369             :     {
     370           0 :         SetRobinson(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     371             :     }
     372             : 
     373          15 :     else if (STARTS_WITH_CI(pszProj, "SGDO"))
     374             :     {
     375           0 :         SetOS(padfPrjParams[3], padfPrjParams[2],
     376           0 :               (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
     377           0 :               padfPrjParams[6], padfPrjParams[7]);
     378             :     }
     379          15 :     else if (STARTS_WITH_CI(pszProj, "SG"))
     380             :     {
     381           0 :         SetStereographic(padfPrjParams[3], padfPrjParams[2],
     382           0 :                          (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
     383           0 :                          padfPrjParams[6], padfPrjParams[7]);
     384             :     }
     385          15 :     else if (STARTS_WITH_CI(pszProj, "SIN"))
     386             :     {
     387           0 :         SetSinusoidal(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     388             :     }
     389             :     // FIXME: SOM --- Space Oblique Mercator skipped.
     390          15 :     else if (STARTS_WITH_CI(pszProj, "SPCS"))
     391             :     {
     392             :         const int iZone =
     393           0 :             static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
     394             : 
     395           0 :         SetStatePlane(iZone, !bIsNAD27);
     396           0 :         SetLinearUnitsAndUpdateParameters(SRS_UL_METER, 1.0);
     397             :     }
     398          15 :     else if (STARTS_WITH_CI(pszProj, "SPIF"))
     399             :     {
     400             :         const int iZone =
     401           0 :             static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
     402             : 
     403           0 :         SetStatePlane(iZone, !bIsNAD27);
     404           0 :         SetLinearUnitsAndUpdateParameters(SRS_UL_FOOT,
     405             :                                           CPLAtof(SRS_UL_FOOT_CONV));
     406             :     }
     407          15 :     else if (STARTS_WITH_CI(pszProj, "SPAF"))
     408             :     {
     409             :         const int iZone =
     410           0 :             static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
     411             : 
     412           0 :         SetStatePlane(iZone, !bIsNAD27);
     413           0 :         SetLinearUnitsAndUpdateParameters(SRS_UL_US_FOOT,
     414             :                                           CPLAtof(SRS_UL_US_FOOT_CONV));
     415             :     }
     416          15 :     else if (STARTS_WITH_CI(pszProj, "TM"))
     417             :     {
     418           0 :         SetTM(padfPrjParams[3], padfPrjParams[2],
     419           0 :               (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
     420           0 :               padfPrjParams[6], padfPrjParams[7]);
     421             :     }
     422             : 
     423          15 :     else if (STARTS_WITH_CI(pszProj, "UTM"))
     424             :     {
     425          15 :         bool bNorth = true;
     426             : 
     427             :         int iZone =
     428          15 :             static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 4, 5));
     429          15 :         if (iZone < 0)
     430             :         {
     431           0 :             iZone = -iZone;
     432           0 :             bNorth = false;
     433             :         }
     434             : 
     435             :         // Check for a zone letter. PCI uses, accidentally, MGRS
     436             :         // type row lettering in its UTM projection.
     437          15 :         char byZoneID = 0;
     438             : 
     439          15 :         if (strlen(pszProj) > 10 && pszProj[10] != ' ')
     440           4 :             byZoneID = pszProj[10];
     441             : 
     442             :         // Determine if the MGRS zone falls above or below the equator.
     443          15 :         if (byZoneID != 0)
     444             :         {
     445           4 :             CPLDebug("OSR_PCI", "Found MGRS zone in UTM projection string: %c",
     446             :                      byZoneID);
     447             : 
     448           4 :             if (byZoneID >= 'N' && byZoneID <= 'X')
     449             :             {
     450           3 :                 bNorth = true;
     451             :             }
     452           1 :             else if (byZoneID >= 'C' && byZoneID <= 'M')
     453             :             {
     454           1 :                 bNorth = false;
     455             :             }
     456             :             else
     457             :             {
     458             :                 // Yikes.  Most likely we got something that was not really
     459             :                 // an MGRS zone code so we ignore it.
     460             :             }
     461             :         }
     462             : 
     463          15 :         SetUTM(iZone, bNorth);
     464             :     }
     465           0 :     else if (STARTS_WITH_CI(pszProj, "VDG"))
     466             :     {
     467           0 :         SetVDG(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
     468             :     }
     469             :     else
     470             :     {
     471           0 :         CPLDebug("OSR_PCI", "Unsupported projection: %s", pszProj);
     472           0 :         SetLocalCS(pszProj);
     473             :     }
     474             : 
     475             :     /* ==================================================================== */
     476             :     /*      Translate the datum/spheroid.                                   */
     477             :     /* ==================================================================== */
     478             : 
     479             :     /* -------------------------------------------------------------------- */
     480             :     /*      We have an earthmodel string, look it up in the datum list.     */
     481             :     /* -------------------------------------------------------------------- */
     482        2402 :     if (strlen(szEarthModel) > 0 &&
     483        1201 :         (GetRoot() == nullptr || IsProjected() || IsGeographic()))
     484             :     {
     485          30 :         const PCIDatums *pasDatum = asDatums;
     486             : 
     487             :         // Search for matching datum.
     488         729 :         while (pasDatum->pszPCIDatum)
     489             :         {
     490         715 :             if (EQUALN(szEarthModel, pasDatum->pszPCIDatum, 4))
     491             :             {
     492          32 :                 OGRSpatialReference oGCS;
     493          16 :                 oGCS.importFromEPSG(pasDatum->nEPSGCode);
     494          16 :                 CopyGeogCSFrom(&oGCS);
     495          16 :                 break;
     496             :             }
     497         699 :             pasDatum++;
     498             :         }
     499             : 
     500             :         /* --------------------------------------------------------------------
     501             :          */
     502             :         /*      If we did not find a datum definition in our in-code EPSG */
     503             :         /*      lookup table, then try fetching from the pci_datum.txt */
     504             :         /*      file. */
     505             :         /* --------------------------------------------------------------------
     506             :          */
     507          30 :         char **papszDatumDefn = nullptr;
     508             : 
     509          30 :         if (!pasDatum->pszPCIDatum && szEarthModel[0] == 'D')
     510             :         {
     511           1 :             const char *pszDatumCSV = CSVFilename("pci_datum.txt");
     512           1 :             VSILFILE *fp = pszDatumCSV ? VSIFOpenL(pszDatumCSV, "r") : nullptr;
     513             : 
     514           1 :             if (fp != nullptr)
     515             :             {
     516           1 :                 char **papszLineItems = nullptr;
     517             : 
     518         315 :                 while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
     519             :                 {
     520         607 :                     if (CSLCount(papszLineItems) > 3 &&
     521         292 :                         EQUALN(papszLineItems[0], szEarthModel,
     522             :                                sizeof(szEarthModel) - 1))
     523             :                     {
     524           1 :                         papszDatumDefn = papszLineItems;
     525           1 :                         strncpy(szEarthModel, papszLineItems[2],
     526             :                                 sizeof(szEarthModel) - 1);
     527           1 :                         break;
     528             :                     }
     529         314 :                     CSLDestroy(papszLineItems);
     530             :                 }
     531             : 
     532           1 :                 VSIFCloseL(fp);
     533             :             }
     534             :         }
     535             : 
     536             :         /* --------------------------------------------------------------------
     537             :          */
     538             :         /*      If not, look in the ellipsoid/EPSG matching list. */
     539             :         /* --------------------------------------------------------------------
     540             :          */
     541          30 :         if (!pasDatum->pszPCIDatum)  // No matching; search for ellipsoids.
     542             :         {
     543          14 :             char *pszName = nullptr;
     544          14 :             double dfSemiMajor = 0.0;
     545          14 :             double dfInvFlattening = 0.0;
     546          14 :             int nEPSGCode = 0;
     547             : 
     548          14 :             pasDatum = asEllips;
     549             : 
     550          63 :             while (pasDatum->pszPCIDatum)
     551             :             {
     552          62 :                 if (EQUALN(szEarthModel, pasDatum->pszPCIDatum, 4))
     553             :                 {
     554          13 :                     nEPSGCode = pasDatum->nEPSGCode;
     555          13 :                     CPL_IGNORE_RET_VAL(
     556          13 :                         OSRGetEllipsoidInfo(pasDatum->nEPSGCode, &pszName,
     557             :                                             &dfSemiMajor, &dfInvFlattening));
     558          13 :                     break;
     559             :                 }
     560          49 :                 pasDatum++;
     561             :             }
     562             : 
     563             :             /* --------------------------------------------------------------------
     564             :              */
     565             :             /*      If we don't find it in that list, do a lookup in the */
     566             :             /*      pci_ellips.txt file. */
     567             :             /* --------------------------------------------------------------------
     568             :              */
     569          14 :             if (!pasDatum->pszPCIDatum && szEarthModel[0] == 'E')
     570             :             {
     571           1 :                 const char *pszCSV = CSVFilename("pci_ellips.txt");
     572           1 :                 VSILFILE *fp = pszCSV ? VSIFOpenL(pszCSV, "r") : nullptr;
     573             : 
     574           1 :                 if (fp != nullptr)
     575             :                 {
     576           1 :                     char **papszLineItems = nullptr;
     577             : 
     578          86 :                     while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
     579             :                     {
     580         138 :                         if (CSLCount(papszLineItems) > 3 &&
     581          52 :                             EQUALN(papszLineItems[0], szEarthModel, 4))
     582             :                         {
     583           1 :                             dfSemiMajor = CPLAtof(papszLineItems[2]);
     584             :                             const double dfSemiMinor =
     585           1 :                                 CPLAtof(papszLineItems[3]);
     586           1 :                             dfInvFlattening =
     587           1 :                                 OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
     588           1 :                             break;
     589             :                         }
     590          85 :                         CSLDestroy(papszLineItems);
     591             :                     }
     592           1 :                     CSLDestroy(papszLineItems);
     593             : 
     594           1 :                     VSIFCloseL(fp);
     595             :                 }
     596             :             }
     597             : 
     598             :             /* --------------------------------------------------------------------
     599             :              */
     600             :             /*      Custom spheroid? */
     601             :             /* --------------------------------------------------------------------
     602             :              */
     603          14 :             if (dfSemiMajor == 0.0 && STARTS_WITH_CI(szEarthModel, "E999") &&
     604           0 :                 padfPrjParams[0] != 0.0)
     605             :             {
     606           0 :                 dfSemiMajor = padfPrjParams[0];
     607           0 :                 double dfSemiMinor = padfPrjParams[1];
     608           0 :                 dfInvFlattening =
     609           0 :                     OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
     610             :             }
     611             : 
     612             :             /* --------------------------------------------------------------------
     613             :              */
     614             :             /*      If nothing else, fall back to WGS84 parameters. */
     615             :             /* --------------------------------------------------------------------
     616             :              */
     617          14 :             if (dfSemiMajor == 0.0)
     618             :             {
     619           0 :                 dfSemiMajor = SRS_WGS84_SEMIMAJOR;
     620           0 :                 dfInvFlattening = SRS_WGS84_INVFLATTENING;
     621             :             }
     622             : 
     623             :             /* --------------------------------------------------------------------
     624             :              */
     625             :             /*      Now try to put this all together into a GEOGCS definition.
     626             :              */
     627             :             /* --------------------------------------------------------------------
     628             :              */
     629             : 
     630          28 :             CPLString osEllipseName;
     631          14 :             if (pszName)
     632          13 :                 osEllipseName = pszName;
     633             :             else
     634           1 :                 osEllipseName.Printf("Unknown - PCI %s", szEarthModel);
     635          14 :             CPLFree(pszName);
     636             : 
     637          28 :             CPLString osDatumName;
     638          14 :             if (papszDatumDefn)
     639           1 :                 osDatumName = papszDatumDefn[1];
     640             :             else
     641          13 :                 osDatumName.Printf("Unknown - PCI %s", szEarthModel);
     642             : 
     643          28 :             const CPLString osGCSName = osDatumName;
     644             : 
     645          14 :             SetGeogCS(osGCSName, osDatumName, osEllipseName, dfSemiMajor,
     646             :                       dfInvFlattening);
     647             : 
     648             :             // Do we have an ellipsoid EPSG code?
     649          14 :             if (nEPSGCode != 0)
     650          13 :                 SetAuthority("SPHEROID", "EPSG", nEPSGCode);
     651             : 
     652             :             // Do we have 7 datum shift parameters?
     653          15 :             if (papszDatumDefn != nullptr && CSLCount(papszDatumDefn) >= 15 &&
     654           1 :                 CPLAtof(papszDatumDefn[14]) != 0.0)
     655             :             {
     656           1 :                 double dfScale = CPLAtof(papszDatumDefn[14]);
     657             : 
     658             :                 // we want scale in parts per million off 1.0
     659             :                 // but pci uses a mix of forms.
     660           1 :                 if (dfScale >= 0.999 && dfScale <= 1.001)
     661           1 :                     dfScale = (dfScale - 1.0) * 1000000.0;
     662             : 
     663           1 :                 SetTOWGS84(
     664           1 :                     CPLAtof(papszDatumDefn[3]), CPLAtof(papszDatumDefn[4]),
     665           1 :                     CPLAtof(papszDatumDefn[5]), CPLAtof(papszDatumDefn[11]),
     666           1 :                     CPLAtof(papszDatumDefn[12]), CPLAtof(papszDatumDefn[13]),
     667             :                     dfScale);
     668             :             }
     669             : 
     670             :             // Do we have 7 datum shift parameters?
     671          13 :             else if (papszDatumDefn != nullptr &&
     672          13 :                      CSLCount(papszDatumDefn) == 11 &&
     673           0 :                      (CPLAtof(papszDatumDefn[3]) != 0.0 ||
     674           0 :                       CPLAtof(papszDatumDefn[4]) != 0.0 ||
     675           0 :                       CPLAtof(papszDatumDefn[5]) != 0.0))
     676             :             {
     677           0 :                 SetTOWGS84(CPLAtof(papszDatumDefn[3]),
     678           0 :                            CPLAtof(papszDatumDefn[4]),
     679           0 :                            CPLAtof(papszDatumDefn[5]));
     680             :             }
     681             :         }
     682             : 
     683          30 :         CSLDestroy(papszDatumDefn);
     684             :     }
     685             : 
     686             :     /* -------------------------------------------------------------------- */
     687             :     /*      Grid units translation                                          */
     688             :     /* -------------------------------------------------------------------- */
     689        1201 :     if ((IsLocal() || IsProjected()) && pszUnits)
     690             :     {
     691           5 :         if (EQUAL(pszUnits, "METRE"))
     692           5 :             SetLinearUnits(SRS_UL_METER, 1.0);
     693           0 :         else if (EQUAL(pszUnits, "DEGREE"))
     694           0 :             SetAngularUnits(SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV));
     695             :         else
     696           0 :             SetLinearUnits(SRS_UL_METER, 1.0);
     697             :     }
     698             : 
     699        1201 :     return OGRERR_NONE;
     700             : }
     701             : 
     702             : /************************************************************************/
     703             : /*                          OSRExportToPCI()                            */
     704             : /************************************************************************/
     705             : /**
     706             :  * \brief Export coordinate system in PCI projection definition.
     707             :  *
     708             :  * This function is the same as OGRSpatialReference::exportToPCI().
     709             :  */
     710           7 : OGRErr OSRExportToPCI(OGRSpatialReferenceH hSRS, char **ppszProj,
     711             :                       char **ppszUnits, double **ppadfPrjParams)
     712             : 
     713             : {
     714           7 :     VALIDATE_POINTER1(hSRS, "OSRExportToPCI", OGRERR_FAILURE);
     715             : 
     716           7 :     *ppszProj = nullptr;
     717           7 :     *ppszUnits = nullptr;
     718           7 :     *ppadfPrjParams = nullptr;
     719             : 
     720           7 :     return OGRSpatialReference::FromHandle(hSRS)->exportToPCI(
     721           7 :         ppszProj, ppszUnits, ppadfPrjParams);
     722             : }
     723             : 
     724             : /************************************************************************/
     725             : /*                           exportToPCI()                              */
     726             : /************************************************************************/
     727             : 
     728             : /**
     729             :  * \brief Export coordinate system in PCI projection definition.
     730             :  *
     731             :  * Converts the loaded coordinate reference system into PCI projection
     732             :  * definition to the extent possible. The strings returned in ppszProj,
     733             :  * ppszUnits and ppadfPrjParams array should be deallocated by the caller
     734             :  * with CPLFree() when no longer needed.
     735             :  *
     736             :  * LOCAL_CS coordinate systems are not translatable.  An empty string
     737             :  * will be returned along with OGRERR_NONE.
     738             :  *
     739             :  * This method is the equivalent of the C function OSRExportToPCI().
     740             :  *
     741             :  * @param ppszProj pointer to which dynamically allocated PCI projection
     742             :  * definition will be assigned.
     743             :  *
     744             :  * @param ppszUnits pointer to which dynamically allocated units definition
     745             :  * will be assigned.
     746             :  *
     747             :  * @param ppadfPrjParams pointer to which dynamically allocated array of
     748             :  * 17 projection parameters will be assigned. See importFromPCI() for the list
     749             :  * of parameters.
     750             :  *
     751             :  * @return OGRERR_NONE on success or an error code on failure.
     752             :  */
     753             : 
     754          69 : OGRErr OGRSpatialReference::exportToPCI(char **ppszProj, char **ppszUnits,
     755             :                                         double **ppadfPrjParams) const
     756             : 
     757             : {
     758          69 :     const char *pszProjection = GetAttrValue("PROJECTION");
     759             : 
     760             :     /* -------------------------------------------------------------------- */
     761             :     /*      Fill all projection parameters with zero.                       */
     762             :     /* -------------------------------------------------------------------- */
     763          69 :     *ppadfPrjParams = static_cast<double *>(CPLMalloc(17 * sizeof(double)));
     764        1242 :     for (int i = 0; i < 17; i++)
     765        1173 :         (*ppadfPrjParams)[i] = 0.0;
     766             : 
     767             : /* -------------------------------------------------------------------- */
     768             : /*      Get the prime meridian info.                                    */
     769             : /* -------------------------------------------------------------------- */
     770             : #if 0
     771             :     const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" );
     772             :     double dfFromGreenwich = 0.0;
     773             : 
     774             :     if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2
     775             :         && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 )
     776             :     {
     777             :         dfFromGreenwich = CPLAtof(poPRIMEM->GetChild(1)->GetValue());
     778             :     }
     779             : #endif
     780             : 
     781             :     /* ==================================================================== */
     782             :     /*      Handle the projection definition.                               */
     783             :     /* ==================================================================== */
     784          69 :     char szProj[knProjSize + 1] = {};
     785             : 
     786          69 :     if (IsLocal())
     787             :     {
     788           2 :         if (GetLinearUnits() > 0.30479999 && GetLinearUnits() < 0.3048010)
     789           0 :             CPLPrintStringFill(szProj, "FEET", knProjSize);
     790             :         else
     791           2 :             CPLPrintStringFill(szProj, "METER", knProjSize);
     792             :     }
     793          67 :     else if (pszProjection == nullptr)
     794             :     {
     795          54 :         CPLPrintStringFill(szProj, "LONG/LAT", knProjSize);
     796             :     }
     797          13 :     else if (EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
     798             :     {
     799           0 :         CPLPrintStringFill(szProj, "ACEA", knProjSize);
     800           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     801           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     802           0 :         (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
     803           0 :         (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
     804           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     805           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     806             :     }
     807          13 :     else if (EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT))
     808             :     {
     809           0 :         CPLPrintStringFill(szProj, "AE", knProjSize);
     810           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     811           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     812           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     813           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     814             :     }
     815          13 :     else if (EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER))
     816             :     {
     817           0 :         CPLPrintStringFill(szProj, "CASS", knProjSize);
     818           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     819           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     820           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     821           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     822             :     }
     823          13 :     else if (EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC))
     824             :     {
     825           1 :         CPLPrintStringFill(szProj, "EC", knProjSize);
     826           1 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
     827           1 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
     828           1 :         (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
     829           1 :         (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
     830           1 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     831           1 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     832             :     }
     833          12 :     else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
     834             :     {
     835           0 :         CPLPrintStringFill(szProj, "ER", knProjSize);
     836           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     837           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
     838           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     839           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     840             :     }
     841          12 :     else if (EQUAL(pszProjection, SRS_PT_GNOMONIC))
     842             :     {
     843           0 :         CPLPrintStringFill(szProj, "GNO", knProjSize);
     844           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     845           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     846           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     847           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     848             :     }
     849          12 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
     850             :     {
     851           0 :         CPLPrintStringFill(szProj, "LAEA", knProjSize);
     852           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     853           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     854           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     855           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     856             :     }
     857          12 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
     858             :     {
     859           2 :         CPLPrintStringFill(szProj, "LCC", knProjSize);
     860           2 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     861           2 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     862           2 :         (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
     863           2 :         (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
     864           2 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     865           2 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     866             :     }
     867          10 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
     868             :     {
     869           0 :         CPLPrintStringFill(szProj, "LCC_1SP", knProjSize);
     870           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     871           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     872           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
     873           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     874           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     875             :     }
     876          10 :     else if (EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL))
     877             :     {
     878           0 :         CPLPrintStringFill(szProj, "MC", knProjSize);
     879           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     880           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     881           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     882           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     883             :     }
     884          10 :     else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
     885             :     {
     886           1 :         CPLPrintStringFill(szProj, "MER", knProjSize);
     887           1 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     888           1 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     889           1 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     890           1 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     891           1 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
     892             :     }
     893           9 :     else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
     894             :     {
     895           0 :         CPLPrintStringFill(szProj, "OG", knProjSize);
     896           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     897           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     898           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     899           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     900             :     }
     901           9 :     else if (EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
     902             :     {
     903           0 :         CPLPrintStringFill(szProj, "OM", knProjSize);
     904           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
     905           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
     906           0 :         (*ppadfPrjParams)[14] = GetNormProjParm(SRS_PP_AZIMUTH, 0.0);
     907             :         // Note: Ignoring rectified_grid_angle which has no PCI analog.
     908           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0);
     909           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     910           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     911             :     }
     912           9 :     else if (EQUAL(pszProjection,
     913             :                    SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN))
     914             :     {
     915           0 :         CPLPrintStringFill(szProj, "OM", knProjSize);
     916           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
     917           0 :         (*ppadfPrjParams)[11] =
     918           0 :             GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0);
     919           0 :         (*ppadfPrjParams)[10] =
     920           0 :             GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0);
     921           0 :         (*ppadfPrjParams)[13] =
     922           0 :             GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2, 0.0);
     923           0 :         (*ppadfPrjParams)[12] =
     924           0 :             GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 0.0);
     925           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0);
     926           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     927           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     928             :     }
     929           9 :     else if (EQUAL(pszProjection, SRS_PT_POLYCONIC))
     930             :     {
     931           0 :         CPLPrintStringFill(szProj, "PC", knProjSize);
     932           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     933           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     934           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     935           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     936             :     }
     937           9 :     else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
     938             :     {
     939           0 :         CPLPrintStringFill(szProj, "PS", knProjSize);
     940           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     941           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     942           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     943           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     944           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
     945             :     }
     946           9 :     else if (EQUAL(pszProjection, SRS_PT_ROBINSON))
     947             :     {
     948           0 :         CPLPrintStringFill(szProj, "ROB", knProjSize);
     949           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     950           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     951           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     952             :     }
     953           9 :     else if (EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC))
     954             :     {
     955           0 :         CPLPrintStringFill(szProj, "SGDO", knProjSize);
     956           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     957           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     958           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     959           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     960           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
     961             :     }
     962           9 :     else if (EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC))
     963             :     {
     964           0 :         CPLPrintStringFill(szProj, "SG", knProjSize);
     965           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     966           0 :         (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     967           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     968           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     969           0 :         (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
     970             :     }
     971           9 :     else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
     972             :     {
     973           0 :         CPLPrintStringFill(szProj, "SIN", knProjSize);
     974           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
     975           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     976           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
     977             :     }
     978           9 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
     979             :     {
     980           9 :         int bNorth = FALSE;
     981           9 :         int nZone = GetUTMZone(&bNorth);
     982             : 
     983           9 :         if (nZone != 0)
     984             :         {
     985           9 :             CPLPrintStringFill(szProj, "UTM", knProjSize);
     986           9 :             if (bNorth)
     987           9 :                 CPLPrintInt32(szProj + 5, nZone, 4);
     988             :             else
     989           0 :                 CPLPrintInt32(szProj + 5, -nZone, 4);
     990             :         }
     991             :         else
     992             :         {
     993           0 :             CPLPrintStringFill(szProj, "TM", knProjSize);
     994           0 :             (*ppadfPrjParams)[2] =
     995           0 :                 GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
     996           0 :             (*ppadfPrjParams)[3] =
     997           0 :                 GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
     998           0 :             (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
     999           0 :             (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
    1000           0 :             (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
    1001             :         }
    1002             :     }
    1003           0 :     else if (EQUAL(pszProjection, SRS_PT_VANDERGRINTEN))
    1004             :     {
    1005           0 :         CPLPrintStringFill(szProj, "VDG", knProjSize);
    1006           0 :         (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
    1007           0 :         (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
    1008           0 :         (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
    1009             :     }
    1010             :     // Projection unsupported by PCI.
    1011             :     else
    1012             :     {
    1013           0 :         CPLDebug("OSR_PCI",
    1014             :                  "Projection \"%s\" unsupported by PCI. "
    1015             :                  "PIXEL value will be used.",
    1016             :                  pszProjection);
    1017           0 :         CPLPrintStringFill(szProj, "PIXEL", knProjSize);
    1018             :     }
    1019             : 
    1020             :     /* ==================================================================== */
    1021             :     /*      Translate the earth model.                                      */
    1022             :     /* ==================================================================== */
    1023             : 
    1024             :     /* -------------------------------------------------------------------- */
    1025             :     /*      Is this a well known datum?                                     */
    1026             :     /* -------------------------------------------------------------------- */
    1027          69 :     const char *pszDatum = GetAttrValue("DATUM");
    1028          69 :     char szEarthModel[5] = {};
    1029             : 
    1030          69 :     if (pszDatum == nullptr || strlen(pszDatum) == 0)
    1031             :     {
    1032             :         // Do nothing.
    1033             :     }
    1034          67 :     else if (EQUAL(pszDatum, SRS_DN_NAD27))
    1035             :     {
    1036           6 :         CPLPrintStringFill(szEarthModel, "D-01", 4);
    1037             :     }
    1038          61 :     else if (EQUAL(pszDatum, SRS_DN_NAD83))
    1039             :     {
    1040           0 :         CPLPrintStringFill(szEarthModel, "D-02", 4);
    1041             :     }
    1042          61 :     else if (EQUAL(pszDatum, SRS_DN_WGS84))
    1043             :     {
    1044          53 :         CPLPrintStringFill(szEarthModel, "D000", 4);
    1045          53 :         if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
    1046             :         {
    1047             :             // Special case for Web Mercator
    1048           1 :             const char *method_name = "";
    1049           1 :             GetWKT2ProjectionMethod(&method_name, nullptr, nullptr);
    1050           1 :             if (EQUAL(method_name, "Popular Visualisation Pseudo Mercator"))
    1051             :             {
    1052           1 :                 CPLPrintStringFill(szEarthModel, "D894", 4);
    1053             :             }
    1054             :         }
    1055             :     }
    1056             : 
    1057             :     /* -------------------------------------------------------------------- */
    1058             :     /*      If not a very well known datum, try for an EPSG based           */
    1059             :     /*      translation.                                                    */
    1060             :     /* -------------------------------------------------------------------- */
    1061          69 :     if (szEarthModel[0] == '\0')
    1062             :     {
    1063          10 :         const char *pszAuthority = GetAuthorityName("GEOGCS");
    1064             : 
    1065          10 :         if (pszAuthority && EQUAL(pszAuthority, "EPSG"))
    1066             :         {
    1067           1 :             const int nGCS_EPSG = atoi(GetAuthorityCode("GEOGCS"));
    1068             : 
    1069          10 :             for (int i = 0; asDatums[i].nEPSGCode != 0; i++)
    1070             :             {
    1071          10 :                 if (asDatums[i].nEPSGCode == nGCS_EPSG)
    1072             :                 {
    1073           1 :                     snprintf(szEarthModel, sizeof(szEarthModel), "%s",
    1074           1 :                              asDatums[i].pszPCIDatum);
    1075           1 :                     break;
    1076             :                 }
    1077             :             }
    1078             :         }
    1079             :     }
    1080             : 
    1081             :     /* -------------------------------------------------------------------- */
    1082             :     /*      If we haven't found something yet, try translating the          */
    1083             :     /*      ellipsoid.                                                      */
    1084             :     /* -------------------------------------------------------------------- */
    1085          69 :     const double dfSemiMajor = GetSemiMajor();
    1086          69 :     const double dfInvFlattening = GetInvFlattening();
    1087          69 :     if (szEarthModel[0] == '\0')
    1088             :     {
    1089           9 :         const PCIDatums *pasDatum = asEllips;
    1090             : 
    1091         137 :         while (pasDatum->pszPCIDatum)
    1092             :         {
    1093         133 :             double dfSM = 0.0;
    1094         133 :             double dfIF = 0.0;
    1095             : 
    1096         133 :             if (OSRGetEllipsoidInfo(pasDatum->nEPSGCode, nullptr, &dfSM,
    1097         133 :                                     &dfIF) == OGRERR_NONE &&
    1098         143 :                 CPLIsEqual(dfSemiMajor, dfSM) &&
    1099          10 :                 CPLIsEqual(dfInvFlattening, dfIF))
    1100             :             {
    1101           5 :                 CPLPrintStringFill(szEarthModel, pasDatum->pszPCIDatum, 4);
    1102           5 :                 break;
    1103             :             }
    1104             : 
    1105         128 :             pasDatum++;
    1106             :         }
    1107             :     }
    1108             : 
    1109             :     // Try to find in pci_ellips.txt.
    1110          69 :     if (szEarthModel[0] == '\0')
    1111             :     {
    1112           4 :         const char *pszCSV = CSVFilename("pci_ellips.txt");
    1113             :         const double dfSemiMinor =
    1114           4 :             OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
    1115             : 
    1116           4 :         VSILFILE *fp = pszCSV ? VSIFOpenL(pszCSV, "r") : nullptr;
    1117             : 
    1118           4 :         if (fp != nullptr)
    1119             :         {
    1120           4 :             char **papszLineItems = nullptr;
    1121             : 
    1122         194 :             while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
    1123             :             {
    1124         194 :                 if (CSLCount(papszLineItems) >= 4 &&
    1125         198 :                     CPLIsEqual(dfSemiMajor, CPLAtof(papszLineItems[2])) &&
    1126           4 :                     CPLIsEqual(dfSemiMinor, CPLAtof(papszLineItems[3])))
    1127             :                 {
    1128           4 :                     snprintf(szEarthModel, sizeof(szEarthModel), "%s",
    1129             :                              papszLineItems[0]);
    1130           4 :                     break;
    1131             :                 }
    1132             : 
    1133         190 :                 CSLDestroy(papszLineItems);
    1134             :             }
    1135             : 
    1136           4 :             CSLDestroy(papszLineItems);
    1137           4 :             VSIFCloseL(fp);
    1138             :         }
    1139             :     }
    1140             : 
    1141             :     // Custom ellipsoid parameters.
    1142          69 :     if (szEarthModel[0] == '\0')
    1143             :     {
    1144           0 :         CPLPrintStringFill(szEarthModel, "E999", 4);
    1145           0 :         (*ppadfPrjParams)[0] = dfSemiMajor;
    1146           0 :         (*ppadfPrjParams)[1] =
    1147           0 :             OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
    1148             :     }
    1149             : 
    1150             :     /* -------------------------------------------------------------------- */
    1151             :     /*      If we have a non-parameteric ellipsoid, scan the                */
    1152             :     /*      pci_datum.txt for a match.                                      */
    1153             :     /* -------------------------------------------------------------------- */
    1154          69 :     if (szEarthModel[0] == 'E' && !EQUAL(szEarthModel, "E999") &&
    1155             :         pszDatum != nullptr)
    1156             :     {
    1157           7 :         const char *pszDatumCSV = CSVFilename("pci_datum.txt");
    1158           7 :         double adfTOWGS84[7] = {};
    1159           7 :         const bool bHaveTOWGS84 = GetTOWGS84(adfTOWGS84, 7) == OGRERR_NONE;
    1160             : 
    1161           7 :         VSILFILE *fp = pszDatumCSV ? VSIFOpenL(pszDatumCSV, "r") : nullptr;
    1162             : 
    1163           7 :         if (fp != nullptr)
    1164             :         {
    1165           7 :             char **papszLineItems = nullptr;
    1166             : 
    1167        3285 :             while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
    1168             :             {
    1169             :                 // Compare based on datum name.  This is mostly for
    1170             :                 // PCI round-tripping.  We won't usually get exact matches
    1171             :                 // from other sources.
    1172        3280 :                 if (CSLCount(papszLineItems) > 3 &&
    1173        3281 :                     EQUAL(papszLineItems[1], pszDatum) &&
    1174           1 :                     EQUAL(papszLineItems[2], szEarthModel))
    1175             :                 {
    1176           1 :                     snprintf(szEarthModel, sizeof(szEarthModel), "%s",
    1177             :                              papszLineItems[0]);
    1178           1 :                     break;
    1179             :                 }
    1180             : 
    1181        3279 :                 bool bTOWGS84Match = bHaveTOWGS84;
    1182             : 
    1183        3279 :                 if (CSLCount(papszLineItems) < 11)
    1184         848 :                     bTOWGS84Match = false;
    1185             : 
    1186        3780 :                 if (bTOWGS84Match &&
    1187         501 :                     (!CPLIsEqual(adfTOWGS84[0], CPLAtof(papszLineItems[3])) ||
    1188           1 :                      !CPLIsEqual(adfTOWGS84[1], CPLAtof(papszLineItems[4])) ||
    1189           1 :                      !CPLIsEqual(adfTOWGS84[2], CPLAtof(papszLineItems[5]))))
    1190         500 :                     bTOWGS84Match = false;
    1191             : 
    1192        3280 :                 if (bTOWGS84Match && CSLCount(papszLineItems) >= 15 &&
    1193           1 :                     (!CPLIsEqual(adfTOWGS84[3], CPLAtof(papszLineItems[11])) ||
    1194           1 :                      !CPLIsEqual(adfTOWGS84[4], CPLAtof(papszLineItems[12])) ||
    1195           1 :                      !CPLIsEqual(adfTOWGS84[5], CPLAtof(papszLineItems[13]))))
    1196           0 :                     bTOWGS84Match = false;
    1197             : 
    1198        3279 :                 if (bTOWGS84Match && CSLCount(papszLineItems) >= 15)
    1199             :                 {
    1200           1 :                     double dfScale = CPLAtof(papszLineItems[14]);
    1201             : 
    1202             :                     // Convert to parts per million if is a 1 based scaling.
    1203           1 :                     if (dfScale >= 0.999 && dfScale <= 1.001)
    1204           1 :                         dfScale = (dfScale - 1.0) * 1000000.0;
    1205             : 
    1206           1 :                     if (fabs(adfTOWGS84[6] - dfScale) >
    1207           1 :                         1e-11 * fabs(adfTOWGS84[6]))
    1208           0 :                         bTOWGS84Match = false;
    1209             :                 }
    1210             : 
    1211        3279 :                 if (bTOWGS84Match && CSLCount(papszLineItems) < 15 &&
    1212           0 :                     (!CPLIsEqual(adfTOWGS84[3], 0.0) ||
    1213           0 :                      !CPLIsEqual(adfTOWGS84[4], 0.0) ||
    1214           0 :                      !CPLIsEqual(adfTOWGS84[5], 0.0) ||
    1215           0 :                      !CPLIsEqual(adfTOWGS84[6], 0.0)))
    1216           0 :                     bTOWGS84Match = false;
    1217             : 
    1218        3279 :                 if (bTOWGS84Match)
    1219             :                 {
    1220           1 :                     snprintf(szEarthModel, sizeof(szEarthModel), "%s",
    1221             :                              papszLineItems[0]);
    1222           1 :                     break;
    1223             :                 }
    1224             : 
    1225        3278 :                 CSLDestroy(papszLineItems);
    1226             :             }
    1227             : 
    1228           7 :             CSLDestroy(papszLineItems);
    1229           7 :             VSIFCloseL(fp);
    1230             :         }
    1231             :     }
    1232             : 
    1233          69 :     CPLPrintStringFill(szProj + 12, szEarthModel, 4);
    1234             : 
    1235          69 :     CPLDebug("OSR_PCI", "Translated as '%s'", szProj);
    1236             : 
    1237             :     /* -------------------------------------------------------------------- */
    1238             :     /*      Translate the linear units.                                     */
    1239             :     /* -------------------------------------------------------------------- */
    1240          69 :     const char *pszUnits =
    1241          69 :         STARTS_WITH_CI(szProj, "LONG/LAT") ? "DEGREE" : "METRE";
    1242             : 
    1243             :     /* -------------------------------------------------------------------- */
    1244             :     /*      Report results.                                                 */
    1245             :     /* -------------------------------------------------------------------- */
    1246          69 :     szProj[knProjSize] = '\0';
    1247          69 :     *ppszProj = CPLStrdup(szProj);
    1248             : 
    1249          69 :     *ppszUnits = CPLStrdup(pszUnits);
    1250             : 
    1251          69 :     return OGRERR_NONE;
    1252             : }

Generated by: LCOV version 1.14