Coverage Report

Created: 2025-08-28 06:57

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