LCOV - code coverage report
Current view: top level - frmts/gtiff - gt_citation.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 281 399 70.4 %
Date: 2024-11-21 22:18:42 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  Implements special parsing of Imagine citation strings, and
       5             :  *           to encode PE String info in citation fields as needed.
       6             :  * Author:   Xiuguang Zhou (ESRI)
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2008, Xiuguang Zhou (ESRI)
      10             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : 
      17             : #include "gt_citation.h"
      18             : 
      19             : #include <cstddef>
      20             : #include <cstdlib>
      21             : #include <cstring>
      22             : #include <algorithm>
      23             : #include <string>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_string.h"
      27             : #include "geokeys.h"
      28             : #include "geotiff.h"
      29             : #include "geovalues.h"
      30             : #include "gt_wkt_srs_priv.h"
      31             : #include "ogr_core.h"
      32             : 
      33             : static const char *const apszUnitMap[] = {"meters",
      34             :                                           "1.0",
      35             :                                           "meter",
      36             :                                           "1.0",
      37             :                                           "m",
      38             :                                           "1.0",
      39             :                                           "centimeters",
      40             :                                           "0.01",
      41             :                                           "centimeter",
      42             :                                           "0.01",
      43             :                                           "cm",
      44             :                                           "0.01",
      45             :                                           "millimeters",
      46             :                                           "0.001",
      47             :                                           "millimeter",
      48             :                                           "0.001",
      49             :                                           "mm",
      50             :                                           "0.001",
      51             :                                           "kilometers",
      52             :                                           "1000.0",
      53             :                                           "kilometer",
      54             :                                           "1000.0",
      55             :                                           "km",
      56             :                                           "1000.0",
      57             :                                           "us_survey_feet",
      58             :                                           "0.3048006096012192",
      59             :                                           "us_survey_foot",
      60             :                                           "0.3048006096012192",
      61             :                                           "feet",
      62             :                                           "0.3048006096012192",
      63             :                                           "foot",
      64             :                                           "0.3048006096012192",
      65             :                                           "ft",
      66             :                                           "0.3048006096012192",
      67             :                                           "international_feet",
      68             :                                           "0.3048",
      69             :                                           "international_foot",
      70             :                                           "0.3048",
      71             :                                           "inches",
      72             :                                           "0.0254000508001",
      73             :                                           "inch",
      74             :                                           "0.0254000508001",
      75             :                                           "in",
      76             :                                           "0.0254000508001",
      77             :                                           "yards",
      78             :                                           "0.9144",
      79             :                                           "yard",
      80             :                                           "0.9144",
      81             :                                           "yd",
      82             :                                           "0.9144",
      83             :                                           "miles",
      84             :                                           "1304.544",
      85             :                                           "mile",
      86             :                                           "1304.544",
      87             :                                           "mi",
      88             :                                           "1304.544",
      89             :                                           "modified_american_feet",
      90             :                                           "0.3048122530",
      91             :                                           "modified_american_foot",
      92             :                                           "0.3048122530",
      93             :                                           "clarke_feet",
      94             :                                           "0.3047972651",
      95             :                                           "clarke_foot",
      96             :                                           "0.3047972651",
      97             :                                           "indian_feet",
      98             :                                           "0.3047995142",
      99             :                                           "indian_foot",
     100             :                                           "0.3047995142",
     101             :                                           "Yard_Indian",
     102             :                                           "0.9143985307444408",
     103             :                                           "Foot_Clarke",
     104             :                                           "0.30479726540",
     105             :                                           "Foot_Gold_Coast",
     106             :                                           "0.3047997101815088",
     107             :                                           "Link_Clarke",
     108             :                                           "0.2011661951640",
     109             :                                           "Yard_Sears",
     110             :                                           "0.9143984146160287",
     111             :                                           "50_Kilometers",
     112             :                                           "50000.0",
     113             :                                           "150_Kilometers",
     114             :                                           "150000.0",
     115             :                                           nullptr,
     116             :                                           nullptr};
     117             : 
     118             : /************************************************************************/
     119             : /*                     ImagineCitationTranslation()                     */
     120             : /*                                                                      */
     121             : /*      Translate ERDAS Imagine GeoTif citation                         */
     122             : /************************************************************************/
     123        3333 : char *ImagineCitationTranslation(char *psCitation, geokey_t keyID)
     124             : {
     125        3333 :     if (!psCitation)
     126           0 :         return nullptr;
     127        3333 :     char *ret = nullptr;
     128        3333 :     if (STARTS_WITH_CI(psCitation, "IMAGINE GeoTIFF Support"))
     129             :     {
     130             :         static const char *const keyNames[] = {
     131             :             "NAD = ", "Datum = ", "Ellipsoid = ", "Units = ", nullptr};
     132             : 
     133             :         // This is a handle IMAGING style citation.
     134          12 :         CPLString osName;
     135           6 :         char *p1 = nullptr;
     136             : 
     137           6 :         char *p = strchr(psCitation, '$');
     138           6 :         if (p && strchr(p, '\n'))
     139           5 :             p = strchr(p, '\n') + 1;
     140           6 :         if (p)
     141             :         {
     142           5 :             p1 = p + strlen(p);
     143           5 :             char *p2 = strchr(p, '\n');
     144           5 :             if (p2)
     145           5 :                 p1 = std::min(p1, p2);
     146           5 :             p2 = strchr(p, '\0');
     147           5 :             if (p2)
     148           5 :                 p1 = std::min(p1, p2);
     149             : 
     150          25 :             for (int i = 0; keyNames[i] != nullptr; i++)
     151             :             {
     152          20 :                 p2 = strstr(p, keyNames[i]);
     153          20 :                 if (p2)
     154           5 :                     p1 = std::min(p1, p2);
     155             :             }
     156             :         }
     157             : 
     158             :         // PCS name, GCS name and PRJ name.
     159           6 :         if (p && p1)
     160             :         {
     161           5 :             switch (keyID)
     162             :             {
     163           0 :                 case PCSCitationGeoKey:
     164           0 :                     if (strstr(psCitation, "Projection = "))
     165           0 :                         osName = "PRJ Name = ";
     166             :                     else
     167           0 :                         osName = "PCS Name = ";
     168           0 :                     break;
     169           5 :                 case GTCitationGeoKey:
     170           5 :                     osName = "PCS Name = ";
     171           5 :                     break;
     172           0 :                 case GeogCitationGeoKey:
     173           0 :                     if (!strstr(p, "Unable to"))
     174           0 :                         osName = "GCS Name = ";
     175           0 :                     break;
     176           0 :                 default:
     177           0 :                     break;
     178             :             }
     179           5 :             if (!osName.empty())
     180             :             {
     181             :                 // TODO(schwehr): What exactly is this code trying to do?
     182             :                 // Added in r15993 and modified in r21844 by warmerdam.
     183           5 :                 char *p2 = nullptr;
     184           5 :                 if ((p2 = strstr(psCitation, "Projection Name = ")) != nullptr)
     185           5 :                     p = p2 + strlen("Projection Name = ");
     186           5 :                 if ((p2 = strstr(psCitation, "Projection = ")) != nullptr)
     187           0 :                     p = p2 + strlen("Projection = ");
     188           5 :                 if (p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ')
     189           5 :                     p1--;
     190           5 :                 p2 = p1 - 1;
     191           5 :                 while (p2 != nullptr &&
     192           5 :                        (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n'))
     193             :                 {
     194           0 :                     p2--;
     195             :                 }
     196           5 :                 if (p2 != p1 - 1)
     197             :                 {
     198           0 :                     p1 = p2;
     199             :                 }
     200           5 :                 if (p1 >= p)
     201             :                 {
     202           5 :                     osName.append(p, p1 - p + 1);
     203           5 :                     osName += '|';
     204             :                 }
     205             :             }
     206             :         }
     207             : 
     208             :         // All other parameters.
     209          30 :         for (int i = 0; keyNames[i] != nullptr; i++)
     210             :         {
     211          24 :             p = strstr(psCitation, keyNames[i]);
     212          24 :             if (p)
     213             :             {
     214           6 :                 p += strlen(keyNames[i]);
     215           6 :                 p1 = p + strlen(p);
     216           6 :                 char *p2 = strchr(p, '\n');
     217           6 :                 if (p2)
     218           6 :                     p1 = std::min(p1, p2);
     219           6 :                 p2 = strchr(p, '\0');
     220           6 :                 if (p2)
     221           6 :                     p1 = std::min(p1, p2);
     222          30 :                 for (int j = 0; keyNames[j] != nullptr; j++)
     223             :                 {
     224          24 :                     p2 = strstr(p, keyNames[j]);
     225          24 :                     if (p2)
     226           6 :                         p1 = std::min(p1, p2);
     227             :                 }
     228             :             }
     229          24 :             if (p && p1 && p1 > p)
     230             :             {
     231           6 :                 if (EQUAL(keyNames[i], "Units = "))
     232           6 :                     osName += "LUnits = ";
     233             :                 else
     234           0 :                     osName += keyNames[i];
     235           6 :                 if (p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ')
     236           6 :                     p1--;
     237           6 :                 char *p2 = p1 - 1;
     238           6 :                 while (p2 != nullptr &&
     239           6 :                        (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n'))
     240             :                 {
     241           0 :                     p2--;
     242             :                 }
     243           6 :                 if (p2 != p1 - 1)
     244             :                 {
     245           0 :                     p1 = p2;
     246             :                 }
     247           6 :                 if (p1 >= p)
     248             :                 {
     249           6 :                     osName.append(p, p1 - p + 1);
     250           6 :                     osName += '|';
     251             :                 }
     252             :             }
     253             :         }
     254           6 :         if (!osName.empty())
     255           6 :             ret = CPLStrdup(osName);
     256             :     }
     257        3333 :     return ret;
     258             : }
     259             : 
     260             : /************************************************************************/
     261             : /*                        CitationStringParse()                         */
     262             : /*                                                                      */
     263             : /*      Parse a Citation string                                         */
     264             : /************************************************************************/
     265             : 
     266        3333 : char **CitationStringParse(char *psCitation, geokey_t keyID)
     267             : {
     268        3333 :     if (!psCitation)
     269           0 :         return nullptr;
     270             : 
     271             :     char **ret =
     272        3333 :         static_cast<char **>(CPLCalloc(sizeof(char *), nCitationNameTypes));
     273        3333 :     char *pDelimit = nullptr;
     274        3333 :     char *pStr = psCitation;
     275        3333 :     char name[512] = {'\0'};
     276        3333 :     bool nameSet = false;
     277        3333 :     int nameLen = static_cast<int>(strlen(psCitation));
     278        3333 :     bool nameFound = false;
     279        6893 :     while ((pStr - psCitation + 1) < nameLen)
     280             :     {
     281        3560 :         if ((pDelimit = strstr(pStr, "|")) != nullptr)
     282             :         {
     283         315 :             strncpy(name, pStr, pDelimit - pStr);
     284         315 :             name[pDelimit - pStr] = '\0';
     285         315 :             pStr = pDelimit + 1;
     286         315 :             nameSet = true;
     287             :         }
     288             :         else
     289             :         {
     290        3245 :             strcpy(name, pStr);
     291        3245 :             pStr += strlen(pStr);
     292        3245 :             nameSet = true;
     293             :         }
     294        3560 :         if (strstr(name, "PCS Name = ") && ret[CitPcsName] == nullptr)
     295             :         {
     296          12 :             ret[CitPcsName] = CPLStrdup(name + strlen("PCS Name = "));
     297          12 :             nameFound = true;
     298             :         }
     299        3560 :         if (strstr(name, "PRJ Name = ") && ret[CitProjectionName] == nullptr)
     300             :         {
     301           0 :             ret[CitProjectionName] = CPLStrdup(name + strlen("PRJ Name = "));
     302           0 :             nameFound = true;
     303             :         }
     304        3560 :         if (strstr(name, "LUnits = ") && ret[CitLUnitsName] == nullptr)
     305             :         {
     306          10 :             ret[CitLUnitsName] = CPLStrdup(name + strlen("LUnits = "));
     307          10 :             nameFound = true;
     308             :         }
     309        3560 :         if (strstr(name, "GCS Name = ") && ret[CitGcsName] == nullptr)
     310             :         {
     311          82 :             ret[CitGcsName] = CPLStrdup(name + strlen("GCS Name = "));
     312          82 :             nameFound = true;
     313             :         }
     314        3560 :         if (strstr(name, "Datum = ") && ret[CitDatumName] == nullptr)
     315             :         {
     316          63 :             ret[CitDatumName] = CPLStrdup(name + strlen("Datum = "));
     317          63 :             nameFound = true;
     318             :         }
     319        3560 :         if (strstr(name, "Ellipsoid = ") && ret[CitEllipsoidName] == nullptr)
     320             :         {
     321          62 :             ret[CitEllipsoidName] = CPLStrdup(name + strlen("Ellipsoid = "));
     322          62 :             nameFound = true;
     323             :         }
     324        3560 :         if (strstr(name, "Primem = ") && ret[CitPrimemName] == nullptr)
     325             :         {
     326          82 :             ret[CitPrimemName] = CPLStrdup(name + strlen("Primem = "));
     327          82 :             nameFound = true;
     328             :         }
     329        3560 :         if (strstr(name, "AUnits = ") && ret[CitAUnitsName] == nullptr)
     330             :         {
     331          15 :             ret[CitAUnitsName] = CPLStrdup(name + strlen("AUnits = "));
     332          15 :             nameFound = true;
     333             :         }
     334             :     }
     335        3333 :     if (!nameFound && keyID == GeogCitationGeoKey && nameSet)
     336             :     {
     337           1 :         ret[CitGcsName] = CPLStrdup(name);
     338           1 :         nameFound = true;
     339             :     }
     340        3333 :     if (!nameFound)
     341             :     {
     342        3233 :         CPLFree(ret);
     343        3233 :         ret = nullptr;
     344             :     }
     345        3333 :     return ret;
     346             : }
     347             : 
     348             : /************************************************************************/
     349             : /*                       SetLinearUnitCitation()                        */
     350             : /*                                                                      */
     351             : /*      Set linear unit Citation string                                 */
     352             : /************************************************************************/
     353           2 : void SetLinearUnitCitation(std::map<geokey_t, std::string> &oMapAsciiKeys,
     354             :                            const char *pszLinearUOMName)
     355             : {
     356           2 :     CPLString osCitation;
     357           2 :     auto oIter = oMapAsciiKeys.find(PCSCitationGeoKey);
     358           2 :     if (oIter != oMapAsciiKeys.end())
     359             :     {
     360           0 :         osCitation = oIter->second;
     361             :     }
     362           2 :     if (!osCitation.empty())
     363             :     {
     364           0 :         size_t n = osCitation.size();
     365           0 :         if (osCitation[n - 1] != '|')
     366           0 :             osCitation += "|";
     367           0 :         osCitation += "LUnits = ";
     368           0 :         osCitation += pszLinearUOMName;
     369           0 :         osCitation += "|";
     370             :     }
     371             :     else
     372             :     {
     373           2 :         osCitation = "LUnits = ";
     374           2 :         osCitation += pszLinearUOMName;
     375             :     }
     376           2 :     oMapAsciiKeys[PCSCitationGeoKey] = std::move(osCitation);
     377           2 : }
     378             : 
     379             : /************************************************************************/
     380             : /*                         SetGeogCSCitation()                          */
     381             : /*                                                                      */
     382             : /*      Set geogcs Citation string                                      */
     383             : /************************************************************************/
     384         187 : void SetGeogCSCitation(GTIF *psGTIF,
     385             :                        std::map<geokey_t, std::string> &oMapAsciiKeys,
     386             :                        const OGRSpatialReference *poSRS,
     387             :                        const char *angUnitName, int nDatum, short nSpheroid)
     388             : {
     389         187 :     bool bRewriteGeogCitation = false;
     390         187 :     CPLString osOriginalGeogCitation;
     391         187 :     auto oIter = oMapAsciiKeys.find(GeogCitationGeoKey);
     392         187 :     if (oIter != oMapAsciiKeys.end())
     393             :     {
     394          71 :         osOriginalGeogCitation = oIter->second;
     395             :     }
     396         187 :     if (osOriginalGeogCitation.empty())
     397         116 :         return;
     398             : 
     399         142 :     CPLString osCitation;
     400          71 :     if (!STARTS_WITH_CI(osOriginalGeogCitation, "GCS Name = "))
     401             :     {
     402          71 :         osCitation = "GCS Name = ";
     403          71 :         osCitation += std::move(osOriginalGeogCitation);
     404             :     }
     405             :     else
     406             :     {
     407           0 :         osCitation = std::move(osOriginalGeogCitation);
     408             :     }
     409             : 
     410          71 :     if (nDatum == KvUserDefined)
     411             :     {
     412          45 :         const char *datumName = poSRS->GetAttrValue("DATUM");
     413          45 :         if (datumName && strlen(datumName) > 0)
     414             :         {
     415          45 :             osCitation += "|Datum = ";
     416          45 :             osCitation += datumName;
     417          45 :             bRewriteGeogCitation = true;
     418             :         }
     419             :     }
     420          71 :     if (nSpheroid == KvUserDefined)
     421             :     {
     422          46 :         const char *spheroidName = poSRS->GetAttrValue("SPHEROID");
     423          46 :         if (spheroidName && strlen(spheroidName) > 0)
     424             :         {
     425          46 :             osCitation += "|Ellipsoid = ";
     426          46 :             osCitation += spheroidName;
     427          46 :             bRewriteGeogCitation = true;
     428             :         }
     429             :     }
     430             : 
     431          71 :     const char *primemName = poSRS->GetAttrValue("PRIMEM");
     432          71 :     if (primemName && strlen(primemName) > 0)
     433             :     {
     434          71 :         osCitation += "|Primem = ";
     435          71 :         osCitation += primemName;
     436          71 :         bRewriteGeogCitation = true;
     437             : 
     438          71 :         double primemValue = poSRS->GetPrimeMeridian(nullptr);
     439          71 :         if (angUnitName && !EQUAL(angUnitName, "Degree"))
     440             :         {
     441           8 :             const double aUnit = poSRS->GetAngularUnits(nullptr);
     442           8 :             primemValue *= aUnit;
     443             :         }
     444          71 :         GTIFKeySet(psGTIF, GeogPrimeMeridianLongGeoKey, TYPE_DOUBLE, 1,
     445             :                    primemValue);
     446             :     }
     447          71 :     if (angUnitName && strlen(angUnitName) > 0 && !EQUAL(angUnitName, "Degree"))
     448             :     {
     449           8 :         osCitation += "|AUnits = ";
     450           8 :         osCitation += angUnitName;
     451           8 :         bRewriteGeogCitation = true;
     452             :     }
     453             : 
     454          71 :     if (osCitation.back() != '|')
     455          71 :         osCitation += "|";
     456             : 
     457          71 :     if (bRewriteGeogCitation)
     458             :     {
     459          71 :         oMapAsciiKeys[GeogCitationGeoKey] = std::move(osCitation);
     460             :     }
     461             : }
     462             : 
     463             : /************************************************************************/
     464             : /*                          SetCitationToSRS()                          */
     465             : /*                                                                      */
     466             : /*      Parse and set Citation string to SRS                            */
     467             : /************************************************************************/
     468        3250 : OGRBoolean SetCitationToSRS(GTIF *hGTIF, char *szCTString, int nCTStringLen,
     469             :                             geokey_t geoKey, OGRSpatialReference *poSRS,
     470             :                             OGRBoolean *linearUnitIsSet)
     471             : {
     472        3250 :     OGRBoolean ret = FALSE;
     473        3250 :     const char *lUnitName = nullptr;
     474             : 
     475        3250 :     poSRS->GetLinearUnits(&lUnitName);
     476        3250 :     if (!lUnitName || strlen(lUnitName) == 0 || EQUAL(lUnitName, "unknown"))
     477        3248 :         *linearUnitIsSet = FALSE;
     478             :     else
     479           2 :         *linearUnitIsSet = TRUE;
     480             : 
     481        3250 :     char *imgCTName = ImagineCitationTranslation(szCTString, geoKey);
     482        3250 :     if (imgCTName)
     483             :     {
     484           6 :         strncpy(szCTString, imgCTName, nCTStringLen);
     485           6 :         szCTString[nCTStringLen - 1] = '\0';
     486           6 :         CPLFree(imgCTName);
     487             :     }
     488        3250 :     char **ctNames = CitationStringParse(szCTString, geoKey);
     489        3250 :     if (ctNames)
     490             :     {
     491          17 :         if (poSRS->GetRoot() == nullptr)
     492           3 :             poSRS->SetNode("PROJCS", "unnamed");
     493          17 :         if (ctNames[CitPcsName])
     494             :         {
     495          12 :             poSRS->SetNode("PROJCS", ctNames[CitPcsName]);
     496          12 :             ret = TRUE;
     497             :         }
     498          17 :         if (ctNames[CitProjectionName])
     499           0 :             poSRS->SetProjection(ctNames[CitProjectionName]);
     500             : 
     501          17 :         if (ctNames[CitLUnitsName])
     502             :         {
     503          10 :             double unitSize = 0.0;
     504          10 :             int size = static_cast<int>(strlen(ctNames[CitLUnitsName]));
     505          10 :             if (strchr(ctNames[CitLUnitsName], '\0'))
     506          10 :                 size -= 1;
     507         122 :             for (int i = 0; apszUnitMap[i] != nullptr; i += 2)
     508             :             {
     509         120 :                 if (EQUALN(apszUnitMap[i], ctNames[CitLUnitsName], size))
     510             :                 {
     511           8 :                     unitSize = CPLAtof(apszUnitMap[i + 1]);
     512           8 :                     break;
     513             :                 }
     514             :             }
     515          10 :             if (unitSize == 0.0)
     516             :             {
     517           2 :                 CPL_IGNORE_RET_VAL(GDALGTIFKeyGetDOUBLE(
     518             :                     hGTIF, ProjLinearUnitSizeGeoKey, &unitSize, 0, 1));
     519             :             }
     520          10 :             poSRS->SetLinearUnits(ctNames[CitLUnitsName], unitSize);
     521          10 :             *linearUnitIsSet = TRUE;
     522             :         }
     523         170 :         for (int i = 0; i < nCitationNameTypes; i++)
     524         153 :             CPLFree(ctNames[i]);
     525          17 :         CPLFree(ctNames);
     526             :     }
     527             : 
     528             :     // If no "PCS Name = " (from Erdas) in GTCitationGeoKey.
     529        3250 :     if (geoKey == GTCitationGeoKey)
     530             :     {
     531        3242 :         if (strlen(szCTString) > 0 && !strstr(szCTString, "PCS Name = "))
     532             :         {
     533        3230 :             const char *pszProjCS = poSRS->GetAttrValue("PROJCS");
     534        3230 :             if ((!(pszProjCS && strlen(pszProjCS) > 0) &&
     535        3230 :                  !strstr(szCTString, "Projected Coordinates")) ||
     536        3149 :                 (pszProjCS && strstr(pszProjCS, "unnamed")))
     537         166 :                 poSRS->SetNode("PROJCS", szCTString);
     538        3230 :             ret = TRUE;
     539             :         }
     540             :     }
     541             : 
     542        3250 :     return ret;
     543             : }
     544             : 
     545             : /************************************************************************/
     546             : /*                       GetGeogCSFromCitation()                        */
     547             : /*                                                                      */
     548             : /*      Parse and get geogcs names from a Citation string               */
     549             : /************************************************************************/
     550          83 : void GetGeogCSFromCitation(char *szGCSName, int nGCSName, geokey_t geoKey,
     551             :                            char **ppszGeogName, char **ppszDatumName,
     552             :                            char **ppszPMName, char **ppszSpheroidName,
     553             :                            char **ppszAngularUnits)
     554             : {
     555          83 :     *ppszGeogName = nullptr;
     556          83 :     *ppszDatumName = nullptr;
     557          83 :     *ppszPMName = nullptr;
     558          83 :     *ppszSpheroidName = nullptr;
     559          83 :     *ppszAngularUnits = nullptr;
     560             : 
     561          83 :     char *imgCTName = ImagineCitationTranslation(szGCSName, geoKey);
     562          83 :     if (imgCTName)
     563             :     {
     564           0 :         strncpy(szGCSName, imgCTName, nGCSName);
     565           0 :         szGCSName[nGCSName - 1] = '\0';
     566           0 :         CPLFree(imgCTName);
     567             :     }
     568          83 :     char **ctNames = CitationStringParse(szGCSName, geoKey);
     569          83 :     if (ctNames)
     570             :     {
     571          83 :         if (ctNames[CitGcsName])
     572          83 :             *ppszGeogName = CPLStrdup(ctNames[CitGcsName]);
     573             : 
     574          83 :         if (ctNames[CitDatumName])
     575          63 :             *ppszDatumName = CPLStrdup(ctNames[CitDatumName]);
     576             : 
     577          83 :         if (ctNames[CitEllipsoidName])
     578          62 :             *ppszSpheroidName = CPLStrdup(ctNames[CitEllipsoidName]);
     579             : 
     580          83 :         if (ctNames[CitPrimemName])
     581          82 :             *ppszPMName = CPLStrdup(ctNames[CitPrimemName]);
     582             : 
     583          83 :         if (ctNames[CitAUnitsName])
     584          15 :             *ppszAngularUnits = CPLStrdup(ctNames[CitAUnitsName]);
     585             : 
     586         830 :         for (int i = 0; i < nCitationNameTypes; i++)
     587         747 :             CPLFree(ctNames[i]);
     588          83 :         CPLFree(ctNames);
     589             :     }
     590          83 :     return;
     591             : }
     592             : 
     593             : /************************************************************************/
     594             : /*               CheckCitationKeyForStatePlaneUTM()                     */
     595             : /*                                                                      */
     596             : /*      Handle state plane and UTM in citation key                      */
     597             : /************************************************************************/
     598        3173 : OGRBoolean CheckCitationKeyForStatePlaneUTM(GTIF *hGTIF, GTIFDefn *psDefn,
     599             :                                             OGRSpatialReference *poSRS,
     600             :                                             OGRBoolean *pLinearUnitIsSet)
     601             : {
     602        3173 :     if (!hGTIF || !psDefn || !poSRS)
     603           0 :         return FALSE;
     604             : 
     605             : /* -------------------------------------------------------------------- */
     606             : /*      For ESRI builds we are interested in maximizing PE              */
     607             : /*      compatibility, but generally we prefer to use EPSG              */
     608             : /*      definitions of the coordinate system if PCS is defined.         */
     609             : /* -------------------------------------------------------------------- */
     610             : #if !defined(ESRI_BUILD)
     611        3173 :     if (psDefn->PCS != KvUserDefined)
     612        3086 :         return FALSE;
     613             : #endif
     614             : 
     615          87 :     char szCTString[512] = {'\0'};
     616             : 
     617             :     // Check units.
     618          87 :     char units[32] = {'\0'};
     619             : 
     620          87 :     bool hasUnits = false;
     621          87 :     if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
     622          87 :                             sizeof(szCTString)))
     623             :     {
     624          84 :         const CPLString osLCCT = CPLString(szCTString).tolower();
     625             : 
     626          84 :         if (strstr(osLCCT, "us") && strstr(osLCCT, "survey") &&
     627           0 :             (strstr(osLCCT, "feet") || strstr(osLCCT, "foot")))
     628           0 :             strcpy(units, "us_survey_feet");
     629          84 :         else if (strstr(osLCCT, "linear_feet") ||
     630         168 :                  strstr(osLCCT, "linear_foot") ||
     631          84 :                  strstr(osLCCT, "international"))
     632           0 :             strcpy(units, "international_feet");
     633          84 :         else if (strstr(osLCCT, "meter"))
     634           0 :             strcpy(units, "meters");
     635             : 
     636          84 :         if (strlen(units) > 0)
     637           0 :             hasUnits = true;
     638             : 
     639          84 :         if (strstr(szCTString, "Projection Name = ") &&
     640           0 :             strstr(szCTString, "_StatePlane_"))
     641             :         {
     642           0 :             const char *pStr = strstr(szCTString, "Projection Name = ") +
     643             :                                strlen("Projection Name = ");
     644           0 :             CPLString osCSName(pStr);
     645           0 :             const char *pReturn = strchr(pStr, '\n');
     646           0 :             if (pReturn)
     647           0 :                 osCSName.resize(pReturn - pStr);
     648           0 :             if (poSRS->ImportFromESRIStatePlaneWKT(0, nullptr, nullptr, 32767,
     649           0 :                                                    osCSName) == OGRERR_NONE)
     650             :             {
     651             :                 // For some erdas citation keys, the state plane CS name is
     652             :                 // incomplete, the unit check is necessary.
     653           0 :                 bool done = false;
     654           0 :                 if (hasUnits)
     655             :                 {
     656           0 :                     OGR_SRSNode *poUnit = poSRS->GetAttrNode("PROJCS|UNIT");
     657             : 
     658           0 :                     if (poUnit != nullptr && poUnit->GetChildCount() >= 2)
     659             :                     {
     660             :                         const CPLString unitName =
     661           0 :                             CPLString(poUnit->GetChild(0)->GetValue())
     662           0 :                                 .tolower();
     663             : 
     664           0 :                         if (strstr(units, "us_survey_feet"))
     665             :                         {
     666           0 :                             if (strstr(unitName, "us_survey_feet") ||
     667           0 :                                 strstr(unitName, "foot_us"))
     668           0 :                                 done = true;
     669             :                         }
     670           0 :                         else if (strstr(units, "international_feet"))
     671             :                         {
     672           0 :                             if (strstr(unitName, "feet") ||
     673           0 :                                 strstr(unitName, "foot"))
     674           0 :                                 done = true;
     675             :                         }
     676           0 :                         else if (strstr(units, "meters"))
     677             :                         {
     678           0 :                             if (strstr(unitName, "meter"))
     679           0 :                                 done = true;
     680             :                         }
     681             :                     }
     682             :                 }
     683           0 :                 if (done)
     684           0 :                     return true;
     685             :             }
     686             :         }
     687             :     }
     688          87 :     if (!hasUnits)
     689             :     {
     690          87 :         char *pszUnitsName = nullptr;
     691          87 :         GTIFGetUOMLengthInfo(psDefn->UOMLength, &pszUnitsName, nullptr);
     692          87 :         if (pszUnitsName)
     693             :         {
     694         164 :             const CPLString osLCCT = CPLString(pszUnitsName).tolower();
     695          82 :             GTIFFreeMemory(pszUnitsName);
     696             : 
     697          94 :             if (strstr(osLCCT, "us") && strstr(osLCCT, "survey") &&
     698          12 :                 (strstr(osLCCT, "feet") || strstr(osLCCT, "foot")))
     699          12 :                 strcpy(units, "us_survey_feet");
     700          70 :             else if (strstr(osLCCT, "feet") || strstr(osLCCT, "foot"))
     701           0 :                 strcpy(units, "international_feet");
     702          70 :             else if (strstr(osLCCT, "meter"))
     703           0 :                 strcpy(units, "meters");
     704             :             // hasUnits = true;
     705             :         }
     706             :     }
     707             : 
     708          87 :     if (strlen(units) == 0)
     709          75 :         strcpy(units, "meters");
     710             : 
     711             :     // Check PCSCitationGeoKey if it exists.
     712          87 :     szCTString[0] = '\0';
     713          87 :     if (GDALGTIFKeyGetASCII(hGTIF, PCSCitationGeoKey, szCTString,
     714          87 :                             sizeof(szCTString)))
     715             :     {
     716             :         // For tif created by LEICA(ERDAS), ESRI state plane pe string was
     717             :         // used and the state plane zone is given in PCSCitation. Therefore
     718             :         // try ESRI pe string first.
     719           4 :         SetCitationToSRS(hGTIF, szCTString,
     720           4 :                          static_cast<int>(strlen(szCTString)),
     721             :                          PCSCitationGeoKey, poSRS, pLinearUnitIsSet);
     722           4 :         const char *pcsName = poSRS->GetAttrValue("PROJCS");
     723           4 :         const char *pStr = nullptr;
     724           8 :         if ((pcsName &&
     725           8 :              (pStr = strstr(pcsName, "State Plane Zone ")) != nullptr) ||
     726           4 :             (pStr = strstr(szCTString, "State Plane Zone ")) != nullptr)
     727             :         {
     728           0 :             pStr += strlen("State Plane Zone ");
     729           0 :             int statePlaneZone = atoi(pStr);
     730             :             // Safe version of statePlaneZone = abs(statePlaneZone), but
     731             :             // I (ERO)'ve no idea why negative zone number would make sense...
     732           0 :             if (statePlaneZone < 0 && statePlaneZone > INT_MIN)
     733           0 :                 statePlaneZone = -statePlaneZone;
     734             :             char nad[32];
     735           0 :             strcpy(nad, "HARN");
     736           0 :             if (strstr(szCTString, "NAD83") || strstr(szCTString, "NAD = 83"))
     737           0 :                 strcpy(nad, "NAD83");
     738           0 :             else if (strstr(szCTString, "NAD27") ||
     739           0 :                      strstr(szCTString, "NAD = 27"))
     740           0 :                 strcpy(nad, "NAD27");
     741           0 :             if (poSRS->ImportFromESRIStatePlaneWKT(statePlaneZone, nad, units,
     742           0 :                                                    psDefn->PCS) == OGRERR_NONE)
     743           0 :                 return TRUE;
     744             :         }
     745           4 :         else if (pcsName &&
     746           4 :                  (/* pStr = */ strstr(pcsName, "UTM Zone ")) != nullptr)
     747             :         {
     748           0 :             CheckUTM(psDefn, szCTString);
     749             :         }
     750             :     }
     751             : 
     752             :     // Check state plane again to see if a pe string is available.
     753          87 :     if (psDefn->PCS != KvUserDefined)
     754             :     {
     755           0 :         if (poSRS->ImportFromESRIStatePlaneWKT(0, nullptr, units,
     756           0 :                                                psDefn->PCS) == OGRERR_NONE)
     757           0 :             return TRUE;
     758             :     }
     759             : 
     760          87 :     return FALSE;
     761             : }
     762             : 
     763             : /************************************************************************/
     764             : /*                               CheckUTM()                             */
     765             : /*                                                                      */
     766             : /*        Check utm proj code by its name.                              */
     767             : /************************************************************************/
     768           0 : void CheckUTM(GTIFDefn *psDefn, const char *pszCtString)
     769             : {
     770           0 :     if (!psDefn || !pszCtString)
     771           0 :         return;
     772             : 
     773           0 :     const char *p = strstr(pszCtString, "Datum = ");
     774           0 :     char datumName[128] = {'\0'};
     775           0 :     if (p)
     776             :     {
     777           0 :         p += strlen("Datum = ");
     778           0 :         const char *p1 = strchr(p, '|');
     779           0 :         if (p1 && p1 - p < static_cast<int>(sizeof(datumName)))
     780             :         {
     781           0 :             strncpy(datumName, p, p1 - p);
     782           0 :             datumName[p1 - p] = '\0';
     783             :         }
     784             :         else
     785             :         {
     786           0 :             CPLStrlcpy(datumName, p, sizeof(datumName));
     787             :         }
     788             :     }
     789             :     else
     790             :     {
     791           0 :         datumName[0] = '\0';
     792             :     }
     793             : 
     794           0 :     p = strstr(pszCtString, "UTM Zone ");
     795           0 :     if (p)
     796             :     {
     797           0 :         p += strlen("UTM Zone ");
     798           0 :         const char *p1 = strchr(p, '|');
     799           0 :         char utmName[64] = {'\0'};
     800           0 :         if (p1 && p1 - p < static_cast<int>(sizeof(utmName)))
     801             :         {
     802           0 :             strncpy(utmName, p, p1 - p);
     803           0 :             utmName[p1 - p] = '\0';
     804             :         }
     805             :         else
     806             :         {
     807           0 :             CPLStrlcpy(utmName, p, sizeof(utmName));
     808             :         }
     809             : 
     810             :         // Static to get this off the stack and constructed only one time.
     811             :         static const char *const apszUtmProjCode[] = {
     812             :             "PSAD56", "17N", "16017", "PSAD56", "18N",   "16018",
     813             :             "PSAD56", "19N", "16019", "PSAD56", "20N",   "16020",
     814             :             "PSAD56", "21N", "16021", "PSAD56", "17S",   "16117",
     815             :             "PSAD56", "18S", "16118", "PSAD56", "19S",   "16119",
     816             :             "PSAD56", "20S", "16120", "PSAD56", "21S",   "16121",
     817             :             "PSAD56", "22S", "16122", nullptr,  nullptr, nullptr};
     818             : 
     819           0 :         for (int i = 0; apszUtmProjCode[i] != nullptr; i += 3)
     820             :         {
     821           0 :             if (EQUALN(utmName, apszUtmProjCode[i + 1],
     822           0 :                        strlen(apszUtmProjCode[i + 1])) &&
     823           0 :                 EQUAL(datumName, apszUtmProjCode[i]))
     824             :             {
     825           0 :                 if (psDefn->ProjCode != atoi(apszUtmProjCode[i + 2]))
     826             :                 {
     827           0 :                     psDefn->ProjCode =
     828           0 :                         static_cast<short>(atoi(apszUtmProjCode[i + 2]));
     829           0 :                     GTIFGetProjTRFInfo(psDefn->ProjCode, nullptr,
     830           0 :                                        &(psDefn->Projection), psDefn->ProjParm);
     831           0 :                     break;
     832             :                 }
     833             :             }
     834             :         }
     835             :     }
     836             : 
     837           0 :     return;
     838             : }

Generated by: LCOV version 1.14