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: 2026-03-17 00:06:02 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       10463 : void LibgeotiffOneTimeInit()
     105             : {
     106       10463 :     std::lock_guard<std::mutex> oLock(oDeleteMutex);
     107             : 
     108             :     static bool bOneTimeInitDone = false;
     109             : 
     110       10463 :     if (bOneTimeInitDone)
     111        9771 :         return;
     112             : 
     113         692 :     bOneTimeInitDone = true;
     114             : 
     115             :     // This isn't thread-safe, so better do it now
     116         692 :     XTIFFInitialize();
     117             : }
     118             : 
     119             : /************************************************************************/
     120             : /*                       GTIFToCPLRecyleString()                        */
     121             : /*                                                                      */
     122             : /*      This changes a string from the libgeotiff heap to the GDAL      */
     123             : /*      heap.                                                           */
     124             : /************************************************************************/
     125             : 
     126       42578 : static void GTIFToCPLRecycleString(char **ppszTarget)
     127             : 
     128             : {
     129       42578 :     if (*ppszTarget == nullptr)
     130          54 :         return;
     131             : 
     132       42524 :     char *pszTempString = CPLStrdup(*ppszTarget);
     133       42524 :     GTIFFreeMemory(*ppszTarget);
     134       42524 :     *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         258 : static PJ *GTIFGetOfficialCRS(GTIF *hGTIF, PJ_TYPE searchType,
     460             :                               const char *pszAuthName, const char *pszName)
     461             : {
     462         258 :     PJ *ret = nullptr;
     463             :     /* Search in database the corresponding EPSG 'official' name */
     464             :     auto ctx =
     465         258 :         static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(hGTIF, true, nullptr));
     466             :     auto list =
     467         258 :         proj_create_from_name(ctx, pszAuthName, pszName, &searchType, 1,
     468             :                               /* approximateMatch = */ false, 1, nullptr);
     469         258 :     if (list)
     470             :     {
     471         258 :         const auto listSize = proj_list_get_count(list);
     472         258 :         if (listSize == 1)
     473             :         {
     474          29 :             ret = proj_list_get(ctx, list, 0);
     475             :         }
     476         258 :         proj_list_destroy(list);
     477             :     }
     478         258 :     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        8928 : OGRSpatialReferenceH GTIFGetOGISDefnAsOSR(GTIF *hGTIF, GTIFDefn *psDefn)
     507             : 
     508             : {
     509       17856 :     OGRSpatialReference oSRS;
     510             : 
     511        8928 :     LibgeotiffOneTimeInit();
     512             : 
     513             : #if LIBGEOTIFF_VERSION >= 1600
     514        8928 :     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        8928 :     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        8928 :     unsigned short verticalCSType = 0;
     530        8928 :     unsigned short verticalDatum = 0;
     531        8928 :     unsigned short verticalUnits = 0;
     532             : 
     533        8928 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalCSTypeGeoKey, &verticalCSType, 0, 1);
     534        8928 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalDatumGeoKey, &verticalDatum, 0, 1);
     535        8928 :     GDALGTIFKeyGetSHORT(hGTIF, VerticalUnitsGeoKey, &verticalUnits, 0, 1);
     536             : 
     537        8928 :     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        8928 :     if (psDefn->Model != ModelTypeProjected &&
     598        4875 :         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        8584 :     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        8584 :         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        8584 :     unsigned short bLinearUnitsMarkedCorrect = FALSE;
     798             : 
     799        8584 :     GDALGTIFKeyGetSHORT(hGTIF, ProjLinearUnitsInterpCorrectGeoKey,
     800             :                         &bLinearUnitsMarkedCorrect, 0, 1);
     801             : 
     802        8584 :     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        8584 :     OGRBoolean linearUnitIsSet = FALSE;
     837        8584 :     if (psDefn->Model == ModelTypeProjected)
     838             :     {
     839        4053 :         char szCTString[512] = {'\0'};
     840        4053 :         if (psDefn->PCS != KvUserDefined)
     841             :         {
     842        3954 :             char *pszPCSName = nullptr;
     843             : 
     844             : #if LIBGEOTIFF_VERSION >= 1600
     845        3954 :             GTIFGetPCSInfoEx(projContext,
     846             : #else
     847             :             GTIFGetPCSInfo(
     848             : #endif
     849        3954 :                              psDefn->PCS, &pszPCSName, nullptr, nullptr,
     850             :                              nullptr);
     851             : 
     852        3954 :             oSRS.SetProjCS(pszPCSName ? pszPCSName : "unnamed");
     853        3954 :             if (pszPCSName)
     854        3954 :                 GTIFFreeMemory(pszPCSName);
     855             : 
     856        3954 :             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        4053 :         if (CheckCitationKeyForStatePlaneUTM(hGTIF, psDefn, &oSRS,
     902        4053 :                                              &linearUnitIsSet))
     903             :         {
     904           0 :             return OGRSpatialReference::ToHandle(oSRS.Clone());
     905             :         }
     906             : 
     907             :         /* Handle ESRI PE string in citation */
     908        4053 :         szCTString[0] = '\0';
     909        4053 :         if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
     910        4053 :                                 sizeof(szCTString)))
     911        4029 :             SetCitationToSRS(hGTIF, szCTString, sizeof(szCTString),
     912             :                              GTCitationGeoKey, &oSRS, &linearUnitIsSet);
     913             :     }
     914             : 
     915             :     /* ==================================================================== */
     916             :     /*      Setup the GeogCS                                                */
     917             :     /* ==================================================================== */
     918        8584 :     char *pszGeogName = nullptr;
     919        8584 :     char *pszDatumName = nullptr;
     920        8584 :     char *pszPMName = nullptr;
     921        8584 :     char *pszSpheroidName = nullptr;
     922        8584 :     char *pszAngularUnits = nullptr;
     923        8584 :     char szGCSName[512] = {'\0'};
     924             : 
     925        8584 :     if (!
     926             : #if LIBGEOTIFF_VERSION >= 1600
     927       17168 :         GTIFGetGCSInfoEx(projContext,
     928             : #else
     929             :         GTIFGetGCSInfo(
     930             : #endif
     931        8584 :                          psDefn->GCS, &pszGeogName, nullptr, nullptr,
     932        8689 :                          nullptr) &&
     933         105 :         GDALGTIFKeyGetASCII(hGTIF, GeogCitationGeoKey, szGCSName,
     934             :                             sizeof(szGCSName)))
     935             :     {
     936          92 :         GetGeogCSFromCitation(szGCSName, sizeof(szGCSName), GeogCitationGeoKey,
     937             :                               &pszGeogName, &pszDatumName, &pszPMName,
     938             :                               &pszSpheroidName, &pszAngularUnits);
     939             :     }
     940             :     else
     941             :     {
     942        8492 :         GTIFToCPLRecycleString(&pszGeogName);
     943             :     }
     944             : 
     945        8584 :     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        8584 :     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        8584 :     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        8584 :     if (!pszDatumName)
     982             :     {
     983             : #if LIBGEOTIFF_VERSION >= 1600
     984        8518 :         GTIFGetDatumInfoEx(projContext,
     985             : #else
     986             :         GTIFGetDatumInfo(
     987             : #endif
     988        8518 :                            psDefn->Datum, &pszDatumName, nullptr);
     989        8518 :         GTIFToCPLRecycleString(&pszDatumName);
     990             :     }
     991             : 
     992        8584 :     double dfSemiMajor = 0.0;
     993        8584 :     double dfInvFlattening = 0.0;
     994        8584 :     if (!pszSpheroidName)
     995             :     {
     996             : #if LIBGEOTIFF_VERSION >= 1600
     997        8516 :         GTIFGetEllipsoidInfoEx(projContext,
     998             : #else
     999             :         GTIFGetEllipsoidInfo(
    1000             : #endif
    1001        8516 :                                psDefn->Ellipsoid, &pszSpheroidName, nullptr,
    1002             :                                nullptr);
    1003        8516 :         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        8584 :     if (!pszPMName)
    1019             :     {
    1020             : #if LIBGEOTIFF_VERSION >= 1600
    1021        8494 :         GTIFGetPMInfoEx(projContext,
    1022             : #else
    1023             :         GTIFGetPMInfo(
    1024             : #endif
    1025        8494 :                         psDefn->PM, &pszPMName, nullptr);
    1026        8494 :         GTIFToCPLRecycleString(&pszPMName);
    1027             :     }
    1028             :     else
    1029             :     {
    1030          90 :         CPL_IGNORE_RET_VAL(
    1031          90 :             GDALGTIFKeyGetDOUBLE(hGTIF, GeogPrimeMeridianLongGeoKey,
    1032             :                                  &(psDefn->PMLongToGreenwich), 0, 1));
    1033             :     }
    1034             : 
    1035        8584 :     if (!pszAngularUnits)
    1036             :     {
    1037             : #if LIBGEOTIFF_VERSION >= 1600
    1038        8569 :         GTIFGetUOMAngleInfoEx(projContext,
    1039             : #else
    1040             :         GTIFGetUOMAngleInfo(
    1041             : #endif
    1042        8569 :                               psDefn->UOMAngle, &pszAngularUnits,
    1043             :                               &psDefn->UOMAngleInDegrees);
    1044        8569 :         if (pszAngularUnits == nullptr)
    1045          11 :             pszAngularUnits = CPLStrdup("unknown");
    1046             :         else
    1047        8558 :             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        8584 :     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        8584 :     dfSemiMajor = psDefn->SemiMajor;
    1068        8584 :     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        8573 :     else if (dfInvFlattening == 0.0 &&
    1076        8516 :              ((psDefn->SemiMinor / psDefn->SemiMajor) < 0.99999999999999999 ||
    1077          14 :               (psDefn->SemiMinor / psDefn->SemiMajor) > 1.00000000000000001))
    1078             :     {
    1079        8502 :         dfInvFlattening =
    1080        8502 :             OSRCalcInvFlattening(psDefn->SemiMajor, psDefn->SemiMinor);
    1081             : 
    1082             :         /* Take official inverse flattening definition in the WGS84 case */
    1083        8502 :         if (fabs(dfSemiMajor - SRS_WGS84_SEMIMAJOR) < 1e-10 &&
    1084        4909 :             fabs(dfInvFlattening - SRS_WGS84_INVFLATTENING) < 1e-10)
    1085        4832 :             dfInvFlattening = SRS_WGS84_INVFLATTENING;
    1086             :     }
    1087        8584 :     if (!pszGeogName || strlen(pszGeogName) == 0)
    1088             :     {
    1089          13 :         CPLFree(pszGeogName);
    1090          13 :         pszGeogName = CPLStrdup(pszDatumName ? pszDatumName : "unknown");
    1091             :     }
    1092             : 
    1093        8584 :     oSRS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
    1094             :                    dfInvFlattening, pszPMName, psDefn->PMLongToGreenwich,
    1095             :                    pszAngularUnits,
    1096        8584 :                    psDefn->UOMAngleInDegrees * CPLAtof(SRS_UA_DEGREE_CONV));
    1097             : 
    1098        8584 :     bool bGeog3DCRS = false;
    1099        8584 :     bool bSetDatumEllipsoidCode = true;
    1100        8584 :     bool bHasWarnedInconsistentGeogCRSEPSG = false;
    1101             :     {
    1102        8584 :         const int nGCS = psDefn->GCS;
    1103        8584 :         if (nGCS != KvUserDefined && nGCS > 0 &&
    1104        8479 :             psDefn->Model != ModelTypeGeocentric)
    1105             :         {
    1106       16958 :             OGRSpatialReference oSRSGeog;
    1107             :             const bool bGCSCodeValid =
    1108        8479 :                 oSRSGeog.importFromEPSG(nGCS) == OGRERR_NONE;
    1109             : 
    1110             :             const std::string osGTiffSRSSource =
    1111       16958 :                 CPLGetConfigOption("GTIFF_SRS_SOURCE", "");
    1112             : 
    1113             :             // GeoTIFF 1.0 might put a Geographic 3D code in GeodeticCRSGeoKey
    1114        8479 :             bool bTryCompareToEPSG = oSRSGeog.GetAxesCount() == 2;
    1115             : 
    1116        8479 :             if (psDefn->Datum != KvUserDefined)
    1117             :             {
    1118             :                 char szCode[12];
    1119        8479 :                 snprintf(szCode, sizeof(szCode), "%d", psDefn->Datum);
    1120             :                 auto ctx = static_cast<PJ_CONTEXT *>(
    1121        8479 :                     GTIFGetPROJContext(hGTIF, true, nullptr));
    1122        8479 :                 auto datum = proj_create_from_database(
    1123             :                     ctx, "EPSG", szCode, PJ_CATEGORY_DATUM, 0, nullptr);
    1124        8479 :                 if (datum)
    1125             :                 {
    1126        8478 :                     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        8478 :                     proj_destroy(datum);
    1135             :                 }
    1136             :             }
    1137             : 
    1138        8483 :             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        8479 :             if (EQUAL(osGTiffSRSSource.c_str(), "EPSG"))
    1160             :             {
    1161           1 :                 oSRS.CopyGeogCSFrom(&oSRSGeog);
    1162             :             }
    1163       13294 :             else if (osGTiffSRSSource.empty() && oSRSGeog.IsDynamic() &&
    1164        4816 :                      psDefn->Model == ModelTypeGeographic)
    1165             :             {
    1166             :                 // We should perhaps be more careful and detect overrides
    1167             :                 // of geokeys...
    1168        4485 :                 oSRS = oSRSGeog;
    1169        4485 :                 bSetDatumEllipsoidCode = false;
    1170             :             }
    1171        3993 :             else if (bGCSCodeValid && osGTiffSRSSource.empty())
    1172             :             {
    1173        3992 :                 oSRS.SetAuthority("GEOGCS", "EPSG", nGCS);
    1174             :             }
    1175             :             else
    1176             :             {
    1177           1 :                 bSetDatumEllipsoidCode = false;
    1178             :             }
    1179             : 
    1180        8479 :             int nVertSRSCode = verticalCSType;
    1181        8479 :             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        8479 :             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        8584 :     if (bSetDatumEllipsoidCode)
    1216             :     {
    1217        4096 :         if (psDefn->Datum != KvUserDefined)
    1218        4017 :             oSRS.SetAuthority("DATUM", "EPSG", psDefn->Datum);
    1219             : 
    1220        4096 :         if (psDefn->Ellipsoid != KvUserDefined)
    1221        4021 :             oSRS.SetAuthority("SPHEROID", "EPSG", psDefn->Ellipsoid);
    1222             :     }
    1223             : 
    1224        8584 :     CPLFree(pszGeogName);
    1225        8584 :     CPLFree(pszDatumName);
    1226        8584 :     CPLFree(pszSpheroidName);
    1227        8584 :     CPLFree(pszPMName);
    1228        8584 :     CPLFree(pszAngularUnits);
    1229             : 
    1230             :     /* -------------------------------------------------------------------- */
    1231             :     /*      Set projection units if not yet done                            */
    1232             :     /* -------------------------------------------------------------------- */
    1233        8584 :     if (psDefn->Model == ModelTypeProjected && !linearUnitIsSet)
    1234             :     {
    1235        4046 :         char *pszUnitsName = nullptr;
    1236             : 
    1237        4046 :         if (psDefn->UOMLength != KvUserDefined)
    1238             :         {
    1239             : #if LIBGEOTIFF_VERSION >= 1600
    1240        4039 :             GTIFGetUOMLengthInfoEx(projContext,
    1241             : #else
    1242             :             GTIFGetUOMLengthInfo(
    1243             : #endif
    1244        4039 :                                    psDefn->UOMLength, &pszUnitsName, nullptr);
    1245             :         }
    1246             : 
    1247        4046 :         if (pszUnitsName != nullptr)
    1248             :         {
    1249             :             char szUOMLength[12];
    1250        4039 :             snprintf(szUOMLength, sizeof(szUOMLength), "%d", psDefn->UOMLength);
    1251        4039 :             oSRS.SetTargetLinearUnits(nullptr, pszUnitsName,
    1252             :                                       psDefn->UOMLengthInMeters, "EPSG",
    1253             :                                       szUOMLength);
    1254             :         }
    1255             :         else
    1256           7 :             oSRS.SetLinearUnits("unknown", psDefn->UOMLengthInMeters);
    1257             : 
    1258        4046 :         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        8584 :     unsigned short tmp = 0;
    1268        8584 :     bool bGotFromEPSG = false;
    1269        4053 :     if (psDefn->Model == ModelTypeProjected && psDefn->PCS != KvUserDefined &&
    1270        3954 :         GDALGTIFKeyGetSHORT(hGTIF, ProjectionGeoKey, &tmp, 0, 1) == 0 &&
    1271        3953 :         GDALGTIFKeyGetSHORT(hGTIF, ProjCoordTransGeoKey, &tmp, 0, 1) == 0 &&
    1272        3953 :         GDALGTIFKeyGetSHORT(hGTIF, GeographicTypeGeoKey, &tmp, 0, 1) == 0 &&
    1273        3857 :         GDALGTIFKeyGetSHORT(hGTIF, GeogGeodeticDatumGeoKey, &tmp, 0, 1) == 0 &&
    1274       16492 :         GDALGTIFKeyGetSHORT(hGTIF, GeogEllipsoidGeoKey, &tmp, 0, 1) == 0 &&
    1275        3855 :         CPLTestBool(CPLGetConfigOption("GTIFF_IMPORT_FROM_EPSG", "YES")))
    1276             :     {
    1277             :         // Save error state as importFromEPSGA() will call CPLReset()
    1278        3855 :         CPLErrorNum errNo = CPLGetLastErrorNo();
    1279        3855 :         CPLErr eErr = CPLGetLastErrorType();
    1280        3855 :         const char *pszTmp = CPLGetLastErrorMsg();
    1281        3855 :         char *pszLastErrorMsg = CPLStrdup(pszTmp ? pszTmp : "");
    1282        3855 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1283        7710 :         OGRSpatialReference oSRSTmp;
    1284        3855 :         OGRErr eImportErr = oSRSTmp.importFromEPSG(psDefn->PCS);
    1285        3855 :         CPLPopErrorHandler();
    1286             :         // Restore error state
    1287        3855 :         CPLErrorSetState(eErr, errNo, pszLastErrorMsg);
    1288        3855 :         CPLFree(pszLastErrorMsg);
    1289        3855 :         bGotFromEPSG = eImportErr == OGRERR_NONE;
    1290             : 
    1291        3855 :         if (bGotFromEPSG)
    1292             :         {
    1293             :             // See #6210. In case there's an overridden linear units, take it
    1294             :             // into account
    1295        3855 :             const char *pszUnitsName = nullptr;
    1296        3855 :             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        3855 :             if (fabs(dfUOMLengthInMeters - oSRSTmp.GetLinearUnits(nullptr)) >
    1302        3855 :                 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        3855 :             if (bGeog3DCRS)
    1319             :             {
    1320           1 :                 oSRSTmp.CopyGeogCSFrom(&oSRS);
    1321           1 :                 oSRSTmp.UpdateCoordinateSystemFromGeogCRS();
    1322             :             }
    1323        3855 :             oSRS = std::move(oSRSTmp);
    1324             :         }
    1325             :     }
    1326             : 
    1327             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    1328        8607 :     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        8584 :     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        8584 :     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        8584 :     if (psDefn->Model == ModelTypeProjected && psDefn->PCS != KvUserDefined &&
    1600        3954 :         !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        8584 :     const bool bIs2DProjCRS = oSRS.IsProjected() && oSRS.GetAxesCount() == 2;
    1648        8584 :     if (bIs2DProjCRS)
    1649             :     {
    1650        4050 :         const char *pszProjCRSName = oSRS.GetAttrValue("PROJCS");
    1651        4050 :         if (pszProjCRSName)
    1652             :         {
    1653             :             // Hack to be able to read properly what we have written for
    1654             :             // ESRI:102113 (ESRI ancient WebMercator).
    1655        4050 :             if (EQUAL(pszProjCRSName, "WGS_1984_Web_Mercator"))
    1656           2 :                 oSRS.SetFromUserInput("ESRI:102113");
    1657             :             // And for EPSG:900913
    1658        4048 :             else if (EQUAL(pszProjCRSName, "Google Maps Global Mercator"))
    1659           0 :                 oSRS.importFromEPSG(900913);
    1660        4048 :             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        4534 :     if ((bIs2DProjCRS || oSRS.IsGeographic()) &&
    1675       13246 :         oSRS.GetAuthorityCode(nullptr) == nullptr &&
    1676         128 :         psDefn->UOMAngleInDegrees == 1.0)
    1677             :     {
    1678         132 :         const PJ_TYPE type = bIs2DProjCRS ? PJ_TYPE_PROJECTED_CRS
    1679          18 :                              : oSRS.GetAxesCount() == 2
    1680          18 :                                  ? PJ_TYPE_GEOGRAPHIC_2D_CRS
    1681         114 :                                  : 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         114 :         PJ *refCRS = GTIFGetOfficialCRS(hGTIF, type, "EPSG", oSRS.GetName());
    1687         114 :         if (!refCRS)
    1688          93 :             refCRS = GTIFGetOfficialCRS(hGTIF, type, nullptr, oSRS.GetName());
    1689         114 :         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        8584 :     bool bNeedManualVertCS = false;
    1748        8584 :     char citation[2048] = {'\0'};
    1749             : 
    1750             :     // See https://github.com/OSGeo/gdal/pull/4197
    1751        8584 :     if (verticalCSType > KvUserDefined || verticalDatum > KvUserDefined ||
    1752        8584 :         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        8616 :     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        8584 :     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        8584 :     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          21 :             oSRS.importFromWkt(pszWKT, apszOptionsImport);
    1891             :         }
    1892          21 :         CPLFree(pszWKT);
    1893             :     }
    1894             : 
    1895        8584 :     oSRS.StripTOWGS84IfKnownDatumAndAllowed();
    1896             : 
    1897        8584 :     double dfCoordinateEpoch = 0.0;
    1898        8584 :     if (GDALGTIFKeyGetDOUBLE(hGTIF, CoordinateEpochGeoKey, &dfCoordinateEpoch,
    1899        8584 :                              0, 1))
    1900             :     {
    1901           2 :         oSRS.SetCoordinateEpoch(dfCoordinateEpoch);
    1902             :     }
    1903             : 
    1904        8584 :     return OGRSpatialReference::ToHandle(oSRS.Clone());
    1905             : }
    1906             : 
    1907             : /************************************************************************/
    1908             : /*                          GTIFGetOGISDefn()                           */
    1909             : /************************************************************************/
    1910             : 
    1911           0 : char *GTIFGetOGISDefn(GTIF *hGTIF, GTIFDefn *psDefn)
    1912             : {
    1913           0 :     OGRSpatialReferenceH hSRS = GTIFGetOGISDefnAsOSR(hGTIF, psDefn);
    1914             : 
    1915           0 :     char *pszWKT = nullptr;
    1916           0 :     if (hSRS && OGRSpatialReference::FromHandle(hSRS)->exportToWkt(&pszWKT) ==
    1917             :                     OGRERR_NONE)
    1918             :     {
    1919           0 :         OSRDestroySpatialReference(hSRS);
    1920           0 :         return pszWKT;
    1921             :     }
    1922           0 :     CPLFree(pszWKT);
    1923           0 :     OSRDestroySpatialReference(hSRS);
    1924             : 
    1925           0 :     return nullptr;
    1926             : }
    1927             : 
    1928             : /************************************************************************/
    1929             : /*                     OGCDatumName2EPSGDatumCode()                     */
    1930             : /************************************************************************/
    1931             : 
    1932         578 : static int OGCDatumName2EPSGDatumCode(GTIF *psGTIF, const char *pszOGCName)
    1933             : 
    1934             : {
    1935         578 :     int nReturn = KvUserDefined;
    1936             : 
    1937             :     /* -------------------------------------------------------------------- */
    1938             :     /*      Do we know it as a built in?                                    */
    1939             :     /* -------------------------------------------------------------------- */
    1940         578 :     if (EQUAL(pszOGCName, "NAD27") ||
    1941         578 :         EQUAL(pszOGCName, "North_American_Datum_1927"))
    1942           0 :         return Datum_North_American_Datum_1927;
    1943         578 :     else if (EQUAL(pszOGCName, "NAD83") ||
    1944         578 :              EQUAL(pszOGCName, "North_American_Datum_1983"))
    1945           0 :         return Datum_North_American_Datum_1983;
    1946         578 :     else if (EQUAL(pszOGCName, "WGS84") || EQUAL(pszOGCName, "WGS_1984") ||
    1947          48 :              EQUAL(pszOGCName, "WGS 84"))
    1948         530 :         return Datum_WGS84;
    1949          48 :     else if (EQUAL(pszOGCName, "WGS72") || EQUAL(pszOGCName, "WGS_1972"))
    1950           0 :         return Datum_WGS72;
    1951             : 
    1952             :     /* Search in database */
    1953             :     auto ctx =
    1954          48 :         static_cast<PJ_CONTEXT *>(GTIFGetPROJContext(psGTIF, true, nullptr));
    1955          48 :     const PJ_TYPE searchType = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
    1956             :     auto list =
    1957          48 :         proj_create_from_name(ctx, "EPSG", pszOGCName, &searchType, 1,
    1958             :                               /* approximateMatch = */ true, 10, nullptr);
    1959          48 :     if (list)
    1960             :     {
    1961          48 :         const auto listSize = proj_list_get_count(list);
    1962          61 :         for (int i = 0; nReturn == KvUserDefined && i < listSize; i++)
    1963             :         {
    1964          13 :             auto datum = proj_list_get(ctx, list, i);
    1965          13 :             if (datum)
    1966             :             {
    1967          13 :                 const char *pszDatumName = proj_get_name(datum);
    1968          13 :                 if (pszDatumName)
    1969             :                 {
    1970          13 :                     char *pszTmp = CPLStrdup(pszDatumName);
    1971          13 :                     WKTMassageDatum(&pszTmp);
    1972          13 :                     if (EQUAL(pszTmp, pszOGCName))
    1973             :                     {
    1974           3 :                         const char *pszCode = proj_get_id_code(datum, 0);
    1975           3 :                         if (pszCode)
    1976             :                         {
    1977           3 :                             nReturn = atoi(pszCode);
    1978             :                         }
    1979             :                     }
    1980          13 :                     CPLFree(pszTmp);
    1981             :                 }
    1982             :             }
    1983          13 :             proj_destroy(datum);
    1984             :         }
    1985          48 :         proj_list_destroy(list);
    1986             :     }
    1987             : 
    1988          48 :     return nReturn;
    1989             : }
    1990             : 
    1991             : /************************************************************************/
    1992             : /*                        GTIFSetFromOGISDefn()                         */
    1993             : /*                                                                      */
    1994             : /*      Write GeoTIFF projection tags from an OGC WKT definition.       */
    1995             : /************************************************************************/
    1996             : 
    1997           0 : int GTIFSetFromOGISDefn(GTIF *psGTIF, const char *pszOGCWKT)
    1998             : 
    1999             : {
    2000             :     /* -------------------------------------------------------------------- */
    2001             :     /*      Create an OGRSpatialReference object corresponding to the       */
    2002             :     /*      string.                                                         */
    2003             :     /* -------------------------------------------------------------------- */
    2004             : 
    2005           0 :     OGRSpatialReference oSRS;
    2006           0 :     if (oSRS.importFromWkt(pszOGCWKT) != OGRERR_NONE)
    2007             :     {
    2008           0 :         return FALSE;
    2009             :     }
    2010           0 :     return GTIFSetFromOGISDefnEx(psGTIF, OGRSpatialReference::ToHandle(&oSRS),
    2011           0 :                                  GEOTIFF_KEYS_STANDARD, GEOTIFF_VERSION_1_0);
    2012             : }
    2013             : 
    2014        3388 : int GTIFSetFromOGISDefnEx(GTIF *psGTIF, OGRSpatialReferenceH hSRS,
    2015             :                           GTIFFKeysFlavorEnum eFlavor,
    2016             :                           GeoTIFFVersionEnum eVersion)
    2017             : {
    2018        6776 :     std::map<geokey_t, std::string> oMapAsciiKeys;
    2019             : 
    2020        3388 :     GTIFKeySet(psGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1, RasterPixelIsArea);
    2021             : 
    2022        3388 :     const OGRSpatialReference *poSRS = OGRSpatialReference::FromHandle(hSRS);
    2023             : 
    2024             :     /* -------------------------------------------------------------------- */
    2025             :     /*      Set version number.                                             */
    2026             :     /* -------------------------------------------------------------------- */
    2027        3388 :     if (eVersion == GEOTIFF_VERSION_AUTO)
    2028             :     {
    2029        6299 :         if (poSRS->IsCompound() ||
    2030        3143 :             (poSRS->IsGeographic() && poSRS->GetAxesCount() == 3))
    2031             :         {
    2032          15 :             eVersion = GEOTIFF_VERSION_1_1;
    2033             :         }
    2034             :         else
    2035             :         {
    2036        3141 :             eVersion = GEOTIFF_VERSION_1_0;
    2037             :         }
    2038             :     }
    2039        3388 :     CPLAssert(eVersion == GEOTIFF_VERSION_1_0 ||
    2040             :               eVersion == GEOTIFF_VERSION_1_1);
    2041        3388 :     if (eVersion >= GEOTIFF_VERSION_1_1)
    2042             :     {
    2043             : #if LIBGEOTIFF_VERSION >= 1600
    2044          18 :         GTIFSetVersionNumbers(psGTIF, GEOTIFF_SPEC_1_1_VERSION,
    2045             :                               GEOTIFF_SPEC_1_1_KEY_REVISION,
    2046             :                               GEOTIFF_SPEC_1_1_MINOR_REVISION);
    2047             : #else
    2048             :         CPLError(CE_Warning, CPLE_AppDefined,
    2049             :                  "Setting GeoTIFF 1.1 requires libgeotiff >= 1.6. Key values "
    2050             :                  "will be written as GeoTIFF 1.1, but the version number "
    2051             :                  "will be seen as 1.0, which might confuse GeoTIFF readers");
    2052             : #endif
    2053             :     }
    2054             : 
    2055             :     /* -------------------------------------------------------------------- */
    2056             :     /*      Get the ellipsoid definition.                                   */
    2057             :     /* -------------------------------------------------------------------- */
    2058        3388 :     short nSpheroid = KvUserDefined;
    2059             : 
    2060        4440 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID") != nullptr &&
    2061        1052 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM|SPHEROID"), "EPSG"))
    2062             :     {
    2063        1052 :         nSpheroid = static_cast<short>(
    2064        1052 :             atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM|SPHEROID")));
    2065             :     }
    2066        3979 :     else if (poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID") != nullptr &&
    2067        1643 :              EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM|SPHEROID"), "EPSG"))
    2068             :     {
    2069        1643 :         nSpheroid = static_cast<short>(
    2070        1643 :             atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM|SPHEROID")));
    2071             :     }
    2072             : 
    2073        3388 :     OGRErr eErr = OGRERR_NONE;
    2074        3388 :     double dfSemiMajor = 0;
    2075        3388 :     double dfInvFlattening = 0;
    2076        3388 :     bool bHasEllipsoid = false;
    2077        3388 :     if (!poSRS->IsLocal())
    2078             :     {
    2079        3386 :         bHasEllipsoid = true;
    2080        3386 :         if (poSRS->IsCompound())
    2081             :         {
    2082          26 :             OGRSpatialReference oSRSTmp(*poSRS);
    2083          13 :             oSRSTmp.StripVertical();
    2084          13 :             bHasEllipsoid = CPL_TO_BOOL(!oSRSTmp.IsLocal());
    2085             :         }
    2086        3386 :         if (bHasEllipsoid)
    2087             :         {
    2088        3385 :             dfSemiMajor = poSRS->GetSemiMajor(&eErr);
    2089        3385 :             dfInvFlattening = poSRS->GetInvFlattening(&eErr);
    2090        3385 :             if (eErr != OGRERR_NONE)
    2091             :             {
    2092         111 :                 dfSemiMajor = 0.0;
    2093         111 :                 dfInvFlattening = 0.0;
    2094             :             }
    2095             :         }
    2096             :     }
    2097             : 
    2098             :     /* -------------------------------------------------------------------- */
    2099             :     /*      Get the Datum so we can special case a few PCS codes.           */
    2100             :     /* -------------------------------------------------------------------- */
    2101        3388 :     int nDatum = KvUserDefined;
    2102             : 
    2103        4443 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM") != nullptr &&
    2104        1055 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS|DATUM"), "EPSG"))
    2105        1055 :         nDatum = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS|DATUM"));
    2106        3974 :     else if (poSRS->GetAuthorityName("GEOGCS|DATUM") != nullptr &&
    2107        1641 :              EQUAL(poSRS->GetAuthorityName("GEOGCS|DATUM"), "EPSG"))
    2108        1641 :         nDatum = atoi(poSRS->GetAuthorityCode("GEOGCS|DATUM"));
    2109         692 :     else if (poSRS->GetAttrValue("DATUM") != nullptr)
    2110             :         nDatum =
    2111         578 :             OGCDatumName2EPSGDatumCode(psGTIF, poSRS->GetAttrValue("DATUM"));
    2112             : 
    2113             :     /* -------------------------------------------------------------------- */
    2114             :     /*      Get the GCS if possible.                                        */
    2115             :     /* -------------------------------------------------------------------- */
    2116        3388 :     int nGCS = KvUserDefined;
    2117             : 
    2118        4417 :     if (poSRS->GetAuthorityName("PROJCS|GEOGCS") != nullptr &&
    2119        1029 :         EQUAL(poSRS->GetAuthorityName("PROJCS|GEOGCS"), "EPSG"))
    2120        1029 :         nGCS = atoi(poSRS->GetAuthorityCode("PROJCS|GEOGCS"));
    2121        4000 :     else if (poSRS->GetAuthorityName("GEOGCS") != nullptr &&
    2122        1641 :              EQUAL(poSRS->GetAuthorityName("GEOGCS"), "EPSG"))
    2123        1640 :         nGCS = atoi(poSRS->GetAuthorityCode("GEOGCS"));
    2124             : 
    2125        3388 :     int nVerticalCSKeyValue = 0;
    2126        5563 :     bool hasEllipsoidHeight = !poSRS->IsCompound() && poSRS->IsGeographic() &&
    2127        2175 :                               poSRS->GetAxesCount() == 3;
    2128        3388 :     if (nGCS == 4937 && eVersion >= GEOTIFF_VERSION_1_1)
    2129             :     {
    2130             :         // Workaround a bug of PROJ 6.3.0
    2131             :         // See https://github.com/OSGeo/PROJ/pull/1880
    2132             :         // EPSG:4937 = ETRS89 3D
    2133           1 :         hasEllipsoidHeight = true;
    2134           1 :         nVerticalCSKeyValue = nGCS;
    2135           1 :         nGCS = 4258;  // ETRS89 2D
    2136             :     }
    2137        3387 :     else if (nGCS != KvUserDefined)
    2138             :     {
    2139        5336 :         OGRSpatialReference oGeogCRS;
    2140        2668 :         if (oGeogCRS.importFromEPSG(nGCS) == OGRERR_NONE &&
    2141        2668 :             oGeogCRS.IsGeographic() && oGeogCRS.GetAxesCount() == 3)
    2142             :         {
    2143           3 :             hasEllipsoidHeight = true;
    2144           3 :             if (eVersion >= GEOTIFF_VERSION_1_1)
    2145             :             {
    2146           2 :                 const auto candidate_nVerticalCSKeyValue = nGCS;
    2147           2 :                 nGCS = KvUserDefined;
    2148             : 
    2149             :                 // In case of a geographic 3D CRS, find the corresponding
    2150             :                 // geographic 2D CRS
    2151             :                 auto ctx = static_cast<PJ_CONTEXT *>(
    2152           2 :                     GTIFGetPROJContext(psGTIF, true, nullptr));
    2153           2 :                 const auto type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
    2154           2 :                 auto list = proj_create_from_name(
    2155             :                     ctx, "EPSG", oGeogCRS.GetName(), &type, 1,
    2156             :                     /* approximateMatch = */ false,
    2157             :                     1,  // result set limit size,
    2158             :                     nullptr);
    2159           2 :                 if (list && proj_list_get_count(list) == 1)
    2160             :                 {
    2161           2 :                     auto crs2D = proj_list_get(ctx, list, 0);
    2162           2 :                     if (crs2D)
    2163             :                     {
    2164           2 :                         const char *pszCode = proj_get_id_code(crs2D, 0);
    2165           2 :                         if (pszCode)
    2166             :                         {
    2167           2 :                             nVerticalCSKeyValue = candidate_nVerticalCSKeyValue;
    2168           2 :                             nGCS = atoi(pszCode);
    2169             :                         }
    2170           2 :                         proj_destroy(crs2D);
    2171             :                     }
    2172             :                 }
    2173           2 :                 proj_list_destroy(list);
    2174             :             }
    2175             :         }
    2176             :     }
    2177             : 
    2178             :     // Deprecated way of encoding ellipsoidal height
    2179        3388 :     if (hasEllipsoidHeight && nVerticalCSKeyValue == 0)
    2180             :     {
    2181           1 :         if (nGCS == 4979 || nDatum == 6326 || nSpheroid == 7030)
    2182             :         {
    2183           0 :             nVerticalCSKeyValue = 5030;  // WGS_84_ellipsoid
    2184           0 :             if (nGCS == 4979 || nDatum == 6326)
    2185             :             {
    2186           0 :                 nGCS = 4326;
    2187             :             }
    2188             :         }
    2189           1 :         else if (nDatum >= 6001 && nDatum <= 6033)
    2190             :         {
    2191           0 :             nVerticalCSKeyValue = nDatum - 1000;
    2192             :         }
    2193           1 :         else if (nSpheroid >= 7001 && nSpheroid <= 7033)
    2194             :         {
    2195           1 :             nVerticalCSKeyValue = nSpheroid - 2000;
    2196             :         }
    2197             :     }
    2198             : 
    2199        3388 :     if (nGCS > 32767)
    2200           0 :         nGCS = KvUserDefined;
    2201             : 
    2202             :     /* -------------------------------------------------------------------- */
    2203             :     /*      Get the linear units.                                           */
    2204             :     /* -------------------------------------------------------------------- */
    2205        3388 :     const char *pszLinearUOMNameTmp = nullptr;
    2206        3388 :     const double dfLinearUOM = poSRS->GetLinearUnits(&pszLinearUOMNameTmp);
    2207             :     const std::string osLinearUOMName(pszLinearUOMNameTmp ? pszLinearUOMNameTmp
    2208        6776 :                                                           : "");
    2209        3388 :     int nUOMLengthCode = 9001;  // Meters.
    2210             : 
    2211        3388 :     if (poSRS->GetAuthorityName("PROJCS|UNIT") != nullptr &&
    2212        4471 :         EQUAL(poSRS->GetAuthorityName("PROJCS|UNIT"), "EPSG") &&
    2213        1083 :         poSRS->GetAttrNode("PROJCS|UNIT") != poSRS->GetAttrNode("GEOGCS|UNIT"))
    2214        1083 :         nUOMLengthCode = atoi(poSRS->GetAuthorityCode("PROJCS|UNIT"));
    2215        4610 :     else if (EQUAL(osLinearUOMName.c_str(), SRS_UL_FOOT) ||
    2216        2305 :              fabs(dfLinearUOM - GTIFAtof(SRS_UL_FOOT_CONV)) < 0.0000001)
    2217           0 :         nUOMLengthCode = 9002;  // International foot.
    2218        4609 :     else if (EQUAL(osLinearUOMName.c_str(), SRS_UL_US_FOOT) ||
    2219        2304 :              std::abs(dfLinearUOM - GTIFAtof(SRS_UL_US_FOOT_CONV)) < 0.0000001)
    2220           2 :         nUOMLengthCode = 9003;  // US survey foot.
    2221        2303 :     else if (fabs(dfLinearUOM - 1.0) > 0.00000001)
    2222           2 :         nUOMLengthCode = KvUserDefined;
    2223             : 
    2224             :     /* -------------------------------------------------------------------- */
    2225             :     /*      Get some authority values.                                      */
    2226             :     /* -------------------------------------------------------------------- */
    2227        3388 :     int nPCS = KvUserDefined;
    2228             : 
    2229        4411 :     if (poSRS->GetAuthorityName("PROJCS") != nullptr &&
    2230        1023 :         EQUAL(poSRS->GetAuthorityName("PROJCS"), "EPSG"))
    2231             :     {
    2232        1021 :         nPCS = atoi(poSRS->GetAuthorityCode("PROJCS"));
    2233        1021 :         if (nPCS > 32767)
    2234           0 :             nPCS = KvUserDefined;
    2235             :     }
    2236             : 
    2237             :     /* -------------------------------------------------------------------- */
    2238             :     /*      Handle the projection transformation.                           */
    2239             :     /* -------------------------------------------------------------------- */
    2240        3388 :     bool bWritePEString = false;
    2241        3388 :     bool bUnknownProjection = false;
    2242             : 
    2243        3388 :     const OGRSpatialReference *poSRSCompatibleOfWKT1 = poSRS;
    2244             : #if PROJ_AT_LEAST_VERSION(6, 3, 0)
    2245        6776 :     OGRSpatialReference oSRSCompatibleOfWKT1;
    2246        4473 :     if (poSRS->IsProjected() && !poSRS->IsCompound() &&
    2247        1085 :         poSRS->GetAxesCount() == 3)
    2248             :     {
    2249           0 :         oSRSCompatibleOfWKT1 = *poSRS;
    2250           0 :         oSRSCompatibleOfWKT1.DemoteTo2D(nullptr);
    2251           0 :         poSRSCompatibleOfWKT1 = &oSRSCompatibleOfWKT1;
    2252           0 :         bWritePEString = true;
    2253             :     }
    2254             : #endif
    2255             : 
    2256        3388 :     double dfAngUnitValue = 0;
    2257        3388 :     std::string osAngUnitName;
    2258        3388 :     if (bHasEllipsoid)
    2259             :     {
    2260        3385 :         const char *angUnitNameTmp = "";
    2261        3385 :         dfAngUnitValue = poSRS->GetAngularUnits(&angUnitNameTmp);
    2262        3385 :         osAngUnitName = angUnitNameTmp;
    2263             :     }
    2264             : 
    2265             :     // Convert angular projection parameters from its normalized value in degree
    2266             :     // to the units of GeogAngularUnitsGeoKey.
    2267             :     // Note: for GDAL <= 3.9.0, we always have written them in degrees !
    2268             :     // We can set GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE=YES to get that
    2269             :     // non-conformant behavior...
    2270         266 :     const auto ConvertAngularParam = [dfAngUnitValue](double dfValInDeg)
    2271             :     {
    2272         132 :         constexpr double DEG_TO_RAD = M_PI / 180.0;
    2273         132 :         return dfAngUnitValue != 0 &&
    2274         132 :                        std::fabs(dfAngUnitValue - DEG_TO_RAD) > 1e-10 &&
    2275           4 :                        !CPLTestBool(CPLGetConfigOption(
    2276             :                            "GTIFF_WRITE_ANGULAR_PARAMS_IN_DEGREE", "NO"))
    2277         264 :                    ? dfValInDeg * DEG_TO_RAD / dfAngUnitValue
    2278         132 :                    : dfValInDeg;
    2279        3388 :     };
    2280             : 
    2281             :     const char *pszProjection =
    2282        3388 :         poSRSCompatibleOfWKT1->GetAttrValue("PROJECTION");
    2283        3388 :     if (nPCS != KvUserDefined)
    2284             :     {
    2285             :         // If ESRI_PE flavor is explicitly required, then for EPSG:3857
    2286             :         // we will have to write a completely non-standard definition
    2287             :         // that requires not setting GTModelTypeGeoKey to ProjectedCSTypeGeoKey.
    2288        1021 :         if (eFlavor == GEOTIFF_KEYS_ESRI_PE && nPCS == 3857)
    2289             :         {
    2290           1 :             bWritePEString = true;
    2291             :         }
    2292             :         else
    2293             :         {
    2294        1020 :             GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2295             :                        ModelTypeProjected);
    2296        1020 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2297             :         }
    2298             :     }
    2299        2367 :     else if (poSRSCompatibleOfWKT1->IsGeocentric())
    2300             :     {
    2301           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2302             :                    ModelTypeGeocentric);
    2303             :     }
    2304        2365 :     else if (pszProjection == nullptr)
    2305             :     {
    2306        2294 :         if (poSRSCompatibleOfWKT1->IsGeographic())
    2307        2180 :             GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2308             :                        ModelTypeGeographic);
    2309             :         // Otherwise, presumably something like LOCAL_CS.
    2310             :     }
    2311          71 :     else if (EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
    2312             :     {
    2313           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2314             :                    ModelTypeProjected);
    2315           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2316           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2317             : 
    2318           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2319             :                    CT_AlbersEqualArea);
    2320             : 
    2321           3 :         GTIFKeySet(psGTIF, ProjStdParallelGeoKey, TYPE_DOUBLE, 1,
    2322             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2323             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2324             : 
    2325           3 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    2326             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2327             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    2328             : 
    2329           3 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2330             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2331             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2332             : 
    2333           3 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2334             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2335             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2336             : 
    2337           3 :         GTIFKeySet(
    2338             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2339             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2340             : 
    2341           3 :         GTIFKeySet(
    2342             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2343             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2344             :     }
    2345             : 
    2346          68 :     else if (poSRSCompatibleOfWKT1->GetUTMZone() != 0)
    2347             :     {
    2348          10 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2349             :                    ModelTypeProjected);
    2350             : 
    2351          10 :         int bNorth = 0;
    2352          10 :         const int nZone = poSRSCompatibleOfWKT1->GetUTMZone(&bNorth);
    2353             : 
    2354          10 :         if (nDatum == Datum_North_American_Datum_1983 && nZone >= 3 &&
    2355           1 :             nZone <= 22 && bNorth && nUOMLengthCode == 9001)
    2356             :         {
    2357           1 :             nPCS = 26900 + nZone;
    2358             : 
    2359           1 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2360             :         }
    2361           9 :         else if (nDatum == Datum_North_American_Datum_1927 && nZone >= 3 &&
    2362           1 :                  nZone <= 22 && bNorth && nUOMLengthCode == 9001)
    2363             :         {
    2364           1 :             nPCS = 26700 + nZone;
    2365             : 
    2366           1 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2367             :         }
    2368           8 :         else if (nDatum == Datum_WGS84 && nUOMLengthCode == 9001)
    2369             :         {
    2370           2 :             if (bNorth)
    2371           1 :                 nPCS = 32600 + nZone;
    2372             :             else
    2373           1 :                 nPCS = 32700 + nZone;
    2374             : 
    2375           2 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, nPCS);
    2376             :         }
    2377             :         else
    2378             :         {
    2379           6 :             const int nProjection = nZone + (bNorth ? 16000 : 16100);
    2380           6 :             GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1,
    2381             :                        KvUserDefined);
    2382             : 
    2383           6 :             GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, nProjection);
    2384             :         }
    2385             :     }
    2386             : 
    2387          58 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
    2388             :     {
    2389           9 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2390             :                    ModelTypeProjected);
    2391           9 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2392           9 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2393             : 
    2394           9 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2395             :                    CT_TransverseMercator);
    2396             : 
    2397           9 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2398             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2399             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2400             : 
    2401           9 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2402             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2403             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2404             : 
    2405           9 :         GTIFKeySet(
    2406             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2407             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2408             : 
    2409           9 :         GTIFKeySet(
    2410             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2411             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2412             : 
    2413           9 :         GTIFKeySet(
    2414             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2415             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2416             :     }
    2417             : 
    2418          49 :     else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR_SOUTH_ORIENTED))
    2419             :     {
    2420           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2421             :                    ModelTypeProjected);
    2422           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2423           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2424             : 
    2425           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2426             :                    CT_TransvMercator_SouthOriented);
    2427             : 
    2428           0 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2429             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2430             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2431             : 
    2432           0 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2433             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2434             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2435             : 
    2436           0 :         GTIFKeySet(
    2437             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2438             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2439             : 
    2440           0 :         GTIFKeySet(
    2441             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2442             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2443             : 
    2444           0 :         GTIFKeySet(
    2445             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2446             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2447             :     }
    2448             : 
    2449          49 :     else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
    2450          48 :              EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
    2451             : 
    2452             :     {
    2453           5 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2454             :                    ModelTypeProjected);
    2455           5 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2456           5 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2457             : 
    2458           5 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Mercator);
    2459             : 
    2460           5 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2461             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2462             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2463             : 
    2464           5 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2465             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2466             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2467             : 
    2468           5 :         if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP))
    2469           1 :             GTIFKeySet(
    2470             :                 psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2471             :                 ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2472             :                     SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2473             :         else
    2474           4 :             GTIFKeySet(psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2475             :                        poSRSCompatibleOfWKT1->GetNormProjParm(
    2476             :                            SRS_PP_SCALE_FACTOR, 1.0));
    2477             : 
    2478           5 :         GTIFKeySet(
    2479             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2480             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2481             : 
    2482           5 :         GTIFKeySet(
    2483             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2484             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2485             :     }
    2486             : 
    2487          44 :     else if (EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC))
    2488             :     {
    2489           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2490             :                    ModelTypeProjected);
    2491           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2492           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2493             : 
    2494           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2495             :                    CT_ObliqueStereographic);
    2496             : 
    2497           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2498             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2499             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2500             : 
    2501           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2502             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2503             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2504             : 
    2505           1 :         GTIFKeySet(
    2506             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2507             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2508             : 
    2509           1 :         GTIFKeySet(
    2510             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2511             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2512             : 
    2513           1 :         GTIFKeySet(
    2514             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2515             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2516             :     }
    2517             : 
    2518          43 :     else if (EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC))
    2519             :     {
    2520           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2521             :                    ModelTypeProjected);
    2522           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2523           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2524             : 
    2525           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2526             :                    CT_Stereographic);
    2527             : 
    2528           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2529             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2530             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2531             : 
    2532           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2533             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2534             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2535             : 
    2536           0 :         GTIFKeySet(
    2537             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2538             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2539             : 
    2540           0 :         GTIFKeySet(
    2541             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2542             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2543             : 
    2544           0 :         GTIFKeySet(
    2545             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2546             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2547             :     }
    2548             : 
    2549          43 :     else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
    2550             :     {
    2551           4 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2552             :                    ModelTypeProjected);
    2553           4 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2554           4 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2555             : 
    2556           4 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2557             :                    CT_PolarStereographic);
    2558             : 
    2559           4 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2560             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2561             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2562             : 
    2563           4 :         GTIFKeySet(psGTIF, ProjStraightVertPoleLongGeoKey, TYPE_DOUBLE, 1,
    2564             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2565             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2566             : 
    2567           4 :         GTIFKeySet(
    2568             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2569             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2570             : 
    2571           4 :         GTIFKeySet(
    2572             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2573             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2574             : 
    2575           4 :         GTIFKeySet(
    2576             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2577             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2578             :     }
    2579             : 
    2580          39 :     else if (EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
    2581             :     {
    2582           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2583             :                    ModelTypeProjected);
    2584           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2585           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2586             : 
    2587           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2588             :                    CT_ObliqueMercator);
    2589             : 
    2590           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2591             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2592             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2593             : 
    2594           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2595             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2596             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2597             : 
    2598           1 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2599             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2600             : 
    2601           1 :         GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1,
    2602             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2603             :                        SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)));
    2604             : 
    2605           1 :         GTIFKeySet(
    2606             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2607             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2608             : 
    2609           1 :         GTIFKeySet(
    2610             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2611             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2612             : 
    2613           1 :         GTIFKeySet(
    2614             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2615             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2616             :     }
    2617             : 
    2618          38 :     else if (EQUAL(pszProjection,
    2619             :                    SRS_PT_HOTINE_OBLIQUE_MERCATOR_AZIMUTH_CENTER))
    2620             :     {
    2621           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2622             :                    ModelTypeProjected);
    2623           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2624           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2625             : 
    2626           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2627             :                    CT_HotineObliqueMercatorAzimuthCenter);
    2628             : 
    2629           2 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2630             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2631             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2632             : 
    2633           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2634             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2635             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2636             : 
    2637           2 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2638             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2639             : 
    2640           2 :         GTIFKeySet(psGTIF, ProjRectifiedGridAngleGeoKey, TYPE_DOUBLE, 1,
    2641             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2642             :                        SRS_PP_RECTIFIED_GRID_ANGLE, 0.0)));
    2643             : 
    2644           2 :         GTIFKeySet(
    2645             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2646             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2647             : 
    2648           2 :         GTIFKeySet(
    2649             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2650             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2651             : 
    2652           2 :         GTIFKeySet(
    2653             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2654             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2655             :     }
    2656             : 
    2657          36 :     else if (EQUAL(pszProjection, "Laborde_Oblique_Mercator"))
    2658             :     {
    2659           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2660             :                    ModelTypeProjected);
    2661           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2662           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2663             : 
    2664           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2665             :                    CT_ObliqueMercator_Laborde);
    2666             : 
    2667           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2668             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2669             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2670             : 
    2671           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2672             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2673             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2674             : 
    2675           0 :         GTIFKeySet(psGTIF, ProjAzimuthAngleGeoKey, TYPE_DOUBLE, 1,
    2676             :                    poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_AZIMUTH, 0.0));
    2677             : 
    2678           0 :         GTIFKeySet(
    2679             :             psGTIF, ProjScaleAtCenterGeoKey, TYPE_DOUBLE, 1,
    2680             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2681             : 
    2682           0 :         GTIFKeySet(
    2683             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2684             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2685             : 
    2686           0 :         GTIFKeySet(
    2687             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2688             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2689             :     }
    2690             : 
    2691          36 :     else if (EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER))
    2692             :     {
    2693           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2694             :                    ModelTypeProjected);
    2695           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2696           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2697             : 
    2698           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2699             :                    CT_CassiniSoldner);
    2700             : 
    2701           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2702             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2703             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2704             : 
    2705           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2706             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2707             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2708             : 
    2709           1 :         GTIFKeySet(
    2710             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2711             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2712             : 
    2713           1 :         GTIFKeySet(
    2714             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2715             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2716             :     }
    2717             : 
    2718          35 :     else if (EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC))
    2719             :     {
    2720           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2721             :                    ModelTypeProjected);
    2722           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2723           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2724             : 
    2725           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2726             :                    CT_EquidistantConic);
    2727             : 
    2728           1 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2729             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2730             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2731             : 
    2732           1 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    2733             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2734             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    2735             : 
    2736           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2737             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2738             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2739             : 
    2740           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2741             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2742             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2743             : 
    2744           1 :         GTIFKeySet(
    2745             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2746             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2747             : 
    2748           1 :         GTIFKeySet(
    2749             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2750             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2751             :     }
    2752             : 
    2753          34 :     else if (EQUAL(pszProjection, SRS_PT_POLYCONIC))
    2754             :     {
    2755           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2756             :                    ModelTypeProjected);
    2757           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2758           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2759             : 
    2760           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Polyconic);
    2761             : 
    2762           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2763             :                    poSRSCompatibleOfWKT1->GetNormProjParm(
    2764             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2765             : 
    2766           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2767             :                    poSRSCompatibleOfWKT1->GetNormProjParm(
    2768             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0));
    2769             : 
    2770           1 :         GTIFKeySet(
    2771             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    2772             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2773             : 
    2774           1 :         GTIFKeySet(
    2775             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2776             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2777             : 
    2778           1 :         GTIFKeySet(
    2779             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2780             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2781             :     }
    2782             : 
    2783          33 :     else if (EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT))
    2784             :     {
    2785           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2786             :                    ModelTypeProjected);
    2787           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2788           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2789             : 
    2790           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2791             :                    CT_AzimuthalEquidistant);
    2792             : 
    2793           3 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2794             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2795             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2796             : 
    2797           3 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2798             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2799             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2800             : 
    2801           3 :         GTIFKeySet(
    2802             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2803             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2804             : 
    2805           3 :         GTIFKeySet(
    2806             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2807             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2808             :     }
    2809             : 
    2810          30 :     else if (EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL))
    2811             :     {
    2812           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2813             :                    ModelTypeProjected);
    2814           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2815           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2816             : 
    2817           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2818             :                    CT_MillerCylindrical);
    2819             : 
    2820           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2821             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2822             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2823             : 
    2824           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2825             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2826             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2827             : 
    2828           1 :         GTIFKeySet(
    2829             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2830             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2831             : 
    2832           1 :         GTIFKeySet(
    2833             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2834             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2835             :     }
    2836             : 
    2837          29 :     else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
    2838             :     {
    2839           9 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2840             :                    ModelTypeProjected);
    2841           9 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2842           9 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2843             : 
    2844           9 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2845             :                    CT_Equirectangular);
    2846             : 
    2847           9 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2848             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2849             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2850             : 
    2851           9 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2852             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2853             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2854             : 
    2855           9 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    2856             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2857             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    2858             : 
    2859           9 :         GTIFKeySet(
    2860             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2861             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2862             : 
    2863           9 :         GTIFKeySet(
    2864             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2865             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2866             :     }
    2867             : 
    2868          20 :     else if (EQUAL(pszProjection, SRS_PT_GNOMONIC))
    2869             :     {
    2870           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2871             :                    ModelTypeProjected);
    2872           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2873           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2874             : 
    2875           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Gnomonic);
    2876             : 
    2877           1 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2878             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2879             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2880             : 
    2881           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2882             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2883             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2884             : 
    2885           1 :         GTIFKeySet(
    2886             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2887             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2888             : 
    2889           1 :         GTIFKeySet(
    2890             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2891             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2892             :     }
    2893             : 
    2894          19 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
    2895             :     {
    2896           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2897             :                    ModelTypeProjected);
    2898           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2899           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2900             : 
    2901           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2902             :                    CT_LambertAzimEqualArea);
    2903             : 
    2904           2 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2905             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2906             :                        SRS_PP_LATITUDE_OF_CENTER, 0.0)));
    2907             : 
    2908           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2909             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2910             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2911             : 
    2912           2 :         GTIFKeySet(
    2913             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2914             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2915             : 
    2916           2 :         GTIFKeySet(
    2917             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2918             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2919             :     }
    2920             : 
    2921          17 :     else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
    2922             :     {
    2923           0 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2924             :                    ModelTypeProjected);
    2925           0 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2926           0 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2927             : 
    2928           0 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2929             :                    CT_Orthographic);
    2930             : 
    2931           0 :         GTIFKeySet(psGTIF, ProjCenterLatGeoKey, TYPE_DOUBLE, 1,
    2932             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2933             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2934             : 
    2935           0 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2936             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2937             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2938             : 
    2939           0 :         GTIFKeySet(
    2940             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2941             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2942             : 
    2943           0 :         GTIFKeySet(
    2944             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2945             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2946             :     }
    2947             : 
    2948          17 :     else if (EQUAL(pszProjection, SRS_PT_NEW_ZEALAND_MAP_GRID))
    2949             :     {
    2950           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2951             :                    ModelTypeProjected);
    2952           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2953           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2954             : 
    2955           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    2956             :                    CT_NewZealandMapGrid);
    2957             : 
    2958           1 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    2959             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2960             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    2961             : 
    2962           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    2963             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2964             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2965             : 
    2966           1 :         GTIFKeySet(
    2967             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2968             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2969             : 
    2970           1 :         GTIFKeySet(
    2971             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2972             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2973             :     }
    2974             : 
    2975          16 :     else if (EQUAL(pszProjection, SRS_PT_ROBINSON))
    2976             :     {
    2977           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    2978             :                    ModelTypeProjected);
    2979           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2980           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    2981             : 
    2982           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Robinson);
    2983             : 
    2984           1 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    2985             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    2986             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2987             : 
    2988           1 :         GTIFKeySet(
    2989             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    2990             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    2991             : 
    2992           1 :         GTIFKeySet(
    2993             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    2994             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    2995             :     }
    2996             : 
    2997          15 :     else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
    2998             :     {
    2999           4 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3000             :                    ModelTypeProjected);
    3001           4 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3002           4 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3003             : 
    3004           4 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1, CT_Sinusoidal);
    3005             : 
    3006           4 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    3007             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3008             :                        SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    3009             : 
    3010           4 :         GTIFKeySet(
    3011             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3012             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3013             : 
    3014           4 :         GTIFKeySet(
    3015             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3016             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3017             :     }
    3018             : 
    3019          11 :     else if (EQUAL(pszProjection, SRS_PT_VANDERGRINTEN))
    3020             :     {
    3021           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3022             :                    ModelTypeProjected);
    3023           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3024           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3025             : 
    3026           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3027             :                    CT_VanDerGrinten);
    3028             : 
    3029           2 :         GTIFKeySet(psGTIF, ProjCenterLongGeoKey, TYPE_DOUBLE, 1,
    3030             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3031             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3032             : 
    3033           2 :         GTIFKeySet(
    3034             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3035             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3036             : 
    3037           2 :         GTIFKeySet(
    3038             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3039             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3040             :     }
    3041             : 
    3042           9 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    3043             :     {
    3044           2 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3045             :                    ModelTypeProjected);
    3046           2 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3047           2 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3048             : 
    3049           2 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3050             :                    CT_LambertConfConic_2SP);
    3051             : 
    3052           2 :         GTIFKeySet(psGTIF, ProjFalseOriginLatGeoKey, TYPE_DOUBLE, 1,
    3053             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3054             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    3055             : 
    3056           2 :         GTIFKeySet(psGTIF, ProjFalseOriginLongGeoKey, TYPE_DOUBLE, 1,
    3057             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3058             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3059             : 
    3060           2 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    3061             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3062             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    3063             : 
    3064           2 :         GTIFKeySet(psGTIF, ProjStdParallel2GeoKey, TYPE_DOUBLE, 1,
    3065             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3066             :                        SRS_PP_STANDARD_PARALLEL_2, 0.0)));
    3067             : 
    3068           2 :         GTIFKeySet(
    3069             :             psGTIF, ProjFalseOriginEastingGeoKey, TYPE_DOUBLE, 1,
    3070             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3071             : 
    3072           2 :         GTIFKeySet(
    3073             :             psGTIF, ProjFalseOriginNorthingGeoKey, TYPE_DOUBLE, 1,
    3074             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3075             :     }
    3076             : 
    3077           7 :     else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
    3078             :     {
    3079           3 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3080             :                    ModelTypeProjected);
    3081           3 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3082           3 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3083             : 
    3084           3 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3085             :                    CT_LambertConfConic_1SP);
    3086             : 
    3087           3 :         GTIFKeySet(psGTIF, ProjNatOriginLatGeoKey, TYPE_DOUBLE, 1,
    3088             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3089             :                        SRS_PP_LATITUDE_OF_ORIGIN, 0.0)));
    3090             : 
    3091           3 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    3092             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3093             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3094             : 
    3095           3 :         GTIFKeySet(
    3096             :             psGTIF, ProjScaleAtNatOriginGeoKey, TYPE_DOUBLE, 1,
    3097             :             poSRSCompatibleOfWKT1->GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    3098             : 
    3099           3 :         GTIFKeySet(
    3100             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3101             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3102             : 
    3103           3 :         GTIFKeySet(
    3104             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3105             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3106             :     }
    3107             : 
    3108           4 :     else if (EQUAL(pszProjection, SRS_PT_CYLINDRICAL_EQUAL_AREA))
    3109             :     {
    3110           1 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1,
    3111             :                    ModelTypeProjected);
    3112           1 :         GTIFKeySet(psGTIF, ProjectedCSTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3113           1 :         GTIFKeySet(psGTIF, ProjectionGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3114             : 
    3115           1 :         GTIFKeySet(psGTIF, ProjCoordTransGeoKey, TYPE_SHORT, 1,
    3116             :                    CT_CylindricalEqualArea);
    3117             : 
    3118           1 :         GTIFKeySet(psGTIF, ProjNatOriginLongGeoKey, TYPE_DOUBLE, 1,
    3119             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3120             :                        SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    3121             : 
    3122           1 :         GTIFKeySet(psGTIF, ProjStdParallel1GeoKey, TYPE_DOUBLE, 1,
    3123             :                    ConvertAngularParam(poSRSCompatibleOfWKT1->GetNormProjParm(
    3124             :                        SRS_PP_STANDARD_PARALLEL_1, 0.0)));
    3125             : 
    3126           1 :         GTIFKeySet(
    3127             :             psGTIF, ProjFalseEastingGeoKey, TYPE_DOUBLE, 1,
    3128             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_EASTING, 0.0));
    3129             : 
    3130           1 :         GTIFKeySet(
    3131             :             psGTIF, ProjFalseNorthingGeoKey, TYPE_DOUBLE, 1,
    3132             :             poSRSCompatibleOfWKT1->GetProjParm(SRS_PP_FALSE_NORTHING, 0.0));
    3133             :     }
    3134             :     else
    3135             :     {
    3136           3 :         bWritePEString = true;
    3137           3 :         bUnknownProjection = true;
    3138             :     }
    3139             : 
    3140             :     // Note that VERTCS is an ESRI "spelling" of VERT_CS so we assume if
    3141             :     // we find it that we should try to treat this as a PE string.
    3142        6775 :     if (eFlavor == GEOTIFF_KEYS_ESRI_PE ||
    3143        3387 :         poSRS->GetAttrValue("VERTCS") != nullptr)
    3144             :     {
    3145           1 :         bWritePEString = true;
    3146             :     }
    3147             : 
    3148        3388 :     if (nPCS == KvUserDefined)
    3149             :     {
    3150             :         const char *pszPROJ4Ext =
    3151        2363 :             poSRS->GetExtension("PROJCS", "PROJ4", nullptr);
    3152        2363 :         if (pszPROJ4Ext &&
    3153           4 :             strstr(pszPROJ4Ext, "+proj=merc +a=6378137 +b=6378137"))
    3154             :         {
    3155           3 :             bWritePEString = true;
    3156             :         }
    3157             :     }
    3158             : 
    3159        3388 :     bWritePEString &=
    3160        3388 :         CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES"));
    3161             : 
    3162        3388 :     bool peStrStored = false;
    3163             : 
    3164        3388 :     if (bWritePEString)
    3165             :     {
    3166             :         // Anything we can't map, store as an ESRI PE string with a citation
    3167             :         // key.
    3168           7 :         char *pszPEString = nullptr;
    3169             :         // We cheat a bit, but if we have a custom_proj4, do not morph to ESRI
    3170             :         // so as to keep the EXTENSION PROJ4 node
    3171           7 :         const char *const apszOptionsDefault[] = {nullptr};
    3172           7 :         const char *const apszOptionsEsri[] = {"FORMAT=WKT1_ESRI", nullptr};
    3173           7 :         const char *const *papszOptions = apszOptionsDefault;
    3174          17 :         if (!(bUnknownProjection &&
    3175          14 :               poSRS->GetExtension("PROJCS", "PROJ4", nullptr) != nullptr) &&
    3176           7 :             !(poSRS->IsProjected() && !poSRS->IsCompound() &&
    3177           7 :               poSRS->GetAxesCount() == 3))
    3178             :         {
    3179           7 :             papszOptions = apszOptionsEsri;
    3180             :         }
    3181           7 :         poSRS->exportToWkt(&pszPEString, papszOptions);
    3182           7 :         const int peStrLen = static_cast<int>(strlen(pszPEString));
    3183           7 :         if (peStrLen > 0)
    3184             :         {
    3185             :             char *outPeStr = static_cast<char *>(
    3186           7 :                 CPLMalloc(peStrLen + strlen("ESRI PE String = ") + 1));
    3187           7 :             strcpy(outPeStr, "ESRI PE String = ");
    3188           7 :             strcat(outPeStr, pszPEString);
    3189           7 :             oMapAsciiKeys[PCSCitationGeoKey] = outPeStr;
    3190           7 :             peStrStored = true;
    3191           7 :             CPLFree(outPeStr);
    3192             :         }
    3193           7 :         CPLFree(pszPEString);
    3194           7 :         GTIFKeySet(psGTIF, GTModelTypeGeoKey, TYPE_SHORT, 1, KvUserDefined);
    3195             : 
    3196             :         // Not completely sure we really need to imitate ArcGIS to that point
    3197             :         // but that cannot hurt.
    3198           7 :         if (nPCS == 3857)
    3199             :         {
    3200           1 :             oMapAsciiKeys[GTCitationGeoKey] =
    3201           1 :                 "PCS Name = WGS_1984_Web_Mercator_Auxiliary_Sphere";
    3202           1 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, GCS_WGS_84);
    3203           1 :             GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3204             :                        6378137.0);
    3205           1 :             GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3206             :                        298.257223563);
    3207             :         }
    3208             :     }
    3209             : 
    3210             :     /* -------------------------------------------------------------------- */
    3211             :     /*      Is there a false easting/northing set?  If so, write out a      */
    3212             :     /*      special geokey tag to indicate that GDAL has written these      */
    3213             :     /*      with the proper interpretation of the linear units.             */
    3214             :     /* -------------------------------------------------------------------- */
    3215        3388 :     double dfFE = 0.0;
    3216        3388 :     double dfFN = 0.0;
    3217             : 
    3218        6758 :     if (eVersion == GEOTIFF_VERSION_1_0 &&
    3219        3370 :         (GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFE, 0, 1) ||
    3220        3314 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFN, 0, 1) ||
    3221        3314 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey, &dfFE, 0,
    3222        3312 :                               1) ||
    3223        3312 :          GDALGTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey, &dfFN, 0,
    3224          58 :                               1)) &&
    3225        6758 :         (dfFE != 0.0 || dfFN != 0.0) && nUOMLengthCode != 9001)
    3226             :     {
    3227           2 :         GTIFKeySet(psGTIF, ProjLinearUnitsInterpCorrectGeoKey, TYPE_SHORT, 1,
    3228             :                    static_cast<short>(1));
    3229             :     }
    3230             : 
    3231             :     /* -------------------------------------------------------------------- */
    3232             :     /*      Write linear units information.                                 */
    3233             :     /* -------------------------------------------------------------------- */
    3234        3388 :     if (poSRS->IsGeocentric())
    3235             :     {
    3236           2 :         GTIFKeySet(psGTIF, GeogLinearUnitsGeoKey, TYPE_SHORT, 1,
    3237             :                    nUOMLengthCode);
    3238           2 :         if (nUOMLengthCode == KvUserDefined)
    3239           0 :             GTIFKeySet(psGTIF, GeogLinearUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3240             :                        dfLinearUOM);
    3241             :     }
    3242        4411 :     else if (!poSRS->IsGeographic() &&
    3243        1025 :              (nPCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))
    3244             :     {
    3245        1198 :         GTIFKeySet(psGTIF, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
    3246             :                    nUOMLengthCode);
    3247        1198 :         if (nUOMLengthCode == KvUserDefined)
    3248           2 :             GTIFKeySet(psGTIF, ProjLinearUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3249             :                        dfLinearUOM);
    3250             : 
    3251             :         // If linear units name is available and user defined, store it as
    3252             :         // citation.
    3253        1191 :         if (!peStrStored && nUOMLengthCode == KvUserDefined &&
    3254        2391 :             !osLinearUOMName.empty() &&
    3255           2 :             CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES")))
    3256             :         {
    3257           2 :             SetLinearUnitCitation(oMapAsciiKeys, osLinearUOMName.c_str());
    3258             :         }
    3259             :     }
    3260             : 
    3261             :     /* -------------------------------------------------------------------- */
    3262             :     /*      Write angular units.                                            */
    3263             :     /* -------------------------------------------------------------------- */
    3264             : 
    3265        3388 :     if (bHasEllipsoid &&
    3266        2669 :         (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))
    3267             :     {
    3268        3368 :         if (EQUAL(osAngUnitName.c_str(), "Degree"))
    3269        3357 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3270             :                        Angular_Degree);
    3271          11 :         else if (EQUAL(osAngUnitName.c_str(), "arc-second"))
    3272           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3273             :                        Angular_Arc_Second);
    3274          10 :         else if (EQUAL(osAngUnitName.c_str(), "arc-minute"))
    3275           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3276             :                        Angular_Arc_Minute);
    3277           9 :         else if (EQUAL(osAngUnitName.c_str(), "grad"))
    3278           4 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3279             :                        Angular_Grad);
    3280           5 :         else if (EQUAL(osAngUnitName.c_str(), "gon"))
    3281           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3282             :                        Angular_Gon);
    3283           4 :         else if (EQUAL(osAngUnitName.c_str(), "radian"))
    3284           1 :             GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3285             :                        Angular_Radian);
    3286             :         // else if (EQUAL(osAngUnitName.c_str(), "microradian"))
    3287             :         //    GTIFKeySet(psGTIF, GeogAngularUnitsGeoKey, TYPE_SHORT, 1,
    3288             :         //               9109);
    3289             :         else
    3290             :         {
    3291             :             // GeogCitationGeoKey may be rewritten if the gcs is user defined.
    3292           3 :             oMapAsciiKeys[GeogCitationGeoKey] = osAngUnitName;
    3293           3 :             GTIFKeySet(psGTIF, GeogAngularUnitSizeGeoKey, TYPE_DOUBLE, 1,
    3294             :                        dfAngUnitValue);
    3295             :         }
    3296             :     }
    3297             : 
    3298             :     /* -------------------------------------------------------------------- */
    3299             :     /*      Try to write a citation from the main coordinate system         */
    3300             :     /*      name.                                                           */
    3301             :     /* -------------------------------------------------------------------- */
    3302        6665 :     if (poSRS->GetName() != nullptr &&
    3303        3277 :         ((poSRS->IsProjected() &&
    3304        1025 :           (nPCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)) ||
    3305        2193 :          poSRS->IsCompound() || poSRS->IsLocal() ||
    3306        2178 :          (poSRS->IsGeocentric() &&
    3307           0 :           (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0))))
    3308             :     {
    3309        1101 :         if (!(bWritePEString && nPCS == 3857))
    3310             :         {
    3311        1100 :             oMapAsciiKeys[GTCitationGeoKey] = poSRS->GetName();
    3312             :         }
    3313             :     }
    3314             : 
    3315             :     /* -------------------------------------------------------------------- */
    3316             :     /*      Try to write a GCS citation.                                    */
    3317             :     /* -------------------------------------------------------------------- */
    3318        3388 :     if (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)
    3319             :     {
    3320        3371 :         const OGR_SRSNode *poGCS = poSRS->GetAttrNode("GEOGCS");
    3321             : 
    3322        3371 :         if (poGCS != nullptr && poGCS->GetChild(0) != nullptr)
    3323             :         {
    3324        3254 :             oMapAsciiKeys[GeogCitationGeoKey] = poGCS->GetChild(0)->GetValue();
    3325             :         }
    3326             :     }
    3327             : 
    3328             :     /* -------------------------------------------------------------------- */
    3329             :     /*      Try to identify the GCS/datum, scanning the EPSG datum file for */
    3330             :     /*      a match.                                                        */
    3331             :     /* -------------------------------------------------------------------- */
    3332        3388 :     if (nPCS == KvUserDefined)
    3333             :     {
    3334        2903 :         if (nGCS == KvUserDefined && poSRS->IsGeographic() &&
    3335         540 :             std::fabs(poSRS->GetAngularUnits() - CPLAtof(SRS_UA_DEGREE_CONV)) <
    3336             :                 1e-9)
    3337             :         {
    3338         533 :             if (nDatum == Datum_North_American_Datum_1927)
    3339           0 :                 nGCS = GCS_NAD27;
    3340         533 :             else if (nDatum == Datum_North_American_Datum_1983)
    3341           0 :                 nGCS = GCS_NAD83;
    3342         533 :             else if (nDatum == Datum_WGS84 || nDatum == DatumE_WGS84)
    3343         527 :                 nGCS = GCS_WGS_84;
    3344             :         }
    3345             : 
    3346        2363 :         if (nGCS != KvUserDefined)
    3347             :         {
    3348        2176 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1, nGCS);
    3349             :         }
    3350         187 :         else if (nDatum != KvUserDefined)
    3351             :         {
    3352          29 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3353             :                        KvUserDefined);
    3354          29 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1, nDatum);
    3355             :         }
    3356         158 :         else if (nSpheroid != KvUserDefined)
    3357             :         {
    3358           3 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3359             :                        KvUserDefined);
    3360           3 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1,
    3361             :                        KvUserDefined);
    3362           3 :             GTIFKeySet(psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1, nSpheroid);
    3363             :         }
    3364         155 :         else if (dfSemiMajor != 0.0)
    3365             :         {
    3366          41 :             GTIFKeySet(psGTIF, GeographicTypeGeoKey, TYPE_SHORT, 1,
    3367             :                        KvUserDefined);
    3368          41 :             GTIFKeySet(psGTIF, GeogGeodeticDatumGeoKey, TYPE_SHORT, 1,
    3369             :                        KvUserDefined);
    3370          41 :             GTIFKeySet(psGTIF, GeogEllipsoidGeoKey, TYPE_SHORT, 1,
    3371             :                        KvUserDefined);
    3372          41 :             GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3373             :                        dfSemiMajor);
    3374          41 :             if (dfInvFlattening == 0.0)
    3375          14 :                 GTIFKeySet(psGTIF, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
    3376             :                            dfSemiMajor);
    3377             :             else
    3378          27 :                 GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3379             :                            dfInvFlattening);
    3380             :         }
    3381         114 :         else if (poSRS->GetAttrValue("DATUM") != nullptr &&
    3382         114 :                  strstr(poSRS->GetAttrValue("DATUM"), "unknown") == nullptr &&
    3383           0 :                  strstr(poSRS->GetAttrValue("DATUM"), "unnamed") == nullptr)
    3384             : 
    3385             :         {
    3386           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    3387             :                      "Couldn't translate `%s' to a GeoTIFF datum.",
    3388             :                      poSRS->GetAttrValue("DATUM"));
    3389             :         }
    3390             : 
    3391        2363 :         if (nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0)
    3392             :         {
    3393             :             // Always set InvFlattening if it is available.
    3394             :             // So that it doesn't need to calculate from SemiMinor.
    3395        2354 :             if (dfInvFlattening != 0.0)
    3396        2222 :                 GTIFKeySet(psGTIF, GeogInvFlatteningGeoKey, TYPE_DOUBLE, 1,
    3397             :                            dfInvFlattening);
    3398             :             // Always set SemiMajor to keep the precision and in case of
    3399             :             // editing.
    3400        2354 :             if (dfSemiMajor != 0.0)
    3401        2240 :                 GTIFKeySet(psGTIF, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
    3402             :                            dfSemiMajor);
    3403             : 
    3404        2541 :             if (nGCS == KvUserDefined &&
    3405         187 :                 CPLTestBool(CPLGetConfigOption("GTIFF_ESRI_CITATION", "YES")))
    3406             :             {
    3407         187 :                 SetGeogCSCitation(psGTIF, oMapAsciiKeys, poSRS,
    3408             :                                   osAngUnitName.c_str(), nDatum, nSpheroid);
    3409             :             }
    3410             :         }
    3411             :     }
    3412             : 
    3413             : /* -------------------------------------------------------------------- */
    3414             : /*      Do we have TOWGS84 parameters?                                  */
    3415             : /* -------------------------------------------------------------------- */
    3416             : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
    3417        3388 :     double adfTOWGS84[7] = {0.0};
    3418             : 
    3419        6759 :     if ((nGCS == KvUserDefined || eVersion == GEOTIFF_VERSION_1_0) &&
    3420        3371 :         poSRS->GetTOWGS84(adfTOWGS84) == OGRERR_NONE)
    3421             :     {
    3422             :         // If we are writing a SRS with a EPSG code, and that the EPSG code
    3423             :         // of the current SRS object and the one coming from the EPSG code
    3424             :         // are the same, then by default, do not write them.
    3425          23 :         bool bUseReferenceTOWGS84 = false;
    3426          23 :         const char *pszAuthName = poSRS->GetAuthorityName(nullptr);
    3427          23 :         const char *pszAuthCode = poSRS->GetAuthorityCode(nullptr);
    3428          23 :         if (pszAuthName && EQUAL(pszAuthName, "EPSG") && pszAuthCode)
    3429             :         {
    3430          24 :             CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    3431          24 :             OGRSpatialReference oRefSRS;
    3432          12 :             double adfRefTOWGS84[7] = {0.0};
    3433          12 :             if (oRefSRS.importFromEPSG(atoi(pszAuthCode)) == OGRERR_NONE)
    3434             :             {
    3435          12 :                 oRefSRS.AddGuessedTOWGS84();
    3436          15 :                 if (oRefSRS.GetTOWGS84(adfRefTOWGS84) == OGRERR_NONE &&
    3437           3 :                     memcmp(adfRefTOWGS84, adfTOWGS84, sizeof(adfTOWGS84)) == 0)
    3438             :                 {
    3439           2 :                     bUseReferenceTOWGS84 = true;
    3440             :                 }
    3441             :             }
    3442             :         }
    3443             :         const char *pszWriteTOWGS84 =
    3444          23 :             CPLGetConfigOption("GTIFF_WRITE_TOWGS84", "AUTO");
    3445          23 :         if ((EQUAL(pszWriteTOWGS84, "YES") || EQUAL(pszWriteTOWGS84, "TRUE") ||
    3446          22 :              EQUAL(pszWriteTOWGS84, "ON")) ||
    3447          22 :             (!bUseReferenceTOWGS84 && EQUAL(pszWriteTOWGS84, "AUTO")))
    3448             :         {
    3449          22 :             if (adfTOWGS84[3] == 0.0 && adfTOWGS84[4] == 0.0 &&
    3450          18 :                 adfTOWGS84[5] == 0.0 && adfTOWGS84[6] == 0.0)
    3451             :             {
    3452          15 :                 if (nGCS == GCS_WGS_84 && adfTOWGS84[0] == 0.0 &&
    3453           9 :                     adfTOWGS84[1] == 0.0 && adfTOWGS84[2] == 0.0)
    3454             :                 {
    3455             :                     ;  // Do nothing.
    3456             :                 }
    3457             :                 else
    3458           6 :                     GTIFKeySet(psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 3,
    3459             :                                adfTOWGS84);
    3460             :             }
    3461             :             else
    3462           7 :                 GTIFKeySet(psGTIF, GeogTOWGS84GeoKey, TYPE_DOUBLE, 7,
    3463             :                            adfTOWGS84);
    3464             :         }
    3465             :     }
    3466             : #endif
    3467             : 
    3468             :     /* -------------------------------------------------------------------- */
    3469             :     /*      Do we have vertical information to set?                         */
    3470             :     /* -------------------------------------------------------------------- */
    3471        3388 :     if (poSRS->GetAttrValue("COMPD_CS|VERT_CS") != nullptr)
    3472             :     {
    3473          13 :         bool bGotVertCSCode = false;
    3474          13 :         const char *pszVertCSCode = poSRS->GetAuthorityCode("COMPD_CS|VERT_CS");
    3475             :         const char *pszVertCSAuthName =
    3476          13 :             poSRS->GetAuthorityName("COMPD_CS|VERT_CS");
    3477          13 :         if (pszVertCSCode && pszVertCSAuthName && atoi(pszVertCSCode) &&
    3478          11 :             EQUAL(pszVertCSAuthName, "EPSG"))
    3479             :         {
    3480          10 :             bGotVertCSCode = true;
    3481          10 :             GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3482             :                        atoi(pszVertCSCode));
    3483             :         }
    3484           3 :         else if (eVersion >= GEOTIFF_VERSION_1_1)
    3485             :         {
    3486           3 :             GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3487             :                        KvUserDefined);
    3488             :         }
    3489             : 
    3490          13 :         if (eVersion == GEOTIFF_VERSION_1_0 || !bGotVertCSCode)
    3491             :         {
    3492           0 :             oMapAsciiKeys[VerticalCitationGeoKey] =
    3493           3 :                 poSRS->GetAttrValue("COMPD_CS|VERT_CS");
    3494             : 
    3495             :             const char *pszVertDatumCode =
    3496           3 :                 poSRS->GetAuthorityCode("COMPD_CS|VERT_CS|VERT_DATUM");
    3497             :             const char *pszVertDatumAuthName =
    3498           3 :                 poSRS->GetAuthorityName("COMPD_CS|VERT_CS|VERT_DATUM");
    3499           3 :             if (pszVertDatumCode && pszVertDatumAuthName &&
    3500           2 :                 atoi(pszVertDatumCode) && EQUAL(pszVertDatumAuthName, "EPSG"))
    3501             :             {
    3502           1 :                 GTIFKeySet(psGTIF, VerticalDatumGeoKey, TYPE_SHORT, 1,
    3503             :                            atoi(pszVertDatumCode));
    3504             :             }
    3505           2 :             else if (eVersion >= GEOTIFF_VERSION_1_1)
    3506             :             {
    3507           2 :                 GTIFKeySet(psGTIF, VerticalDatumGeoKey, TYPE_SHORT, 1,
    3508             :                            KvUserDefined);
    3509             :             }
    3510             : 
    3511             :             const char *pszVertUnitCode =
    3512           3 :                 poSRS->GetAuthorityCode("COMPD_CS|VERT_CS|UNIT");
    3513             :             const char *pszVertUnitAuthName =
    3514           3 :                 poSRS->GetAuthorityName("COMPD_CS|VERT_CS|UNIT");
    3515           3 :             if (pszVertUnitCode && pszVertUnitAuthName &&
    3516           3 :                 atoi(pszVertUnitCode) && EQUAL(pszVertUnitAuthName, "EPSG"))
    3517             :             {
    3518           3 :                 GTIFKeySet(psGTIF, VerticalUnitsGeoKey, TYPE_SHORT, 1,
    3519             :                            atoi(pszVertUnitCode));
    3520             :             }
    3521           0 :             else if (eVersion >= GEOTIFF_VERSION_1_1)
    3522             :             {
    3523           0 :                 GTIFKeySet(psGTIF, VerticalUnitsGeoKey, TYPE_SHORT, 1,
    3524             :                            KvUserDefined);
    3525             :             }
    3526             :         }
    3527             :     }
    3528        3375 :     else if (eVersion >= GEOTIFF_VERSION_1_1 && nVerticalCSKeyValue != 0)
    3529             :     {
    3530           3 :         GTIFKeySet(psGTIF, VerticalCSTypeGeoKey, TYPE_SHORT, 1,
    3531             :                    nVerticalCSKeyValue);
    3532             :     }
    3533             : 
    3534        3388 :     const double dfCoordinateEpoch = poSRS->GetCoordinateEpoch();
    3535        3388 :     if (dfCoordinateEpoch > 0)
    3536             :     {
    3537           2 :         GTIFKeySet(psGTIF, CoordinateEpochGeoKey, TYPE_DOUBLE, 1,
    3538             :                    dfCoordinateEpoch);
    3539             :     }
    3540             : 
    3541             :     /* -------------------------------------------------------------------- */
    3542             :     /*      Write all ascii keys                                            */
    3543             :     /* -------------------------------------------------------------------- */
    3544        7755 :     for (const auto &oIter : oMapAsciiKeys)
    3545             :     {
    3546        4367 :         GTIFKeySet(psGTIF, oIter.first, TYPE_ASCII, 0, oIter.second.c_str());
    3547             :     }
    3548             : 
    3549        6776 :     return TRUE;
    3550             : }
    3551             : 
    3552             : /************************************************************************/
    3553             : /*                         GTIFWktFromMemBuf()                          */
    3554             : /************************************************************************/
    3555             : 
    3556           0 : CPLErr GTIFWktFromMemBuf(int nSize, unsigned char *pabyBuffer, char **ppszWKT,
    3557             :                          double *padfGeoTransform, int *pnGCPCount,
    3558             :                          GDAL_GCP **ppasGCPList)
    3559             : {
    3560           0 :     OGRSpatialReferenceH hSRS = nullptr;
    3561           0 :     if (ppszWKT)
    3562           0 :         *ppszWKT = nullptr;
    3563             :     CPLErr eErr =
    3564           0 :         GTIFWktFromMemBufEx(nSize, pabyBuffer, &hSRS, padfGeoTransform,
    3565             :                             pnGCPCount, ppasGCPList, nullptr, nullptr);
    3566           0 :     if (eErr == CE_None)
    3567             :     {
    3568           0 :         if (hSRS && ppszWKT)
    3569             :         {
    3570           0 :             OSRExportToWkt(hSRS, ppszWKT);
    3571             :         }
    3572             :     }
    3573           0 :     OSRDestroySpatialReference(hSRS);
    3574           0 :     return eErr;
    3575             : }
    3576             : 
    3577         581 : CPLErr GTIFWktFromMemBufEx(int nSize, unsigned char *pabyBuffer,
    3578             :                            OGRSpatialReferenceH *phSRS,
    3579             :                            double *padfGeoTransform, int *pnGCPCount,
    3580             :                            GDAL_GCP **ppasGCPList, int *pbPixelIsPoint,
    3581             :                            char ***ppapszRPCMD)
    3582             : 
    3583             : {
    3584             :     const std::string osFilename(
    3585        1162 :         VSIMemGenerateHiddenFilename("wkt_from_mem_buf.tif"));
    3586             : 
    3587             :     /* -------------------------------------------------------------------- */
    3588             :     /*      Initialization of libtiff and libgeotiff.                       */
    3589             :     /* -------------------------------------------------------------------- */
    3590         581 :     GTiffOneTimeInit();  // For RPC tag.
    3591         581 :     LibgeotiffOneTimeInit();
    3592             : 
    3593             :     /* -------------------------------------------------------------------- */
    3594             :     /*      Create a memory file from the buffer.                           */
    3595             :     /* -------------------------------------------------------------------- */
    3596             :     VSILFILE *fp =
    3597         581 :         VSIFileFromMemBuffer(osFilename.c_str(), pabyBuffer, nSize, FALSE);
    3598         581 :     if (fp == nullptr)
    3599           0 :         return CE_Failure;
    3600             : 
    3601             :     /* -------------------------------------------------------------------- */
    3602             :     /*      Initialize access to the memory geotiff structure.              */
    3603             :     /* -------------------------------------------------------------------- */
    3604         581 :     TIFF *hTIFF = VSI_TIFFOpen(osFilename.c_str(), "rc", fp);
    3605             : 
    3606         581 :     if (hTIFF == nullptr)
    3607             :     {
    3608           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3609             :                  "TIFF/GeoTIFF structure is corrupt.");
    3610           0 :         VSIUnlink(osFilename.c_str());
    3611           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    3612           0 :         return CE_Failure;
    3613             :     }
    3614             : 
    3615             :     /* -------------------------------------------------------------------- */
    3616             :     /*      Get the projection definition.                                  */
    3617             :     /* -------------------------------------------------------------------- */
    3618         581 :     bool bPixelIsPoint = false;
    3619         581 :     bool bPointGeoIgnore = false;
    3620         581 :     unsigned short nRasterType = 0;
    3621             : 
    3622         581 :     GTIF *hGTIF = GTIFNew(hTIFF);
    3623         581 :     if (hGTIF)
    3624         581 :         GTIFAttachPROJContext(hGTIF, OSRGetProjTLSContext());
    3625             : 
    3626        1162 :     if (hGTIF != nullptr &&
    3627         581 :         GDALGTIFKeyGetSHORT(hGTIF, GTRasterTypeGeoKey, &nRasterType, 0, 1) ==
    3628        1162 :             1 &&
    3629         580 :         nRasterType == static_cast<unsigned short>(RasterPixelIsPoint))
    3630             :     {
    3631           5 :         bPixelIsPoint = true;
    3632             :         bPointGeoIgnore =
    3633           5 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3634             :     }
    3635         581 :     if (pbPixelIsPoint)
    3636         581 :         *pbPixelIsPoint = bPixelIsPoint;
    3637         581 :     if (ppapszRPCMD)
    3638         581 :         *ppapszRPCMD = nullptr;
    3639             : 
    3640         581 :     if (phSRS)
    3641             :     {
    3642         581 :         *phSRS = nullptr;
    3643         581 :         if (hGTIF != nullptr)
    3644             :         {
    3645         581 :             GTIFDefn *psGTIFDefn = GTIFAllocDefn();
    3646         581 :             if (GTIFGetDefn(hGTIF, psGTIFDefn))
    3647             :             {
    3648         580 :                 *phSRS = GTIFGetOGISDefnAsOSR(hGTIF, psGTIFDefn);
    3649             :             }
    3650         581 :             GTIFFreeDefn(psGTIFDefn);
    3651             :         }
    3652             :     }
    3653         581 :     if (hGTIF)
    3654         581 :         GTIFFree(hGTIF);
    3655             : 
    3656             :     /* -------------------------------------------------------------------- */
    3657             :     /*      Get geotransform or tiepoints.                                  */
    3658             :     /* -------------------------------------------------------------------- */
    3659         581 :     double *padfTiePoints = nullptr;
    3660         581 :     double *padfScale = nullptr;
    3661         581 :     double *padfMatrix = nullptr;
    3662         581 :     int16_t nCount = 0;
    3663             : 
    3664         581 :     padfGeoTransform[0] = 0.0;
    3665         581 :     padfGeoTransform[1] = 1.0;
    3666         581 :     padfGeoTransform[2] = 0.0;
    3667         581 :     padfGeoTransform[3] = 0.0;
    3668         581 :     padfGeoTransform[4] = 0.0;
    3669         581 :     padfGeoTransform[5] = 1.0;
    3670             : 
    3671         581 :     *pnGCPCount = 0;
    3672         581 :     *ppasGCPList = nullptr;
    3673             : 
    3674        1123 :     if (TIFFGetField(hTIFF, TIFFTAG_GEOPIXELSCALE, &nCount, &padfScale) &&
    3675         542 :         nCount >= 2)
    3676             :     {
    3677         542 :         padfGeoTransform[1] = padfScale[0];
    3678         542 :         padfGeoTransform[5] = -std::abs(padfScale[1]);
    3679             : 
    3680         542 :         if (TIFFGetField(hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
    3681        1084 :                          &padfTiePoints) &&
    3682         542 :             nCount >= 6)
    3683             :         {
    3684         542 :             padfGeoTransform[0] =
    3685         542 :                 padfTiePoints[3] - padfTiePoints[0] * padfGeoTransform[1];
    3686         542 :             padfGeoTransform[3] =
    3687         542 :                 padfTiePoints[4] - padfTiePoints[1] * padfGeoTransform[5];
    3688             : 
    3689             :             // Adjust for pixel is point in transform.
    3690         542 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3691             :             {
    3692           4 :                 padfGeoTransform[0] -=
    3693           4 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3694           4 :                 padfGeoTransform[3] -=
    3695           4 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3696             :             }
    3697             :         }
    3698             :     }
    3699          39 :     else if (TIFFGetField(hTIFF, TIFFTAG_GEOTIEPOINTS, &nCount,
    3700          52 :                           &padfTiePoints) &&
    3701          13 :              nCount >= 6)
    3702             :     {
    3703          13 :         *pnGCPCount = nCount / 6;
    3704          13 :         *ppasGCPList =
    3705          13 :             static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), *pnGCPCount));
    3706             : 
    3707          48 :         for (int iGCP = 0; iGCP < *pnGCPCount; iGCP++)
    3708             :         {
    3709          35 :             char szID[32] = {};
    3710          35 :             GDAL_GCP *psGCP = *ppasGCPList + iGCP;
    3711             : 
    3712          35 :             snprintf(szID, sizeof(szID), "%d", iGCP + 1);
    3713          35 :             psGCP->pszId = CPLStrdup(szID);
    3714          35 :             psGCP->pszInfo = CPLStrdup("");
    3715          35 :             psGCP->dfGCPPixel = padfTiePoints[iGCP * 6 + 0];
    3716          35 :             psGCP->dfGCPLine = padfTiePoints[iGCP * 6 + 1];
    3717          35 :             psGCP->dfGCPX = padfTiePoints[iGCP * 6 + 3];
    3718          35 :             psGCP->dfGCPY = padfTiePoints[iGCP * 6 + 4];
    3719          35 :             psGCP->dfGCPZ = padfTiePoints[iGCP * 6 + 5];
    3720             :         }
    3721             :     }
    3722          26 :     else if (TIFFGetField(hTIFF, TIFFTAG_GEOTRANSMATRIX, &nCount,
    3723          33 :                           &padfMatrix) &&
    3724           7 :              nCount == 16)
    3725             :     {
    3726           7 :         padfGeoTransform[0] = padfMatrix[3];
    3727           7 :         padfGeoTransform[1] = padfMatrix[0];
    3728           7 :         padfGeoTransform[2] = padfMatrix[1];
    3729           7 :         padfGeoTransform[3] = padfMatrix[7];
    3730           7 :         padfGeoTransform[4] = padfMatrix[4];
    3731           7 :         padfGeoTransform[5] = padfMatrix[5];
    3732             :     }
    3733             : 
    3734             :     /* -------------------------------------------------------------------- */
    3735             :     /*      Read RPC                                                        */
    3736             :     /* -------------------------------------------------------------------- */
    3737         581 :     if (ppapszRPCMD != nullptr)
    3738             :     {
    3739         581 :         *ppapszRPCMD = GTiffDatasetReadRPCTag(hTIFF);
    3740             :     }
    3741             : 
    3742             :     /* -------------------------------------------------------------------- */
    3743             :     /*      Cleanup.                                                        */
    3744             :     /* -------------------------------------------------------------------- */
    3745         581 :     XTIFFClose(hTIFF);
    3746         581 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    3747             : 
    3748         581 :     VSIUnlink(osFilename.c_str());
    3749             : 
    3750         581 :     if (phSRS && *phSRS == nullptr)
    3751           1 :         return CE_Failure;
    3752             : 
    3753         580 :     return CE_None;
    3754             : }
    3755             : 
    3756             : /************************************************************************/
    3757             : /*                         GTIFMemBufFromWkt()                          */
    3758             : /************************************************************************/
    3759             : 
    3760           0 : CPLErr GTIFMemBufFromWkt(const char *pszWKT, const double *padfGeoTransform,
    3761             :                          int nGCPCount, const GDAL_GCP *pasGCPList, int *pnSize,
    3762             :                          unsigned char **ppabyBuffer)
    3763             : {
    3764           0 :     OGRSpatialReference oSRS;
    3765           0 :     if (pszWKT != nullptr)
    3766           0 :         oSRS.importFromWkt(pszWKT);
    3767           0 :     return GTIFMemBufFromSRS(OGRSpatialReference::ToHandle(&oSRS),
    3768             :                              padfGeoTransform, nGCPCount, pasGCPList, pnSize,
    3769           0 :                              ppabyBuffer, FALSE, nullptr);
    3770             : }
    3771             : 
    3772         226 : CPLErr GTIFMemBufFromSRS(OGRSpatialReferenceH hSRS,
    3773             :                          const double *padfGeoTransform, int nGCPCount,
    3774             :                          const GDAL_GCP *pasGCPList, int *pnSize,
    3775             :                          unsigned char **ppabyBuffer, int bPixelIsPoint,
    3776             :                          char **papszRPCMD)
    3777             : 
    3778             : {
    3779             :     const std::string osFilename(
    3780         452 :         VSIMemGenerateHiddenFilename("wkt_from_mem_buf.tif"));
    3781             : 
    3782             :     /* -------------------------------------------------------------------- */
    3783             :     /*      Initialization of libtiff and libgeotiff.                       */
    3784             :     /* -------------------------------------------------------------------- */
    3785         226 :     GTiffOneTimeInit();  // For RPC tag.
    3786         226 :     LibgeotiffOneTimeInit();
    3787             : 
    3788             :     /* -------------------------------------------------------------------- */
    3789             :     /*      Initialize access to the memory geotiff structure.              */
    3790             :     /* -------------------------------------------------------------------- */
    3791         226 :     VSILFILE *fpL = VSIFOpenL(osFilename.c_str(), "w");
    3792         226 :     if (fpL == nullptr)
    3793           0 :         return CE_Failure;
    3794             : 
    3795         226 :     TIFF *hTIFF = VSI_TIFFOpen(osFilename.c_str(), "w", fpL);
    3796             : 
    3797         226 :     if (hTIFF == nullptr)
    3798             :     {
    3799           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3800             :                  "TIFF/GeoTIFF structure is corrupt.");
    3801           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    3802           0 :         VSIUnlink(osFilename.c_str());
    3803           0 :         return CE_Failure;
    3804             :     }
    3805             : 
    3806             :     /* -------------------------------------------------------------------- */
    3807             :     /*      Write some minimal set of image parameters.                     */
    3808             :     /* -------------------------------------------------------------------- */
    3809         226 :     TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, 1);
    3810         226 :     TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, 1);
    3811         226 :     TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, 8);
    3812         226 :     TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
    3813         226 :     TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, 1);
    3814         226 :     TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    3815         226 :     TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    3816             : 
    3817             :     /* -------------------------------------------------------------------- */
    3818             :     /*      Get the projection definition.                                  */
    3819             :     /* -------------------------------------------------------------------- */
    3820             : 
    3821         226 :     bool bPointGeoIgnore = false;
    3822         226 :     if (bPixelIsPoint)
    3823             :     {
    3824             :         bPointGeoIgnore =
    3825           1 :             CPLTestBool(CPLGetConfigOption("GTIFF_POINT_GEO_IGNORE", "FALSE"));
    3826             :     }
    3827             : 
    3828         226 :     GTIF *hGTIF = nullptr;
    3829         226 :     if (hSRS != nullptr || bPixelIsPoint)
    3830             :     {
    3831         226 :         hGTIF = GTIFNew(hTIFF);
    3832         226 :         if (hGTIF)
    3833             :         {
    3834         226 :             GTIFAttachPROJContext(hGTIF, OSRGetProjTLSContext());
    3835             : 
    3836         226 :             if (hSRS != nullptr)
    3837         226 :                 GTIFSetFromOGISDefnEx(hGTIF, hSRS, GEOTIFF_KEYS_STANDARD,
    3838             :                                       GEOTIFF_VERSION_1_0);
    3839             : 
    3840         226 :             if (bPixelIsPoint)
    3841             :             {
    3842           1 :                 GTIFKeySet(hGTIF, GTRasterTypeGeoKey, TYPE_SHORT, 1,
    3843             :                            RasterPixelIsPoint);
    3844             :             }
    3845             : 
    3846         226 :             GTIFWriteKeys(hGTIF);
    3847         226 :             GTIFFree(hGTIF);
    3848             :         }
    3849             :     }
    3850             : 
    3851             :     /* -------------------------------------------------------------------- */
    3852             :     /*      Set the geotransform, or GCPs.                                  */
    3853             :     /* -------------------------------------------------------------------- */
    3854             : 
    3855          28 :     if (padfGeoTransform[0] != 0.0 || padfGeoTransform[1] != 1.0 ||
    3856          28 :         padfGeoTransform[2] != 0.0 || padfGeoTransform[3] != 0.0 ||
    3857         254 :         padfGeoTransform[4] != 0.0 || std::abs(padfGeoTransform[5]) != 1.0)
    3858             :     {
    3859             : 
    3860         209 :         if (padfGeoTransform[2] == 0.0 && padfGeoTransform[4] == 0.0)
    3861             :         {
    3862         206 :             double adfPixelScale[3] = {padfGeoTransform[1],
    3863         206 :                                        fabs(padfGeoTransform[5]), 0.0};
    3864             : 
    3865         206 :             TIFFSetField(hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale);
    3866             : 
    3867         206 :             double adfTiePoints[6] = {
    3868         206 :                 0.0, 0.0, 0.0, padfGeoTransform[0], padfGeoTransform[3], 0.0};
    3869             : 
    3870         206 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3871             :             {
    3872           1 :                 adfTiePoints[3] +=
    3873           1 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3874           1 :                 adfTiePoints[4] +=
    3875           1 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3876             :             }
    3877             : 
    3878         206 :             TIFFSetField(hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints);
    3879             :         }
    3880             :         else
    3881             :         {
    3882           3 :             double adfMatrix[16] = {0.0};
    3883             : 
    3884           3 :             adfMatrix[0] = padfGeoTransform[1];
    3885           3 :             adfMatrix[1] = padfGeoTransform[2];
    3886           3 :             adfMatrix[3] = padfGeoTransform[0];
    3887           3 :             adfMatrix[4] = padfGeoTransform[4];
    3888           3 :             adfMatrix[5] = padfGeoTransform[5];
    3889           3 :             adfMatrix[7] = padfGeoTransform[3];
    3890           3 :             adfMatrix[15] = 1.0;
    3891             : 
    3892           3 :             if (bPixelIsPoint && !bPointGeoIgnore)
    3893             :             {
    3894           0 :                 adfMatrix[3] +=
    3895           0 :                     padfGeoTransform[1] * 0.5 + padfGeoTransform[2] * 0.5;
    3896           0 :                 adfMatrix[7] +=
    3897           0 :                     padfGeoTransform[4] * 0.5 + padfGeoTransform[5] * 0.5;
    3898             :             }
    3899             : 
    3900           3 :             TIFFSetField(hTIFF, TIFFTAG_GEOTRANSMATRIX, 16, adfMatrix);
    3901             :         }
    3902             :     }
    3903             : 
    3904             :     /* -------------------------------------------------------------------- */
    3905             :     /*      Otherwise write tiepoints if they are available.                */
    3906             :     /* -------------------------------------------------------------------- */
    3907          17 :     else if (nGCPCount > 0)
    3908             :     {
    3909             :         double *padfTiePoints =
    3910           5 :             static_cast<double *>(CPLMalloc(6 * sizeof(double) * nGCPCount));
    3911             : 
    3912          16 :         for (int iGCP = 0; iGCP < nGCPCount; iGCP++)
    3913             :         {
    3914             : 
    3915          11 :             padfTiePoints[iGCP * 6 + 0] = pasGCPList[iGCP].dfGCPPixel;
    3916          11 :             padfTiePoints[iGCP * 6 + 1] = pasGCPList[iGCP].dfGCPLine;
    3917          11 :             padfTiePoints[iGCP * 6 + 2] = 0;
    3918          11 :             padfTiePoints[iGCP * 6 + 3] = pasGCPList[iGCP].dfGCPX;
    3919          11 :             padfTiePoints[iGCP * 6 + 4] = pasGCPList[iGCP].dfGCPY;
    3920          11 :             padfTiePoints[iGCP * 6 + 5] = pasGCPList[iGCP].dfGCPZ;
    3921             :         }
    3922             : 
    3923           5 :         TIFFSetField(hTIFF, TIFFTAG_GEOTIEPOINTS, 6 * nGCPCount, padfTiePoints);
    3924           5 :         CPLFree(padfTiePoints);
    3925             :     }
    3926             : 
    3927             :     /* -------------------------------------------------------------------- */
    3928             :     /*      Write RPC                                                       */
    3929             :     /* -------------------------------------------------------------------- */
    3930         226 :     if (papszRPCMD != nullptr)
    3931             :     {
    3932           1 :         GTiffDatasetWriteRPCTag(hTIFF, papszRPCMD);
    3933             :     }
    3934             : 
    3935             :     /* -------------------------------------------------------------------- */
    3936             :     /*      Cleanup and return the created memory buffer.                   */
    3937             :     /* -------------------------------------------------------------------- */
    3938         226 :     GByte bySmallImage = 0;
    3939             : 
    3940         226 :     TIFFWriteEncodedStrip(hTIFF, 0, reinterpret_cast<char *>(&bySmallImage), 1);
    3941         226 :     TIFFWriteCheck(hTIFF, TIFFIsTiled(hTIFF), "GTIFMemBufFromWkt");
    3942         226 :     TIFFWriteDirectory(hTIFF);
    3943             : 
    3944         226 :     XTIFFClose(hTIFF);
    3945         226 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    3946             : 
    3947             :     /* -------------------------------------------------------------------- */
    3948             :     /*      Read back from the memory buffer.  It would be preferable       */
    3949             :     /*      to be able to "steal" the memory buffer, but there isn't        */
    3950             :     /*      currently any support for this.                                 */
    3951             :     /* -------------------------------------------------------------------- */
    3952         226 :     GUIntBig nBigLength = 0;
    3953             : 
    3954         226 :     *ppabyBuffer = VSIGetMemFileBuffer(osFilename.c_str(), &nBigLength, TRUE);
    3955         226 :     *pnSize = static_cast<int>(nBigLength);
    3956             : 
    3957         226 :     return CE_None;
    3958             : }

Generated by: LCOV version 1.14