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

Generated by: LCOV version 1.14