LCOV - code coverage report
Current view: top level - frmts/gtiff - gt_wkt_srs.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1445 1593 90.7 %
Date: 2025-09-10 17:48:50 Functions: 13 17 76.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GeoTIFF Driver
       4             :  * Purpose:  Implements translation between GeoTIFF normalized projection
       5             :  *           definitions and OpenGIS WKT SRS format.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Frank Warmerdam
      10             :  * Copyright (c) 2008-2014, 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_wkt_srs.h"
      18             : 
      19             : #include <cmath>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : 
      24             : #include <algorithm>
      25             : #include <mutex>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_string.h"
      30             : #include "cpl_vsi.h"
      31             : #include "gt_citation.h"
      32             : #include "gt_wkt_srs_for_gdal.h"
      33             : #include "gt_wkt_srs_priv.h"
      34             : #include "gtiff.h"
      35             : #include "gdal.h"
      36             : #include "geokeys.h"
      37             : #include "geovalues.h"
      38             : #include "ogr_core.h"
      39             : #include "ogr_spatialref.h"
      40             : #include "ogr_srs_api.h"
      41             : #include "ogr_proj_p.h"
      42             : #include "tiff.h"
      43             : #include "tiffio.h"
      44             : #include "tifvsi.h"
      45             : #include "xtiffio.h"
      46             : 
      47             : #include "proj.h"
      48             : 
      49             : static const geokey_t ProjLinearUnitsInterpCorrectGeoKey =
      50             :     static_cast<geokey_t>(3059);
      51             : 
      52             : #ifndef CT_HotineObliqueMercatorAzimuthCenter
      53             : #define CT_HotineObliqueMercatorAzimuthCenter 9815
      54             : #endif
      55             : 
      56             : #if !defined(GTIFAtof)
      57             : #define GTIFAtof CPLAtof
      58             : #endif
      59             : 
      60             : // To remind myself not to use CPLString in this file!
      61             : #define CPLString Please_do_not_use_CPLString_in_this_file
      62             : 
      63             : static const char *const papszDatumEquiv[] = {
      64             :     "Militar_Geographische_Institut",
      65             :     "Militar_Geographische_Institute",
      66             :     "World_Geodetic_System_1984",
      67             :     "WGS_1984",
      68             :     "WGS_72_Transit_Broadcast_Ephemeris",
      69             :     "WGS_1972_Transit_Broadcast_Ephemeris",
      70             :     "World_Geodetic_System_1972",
      71             :     "WGS_1972",
      72             :     "European_Terrestrial_Reference_System_89",
      73             :     "European_Reference_System_1989",
      74             :     "D_North_American_1927",
      75             :     "North_American_Datum_1927",  // #6863
      76             :     nullptr};
      77             : 
      78             : // Older libgeotiff's won't list this.
      79             : #ifndef CT_CylindricalEqualArea
      80             : #define CT_CylindricalEqualArea 28
      81             : #endif
      82             : 
      83             : #if LIBGEOTIFF_VERSION < 1700
      84             : constexpr geokey_t CoordinateEpochGeoKey = static_cast<geokey_t>(5120);
      85             : #endif
      86             : 
      87             : // Exists since 8.0.1
      88             : #ifndef PROJ_AT_LEAST_VERSION
      89             : #define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
      90             :     ((maj)*10000 + (min)*100 + (patch))
      91             : #define PROJ_VERSION_NUMBER                                                    \
      92             :     PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
      93             :                          PROJ_VERSION_PATCH)
      94             : #define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
      95             :     (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
      96             : #endif
      97             : 
      98             : /************************************************************************/
      99             : /*                       LibgeotiffOneTimeInit()                        */
     100             : /************************************************************************/
     101             : 
     102             : static std::mutex oDeleteMutex;
     103             : 
     104       10035 : void LibgeotiffOneTimeInit()
     105             : {
     106       10035 :     std::lock_guard<std::mutex> oLock(oDeleteMutex);
     107             : 
     108             :     static bool bOneTimeInitDone = false;
     109             : 
     110       10035 :     if (bOneTimeInitDone)
     111        9364 :         return;
     112             : 
     113         671 :     bOneTimeInitDone = true;
     114             : 
     115             :     // This isn't thread-safe, so better do it now
     116         671 :     XTIFFInitialize();
     117             : }
     118             : 
     119             : /************************************************************************/
     120             : /*                       GTIFToCPLRecyleString()                        */
     121             : /*                                                                      */
     122             : /*      This changes a string from the libgeotiff heap to the GDAL      */
     123             : /*      heap.                                                           */
     124             : /************************************************************************/
     125             : 
     126       40556 : static void GTIFToCPLRecycleString(char **ppszTarget)
     127             : 
     128             : {
     129       40556 :     if (*ppszTarget == nullptr)
     130          54 :         return;
     131             : 
     132       40502 :     char *pszTempString = CPLStrdup(*ppszTarget);
     133       40502 :     GTIFFreeMemory(*ppszTarget);
     134       40502 :     *ppszTarget = pszTempString;
     135             : }
     136             : 
     137             : /************************************************************************/
     138             : /*                          WKTMassageDatum()                           */
     139             : /*                                                                      */
     140             : /*      Massage an EPSG datum name into WMT format.  Also transform     */
     141             : /*      specific exception cases into WKT versions.                     */
     142             : /************************************************************************/
     143             : 
     144          13 : static void WKTMassageDatum(char **ppszDatum)
     145             : 
     146             : {
     147          13 :     char *pszDatum = *ppszDatum;
     148          13 :     if (!pszDatum || pszDatum[0] == '\0')
     149           0 :         return;
     150             : 
     151             :     /* -------------------------------------------------------------------- */
     152             :     /*      Translate non-alphanumeric values to underscores.               */
     153             :     /* -------------------------------------------------------------------- */
     154         435 :     for (int i = 0; pszDatum[i] != '\0'; i++)
     155             :     {
     156         422 :         if (pszDatum[i] != '+' && !(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') &&
     157         375 :             !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') &&
     158         124 :             !(pszDatum[i] >= '0' && pszDatum[i] <= '9'))
     159             :         {
     160          62 :             pszDatum[i] = '_';
     161             :         }
     162             :     }
     163             : 
     164             :     /* -------------------------------------------------------------------- */
     165             :     /*      Remove repeated and trailing underscores.                       */
     166             :     /* -------------------------------------------------------------------- */
     167          13 :     int j = 0;  // Used after for.
     168         422 :     for (int i = 1; pszDatum[i] != '\0'; i++)
     169             :     {
     170         409 :         if (pszDatum[j] == '_' && pszDatum[i] == '_')
     171           8 :             continue;
     172             : 
     173         401 :         pszDatum[++j] = pszDatum[i];
     174             :     }
     175          13 :     if (pszDatum[j] == '_')
     176           8 :         pszDatum[j] = '\0';
     177             :     else
     178           5 :         pszDatum[j + 1] = '\0';
     179             : 
     180             :     /* -------------------------------------------------------------------- */
     181             :     /*      Search for datum equivalences.  Specific massaged names get     */
     182             :     /*      mapped to OpenGIS specified names.                              */
     183             :     /* -------------------------------------------------------------------- */
     184          77 :     for (int i = 0; papszDatumEquiv[i] != nullptr; i += 2)
     185             :     {
     186          68 :         if (EQUAL(*ppszDatum, papszDatumEquiv[i]))
     187             :         {
     188           4 :             CPLFree(*ppszDatum);
     189           4 :             *ppszDatum = CPLStrdup(papszDatumEquiv[i + 1]);
     190           4 :             return;
     191             :         }
     192             :     }
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                      GTIFCleanupImageineNames()                      */
     197             : /*                                                                      */
     198             : /*      Erdas Imagine sometimes emits big copyright messages, and       */
     199             : /*      other stuff into citations.  These can be pretty messy when     */
     200             : /*      turned into WKT, so we try to trim and clean the strings        */
     201             : /*      somewhat.                                                       */
     202             : /************************************************************************/
     203             : 
     204             : /* For example:
     205             :    GTCitationGeoKey (Ascii,215): "IMAGINE GeoTIFF Support\nCopyright 1991 - 2001
     206             :    by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 34309 $ $Date:
     207             :    2016-05-29 11:29:40 -0700 (Sun, 29 May 2016) $\nProjection Name = UTM\nUnits
     208             :    = meters\nGeoTIFF Units = meters"
     209             : 
     210             :    GeogCitationGeoKey (Ascii,267): "IMAGINE GeoTIFF Support\nCopyright 1991 -
     211             :    2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 34309 $
     212             :    $Date: 2016-05-29 11:29:40 -0700 (Sun, 29 May 2016) $\nUnable to match
     213             :    Ellipsoid (Datum) to a GeographicTypeGeoKey value\nEllipsoid = Clarke
     214             :    1866\nDatum = NAD27 (CONUS)"
     215             : 
     216             :    PCSCitationGeoKey (Ascii,214): "IMAGINE GeoTIFF Support\nCopyright 1991 -
     217             :    2001 by ERDAS, Inc. All Rights Reserved\n@(#)$RCSfile$ $Revision: 34309 $
     218             :    $Date: 2016-05-29 11:29:40 -0700 (Sun, 29 May 2016) $\nUTM Zone
     219             :    10N\nEllipsoid = Clarke 1866\nDatum = NAD27 (CONUS)"
     220             : */
     221             : 
     222         335 : static void GTIFCleanupImagineNames(char *pszCitation)
     223             : 
     224             : {
     225         335 :     if (strstr(pszCitation, "IMAGINE GeoTIFF") == nullptr)
     226         331 :         return;
     227             : 
     228             :     /* -------------------------------------------------------------------- */
     229             :     /*      First, we skip past all the copyright, and RCS stuff.  We       */
     230             :     /*      assume that this will have a "$" at the end of it all.          */
     231             :     /* -------------------------------------------------------------------- */
     232           4 :     char *pszSkip = pszCitation + strlen(pszCitation) - 1;
     233             : 
     234         428 :     for (; pszSkip != pszCitation && *pszSkip != '$'; pszSkip--)
     235             :     {
     236             :     }
     237             : 
     238           4 :     if (*pszSkip == '$')
     239           2 :         pszSkip++;
     240           4 :     if (*pszSkip == '\n')
     241           2 :         pszSkip++;
     242             : 
     243           4 :     memmove(pszCitation, pszSkip, strlen(pszSkip) + 1);
     244             : 
     245             :     /* -------------------------------------------------------------------- */
     246             :     /*      Convert any newlines into spaces, they really gum up the        */
     247             :     /*      WKT.                                                            */
     248             :     /* -------------------------------------------------------------------- */
     249         428 :     for (int i = 0; pszCitation[i] != '\0'; i++)
     250             :     {
     251         424 :         if (pszCitation[i] == '\n')
     252          14 :             pszCitation[i] = ' ';
     253             :     }
     254             : }
     255             : 
     256             : #if LIBGEOTIFF_VERSION < 1600
     257             : 
     258             : /************************************************************************/
     259             : /*                       GDALGTIFKeyGet()                               */
     260             : /************************************************************************/
     261             : 
     262             : static int GDALGTIFKeyGet(GTIF *hGTIF, geokey_t key, void *pData, int nIndex,
     263             :                           int nCount, tagtype_t expected_tagtype)
     264             : {
     265             :     tagtype_t tagtype = TYPE_UNKNOWN;
     266             :     if (!GTIFKeyInfo(hGTIF, key, nullptr, &tagtype))
     267             :         return 0;
     268             :     if (tagtype != expected_tagtype)
     269             :     {
     270             :         CPLError(CE_Warning, CPLE_AppDefined,
     271             :                  "Expected key %s to be of type %s. Got %s", GTIFKeyName(key),
     272             :                  GTIFTypeName(expected_tagtype), GTIFTypeName(tagtype));
     273             :         return 0;
     274             :     }
     275             :     return GTIFKeyGet(hGTIF, key, pData, nIndex, nCount);
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*                       GDALGTIFKeyGetASCII()                          */
     280             : /************************************************************************/
     281             : 
     282             : int GDALGTIFKeyGetASCII(GTIF *hGTIF, geokey_t key, char *szStr, int szStrMaxLen)
     283             : {
     284             :     return GDALGTIFKeyGet(hGTIF, key, szStr, 0, szStrMaxLen, TYPE_ASCII);
     285             : }
     286             : 
     287             : /************************************************************************/
     288             : /*                       GDALGTIFKeyGetSHORT()                          */
     289             : /************************************************************************/
     290             : 
     291             : int GDALGTIFKeyGetSHORT(GTIF *hGTIF, geokey_t key, unsigned short *pnVal,
     292             :                         int nIndex, int nCount)
     293             : {
     294             :     return GDALGTIFKeyGet(hGTIF, key, pnVal, nIndex, nCount, TYPE_SHORT);
     295             : }
     296             : 
     297             : /************************************************************************/
     298             : /*                        GDALGTIFKeyGetDOUBLE()                        */
     299             : /************************************************************************/
     300             : 
     301             : int GDALGTIFKeyGetDOUBLE(GTIF *hGTIF, geokey_t key, double *pdfVal, int nIndex,
     302             :                          int nCount)
     303             : {
     304             :     return GDALGTIFKeyGet(hGTIF, key, pdfVal, nIndex, nCount, TYPE_DOUBLE);
     305             : }
     306             : 
     307             : #endif
     308             : 
     309             : /************************************************************************/
     310             : /*                    FillCompoundCRSWithManualVertCS()                 */
     311             : /************************************************************************/
     312             : 
     313          15 : static void FillCompoundCRSWithManualVertCS(GTIF *hGTIF,
     314             :                                             OGRSpatialReference &oSRS,
     315             :                                             const char *pszVertCSName,
     316             :                                             int verticalDatum,
     317             :                                             int verticalUnits)
     318             : {
     319             :     /* -------------------------------------------------------------------- */
     320             :     /*      Setup VERT_CS with citation if present.                         */
     321             :     /* -------------------------------------------------------------------- */
     322          15 :     oSRS.SetNode("COMPD_CS|VERT_CS", pszVertCSName);
     323             : 
     324             :     /* -------------------------------------------------------------------- */
     325             :     /*      Setup the vertical datum.                                       */
     326             :     /* -------------------------------------------------------------------- */
     327          30 :     std::string osVDatumName = "unknown";
     328          15 :     const char *pszVDatumType = "2005";  // CS_VD_GeoidModelDerived
     329          30 :     std::string osVDatumAuthName;
     330          15 :     int nVDatumCode = 0;
     331             : 
     332          15 :     if (verticalDatum > 0 && verticalDatum != KvUserDefined)
     333             :     {
     334           3 :         osVDatumAuthName = "EPSG";
     335           3 :         nVDatumCode = verticalDatum;
     336             : 
     337             :         char szCode[12];
     338           3 :         snprintf(szCode, sizeof(szCode), "%d", verticalDatum);
     339             :         auto ctx =
     340           3 :             static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(hGTIF, true, nullptr));
     341           3 :         auto datum = proj_create_from_database(ctx, "EPSG", szCode,
     342             :                                                PJ_CATEGORY_DATUM, 0, nullptr);
     343           3 :         if (datum)
     344             :         {
     345           3 :             const char *pszName = proj_get_name(datum);
     346           3 :             if (pszName)
     347             :             {
     348           3 :                 osVDatumName = pszName;
     349             :             }
     350           3 :             proj_destroy(datum);
     351           3 :         }
     352             :     }
     353          12 :     else if (verticalDatum == KvUserDefined)
     354             :     {
     355             :         // If the vertical datum is unknown, try to find the vertical CRS
     356             :         // from the database, and extra the datum information from it.
     357             :         auto ctx =
     358           3 :             static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(hGTIF, true, nullptr));
     359           3 :         const auto type = PJ_TYPE_VERTICAL_CRS;
     360           3 :         auto list = proj_create_from_name(ctx, nullptr, pszVertCSName, &type, 1,
     361             :                                           /* approximateMatch = */ false,
     362             :                                           -1,  // result set limit size,
     363             :                                           nullptr);
     364           3 :         if (list)
     365             :         {
     366             :             // If we have several matches, check they all refer to the
     367             :             // same datum
     368           3 :             bool bGoOn = true;
     369           3 :             int ncount = proj_list_get_count(list);
     370           4 :             for (int i = 0; bGoOn && i < ncount; ++i)
     371             :             {
     372           1 :                 auto crs = proj_list_get(ctx, list, i);
     373           1 :                 if (crs)
     374             :                 {
     375           1 :                     auto datum = proj_crs_get_datum(ctx, crs);
     376           1 :                     if (datum)
     377             :                     {
     378             :                         const char *pszAuthName =
     379           1 :                             proj_get_id_auth_name(datum, 0);
     380           1 :                         const char *pszCode = proj_get_id_code(datum, 0);
     381           1 :                         if (pszCode && atoi(pszCode) && pszAuthName)
     382             :                         {
     383           1 :                             if (osVDatumAuthName.empty())
     384             :                             {
     385           1 :                                 osVDatumName = proj_get_name(datum);
     386           1 :                                 osVDatumAuthName = pszAuthName;
     387           1 :                                 nVDatumCode = atoi(pszCode);
     388             :                             }
     389           0 :                             else if (osVDatumAuthName != pszAuthName ||
     390           0 :                                      nVDatumCode != atoi(pszCode))
     391             :                             {
     392           0 :                                 osVDatumName = "unknown";
     393           0 :                                 osVDatumAuthName.clear();
     394           0 :                                 nVDatumCode = 0;
     395           0 :                                 bGoOn = false;
     396             :                             }
     397             :                         }
     398           1 :                         proj_destroy(datum);
     399             :                     }
     400           1 :                     proj_destroy(crs);
     401             :                 }
     402             :             }
     403             :         }
     404           3 :         proj_list_destroy(list);
     405             :     }
     406             : 
     407          15 :     oSRS.SetNode("COMPD_CS|VERT_CS|VERT_DATUM", osVDatumName.c_str());
     408             :     oSRS.GetAttrNode("COMPD_CS|VERT_CS|VERT_DATUM")
     409          15 :         ->AddChild(new OGR_SRSNode(pszVDatumType));
     410          15 :     if (!osVDatumAuthName.empty())
     411           4 :         oSRS.SetAuthority("COMPD_CS|VERT_CS|VERT_DATUM",
     412             :                           osVDatumAuthName.c_str(), nVDatumCode);
     413             : 
     414             :     /* -------------------------------------------------------------------- */
     415             :     /*      Set the vertical units.                                         */
     416             :     /* -------------------------------------------------------------------- */
     417          15 :     if (verticalUnits > 0 && verticalUnits != KvUserDefined &&
     418             :         verticalUnits != 9001)
     419             :     {
     420             :         char szCode[12];
     421           1 :         snprintf(szCode, sizeof(szCode), "%d", verticalUnits);
     422             :         auto ctx =
     423           1 :             static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(hGTIF, true, nullptr));
     424           1 :         const char *pszName = nullptr;
     425           1 :         double dfInMeters = 0.0;
     426           1 :         if (proj_uom_get_info_from_database(ctx, "EPSG", szCode, &pszName,
     427           1 :                                             &dfInMeters, nullptr))
     428             :         {
     429           1 :             if (pszName)
     430           1 :                 oSRS.SetNode("COMPD_CS|VERT_CS|UNIT", pszName);
     431             : 
     432           1 :             char szInMeters[128] = {};
     433           1 :             CPLsnprintf(szInMeters, sizeof(szInMeters), "%.16g", dfInMeters);
     434             :             oSRS.GetAttrNode("COMPD_CS|VERT_CS|UNIT")
     435           1 :                 ->AddChild(new OGR_SRSNode(szInMeters));
     436             :         }
     437             : 
     438           1 :         oSRS.SetAuthority("COMPD_CS|VERT_CS|UNIT", "EPSG", verticalUnits);
     439             :     }
     440             :     else
     441             :     {
     442          14 :         oSRS.SetNode("COMPD_CS|VERT_CS|UNIT", "metre");
     443             :         oSRS.GetAttrNode("COMPD_CS|VERT_CS|UNIT")
     444          14 :             ->AddChild(new OGR_SRSNode("1.0"));
     445          14 :         oSRS.SetAuthority("COMPD_CS|VERT_CS|UNIT", "EPSG", 9001);
     446             :     }
     447             : 
     448             :     /* -------------------------------------------------------------------- */
     449             :     /*      Set the axis and VERT_CS authority.                             */
     450             :     /* -------------------------------------------------------------------- */
     451          15 :     oSRS.SetNode("COMPD_CS|VERT_CS|AXIS", "Up");
     452          15 :     oSRS.GetAttrNode("COMPD_CS|VERT_CS|AXIS")->AddChild(new OGR_SRSNode("UP"));
     453          15 : }
     454             : 
     455             : /************************************************************************/
     456             : /*                       GTIFGetOfficialCRS()                           */
     457             : /************************************************************************/
     458             : 
     459         256 : static PJ *GTIFGetOfficialCRS(GTIF *hGTIF, PJ_TYPE searchType,
     460             :                               const char *pszAuthName, const char *pszName)
     461             : {
     462         256 :     PJ *ret = nullptr;
     463             :     /* Search in database the corresponding EPSG 'official' name */
     464             :     auto ctx =
     465         256 :         static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(hGTIF, true, nullptr));
     466             :     auto list =
     467         256 :         proj_create_from_name(ctx, pszAuthName, pszName, &searchType, 1,
     468             :                               /* approximateMatch = */ false, 1, nullptr);
     469         256 :     if (list)
     470             :     {
     471         256 :         const auto listSize = proj_list_get_count(list);
     472         256 :         if (listSize == 1)
     473             :         {
     474          29 :             ret = proj_list_get(ctx, list, 0);
     475             :         }
     476         256 :         proj_list_destroy(list);
     477             :     }
     478         256 :     return ret;
     479             : }
     480             : 
     481             : /************************************************************************/
     482             : /*                    GTIFGetEPSGOfficialName()                         */
     483             : /************************************************************************/
     484             : 
     485          51 : static char *GTIFGetEPSGOfficialName(GTIF *hGTIF, PJ_TYPE searchType,
     486             :                                      const char *pszName)
     487             : {
     488          51 :     char *pszRet = nullptr;
     489          51 :     PJ *crs = GTIFGetOfficialCRS(hGTIF, searchType, "EPSG", pszName);
     490          51 :     if (crs)
     491             :     {
     492           4 :         const char *pszOfficialName = proj_get_name(crs);
     493           4 :         if (pszOfficialName)
     494             :         {
     495           4 :             pszRet = CPLStrdup(pszOfficialName);
     496             :         }
     497           4 :         proj_destroy(crs);
     498             :     }
     499          51 :     return pszRet;
     500             : }
     501             : 
     502             : /************************************************************************/
     503             : /*                      GTIFGetOGISDefnAsOSR()                          */
     504             : /************************************************************************/
     505             : 
     506        8523 : OGRSpatialReferenceH GTIFGetOGISDefnAsOSR(GTIF *hGTIF, GTIFDefn *psDefn)
     507             : 
     508             : {
     509       17046 :     OGRSpatialReference oSRS;
     510             : 
     511        8523 :     LibgeotiffOneTimeInit();
     512             : 
     513             : #if LIBGEOTIFF_VERSION >= 1600
     514        8523 :     void *projContext = GTIFGetPROJContext(hGTIF, FALSE, nullptr);
     515             : #endif
     516             : 
     517             :     /* -------------------------------------------------------------------- */
     518             :     /*  Handle non-standard coordinate systems where GTModelTypeGeoKey      */
     519             :     /*  is not defined, but ProjectedCSTypeGeoKey is defined (ticket #3019) */
     520             :     /* -------------------------------------------------------------------- */
     521        8523 :     if (psDefn->Model == KvUserDefined && psDefn->PCS != KvUserDefined)
     522             :     {
     523           5 :         psDefn->Model = ModelTypeProjected;
     524             :     }
     525             : 
     526             :     /* ==================================================================== */
     527             :     /*      Read keys related to vertical component.                        */
     528             :     /* ==================================================================== */
     529        8523 :     unsigned short verticalCSType = 0;
     530        8523 :     unsigned short verticalDatum = 0;
     531        8523 :     unsigned short verticalUnits = 0;
     532             : 
     533        8523 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalCSTypeGeoKey, &verticalCSType, 0, 1);
     534        8523 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalDatumGeoKey, &verticalDatum, 0, 1);
     535        8523 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalUnitsGeoKey, &verticalUnits, 0, 1);
     536             : 
     537        8523 :     if (verticalCSType != 0 || verticalDatum != 0 || verticalUnits != 0)
     538             :     {
     539             :         int versions[3];
     540          42 :         GTIFDirectoryInfo(hGTIF, versions, nullptr);
     541             :         // GeoTIFF 1.0
     542          42 :         if (versions[0] == 1 && versions[1] == 1 && versions[2] == 0)
     543             :         {
     544             :             /* --------------------------------------------------------------------
     545             :              */
     546             :             /*      The original geotiff specification appears to have */
     547             :             /*      misconstrued the EPSG codes 5101 to 5106 to be vertical */
     548             :             /*      coordinate system codes, when in fact they are vertical */
     549             :             /*      datum codes.  So if these are found in the */
     550             :             /*      VerticalCSTypeGeoKey move them to the VerticalDatumGeoKey */
     551             :             /*      and insert the "normal" corresponding VerticalCSTypeGeoKey
     552             :              */
     553             :             /*      value. */
     554             :             /* --------------------------------------------------------------------
     555             :              */
     556          12 :             if ((verticalCSType >= 5101 && verticalCSType <= 5112) &&
     557           0 :                 verticalDatum == 0)
     558             :             {
     559           0 :                 verticalDatum = verticalCSType;
     560           0 :                 verticalCSType = verticalDatum + 600;
     561             :             }
     562             : 
     563             :             /* --------------------------------------------------------------------
     564             :              */
     565             :             /*      This addresses another case where the EGM96 Vertical Datum
     566             :              * code */
     567             :             /*      is misused as a Vertical CS code (#4922). */
     568             :             /* --------------------------------------------------------------------
     569             :              */
     570          12 :             if (verticalCSType == 5171)
     571             :             {
     572           0 :                 verticalDatum = 5171;
     573           0 :                 verticalCSType = 5773;
     574             :             }
     575             :         }
     576             : 
     577             :         /* --------------------------------------------------------------------
     578             :          */
     579             :         /*      Somewhat similarly, codes 5001 to 5033 were treated as */
     580             :         /*      vertical coordinate systems based on ellipsoidal heights. */
     581             :         /*      We use the corresponding geodetic datum as the vertical */
     582             :         /*      datum and clear the vertical coordinate system code since */
     583             :         /*      there isn't one in EPSG. */
     584             :         /* --------------------------------------------------------------------
     585             :          */
     586          42 :         if ((verticalCSType >= 5001 && verticalCSType <= 5033) &&
     587           1 :             verticalDatum == 0)
     588             :         {
     589           1 :             verticalDatum = verticalCSType + 1000;
     590           1 :             verticalCSType = 0;
     591             :         }
     592             :     }
     593             : 
     594             :     /* -------------------------------------------------------------------- */
     595             :     /*      Handle non-standard coordinate systems as LOCAL_CS.             */
     596             :     /* -------------------------------------------------------------------- */
     597        8523 :     if (psDefn->Model != ModelTypeProjected &&
     598        4702 :         psDefn->Model != ModelTypeGeographic &&
     599         346 :         psDefn->Model != ModelTypeGeocentric)
     600             :     {
     601         344 :         char szPeStr[2400] = {'\0'};
     602             : 
     603             :         /** check if there is a pe string citation key **/
     604         344 :         if (GDALGTIFKeyGetASCII(hGTIF, PCSCitationGeoKey, szPeStr,
     605         353 :                                 sizeof(szPeStr)) &&
     606           9 :             strstr(szPeStr, "ESRI PE String = "))
     607             :         {
     608           9 :             const char *pszWKT = szPeStr + strlen("ESRI PE String = ");
     609           9 :             oSRS.importFromWkt(pszWKT);
     610             : 
     611           9 :             if (strstr(pszWKT,
     612             :                        "PROJCS[\"WGS_1984_Web_Mercator_Auxiliary_Sphere\""))
     613             :             {
     614           6 :                 oSRS.SetExtension(
     615             :                     "PROJCS", "PROJ4",
     616             :                     "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 "
     617             :                     "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null "
     618             :                     "+wktext  +no_defs");  // TODO(schwehr): Why 2 spaces?
     619             :             }
     620             : 
     621           9 :             return OGRSpatialReference::ToHandle(oSRS.Clone());
     622             :         }
     623             :         else
     624             :         {
     625         335 :             char *pszUnitsName = nullptr;
     626         335 :             char szPCSName[300] = {'\0'};
     627         335 :             int nKeyCount = 0;
     628         335 :             int anVersion[3] = {0};
     629             : 
     630         335 :             GTIFDirectoryInfo(hGTIF, anVersion, &nKeyCount);
     631             : 
     632         335 :             if (nKeyCount > 0)  // Use LOCAL_CS if we have any geokeys at all.
     633             :             {
     634             :                 // Handle citation.
     635         335 :                 strcpy(szPCSName, "unnamed");
     636         335 :                 if (!GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szPCSName,
     637             :                                          sizeof(szPCSName)))
     638         320 :                     GDALGTIFKeyGetASCII(hGTIF, GeogCitationGeoKey, szPCSName,
     639             :                                         sizeof(szPCSName));
     640             : 
     641         335 :                 GTIFCleanupImagineNames(szPCSName);
     642         335 :                 oSRS.SetLocalCS(szPCSName);
     643             : 
     644             :                 // Handle units
     645         335 :                 if (psDefn->UOMLength != KvUserDefined)
     646             :                 {
     647             : #if LIBGEOTIFF_VERSION >= 1600
     648         323 :                     GTIFGetUOMLengthInfoEx(projContext,
     649             : #else
     650             :                     GTIFGetUOMLengthInfo(
     651             : #endif
     652         323 :                                            psDefn->UOMLength, &pszUnitsName,
     653             :                                            nullptr);
     654             :                 }
     655             : 
     656         335 :                 if (pszUnitsName != nullptr)
     657             :                 {
     658             :                     char szUOMLength[12];
     659         323 :                     snprintf(szUOMLength, sizeof(szUOMLength), "%d",
     660         323 :                              psDefn->UOMLength);
     661         323 :                     oSRS.SetTargetLinearUnits(nullptr, pszUnitsName,
     662             :                                               psDefn->UOMLengthInMeters, "EPSG",
     663             :                                               szUOMLength);
     664             :                 }
     665             :                 else
     666          12 :                     oSRS.SetLinearUnits("unknown", psDefn->UOMLengthInMeters);
     667             : 
     668         335 :                 if (verticalUnits != 0)
     669             :                 {
     670           3 :                     char szVertCSCitation[2048] = {0};
     671           3 :                     if (GDALGTIFKeyGetASCII(hGTIF, VerticalCitationGeoKey,
     672             :                                             szVertCSCitation,
     673           3 :                                             sizeof(szVertCSCitation)))
     674             :                     {
     675           1 :                         if (STARTS_WITH_CI(szVertCSCitation, "VCS Name = "))
     676             :                         {
     677           0 :                             memmove(szVertCSCitation,
     678             :                                     szVertCSCitation + strlen("VCS Name = "),
     679           0 :                                     strlen(szVertCSCitation +
     680             :                                            strlen("VCS Name = ")) +
     681             :                                         1);
     682           0 :                             char *pszPipeChar = strchr(szVertCSCitation, '|');
     683           0 :                             if (pszPipeChar)
     684           0 :                                 *pszPipeChar = '\0';
     685             :                         }
     686             :                     }
     687             :                     else
     688             :                     {
     689           2 :                         strcpy(szVertCSCitation, "unknown");
     690             :                     }
     691             : 
     692           3 :                     const char *pszHorizontalName = oSRS.GetName();
     693             :                     const std::string osHorizontalName(
     694           6 :                         pszHorizontalName ? pszHorizontalName : "unnamed");
     695             :                     /* --------------------------------------------------------------------
     696             :                      */
     697             :                     /*      Promote to being a compound coordinate system. */
     698             :                     /* --------------------------------------------------------------------
     699             :                      */
     700           3 :                     OGR_SRSNode *poOldRoot = oSRS.GetRoot()->Clone();
     701             : 
     702           3 :                     oSRS.Clear();
     703             : 
     704             :                     /* --------------------------------------------------------------------
     705             :                      */
     706             :                     /*      Set COMPD_CS name. */
     707             :                     /* --------------------------------------------------------------------
     708             :                      */
     709             :                     char szCTString[512];
     710           3 :                     szCTString[0] = '\0';
     711           3 :                     if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
     712           4 :                                             sizeof(szCTString)) &&
     713           1 :                         strstr(szCTString, " = ") == nullptr)
     714             :                     {
     715           1 :                         oSRS.SetNode("COMPD_CS", szCTString);
     716             :                     }
     717             :                     else
     718             :                     {
     719           2 :                         oSRS.SetNode("COMPD_CS", (osHorizontalName + " + " +
     720             :                                                   szVertCSCitation)
     721             :                                                      .c_str());
     722             :                     }
     723             : 
     724           3 :                     oSRS.GetRoot()->AddChild(poOldRoot);
     725             : 
     726           3 :                     FillCompoundCRSWithManualVertCS(
     727             :                         hGTIF, oSRS, szVertCSCitation, verticalDatum,
     728             :                         verticalUnits);
     729             :                 }
     730             : 
     731         335 :                 GTIFFreeMemory(pszUnitsName);
     732             :             }
     733         335 :             return OGRSpatialReference::ToHandle(oSRS.Clone());
     734             :         }
     735             :     }
     736             : 
     737             :     /* -------------------------------------------------------------------- */
     738             :     /*      Handle Geocentric coordinate systems.                           */
     739             :     /* -------------------------------------------------------------------- */
     740        8179 :     if (psDefn->Model == ModelTypeGeocentric)
     741             :     {
     742           2 :         char szName[300] = {'\0'};
     743             : 
     744           2 :         strcpy(szName, "unnamed");
     745           2 :         if (!GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szName,
     746             :                                  sizeof(szName)))
     747           0 :             GDALGTIFKeyGetASCII(hGTIF, GeogCitationGeoKey, szName,
     748             :                                 sizeof(szName));
     749             : 
     750           2 :         oSRS.SetGeocCS(szName);
     751             : 
     752           2 :         char *pszUnitsName = nullptr;
     753             : 
     754           2 :         if (psDefn->UOMLength != KvUserDefined)
     755             :         {
     756             : #if LIBGEOTIFF_VERSION >= 1600
     757           2 :             GTIFGetUOMLengthInfoEx(projContext,
     758             : #else
     759             :             GTIFGetUOMLengthInfo(
     760             : #endif
     761           2 :                                    psDefn->UOMLength, &pszUnitsName, nullptr);
     762             :         }
     763             : 
     764           2 :         if (pszUnitsName != nullptr)
     765             :         {
     766             :             char szUOMLength[12];
     767           2 :             snprintf(szUOMLength, sizeof(szUOMLength), "%d", psDefn->UOMLength);
     768           2 :             oSRS.SetTargetLinearUnits(nullptr, pszUnitsName,
     769             :                                       psDefn->UOMLengthInMeters, "EPSG",
     770             :                                       szUOMLength);
     771             :         }
     772             :         else
     773           0 :             oSRS.SetLinearUnits("unknown", psDefn->UOMLengthInMeters);
     774             : 
     775           2 :         GTIFFreeMemory(pszUnitsName);
     776             :     }
     777             : 
     778             :     /* -------------------------------------------------------------------- */
     779             :     /*      #3901: In libgeotiff 1.3.0 and earlier we incorrectly           */
     780             :     /*      interpreted linear projection parameter geokeys (false          */
     781             :     /*      easting/northing) as being in meters instead of the             */
     782             :     /*      coordinate system of the file.   The following code attempts    */
     783             :     /*      to provide mechanisms for fixing the issue if we are linked     */
     784             :     /*      with an older version of libgeotiff.                            */
     785             :     /* -------------------------------------------------------------------- */
     786             :     const char *pszLinearUnits =
     787        8179 :         CPLGetConfigOption("GTIFF_LINEAR_UNITS", "DEFAULT");
     788             : 
     789             :     /* -------------------------------------------------------------------- */
     790             :     /*      #3901: If folks have broken GeoTIFF files generated with        */
     791             :     /*      older versions of GDAL+libgeotiff, then they may need a         */
     792             :     /*      hack to allow them to be read properly.  This is that           */
     793             :     /*      hack.  We basically try to undue the conversion applied by      */
     794             :     /*      libgeotiff to meters (or above) to simulate the old             */
     795             :     /*      behavior.                                                       */
     796             :     /* -------------------------------------------------------------------- */
     797        8179 :     unsigned short bLinearUnitsMarkedCorrect = FALSE;
     798             : 
     799        8179 :     GDALGTIFKeyGetSHORT(hGTIF, ProjLinearUnitsInterpCorrectGeoKey,
     800             :                         &bLinearUnitsMarkedCorrect, 0, 1);
     801             : 
     802        8179 :     if (EQUAL(pszLinearUnits, "BROKEN") &&
     803           3 :         psDefn->Projection == KvUserDefined && !bLinearUnitsMarkedCorrect)
     804             :     {
     805          16 :         for (int iParam = 0; iParam < psDefn->nParms; iParam++)
     806             :         {
     807          14 :             switch (psDefn->ProjParmId[iParam])
     808             :             {
     809           4 :                 case ProjFalseEastingGeoKey:
     810             :                 case ProjFalseNorthingGeoKey:
     811             :                 case ProjFalseOriginEastingGeoKey:
     812             :                 case ProjFalseOriginNorthingGeoKey:
     813             :                 case ProjCenterEastingGeoKey:
     814             :                 case ProjCenterNorthingGeoKey:
     815           4 :                     if (psDefn->UOMLengthInMeters != 0 &&
     816           4 :                         psDefn->UOMLengthInMeters != 1.0)
     817             :                     {
     818           4 :                         psDefn->ProjParm[iParam] /= psDefn->UOMLengthInMeters;
     819           4 :                         CPLDebug(
     820             :                             "GTIFF",
     821             :                             "Converting geokey to accommodate old broken file "
     822             :                             "due to GTIFF_LINEAR_UNITS=BROKEN setting.");
     823             :                     }
     824           4 :                     break;
     825             : 
     826          10 :                 default:
     827          10 :                     break;
     828             :             }
     829             :         }
     830             :     }
     831             : 
     832             :     /* -------------------------------------------------------------------- */
     833             :     /*      If this is a projected SRS we set the PROJCS keyword first      */
     834             :     /*      to ensure that the GEOGCS will be a child.                      */
     835             :     /* -------------------------------------------------------------------- */
     836        8179 :     OGRBoolean linearUnitIsSet = FALSE;
     837        8179 :     if (psDefn->Model == ModelTypeProjected)
     838             :     {
     839        3821 :         char szCTString[512] = {'\0'};
     840        3821 :         if (psDefn->PCS != KvUserDefined)
     841             :         {
     842        3722 :             char *pszPCSName = nullptr;
     843             : 
     844             : #if LIBGEOTIFF_VERSION >= 1600
     845        3722 :             GTIFGetPCSInfoEx(projContext,
     846             : #else
     847             :             GTIFGetPCSInfo(
     848             : #endif
     849        3722 :                              psDefn->PCS, &pszPCSName, nullptr, nullptr,
     850             :                              nullptr);
     851             : 
     852        3722 :             oSRS.SetProjCS(pszPCSName ? pszPCSName : "unnamed");
     853        3722 :             if (pszPCSName)
     854        3722 :                 GTIFFreeMemory(pszPCSName);
     855             : 
     856        3722 :             oSRS.SetLinearUnits("unknown", 1.0);
     857             :         }
     858             :         else
     859             :         {
     860          99 :             bool bTryGTCitationGeoKey = true;
     861          99 :             if (GDALGTIFKeyGetASCII(hGTIF, PCSCitationGeoKey, szCTString,
     862          99 :                                     sizeof(szCTString)))
     863             :             {
     864           6 :                 bTryGTCitationGeoKey = false;
     865           6 :                 if (!SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString),
     866             :                                       PCSCitationGeoKey, &oSRS,
     867             :                                       &linearUnitIsSet))
     868             :                 {
     869           6 :                     if (!STARTS_WITH_CI(szCTString, "LUnits = "))
     870             :                     {
     871           4 :                         oSRS.SetProjCS(szCTString);
     872           4 :                         oSRS.SetLinearUnits("unknown", 1.0);
     873             :                     }
     874             :                     else
     875             :                     {
     876           2 :                         bTryGTCitationGeoKey = true;
     877             :                     }
     878             :                 }
     879             :             }
     880             : 
     881          99 :             if (bTryGTCitationGeoKey)
     882             :             {
     883          95 :                 if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
     884         188 :                                         sizeof(szCTString)) &&
     885          93 :                     !SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString),
     886             :                                       GTCitationGeoKey, &oSRS,
     887             :                                       &linearUnitIsSet))
     888             :                 {
     889           0 :                     oSRS.SetNode("PROJCS", szCTString);
     890           0 :                     oSRS.SetLinearUnits("unknown", 1.0);
     891             :                 }
     892             :                 else
     893             :                 {
     894          95 :                     oSRS.SetNode("PROJCS", "unnamed");
     895          95 :                     oSRS.SetLinearUnits("unknown", 1.0);
     896             :                 }
     897             :             }
     898             :         }
     899             : 
     900             :         /* Handle ESRI/Erdas style state plane and UTM in citation key */
     901        3821 :         if (CheckCitationKeyForStatePlaneUTM(hGTIF, psDefn, &oSRS,
     902        3821 :                                              &linearUnitIsSet))
     903             :         {
     904           0 :             return OGRSpatialReference::ToHandle(oSRS.Clone());
     905             :         }
     906             : 
     907             :         /* Handle ESRI PE string in citation */
     908        3821 :         szCTString[0] = '\0';
     909        3821 :         if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
     910        3821 :                                 sizeof(szCTString)))
     911        3801 :             SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString),
     912             :                              GTCitationGeoKey, &oSRS, &linearUnitIsSet);
     913             :     }
     914             : 
     915             :     /* ==================================================================== */
     916             :     /*      Setup the GeogCS                                                */
     917             :     /* ==================================================================== */
     918        8179 :     char *pszGeogName = nullptr;
     919        8179 :     char *pszDatumName = nullptr;
     920        8179 :     char *pszPMName = nullptr;
     921        8179 :     char *pszSpheroidName = nullptr;
     922        8179 :     char *pszAngularUnits = nullptr;
     923        8179 :     char szGCSName[512] = {'\0'};
     924             : 
     925        8179 :     if (!
     926             : #if LIBGEOTIFF_VERSION >= 1600
     927       16358 :         GTIFGetGCSInfoEx(projContext,
     928             : #else
     929             :         GTIFGetGCSInfo(
     930             : #endif
     931        8179 :                          psDefn->GCS, &pszGeogName, nullptr, nullptr,
     932        8283 :                          nullptr) &&
     933         104 :         GDALGTIFKeyGetASCII(hGTIF, GeogCitationGeoKey, szGCSName,
     934             :                             sizeof(szGCSName)))
     935             :     {
     936          91 :         GetGeogCSFromCitation(szGCSName, sizeof(szGCSName), GeogCitationGeoKey,
     937             :                               &pszGeogName, &pszDatumName, &pszPMName,
     938             :                               &pszSpheroidName, &pszAngularUnits);
     939             :     }
     940             :     else
     941             :     {
     942        8088 :         GTIFToCPLRecycleString(&pszGeogName);
     943             :     }
     944             : 
     945        8179 :     if (pszGeogName && STARTS_WITH(pszGeogName, "GCS_"))
     946             :     {
     947             :         // Morph from potential ESRI name
     948          11 :         char *pszOfficialName = GTIFGetEPSGOfficialName(
     949             :             hGTIF, PJ_TYPE_GEOGRAPHIC_2D_CRS, pszGeogName);
     950          11 :         if (pszOfficialName)
     951             :         {
     952           0 :             CPLFree(pszGeogName);
     953           0 :             pszGeogName = pszOfficialName;
     954             :         }
     955             :     }
     956             : 
     957        8179 :     if (pszDatumName && strchr(pszDatumName, '_'))
     958             :     {
     959             :         // Morph from potential ESRI name
     960          23 :         char *pszOfficialName = GTIFGetEPSGOfficialName(
     961             :             hGTIF, PJ_TYPE_GEODETIC_REFERENCE_FRAME, pszDatumName);
     962          23 :         if (pszOfficialName)
     963             :         {
     964           3 :             CPLFree(pszDatumName);
     965           3 :             pszDatumName = pszOfficialName;
     966             :         }
     967             :     }
     968             : 
     969        8179 :     if (pszSpheroidName && strchr(pszSpheroidName, '_'))
     970             :     {
     971             :         // Morph from potential ESRI name
     972             :         char *pszOfficialName =
     973           5 :             GTIFGetEPSGOfficialName(hGTIF, PJ_TYPE_ELLIPSOID, pszSpheroidName);
     974           5 :         if (pszOfficialName)
     975             :         {
     976           1 :             CPLFree(pszSpheroidName);
     977           1 :             pszSpheroidName = pszOfficialName;
     978             :         }
     979             :     }
     980             : 
     981        8179 :     if (!pszDatumName)
     982             :     {
     983             : #if LIBGEOTIFF_VERSION >= 1600
     984        8114 :         GTIFGetDatumInfoEx(projContext,
     985             : #else
     986             :         GTIFGetDatumInfo(
     987             : #endif
     988        8114 :                            psDefn->Datum, &pszDatumName, nullptr);
     989        8114 :         GTIFToCPLRecycleString(&pszDatumName);
     990             :     }
     991             : 
     992        8179 :     double dfSemiMajor = 0.0;
     993        8179 :     double dfInvFlattening = 0.0;
     994        8179 :     if (!pszSpheroidName)
     995             :     {
     996             : #if LIBGEOTIFF_VERSION >= 1600
     997        8111 :         GTIFGetEllipsoidInfoEx(projContext,
     998             : #else
     999             :         GTIFGetEllipsoidInfo(
    1000             : #endif
    1001        8111 :                                psDefn->Ellipsoid, &pszSpheroidName, nullptr,
    1002             :                                nullptr);
    1003        8111 :         GTIFToCPLRecycleString(&pszSpheroidName);
    1004             :     }
    1005             :     else
    1006             :     {
    1007          68 :         CPL_IGNORE_RET_VAL(GDALGTIFKeyGetDOUBLE(hGTIF, GeogSemiMajorAxisGeoKey,
    1008             :                                                 &(psDefn->SemiMajor), 0, 1));
    1009          68 :         CPL_IGNORE_RET_VAL(GDALGTIFKeyGetDOUBLE(hGTIF, GeogInvFlatteningGeoKey,
    1010             :                                                 &dfInvFlattening, 0, 1));
    1011          68 :         if (std::isinf(dfInvFlattening))
    1012             :         {
    1013             :             // Deal with the non-nominal case of
    1014             :             // https://github.com/OSGeo/PROJ/issues/2317
    1015           0 :             dfInvFlattening = 0;
    1016             :         }
    1017             :     }
    1018        8179 :     if (!pszPMName)
    1019             :     {
    1020             : #if LIBGEOTIFF_VERSION >= 1600
    1021        8090 :         GTIFGetPMInfoEx(projContext,
    1022             : #else
    1023             :         GTIFGetPMInfo(
    1024             : #endif
    1025        8090 :                         psDefn->PM, &pszPMName, nullptr);
    1026        8090 :         GTIFToCPLRecycleString(&pszPMName);
    1027             :     }
    1028             :     else
    1029             :     {
    1030          89 :         CPL_IGNORE_RET_VAL(
    1031          89 :             GDALGTIFKeyGetDOUBLE(hGTIF, GeogPrimeMeridianLongGeoKey,
    1032             :                                  &(psDefn->PMLongToGreenwich), 0, 1));
    1033             :     }
    1034             : 
    1035        8179 :     if (!pszAngularUnits)
    1036             :     {
    1037             : #if LIBGEOTIFF_VERSION >= 1600
    1038        8164 :         GTIFGetUOMAngleInfoEx(projContext,
    1039             : #else
    1040             :         GTIFGetUOMAngleInfo(
    1041             : #endif
    1042        8164 :                               psDefn->UOMAngle, &pszAngularUnits,
    1043             :                               &psDefn->UOMAngleInDegrees);
    1044        8164 :         if (pszAngularUnits == nullptr)
    1045          11 :             pszAngularUnits = CPLStrdup("unknown");
    1046             :         else
    1047        8153 :             GTIFToCPLRecycleString(&pszAngularUnits);
    1048             :     }
    1049             :     else
    1050             :     {
    1051          15 :         double dfRadians = 0.0;
    1052          15 :         if (GDALGTIFKeyGetDOUBLE(hGTIF, GeogAngularUnitSizeGeoKey, &dfRadians,
    1053          15 :                                  0, 1))
    1054             :         {
    1055           4 :             psDefn->UOMAngleInDegrees = dfRadians / CPLAtof(SRS_UA_DEGREE_CONV);
    1056             :         }
    1057             :     }
    1058             : 
    1059             :     // Avoid later division by zero.
    1060        8179 :     if (psDefn->UOMAngleInDegrees == 0)
    1061             :     {
    1062           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    1063             :                  "Invalid value for GeogAngularUnitSizeGeoKey.");
    1064           1 :         psDefn->UOMAngleInDegrees = 1;
    1065             :     }
    1066             : 
    1067        8179 :     dfSemiMajor = psDefn->SemiMajor;
    1068        8179 :     if (dfSemiMajor == 0.0)
    1069             :     {
    1070          11 :         CPLFree(pszSpheroidName);
    1071          11 :         pszSpheroidName = CPLStrdup("unretrievable - using WGS84");
    1072          11 :         dfSemiMajor = SRS_WGS84_SEMIMAJOR;
    1073          11 :         dfInvFlattening = SRS_WGS84_INVFLATTENING;
    1074             :     }
    1075        8168 :     else if (dfInvFlattening == 0.0 &&
    1076        8111 :              ((psDefn->SemiMinor / psDefn->SemiMajor) < 0.99999999999999999 ||
    1077          14 :               (psDefn->SemiMinor / psDefn->SemiMajor) > 1.00000000000000001))
    1078             :     {
    1079        8097 :         dfInvFlattening =
    1080        8097 :             OSRCalcInvFlattening(psDefn->SemiMajor, psDefn->SemiMinor);
    1081             : 
    1082             :         /* Take official inverse flattening definition in the WGS84 case */
    1083        8097 :         if (fabs(dfSemiMajor - SRS_WGS84_SEMIMAJOR) < 1e-10 &&
    1084        4701 :             fabs(dfInvFlattening - SRS_WGS84_INVFLATTENING) < 1e-10)
    1085        4625 :             dfInvFlattening = SRS_WGS84_INVFLATTENING;
    1086             :     }
    1087        8179 :     if (!pszGeogName || strlen(pszGeogName) == 0)
    1088             :     {
    1089          13 :         CPLFree(pszGeogName);
    1090          13 :         pszGeogName = CPLStrdup(pszDatumName ? pszDatumName : "unknown");
    1091             :     }
    1092             : 
    1093        8179 :     oSRS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
    1094             :                    dfInvFlattening, pszPMName, psDefn->PMLongToGreenwich,
    1095             :                    pszAngularUnits,
    1096        8179 :                    psDefn->UOMAngleInDegrees * CPLAtof(SRS_UA_DEGREE_CONV));
    1097             : 
    1098        8179 :     bool bGeog3DCRS = false;
    1099        8179 :     bool bSetDatumEllipsoidCode = true;
    1100        8179 :     bool bHasWarnedInconsistentGeogCRSEPSG = false;
    1101             :     {
    1102        8179 :         const int nGCS = psDefn->GCS;
    1103        8179 :         if (nGCS != KvUserDefined && nGCS > 0 &&
    1104        8075 :             psDefn->Model != ModelTypeGeocentric)
    1105             :         {
    1106       16150 :             OGRSpatialReference oSRSGeog;
    1107             :             const bool bGCSCodeValid =
    1108        8075 :                 oSRSGeog.importFromEPSG(nGCS) == OGRERR_NONE;
    1109             : 
    1110             :             const std::string osGTiffSRSSource =
    1111       16150 :                 CPLGetConfigOption("GTIFF_SRS_SOURCE", "");
    1112             : 
    1113             :             // GeoTIFF 1.0 might put a Geographic 3D code in GeodeticCRSGeoKey
    1114        8075 :             bool bTryCompareToEPSG = oSRSGeog.GetAxesCount() == 2;
    1115             : 
    1116        8075 :             if (psDefn->Datum != KvUserDefined)
    1117             :             {
    1118             :                 char szCode[12];
    1119        8075 :                 snprintf(szCode, sizeof(szCode), "%d", psDefn->Datum);
    1120             :                 auto ctx = static_cast<PJ_CONTEXT *>(
    1121        8075 :                     GTIFGetPROJContext(hGTIF, true, nullptr));
    1122        8075 :                 auto datum = proj_create_from_database(
    1123             :                     ctx, "EPSG", szCode, PJ_CATEGORY_DATUM, 0, nullptr);
    1124        8075 :                 if (datum)
    1125             :                 {
    1126        8074 :                     if (proj_get_type(datum) ==
    1127             :                         PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME)
    1128             :                     {
    1129             :                         // Current PROJ versions will not manage to
    1130             :                         // consider a CRS with a regular datum and another one
    1131             :                         // with a dynamic datum as being equivalent.
    1132           0 :                         bTryCompareToEPSG = false;
    1133             :                     }
    1134        8074 :                     proj_destroy(datum);
    1135             :                 }
    1136             :             }
    1137             : 
    1138        8079 :             if (bTryCompareToEPSG && !oSRSGeog.IsSameGeogCS(&oSRS) &&
    1139           4 :                 osGTiffSRSSource.empty())
    1140             :             {
    1141             :                 // See https://github.com/OSGeo/gdal/issues/5399
    1142             :                 // where a file has inconsistent GeogSemiMinorAxisGeoKey /
    1143             :                 // GeogInvFlatteningGeoKey values, which cause its datum to be
    1144             :                 // considered as non-equivalent to the EPSG one.
    1145           2 :                 CPLError(
    1146             :                     CE_Warning, CPLE_AppDefined,
    1147             :                     "The definition of geographic CRS EPSG:%d got from GeoTIFF "
    1148             :                     "keys "
    1149             :                     "is not the same as the one from the EPSG registry, "
    1150             :                     "which may cause issues during reprojection operations. "
    1151             :                     "Set GTIFF_SRS_SOURCE configuration option to EPSG to "
    1152             :                     "use official parameters (overriding the ones from GeoTIFF "
    1153             :                     "keys), "
    1154             :                     "or to GEOKEYS to use custom values from GeoTIFF keys "
    1155             :                     "and drop the EPSG code.",
    1156             :                     nGCS);
    1157           2 :                 bHasWarnedInconsistentGeogCRSEPSG = true;
    1158             :             }
    1159        8075 :             if (EQUAL(osGTiffSRSSource.c_str(), "EPSG"))
    1160             :             {
    1161           1 :                 oSRS.CopyGeogCSFrom(&oSRSGeog);
    1162             :             }
    1163       12683 :             else if (osGTiffSRSSource.empty() && oSRSGeog.IsDynamic() &&
    1164        4609 :                      psDefn->Model == ModelTypeGeographic)
    1165             :             {
    1166             :                 // We should perhaps be more careful and detect overrides
    1167             :                 // of geokeys...
    1168        4313 :                 oSRS = oSRSGeog;
    1169        4313 :                 bSetDatumEllipsoidCode = false;
    1170             :             }
    1171        3761 :             else if (bGCSCodeValid && osGTiffSRSSource.empty())
    1172             :             {
    1173        3760 :                 oSRS.SetAuthority("GEOGCS", "EPSG", nGCS);
    1174             :             }
    1175             :             else
    1176             :             {
    1177           1 :                 bSetDatumEllipsoidCode = false;
    1178             :             }
    1179             : 
    1180        8075 :             int nVertSRSCode = verticalCSType;
    1181        8075 :             if (verticalDatum == 6030 && nGCS == 4326)  // DatumE_WGS84
    1182             :             {
    1183           1 :                 nVertSRSCode = 4979;
    1184             :             }
    1185             : 
    1186             :             // Try to reconstruct a Geographic3D CRS from the
    1187             :             // GeodeticCRSGeoKey and the VerticalGeoKey, when they are
    1188             :             // consistent
    1189        8075 :             if (nVertSRSCode > 0 && nVertSRSCode != KvUserDefined)
    1190             :             {
    1191          52 :                 OGRSpatialReference oTmpVertSRS;
    1192          52 :                 if (oSRSGeog.IsGeographic() && oSRSGeog.GetAxesCount() == 2 &&
    1193          26 :                     oTmpVertSRS.importFromEPSG(nVertSRSCode) == OGRERR_NONE &&
    1194          58 :                     oTmpVertSRS.IsGeographic() &&
    1195           6 :                     oTmpVertSRS.GetAxesCount() == 3)
    1196             :                 {
    1197             :                     const char *pszTmpCode =
    1198           6 :                         oSRSGeog.GetAuthorityCode("GEOGCS|DATUM");
    1199             :                     const char *pszTmpVertCode =
    1200           6 :                         oTmpVertSRS.GetAuthorityCode("GEOGCS|DATUM");
    1201           6 :                     if (pszTmpCode && pszTmpVertCode &&
    1202           6 :                         atoi(pszTmpCode) == atoi(pszTmpVertCode))
    1203             :                     {
    1204           6 :                         verticalCSType = 0;
    1205           6 :                         verticalDatum = 0;
    1206           6 :                         verticalUnits = 0;
    1207           6 :                         oSRS.CopyGeogCSFrom(&oTmpVertSRS);
    1208           6 :                         bSetDatumEllipsoidCode = false;
    1209           6 :                         bGeog3DCRS = true;
    1210             :                     }
    1211             :                 }
    1212             :             }
    1213             :         }
    1214             :     }
    1215        8179 :     if (bSetDatumEllipsoidCode)
    1216             :     {
    1217        3863 :         if (psDefn->Datum != KvUserDefined)
    1218        3785 :             oSRS.SetAuthority("DATUM", "EPSG", psDefn->Datum);
    1219             : 
    1220        3863 :         if (psDefn->Ellipsoid != KvUserDefined)
    1221        3788 :             oSRS.SetAuthority("SPHEROID", "EPSG", psDefn->Ellipsoid);
    1222             :     }
    1223             : 
    1224        8179 :     CPLFree(pszGeogName);
    1225        8179 :     CPLFree(pszDatumName);
    1226        8179 :     CPLFree(pszSpheroidName);
    1227        8179 :     CPLFree(pszPMName);
    1228        8179 :     CPLFree(pszAngularUnits);
    1229             : 
    1230             :     /* -------------------------------------------------------------------- */
    1231             :     /*      Set projection units if not yet done                            */
    1232             :     /* -------------------------------------------------------------------- */
    1233        8179 :     if (psDefn->Model == ModelTypeProjected && !linearUnitIsSet)
    1234             :     {
    1235        3814 :         char *pszUnitsName = nullptr;
    1236             : 
    1237        3814 :         if (psDefn->UOMLength != KvUserDefined)
    1238             :         {
    1239             : #if LIBGEOTIFF_VERSION >= 1600
    1240        3807 :             GTIFGetUOMLengthInfoEx(projContext,
    1241             : #else
    1242             :             GTIFGetUOMLengthInfo(
    1243             : #endif
    1244        3807 :                                    psDefn->UOMLength, &pszUnitsName, nullptr);
    1245             :         }
    1246             : 
    1247        3814 :         if (pszUnitsName != nullptr)
    1248             :         {
    1249             :             char szUOMLength[12];
    1250        3807 :             snprintf(szUOMLength, sizeof(szUOMLength), "%d", psDefn->UOMLength);
    1251        3807 :             oSRS.SetTargetLinearUnits(nullptr, pszUnitsName,
    1252             :                                       psDefn->UOMLengthInMeters, "EPSG",
    1253             :                                       szUOMLength);
    1254             :         }
    1255             :         else
    1256           7 :             oSRS.SetLinearUnits("unknown", psDefn->UOMLengthInMeters);
    1257             : 
    1258        3814 :         GTIFFreeMemory(pszUnitsName);
    1259             :     }
    1260             : 
    1261             :     /* ==================================================================== */
    1262             :     /*      Try to import PROJCS from ProjectedCSTypeGeoKey if we           */
    1263             :     /*      have essentially only it. We could relax a bit the constraints  */
    1264             :     /*      but that should do for now. This may mask shortcomings in the   */
    1265             :     /*      libgeotiff GTIFGetDefn() function.                              */
    1266             :     /* ==================================================================== */
    1267        8179 :     unsigned short tmp = 0;
    1268        8179 :     bool bGotFromEPSG = false;
    1269        3821 :     if (psDefn->Model == ModelTypeProjected && psDefn->PCS != KvUserDefined &&
    1270        3722 :         GDALGTIFKeyGetSHORT(hGTIF, ProjectionGeoKey, &tmp, 0, 1) == 0 &&
    1271        3721 :         GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) == 0 &&
    1272        3721 :         GDALGTIFKeyGetSHORT(hGTIF, GeographicTypeGeoKey, &tmp, 0, 1) == 0 &&
    1273        3625 :         GDALGTIFKeyGetSHORT(hGTIF, GeogGeodeticDatumGeoKey, &tmp, 0, 1) == 0 &&
    1274       15623 :         GDALGTIFKeyGetSHORT(hGTIF, GeogEllipsoidGeoKey, &tmp, 0, 1) == 0 &&
    1275        3623 :         CPLTestBool(CPLGetConfigOption("GTIFF_IMPORT_FROM_EPSG", "YES")))
    1276             :     {
    1277             :         // Save error state as importFromEPSGA() will call CPLReset()
    1278        3623 :         CPLErrorNum errNo = CPLGetLastErrorNo();
    1279        3623 :         CPLErr eErr = CPLGetLastErrorType();
    1280        3623 :         const char *pszTmp = CPLGetLastErrorMsg();
    1281        3623 :         char *pszLastErrorMsg = CPLStrdup(pszTmp ? pszTmp : "");
    1282        3623 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1283        7246 :         OGRSpatialReference oSRSTmp;
    1284        3623 :         OGRErr eImportErr = oSRSTmp.importFromEPSG(psDefn->PCS);
    1285        3623 :         CPLPopErrorHandler();
    1286             :         // Restore error state
    1287        3623 :         CPLErrorSetState(eErr, errNo, pszLastErrorMsg);
    1288        3623 :         CPLFree(pszLastErrorMsg);
    1289        3623 :         bGotFromEPSG = eImportErr == OGRERR_NONE;
    1290             : 
    1291        3623 :         if (bGotFromEPSG)
    1292             :         {
    1293             :             // See #6210. In case there's an overridden linear units, take it
    1294             :             // into account
    1295        3623 :             const char *pszUnitsName = nullptr;
    1296        3623 :             double dfUOMLengthInMeters = oSRS.GetLinearUnits(&pszUnitsName);
    1297             :             // Non exact comparison, as there's a slight difference between
    1298             :             // the evaluation of US Survey foot hardcoded in geo_normalize.c to
    1299             :             // 12.0 / 39.37, and the corresponding value returned by
    1300             :             // PROJ >= 6.0.0 and <= 7.0.0 for EPSG:9003
    1301        3623 :             if (fabs(dfUOMLengthInMeters - oSRSTmp.GetLinearUnits(nullptr)) >
    1302        3623 :                 1e-15 * dfUOMLengthInMeters)
    1303             :             {
    1304           2 :                 CPLDebug("GTiff", "Modify EPSG:%d to have %s linear units...",
    1305           4 :                          psDefn->PCS, pszUnitsName ? pszUnitsName : "unknown");
    1306             : 
    1307             :                 const char *pszUnitAuthorityCode =
    1308           2 :                     oSRS.GetAuthorityCode("PROJCS|UNIT");
    1309             :                 const char *pszUnitAuthorityName =
    1310           2 :                     oSRS.GetAuthorityName("PROJCS|UNIT");
    1311             : 
    1312           2 :                 if (pszUnitsName)
    1313           2 :                     oSRSTmp.SetLinearUnitsAndUpdateParameters(
    1314             :                         pszUnitsName, dfUOMLengthInMeters, pszUnitAuthorityCode,
    1315             :                         pszUnitAuthorityName);
    1316             :             }
    1317             : 
    1318        3623 :             if (bGeog3DCRS)
    1319             :             {
    1320           1 :                 oSRSTmp.CopyGeogCSFrom(&oSRS);
    1321           1 :                 oSRSTmp.UpdateCoordinateSystemFromGeogCRS();
    1322             :             }
    1323        3623 :             oSRS = std::move(oSRSTmp);
    1324             :         }
    1325             :     }
    1326             : 
    1327             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    1328        8202 :     if (psDefn->TOWGS84Count > 0 && bGotFromEPSG &&
    1329          23 :         CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
    1330             :     {
    1331          20 :         CPLDebug("OSR", "TOWGS84 information has been removed. "
    1332             :                         "It can be kept by setting the OSR_STRIP_TOWGS84 "
    1333             :                         "configuration option to NO");
    1334             :     }
    1335        8179 :     else if (psDefn->TOWGS84Count > 0 &&
    1336          20 :              (!bGotFromEPSG ||
    1337           3 :               !CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES"))))
    1338             :     {
    1339          20 :         if (bGotFromEPSG)
    1340             :         {
    1341           3 :             double adfTOWGS84[7] = {0.0};
    1342           3 :             CPL_IGNORE_RET_VAL(oSRS.GetTOWGS84(adfTOWGS84));
    1343           3 :             bool bSame = true;
    1344           3 :             for (int i = 0; i < 7; i++)
    1345             :             {
    1346           3 :                 if (fabs(adfTOWGS84[i] - psDefn->TOWGS84[i]) > 1e-5)
    1347             :                 {
    1348           3 :                     bSame = false;
    1349           3 :                     break;
    1350             :                 }
    1351             :             }
    1352           3 :             if (!bSame)
    1353             :             {
    1354           3 :                 CPLDebug("GTiff",
    1355             :                          "Modify EPSG:%d to have "
    1356             :                          "TOWGS84=%f,%f,%f,%f,%f,%f,%f "
    1357             :                          "coming from GeogTOWGS84GeoKey, instead of "
    1358             :                          "%f,%f,%f,%f,%f,%f,%f coming from EPSG",
    1359           3 :                          psDefn->PCS, psDefn->TOWGS84[0], psDefn->TOWGS84[1],
    1360             :                          psDefn->TOWGS84[2], psDefn->TOWGS84[3],
    1361             :                          psDefn->TOWGS84[4], psDefn->TOWGS84[5],
    1362             :                          psDefn->TOWGS84[6], adfTOWGS84[0], adfTOWGS84[1],
    1363             :                          adfTOWGS84[2], adfTOWGS84[3], adfTOWGS84[4],
    1364             :                          adfTOWGS84[5], adfTOWGS84[6]);
    1365             :             }
    1366             :         }
    1367             : 
    1368          20 :         oSRS.SetTOWGS84(psDefn->TOWGS84[0], psDefn->TOWGS84[1],
    1369             :                         psDefn->TOWGS84[2], psDefn->TOWGS84[3],
    1370             :                         psDefn->TOWGS84[4], psDefn->TOWGS84[5],
    1371             :                         psDefn->TOWGS84[6]);
    1372             :     }
    1373             : #endif
    1374             : 
    1375             :     /* ==================================================================== */
    1376             :     /*      Handle projection parameters.                                   */
    1377             :     /* ==================================================================== */
    1378        8179 :     if (psDefn->Model == ModelTypeProjected && !bGotFromEPSG)
    1379             :     {
    1380             :         /* --------------------------------------------------------------------
    1381             :          */
    1382             :         /*      Make a local copy of params, and convert back into the */
    1383             :         /*      angular units of the GEOGCS and the linear units of the */
    1384             :         /*      projection. */
    1385             :         /* --------------------------------------------------------------------
    1386             :          */
    1387         198 :         double adfParam[10] = {0.0};
    1388         198 :         int i = 0;  // Used after for.
    1389             : 
    1390        1570 :         for (; i < std::min(10, psDefn->nParms); i++)
    1391        1372 :             adfParam[i] = psDefn->ProjParm[i];
    1392             : 
    1393         806 :         for (; i < 10; i++)
    1394         608 :             adfParam[i] = 0.0;
    1395             : 
    1396             : #if LIBGEOTIFF_VERSION <= 1730
    1397             :         // libgeotiff <= 1.7.3 is unfortunately inconsistent. When it synthesizes the
    1398             :         // projection parameters from the EPSG ProjectedCRS code, it returns
    1399             :         // them normalized in degrees. But when it gets them from
    1400             :         // ProjCoordTransGeoKey and other Proj....GeoKey's it return them in
    1401             :         // a raw way, that is in the units of GeogAngularUnitSizeGeoKey
    1402             :         // The below oSRS.SetXXXXX() methods assume the angular projection
    1403             :         // parameters to be in degrees, so convert them to degrees in that later case.
    1404             :         // From GDAL 3.0 to 3.9.0, we didn't do that conversion...
    1405             :         // And all versions of GDAL <= 3.9.0 when writing those geokeys, wrote
    1406             :         // them as degrees, hence this GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE
    1407             :         // config option that can be set to YES to avoid that conversion and
    1408             :         // assume that the angular parameters have been written as degree.
    1409             :         if (GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) &&
    1410             :             !CPLTestBool(CPLGetConfigOption(
    1411             :                 "GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE", "NO")))
    1412             :         {
    1413             :             adfParam[0] *= psDefn->UOMAngleInDegrees;
    1414             :             adfParam[1] *= psDefn->UOMAngleInDegrees;
    1415             :             adfParam[2] *= psDefn->UOMAngleInDegrees;
    1416             :             adfParam[3] *= psDefn->UOMAngleInDegrees;
    1417             :         }
    1418             : #else
    1419             :         // If GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE=YES (non-nominal case), undo
    1420             :         // the conversion to degrees, that has been done by libgeotiff > 1.7.3
    1421         198 :         if (GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) &&
    1422         201 :             psDefn->UOMAngleInDegrees != 0 && psDefn->UOMAngleInDegrees != 1 &&
    1423           3 :             CPLTestBool(CPLGetConfigOption(
    1424             :                 "GTIFF_READ_ANGULAR_PARAMS_IN_DEGREE", "NO")))
    1425             :         {
    1426           1 :             adfParam[0] /= psDefn->UOMAngleInDegrees;
    1427           1 :             adfParam[1] /= psDefn->UOMAngleInDegrees;
    1428           1 :             adfParam[2] /= psDefn->UOMAngleInDegrees;
    1429           1 :             adfParam[3] /= psDefn->UOMAngleInDegrees;
    1430             :         }
    1431             : #endif
    1432             : 
    1433             :         /* --------------------------------------------------------------------
    1434             :          */
    1435             :         /*      Translation the fundamental projection. */
    1436             :         /* --------------------------------------------------------------------
    1437             :          */
    1438         198 :         switch (psDefn->CTProjection)
    1439             :         {
    1440         109 :             case CT_TransverseMercator:
    1441         109 :                 oSRS.SetTM(adfParam[0], adfParam[1], adfParam[4], adfParam[5],
    1442             :                            adfParam[6]);
    1443         109 :                 break;
    1444             : 
    1445           0 :             case CT_TransvMercator_SouthOriented:
    1446           0 :                 oSRS.SetTMSO(adfParam[0], adfParam[1], adfParam[4], adfParam[5],
    1447             :                              adfParam[6]);
    1448           0 :                 break;
    1449             : 
    1450          15 :             case CT_Mercator:
    1451             :                 // If a lat_ts was specified use 2SP, otherwise use 1SP.
    1452          15 :                 if (psDefn->ProjParmId[2] == ProjStdParallel1GeoKey)
    1453             :                 {
    1454           3 :                     if (psDefn->ProjParmId[4] == ProjScaleAtNatOriginGeoKey)
    1455           2 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1456             :                                  "Mercator projection should not define "
    1457             :                                  "both StdParallel1 and ScaleAtNatOrigin.  "
    1458             :                                  "Using StdParallel1 and ignoring "
    1459             :                                  "ScaleAtNatOrigin.");
    1460           3 :                     oSRS.SetMercator2SP(adfParam[2], adfParam[0], adfParam[1],
    1461             :                                         adfParam[5], adfParam[6]);
    1462             :                 }
    1463             :                 else
    1464          12 :                     oSRS.SetMercator(adfParam[0], adfParam[1], adfParam[4],
    1465             :                                      adfParam[5], adfParam[6]);
    1466             : 
    1467             :                 // Override hack for google mercator.
    1468          15 :                 if (psDefn->Projection == 1024 || psDefn->Projection == 9841)
    1469             :                 {
    1470           7 :                     oSRS.SetExtension(
    1471             :                         "PROJCS", "PROJ4",
    1472             :                         "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 "
    1473             :                         "+lon_0=0.0 "
    1474             :                         "+x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null "
    1475             :                         "+wktext  +no_defs");  // TODO(schwehr): Why 2 spaces?
    1476             :                 }
    1477          15 :                 break;
    1478             : 
    1479           4 :             case CT_ObliqueStereographic:
    1480           4 :                 oSRS.SetOS(adfParam[0], adfParam[1], adfParam[4], adfParam[5],
    1481             :                            adfParam[6]);
    1482           4 :                 break;
    1483             : 
    1484           0 :             case CT_Stereographic:
    1485           0 :                 oSRS.SetStereographic(adfParam[0], adfParam[1], adfParam[4],
    1486             :                                       adfParam[5], adfParam[6]);
    1487           0 :                 break;
    1488             : 
    1489           1 :             case CT_ObliqueMercator:  // Hotine.
    1490           1 :                 oSRS.SetHOM(adfParam[0], adfParam[1], adfParam[2], adfParam[3],
    1491             :                             adfParam[4], adfParam[5], adfParam[6]);
    1492           1 :                 break;
    1493             : 
    1494           2 :             case CT_HotineObliqueMercatorAzimuthCenter:
    1495           2 :                 oSRS.SetHOMAC(adfParam[0], adfParam[1], adfParam[2],
    1496             :                               adfParam[3], adfParam[4], adfParam[5],
    1497             :                               adfParam[6]);
    1498           2 :                 break;
    1499             : 
    1500           0 :             case CT_ObliqueMercator_Laborde:
    1501           0 :                 oSRS.SetLOM(adfParam[0], adfParam[1], adfParam[2], adfParam[4],
    1502             :                             adfParam[5], adfParam[6]);
    1503           0 :                 break;
    1504             : 
    1505           1 :             case CT_EquidistantConic:
    1506           1 :                 oSRS.SetEC(adfParam[0], adfParam[1], adfParam[2], adfParam[3],
    1507             :                            adfParam[5], adfParam[6]);
    1508           1 :                 break;
    1509             : 
    1510           1 :             case CT_CassiniSoldner:
    1511           1 :                 oSRS.SetCS(adfParam[0], adfParam[1], adfParam[5], adfParam[6]);
    1512           1 :                 break;
    1513             : 
    1514           1 :             case CT_Polyconic:
    1515           1 :                 oSRS.SetPolyconic(adfParam[0], adfParam[1], adfParam[5],
    1516             :                                   adfParam[6]);
    1517           1 :                 break;
    1518             : 
    1519          16 :             case CT_AzimuthalEquidistant:
    1520          16 :                 oSRS.SetAE(adfParam[0], adfParam[1], adfParam[5], adfParam[6]);
    1521          16 :                 break;
    1522             : 
    1523           1 :             case CT_MillerCylindrical:
    1524           1 :                 oSRS.SetMC(adfParam[0], adfParam[1], adfParam[5], adfParam[6]);
    1525           1 :                 break;
    1526             : 
    1527           5 :             case CT_Equirectangular:
    1528           5 :                 oSRS.SetEquirectangular2(adfParam[0], adfParam[1], adfParam[2],
    1529             :                                          adfParam[5], adfParam[6]);
    1530           5 :                 break;
    1531             : 
    1532           1 :             case CT_Gnomonic:
    1533           1 :                 oSRS.SetGnomonic(adfParam[0], adfParam[1], adfParam[5],
    1534             :                                  adfParam[6]);
    1535           1 :                 break;
    1536             : 
    1537           2 :             case CT_LambertAzimEqualArea:
    1538           2 :                 oSRS.SetLAEA(adfParam[0], adfParam[1], adfParam[5],
    1539             :                              adfParam[6]);
    1540           2 :                 break;
    1541             : 
    1542           0 :             case CT_Orthographic:
    1543           0 :                 oSRS.SetOrthographic(adfParam[0], adfParam[1], adfParam[5],
    1544             :                                      adfParam[6]);
    1545           0 :                 break;
    1546             : 
    1547           1 :             case CT_Robinson:
    1548           1 :                 oSRS.SetRobinson(adfParam[1], adfParam[5], adfParam[6]);
    1549           1 :                 break;
    1550             : 
    1551           5 :             case CT_Sinusoidal:
    1552           5 :                 oSRS.SetSinusoidal(adfParam[1], adfParam[5], adfParam[6]);
    1553           5 :                 break;
    1554             : 
    1555           1 :             case CT_VanDerGrinten:
    1556           1 :                 oSRS.SetVDG(adfParam[1], adfParam[5], adfParam[6]);
    1557           1 :                 break;
    1558             : 
    1559           6 :             case CT_PolarStereographic:
    1560           6 :                 oSRS.SetPS(adfParam[0], adfParam[1], adfParam[4], adfParam[5],
    1561             :                            adfParam[6]);
    1562           6 :                 break;
    1563             : 
    1564          12 :             case CT_LambertConfConic_2SP:
    1565          12 :                 oSRS.SetLCC(adfParam[2], adfParam[3], adfParam[0], adfParam[1],
    1566             :                             adfParam[5], adfParam[6]);
    1567          12 :                 break;
    1568             : 
    1569           5 :             case CT_LambertConfConic_1SP:
    1570           5 :                 oSRS.SetLCC1SP(adfParam[0], adfParam[1], adfParam[4],
    1571             :                                adfParam[5], adfParam[6]);
    1572           5 :                 break;
    1573             : 
    1574           5 :             case CT_AlbersEqualArea:
    1575           5 :                 oSRS.SetACEA(adfParam[0], adfParam[1], adfParam[2], adfParam[3],
    1576             :                              adfParam[5], adfParam[6]);
    1577           5 :                 break;
    1578             : 
    1579           1 :             case CT_NewZealandMapGrid:
    1580           1 :                 oSRS.SetNZMG(adfParam[0], adfParam[1], adfParam[5],
    1581             :                              adfParam[6]);
    1582           1 :                 break;
    1583             : 
    1584           1 :             case CT_CylindricalEqualArea:
    1585           1 :                 oSRS.SetCEA(adfParam[0], adfParam[1], adfParam[5], adfParam[6]);
    1586           1 :                 break;
    1587           2 :             default:
    1588           2 :                 if (oSRS.IsProjected())
    1589             :                 {
    1590           2 :                     const char *pszName = oSRS.GetName();
    1591           4 :                     std::string osName(pszName ? pszName : "unnamed");
    1592           2 :                     oSRS.Clear();
    1593           2 :                     oSRS.SetLocalCS(osName.c_str());
    1594             :                 }
    1595           2 :                 break;
    1596             :         }
    1597             :     }
    1598             : 
    1599        8179 :     if (psDefn->Model == ModelTypeProjected && psDefn->PCS != KvUserDefined &&
    1600        3722 :         !bGotFromEPSG)
    1601             :     {
    1602         198 :         OGRSpatialReference oSRSTest(oSRS);
    1603         198 :         OGRSpatialReference oSRSTmp;
    1604             : 
    1605             :         const bool bPCSCodeValid =
    1606          99 :             oSRSTmp.importFromEPSG(psDefn->PCS) == OGRERR_NONE;
    1607          99 :         oSRSTmp.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1608             : 
    1609             :         // Force axis to avoid issues with SRS with northing, easting order
    1610          99 :         oSRSTest.SetAxes(nullptr, "X", OAO_East, "Y", OAO_North);
    1611          99 :         oSRSTmp.SetAxes(nullptr, "X", OAO_East, "Y", OAO_North);
    1612             : 
    1613             :         const std::string osGTiffSRSSource =
    1614         198 :             CPLGetConfigOption("GTIFF_SRS_SOURCE", "");
    1615          99 :         const char *const apszOptions[] = {
    1616             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    1617         295 :         if (!bHasWarnedInconsistentGeogCRSEPSG &&
    1618         100 :             !oSRSTmp.IsSame(&oSRS, apszOptions) &&
    1619           1 :             EQUAL(osGTiffSRSSource.c_str(), ""))
    1620             :         {
    1621             :             // See https://github.com/OSGeo/gdal/issues/5399
    1622             :             // where a file has inconsistent GeogSemiMinorAxisGeoKey /
    1623             :             // GeogInvFlatteningGeoKey values, which cause its datum to be
    1624             :             // considered as non-equivalent to the EPSG one.
    1625           0 :             CPLError(
    1626             :                 CE_Warning, CPLE_AppDefined,
    1627             :                 "The definition of projected CRS EPSG:%d got from GeoTIFF keys "
    1628             :                 "is not the same as the one from the EPSG registry, "
    1629             :                 "which may cause issues during reprojection operations. "
    1630             :                 "Set GTIFF_SRS_SOURCE configuration option to EPSG to "
    1631             :                 "use official parameters (overriding the ones from GeoTIFF "
    1632             :                 "keys), "
    1633             :                 "or to GEOKEYS to use custom values from GeoTIFF keys "
    1634             :                 "and drop the EPSG code.",
    1635           0 :                 psDefn->PCS);
    1636             :         }
    1637          99 :         if (EQUAL(osGTiffSRSSource.c_str(), "EPSG"))
    1638             :         {
    1639           1 :             oSRS = std::move(oSRSTmp);
    1640             :         }
    1641          98 :         else if (bPCSCodeValid && EQUAL(osGTiffSRSSource.c_str(), ""))
    1642             :         {
    1643          97 :             oSRS.SetAuthority(nullptr, "EPSG", psDefn->PCS);
    1644             :         }
    1645             :     }
    1646             : 
    1647        8179 :     const bool bIs2DProjCRS = oSRS.IsProjected() && oSRS.GetAxesCount() == 2;
    1648        8179 :     if (bIs2DProjCRS)
    1649             :     {
    1650        3818 :         const char *pszProjCRSName = oSRS.GetAttrValue("PROJCS");
    1651        3818 :         if (pszProjCRSName)
    1652             :         {
    1653             :             // Hack to be able to read properly what we have written for
    1654             :             // ESRI:102113 (ESRI ancient WebMercator).
    1655        3818 :             if (EQUAL(pszProjCRSName, "WGS_1984_Web_Mercator"))
    1656           2 :                 oSRS.SetFromUserInput("ESRI:102113");
    1657             :             // And for EPSG:900913
    1658        3816 :             else if (EQUAL(pszProjCRSName, "Google Maps Global Mercator"))
    1659           0 :                 oSRS.importFromEPSG(900913);
    1660        3816 :             else if (strchr(pszProjCRSName, '_'))
    1661             :             {
    1662             :                 // Morph from potential ESRI name
    1663          12 :                 char *pszOfficialName = GTIFGetEPSGOfficialName(
    1664             :                     hGTIF, PJ_TYPE_PROJECTED_CRS, pszProjCRSName);
    1665          12 :                 if (pszOfficialName)
    1666             :                 {
    1667           0 :                     oSRS.SetProjCS(pszOfficialName);
    1668           0 :                     CPLFree(pszOfficialName);
    1669             :                 }
    1670             :             }
    1671             :         }
    1672             :     }
    1673             : 
    1674        4361 :     if ((bIs2DProjCRS || oSRS.IsGeographic()) &&
    1675       12667 :         oSRS.GetAuthorityCode(nullptr) == nullptr &&
    1676         127 :         psDefn->UOMAngleInDegrees == 1.0)
    1677             :     {
    1678         130 :         const PJ_TYPE type = bIs2DProjCRS ? PJ_TYPE_PROJECTED_CRS
    1679          17 :                              : oSRS.GetAxesCount() == 2
    1680          17 :                                  ? PJ_TYPE_GEOGRAPHIC_2D_CRS
    1681         113 :                                  : PJ_TYPE_GEOGRAPHIC_3D_CRS;
    1682             :         // If the CRS has no known id, try to find one by the CRS name
    1683             :         // in the PROJ database, and validate equivalence of the
    1684             :         // definition from the GeoTIFF keys with the one from the
    1685             :         // PROJ database.
    1686         113 :         PJ *refCRS = GTIFGetOfficialCRS(hGTIF, type, "EPSG", oSRS.GetName());
    1687         113 :         if (!refCRS)
    1688          92 :             refCRS = GTIFGetOfficialCRS(hGTIF, type, nullptr, oSRS.GetName());
    1689         113 :         if (refCRS)
    1690             :         {
    1691             :             auto ctx = static_cast<PJ_CONTEXT *>(
    1692          25 :                 GTIFGetPROJContext(hGTIF, true, nullptr));
    1693          25 :             const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
    1694          25 :             auto crs = proj_create(ctx, oSRS.exportToWkt(apszOptions).c_str());
    1695          25 :             if (crs)
    1696             :             {
    1697          25 :                 if (proj_is_equivalent_to(
    1698             :                         crs, refCRS,
    1699          25 :                         PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS))
    1700             :                 {
    1701          13 :                     const char *pszAuthName = proj_get_id_auth_name(refCRS, 0);
    1702          13 :                     const char *pszCode = proj_get_id_code(refCRS, 0);
    1703          13 :                     if (pszAuthName && pszCode)
    1704             :                     {
    1705          26 :                         oSRS.SetFromUserInput(std::string(pszAuthName)
    1706          13 :                                                   .append(":")
    1707          13 :                                                   .append(pszCode)
    1708             :                                                   .c_str());
    1709             :                     }
    1710             :                 }
    1711             :                 else
    1712             :                 {
    1713          24 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1714          12 :                     auto refCrsFromWKT1 = proj_create(
    1715             :                         ctx, proj_as_wkt(ctx, refCRS, PJ_WKT1_GDAL, nullptr));
    1716          12 :                     if (refCrsFromWKT1)
    1717             :                     {
    1718          12 :                         if (proj_is_equivalent_to(
    1719             :                                 crs, refCrsFromWKT1,
    1720          12 :                                 PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS))
    1721             :                         {
    1722             :                             const char *pszAuthName =
    1723           1 :                                 proj_get_id_auth_name(refCrsFromWKT1, 0);
    1724             :                             const char *pszCode =
    1725           1 :                                 proj_get_id_code(refCrsFromWKT1, 0);
    1726           1 :                             if (pszAuthName && pszCode)
    1727             :                             {
    1728           2 :                                 oSRS.SetFromUserInput(std::string(pszAuthName)
    1729           1 :                                                           .append(":")
    1730           1 :                                                           .append(pszCode)
    1731             :                                                           .c_str());
    1732             :                             }
    1733             :                         }
    1734          12 :                         proj_destroy(refCrsFromWKT1);
    1735             :                     }
    1736             :                 }
    1737          25 :                 proj_destroy(crs);
    1738             :             }
    1739             : 
    1740          25 :             proj_destroy(refCRS);
    1741             :         }
    1742             :     }
    1743             : 
    1744             :     /* ==================================================================== */
    1745             :     /*      Handle vertical coordinate system information if we have it.    */
    1746             :     /* ==================================================================== */
    1747        8179 :     bool bNeedManualVertCS = false;
    1748        8179 :     char citation[2048] = {'\0'};
    1749             : 
    1750             :     // See https://github.com/OSGeo/gdal/pull/4197
    1751        8179 :     if (verticalCSType > KvUserDefined || verticalDatum > KvUserDefined ||
    1752        8179 :         verticalUnits > KvUserDefined)
    1753             :     {
    1754           1 :         CPLError(CE_Warning, CPLE_AppDefined,
    1755             :                  "At least one of VerticalCSTypeGeoKey, VerticalDatumGeoKey or "
    1756             :                  "VerticalUnitsGeoKey has a value in the private user range. "
    1757             :                  "Ignoring vertical information.");
    1758           1 :         verticalCSType = 0;
    1759           1 :         verticalDatum = 0;
    1760           1 :         verticalUnits = 0;
    1761             :     }
    1762             : 
    1763        8211 :     if ((verticalCSType != 0 || verticalDatum != 0 || verticalUnits != 0) &&
    1764          32 :         (oSRS.IsGeographic() || oSRS.IsProjected() || oSRS.IsLocal()))
    1765             :     {
    1766          64 :         std::string osVertCRSName;
    1767          32 :         if (GDALGTIFKeyGetASCII(hGTIF, VerticalCitationGeoKey, citation,
    1768          32 :                                 sizeof(citation)))
    1769             :         {
    1770           7 :             if (STARTS_WITH_CI(citation, "VCS Name = "))
    1771             :             {
    1772           1 :                 memmove(citation, citation + strlen("VCS Name = "),
    1773           1 :                         strlen(citation + strlen("VCS Name = ")) + 1);
    1774           1 :                 char *pszPipeChar = strchr(citation, '|');
    1775           1 :                 if (pszPipeChar)
    1776           1 :                     *pszPipeChar = '\0';
    1777           1 :                 osVertCRSName = citation;
    1778             :             }
    1779             :         }
    1780             : 
    1781          64 :         OGRSpatialReference oVertSRS;
    1782          32 :         bool bCanBuildCompoundCRS = oSRS.GetRoot() != nullptr;
    1783          32 :         if (verticalCSType != KvUserDefined && verticalCSType > 0)
    1784             :         {
    1785          40 :             if (!(oVertSRS.importFromEPSG(verticalCSType) == OGRERR_NONE &&
    1786          20 :                   oVertSRS.IsVertical()))
    1787             :             {
    1788           0 :                 bCanBuildCompoundCRS = false;
    1789             :             }
    1790             :             else
    1791             :             {
    1792          20 :                 osVertCRSName = oVertSRS.GetName();
    1793             :             }
    1794             :         }
    1795          32 :         if (osVertCRSName.empty())
    1796          11 :             osVertCRSName = "unknown";
    1797             : 
    1798          32 :         if (bCanBuildCompoundCRS)
    1799             :         {
    1800             :             const bool bHorizontalHasCode =
    1801          32 :                 oSRS.GetAuthorityCode(nullptr) != nullptr;
    1802          32 :             const char *pszHorizontalName = oSRS.GetName();
    1803             :             const std::string osHorizontalName(
    1804          64 :                 pszHorizontalName ? pszHorizontalName : "unnamed");
    1805             :             /* --------------------------------------------------------------------
    1806             :              */
    1807             :             /*      Promote to being a compound coordinate system. */
    1808             :             /* --------------------------------------------------------------------
    1809             :              */
    1810          32 :             OGR_SRSNode *poOldRoot = oSRS.GetRoot()->Clone();
    1811             : 
    1812          32 :             oSRS.Clear();
    1813             : 
    1814             :             /* --------------------------------------------------------------------
    1815             :              */
    1816             :             /*      Set COMPD_CS name. */
    1817             :             /* --------------------------------------------------------------------
    1818             :              */
    1819             :             char szCTString[512];
    1820          32 :             szCTString[0] = '\0';
    1821          32 :             if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
    1822          57 :                                     sizeof(szCTString)) &&
    1823          25 :                 strstr(szCTString, " = ") == nullptr)
    1824             :             {
    1825          25 :                 oSRS.SetNode("COMPD_CS", szCTString);
    1826             :             }
    1827             :             else
    1828             :             {
    1829           7 :                 oSRS.SetNode(
    1830             :                     "COMPD_CS",
    1831          14 :                     (osHorizontalName + " + " + osVertCRSName).c_str());
    1832             :             }
    1833             : 
    1834          32 :             oSRS.GetRoot()->AddChild(poOldRoot);
    1835             : 
    1836             :             /* --------------------------------------------------------------------
    1837             :              */
    1838             :             /*      If we have the vertical cs, try to look it up, and use the
    1839             :              */
    1840             :             /*      definition provided by that. */
    1841             :             /* --------------------------------------------------------------------
    1842             :              */
    1843          32 :             bNeedManualVertCS = true;
    1844             : 
    1845          32 :             if (!oVertSRS.IsEmpty())
    1846             :             {
    1847          20 :                 oSRS.GetRoot()->AddChild(oVertSRS.GetRoot()->Clone());
    1848          20 :                 bNeedManualVertCS = false;
    1849             : 
    1850             :                 // GeoTIFF doesn't store EPSG code of CompoundCRS, so
    1851             :                 // if we have an EPSG code for both the horizontal and vertical
    1852             :                 // parts, check if there's a known CompoundCRS associating
    1853             :                 // both
    1854          20 :                 if (bHorizontalHasCode && verticalCSType != KvUserDefined &&
    1855          20 :                     verticalCSType > 0)
    1856             :                 {
    1857          20 :                     const auto *poSRSMatch = oSRS.FindBestMatch(100);
    1858          20 :                     if (poSRSMatch)
    1859           3 :                         oSRS = *poSRSMatch;
    1860          20 :                     delete poSRSMatch;
    1861             :                 }
    1862             :             }
    1863             :         }
    1864             :     }
    1865             : 
    1866             :     /* -------------------------------------------------------------------- */
    1867             :     /*      Collect some information from the VerticalCS if not provided    */
    1868             :     /*      via geokeys.                                                    */
    1869             :     /* -------------------------------------------------------------------- */
    1870        8179 :     if (bNeedManualVertCS)
    1871             :     {
    1872          12 :         FillCompoundCRSWithManualVertCS(hGTIF, oSRS, citation, verticalDatum,
    1873             :                                         verticalUnits);
    1874             :     }
    1875             : 
    1876             :     // Hack for tiff_read.py:test_tiff_grads so as to normalize angular
    1877             :     // parameters to grad
    1878        8179 :     if (psDefn->UOMAngleInDegrees != 1.0)
    1879             :     {
    1880          21 :         char *pszWKT = nullptr;
    1881          21 :         const char *const apszOptions[] = {
    1882             :             "FORMAT=WKT1", "ADD_TOWGS84_ON_EXPORT_TO_WKT1=NO", nullptr};
    1883          21 :         if (oSRS.exportToWkt(&pszWKT, apszOptions) == OGRERR_NONE)
    1884             :         {
    1885          21 :             const char *const apszOptionsImport[] = {
    1886             : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
    1887             :                 "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
    1888             : #endif
    1889             :                 nullptr
    1890             :             };
    1891          21 :             oSRS.importFromWkt(pszWKT, apszOptionsImport);
    1892             :         }
    1893          21 :         CPLFree(pszWKT);
    1894             :     }
    1895             : 
    1896        8179 :     oSRS.StripTOWGS84IfKnownDatumAndAllowed();
    1897             : 
    1898        8179 :     double dfCoordinateEpoch = 0.0;
    1899        8179 :     if (GDALGTIFKeyGetDOUBLE(hGTIF, CoordinateEpochGeoKey, &dfCoordinateEpoch,
    1900        8179 :                              0, 1))
    1901             :     {
    1902           2 :         oSRS.SetCoordinateEpoch(dfCoordinateEpoch);
    1903             :     }
    1904             : 
    1905        8179 :     return OGRSpatialReference::ToHandle(oSRS.Clone());
    1906             : }
    1907             : 
    1908             : /************************************************************************/
    1909             : /*                          GTIFGetOGISDefn()                           */
    1910             : /************************************************************************/
    1911             : 
    1912           0 : char *GTIFGetOGISDefn(GTIF *hGTIF, GTIFDefn *psDefn)
    1913             : {
    1914           0 :     OGRSpatialReferenceH hSRS = GTIFGetOGISDefnAsOSR(hGTIF, psDefn);
    1915             : 
    1916           0 :     char *pszWKT = nullptr;
    1917           0 :     if (hSRS && OGRSpatialReference::FromHandle(hSRS)->exportToWkt(&pszWKT) ==
    1918             :                     OGRERR_NONE)
    1919             :     {
    1920           0 :         OSRDestroySpatialReference(hSRS);
    1921           0 :         return pszWKT;
    1922             :     }
    1923           0 :     CPLFree(pszWKT);
    1924           0 :     OSRDestroySpatialReference(hSRS);
    1925             : 
    1926           0 :     return nullptr;
    1927             : }
    1928             : 
    1929             : /************************************************************************/
    1930             : /*                     OGCDatumName2EPSGDatumCode()                     */
    1931             : /************************************************************************/
    1932             : 
    1933         511 : static int OGCDatumName2EPSGDatumCode(GTIF *psGTIF, const char *pszOGCName)
    1934             : 
    1935             : {
    1936         511 :     int nReturn = KvUserDefined;
    1937             : 
    1938             :     /* -------------------------------------------------------------------- */
    1939             :     /*      Do we know it as a built in?                                    */
    1940             :     /* -------------------------------------------------------------------- */
    1941         511 :     if (EQUAL(pszOGCName, "NAD27") ||
    1942         511 :         EQUAL(pszOGCName, "North_American_Datum_1927"))
    1943           0 :         return Datum_North_American_Datum_1927;
    1944         511 :     else if (EQUAL(pszOGCName, "NAD83") ||
    1945         511 :              EQUAL(pszOGCName, "North_American_Datum_1983"))
    1946           0 :         return Datum_North_American_Datum_1983;
    1947         511 :     else if (EQUAL(pszOGCName, "WGS84") || EQUAL(pszOGCName, "WGS_1984") ||
    1948          47 :              EQUAL(pszOGCName, "WGS 84"))
    1949         464 :         return Datum_WGS84;
    1950          47 :     else if (EQUAL(pszOGCName, "WGS72") || EQUAL(pszOGCName, "WGS_1972"))
    1951           0 :         return Datum_WGS72;
    1952             : 
    1953             :     /* Search in database */
    1954             :     auto ctx =
    1955          47 :         static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(psGTIF, true, nullptr));
    1956          47 :     const PJ_TYPE searchType = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
    1957             :     auto list =
    1958          47 :         proj_create_from_name(ctx, "EPSG", pszOGCName, &searchType, 1,
    1959             :                               /* approximateMatch = */ true, 10, nullptr);
    1960          47 :     if (list)
    1961             :     {
    1962          47 :         const auto listSize = proj_list_get_count(list);
    1963          60 :         for (int i = 0; nReturn == KvUserDefined && i < listSize; i++)
    1964             :         {
    1965          13 :             auto datum = proj_list_get(ctx, list, i);
    1966          13 :             if (datum)
    1967             :             {
    1968          13 :                 const char *pszDatumName = proj_get_name(datum);
    1969          13 :                 if (pszDatumName)
    1970             :                 {
    1971          13 :                     char *pszTmp = CPLStrdup(pszDatumName);
    1972          13 :                     WKTMassageDatum(&pszTmp);
    1973          13 :                     if (EQUAL(pszTmp, pszOGCName))
    1974             :                     {
    1975           3 :                         const char *pszCode = proj_get_id_code(datum, 0);
    1976           3 :                         if (pszCode)
    1977             :                         {
    1978           3 :                             nReturn = atoi(pszCode);
    1979             :                         }
    1980             :                     }
    1981          13 :                     CPLFree(pszTmp);
    1982             :                 }
    1983             :             }
    1984          13 :             proj_destroy(datum);
    1985             :         }
    1986          47 :         proj_list_destroy(list);
    1987             :     }
    1988             : 
    1989          47 :     return nReturn;
    1990             : }
    1991             : 
    1992             : /************************************************************************/
    1993             : /*                        GTIFSetFromOGISDefn()                         */
    1994             : /*                                                                      */
    1995             : /*      Write GeoTIFF projection tags from an OGC WKT definition.       */
    1996             : /************************************************************************/
    1997             : 
    1998           0 : int GTIFSetFromOGISDefn(GTIF *psGTIF, const char *pszOGCWKT)
    1999             : 
    2000             : {
    2001             :     /* -------------------------------------------------------------------- */
    2002             :     /*      Create an OGRSpatialReference object corresponding to the       */
    2003             :     /*      string.                                                         */
    2004             :     /* -------------------------------------------------------------------- */
    2005             : 
    2006           0 :     OGRSpatialReference oSRS;
    2007           0 :     if (oSRS.importFromWkt(pszOGCWKT) != OGRERR_NONE)
    2008             :     {
    2009           0 :         return FALSE;
    2010             :     }
    2011           0 :     return GTIFSetFromOGISDefnEx(psGTIF, OGRSpatialReference::ToHandle(&oSRS),
    2012           0 :                                  GEOTIFF_KEYS_STANDARD, GEOTIFF_VERSION_1_0);
    2013             : }
    2014             : 
    2015        3209 : int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS,
    2016             :                           GTIFFKeysFlavorEnum eFlavor,
    2017             :                           GeoTIFFVersionEnum eVersion)
    2018             : {
    2019        6418 :     std::map<geokey_t, std::string> oMapAsciiKeys;
    2020             : 
    2021        3209 :     GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, RasterPixelIsArea);
    2022             : 
    2023        3209 :     const OGRSpatialReference *poSRS = OGRSpatialReference::FromHandle(hSRS);
    2024             : 
    2025             :     /* -------------------------------------------------------------------- */
    2026             :     /*      Set version number.                                             */
    2027             :     /* -------------------------------------------------------------------- */
    2028        3209 :     if (eVersion == GEOTIFF_VERSION_AUTO)
    2029             :     {
    2030        5943 :         if (poSRS->IsCompound() ||
    2031        2965 :             (poSRS->IsGeographic() && poSRS->GetAxesCount() == 3))
    2032             :         {
    2033          15 :             eVersion = GEOTIFF_VERSION_1_1;
    2034             :         }
    2035             :         else
    2036             :         {
    2037        2963 :             eVersion = GEOTIFF_VERSION_1_0;
    2038             :         }
    2039             :     }
    2040        3209 :     CPLAssert(eVersion == GEOTIFF_VERSION_1_0 ||
    2041             :               eVersion == GEOTIFF_VERSION_1_1);
    2042        3209 :     if (eVersion >= GEOTIFF_VERSION_1_1)
    2043             :     {
    2044             : #if LIBGEOTIFF_VERSION >= 1600
    2045          18 :         GTIFSetVersionNumbers(psGTIF, GEOTIFF_SPEC_1_1_VERSION,
    2046             :                               GEOTIFF_SPEC_1_1_KEY_REVISION,
    2047             :                               GEOTIFF_SPEC_1_1_MINOR_REVISION);
    2048             : #else
    2049             :         CPLError(CE_Warning, CPLE_AppDefined,
    2050             :                  "Setting GeoTIFF 1.1 requires libgeotiff >= 1.6. Key values "
    2051             :                  "will be written as GeoTIFF 1.1, but the version number "
    2052             :                  "will be seen as 1.0, which might confuse GeoTIFF readers");
    2053             : #endif
    2054             :     }
    2055             : 
    2056             :     /* -------------------------------------------------------------------- */
    2057             :     /*      Get the ellipsoid definition.                                   */
    2058             :     /* -------------------------------------------------------------------- */
    2059        3209 :     short nSpheroid = KvUserDefined;
    2060             : 
    2061        4192 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID") != nullptr &&
    2062         983 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID"), "EPSG"))
    2063             :     {
    2064         983 :         nSpheroid = static_cast<short>(
    2065         983 :             atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM|SPHEROID")));
    2066             :     }
    2067        3825 :     else if (poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID") != nullptr &&
    2068        1599 :              EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID"), "EPSG"))
    2069             :     {
    2070        1599 :         nSpheroid = static_cast<short>(
    2071        1599 :             atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM|SPHEROID")));
    2072             :     }
    2073             : 
    2074        3209 :     OGRErr eErr = OGRERR_NONE;
    2075        3209 :     double dfSemiMajor = 0;
    2076        3209 :     double dfInvFlattening = 0;
    2077        3209 :     bool bHasEllipsoid = false;
    2078        3209 :     if (!poSRS->IsLocal())
    2079             :     {
    2080        3207 :         bHasEllipsoid = true;
    2081        3207 :         if (poSRS->IsCompound())
    2082             :         {
    2083          26 :             OGRSpatialReference oSRSTmp(*poSRS);
    2084          13 :             oSRSTmp.StripVertical();
    2085          13 :             bHasEllipsoid = CPL_TO_BOOL(!oSRSTmp.IsLocal());
    2086             :         }
    2087        3207 :         if (bHasEllipsoid)
    2088             :         {
    2089        3206 :             dfSemiMajor = poSRS->GetSemiMajor(&eErr);
    2090        3206 :             dfInvFlattening = poSRS->GetInvFlattening(&eErr);
    2091        3206 :             if (eErr != OGRERR_NONE)
    2092             :             {
    2093         111 :                 dfSemiMajor = 0.0;
    2094         111 :                 dfInvFlattening = 0.0;
    2095             :             }
    2096             :         }
    2097             :     }
    2098             : 
    2099             :     /* -------------------------------------------------------------------- */
    2100             :     /*      Get the Datum so we can special case a few PCS codes.           */
    2101             :     /* -------------------------------------------------------------------- */
    2102        3209 :     int nDatum = KvUserDefined;
    2103             : 
    2104        4195 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM") != nullptr &&
    2105         986 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM"), "EPSG"))
    2106         986 :         nDatum = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM"));
    2107        3821 :     else if (poSRS->GetAuthorityName("GEOGCS|DATUM") != nullptr &&
    2108        1598 :              EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM"), "EPSG"))
    2109        1598 :         nDatum = atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM"));
    2110         625 :     else if (poSRS->GetAttrValue("DATUM") != nullptr)
    2111             :         nDatum =
    2112         511 :             OGCDatumName2EPSGDatumCode(psGTIF, poSRS->GetAttrValue("DATUM"));
    2113             : 
    2114             :     /* -------------------------------------------------------------------- */
    2115             :     /*      Get the GCS if possible.                                        */
    2116             :     /* -------------------------------------------------------------------- */
    2117        3209 :     int nGCS = KvUserDefined;
    2118             : 
    2119        4169 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS") != nullptr &&
    2120         960 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS"), "EPSG"))
    2121         960 :         nGCS = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS"));
    2122        3847 :     else if (poSRS->GetAuthorityName("GEOGCS") != nullptr &&
    2123        1598 :              EQUAL(poSRS->GetAuthorityName("GEOGCS"), "EPSG"))
    2124        1597 :         nGCS = atoi(poSRS->GetAuthorityCode("GEOGCS"));
    2125             : 
    2126        3209 :     int nVerticalCSKeyValue = 0;
    2127        5274 :     bool hasEllipsoidHeight = !poSRS->IsCompound() && poSRS->IsGeographic() &&
    2128        2065 :                               poSRS->GetAxesCount() == 3;
    2129        3209 :     if (nGCS == 4937 && eVersion >= GEOTIFF_VERSION_1_1)
    2130             :     {
    2131             :         // Workaround a bug of PROJ 6.3.0
    2132             :         // See https://github.com/OSGeo/PROJ/pull/1880
    2133             :         // EPSG:4937 = ETRS89 3D
    2134           1 :         hasEllipsoidHeight = true;
    2135           1 :         nVerticalCSKeyValue = nGCS;
    2136           1 :         nGCS = 4258;  // ETRS89 2D
    2137             :     }
    2138        3208 :     else if (nGCS != KvUserDefined)
    2139             :     {
    2140        5112 :         OGRSpatialReference oGeogCRS;
    2141        2556 :         if (oGeogCRS.importFromEPSG(nGCS) == OGRERR_NONE &&
    2142        2556 :             oGeogCRS.IsGeographic() && oGeogCRS.GetAxesCount() == 3)
    2143             :         {
    2144           3 :             hasEllipsoidHeight = true;
    2145           3 :             if (eVersion >= GEOTIFF_VERSION_1_1)
    2146             :             {
    2147           2 :                 const auto candidate_nVerticalCSKeyValue = nGCS;
    2148           2 :                 nGCS = KvUserDefined;
    2149             : 
    2150             :                 // In case of a geographic 3D CRS, find the corresponding
    2151             :                 // geographic 2D CRS
    2152             :                 auto ctx = static_cast<PJ_CONTEXT *>(
    2153           2 :                     GTIFGetPROJContext(psGTIF, true, nullptr));
    2154           2 :                 const auto type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
    2155           2 :                 auto list = proj_create_from_name(
    2156             :                     ctx, "EPSG", oGeogCRS.GetName(), &type, 1,
    2157             :                     /* approximateMatch = */ false,
    2158             :                     1,  // result set limit size,
    2159             :                     nullptr);
    2160           2 :                 if (list && proj_list_get_count(list) == 1)
    2161             :                 {
    2162           2 :                     auto crs2D = proj_list_get(ctx, list, 0);
    2163           2 :                     if (crs2D)
    2164             :                     {
    2165           2 :                         const char *pszCode = proj_get_id_code(crs2D, 0);
    2166           2 :                         if (pszCode)
    2167             :                         {
    2168           2 :                             nVerticalCSKeyValue = candidate_nVerticalCSKeyValue;
    2169           2 :                             nGCS = atoi(pszCode);
    2170             :                         }
    2171           2 :                         proj_destroy(crs2D);
    2172             :                     }
    2173             :                 }
    2174           2 :                 proj_list_destroy(list);
    2175             :             }
    2176             :         }
    2177             :     }
    2178             : 
    2179             :     // Deprecated way of encoding ellipsoidal height
    2180        3209 :     if (hasEllipsoidHeight && nVerticalCSKeyValue == 0)
    2181             :     {
    2182           1 :         if (nGCS == 4979 || nDatum == 6326 || nSpheroid == 7030)
    2183             :         {
    2184           0 :             nVerticalCSKeyValue = 5030;  // WGS_84_ellipsoid
    2185           0 :             if (nGCS == 4979 || nDatum == 6326)
    2186             :             {
    2187           0 :                 nGCS = 4326;
    2188             :             }
    2189             :         }
    2190           1 :         else if (nDatum >= 6001 && nDatum <= 6033)
    2191             :         {
    2192           0 :             nVerticalCSKeyValue = nDatum - 1000;
    2193             :         }
    2194           1 :         else if (nSpheroid >= 7001 && nSpheroid <= 7033)
    2195             :         {
    2196           1 :             nVerticalCSKeyValue = nSpheroid - 2000;
    2197             :         }
    2198             :     }
    2199             : 
    2200        3209 :     if (nGCS > 32767)
    2201           0 :         nGCS = KvUserDefined;
    2202             : 
    2203             :     /* -------------------------------------------------------------------- */
    2204             :     /*      Get the linear units.                                           */
    2205             :     /* -------------------------------------------------------------------- */
    2206        3209 :     const char *pszLinearUOMNameTmp = nullptr;
    2207        3209 :     const double dfLinearUOM = poSRS->GetLinearUnits(&pszLinearUOMNameTmp);
    2208             :     const std::string osLinearUOMName(pszLinearUOMNameTmp ? pszLinearUOMNameTmp
    2209        6418 :                                                           : "");
    2210        3209 :     int nUOMLengthCode = 9001;  // Meters.
    2211             : 
    2212        3209 :     if (poSRS->GetAuthorityName("PROJCS|UNIT") != nullptr &&
    2213        4223 :         EQUAL(poSRS->GetAuthorityName("PROJCS|UNIT"), "EPSG") &&
    2214        1014 :         poSRS->GetAttrNode("PROJCS|UNIT") != poSRS->GetAttrNode("GEOGCS|UNIT"))
    2215        1014 :         nUOMLengthCode = atoi(poSRS->GetAuthorityCode("PROJCS|UNIT"));
    2216        4390 :     else if (EQUAL(osLinearUOMName.c_str(), SRS_UL_FOOT) ||
    2217        2195 :              fabs(dfLinearUOM - GTIFAtof(SRS_UL_FOOT_CONV)) < 0.0000001)
    2218           0 :         nUOMLengthCode = 9002;  // International foot.
    2219        4389 :     else if (EQUAL(osLinearUOMName.c_str(), SRS_UL_US_FOOT) ||
    2220        2194 :              std::abs(dfLinearUOM - GTIFAtof(SRS_UL_US_FOOT_CONV)) < 0.0000001)
    2221           2 :         nUOMLengthCode = 9003;  // US survey foot.
    2222        2193 :     else if (fabs(dfLinearUOM - 1.0) > 0.00000001)
    2223           2 :         nUOMLengthCode = KvUserDefined;
    2224             : 
    2225             :     /* -------------------------------------------------------------------- */
    2226             :     /*      Get some authority values.                                      */
    2227             :     /* -------------------------------------------------------------------- */
    2228        3209 :     int nPCS = KvUserDefined;
    2229             : 
    2230        4163 :     if (poSRS->GetAuthorityName("PROJCS") != nullptr &&
    2231         954 :         EQUAL(poSRS->GetAuthorityName("PROJCS"), "EPSG"))
    2232             :     {
    2233         952 :         nPCS = atoi(poSRS->GetAuthorityCode("PROJCS"));
    2234         952 :         if (nPCS > 32767)
    2235           0 :             nPCS = KvUserDefined;
    2236             :     }
    2237             : 
    2238             :     /* -------------------------------------------------------------------- */
    2239             :     /*      Handle the projection transformation.                           */
    2240             :     /* -------------------------------------------------------------------- */
    2241        3209 :     bool bWritePEString = false;
    2242        3209 :     bool bUnknownProjection = false;
    2243             : 
    2244        3209 :     const OGRSpatialReference *poSRSCompatibleOfWKT1 = poSRS;
    2245             : #if PROJ_AT_LEAST_VERSION(6, 3, 0)
    2246        6418 :     OGRSpatialReference oSRSCompatibleOfWKT1;
    2247        4225 :     if (poSRS->IsProjected() && !poSRS->IsCompound() &&
    2248        1016 :         poSRS->GetAxesCount() == 3)
    2249             :     {
    2250           0 :         oSRSCompatibleOfWKT1 = *poSRS;
    2251           0 :         oSRSCompatibleOfWKT1.DemoteTo2D(nullptr);
    2252           0 :         poSRSCompatibleOfWKT1 = &oSRSCompatibleOfWKT1;
    2253           0 :         bWritePEString = true;
    2254             :     }
    2255             : #endif
    2256             : 
    2257        3209 :     double dfAngUnitValue = 0;
    2258        3209 :     std::string osAngUnitName;
    2259        3209 :     if (bHasEllipsoid)
    2260             :     {
    2261        3206 :         const char *angUnitNameTmp = "";
    2262        3206 :         dfAngUnitValue = poSRS->GetAngularUnits(&angUnitNameTmp);
    2263        3206 :         osAngUnitName = angUnitNameTmp;
    2264             :     }
    2265             : 
    2266             :     // Convert angular projection parameters from its normalized value in degree
    2267             :     // to the units of GeogAngularUnitsGeoKey.
    2268             :     // Note: for GDAL <= 3.9.0, we always have written them in degrees !
    2269             :     // We can set GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE=YES to get that
    2270             :     // non-conformant behavior...
    2271         266 :     const auto ConvertAngularParam = [dfAngUnitValue](double dfValInDeg)
    2272             :     {
    2273         132 :         constexpr double DEG_TO_RAD = M_PI / 180.0;
    2274         132 :         return dfAngUnitValue != 0 &&
    2275         132 :                        std::fabs(dfAngUnitValue - DEG_TO_RAD) > 1e-10 &&
    2276           4 :                        !CPLTestBool(CPLGetConfigOption(
    2277             :                            "GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE", "NO"))
    2278         264 :                    ? dfValInDeg * DEG_TO_RAD / dfAngUnitValue
    2279         132 :                    : dfValInDeg;
    2280        3209 :     };
    2281             : 
    2282             :     const char *pszProjection =
    2283        3209 :         poSRSCompatibleOfWKT1->GetAttrValue("PROJECTION");
    2284        3209 :     if (nPCS != KvUserDefined)
    2285             :     {
    2286             :         // If ESRI_PE flavor is explicitly required, then for EPSG:3857
    2287             :         // we will have to write a completely non-standard definition
    2288             :         // that requires not setting GTModelTypeGeoKey to ProjectedCSTypeGeoKey.
    2289         952 :         if (eFlavor == GEOTIFF_KEYS_ESRI_PE && nPCS == 3857)
    2290             :         {
    2291           1 :             bWritePEString = true;
    2292             :         }
    2293             :         else
    2294             :         {
    2295         951 :             GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2296             :                        ModelTypeProjected);
    2297         951 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2298             :         }
    2299             :     }
    2300        2257 :     else if (poSRSCompatibleOfWKT1->IsGeocentric())
    2301             :     {
    2302           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2303             :                    ModelTypeGeocentric);
    2304             :     }
    2305        2255 :     else if (pszProjection == nullptr)
    2306             :     {
    2307        2184 :         if (poSRSCompatibleOfWKT1->IsGeographic())
    2308        2070 :             GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2309             :                        ModelTypeGeographic);
    2310             :         // Otherwise, presumably something like LOCAL_CS.
    2311             :     }
    2312          71 :     else if (EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
    2313             :     {
    2314           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2315             :                    ModelTypeProjected);
    2316           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2317           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2318             : 
    2319           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2320             :                    CT_AlbersEqualArea);
    2321             : 
    2322           3 :         GTIFKeySet(psGTIF, ProjStdParallelGeoKey, TYPE_DOUBLE, 1,
    2323             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2324             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2325             : 
    2326           3 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    2327             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2328             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    2329             : 
    2330           3 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2331             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2332             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2333             : 
    2334           3 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2335             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2336             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2337             : 
    2338           3 :         GTIFKeySet(
    2339             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2340             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2341             : 
    2342           3 :         GTIFKeySet(
    2343             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2344             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2345             :     }
    2346             : 
    2347          68 :     else if (poSRSCompatibleOfWKT1->GetUTMZone() != 0)
    2348             :     {
    2349          10 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2350             :                    ModelTypeProjected);
    2351             : 
    2352          10 :         int bNorth = 0;
    2353          10 :         const int nZone = poSRSCompatibleOfWKT1->GetUTMZone(&bNorth);
    2354             : 
    2355          10 :         if (nDatum == Datum_North_American_Datum_1983 && nZone >= 3 &&
    2356           1 :             nZone <= 22 && bNorth && nUOMLengthCode == 9001)
    2357             :         {
    2358           1 :             nPCS = 26900 + nZone;
    2359             : 
    2360           1 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2361             :         }
    2362           9 :         else if (nDatum == Datum_North_American_Datum_1927 && nZone >= 3 &&
    2363           1 :                  nZone <= 22 && bNorth && nUOMLengthCode == 9001)
    2364             :         {
    2365           1 :             nPCS = 26700 + nZone;
    2366             : 
    2367           1 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2368             :         }
    2369           8 :         else if (nDatum == Datum_WGS84 && nUOMLengthCode == 9001)
    2370             :         {
    2371           2 :             if (bNorth)
    2372           1 :                 nPCS = 32600 + nZone;
    2373             :             else
    2374           1 :                 nPCS = 32700 + nZone;
    2375             : 
    2376           2 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2377             :         }
    2378             :         else
    2379             :         {
    2380           6 :             const int nProjection = nZone + (bNorth ? 16000 : 16100);
    2381           6 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
    2382             :                        KvUserDefined);
    2383             : 
    2384           6 :             GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, nProjection);
    2385             :         }
    2386             :     }
    2387             : 
    2388          58 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
    2389             :     {
    2390           9 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2391             :                    ModelTypeProjected);
    2392           9 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2393           9 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2394             : 
    2395           9 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2396             :                    CT_TransverseMercator);
    2397             : 
    2398           9 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2399             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2400             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2401             : 
    2402           9 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2403             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2404             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2405             : 
    2406           9 :         GTIFKeySet(
    2407             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2408             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2409             : 
    2410           9 :         GTIFKeySet(
    2411             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2412             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2413             : 
    2414           9 :         GTIFKeySet(
    2415             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2416             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2417             :     }
    2418             : 
    2419          49 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED))
    2420             :     {
    2421           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2422             :                    ModelTypeProjected);
    2423           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2424           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2425             : 
    2426           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2427             :                    CT_TransvMercator_SouthOriented);
    2428             : 
    2429           0 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2430             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2431             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2432             : 
    2433           0 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2434             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2435             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2436             : 
    2437           0 :         GTIFKeySet(
    2438             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2439             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2440             : 
    2441           0 :         GTIFKeySet(
    2442             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2443             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2444             : 
    2445           0 :         GTIFKeySet(
    2446             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2447             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2448             :     }
    2449             : 
    2450          49 :     else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
    2451          48 :              EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
    2452             : 
    2453             :     {
    2454           5 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2455             :                    ModelTypeProjected);
    2456           5 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2457           5 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2458             : 
    2459           5 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Mercator);
    2460             : 
    2461           5 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2462             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2463             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2464             : 
    2465           5 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2466             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2467             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2468             : 
    2469           5 :         if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP))
    2470           1 :             GTIFKeySet(
    2471             :                 psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2472             :                 ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2473             :                     SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2474             :         else
    2475           4 :             GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2476             :                        poSRSCompatibleOfWKT1->GetNormProjParm(
    2477             :                            SRS_PP_SCALE_FACTOR, 1.0));
    2478             : 
    2479           5 :         GTIFKeySet(
    2480             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2481             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2482             : 
    2483           5 :         GTIFKeySet(
    2484             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2485             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2486             :     }
    2487             : 
    2488          44 :     else if (EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC))
    2489             :     {
    2490           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2491             :                    ModelTypeProjected);
    2492           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2493           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2494             : 
    2495           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2496             :                    CT_ObliqueStereographic);
    2497             : 
    2498           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2499             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2500             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2501             : 
    2502           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2503             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2504             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2505             : 
    2506           1 :         GTIFKeySet(
    2507             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2508             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2509             : 
    2510           1 :         GTIFKeySet(
    2511             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2512             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2513             : 
    2514           1 :         GTIFKeySet(
    2515             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2516             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2517             :     }
    2518             : 
    2519          43 :     else if (EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC))
    2520             :     {
    2521           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2522             :                    ModelTypeProjected);
    2523           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2524           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2525             : 
    2526           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2527             :                    CT_Stereographic);
    2528             : 
    2529           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2530             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2531             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2532             : 
    2533           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2534             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2535             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2536             : 
    2537           0 :         GTIFKeySet(
    2538             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2539             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2540             : 
    2541           0 :         GTIFKeySet(
    2542             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2543             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2544             : 
    2545           0 :         GTIFKeySet(
    2546             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2547             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2548             :     }
    2549             : 
    2550          43 :     else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
    2551             :     {
    2552           4 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2553             :                    ModelTypeProjected);
    2554           4 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2555           4 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2556             : 
    2557           4 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2558             :                    CT_PolarStereographic);
    2559             : 
    2560           4 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2561             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2562             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2563             : 
    2564           4 :         GTIFKeySet(psGTIF, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1,
    2565             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2566             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2567             : 
    2568           4 :         GTIFKeySet(
    2569             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2570             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2571             : 
    2572           4 :         GTIFKeySet(
    2573             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2574             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2575             : 
    2576           4 :         GTIFKeySet(
    2577             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2578             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2579             :     }
    2580             : 
    2581          39 :     else if (EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
    2582             :     {
    2583           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2584             :                    ModelTypeProjected);
    2585           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2586           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2587             : 
    2588           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2589             :                    CT_ObliqueMercator);
    2590             : 
    2591           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2592             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2593             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2594             : 
    2595           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2596             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2597             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2598             : 
    2599           1 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2600             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2601             : 
    2602           1 :         GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1,
    2603             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2604             :                        SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)));
    2605             : 
    2606           1 :         GTIFKeySet(
    2607             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2608             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2609             : 
    2610           1 :         GTIFKeySet(
    2611             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2612             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2613             : 
    2614           1 :         GTIFKeySet(
    2615             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2616             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2617             :     }
    2618             : 
    2619          38 :     else if (EQUAL(pszProjection,
    2620             :                    SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER))
    2621             :     {
    2622           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2623             :                    ModelTypeProjected);
    2624           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2625           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2626             : 
    2627           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2628             :                    CT_HotineObliqueMercatorAzimuthCenter);
    2629             : 
    2630           2 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2631             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2632             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2633             : 
    2634           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2635             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2636             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2637             : 
    2638           2 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2639             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2640             : 
    2641           2 :         GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1,
    2642             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2643             :                        SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)));
    2644             : 
    2645           2 :         GTIFKeySet(
    2646             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2647             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2648             : 
    2649           2 :         GTIFKeySet(
    2650             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2651             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2652             : 
    2653           2 :         GTIFKeySet(
    2654             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2655             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2656             :     }
    2657             : 
    2658          36 :     else if (EQUAL(pszProjection, "Laborde_Oblique_Mercator"))
    2659             :     {
    2660           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2661             :                    ModelTypeProjected);
    2662           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2663           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2664             : 
    2665           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2666             :                    CT_ObliqueMercator_Laborde);
    2667             : 
    2668           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2669             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2670             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2671             : 
    2672           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2673             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2674             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2675             : 
    2676           0 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2677             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2678             : 
    2679           0 :         GTIFKeySet(
    2680             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2681             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2682             : 
    2683           0 :         GTIFKeySet(
    2684             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2685             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2686             : 
    2687           0 :         GTIFKeySet(
    2688             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2689             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2690             :     }
    2691             : 
    2692          36 :     else if (EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER))
    2693             :     {
    2694           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2695             :                    ModelTypeProjected);
    2696           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2697           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2698             : 
    2699           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2700             :                    CT_CassiniSoldner);
    2701             : 
    2702           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2703             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2704             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2705             : 
    2706           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2707             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2708             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2709             : 
    2710           1 :         GTIFKeySet(
    2711             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2712             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2713             : 
    2714           1 :         GTIFKeySet(
    2715             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2716             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2717             :     }
    2718             : 
    2719          35 :     else if (EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC))
    2720             :     {
    2721           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2722             :                    ModelTypeProjected);
    2723           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2724           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2725             : 
    2726           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2727             :                    CT_EquidistantConic);
    2728             : 
    2729           1 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2730             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2731             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2732             : 
    2733           1 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    2734             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2735             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    2736             : 
    2737           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2738             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2739             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2740             : 
    2741           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2742             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2743             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2744             : 
    2745           1 :         GTIFKeySet(
    2746             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2747             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2748             : 
    2749           1 :         GTIFKeySet(
    2750             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2751             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2752             :     }
    2753             : 
    2754          34 :     else if (EQUAL(pszProjection, SRS_PT_POLYCONIC))
    2755             :     {
    2756           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2757             :                    ModelTypeProjected);
    2758           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2759           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2760             : 
    2761           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Polyconic);
    2762             : 
    2763           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2764             :                    poSRSCompatibleOfWKT1->GetNormProjParm(
    2765             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2766             : 
    2767           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2768             :                    poSRSCompatibleOfWKT1->GetNormProjParm(
    2769             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0));
    2770             : 
    2771           1 :         GTIFKeySet(
    2772             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2773             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2774             : 
    2775           1 :         GTIFKeySet(
    2776             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2777             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2778             : 
    2779           1 :         GTIFKeySet(
    2780             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2781             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2782             :     }
    2783             : 
    2784          33 :     else if (EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT))
    2785             :     {
    2786           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2787             :                    ModelTypeProjected);
    2788           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2789           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2790             : 
    2791           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2792             :                    CT_AzimuthalEquidistant);
    2793             : 
    2794           3 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2795             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2796             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2797             : 
    2798           3 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2799             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2800             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2801             : 
    2802           3 :         GTIFKeySet(
    2803             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2804             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2805             : 
    2806           3 :         GTIFKeySet(
    2807             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2808             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2809             :     }
    2810             : 
    2811          30 :     else if (EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL))
    2812             :     {
    2813           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2814             :                    ModelTypeProjected);
    2815           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2816           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2817             : 
    2818           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2819             :                    CT_MillerCylindrical);
    2820             : 
    2821           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2822             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2823             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2824             : 
    2825           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2826             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2827             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2828             : 
    2829           1 :         GTIFKeySet(
    2830             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2831             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2832             : 
    2833           1 :         GTIFKeySet(
    2834             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2835             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2836             :     }
    2837             : 
    2838          29 :     else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
    2839             :     {
    2840           9 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2841             :                    ModelTypeProjected);
    2842           9 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2843           9 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2844             : 
    2845           9 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2846             :                    CT_Equirectangular);
    2847             : 
    2848           9 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2849             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2850             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2851             : 
    2852           9 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2853             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2854             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2855             : 
    2856           9 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2857             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2858             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2859             : 
    2860           9 :         GTIFKeySet(
    2861             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2862             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2863             : 
    2864           9 :         GTIFKeySet(
    2865             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2866             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2867             :     }
    2868             : 
    2869          20 :     else if (EQUAL(pszProjection, SRS_PT_GNOMONIC))
    2870             :     {
    2871           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2872             :                    ModelTypeProjected);
    2873           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2874           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2875             : 
    2876           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Gnomonic);
    2877             : 
    2878           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2879             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2880             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2881             : 
    2882           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2883             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2884             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2885             : 
    2886           1 :         GTIFKeySet(
    2887             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2888             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2889             : 
    2890           1 :         GTIFKeySet(
    2891             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2892             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2893             :     }
    2894             : 
    2895          19 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
    2896             :     {
    2897           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2898             :                    ModelTypeProjected);
    2899           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2900           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2901             : 
    2902           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2903             :                    CT_LambertAzimEqualArea);
    2904             : 
    2905           2 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2906             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2907             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2908             : 
    2909           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2910             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2911             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2912             : 
    2913           2 :         GTIFKeySet(
    2914             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2915             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2916             : 
    2917           2 :         GTIFKeySet(
    2918             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2919             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2920             :     }
    2921             : 
    2922          17 :     else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
    2923             :     {
    2924           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2925             :                    ModelTypeProjected);
    2926           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2927           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2928             : 
    2929           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2930             :                    CT_Orthographic);
    2931             : 
    2932           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2933             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2934             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2935             : 
    2936           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2937             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2938             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2939             : 
    2940           0 :         GTIFKeySet(
    2941             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2942             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2943             : 
    2944           0 :         GTIFKeySet(
    2945             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2946             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2947             :     }
    2948             : 
    2949          17 :     else if (EQUAL(pszProjection, SRS_PT_NEW_ZEALAND_MAP_GRID))
    2950             :     {
    2951           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2952             :                    ModelTypeProjected);
    2953           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2954           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2955             : 
    2956           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2957             :                    CT_NewZealandMapGrid);
    2958             : 
    2959           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2960             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2961             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2962             : 
    2963           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2964             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2965             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2966             : 
    2967           1 :         GTIFKeySet(
    2968             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2969             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2970             : 
    2971           1 :         GTIFKeySet(
    2972             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2973             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2974             :     }
    2975             : 
    2976          16 :     else if (EQUAL(pszProjection, SRS_PT_ROBINSON))
    2977             :     {
    2978           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2979             :                    ModelTypeProjected);
    2980           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2981           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2982             : 
    2983           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Robinson);
    2984             : 
    2985           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2986             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2987             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2988             : 
    2989           1 :         GTIFKeySet(
    2990             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2991             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2992             : 
    2993           1 :         GTIFKeySet(
    2994             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2995             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2996             :     }
    2997             : 
    2998          15 :     else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
    2999             :     {
    3000           4 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3001             :                    ModelTypeProjected);
    3002           4 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3003           4 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3004             : 
    3005           4 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Sinusoidal);
    3006             : 
    3007           4 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    3008             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3009             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    3010             : 
    3011           4 :         GTIFKeySet(
    3012             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3013             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3014             : 
    3015           4 :         GTIFKeySet(
    3016             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3017             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3018             :     }
    3019             : 
    3020          11 :     else if (EQUAL(pszProjection, SRS_PT_VANDERGRINTEN))
    3021             :     {
    3022           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3023             :                    ModelTypeProjected);
    3024           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3025           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3026             : 
    3027           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3028             :                    CT_VanDerGrinten);
    3029             : 
    3030           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    3031             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3032             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3033             : 
    3034           2 :         GTIFKeySet(
    3035             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3036             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3037             : 
    3038           2 :         GTIFKeySet(
    3039             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3040             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3041             :     }
    3042             : 
    3043           9 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    3044             :     {
    3045           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3046             :                    ModelTypeProjected);
    3047           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3048           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3049             : 
    3050           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3051             :                    CT_LambertConfConic_2SP);
    3052             : 
    3053           2 :         GTIFKeySet(psGTIF, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1,
    3054             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3055             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    3056             : 
    3057           2 :         GTIFKeySet(psGTIF, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1,
    3058             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3059             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3060             : 
    3061           2 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    3062             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3063             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    3064             : 
    3065           2 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    3066             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3067             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    3068             : 
    3069           2 :         GTIFKeySet(
    3070             :             psGTIF, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1,
    3071             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3072             : 
    3073           2 :         GTIFKeySet(
    3074             :             psGTIF, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1,
    3075             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3076             :     }
    3077             : 
    3078           7 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
    3079             :     {
    3080           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3081             :                    ModelTypeProjected);
    3082           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3083           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3084             : 
    3085           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3086             :                    CT_LambertConfConic_1SP);
    3087             : 
    3088           3 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    3089             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3090             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    3091             : 
    3092           3 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    3093             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3094             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3095             : 
    3096           3 :         GTIFKeySet(
    3097             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    3098             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    3099             : 
    3100           3 :         GTIFKeySet(
    3101             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3102             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3103             : 
    3104           3 :         GTIFKeySet(
    3105             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3106             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3107             :     }
    3108             : 
    3109           4 :     else if (EQUAL(pszProjection, SRS_PT_CYLINDRICAL_EQUAL_AREA))
    3110             :     {
    3111           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3112             :                    ModelTypeProjected);
    3113           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3114           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3115             : 
    3116           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3117             :                    CT_CylindricalEqualArea);
    3118             : 
    3119           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    3120             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3121             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3122             : 
    3123           1 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    3124             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3125             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    3126             : 
    3127           1 :         GTIFKeySet(
    3128             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3129             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3130             : 
    3131           1 :         GTIFKeySet(
    3132             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3133             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3134             :     }
    3135             :     else
    3136             :     {
    3137           3 :         bWritePEString = true;
    3138           3 :         bUnknownProjection = true;
    3139             :     }
    3140             : 
    3141             :     // Note that VERTCS is an ESRI "spelling" of VERT_CS so we assume if
    3142             :     // we find it that we should try to treat this as a PE string.
    3143        6417 :     if (eFlavor == GEOTIFF_KEYS_ESRI_PE ||
    3144        3208 :         poSRS->GetAttrValue("VERTCS") != nullptr)
    3145             :     {
    3146           1 :         bWritePEString = true;
    3147             :     }
    3148             : 
    3149        3209 :     if (nPCS == KvUserDefined)
    3150             :     {
    3151             :         const char *pszPROJ4Ext =
    3152        2253 :             poSRS->GetExtension("PROJCS", "PROJ4", nullptr);
    3153        2253 :         if (pszPROJ4Ext &&
    3154           4 :             strstr(pszPROJ4Ext, "+proj=merc +a=6378137 +b=6378137"))
    3155             :         {
    3156           3 :             bWritePEString = true;
    3157             :         }
    3158             :     }
    3159             : 
    3160        3209 :     bWritePEString &=
    3161        3209 :         CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES"));
    3162             : 
    3163        3209 :     bool peStrStored = false;
    3164             : 
    3165        3209 :     if (bWritePEString)
    3166             :     {
    3167             :         // Anything we can't map, store as an ESRI PE string with a citation
    3168             :         // key.
    3169           7 :         char *pszPEString = nullptr;
    3170             :         // We cheat a bit, but if we have a custom_proj4, do not morph to ESRI
    3171             :         // so as to keep the EXTENSION PROJ4 node
    3172           7 :         const char *const apszOptionsDefault[] = {nullptr};
    3173           7 :         const char *const apszOptionsEsri[] = {"FORMAT=WKT1_ESRI", nullptr};
    3174           7 :         const char *const *papszOptions = apszOptionsDefault;
    3175          17 :         if (!(bUnknownProjection &&
    3176          14 :               poSRS->GetExtension("PROJCS", "PROJ4", nullptr) != nullptr) &&
    3177           7 :             !(poSRS->IsProjected() && !poSRS->IsCompound() &&
    3178           7 :               poSRS->GetAxesCount() == 3))
    3179             :         {
    3180           7 :             papszOptions = apszOptionsEsri;
    3181             :         }
    3182           7 :         poSRS->exportToWkt(&pszPEString, papszOptions);
    3183           7 :         const int peStrLen = static_cast<int>(strlen(pszPEString));
    3184           7 :         if (peStrLen > 0)
    3185             :         {
    3186             :             char *outPeStr = static_cast<char *>(
    3187           7 :                 CPLMalloc(peStrLen + strlen("ESRI PE String = ") + 1));
    3188           7 :             strcpy(outPeStr, "ESRI PE String = ");
    3189           7 :             strcat(outPeStr, pszPEString);
    3190           7 :             oMapAsciiKeys[PCSCitationGeoKey] = outPeStr;
    3191           7 :             peStrStored = true;
    3192           7 :             CPLFree(outPeStr);
    3193             :         }
    3194           7 :         CPLFree(pszPEString);
    3195           7 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3196             : 
    3197             :         // Not completely sure we really need to imitate ArcGIS to that point
    3198             :         // but that cannot hurt.
    3199           7 :         if (nPCS == 3857)
    3200             :         {
    3201           1 :             oMapAsciiKeys[GTCitationGeoKey] =
    3202           1 :                 "PCS Name = WGS_1984_Web_Mercator_Auxiliary_Sphere";
    3203           1 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, GCS_WGS_84);
    3204           1 :             GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3205             :                        6378137.0);
    3206           1 :             GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3207             :                        298.257223563);
    3208             :         }
    3209             :     }
    3210             : 
    3211             :     /* -------------------------------------------------------------------- */
    3212             :     /*      Is there a false easting/northing set?  If so, write out a      */
    3213             :     /*      special geokey tag to indicate that GDAL has written these      */
    3214             :     /*      with the proper interpretation of the linear units.             */
    3215             :     /* -------------------------------------------------------------------- */
    3216        3209 :     double dfFE = 0.0;
    3217        3209 :     double dfFN = 0.0;
    3218             : 
    3219        6400 :     if (eVersion == GEOTIFF_VERSION_1_0 &&
    3220        3191 :         (GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFE, 0, 1) ||
    3221        3135 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFN, 0, 1) ||
    3222        3135 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey, &dfFE, 0,
    3223        3133 :                               1) ||
    3224        3133 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey, &dfFN, 0,
    3225          58 :                               1)) &&
    3226        6400 :         (dfFE != 0.0 || dfFN != 0.0) && nUOMLengthCode != 9001)
    3227             :     {
    3228           2 :         GTIFKeySet(psGTIF, ProjLinearUnitsInterpCorrectGeoKey, TYPE_SHORT, 1,
    3229             :                    static_cast<short>(1));
    3230             :     }
    3231             : 
    3232             :     /* -------------------------------------------------------------------- */
    3233             :     /*      Write linear units information.                                 */
    3234             :     /* -------------------------------------------------------------------- */
    3235        3209 :     if (poSRS->IsGeocentric())
    3236             :     {
    3237           2 :         GTIFKeySet(psGTIF, GeogLinearUnitsGeoKey, TYPE_SHORT, 1,
    3238             :                    nUOMLengthCode);
    3239           2 :         if (nUOMLengthCode == KvUserDefined)
    3240           0 :             GTIFKeySet(psGTIF, GeogLinearUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3241             :                        dfLinearUOM);
    3242             :     }
    3243        4163 :     else if (!poSRS->IsGeographic() &&
    3244         956 :              (nPCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))
    3245             :     {
    3246        1129 :         GTIFKeySet(psGTIF, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
    3247             :                    nUOMLengthCode);
    3248        1129 :         if (nUOMLengthCode == KvUserDefined)
    3249           2 :             GTIFKeySet(psGTIF, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3250             :                        dfLinearUOM);
    3251             : 
    3252             :         // If linear units name is available and user defined, store it as
    3253             :         // citation.
    3254        1122 :         if (!peStrStored && nUOMLengthCode == KvUserDefined &&
    3255        2253 :             !osLinearUOMName.empty() &&
    3256           2 :             CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES")))
    3257             :         {
    3258           2 :             SetLinearUnitCitation(oMapAsciiKeys, osLinearUOMName.c_str());
    3259             :         }
    3260             :     }
    3261             : 
    3262             :     /* -------------------------------------------------------------------- */
    3263             :     /*      Write angular units.                                            */
    3264             :     /* -------------------------------------------------------------------- */
    3265             : 
    3266        3209 :     if (bHasEllipsoid &&
    3267        2557 :         (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))
    3268             :     {
    3269        3189 :         if (EQUAL(osAngUnitName.c_str(), "Degree"))
    3270        3178 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3271             :                        Angular_Degree);
    3272          11 :         else if (EQUAL(osAngUnitName.c_str(), "arc-second"))
    3273           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3274             :                        Angular_Arc_Second);
    3275          10 :         else if (EQUAL(osAngUnitName.c_str(), "arc-minute"))
    3276           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3277             :                        Angular_Arc_Minute);
    3278           9 :         else if (EQUAL(osAngUnitName.c_str(), "grad"))
    3279           4 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3280             :                        Angular_Grad);
    3281           5 :         else if (EQUAL(osAngUnitName.c_str(), "gon"))
    3282           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3283             :                        Angular_Gon);
    3284           4 :         else if (EQUAL(osAngUnitName.c_str(), "radian"))
    3285           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3286             :                        Angular_Radian);
    3287             :         // else if (EQUAL(osAngUnitName.c_str(), "microradian"))
    3288             :         //    GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3289             :         //               9109);
    3290             :         else
    3291             :         {
    3292             :             // GeogCitationGeoKey may be rewritten if the gcs is user defined.
    3293           3 :             oMapAsciiKeys[GeogCitationGeoKey] = osAngUnitName;
    3294           3 :             GTIFKeySet(psGTIF, GeogAngularUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3295             :                        dfAngUnitValue);
    3296             :         }
    3297             :     }
    3298             : 
    3299             :     /* -------------------------------------------------------------------- */
    3300             :     /*      Try to write a citation from the main coordinate system         */
    3301             :     /*      name.                                                           */
    3302             :     /* -------------------------------------------------------------------- */
    3303        6307 :     if (poSRS->GetName() != nullptr &&
    3304        3098 :         ((poSRS->IsProjected() &&
    3305         956 :           (nPCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)) ||
    3306        2083 :          poSRS->IsCompound() || poSRS->IsLocal() ||
    3307        2068 :          (poSRS->IsGeocentric() &&
    3308           0 :           (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))))
    3309             :     {
    3310        1032 :         if (!(bWritePEString && nPCS == 3857))
    3311             :         {
    3312        1031 :             oMapAsciiKeys[GTCitationGeoKey] = poSRS->GetName();
    3313             :         }
    3314             :     }
    3315             : 
    3316             :     /* -------------------------------------------------------------------- */
    3317             :     /*      Try to write a GCS citation.                                    */
    3318             :     /* -------------------------------------------------------------------- */
    3319        3209 :     if (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)
    3320             :     {
    3321        3192 :         const OGR_SRSNode *poGCS = poSRS->GetAttrNode("GEOGCS");
    3322             : 
    3323        3192 :         if (poGCS != nullptr && poGCS->GetChild(0) != nullptr)
    3324             :         {
    3325        3075 :             oMapAsciiKeys[GeogCitationGeoKey] = poGCS->GetChild(0)->GetValue();
    3326             :         }
    3327             :     }
    3328             : 
    3329             :     /* -------------------------------------------------------------------- */
    3330             :     /*      Try to identify the GCS/datum, scanning the EPSG datum file for */
    3331             :     /*      a match.                                                        */
    3332             :     /* -------------------------------------------------------------------- */
    3333        3209 :     if (nPCS == KvUserDefined)
    3334             :     {
    3335        2726 :         if (nGCS == KvUserDefined && poSRS->IsGeographic() &&
    3336         473 :             std::fabs(poSRS->GetAngularUnits() - CPLAtof(SRS_UA_DEGREE_CONV)) <
    3337             :                 1e-9)
    3338             :         {
    3339         466 :             if (nDatum == Datum_North_American_Datum_1927)
    3340           0 :                 nGCS = GCS_NAD27;
    3341         466 :             else if (nDatum == Datum_North_American_Datum_1983)
    3342           0 :                 nGCS = GCS_NAD83;
    3343         466 :             else if (nDatum == Datum_WGS84 || nDatum == DatumE_WGS84)
    3344         461 :                 nGCS = GCS_WGS_84;
    3345             :         }
    3346             : 
    3347        2253 :         if (nGCS != KvUserDefined)
    3348             :         {
    3349        2067 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, nGCS);
    3350             :         }
    3351         186 :         else if (nDatum != KvUserDefined)
    3352             :         {
    3353          29 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3354             :                        KvUserDefined);
    3355          29 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1, nDatum);
    3356             :         }
    3357         157 :         else if (nSpheroid != KvUserDefined)
    3358             :         {
    3359           2 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3360             :                        KvUserDefined);
    3361           2 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1,
    3362             :                        KvUserDefined);
    3363           2 :             GTIFKeySet(psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, nSpheroid);
    3364             :         }
    3365         155 :         else if (dfSemiMajor != 0.0)
    3366             :         {
    3367          41 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3368             :                        KvUserDefined);
    3369          41 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1,
    3370             :                        KvUserDefined);
    3371          41 :             GTIFKeySet(psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1,
    3372             :                        KvUserDefined);
    3373          41 :             GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3374             :                        dfSemiMajor);
    3375          41 :             if (dfInvFlattening == 0.0)
    3376          14 :                 GTIFKeySet(psGTIF, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
    3377             :                            dfSemiMajor);
    3378             :             else
    3379          27 :                 GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3380             :                            dfInvFlattening);
    3381             :         }
    3382         114 :         else if (poSRS->GetAttrValue("DATUM") != nullptr &&
    3383         114 :                  strstr(poSRS->GetAttrValue("DATUM"), "unknown") == nullptr &&
    3384           0 :                  strstr(poSRS->GetAttrValue("DATUM"), "unnamed") == nullptr)
    3385             : 
    3386             :         {
    3387           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3388             :                      "Couldn't translate `%s' to a GeoTIFF datum.",
    3389             :                      poSRS->GetAttrValue("DATUM"));
    3390             :         }
    3391             : 
    3392        2253 :         if (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)
    3393             :         {
    3394             :             // Always set InvFlattening if it is available.
    3395             :             // So that it doesn't need to calculate from SemiMinor.
    3396        2244 :             if (dfInvFlattening != 0.0)
    3397        2112 :                 GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3398             :                            dfInvFlattening);
    3399             :             // Always set SemiMajor to keep the precision and in case of
    3400             :             // editing.
    3401        2244 :             if (dfSemiMajor != 0.0)
    3402        2130 :                 GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3403             :                            dfSemiMajor);
    3404             : 
    3405        2430 :             if (nGCS == KvUserDefined &&
    3406         186 :                 CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES")))
    3407             :             {
    3408         186 :                 SetGeogCSCitation(psGTIF, oMapAsciiKeys, poSRS,
    3409             :                                   osAngUnitName.c_str(), nDatum, nSpheroid);
    3410             :             }
    3411             :         }
    3412             :     }
    3413             : 
    3414             : /* -------------------------------------------------------------------- */
    3415             : /*      Do we have TOWGS84 parameters?                                  */
    3416             : /* -------------------------------------------------------------------- */
    3417             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    3418        3209 :     double adfTOWGS84[7] = {0.0};
    3419             : 
    3420        6401 :     if ((nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0) &&
    3421        3192 :         poSRS->GetTOWGS84(adfTOWGS84) == OGRERR_NONE)
    3422             :     {
    3423             :         // If we are writing a SRS with a EPSG code, and that the EPSG code
    3424             :         // of the current SRS object and the one coming from the EPSG code
    3425             :         // are the same, then by default, do not write them.
    3426          23 :         bool bUseReferenceTOWGS84 = false;
    3427          23 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3428          23 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3429          23 :         if (pszAuthName && EQUAL(pszAuthName, "EPSG") && pszAuthCode)
    3430             :         {
    3431          24 :             CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3432          24 :             OGRSpatialReference oRefSRS;
    3433          12 :             double adfRefTOWGS84[7] = {0.0};
    3434          12 :             if (oRefSRS.importFromEPSG(atoi(pszAuthCode)) == OGRERR_NONE)
    3435             :             {
    3436          12 :                 oRefSRS.AddGuessedTOWGS84();
    3437          15 :                 if (oRefSRS.GetTOWGS84(adfRefTOWGS84) == OGRERR_NONE &&
    3438           3 :                     memcmp(adfRefTOWGS84, adfTOWGS84, sizeof(adfTOWGS84)) == 0)
    3439             :                 {
    3440           2 :                     bUseReferenceTOWGS84 = true;
    3441             :                 }
    3442             :             }
    3443             :         }
    3444             :         const char *pszWriteTOWGS84 =
    3445          23 :             CPLGetConfigOption("GTIFF_WRITE_TOWGS84", "AUTO");
    3446          23 :         if ((EQUAL(pszWriteTOWGS84, "YES") || EQUAL(pszWriteTOWGS84, "TRUE") ||
    3447          22 :              EQUAL(pszWriteTOWGS84, "ON")) ||
    3448          22 :             (!bUseReferenceTOWGS84 && EQUAL(pszWriteTOWGS84, "AUTO")))
    3449             :         {
    3450          22 :             if (adfTOWGS84[3] == 0.0 && adfTOWGS84[4] == 0.0 &&
    3451          18 :                 adfTOWGS84[5] == 0.0 && adfTOWGS84[6] == 0.0)
    3452             :             {
    3453          15 :                 if (nGCS == GCS_WGS_84 && adfTOWGS84[0] == 0.0 &&
    3454           9 :                     adfTOWGS84[1] == 0.0 && adfTOWGS84[2] == 0.0)
    3455             :                 {
    3456             :                     ;  // Do nothing.
    3457             :                 }
    3458             :                 else
    3459           6 :                     GTIFKeySet(psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 3,
    3460             :                                adfTOWGS84);
    3461             :             }
    3462             :             else
    3463           7 :                 GTIFKeySet(psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 7,
    3464             :                            adfTOWGS84);
    3465             :         }
    3466             :     }
    3467             : #endif
    3468             : 
    3469             :     /* -------------------------------------------------------------------- */
    3470             :     /*      Do we have vertical information to set?                         */
    3471             :     /* -------------------------------------------------------------------- */
    3472        3209 :     if (poSRS->GetAttrValue("COMPD_CS|VERT_CS") != nullptr)
    3473             :     {
    3474          13 :         bool bGotVertCSCode = false;
    3475          13 :         const char *pszVertCSCode = poSRS->GetAuthorityCode("COMPD_CS|VERT_CS");
    3476             :         const char *pszVertCSAuthName =
    3477          13 :             poSRS->GetAuthorityName("COMPD_CS|VERT_CS");
    3478          13 :         if (pszVertCSCode && pszVertCSAuthName && atoi(pszVertCSCode) &&
    3479          11 :             EQUAL(pszVertCSAuthName, "EPSG"))
    3480             :         {
    3481          10 :             bGotVertCSCode = true;
    3482          10 :             GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3483             :                        atoi(pszVertCSCode));
    3484             :         }
    3485           3 :         else if (eVersion >= GEOTIFF_VERSION_1_1)
    3486             :         {
    3487           3 :             GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3488             :                        KvUserDefined);
    3489             :         }
    3490             : 
    3491          13 :         if (eVersion == GEOTIFF_VERSION_1_0 || !bGotVertCSCode)
    3492             :         {
    3493           0 :             oMapAsciiKeys[VerticalCitationGeoKey] =
    3494           3 :                 poSRS->GetAttrValue("COMPD_CS|VERT_CS");
    3495             : 
    3496             :             const char *pszVertDatumCode =
    3497           3 :                 poSRS->GetAuthorityCode("COMPD_CS|VERT_CS|VERT_DATUM");
    3498             :             const char *pszVertDatumAuthName =
    3499           3 :                 poSRS->GetAuthorityName("COMPD_CS|VERT_CS|VERT_DATUM");
    3500           3 :             if (pszVertDatumCode && pszVertDatumAuthName &&
    3501           2 :                 atoi(pszVertDatumCode) && EQUAL(pszVertDatumAuthName, "EPSG"))
    3502             :             {
    3503           1 :                 GTIFKeySet(psGTIF, VerticalDatumGeoKey, TYPE_SHORT, 1,
    3504             :                            atoi(pszVertDatumCode));
    3505             :             }
    3506           2 :             else if (eVersion >= GEOTIFF_VERSION_1_1)
    3507             :             {
    3508           2 :                 GTIFKeySet(psGTIF, VerticalDatumGeoKey, TYPE_SHORT, 1,
    3509             :                            KvUserDefined);
    3510             :             }
    3511             : 
    3512             :             const char *pszVertUnitCode =
    3513           3 :                 poSRS->GetAuthorityCode("COMPD_CS|VERT_CS|UNIT");
    3514             :             const char *pszVertUnitAuthName =
    3515           3 :                 poSRS->GetAuthorityName("COMPD_CS|VERT_CS|UNIT");
    3516           3 :             if (pszVertUnitCode && pszVertUnitAuthName &&
    3517           3 :                 atoi(pszVertUnitCode) && EQUAL(pszVertUnitAuthName, "EPSG"))
    3518             :             {
    3519           3 :                 GTIFKeySet(psGTIF, VerticalUnitsGeoKey, TYPE_SHORT, 1,
    3520             :                            atoi(pszVertUnitCode));
    3521             :             }
    3522           0 :             else if (eVersion >= GEOTIFF_VERSION_1_1)
    3523             :             {
    3524           0 :                 GTIFKeySet(psGTIF, VerticalUnitsGeoKey, TYPE_SHORT, 1,
    3525             :                            KvUserDefined);
    3526             :             }
    3527             :         }
    3528             :     }
    3529        3196 :     else if (eVersion >= GEOTIFF_VERSION_1_1 && nVerticalCSKeyValue != 0)
    3530             :     {
    3531           3 :         GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3532             :                    nVerticalCSKeyValue);
    3533             :     }
    3534             : 
    3535        3209 :     const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
    3536        3209 :     if (dfCoordinateEpoch > 0)
    3537             :     {
    3538           2 :         GTIFKeySet(psGTIF, CoordinateEpochGeoKey, TYPE_DOUBLE, 1,
    3539             :                    dfCoordinateEpoch);
    3540             :     }
    3541             : 
    3542             :     /* -------------------------------------------------------------------- */
    3543             :     /*      Write all ascii keys                                            */
    3544             :     /* -------------------------------------------------------------------- */
    3545        7328 :     for (const auto &oIter : oMapAsciiKeys)
    3546             :     {
    3547        4119 :         GTIFKeySet(psGTIF, oIter.first, TYPE_ASCII, 0, oIter.second.c_str());
    3548             :     }
    3549             : 
    3550        6418 :     return TRUE;
    3551             : }
    3552             : 
    3553             : /************************************************************************/
    3554             : /*                         GTIFWktFromMemBuf()                          */
    3555             : /************************************************************************/
    3556             : 
    3557           0 : CPLErr GTIFWktFromMemBuf(int nSize, unsigned char *pabyBuffer, char **ppszWKT,
    3558             :                          double *padfGeoTransform, int *pnGCPCount,
    3559             :                          GDAL_GCP **ppasGCPList)
    3560             : {
    3561           0 :     OGRSpatialReferenceH hSRS = nullptr;
    3562           0 :     if (ppszWKT)
    3563           0 :         *ppszWKT = nullptr;
    3564             :     CPLErr eErr =
    3565           0 :         GTIFWktFromMemBufEx(nSize, pabyBuffer, &hSRS, padfGeoTransform,
    3566             :                             pnGCPCount, ppasGCPList, nullptr, nullptr);
    3567           0 :     if (eErr == CE_None)
    3568             :     {
    3569           0 :         if (hSRS && ppszWKT)
    3570             :         {
    3571           0 :             OSRExportToWkt(hSRS, ppszWKT);
    3572             :         }
    3573             :     }
    3574           0 :     OSRDestroySpatialReference(hSRS);
    3575           0 :     return eErr;
    3576             : }
    3577             : 
    3578         580 : CPLErr GTIFWktFromMemBufEx(int nSize, unsigned char *pabyBuffer,
    3579             :                            OGRSpatialReferenceH *phSRS,
    3580             :                            double *padfGeoTransform, int *pnGCPCount,
    3581             :                            GDAL_GCP **ppasGCPList, int *pbPixelIsPoint,
    3582             :                            char ***ppapszRPCMD)
    3583             : 
    3584             : {
    3585             :     const std::string osFilename(
    3586        1160 :         VSIMemGenerateHiddenFilename("wkt_from_mem_buf.tif"));
    3587             : 
    3588             :     /* -------------------------------------------------------------------- */
    3589             :     /*      Initialization of libtiff and libgeotiff.                       */
    3590             :     /* -------------------------------------------------------------------- */
    3591         580 :     GTiffOneTimeInit();  // For RPC tag.
    3592         580 :     LibgeotiffOneTimeInit();
    3593             : 
    3594             :     /* -------------------------------------------------------------------- */
    3595             :     /*      Create a memory file from the buffer.                           */
    3596             :     /* -------------------------------------------------------------------- */
    3597             :     VSILFILE *fp =
    3598         580 :         VSIFileFromMemBuffer(osFilename.c_str(), pabyBuffer, nSize, FALSE);
    3599         580 :     if (fp == nullptr)
    3600           0 :         return CE_Failure;
    3601             : 
    3602             :     /* -------------------------------------------------------------------- */
    3603             :     /*      Initialize access to the memory geotiff structure.              */
    3604             :     /* -------------------------------------------------------------------- */
    3605         580 :     TIFF *hTIFF = VSI_TIFFOpen(osFilename.c_str(), "rc", fp);
    3606             : 
    3607         580 :     if (hTIFF == nullptr)
    3608             :     {
    3609           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3610             :                  "TIFF/GeoTIFF structure is corrupt.");
    3611           0 :         VSIUnlink(osFilename.c_str());
    3612           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    3613           0 :         return CE_Failure;
    3614             :     }
    3615             : 
    3616             :     /* -------------------------------------------------------------------- */
    3617             :     /*      Get the projection definition.                                  */
    3618             :     /* -------------------------------------------------------------------- */
    3619         580 :     bool bPixelIsPoint = false;
    3620         580 :     bool bPointGeoIgnore = false;
    3621         580 :     unsigned short nRasterType = 0;
    3622             : 
    3623         580 :     GTIF *hGTIF = GTIFNew(hTIFF);
    3624         580 :     if (hGTIF)
    3625         580 :         GTIFAttachPROJContext(hGTIF, OSRGetProjTLSContext());
    3626             : 
    3627        1160 :     if (hGTIF != nullptr &&
    3628         580 :         GDALGTIFKeyGetSHORT(hGTIF, GTRasterTypeGeoKey, &nRasterType, 0, 1) ==
    3629        1160 :             1 &&
    3630         579 :         nRasterType == static_cast<unsigned short>(RasterPixelIsPoint))
    3631             :     {
    3632           5 :         bPixelIsPoint = true;
    3633             :         bPointGeoIgnore =
    3634           5 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3635             :     }
    3636         580 :     if (pbPixelIsPoint)
    3637         580 :         *pbPixelIsPoint = bPixelIsPoint;
    3638         580 :     if (ppapszRPCMD)
    3639         580 :         *ppapszRPCMD = nullptr;
    3640             : 
    3641         580 :     if (phSRS)
    3642             :     {
    3643         580 :         *phSRS = nullptr;
    3644         580 :         if (hGTIF != nullptr)
    3645             :         {
    3646         580 :             GTIFDefn *psGTIFDefn = GTIFAllocDefn();
    3647         580 :             if (GTIFGetDefn(hGTIF, psGTIFDefn))
    3648             :             {
    3649         579 :                 *phSRS = GTIFGetOGISDefnAsOSR(hGTIF, psGTIFDefn);
    3650             :             }
    3651         580 :             GTIFFreeDefn(psGTIFDefn);
    3652             :         }
    3653             :     }
    3654         580 :     if (hGTIF)
    3655         580 :         GTIFFree(hGTIF);
    3656             : 
    3657             :     /* -------------------------------------------------------------------- */
    3658             :     /*      Get geotransform or tiepoints.                                  */
    3659             :     /* -------------------------------------------------------------------- */
    3660         580 :     double *padfTiePoints = nullptr;
    3661         580 :     double *padfScale = nullptr;
    3662         580 :     double *padfMatrix = nullptr;
    3663         580 :     int16_t nCount = 0;
    3664             : 
    3665         580 :     padfGeoTransform[0] = 0.0;
    3666         580 :     padfGeoTransform[1] = 1.0;
    3667         580 :     padfGeoTransform[2] = 0.0;
    3668         580 :     padfGeoTransform[3] = 0.0;
    3669         580 :     padfGeoTransform[4] = 0.0;
    3670         580 :     padfGeoTransform[5] = 1.0;
    3671             : 
    3672         580 :     *pnGCPCount = 0;
    3673         580 :     *ppasGCPList = nullptr;
    3674             : 
    3675        1121 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOPIXELSCALE, &nCount, &padfScale) &&
    3676         541 :         nCount >= 2)
    3677             :     {
    3678         541 :         padfGeoTransform[1] = padfScale[0];
    3679         541 :         padfGeoTransform[5] = -std::abs(padfScale[1]);
    3680             : 
    3681         541 :         if (TIFFGetField(hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
    3682        1082 :                          &padfTiePoints) &&
    3683         541 :             nCount >= 6)
    3684             :         {
    3685         541 :             padfGeoTransform[0] =
    3686         541 :                 padfTiePoints[3] - padfTiePoints[0] * padfGeoTransform[1];
    3687         541 :             padfGeoTransform[3] =
    3688         541 :                 padfTiePoints[4] - padfTiePoints[1] * padfGeoTransform[5];
    3689             : 
    3690             :             // Adjust for pixel is point in transform.
    3691         541 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3692             :             {
    3693           4 :                 padfGeoTransform[0] -=
    3694           4 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3695           4 :                 padfGeoTransform[3] -=
    3696           4 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3697             :             }
    3698             :         }
    3699             :     }
    3700          39 :     else if (TIFFGetField(hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
    3701          52 :                           &padfTiePoints) &&
    3702          13 :              nCount >= 6)
    3703             :     {
    3704          13 :         *pnGCPCount = nCount / 6;
    3705          13 :         *ppasGCPList =
    3706          13 :             static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), *pnGCPCount));
    3707             : 
    3708          48 :         for (int iGCP = 0; iGCP < *pnGCPCount; iGCP++)
    3709             :         {
    3710          35 :             char szID[32] = {};
    3711          35 :             GDAL_GCP *psGCP = *ppasGCPList + iGCP;
    3712             : 
    3713          35 :             snprintf(szID, sizeof(szID), "%d", iGCP + 1);
    3714          35 :             psGCP->pszId = CPLStrdup(szID);
    3715          35 :             psGCP->pszInfo = CPLStrdup("");
    3716          35 :             psGCP->dfGCPPixel = padfTiePoints[iGCP * 6 + 0];
    3717          35 :             psGCP->dfGCPLine = padfTiePoints[iGCP * 6 + 1];
    3718          35 :             psGCP->dfGCPX = padfTiePoints[iGCP * 6 + 3];
    3719          35 :             psGCP->dfGCPY = padfTiePoints[iGCP * 6 + 4];
    3720          35 :             psGCP->dfGCPZ = padfTiePoints[iGCP * 6 + 5];
    3721             :         }
    3722             :     }
    3723          26 :     else if (TIFFGetField(hTIFF, TIFFTAG_GEOTRANSMATRIX, &nCount,
    3724          33 :                           &padfMatrix) &&
    3725           7 :              nCount == 16)
    3726             :     {
    3727           7 :         padfGeoTransform[0] = padfMatrix[3];
    3728           7 :         padfGeoTransform[1] = padfMatrix[0];
    3729           7 :         padfGeoTransform[2] = padfMatrix[1];
    3730           7 :         padfGeoTransform[3] = padfMatrix[7];
    3731           7 :         padfGeoTransform[4] = padfMatrix[4];
    3732           7 :         padfGeoTransform[5] = padfMatrix[5];
    3733             :     }
    3734             : 
    3735             :     /* -------------------------------------------------------------------- */
    3736             :     /*      Read RPC                                                        */
    3737             :     /* -------------------------------------------------------------------- */
    3738         580 :     if (ppapszRPCMD != nullptr)
    3739             :     {
    3740         580 :         *ppapszRPCMD = GTiffDatasetReadRPCTag(hTIFF);
    3741             :     }
    3742             : 
    3743             :     /* -------------------------------------------------------------------- */
    3744             :     /*      Cleanup.                                                        */
    3745             :     /* -------------------------------------------------------------------- */
    3746         580 :     XTIFFClose(hTIFF);
    3747         580 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    3748             : 
    3749         580 :     VSIUnlink(osFilename.c_str());
    3750             : 
    3751         580 :     if (phSRS && *phSRS == nullptr)
    3752           1 :         return CE_Failure;
    3753             : 
    3754         579 :     return CE_None;
    3755             : }
    3756             : 
    3757             : /************************************************************************/
    3758             : /*                         GTIFMemBufFromWkt()                          */
    3759             : /************************************************************************/
    3760             : 
    3761           0 : CPLErr GTIFMemBufFromWkt(const char *pszWKT, const double *padfGeoTransform,
    3762             :                          int nGCPCount, const GDAL_GCP *pasGCPList, int *pnSize,
    3763             :                          unsigned char **ppabyBuffer)
    3764             : {
    3765           0 :     OGRSpatialReference oSRS;
    3766           0 :     if (pszWKT != nullptr)
    3767           0 :         oSRS.importFromWkt(pszWKT);
    3768           0 :     return GTIFMemBufFromSRS(OGRSpatialReference::ToHandle(&oSRS),
    3769             :                              padfGeoTransform, nGCPCount, pasGCPList, pnSize,
    3770           0 :                              ppabyBuffer, FALSE, nullptr);
    3771             : }
    3772             : 
    3773         225 : CPLErr GTIFMemBufFromSRS(OGRSpatialReferenceH hSRS,
    3774             :                          const double *padfGeoTransform, int nGCPCount,
    3775             :                          const GDAL_GCP *pasGCPList, int *pnSize,
    3776             :                          unsigned char **ppabyBuffer, int bPixelIsPoint,
    3777             :                          char **papszRPCMD)
    3778             : 
    3779             : {
    3780             :     const std::string osFilename(
    3781         450 :         VSIMemGenerateHiddenFilename("wkt_from_mem_buf.tif"));
    3782             : 
    3783             :     /* -------------------------------------------------------------------- */
    3784             :     /*      Initialization of libtiff and libgeotiff.                       */
    3785             :     /* -------------------------------------------------------------------- */
    3786         225 :     GTiffOneTimeInit();  // For RPC tag.
    3787         225 :     LibgeotiffOneTimeInit();
    3788             : 
    3789             :     /* -------------------------------------------------------------------- */
    3790             :     /*      Initialize access to the memory geotiff structure.              */
    3791             :     /* -------------------------------------------------------------------- */
    3792         225 :     VSILFILE *fpL = VSIFOpenL(osFilename.c_str(), "w");
    3793         225 :     if (fpL == nullptr)
    3794           0 :         return CE_Failure;
    3795             : 
    3796         225 :     TIFF *hTIFF = VSI_TIFFOpen(osFilename.c_str(), "w", fpL);
    3797             : 
    3798         225 :     if (hTIFF == nullptr)
    3799             :     {
    3800           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3801             :                  "TIFF/GeoTIFF structure is corrupt.");
    3802           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    3803           0 :         VSIUnlink(osFilename.c_str());
    3804           0 :         return CE_Failure;
    3805             :     }
    3806             : 
    3807             :     /* -------------------------------------------------------------------- */
    3808             :     /*      Write some minimal set of image parameters.                     */
    3809             :     /* -------------------------------------------------------------------- */
    3810         225 :     TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, 1);
    3811         225 :     TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, 1);
    3812         225 :     TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, 8);
    3813         225 :     TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
    3814         225 :     TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, 1);
    3815         225 :     TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    3816         225 :     TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    3817             : 
    3818             :     /* -------------------------------------------------------------------- */
    3819             :     /*      Get the projection definition.                                  */
    3820             :     /* -------------------------------------------------------------------- */
    3821             : 
    3822         225 :     bool bPointGeoIgnore = false;
    3823         225 :     if (bPixelIsPoint)
    3824             :     {
    3825             :         bPointGeoIgnore =
    3826           1 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3827             :     }
    3828             : 
    3829         225 :     GTIF *hGTIF = nullptr;
    3830         225 :     if (hSRS != nullptr || bPixelIsPoint)
    3831             :     {
    3832         225 :         hGTIF = GTIFNew(hTIFF);
    3833         225 :         if (hGTIF)
    3834             :         {
    3835         225 :             GTIFAttachPROJContext(hGTIF, OSRGetProjTLSContext());
    3836             : 
    3837         225 :             if (hSRS != nullptr)
    3838         225 :                 GTIFSetFromOGISDefnEx(hGTIF, hSRS, GEOTIFF_KEYS_STANDARD,
    3839             :                                       GEOTIFF_VERSION_1_0);
    3840             : 
    3841         225 :             if (bPixelIsPoint)
    3842             :             {
    3843           1 :                 GTIFKeySet(hGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    3844             :                            RasterPixelIsPoint);
    3845             :             }
    3846             : 
    3847         225 :             GTIFWriteKeys(hGTIF);
    3848         225 :             GTIFFree(hGTIF);
    3849             :         }
    3850             :     }
    3851             : 
    3852             :     /* -------------------------------------------------------------------- */
    3853             :     /*      Set the geotransform, or GCPs.                                  */
    3854             :     /* -------------------------------------------------------------------- */
    3855             : 
    3856          28 :     if (padfGeoTransform[0] != 0.0 || padfGeoTransform[1] != 1.0 ||
    3857          28 :         padfGeoTransform[2] != 0.0 || padfGeoTransform[3] != 0.0 ||
    3858         253 :         padfGeoTransform[4] != 0.0 || std::abs(padfGeoTransform[5]) != 1.0)
    3859             :     {
    3860             : 
    3861         208 :         if (padfGeoTransform[2] == 0.0 && padfGeoTransform[4] == 0.0)
    3862             :         {
    3863         205 :             double adfPixelScale[3] = {padfGeoTransform[1],
    3864         205 :                                        fabs(padfGeoTransform[5]), 0.0};
    3865             : 
    3866         205 :             TIFFSetField(hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3867             : 
    3868         205 :             double adfTiePoints[6] = {
    3869         205 :                 0.0, 0.0, 0.0, padfGeoTransform[0], padfGeoTransform[3], 0.0};
    3870             : 
    3871         205 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3872             :             {
    3873           1 :                 adfTiePoints[3] +=
    3874           1 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3875           1 :                 adfTiePoints[4] +=
    3876           1 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3877             :             }
    3878             : 
    3879         205 :             TIFFSetField(hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3880             :         }
    3881             :         else
    3882             :         {
    3883           3 :             double adfMatrix[16] = {0.0};
    3884             : 
    3885           3 :             adfMatrix[0] = padfGeoTransform[1];
    3886           3 :             adfMatrix[1] = padfGeoTransform[2];
    3887           3 :             adfMatrix[3] = padfGeoTransform[0];
    3888           3 :             adfMatrix[4] = padfGeoTransform[4];
    3889           3 :             adfMatrix[5] = padfGeoTransform[5];
    3890           3 :             adfMatrix[7] = padfGeoTransform[3];
    3891           3 :             adfMatrix[15] = 1.0;
    3892             : 
    3893           3 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3894             :             {
    3895           0 :                 adfMatrix[3] +=
    3896           0 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3897           0 :                 adfMatrix[7] +=
    3898           0 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3899             :             }
    3900             : 
    3901           3 :             TIFFSetField(hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3902             :         }
    3903             :     }
    3904             : 
    3905             :     /* -------------------------------------------------------------------- */
    3906             :     /*      Otherwise write tiepoints if they are available.                */
    3907             :     /* -------------------------------------------------------------------- */
    3908          17 :     else if (nGCPCount > 0)
    3909             :     {
    3910             :         double *padfTiePoints =
    3911           5 :             static_cast<double *>(CPLMalloc(6 * sizeof(double) * nGCPCount));
    3912             : 
    3913          16 :         for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
    3914             :         {
    3915             : 
    3916          11 :             padfTiePoints[iGCP * 6 + 0] = pasGCPList[iGCP].dfGCPPixel;
    3917          11 :             padfTiePoints[iGCP * 6 + 1] = pasGCPList[iGCP].dfGCPLine;
    3918          11 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3919          11 :             padfTiePoints[iGCP * 6 + 3] = pasGCPList[iGCP].dfGCPX;
    3920          11 :             padfTiePoints[iGCP * 6 + 4] = pasGCPList[iGCP].dfGCPY;
    3921          11 :             padfTiePoints[iGCP * 6 + 5] = pasGCPList[iGCP].dfGCPZ;
    3922             :         }
    3923             : 
    3924           5 :         TIFFSetField(hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * nGCPCount, padfTiePoints);
    3925           5 :         CPLFree(padfTiePoints);
    3926             :     }
    3927             : 
    3928             :     /* -------------------------------------------------------------------- */
    3929             :     /*      Write RPC                                                       */
    3930             :     /* -------------------------------------------------------------------- */
    3931         225 :     if (papszRPCMD != nullptr)
    3932             :     {
    3933           1 :         GTiffDatasetWriteRPCTag(hTIFF, papszRPCMD);
    3934             :     }
    3935             : 
    3936             :     /* -------------------------------------------------------------------- */
    3937             :     /*      Cleanup and return the created memory buffer.                   */
    3938             :     /* -------------------------------------------------------------------- */
    3939         225 :     GByte bySmallImage = 0;
    3940             : 
    3941         225 :     TIFFWriteEncodedStrip(hTIFF, 0, reinterpret_cast<char *>(&bySmallImage), 1);
    3942         225 :     TIFFWriteCheck(hTIFF, TIFFIsTiled(hTIFF), "GTIFMemBufFromWkt");
    3943         225 :     TIFFWriteDirectory(hTIFF);
    3944             : 
    3945         225 :     XTIFFClose(hTIFF);
    3946         225 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    3947             : 
    3948             :     /* -------------------------------------------------------------------- */
    3949             :     /*      Read back from the memory buffer.  It would be preferable       */
    3950             :     /*      to be able to "steal" the memory buffer, but there isn't        */
    3951             :     /*      currently any support for this.                                 */
    3952             :     /* -------------------------------------------------------------------- */
    3953         225 :     GUIntBig nBigLength = 0;
    3954             : 
    3955         225 :     *ppabyBuffer = VSIGetMemFileBuffer(osFilename.c_str(), &nBigLength, TRUE);
    3956         225 :     *pnSize = static_cast<int>(nBigLength);
    3957             : 
    3958         225 :     return CE_None;
    3959             : }

Generated by: LCOV version 1.14