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

Generated by: LCOV version 1.14