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