Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRSpatialReference class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
9 : * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_spatialref.h"
16 :
17 : #include <cmath>
18 : #include <cstddef>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <limits>
23 : #include <string>
24 : #include <mutex>
25 : #include <set>
26 : #include <vector>
27 :
28 : #include "cpl_atomic_ops.h"
29 : #include "cpl_conv.h"
30 : #include "cpl_csv.h"
31 : #include "cpl_error.h"
32 : #include "cpl_error_internal.h"
33 : #include "cpl_http.h"
34 : #include "cpl_json.h"
35 : #include "cpl_multiproc.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 : #include "ogr_core.h"
39 : #include "ogr_p.h"
40 : #include "ogr_proj_p.h"
41 : #include "ogr_srs_api.h"
42 : #include "ogrmitabspatialref.h"
43 :
44 : #include "proj.h"
45 : #include "proj_experimental.h"
46 : #include "proj_constants.h"
47 :
48 : // Exists since 8.0.1
49 : #ifndef PROJ_AT_LEAST_VERSION
50 : #define PROJ_COMPUTE_VERSION(maj, min, patch) \
51 : ((maj)*10000 + (min)*100 + (patch))
52 : #define PROJ_VERSION_NUMBER \
53 : PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, \
54 : PROJ_VERSION_PATCH)
55 : #define PROJ_AT_LEAST_VERSION(maj, min, patch) \
56 : (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
57 : #endif
58 :
59 : #define STRINGIFY(s) #s
60 : #define XSTRINGIFY(s) STRINGIFY(s)
61 :
62 : struct OGRSpatialReference::Private
63 : {
64 : struct Listener : public OGR_SRSNode::Listener
65 : {
66 : OGRSpatialReference::Private *m_poObj = nullptr;
67 :
68 196901 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
69 : {
70 196774 : }
71 :
72 : Listener(const Listener &) = delete;
73 : Listener &operator=(const Listener &) = delete;
74 :
75 1821600 : void notifyChange(OGR_SRSNode *) override
76 : {
77 1821600 : m_poObj->nodesChanged();
78 1821600 : }
79 : };
80 :
81 : OGRSpatialReference *m_poSelf = nullptr;
82 : PJ *m_pj_crs = nullptr;
83 :
84 : // Temporary state used for object construction
85 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
86 : CPLString m_osPrimeMeridianName{};
87 : CPLString m_osAngularUnits{};
88 : CPLString m_osLinearUnits{};
89 : CPLString m_osAxisName[3]{};
90 :
91 : std::vector<std::string> m_wktImportWarnings{};
92 : std::vector<std::string> m_wktImportErrors{};
93 : CPLString m_osAreaName{};
94 :
95 : bool m_bIsThreadSafe = false;
96 : bool m_bNodesChanged = false;
97 : bool m_bNodesWKT2 = false;
98 : OGR_SRSNode *m_poRoot = nullptr;
99 :
100 : double dfFromGreenwich = 0.0;
101 : double dfToMeter = 0.0;
102 : double dfToDegrees = 0.0;
103 : double m_dfAngularUnitToRadian = 0.0;
104 :
105 : int nRefCount = 1;
106 : int bNormInfoSet = FALSE;
107 :
108 : PJ *m_pj_geod_base_crs_temp = nullptr;
109 : PJ *m_pj_proj_crs_cs_temp = nullptr;
110 :
111 : bool m_pj_crs_modified_during_demote = false;
112 : PJ *m_pj_bound_crs_target = nullptr;
113 : PJ *m_pj_bound_crs_co = nullptr;
114 : PJ *m_pj_crs_backup = nullptr;
115 : OGR_SRSNode *m_poRootBackup = nullptr;
116 :
117 : bool m_bMorphToESRI = false;
118 : bool m_bHasCenterLong = false;
119 :
120 : std::shared_ptr<Listener> m_poListener{};
121 :
122 : std::recursive_mutex m_mutex{};
123 :
124 : OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125 : std::vector<int> m_axisMapping{1, 2, 3};
126 :
127 : double m_coordinateEpoch = 0; // as decimal year
128 :
129 : explicit Private(OGRSpatialReference *poSelf);
130 : ~Private();
131 : Private(const Private &) = delete;
132 : Private &operator=(const Private &) = delete;
133 :
134 2 : void SetThreadSafe()
135 : {
136 2 : m_bIsThreadSafe = true;
137 2 : }
138 :
139 : void clear();
140 : void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141 : void setRoot(OGR_SRSNode *poRoot);
142 : void refreshProjObj();
143 : void nodesChanged();
144 : void refreshRootFromProjObj();
145 : void invalidateNodes();
146 :
147 : void setMorphToESRI(bool b);
148 :
149 : PJ *getGeodBaseCRS();
150 : PJ *getProjCRSCoordSys();
151 :
152 : const char *getProjCRSName();
153 : OGRErr replaceConversionAndUnref(PJ *conv);
154 :
155 : void demoteFromBoundCRS();
156 : void undoDemoteFromBoundCRS();
157 :
158 993146 : PJ_CONTEXT *getPROJContext()
159 : {
160 993146 : return OSRGetProjTLSContext();
161 : }
162 :
163 : const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
164 :
165 : void refreshAxisMapping();
166 :
167 : // This structures enables locking during calls to OGRSpatialReference
168 : // public methods. Locking is only needed for instances of
169 : // OGRSpatialReference that have been asked to be thread-safe at
170 : // construction.
171 : // The lock is not just for a single call to OGRSpatialReference::Private,
172 : // but for the series of calls done by a OGRSpatialReference method.
173 : // We need a recursive mutex, because some OGRSpatialReference methods
174 : // may call other ones.
175 : struct OptionalLockGuard
176 : {
177 : Private &m_private;
178 :
179 4500800 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 4500800 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 4500800 : }
184 :
185 4500850 : ~OptionalLockGuard()
186 4500850 : {
187 4500850 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 4500850 : }
190 : };
191 :
192 4500910 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 4500910 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 : #define TAKE_OPTIONAL_LOCK() \
199 : auto lock = d->GetOptionalLockGuard(); \
200 : CPL_IGNORE_RET_VAL(lock)
201 :
202 196896 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
203 : {
204 : const char *pszDefaultAMS =
205 196896 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
206 196970 : if (pszDefaultAMS)
207 : {
208 1 : if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
209 0 : return OAMS_AUTHORITY_COMPLIANT;
210 1 : else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
211 1 : return OAMS_TRADITIONAL_GIS_ORDER;
212 : else
213 : {
214 0 : CPLError(CE_Failure, CPLE_AppDefined,
215 : "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
216 : pszDefaultAMS);
217 : }
218 : }
219 196969 : return OAMS_AUTHORITY_COMPLIANT;
220 : }
221 :
222 196935 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
223 : : m_poSelf(poSelf),
224 196935 : m_poListener(std::shared_ptr<Listener>(new Listener(this)))
225 : {
226 : // Get the default value for m_axisMappingStrategy from the
227 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
228 196753 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
229 196970 : }
230 :
231 784436 : OGRSpatialReference::Private::~Private()
232 : {
233 : // In case we destroy the object not in the thread that created it,
234 : // we need to reassign the PROJ context. Having the context bundled inside
235 : // PJ* deeply sucks...
236 196145 : auto ctxt = getPROJContext();
237 :
238 196154 : proj_assign_context(m_pj_crs, ctxt);
239 196151 : proj_destroy(m_pj_crs);
240 :
241 196148 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
242 196148 : proj_destroy(m_pj_geod_base_crs_temp);
243 :
244 196148 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
245 196147 : proj_destroy(m_pj_proj_crs_cs_temp);
246 :
247 196146 : proj_assign_context(m_pj_bound_crs_target, ctxt);
248 196150 : proj_destroy(m_pj_bound_crs_target);
249 :
250 196145 : proj_assign_context(m_pj_bound_crs_co, ctxt);
251 196148 : proj_destroy(m_pj_bound_crs_co);
252 :
253 196145 : proj_assign_context(m_pj_crs_backup, ctxt);
254 196144 : proj_destroy(m_pj_crs_backup);
255 :
256 196141 : delete m_poRootBackup;
257 196147 : delete m_poRoot;
258 196127 : }
259 :
260 98401 : void OGRSpatialReference::Private::clear()
261 : {
262 98401 : proj_assign_context(m_pj_crs, getPROJContext());
263 98400 : proj_destroy(m_pj_crs);
264 98401 : m_pj_crs = nullptr;
265 :
266 98401 : delete m_poRoot;
267 98401 : m_poRoot = nullptr;
268 98401 : m_bNodesChanged = false;
269 :
270 98401 : m_wktImportWarnings.clear();
271 98401 : m_wktImportErrors.clear();
272 :
273 98401 : m_pj_crs_modified_during_demote = false;
274 98401 : m_pjType = PJ_TYPE_UNKNOWN;
275 98401 : m_osPrimeMeridianName.clear();
276 98401 : m_osAngularUnits.clear();
277 98400 : m_osLinearUnits.clear();
278 :
279 98400 : bNormInfoSet = FALSE;
280 98400 : dfFromGreenwich = 1.0;
281 98400 : dfToMeter = 1.0;
282 98400 : dfToDegrees = 1.0;
283 98400 : m_dfAngularUnitToRadian = 0.0;
284 :
285 98400 : m_bMorphToESRI = false;
286 98400 : m_bHasCenterLong = false;
287 :
288 98400 : m_coordinateEpoch = 0.0;
289 98400 : }
290 :
291 23112 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
292 : {
293 23112 : m_poRoot = poRoot;
294 23112 : if (m_poRoot)
295 : {
296 23112 : m_poRoot->RegisterListener(m_poListener);
297 : }
298 23112 : nodesChanged();
299 23112 : }
300 :
301 157375 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
302 : bool doRefreshAxisMapping)
303 : {
304 157375 : auto ctxt = getPROJContext();
305 :
306 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
307 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
308 : {
309 : const double dfEpoch =
310 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
311 : if (!std::isnan(dfEpoch))
312 : {
313 : m_poSelf->SetCoordinateEpoch(dfEpoch);
314 : }
315 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
316 : proj_destroy(pj_crsIn);
317 : pj_crsIn = crs;
318 : }
319 : #endif
320 :
321 157375 : proj_assign_context(m_pj_crs, ctxt);
322 157375 : proj_destroy(m_pj_crs);
323 157375 : m_pj_crs = pj_crsIn;
324 157375 : if (m_pj_crs)
325 : {
326 157323 : m_pjType = proj_get_type(m_pj_crs);
327 : }
328 157375 : if (m_pj_crs_backup)
329 : {
330 21 : m_pj_crs_modified_during_demote = true;
331 : }
332 157375 : invalidateNodes();
333 157375 : if (doRefreshAxisMapping)
334 : {
335 157355 : refreshAxisMapping();
336 : }
337 157375 : }
338 :
339 597983 : void OGRSpatialReference::Private::refreshProjObj()
340 : {
341 597983 : if (m_bNodesChanged && m_poRoot)
342 : {
343 7281 : char *pszWKT = nullptr;
344 7281 : m_poRoot->exportToWkt(&pszWKT);
345 7281 : auto poRootBackup = m_poRoot;
346 7281 : m_poRoot = nullptr;
347 7281 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
348 7281 : clear();
349 7281 : m_coordinateEpoch = dfCoordinateEpochBackup;
350 7281 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
351 :
352 7281 : const char *const options[] = {
353 : "STRICT=NO",
354 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
355 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
356 : #endif
357 : nullptr
358 : };
359 7281 : PROJ_STRING_LIST warnings = nullptr;
360 7281 : PROJ_STRING_LIST errors = nullptr;
361 7281 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
362 : &warnings, &errors));
363 14227 : for (auto iter = warnings; iter && *iter; ++iter)
364 : {
365 6946 : m_wktImportWarnings.push_back(*iter);
366 : }
367 7495 : for (auto iter = errors; iter && *iter; ++iter)
368 : {
369 214 : m_wktImportErrors.push_back(*iter);
370 : }
371 7281 : proj_string_list_destroy(warnings);
372 7281 : proj_string_list_destroy(errors);
373 :
374 7281 : CPLFree(pszWKT);
375 :
376 7281 : m_poRoot = poRootBackup;
377 7281 : m_bNodesChanged = false;
378 : }
379 597983 : }
380 :
381 25217 : void OGRSpatialReference::Private::refreshRootFromProjObj()
382 : {
383 25217 : CPLAssert(m_poRoot == nullptr);
384 :
385 25217 : if (m_pj_crs)
386 : {
387 46136 : CPLStringList aosOptions;
388 23068 : if (!m_bMorphToESRI)
389 : {
390 23064 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
391 23064 : aosOptions.SetNameValue("MULTILINE", "NO");
392 : }
393 23068 : aosOptions.SetNameValue("STRICT", "NO");
394 :
395 : const char *pszWKT;
396 : {
397 23068 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
398 23068 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
399 23068 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
400 23068 : aosOptions.List());
401 23068 : m_bNodesWKT2 = false;
402 : }
403 23068 : if (!m_bMorphToESRI && pszWKT == nullptr)
404 : {
405 70 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
406 70 : aosOptions.List());
407 70 : m_bNodesWKT2 = true;
408 : }
409 23068 : if (pszWKT)
410 : {
411 23068 : auto root = new OGR_SRSNode();
412 23068 : setRoot(root);
413 23068 : root->importFromWkt(&pszWKT);
414 23068 : m_bNodesChanged = false;
415 : }
416 : }
417 25217 : }
418 :
419 181942 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
420 : {
421 181942 : const char *pszName1 = nullptr;
422 181942 : const char *pszDirection1 = nullptr;
423 181942 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
424 : nullptr, nullptr, nullptr, nullptr);
425 181942 : const char *pszName2 = nullptr;
426 181942 : const char *pszDirection2 = nullptr;
427 181942 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
428 : nullptr, nullptr, nullptr, nullptr);
429 181942 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
430 81057 : EQUAL(pszDirection2, "east"))
431 : {
432 80475 : return true;
433 : }
434 101467 : if (pszDirection1 && pszDirection2 &&
435 101467 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
436 100902 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
437 1117 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
438 258 : STARTS_WITH_CI(pszName2, "easting"))
439 : {
440 258 : return true;
441 : }
442 101209 : return false;
443 : }
444 :
445 233091 : void OGRSpatialReference::Private::refreshAxisMapping()
446 : {
447 233091 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
448 51497 : return;
449 :
450 181594 : bool doUndoDemote = false;
451 181594 : if (m_pj_crs_backup == nullptr)
452 : {
453 181573 : doUndoDemote = true;
454 181573 : demoteFromBoundCRS();
455 : }
456 181594 : const auto ctxt = getPROJContext();
457 181594 : PJ *horizCRS = nullptr;
458 181594 : int axisCount = 0;
459 181594 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
460 : {
461 218 : axisCount = 1;
462 : }
463 181376 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
464 : {
465 1100 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
466 1100 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
467 : {
468 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
469 222 : if (baseCRS)
470 : {
471 222 : proj_destroy(horizCRS);
472 222 : horizCRS = baseCRS;
473 : }
474 : }
475 :
476 1100 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
477 1100 : if (vertCRS)
478 : {
479 1097 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
480 : {
481 391 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
482 391 : if (baseCRS)
483 : {
484 391 : proj_destroy(vertCRS);
485 391 : vertCRS = baseCRS;
486 : }
487 : }
488 :
489 1097 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
490 1097 : if (cs)
491 : {
492 1097 : axisCount += proj_cs_get_axis_count(ctxt, cs);
493 1097 : proj_destroy(cs);
494 : }
495 1097 : proj_destroy(vertCRS);
496 : }
497 : }
498 : else
499 : {
500 180276 : horizCRS = m_pj_crs;
501 : }
502 :
503 181594 : bool bSwitchForGisFriendlyOrder = false;
504 181594 : if (horizCRS)
505 : {
506 181373 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
507 181373 : if (cs)
508 : {
509 181373 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
510 181373 : axisCount += nHorizCSAxisCount;
511 181373 : if (nHorizCSAxisCount >= 2)
512 : {
513 181363 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
514 : }
515 181373 : proj_destroy(cs);
516 : }
517 : }
518 181594 : if (horizCRS != m_pj_crs)
519 : {
520 1318 : proj_destroy(horizCRS);
521 : }
522 181594 : if (doUndoDemote)
523 : {
524 181573 : undoDemoteFromBoundCRS();
525 : }
526 :
527 181594 : m_axisMapping.resize(axisCount);
528 181594 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
529 56394 : !bSwitchForGisFriendlyOrder)
530 : {
531 470624 : for (int i = 0; i < axisCount; i++)
532 : {
533 314211 : m_axisMapping[i] = i + 1;
534 156413 : }
535 : }
536 : else
537 : {
538 25181 : m_axisMapping[0] = 2;
539 25181 : m_axisMapping[1] = 1;
540 25181 : if (axisCount == 3)
541 : {
542 328 : m_axisMapping[2] = 3;
543 : }
544 : }
545 : }
546 :
547 1844710 : void OGRSpatialReference::Private::nodesChanged()
548 : {
549 1844710 : m_bNodesChanged = true;
550 1844710 : }
551 :
552 157657 : void OGRSpatialReference::Private::invalidateNodes()
553 : {
554 157657 : delete m_poRoot;
555 157657 : m_poRoot = nullptr;
556 157657 : m_bNodesChanged = false;
557 157657 : }
558 :
559 282 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
560 : {
561 282 : invalidateNodes();
562 282 : m_bMorphToESRI = b;
563 282 : }
564 :
565 523361 : void OGRSpatialReference::Private::demoteFromBoundCRS()
566 : {
567 523361 : CPLAssert(m_pj_bound_crs_target == nullptr);
568 523361 : CPLAssert(m_pj_bound_crs_co == nullptr);
569 523361 : CPLAssert(m_poRootBackup == nullptr);
570 523361 : CPLAssert(m_pj_crs_backup == nullptr);
571 :
572 523361 : m_pj_crs_modified_during_demote = false;
573 :
574 523361 : if (m_pjType == PJ_TYPE_BOUND_CRS)
575 : {
576 2722 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
577 2722 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
578 2722 : m_pj_bound_crs_co =
579 2722 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
580 :
581 2722 : m_poRootBackup = m_poRoot;
582 2722 : m_poRoot = nullptr;
583 2722 : m_pj_crs_backup = m_pj_crs;
584 2722 : m_pj_crs = baseCRS;
585 2722 : m_pjType = proj_get_type(m_pj_crs);
586 : }
587 523361 : }
588 :
589 523362 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
590 : {
591 523362 : if (m_pj_bound_crs_target)
592 : {
593 2722 : CPLAssert(m_poRoot == nullptr);
594 2722 : CPLAssert(m_pj_crs);
595 2722 : if (!m_pj_crs_modified_during_demote)
596 : {
597 2702 : proj_destroy(m_pj_crs);
598 2702 : m_pj_crs = m_pj_crs_backup;
599 2702 : m_pjType = proj_get_type(m_pj_crs);
600 2702 : m_poRoot = m_poRootBackup;
601 : }
602 : else
603 : {
604 20 : delete m_poRootBackup;
605 20 : m_poRootBackup = nullptr;
606 20 : proj_destroy(m_pj_crs_backup);
607 20 : m_pj_crs_backup = nullptr;
608 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
609 20 : m_pj_bound_crs_target,
610 20 : m_pj_bound_crs_co),
611 : false);
612 : }
613 : }
614 :
615 523362 : m_poRootBackup = nullptr;
616 523362 : m_pj_crs_backup = nullptr;
617 523362 : proj_destroy(m_pj_bound_crs_target);
618 523362 : m_pj_bound_crs_target = nullptr;
619 523362 : proj_destroy(m_pj_bound_crs_co);
620 523362 : m_pj_bound_crs_co = nullptr;
621 523362 : m_pj_crs_modified_during_demote = false;
622 523362 : }
623 :
624 112726 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
625 : const char *pszTargetKey)
626 : {
627 112726 : if (pszTargetKey)
628 : {
629 53779 : demoteFromBoundCRS();
630 53779 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
631 26561 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
632 27292 : EQUAL(pszTargetKey, "GEOGCS"))
633 : {
634 6788 : pszTargetKey = nullptr;
635 : }
636 46991 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
637 20 : EQUAL(pszTargetKey, "GEOCCS"))
638 : {
639 0 : pszTargetKey = nullptr;
640 : }
641 46991 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
642 25180 : EQUAL(pszTargetKey, "PROJCS"))
643 : {
644 3610 : pszTargetKey = nullptr;
645 : }
646 43381 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
647 4 : EQUAL(pszTargetKey, "VERT_CS"))
648 : {
649 2 : pszTargetKey = nullptr;
650 : }
651 53779 : undoDemoteFromBoundCRS();
652 : }
653 112726 : return pszTargetKey;
654 : }
655 :
656 8295 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
657 : {
658 8295 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
659 8242 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
660 : {
661 53 : return m_pj_crs;
662 : }
663 :
664 8242 : auto ctxt = getPROJContext();
665 8242 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
666 : {
667 3749 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
668 3749 : proj_destroy(m_pj_geod_base_crs_temp);
669 3749 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
670 3749 : return m_pj_geod_base_crs_temp;
671 : }
672 :
673 4493 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
674 4493 : proj_destroy(m_pj_geod_base_crs_temp);
675 4493 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
676 : nullptr, 0);
677 4493 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
678 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
679 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
680 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
681 4493 : proj_destroy(cs);
682 :
683 4493 : return m_pj_geod_base_crs_temp;
684 : }
685 :
686 4695 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
687 : {
688 4695 : auto ctxt = getPROJContext();
689 4695 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
690 : {
691 3734 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
692 3734 : proj_destroy(m_pj_proj_crs_cs_temp);
693 3734 : m_pj_proj_crs_cs_temp =
694 3734 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
695 3734 : return m_pj_proj_crs_cs_temp;
696 : }
697 :
698 961 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
699 961 : proj_destroy(m_pj_proj_crs_cs_temp);
700 961 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
701 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
702 961 : return m_pj_proj_crs_cs_temp;
703 : }
704 :
705 4747 : const char *OGRSpatialReference::Private::getProjCRSName()
706 : {
707 4747 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
708 : {
709 3750 : return proj_get_name(m_pj_crs);
710 : }
711 :
712 997 : return "unnamed";
713 : }
714 :
715 1370 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
716 : {
717 1370 : refreshProjObj();
718 :
719 1370 : demoteFromBoundCRS();
720 :
721 : auto projCRS =
722 1370 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
723 1370 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
724 1370 : proj_destroy(conv);
725 :
726 1370 : setPjCRS(projCRS);
727 :
728 1370 : undoDemoteFromBoundCRS();
729 1370 : return OGRERR_NONE;
730 : }
731 :
732 : /************************************************************************/
733 : /* ToPointer() */
734 : /************************************************************************/
735 :
736 23550 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
737 : {
738 23550 : return OGRSpatialReference::FromHandle(hSRS);
739 : }
740 :
741 : /************************************************************************/
742 : /* ToHandle() */
743 : /************************************************************************/
744 :
745 4019 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
746 : {
747 4019 : return OGRSpatialReference::ToHandle(poSRS);
748 : }
749 :
750 : /************************************************************************/
751 : /* OGRsnPrintDouble() */
752 : /************************************************************************/
753 :
754 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
755 :
756 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
757 :
758 : {
759 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
760 :
761 126 : const size_t nLen = strlen(pszStrBuf);
762 :
763 : // The following hack is intended to truncate some "precision" in cases
764 : // that appear to be roundoff error.
765 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
766 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
767 : {
768 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
769 : }
770 :
771 : // Force to user periods regardless of locale.
772 126 : if (strchr(pszStrBuf, ',') != nullptr)
773 : {
774 0 : char *const pszDelim = strchr(pszStrBuf, ',');
775 0 : *pszDelim = '.';
776 : }
777 126 : }
778 :
779 : /************************************************************************/
780 : /* OGRSpatialReference() */
781 : /************************************************************************/
782 :
783 : /**
784 : * \brief Constructor.
785 : *
786 : * This constructor takes an optional string argument which if passed
787 : * should be a WKT representation of an SRS. Passing this is equivalent
788 : * to not passing it, and then calling importFromWkt() with the WKT string.
789 : *
790 : * Note that newly created objects are given a reference count of one.
791 : *
792 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
793 : * object are assumed to be in the order of the axis of the CRS definition
794 : (which
795 : * for example means latitude first, longitude second for geographic CRS
796 : belonging
797 : * to the EPSG authority). It is possible to define a data axis to CRS axis
798 : * mapping strategy with the SetAxisMappingStrategy() method.
799 : *
800 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
801 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
802 : later
803 : * being the default value when the option is not set) to control the value of
804 : the
805 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
806 : * created. Calling SetAxisMappingStrategy() will override this default value.
807 :
808 : * The C function OSRNewSpatialReference() does the same thing as this
809 : * constructor.
810 : *
811 : * @param pszWKT well known text definition to which the object should
812 : * be initialized, or NULL (the default).
813 : */
814 :
815 194725 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
816 194725 : : d(new Private(this))
817 : {
818 194639 : if (pszWKT != nullptr)
819 350 : importFromWkt(pszWKT);
820 194639 : }
821 :
822 : /************************************************************************/
823 : /* OSRNewSpatialReference() */
824 : /************************************************************************/
825 :
826 : /**
827 : * \brief Constructor.
828 : *
829 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
830 : * object are assumed to be in the order of the axis of the CRS definition
831 : * (which for example means latitude first, longitude second for geographic CRS
832 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
833 : * axis mapping strategy with the SetAxisMappingStrategy() method.
834 : *
835 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
836 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
837 : * later being the default value when the option is not set) to control the
838 : * value of the data axis to CRS axis mapping strategy when a
839 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
840 : * override this default value.
841 : *
842 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
843 : */
844 2802 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
845 :
846 : {
847 2802 : OGRSpatialReference *poSRS = new OGRSpatialReference();
848 :
849 2802 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
850 : {
851 257 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
852 : {
853 1 : delete poSRS;
854 1 : poSRS = nullptr;
855 : }
856 : }
857 :
858 2802 : return ToHandle(poSRS);
859 : }
860 :
861 : /************************************************************************/
862 : /* OGRSpatialReference() */
863 : /************************************************************************/
864 :
865 : /** Copy constructor. See also Clone().
866 : * @param oOther other spatial reference
867 : */
868 2218 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
869 2218 : : d(new Private(this))
870 : {
871 2218 : *this = oOther;
872 2218 : }
873 :
874 : /************************************************************************/
875 : /* OGRSpatialReference() */
876 : /************************************************************************/
877 :
878 : /** Move constructor.
879 : * @param oOther other spatial reference
880 : */
881 27 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
882 27 : : d(std::move(oOther.d))
883 : {
884 27 : }
885 :
886 : /************************************************************************/
887 : /* ~OGRSpatialReference() */
888 : /************************************************************************/
889 :
890 : /**
891 : * \brief OGRSpatialReference destructor.
892 : *
893 : * The C function OSRDestroySpatialReference() does the same thing as this
894 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
895 : *
896 : * @deprecated
897 : */
898 :
899 246686 : OGRSpatialReference::~OGRSpatialReference()
900 :
901 : {
902 246658 : }
903 :
904 : /************************************************************************/
905 : /* DestroySpatialReference() */
906 : /************************************************************************/
907 :
908 : /**
909 : * \brief OGRSpatialReference destructor.
910 : *
911 : * This static method will destroy a OGRSpatialReference. It is
912 : * equivalent to calling delete on the object, but it ensures that the
913 : * deallocation is properly executed within the OGR libraries heap on
914 : * platforms where this can matter (win32).
915 : *
916 : * This function is the same as OSRDestroySpatialReference()
917 : *
918 : * @param poSRS the object to delete
919 : *
920 : * @since GDAL 1.7.0
921 : */
922 :
923 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
924 : {
925 0 : delete poSRS;
926 0 : }
927 :
928 : /************************************************************************/
929 : /* OSRDestroySpatialReference() */
930 : /************************************************************************/
931 :
932 : /**
933 : * \brief OGRSpatialReference destructor.
934 : *
935 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
936 : * and OGRSpatialReference::DestroySpatialReference()
937 : *
938 : * @param hSRS the object to delete
939 : */
940 8259 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
941 :
942 : {
943 8259 : delete ToPointer(hSRS);
944 8259 : }
945 :
946 : /************************************************************************/
947 : /* Clear() */
948 : /************************************************************************/
949 :
950 : /**
951 : * \brief Wipe current definition.
952 : *
953 : * Returns OGRSpatialReference to a state with no definition, as it
954 : * exists when first created. It does not affect reference counts.
955 : */
956 :
957 91118 : void OGRSpatialReference::Clear()
958 :
959 : {
960 91118 : d->clear();
961 91120 : }
962 :
963 : /************************************************************************/
964 : /* operator=() */
965 : /************************************************************************/
966 :
967 : /** Assignment operator.
968 : * @param oSource SRS to assign to *this
969 : * @return *this
970 : */
971 : OGRSpatialReference &
972 23088 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
973 :
974 : {
975 23088 : if (&oSource != this)
976 : {
977 23087 : Clear();
978 : #ifdef CPPCHECK
979 : // Otherwise cppcheck would protest that nRefCount isn't modified
980 : d->nRefCount = (d->nRefCount + 1) - 1;
981 : #endif
982 :
983 23088 : oSource.d->refreshProjObj();
984 23088 : if (oSource.d->m_pj_crs)
985 22765 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
986 23088 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
987 10483 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
988 12605 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
989 110 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
990 :
991 23088 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
992 : }
993 :
994 23089 : return *this;
995 : }
996 :
997 : /************************************************************************/
998 : /* operator=() */
999 : /************************************************************************/
1000 :
1001 : /** Move assignment operator.
1002 : * @param oSource SRS to assign to *this
1003 : * @return *this
1004 : */
1005 : OGRSpatialReference &
1006 4011 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1007 :
1008 : {
1009 4011 : if (&oSource != this)
1010 : {
1011 4010 : d = std::move(oSource.d);
1012 : }
1013 :
1014 4011 : return *this;
1015 : }
1016 :
1017 : /************************************************************************/
1018 : /* AssignAndSetThreadSafe() */
1019 : /************************************************************************/
1020 :
1021 : /** Assignment method, with thread-safety.
1022 : *
1023 : * Same as an assignment operator, but asking also that the *this instance
1024 : * becomes thread-safe.
1025 : *
1026 : * @param oSource SRS to assign to *this
1027 : * @return *this
1028 : * @since 3.10
1029 : */
1030 :
1031 : OGRSpatialReference &
1032 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1033 : {
1034 2 : *this = oSource;
1035 2 : d->SetThreadSafe();
1036 2 : return *this;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* Reference() */
1041 : /************************************************************************/
1042 :
1043 : /**
1044 : * \brief Increments the reference count by one.
1045 : *
1046 : * The reference count is used keep track of the number of OGRGeometry objects
1047 : * referencing this SRS.
1048 : *
1049 : * The method does the same thing as the C function OSRReference().
1050 : *
1051 : * @return the updated reference count.
1052 : */
1053 :
1054 4024210 : int OGRSpatialReference::Reference()
1055 :
1056 : {
1057 4024210 : return CPLAtomicInc(&d->nRefCount);
1058 : }
1059 :
1060 : /************************************************************************/
1061 : /* OSRReference() */
1062 : /************************************************************************/
1063 :
1064 : /**
1065 : * \brief Increments the reference count by one.
1066 : *
1067 : * This function is the same as OGRSpatialReference::Reference()
1068 : */
1069 923 : int OSRReference(OGRSpatialReferenceH hSRS)
1070 :
1071 : {
1072 923 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1073 :
1074 923 : return ToPointer(hSRS)->Reference();
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* Dereference() */
1079 : /************************************************************************/
1080 :
1081 : /**
1082 : * \brief Decrements the reference count by one.
1083 : *
1084 : * The method does the same thing as the C function OSRDereference().
1085 : *
1086 : * @return the updated reference count.
1087 : */
1088 :
1089 4060420 : int OGRSpatialReference::Dereference()
1090 :
1091 : {
1092 4060420 : if (d->nRefCount <= 0)
1093 0 : CPLDebug("OSR",
1094 : "Dereference() called on an object with refcount %d,"
1095 : "likely already destroyed!",
1096 0 : d->nRefCount);
1097 4060420 : return CPLAtomicDec(&d->nRefCount);
1098 : }
1099 :
1100 : /************************************************************************/
1101 : /* OSRDereference() */
1102 : /************************************************************************/
1103 :
1104 : /**
1105 : * \brief Decrements the reference count by one.
1106 : *
1107 : * This function is the same as OGRSpatialReference::Dereference()
1108 : */
1109 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1110 :
1111 : {
1112 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1113 :
1114 0 : return ToPointer(hSRS)->Dereference();
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* GetReferenceCount() */
1119 : /************************************************************************/
1120 :
1121 : /**
1122 : * \brief Fetch current reference count.
1123 : *
1124 : * @return the current reference count.
1125 : */
1126 180 : int OGRSpatialReference::GetReferenceCount() const
1127 : {
1128 180 : return d->nRefCount;
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* Release() */
1133 : /************************************************************************/
1134 :
1135 : /**
1136 : * \brief Decrements the reference count by one, and destroy if zero.
1137 : *
1138 : * The method does the same thing as the C function OSRRelease().
1139 : */
1140 :
1141 4057650 : void OGRSpatialReference::Release()
1142 :
1143 : {
1144 4057650 : if (Dereference() <= 0)
1145 36196 : delete this;
1146 4057650 : }
1147 :
1148 : /************************************************************************/
1149 : /* OSRRelease() */
1150 : /************************************************************************/
1151 :
1152 : /**
1153 : * \brief Decrements the reference count by one, and destroy if zero.
1154 : *
1155 : * This function is the same as OGRSpatialReference::Release()
1156 : */
1157 5923 : void OSRRelease(OGRSpatialReferenceH hSRS)
1158 :
1159 : {
1160 5923 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1161 :
1162 5923 : ToPointer(hSRS)->Release();
1163 : }
1164 :
1165 80723 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1166 : {
1167 80723 : TAKE_OPTIONAL_LOCK();
1168 :
1169 80723 : if (!d->m_poRoot)
1170 : {
1171 22793 : d->refreshRootFromProjObj();
1172 : }
1173 161446 : return d->m_poRoot;
1174 : }
1175 :
1176 7023 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1177 : {
1178 7023 : TAKE_OPTIONAL_LOCK();
1179 :
1180 7023 : if (!d->m_poRoot)
1181 : {
1182 2424 : d->refreshRootFromProjObj();
1183 : }
1184 14046 : return d->m_poRoot;
1185 : }
1186 :
1187 : /************************************************************************/
1188 : /* SetRoot() */
1189 : /************************************************************************/
1190 :
1191 : /**
1192 : * \brief Set the root SRS node.
1193 : *
1194 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1195 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1196 : * is assumed by the OGRSpatialReference.
1197 : *
1198 : * @param poNewRoot object to assign as root.
1199 : */
1200 :
1201 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1202 :
1203 : {
1204 44 : if (d->m_poRoot != poNewRoot)
1205 : {
1206 44 : delete d->m_poRoot;
1207 44 : d->setRoot(poNewRoot);
1208 : }
1209 44 : }
1210 :
1211 : /************************************************************************/
1212 : /* GetAttrNode() */
1213 : /************************************************************************/
1214 :
1215 : /**
1216 : * \brief Find named node in tree.
1217 : *
1218 : * This method does a pre-order traversal of the node tree searching for
1219 : * a node with this exact value (case insensitive), and returns it. Leaf
1220 : * nodes are not considered, under the assumption that they are just
1221 : * attribute value nodes.
1222 : *
1223 : * If a node appears more than once in the tree (such as UNIT for instance),
1224 : * the first encountered will be returned. Use GetNode() on a subtree to be
1225 : * more specific.
1226 : *
1227 : * @param pszNodePath the name of the node to search for. May contain multiple
1228 : * components such as "GEOGCS|UNIT".
1229 : *
1230 : * @return a pointer to the node found, or NULL if none.
1231 : */
1232 :
1233 77626 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1234 :
1235 : {
1236 77626 : if (strchr(pszNodePath, '|') == nullptr)
1237 : {
1238 : // Fast path
1239 41891 : OGR_SRSNode *poNode = GetRoot();
1240 41891 : if (poNode)
1241 40691 : poNode = poNode->GetNode(pszNodePath);
1242 41891 : return poNode;
1243 : }
1244 :
1245 : char **papszPathTokens =
1246 35735 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1247 :
1248 35735 : if (CSLCount(papszPathTokens) < 1)
1249 : {
1250 0 : CSLDestroy(papszPathTokens);
1251 0 : return nullptr;
1252 : }
1253 :
1254 35735 : OGR_SRSNode *poNode = GetRoot();
1255 107652 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1256 : {
1257 71917 : poNode = poNode->GetNode(papszPathTokens[i]);
1258 : }
1259 :
1260 35735 : CSLDestroy(papszPathTokens);
1261 :
1262 35735 : return poNode;
1263 : }
1264 :
1265 : /**
1266 : * \brief Find named node in tree.
1267 : *
1268 : * This method does a pre-order traversal of the node tree searching for
1269 : * a node with this exact value (case insensitive), and returns it. Leaf
1270 : * nodes are not considered, under the assumption that they are just
1271 : * attribute value nodes.
1272 : *
1273 : * If a node appears more than once in the tree (such as UNIT for instance),
1274 : * the first encountered will be returned. Use GetNode() on a subtree to be
1275 : * more specific.
1276 : *
1277 : * @param pszNodePath the name of the node to search for. May contain multiple
1278 : * components such as "GEOGCS|UNIT".
1279 : *
1280 : * @return a pointer to the node found, or NULL if none.
1281 : */
1282 :
1283 : const OGR_SRSNode *
1284 70611 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1285 :
1286 : {
1287 : OGR_SRSNode *poNode =
1288 70611 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1289 :
1290 70611 : return poNode;
1291 : }
1292 :
1293 : /************************************************************************/
1294 : /* GetAttrValue() */
1295 : /************************************************************************/
1296 :
1297 : /**
1298 : * \brief Fetch indicated attribute of named node.
1299 : *
1300 : * This method uses GetAttrNode() to find the named node, and then extracts
1301 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1302 : * would return the second child of the UNIT node, which is normally the
1303 : * length of the linear unit in meters.
1304 : *
1305 : * This method does the same thing as the C function OSRGetAttrValue().
1306 : *
1307 : * @param pszNodeName the tree node to look for (case insensitive).
1308 : * @param iAttr the child of the node to fetch (zero based).
1309 : *
1310 : * @return the requested value, or NULL if it fails for any reason.
1311 : */
1312 :
1313 21518 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1314 : int iAttr) const
1315 :
1316 : {
1317 21518 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1318 21518 : if (poNode == nullptr)
1319 : {
1320 9623 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1321 : {
1322 14 : return GetAttrValue("METHOD", iAttr);
1323 : }
1324 9609 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1325 : {
1326 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1327 : }
1328 9609 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1329 : {
1330 1 : return GetAttrValue("PROJCRS", iAttr);
1331 : }
1332 9608 : return nullptr;
1333 : }
1334 :
1335 11895 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1336 0 : return nullptr;
1337 :
1338 11895 : return poNode->GetChild(iAttr)->GetValue();
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* OSRGetAttrValue() */
1343 : /************************************************************************/
1344 :
1345 : /**
1346 : * \brief Fetch indicated attribute of named node.
1347 : *
1348 : * This function is the same as OGRSpatialReference::GetAttrValue()
1349 : */
1350 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1351 : const char *pszKey, int iChild)
1352 :
1353 : {
1354 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1355 :
1356 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1357 : }
1358 :
1359 : /************************************************************************/
1360 : /* GetName() */
1361 : /************************************************************************/
1362 :
1363 : /**
1364 : * \brief Return the CRS name.
1365 : *
1366 : * The returned value is only short lived and should not be used after other
1367 : * calls to methods on this object.
1368 : *
1369 : * @since GDAL 3.0
1370 : */
1371 :
1372 5132 : const char *OGRSpatialReference::GetName() const
1373 : {
1374 10264 : TAKE_OPTIONAL_LOCK();
1375 :
1376 5132 : d->refreshProjObj();
1377 5132 : if (!d->m_pj_crs)
1378 113 : return nullptr;
1379 5019 : const char *pszName = proj_get_name(d->m_pj_crs);
1380 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1381 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1382 : {
1383 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1384 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1385 : if (baseCRS)
1386 : {
1387 : pszName = proj_get_name(baseCRS);
1388 : // pszName still remains valid after proj_destroy(), since
1389 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1390 : proj_destroy(baseCRS);
1391 : }
1392 : }
1393 : #endif
1394 5019 : return pszName;
1395 : }
1396 :
1397 : /************************************************************************/
1398 : /* OSRGetName() */
1399 : /************************************************************************/
1400 :
1401 : /**
1402 : * \brief Return the CRS name.
1403 : *
1404 : * The returned value is only short lived and should not be used after other
1405 : * calls to methods on this object.
1406 : *
1407 : * @since GDAL 3.0
1408 : */
1409 41 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1410 :
1411 : {
1412 41 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1413 :
1414 41 : return ToPointer(hSRS)->GetName();
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* Clone() */
1419 : /************************************************************************/
1420 :
1421 : /**
1422 : * \brief Make a duplicate of this OGRSpatialReference.
1423 : *
1424 : * This method is the same as the C function OSRClone().
1425 : *
1426 : * @return a new SRS, which becomes the responsibility of the caller.
1427 : */
1428 :
1429 27004 : OGRSpatialReference *OGRSpatialReference::Clone() const
1430 :
1431 : {
1432 27004 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1433 :
1434 27004 : TAKE_OPTIONAL_LOCK();
1435 :
1436 27003 : d->refreshProjObj();
1437 27004 : if (d->m_pj_crs != nullptr)
1438 26950 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1439 27004 : if (d->m_bHasCenterLong && d->m_poRoot)
1440 : {
1441 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1442 : }
1443 27004 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1444 27004 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1445 27004 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1446 54008 : return poNewRef;
1447 : }
1448 :
1449 : /************************************************************************/
1450 : /* OSRClone() */
1451 : /************************************************************************/
1452 :
1453 : /**
1454 : * \brief Make a duplicate of this OGRSpatialReference.
1455 : *
1456 : * This function is the same as OGRSpatialReference::Clone()
1457 : */
1458 1039 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1459 :
1460 : {
1461 1039 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1462 :
1463 1039 : return ToHandle(ToPointer(hSRS)->Clone());
1464 : }
1465 :
1466 : /************************************************************************/
1467 : /* dumpReadable() */
1468 : /************************************************************************/
1469 :
1470 : /** Dump pretty wkt to stdout, mostly for debugging.
1471 : */
1472 0 : void OGRSpatialReference::dumpReadable()
1473 :
1474 : {
1475 0 : char *pszPrettyWkt = nullptr;
1476 :
1477 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1478 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1479 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1480 0 : CPLFree(pszPrettyWkt);
1481 0 : }
1482 :
1483 : /************************************************************************/
1484 : /* exportToPrettyWkt() */
1485 : /************************************************************************/
1486 :
1487 : /**
1488 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1489 : * person.
1490 : *
1491 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1492 : * Issues</a> page for implementation details of WKT 1 in OGR.
1493 : *
1494 : * Note that the returned WKT string should be freed with
1495 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1496 : *
1497 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1498 : * option. Valid values are the one of the FORMAT option of
1499 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1500 : *
1501 : * This method is the same as the C function OSRExportToPrettyWkt().
1502 : *
1503 : * @param ppszResult the resulting string is returned in this pointer.
1504 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1505 : * stripped off.
1506 : *
1507 : * @return OGRERR_NONE if successful.
1508 : */
1509 :
1510 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1511 : int bSimplify) const
1512 :
1513 : {
1514 116 : CPLStringList aosOptions;
1515 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1516 58 : if (bSimplify)
1517 : {
1518 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1519 : }
1520 116 : return exportToWkt(ppszResult, aosOptions.List());
1521 : }
1522 :
1523 : /************************************************************************/
1524 : /* OSRExportToPrettyWkt() */
1525 : /************************************************************************/
1526 :
1527 : /**
1528 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1529 : * person.
1530 : *
1531 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1532 : * option. Valid values are the one of the FORMAT option of
1533 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1534 : *
1535 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1536 : */
1537 :
1538 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1539 : char **ppszReturn, int bSimplify)
1540 :
1541 : {
1542 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1543 :
1544 56 : *ppszReturn = nullptr;
1545 :
1546 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1547 : }
1548 :
1549 : /************************************************************************/
1550 : /* exportToWkt() */
1551 : /************************************************************************/
1552 :
1553 : /**
1554 : * \brief Convert this SRS into WKT 1 format.
1555 : *
1556 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1557 : * Issues</a> page for implementation details of WKT 1 in OGR.
1558 : *
1559 : * Note that the returned WKT string should be freed with
1560 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1561 : *
1562 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1563 : * option. Valid values are the one of the FORMAT option of
1564 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1565 : *
1566 : * This method is the same as the C function OSRExportToWkt().
1567 : *
1568 : * @param ppszResult the resulting string is returned in this pointer.
1569 : *
1570 : * @return OGRERR_NONE if successful.
1571 : */
1572 :
1573 12689 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1574 :
1575 : {
1576 12689 : return exportToWkt(ppszResult, nullptr);
1577 : }
1578 :
1579 : /************************************************************************/
1580 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1581 : /************************************************************************/
1582 :
1583 553 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1584 : bool onlyIfEPSGCode,
1585 : bool canModifyHorizPart)
1586 : {
1587 553 : PJ *ret = nullptr;
1588 553 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1589 : {
1590 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1591 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1592 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1593 26 : vertCRS &&
1594 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1595 : {
1596 : auto boundHoriz =
1597 : canModifyHorizPart
1598 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1599 3 : : proj_clone(ctx, horizCRS);
1600 : auto boundVert =
1601 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1602 3 : if (boundHoriz && boundVert)
1603 : {
1604 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1605 : boundHoriz, boundVert);
1606 : }
1607 3 : proj_destroy(boundHoriz);
1608 3 : proj_destroy(boundVert);
1609 : }
1610 13 : proj_destroy(horizCRS);
1611 13 : proj_destroy(vertCRS);
1612 : }
1613 1040 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1614 500 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1615 : {
1616 231 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1617 : }
1618 553 : return ret;
1619 : }
1620 :
1621 : /************************************************************************/
1622 : /* exportToWkt() */
1623 : /************************************************************************/
1624 :
1625 : /**
1626 : * Convert this SRS into a WKT string.
1627 : *
1628 : * Note that the returned WKT string should be freed with
1629 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1630 : *
1631 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1632 : * Issues</a> page for implementation details of WKT 1 in OGR.
1633 : *
1634 : * @param ppszResult the resulting string is returned in this pointer.
1635 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1636 : * supported options are
1637 : * <ul>
1638 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1639 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1640 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1641 : * node is returned.
1642 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1643 : * node is returned.
1644 : * WKT1 is an alias of WKT1_GDAL.
1645 : * WKT2 will default to the latest revision implemented (currently
1646 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1647 : * </li>
1648 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1649 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1650 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1651 : * height (for example for use with LAS 1.4 WKT1).
1652 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1653 : * </ul>
1654 : *
1655 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1656 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1657 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1658 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1659 : * TOWGS84[] node may be added.
1660 : *
1661 : * @return OGRERR_NONE if successful.
1662 : * @since GDAL 3.0
1663 : */
1664 :
1665 16801 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1666 : const char *const *papszOptions) const
1667 : {
1668 : // In the past calling this method was thread-safe, even if we never
1669 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1670 : // so this is no longer thread-safe.
1671 33602 : std::lock_guard oLock(d->m_mutex);
1672 :
1673 16801 : d->refreshProjObj();
1674 16801 : if (!d->m_pj_crs)
1675 : {
1676 21 : *ppszResult = CPLStrdup("");
1677 21 : return OGRERR_FAILURE;
1678 : }
1679 :
1680 16780 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1681 : {
1682 0 : return d->m_poRoot->exportToWkt(ppszResult);
1683 : }
1684 :
1685 16780 : auto ctxt = d->getPROJContext();
1686 16780 : auto wktFormat = PJ_WKT1_GDAL;
1687 : const char *pszFormat =
1688 16780 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1689 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1690 16780 : if (EQUAL(pszFormat, "DEFAULT"))
1691 14434 : pszFormat = "";
1692 :
1693 16780 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1694 : {
1695 618 : wktFormat = PJ_WKT1_ESRI;
1696 : }
1697 16162 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1698 15537 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1699 : {
1700 630 : wktFormat = PJ_WKT1_GDAL;
1701 : }
1702 15532 : else if (EQUAL(pszFormat, "WKT2_2015"))
1703 : {
1704 265 : wktFormat = PJ_WKT2_2015;
1705 : }
1706 15267 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1707 14943 : EQUAL(pszFormat, "WKT2_2019"))
1708 : {
1709 1088 : wktFormat = PJ_WKT2_2018;
1710 : }
1711 14179 : else if (pszFormat[0] == '\0')
1712 : {
1713 : // cppcheck-suppress knownConditionTrueFalse
1714 14179 : if (IsDerivedGeographic())
1715 : {
1716 2 : wktFormat = PJ_WKT2_2018;
1717 : }
1718 27725 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1719 13548 : GetAxesCount() == 3)
1720 : {
1721 56 : wktFormat = PJ_WKT2_2018;
1722 : }
1723 : }
1724 : else
1725 : {
1726 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1727 0 : *ppszResult = CPLStrdup("");
1728 0 : return OGRERR_FAILURE;
1729 : }
1730 :
1731 33560 : CPLStringList aosOptions;
1732 16780 : if (wktFormat != PJ_WKT1_ESRI)
1733 : {
1734 16162 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1735 : }
1736 : aosOptions.SetNameValue(
1737 16780 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1738 :
1739 16780 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1740 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1741 16780 : if (pszAllowEllpsHeightAsVertCS)
1742 : {
1743 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1744 0 : pszAllowEllpsHeightAsVertCS);
1745 : }
1746 :
1747 16780 : PJ *boundCRS = nullptr;
1748 31531 : if (wktFormat == PJ_WKT1_GDAL &&
1749 14751 : CPLTestBool(CSLFetchNameValueDef(
1750 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1751 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1752 : {
1753 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1754 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1755 : }
1756 :
1757 33560 : CPLErrorAccumulator oErrorAccumulator;
1758 : const char *pszWKT;
1759 : {
1760 16780 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1761 16780 : CPL_IGNORE_RET_VAL(oAccumulator);
1762 16780 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1763 16780 : aosOptions.List());
1764 : }
1765 16782 : for (const auto &oError : oErrorAccumulator.GetErrors())
1766 : {
1767 32 : if (pszFormat[0] == '\0' &&
1768 14 : (oError.msg.find("Unsupported conversion method") !=
1769 2 : std::string::npos ||
1770 2 : oError.msg.find("can only be exported to WKT2") !=
1771 0 : std::string::npos ||
1772 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1773 : std::string::npos))
1774 : {
1775 14 : CPLErrorReset();
1776 : // If we cannot export in the default mode (WKT1), retry with WKT2
1777 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1778 14 : PJ_WKT2_2018, aosOptions.List());
1779 14 : break;
1780 : }
1781 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1782 : }
1783 :
1784 16780 : if (!pszWKT)
1785 : {
1786 2 : *ppszResult = CPLStrdup("");
1787 2 : proj_destroy(boundCRS);
1788 2 : return OGRERR_FAILURE;
1789 : }
1790 :
1791 16778 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1792 : {
1793 5 : OGR_SRSNode oRoot;
1794 5 : oRoot.importFromWkt(&pszWKT);
1795 5 : oRoot.StripNodes("AXIS");
1796 5 : if (EQUAL(pszFormat, "SFSQL"))
1797 : {
1798 3 : oRoot.StripNodes("TOWGS84");
1799 : }
1800 5 : oRoot.StripNodes("AUTHORITY");
1801 5 : oRoot.StripNodes("EXTENSION");
1802 : OGRErr eErr;
1803 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1804 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1805 : else
1806 3 : eErr = oRoot.exportToWkt(ppszResult);
1807 5 : proj_destroy(boundCRS);
1808 5 : return eErr;
1809 : }
1810 :
1811 16773 : *ppszResult = CPLStrdup(pszWKT);
1812 :
1813 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1814 16773 : if (wktFormat == PJ_WKT2_2018)
1815 : {
1816 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1817 : // related to a wrong EPSG code assigned to UTM South conversions
1818 1146 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1819 1146 : if (pszPtr)
1820 : {
1821 269 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1822 269 : const int nZone = atoi(pszPtr);
1823 806 : while (*pszPtr >= '0' && *pszPtr <= '9')
1824 537 : ++pszPtr;
1825 269 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1826 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1827 : {
1828 1 : pszPtr += 3;
1829 1 : int nLevel = 0;
1830 1 : bool bInString = false;
1831 : // Find the ID node corresponding to this CONVERSION node
1832 480 : while (*pszPtr)
1833 : {
1834 480 : if (bInString)
1835 : {
1836 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1837 : {
1838 0 : ++pszPtr;
1839 : }
1840 197 : else if (*pszPtr == '"')
1841 : {
1842 17 : bInString = false;
1843 : }
1844 : }
1845 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1846 : {
1847 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1848 : 17000 + nZone)))
1849 : {
1850 1 : CPLAssert(pszPtr[11] == '7');
1851 1 : CPLAssert(pszPtr[12] == '0');
1852 1 : pszPtr[11] = '6';
1853 1 : pszPtr[12] = '1';
1854 : }
1855 1 : break;
1856 : }
1857 282 : else if (*pszPtr == '"')
1858 : {
1859 17 : bInString = true;
1860 : }
1861 265 : else if (*pszPtr == '[')
1862 : {
1863 17 : ++nLevel;
1864 : }
1865 248 : else if (*pszPtr == ']')
1866 : {
1867 17 : --nLevel;
1868 : }
1869 :
1870 479 : ++pszPtr;
1871 : }
1872 : }
1873 : }
1874 : }
1875 : #endif
1876 :
1877 16773 : proj_destroy(boundCRS);
1878 16773 : return OGRERR_NONE;
1879 : }
1880 :
1881 : /************************************************************************/
1882 : /* exportToWkt() */
1883 : /************************************************************************/
1884 :
1885 : /**
1886 : * Convert this SRS into a WKT string.
1887 : *
1888 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1889 : * Issues</a> page for implementation details of WKT 1 in OGR.
1890 : *
1891 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1892 : * supported options are
1893 : * <ul>
1894 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1895 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1896 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1897 : * node is returned.
1898 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1899 : * node is returned.
1900 : * WKT1 is an alias of WKT1_GDAL.
1901 : * WKT2 will default to the latest revision implemented (currently
1902 : * WKT2_2019)
1903 : * </li>
1904 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1905 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1906 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1907 : * height (for example for use with LAS 1.4 WKT1).
1908 : * Requires PROJ 7.2.1.</li>
1909 : * </ul>
1910 : *
1911 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1912 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1913 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1914 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1915 : * TOWGS84[] node may be added.
1916 : *
1917 : * @return a non-empty string if successful.
1918 : * @since GDAL 3.9
1919 : */
1920 :
1921 : std::string
1922 97 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1923 : {
1924 97 : std::string osWKT;
1925 97 : char *pszWKT = nullptr;
1926 97 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1927 97 : osWKT = pszWKT;
1928 97 : CPLFree(pszWKT);
1929 194 : return osWKT;
1930 : }
1931 :
1932 : /************************************************************************/
1933 : /* OSRExportToWkt() */
1934 : /************************************************************************/
1935 :
1936 : /**
1937 : * \brief Convert this SRS into WKT 1 format.
1938 : *
1939 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1940 : * Issues</a> page for implementation details of WKT in OGR.
1941 : *
1942 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1943 : * option. Valid values are the one of the FORMAT option of
1944 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1945 : *
1946 : * This function is the same as OGRSpatialReference::exportToWkt().
1947 : */
1948 :
1949 878 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1950 :
1951 : {
1952 878 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1953 :
1954 878 : *ppszReturn = nullptr;
1955 :
1956 878 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
1957 : }
1958 :
1959 : /************************************************************************/
1960 : /* OSRExportToWktEx() */
1961 : /************************************************************************/
1962 :
1963 : /**
1964 : * \brief Convert this SRS into WKT format.
1965 : *
1966 : * This function is the same as OGRSpatialReference::exportToWkt(char **
1967 : * ppszResult,const char* const* papszOptions ) const
1968 : *
1969 : * @since GDAL 3.0
1970 : */
1971 :
1972 1312 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1973 : const char *const *papszOptions)
1974 : {
1975 1312 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1976 :
1977 1312 : *ppszReturn = nullptr;
1978 :
1979 1312 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
1980 : }
1981 :
1982 : /************************************************************************/
1983 : /* exportToPROJJSON() */
1984 : /************************************************************************/
1985 :
1986 : /**
1987 : * Convert this SRS into a PROJJSON string.
1988 : *
1989 : * Note that the returned JSON string should be freed with
1990 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1991 : *
1992 : * @param ppszResult the resulting string is returned in this pointer.
1993 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1994 : * supported options are
1995 : * <ul>
1996 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
1997 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1998 : * on).</li>
1999 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2000 : * disable it.</li>
2001 : * </ul>
2002 : *
2003 : * @return OGRERR_NONE if successful.
2004 : * @since GDAL 3.1 and PROJ 6.2
2005 : */
2006 :
2007 2119 : OGRErr OGRSpatialReference::exportToPROJJSON(
2008 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2009 : {
2010 4238 : TAKE_OPTIONAL_LOCK();
2011 :
2012 2119 : d->refreshProjObj();
2013 2119 : if (!d->m_pj_crs)
2014 : {
2015 0 : *ppszResult = nullptr;
2016 0 : return OGRERR_FAILURE;
2017 : }
2018 :
2019 : const char *pszPROJJSON =
2020 2119 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2021 :
2022 2119 : if (!pszPROJJSON)
2023 : {
2024 0 : *ppszResult = CPLStrdup("");
2025 0 : return OGRERR_FAILURE;
2026 : }
2027 :
2028 2119 : *ppszResult = CPLStrdup(pszPROJJSON);
2029 :
2030 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2031 : {
2032 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2033 : // related to a wrong EPSG code assigned to UTM South conversions
2034 2119 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2035 2119 : if (pszPtr)
2036 : {
2037 231 : pszPtr += strlen("\"name\": \"UTM zone ");
2038 231 : const int nZone = atoi(pszPtr);
2039 692 : while (*pszPtr >= '0' && *pszPtr <= '9')
2040 461 : ++pszPtr;
2041 231 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2042 : {
2043 4 : pszPtr += 2;
2044 4 : int nLevel = 0;
2045 4 : bool bInString = false;
2046 : // Find the id node corresponding to this conversion node
2047 5299 : while (*pszPtr)
2048 : {
2049 5299 : if (bInString)
2050 : {
2051 1950 : if (*pszPtr == '\\')
2052 : {
2053 0 : ++pszPtr;
2054 : }
2055 1950 : else if (*pszPtr == '"')
2056 : {
2057 244 : bInString = false;
2058 : }
2059 : }
2060 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2061 : {
2062 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2063 : const char *pszAuthEPSG =
2064 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2065 4 : char *pszCode = strstr(
2066 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2067 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2068 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2069 4 : pszNextEndCurl - pszCode > 0)
2070 : {
2071 4 : CPLAssert(pszCode[9] == '7');
2072 4 : CPLAssert(pszCode[10] == '0');
2073 4 : pszCode[9] = '6';
2074 4 : pszCode[10] = '1';
2075 : }
2076 4 : break;
2077 : }
2078 3345 : else if (*pszPtr == '"')
2079 : {
2080 244 : bInString = true;
2081 : }
2082 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2083 : {
2084 60 : ++nLevel;
2085 : }
2086 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2087 : {
2088 60 : --nLevel;
2089 : }
2090 :
2091 5295 : ++pszPtr;
2092 : }
2093 : }
2094 : }
2095 : }
2096 : #endif
2097 :
2098 2119 : return OGRERR_NONE;
2099 : }
2100 :
2101 : /************************************************************************/
2102 : /* OSRExportToPROJJSON() */
2103 : /************************************************************************/
2104 :
2105 : /**
2106 : * \brief Convert this SRS into PROJJSON format.
2107 : *
2108 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2109 : *
2110 : * @since GDAL 3.1 and PROJ 6.2
2111 : */
2112 :
2113 62 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2114 : const char *const *papszOptions)
2115 : {
2116 62 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2117 :
2118 62 : *ppszReturn = nullptr;
2119 :
2120 62 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2121 : }
2122 :
2123 : /************************************************************************/
2124 : /* importFromWkt() */
2125 : /************************************************************************/
2126 :
2127 : /**
2128 : * \brief Import from WKT string.
2129 : *
2130 : * This method will wipe the existing SRS definition, and
2131 : * reassign it based on the contents of the passed WKT string. Only as
2132 : * much of the input string as needed to construct this SRS is consumed from
2133 : * the input string, and the input string pointer
2134 : * is then updated to point to the remaining (unused) input.
2135 : *
2136 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2137 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2138 : * and the coordinate epoch potentially present used as the coordinate epoch
2139 : * property of the OGRSpatialReference object.
2140 : *
2141 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2142 : * Issues</a> page for implementation details of WKT in OGR.
2143 : *
2144 : * This method is the same as the C function OSRImportFromWkt().
2145 : *
2146 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2147 : * point to remaining unused input text.
2148 : *
2149 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2150 : * fails for any reason.
2151 : * @since GDAL 2.3
2152 : */
2153 :
2154 16053 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2155 :
2156 : {
2157 16053 : return importFromWkt(ppszInput, nullptr);
2158 : }
2159 :
2160 : /************************************************************************/
2161 : /* importFromWkt() */
2162 : /************************************************************************/
2163 :
2164 : /*! @cond Doxygen_Suppress */
2165 :
2166 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2167 : CSLConstList papszOptions)
2168 :
2169 : {
2170 21 : return importFromWkt(&pszInput, papszOptions);
2171 : }
2172 :
2173 16074 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2174 : CSLConstList papszOptions)
2175 :
2176 : {
2177 32148 : TAKE_OPTIONAL_LOCK();
2178 :
2179 16074 : if (!ppszInput || !*ppszInput)
2180 0 : return OGRERR_FAILURE;
2181 :
2182 16074 : if (strlen(*ppszInput) > 100 * 1000 &&
2183 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2184 : {
2185 0 : CPLError(CE_Failure, CPLE_NotSupported,
2186 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2187 : "You can remove this limitation by definition the "
2188 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2189 0 : return OGRERR_FAILURE;
2190 : }
2191 :
2192 16074 : Clear();
2193 :
2194 16074 : bool canCache = false;
2195 16074 : auto tlsCache = OSRGetProjTLSCache();
2196 32148 : std::string osWkt;
2197 16074 : if (**ppszInput)
2198 : {
2199 15500 : osWkt = *ppszInput;
2200 15500 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2201 15500 : if (cachedObj)
2202 : {
2203 13900 : d->setPjCRS(cachedObj);
2204 : }
2205 : else
2206 : {
2207 3200 : CPLStringList aosOptions(papszOptions);
2208 1600 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2209 1600 : aosOptions.SetNameValue("STRICT", "NO");
2210 1600 : PROJ_STRING_LIST warnings = nullptr;
2211 1600 : PROJ_STRING_LIST errors = nullptr;
2212 1600 : auto ctxt = d->getPROJContext();
2213 1600 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2214 : &warnings, &errors);
2215 1600 : d->setPjCRS(pj);
2216 :
2217 1650 : for (auto iter = warnings; iter && *iter; ++iter)
2218 : {
2219 50 : d->m_wktImportWarnings.push_back(*iter);
2220 : }
2221 1834 : for (auto iter = errors; iter && *iter; ++iter)
2222 : {
2223 234 : d->m_wktImportErrors.push_back(*iter);
2224 234 : if (!d->m_pj_crs)
2225 : {
2226 35 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2227 : }
2228 : }
2229 1600 : if (warnings == nullptr && errors == nullptr)
2230 : {
2231 1323 : canCache = true;
2232 : }
2233 1600 : proj_string_list_destroy(warnings);
2234 1600 : proj_string_list_destroy(errors);
2235 : }
2236 : }
2237 16074 : if (!d->m_pj_crs)
2238 609 : return OGRERR_CORRUPT_DATA;
2239 :
2240 : // Only accept CRS objects
2241 15465 : if (!proj_is_crs(d->m_pj_crs))
2242 : {
2243 0 : Clear();
2244 0 : return OGRERR_CORRUPT_DATA;
2245 : }
2246 :
2247 15465 : if (canCache)
2248 : {
2249 1323 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2250 : }
2251 :
2252 15465 : if (strstr(*ppszInput, "CENTER_LONG"))
2253 : {
2254 0 : auto poRoot = new OGR_SRSNode();
2255 0 : d->setRoot(poRoot);
2256 0 : const char *pszTmp = *ppszInput;
2257 0 : poRoot->importFromWkt(&pszTmp);
2258 0 : d->m_bHasCenterLong = true;
2259 : }
2260 :
2261 : // TODO? we don't really update correctly since we assume that the
2262 : // passed string is only WKT.
2263 15465 : *ppszInput += strlen(*ppszInput);
2264 15465 : return OGRERR_NONE;
2265 :
2266 : #if no_longer_implemented_for_now
2267 : /* -------------------------------------------------------------------- */
2268 : /* The following seems to try and detect and unconsumed */
2269 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2270 : /* import and attach it to the existing root. Likely we will */
2271 : /* need to extend this somewhat to bring it into an acceptable */
2272 : /* OGRSpatialReference organization at some point. */
2273 : /* -------------------------------------------------------------------- */
2274 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2275 : {
2276 : if (((*ppszInput)[0]) == ',')
2277 : (*ppszInput)++;
2278 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2279 : poRoot->AddChild(poNewChild);
2280 : return poNewChild->importFromWkt(ppszInput);
2281 : }
2282 : #endif
2283 : }
2284 :
2285 : /*! @endcond */
2286 :
2287 : /**
2288 : * \brief Import from WKT string.
2289 : *
2290 : * This method will wipe the existing SRS definition, and
2291 : * reassign it based on the contents of the passed WKT string. Only as
2292 : * much of the input string as needed to construct this SRS is consumed from
2293 : * the input string, and the input string pointer
2294 : * is then updated to point to the remaining (unused) input.
2295 : *
2296 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2297 : * Issues</a> page for implementation details of WKT in OGR.
2298 : *
2299 : * This method is the same as the C function OSRImportFromWkt().
2300 : *
2301 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2302 : * point to remaining unused input text.
2303 : *
2304 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2305 : * fails for any reason.
2306 : * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2307 : * char*)
2308 : */
2309 :
2310 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2311 :
2312 : {
2313 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2314 : }
2315 :
2316 : /**
2317 : * \brief Import from WKT string.
2318 : *
2319 : * This method will wipe the existing SRS definition, and
2320 : * reassign it based on the contents of the passed WKT string. Only as
2321 : * much of the input string as needed to construct this SRS is consumed from
2322 : * the input string, and the input string pointer
2323 : * is then updated to point to the remaining (unused) input.
2324 : *
2325 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2326 : * Issues</a> page for implementation details of WKT in OGR.
2327 : *
2328 : * @param pszInput Input WKT
2329 : *
2330 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2331 : * fails for any reason.
2332 : * @since GDAL 2.3
2333 : */
2334 :
2335 15765 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2336 : {
2337 15765 : return importFromWkt(&pszInput);
2338 : }
2339 :
2340 : /************************************************************************/
2341 : /* Validate() */
2342 : /************************************************************************/
2343 :
2344 : /**
2345 : * \brief Validate CRS imported with importFromWkt() or with modified with
2346 : * direct node manipulations. Otherwise the CRS should be always valid.
2347 : *
2348 : * This method attempts to verify that the spatial reference system is
2349 : * well formed, and consists of known tokens. The validation is not
2350 : * comprehensive.
2351 : *
2352 : * This method is the same as the C function OSRValidate().
2353 : *
2354 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2355 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2356 : * but contains non-standard PROJECTION[] values.
2357 : */
2358 :
2359 116 : OGRErr OGRSpatialReference::Validate() const
2360 :
2361 : {
2362 232 : TAKE_OPTIONAL_LOCK();
2363 :
2364 154 : for (const auto &str : d->m_wktImportErrors)
2365 : {
2366 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2367 : }
2368 116 : for (const auto &str : d->m_wktImportWarnings)
2369 : {
2370 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2371 : }
2372 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2373 : {
2374 37 : return OGRERR_CORRUPT_DATA;
2375 : }
2376 79 : if (!d->m_wktImportWarnings.empty())
2377 : {
2378 0 : return OGRERR_UNSUPPORTED_SRS;
2379 : }
2380 79 : return OGRERR_NONE;
2381 : }
2382 :
2383 : /************************************************************************/
2384 : /* OSRValidate() */
2385 : /************************************************************************/
2386 : /**
2387 : * \brief Validate SRS tokens.
2388 : *
2389 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2390 : */
2391 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2392 :
2393 : {
2394 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2395 :
2396 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2397 : }
2398 :
2399 : /************************************************************************/
2400 : /* OSRImportFromWkt() */
2401 : /************************************************************************/
2402 :
2403 : /**
2404 : * \brief Import from WKT string.
2405 : *
2406 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2407 : * Issues</a> page for implementation details of WKT in OGR.
2408 : *
2409 : * This function is the same as OGRSpatialReference::importFromWkt().
2410 : */
2411 :
2412 288 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2413 :
2414 : {
2415 288 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2416 :
2417 288 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2418 : }
2419 :
2420 : /************************************************************************/
2421 : /* SetNode() */
2422 : /************************************************************************/
2423 :
2424 : /**
2425 : * \brief Set attribute value in spatial reference.
2426 : *
2427 : * Missing intermediate nodes in the path will be created if not already
2428 : * in existence. If the attribute has no children one will be created and
2429 : * assigned the value otherwise the zeroth child will be assigned the value.
2430 : *
2431 : * This method does the same as the C function OSRSetAttrValue().
2432 : *
2433 : * @param pszNodePath full path to attribute to be set. For instance
2434 : * "PROJCS|GEOGCS|UNIT".
2435 : *
2436 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2437 : * This may be NULL if you just want to force creation of the intermediate
2438 : * path.
2439 : *
2440 : * @return OGRERR_NONE on success.
2441 : */
2442 :
2443 592 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2444 : const char *pszNewNodeValue)
2445 :
2446 : {
2447 1184 : TAKE_OPTIONAL_LOCK();
2448 :
2449 : char **papszPathTokens =
2450 592 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2451 :
2452 592 : if (CSLCount(papszPathTokens) < 1)
2453 : {
2454 0 : CSLDestroy(papszPathTokens);
2455 0 : return OGRERR_FAILURE;
2456 : }
2457 :
2458 1033 : if (GetRoot() == nullptr ||
2459 441 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2460 : {
2461 274 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2462 119 : CSLCount(papszPathTokens) == 1)
2463 : {
2464 119 : CSLDestroy(papszPathTokens);
2465 119 : return SetProjCS(pszNewNodeValue);
2466 : }
2467 : else
2468 : {
2469 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2470 : }
2471 : }
2472 :
2473 473 : OGR_SRSNode *poNode = GetRoot();
2474 731 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2475 : {
2476 258 : int j = 0; // Used after for.
2477 :
2478 645 : for (; j < poNode->GetChildCount(); j++)
2479 : {
2480 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2481 : {
2482 198 : poNode = poNode->GetChild(j);
2483 198 : j = -1;
2484 198 : break;
2485 : }
2486 : }
2487 :
2488 258 : if (j != -1)
2489 : {
2490 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2491 60 : poNode->AddChild(poNewNode);
2492 60 : poNode = poNewNode;
2493 : }
2494 : }
2495 :
2496 473 : CSLDestroy(papszPathTokens);
2497 :
2498 473 : if (pszNewNodeValue != nullptr)
2499 : {
2500 473 : if (poNode->GetChildCount() > 0)
2501 377 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2502 : else
2503 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2504 : };
2505 473 : return OGRERR_NONE;
2506 : }
2507 :
2508 : /************************************************************************/
2509 : /* OSRSetAttrValue() */
2510 : /************************************************************************/
2511 :
2512 : /**
2513 : * \brief Set attribute value in spatial reference.
2514 : *
2515 : * This function is the same as OGRSpatialReference::SetNode()
2516 : */
2517 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2518 : const char *pszPath, const char *pszValue)
2519 :
2520 : {
2521 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2522 :
2523 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2524 : }
2525 :
2526 : /************************************************************************/
2527 : /* SetNode() */
2528 : /************************************************************************/
2529 :
2530 : /**
2531 : * \brief Set attribute value in spatial reference.
2532 : *
2533 : * Missing intermediate nodes in the path will be created if not already
2534 : * in existence. If the attribute has no children one will be created and
2535 : * assigned the value otherwise the zeroth child will be assigned the value.
2536 : *
2537 : * This method does the same as the C function OSRSetAttrValue().
2538 : *
2539 : * @param pszNodePath full path to attribute to be set. For instance
2540 : * "PROJCS|GEOGCS|UNIT".
2541 : *
2542 : * @param dfValue value to be assigned to node.
2543 : *
2544 : * @return OGRERR_NONE on success.
2545 : */
2546 :
2547 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2548 :
2549 : {
2550 0 : char szValue[64] = {'\0'};
2551 :
2552 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2553 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2554 : else
2555 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2556 :
2557 0 : return SetNode(pszNodePath, szValue);
2558 : }
2559 :
2560 : /************************************************************************/
2561 : /* SetAngularUnits() */
2562 : /************************************************************************/
2563 :
2564 : /**
2565 : * \brief Set the angular units for the geographic coordinate system.
2566 : *
2567 : * This method creates a UNIT subnode with the specified values as a
2568 : * child of the GEOGCS node.
2569 : *
2570 : * This method does the same as the C function OSRSetAngularUnits().
2571 : *
2572 : * @param pszUnitsName the units name to be used. Some preferred units
2573 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2574 : *
2575 : * @param dfInRadians the value to multiple by an angle in the indicated
2576 : * units to transform to radians. Some standard conversion factors can
2577 : * be found in ogr_srs_api.h.
2578 : *
2579 : * @return OGRERR_NONE on success.
2580 : */
2581 :
2582 1033 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2583 : double dfInRadians)
2584 :
2585 : {
2586 2066 : TAKE_OPTIONAL_LOCK();
2587 :
2588 1033 : d->bNormInfoSet = FALSE;
2589 :
2590 1033 : d->refreshProjObj();
2591 1033 : if (!d->m_pj_crs)
2592 0 : return OGRERR_FAILURE;
2593 1033 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2594 1033 : if (!geodCRS)
2595 0 : return OGRERR_FAILURE;
2596 1033 : proj_destroy(geodCRS);
2597 1033 : d->demoteFromBoundCRS();
2598 1033 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2599 : pszUnitsName, dfInRadians,
2600 : nullptr, nullptr));
2601 1033 : d->undoDemoteFromBoundCRS();
2602 :
2603 1033 : d->m_osAngularUnits = pszUnitsName;
2604 1033 : d->m_dfAngularUnitToRadian = dfInRadians;
2605 :
2606 1033 : return OGRERR_NONE;
2607 : }
2608 :
2609 : /************************************************************************/
2610 : /* OSRSetAngularUnits() */
2611 : /************************************************************************/
2612 :
2613 : /**
2614 : * \brief Set the angular units for the geographic coordinate system.
2615 : *
2616 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2617 : */
2618 41 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2619 : double dfInRadians)
2620 :
2621 : {
2622 41 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2623 :
2624 41 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2625 : }
2626 :
2627 : /************************************************************************/
2628 : /* GetAngularUnits() */
2629 : /************************************************************************/
2630 :
2631 : /**
2632 : * \brief Fetch angular geographic coordinate system units.
2633 : *
2634 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2635 : * will be assumed. This method only checks directly under the GEOGCS node
2636 : * for units.
2637 : *
2638 : * This method does the same thing as the C function OSRGetAngularUnits().
2639 : *
2640 : * @param ppszName a pointer to be updated with the pointer to the units name.
2641 : * The returned value remains internal to the OGRSpatialReference and should
2642 : * not be freed, or modified. It may be invalidated on the next
2643 : * OGRSpatialReference call.
2644 : *
2645 : * @return the value to multiply by angular distances to transform them to
2646 : * radians.
2647 : * @since GDAL 2.3.0
2648 : */
2649 :
2650 6600 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2651 :
2652 : {
2653 13200 : TAKE_OPTIONAL_LOCK();
2654 :
2655 6600 : d->refreshProjObj();
2656 :
2657 6600 : if (!d->m_osAngularUnits.empty())
2658 : {
2659 1830 : if (ppszName != nullptr)
2660 143 : *ppszName = d->m_osAngularUnits.c_str();
2661 1830 : return d->m_dfAngularUnitToRadian;
2662 : }
2663 :
2664 : do
2665 : {
2666 4770 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2667 : {
2668 113 : break;
2669 : }
2670 :
2671 : auto geodCRS =
2672 4659 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2673 4659 : if (!geodCRS)
2674 : {
2675 0 : break;
2676 : }
2677 : auto coordSys =
2678 4659 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2679 4659 : proj_destroy(geodCRS);
2680 4659 : if (!coordSys)
2681 : {
2682 0 : break;
2683 : }
2684 4659 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2685 : PJ_CS_TYPE_ELLIPSOIDAL)
2686 : {
2687 2 : proj_destroy(coordSys);
2688 2 : break;
2689 : }
2690 :
2691 4657 : double dfConvFactor = 0.0;
2692 4657 : const char *pszUnitName = nullptr;
2693 4657 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2694 : nullptr, nullptr, &dfConvFactor,
2695 : &pszUnitName, nullptr, nullptr))
2696 : {
2697 0 : proj_destroy(coordSys);
2698 0 : break;
2699 : }
2700 :
2701 4657 : d->m_osAngularUnits = pszUnitName;
2702 :
2703 4657 : proj_destroy(coordSys);
2704 4657 : d->m_dfAngularUnitToRadian = dfConvFactor;
2705 : } while (false);
2706 :
2707 4770 : if (d->m_osAngularUnits.empty())
2708 : {
2709 113 : d->m_osAngularUnits = "degree";
2710 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2711 : }
2712 :
2713 4770 : if (ppszName != nullptr)
2714 2899 : *ppszName = d->m_osAngularUnits.c_str();
2715 4770 : return d->m_dfAngularUnitToRadian;
2716 : }
2717 :
2718 : /**
2719 : * \brief Fetch angular geographic coordinate system units.
2720 : *
2721 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2722 : * will be assumed. This method only checks directly under the GEOGCS node
2723 : * for units.
2724 : *
2725 : * This method does the same thing as the C function OSRGetAngularUnits().
2726 : *
2727 : * @param ppszName a pointer to be updated with the pointer to the units name.
2728 : * The returned value remains internal to the OGRSpatialReference and should
2729 : * not be freed, or modified. It may be invalidated on the next
2730 : * OGRSpatialReference call.
2731 : *
2732 : * @return the value to multiply by angular distances to transform them to
2733 : * radians.
2734 : * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2735 : */
2736 :
2737 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2738 :
2739 : {
2740 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2741 : }
2742 :
2743 : /************************************************************************/
2744 : /* OSRGetAngularUnits() */
2745 : /************************************************************************/
2746 :
2747 : /**
2748 : * \brief Fetch angular geographic coordinate system units.
2749 : *
2750 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2751 : */
2752 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2753 :
2754 : {
2755 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2756 :
2757 1 : return ToPointer(hSRS)->GetAngularUnits(
2758 1 : const_cast<const char **>(ppszName));
2759 : }
2760 :
2761 : /************************************************************************/
2762 : /* SetLinearUnitsAndUpdateParameters() */
2763 : /************************************************************************/
2764 :
2765 : /**
2766 : * \brief Set the linear units for the projection.
2767 : *
2768 : * This method creates a UNIT subnode with the specified values as a
2769 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2770 : * SetLinearUnits() method, but it also updates all existing linear
2771 : * projection parameter values from the old units to the new units.
2772 : *
2773 : * @param pszName the units name to be used. Some preferred units
2774 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2775 : * and SRS_UL_US_FOOT.
2776 : *
2777 : * @param dfInMeters the value to multiple by a length in the indicated
2778 : * units to transform to meters. Some standard conversion factors can
2779 : * be found in ogr_srs_api.h.
2780 : *
2781 : * @param pszUnitAuthority Unit authority name. Or nullptr
2782 : *
2783 : * @param pszUnitCode Unit code. Or nullptr
2784 : *
2785 : * @return OGRERR_NONE on success.
2786 : */
2787 :
2788 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2789 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2790 : const char *pszUnitCode)
2791 :
2792 : {
2793 78 : TAKE_OPTIONAL_LOCK();
2794 :
2795 39 : if (dfInMeters <= 0.0)
2796 0 : return OGRERR_FAILURE;
2797 :
2798 39 : d->refreshProjObj();
2799 39 : if (!d->m_pj_crs)
2800 0 : return OGRERR_FAILURE;
2801 :
2802 39 : d->demoteFromBoundCRS();
2803 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2804 : {
2805 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2806 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2807 : pszUnitAuthority, pszUnitCode, true));
2808 : }
2809 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2810 : pszName, dfInMeters,
2811 : pszUnitAuthority, pszUnitCode));
2812 39 : d->undoDemoteFromBoundCRS();
2813 :
2814 39 : d->m_osLinearUnits = pszName;
2815 39 : d->dfToMeter = dfInMeters;
2816 :
2817 39 : return OGRERR_NONE;
2818 : }
2819 :
2820 : /************************************************************************/
2821 : /* OSRSetLinearUnitsAndUpdateParameters() */
2822 : /************************************************************************/
2823 :
2824 : /**
2825 : * \brief Set the linear units for the projection.
2826 : *
2827 : * This function is the same as
2828 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2829 : */
2830 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2831 : const char *pszUnits,
2832 : double dfInMeters)
2833 :
2834 : {
2835 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2836 : OGRERR_FAILURE);
2837 :
2838 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2839 1 : dfInMeters);
2840 : }
2841 :
2842 : /************************************************************************/
2843 : /* SetLinearUnits() */
2844 : /************************************************************************/
2845 :
2846 : /**
2847 : * \brief Set the linear units for the projection.
2848 : *
2849 : * This method creates a UNIT subnode with the specified values as a
2850 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2851 : * Geographic 3D CRS the vertical axis units will be set.
2852 : *
2853 : * This method does the same as the C function OSRSetLinearUnits().
2854 : *
2855 : * @param pszUnitsName the units name to be used. Some preferred units
2856 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2857 : * and SRS_UL_US_FOOT.
2858 : *
2859 : * @param dfInMeters the value to multiple by a length in the indicated
2860 : * units to transform to meters. Some standard conversion factors can
2861 : * be found in ogr_srs_api.h.
2862 : *
2863 : * @return OGRERR_NONE on success.
2864 : */
2865 :
2866 6587 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2867 : double dfInMeters)
2868 :
2869 : {
2870 6587 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2871 : }
2872 :
2873 : /************************************************************************/
2874 : /* OSRSetLinearUnits() */
2875 : /************************************************************************/
2876 :
2877 : /**
2878 : * \brief Set the linear units for the projection.
2879 : *
2880 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2881 : */
2882 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2883 : double dfInMeters)
2884 :
2885 : {
2886 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2887 :
2888 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2889 : }
2890 :
2891 : /************************************************************************/
2892 : /* SetTargetLinearUnits() */
2893 : /************************************************************************/
2894 :
2895 : /**
2896 : * \brief Set the linear units for the projection.
2897 : *
2898 : * This method creates a UNIT subnode with the specified values as a
2899 : * child of the target node.
2900 : *
2901 : * This method does the same as the C function OSRSetTargetLinearUnits().
2902 : *
2903 : * @param pszTargetKey the keyword to set the linear units for.
2904 : * i.e. "PROJCS" or "VERT_CS"
2905 : *
2906 : * @param pszUnitsName the units name to be used. Some preferred units
2907 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2908 : * and SRS_UL_US_FOOT.
2909 : *
2910 : * @param dfInMeters the value to multiple by a length in the indicated
2911 : * units to transform to meters. Some standard conversion factors can
2912 : * be found in ogr_srs_api.h.
2913 : *
2914 : * @param pszUnitAuthority Unit authority name. Or nullptr
2915 : *
2916 : * @param pszUnitCode Unit code. Or nullptr
2917 : *
2918 : * @return OGRERR_NONE on success.
2919 : *
2920 : * @since OGR 1.9.0
2921 : */
2922 :
2923 10255 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2924 : const char *pszUnitsName,
2925 : double dfInMeters,
2926 : const char *pszUnitAuthority,
2927 : const char *pszUnitCode)
2928 :
2929 : {
2930 20510 : TAKE_OPTIONAL_LOCK();
2931 :
2932 10255 : if (dfInMeters <= 0.0)
2933 0 : return OGRERR_FAILURE;
2934 :
2935 10255 : d->refreshProjObj();
2936 10255 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2937 10255 : if (pszTargetKey == nullptr)
2938 : {
2939 10255 : if (!d->m_pj_crs)
2940 0 : return OGRERR_FAILURE;
2941 :
2942 10255 : d->demoteFromBoundCRS();
2943 10255 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2944 : {
2945 14754 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2946 7377 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2947 : pszUnitAuthority, pszUnitCode, false));
2948 : }
2949 20510 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
2950 10255 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2951 : pszUnitAuthority, pszUnitCode));
2952 10255 : d->undoDemoteFromBoundCRS();
2953 :
2954 10255 : d->m_osLinearUnits = pszUnitsName;
2955 10255 : d->dfToMeter = dfInMeters;
2956 :
2957 10255 : return OGRERR_NONE;
2958 : }
2959 :
2960 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
2961 :
2962 0 : if (poCS == nullptr)
2963 0 : return OGRERR_FAILURE;
2964 :
2965 0 : char szValue[128] = {'\0'};
2966 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
2967 0 : dfInMeters > std::numeric_limits<int>::min() &&
2968 0 : dfInMeters == static_cast<int>(dfInMeters))
2969 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
2970 : else
2971 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
2972 :
2973 0 : OGR_SRSNode *poUnits = nullptr;
2974 0 : if (poCS->FindChild("UNIT") >= 0)
2975 : {
2976 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
2977 0 : if (poUnits->GetChildCount() < 2)
2978 0 : return OGRERR_FAILURE;
2979 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
2980 0 : poUnits->GetChild(1)->SetValue(szValue);
2981 0 : if (poUnits->FindChild("AUTHORITY") != -1)
2982 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
2983 : }
2984 : else
2985 : {
2986 0 : poUnits = new OGR_SRSNode("UNIT");
2987 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
2988 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
2989 :
2990 0 : poCS->AddChild(poUnits);
2991 : }
2992 :
2993 0 : return OGRERR_NONE;
2994 : }
2995 :
2996 : /************************************************************************/
2997 : /* OSRSetLinearUnits() */
2998 : /************************************************************************/
2999 :
3000 : /**
3001 : * \brief Set the linear units for the target node.
3002 : *
3003 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3004 : *
3005 : * @since OGR 1.9.0
3006 : */
3007 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3008 : const char *pszTargetKey, const char *pszUnits,
3009 : double dfInMeters)
3010 :
3011 : {
3012 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3013 :
3014 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3015 1 : dfInMeters);
3016 : }
3017 :
3018 : /************************************************************************/
3019 : /* GetLinearUnits() */
3020 : /************************************************************************/
3021 :
3022 : /**
3023 : * \brief Fetch linear projection units.
3024 : *
3025 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3026 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3027 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3028 : * axis units will be returned.
3029 : *
3030 : * This method does the same thing as the C function OSRGetLinearUnits()
3031 : *
3032 : * @param ppszName a pointer to be updated with the pointer to the units name.
3033 : * The returned value remains internal to the OGRSpatialReference and should
3034 : * not be freed, or modified. It may be invalidated on the next
3035 : * OGRSpatialReference call.
3036 : *
3037 : * @return the value to multiply by linear distances to transform them to
3038 : * meters.
3039 : * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3040 : */
3041 :
3042 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3043 :
3044 : {
3045 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3046 : }
3047 :
3048 : /**
3049 : * \brief Fetch linear projection units.
3050 : *
3051 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3052 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3053 : * for units.
3054 : *
3055 : * This method does the same thing as the C function OSRGetLinearUnits()
3056 : *
3057 : * @param ppszName a pointer to be updated with the pointer to the units name.
3058 : * The returned value remains internal to the OGRSpatialReference and should
3059 : * not be freed, or modified. It may be invalidated on the next
3060 : * OGRSpatialReference call.
3061 : *
3062 : * @return the value to multiply by linear distances to transform them to
3063 : * meters.
3064 : * @since GDAL 2.3.0
3065 : */
3066 :
3067 15393 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3068 :
3069 : {
3070 15393 : return GetTargetLinearUnits(nullptr, ppszName);
3071 : }
3072 :
3073 : /************************************************************************/
3074 : /* OSRGetLinearUnits() */
3075 : /************************************************************************/
3076 :
3077 : /**
3078 : * \brief Fetch linear projection units.
3079 : *
3080 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3081 : */
3082 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3083 :
3084 : {
3085 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3086 :
3087 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3088 : }
3089 :
3090 : /************************************************************************/
3091 : /* GetTargetLinearUnits() */
3092 : /************************************************************************/
3093 :
3094 : /**
3095 : * \brief Fetch linear units for target.
3096 : *
3097 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3098 : *
3099 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3100 : *
3101 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3102 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3103 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3104 : * @param ppszName a pointer to be updated with the pointer to the units name.
3105 : * The returned value remains internal to the OGRSpatialReference and should not
3106 : * be freed, or modified. It may be invalidated on the next
3107 : * OGRSpatialReference call. ppszName can be set to NULL.
3108 : *
3109 : * @return the value to multiply by linear distances to transform them to
3110 : * meters.
3111 : *
3112 : * @since OGR 1.9.0
3113 : * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3114 : * const.
3115 : */
3116 :
3117 15540 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3118 : const char **ppszName) const
3119 :
3120 : {
3121 31080 : TAKE_OPTIONAL_LOCK();
3122 :
3123 15540 : d->refreshProjObj();
3124 :
3125 15540 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3126 15540 : if (pszTargetKey == nullptr)
3127 : {
3128 : // Use cached result if available
3129 15452 : if (!d->m_osLinearUnits.empty())
3130 : {
3131 7359 : if (ppszName)
3132 6645 : *ppszName = d->m_osLinearUnits.c_str();
3133 7359 : return d->dfToMeter;
3134 : }
3135 :
3136 : while (true)
3137 : {
3138 8093 : if (d->m_pj_crs == nullptr)
3139 : {
3140 245 : break;
3141 : }
3142 :
3143 7848 : d->demoteFromBoundCRS();
3144 7848 : PJ *coordSys = nullptr;
3145 7848 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3146 : {
3147 30 : for (int iComponent = 0; iComponent < 2; iComponent++)
3148 : {
3149 30 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3150 30 : d->m_pj_crs, iComponent);
3151 30 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3152 : {
3153 : auto temp =
3154 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3155 0 : proj_destroy(subCRS);
3156 0 : subCRS = temp;
3157 : }
3158 60 : if (subCRS &&
3159 30 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3160 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3161 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3162 : {
3163 24 : coordSys = proj_crs_get_coordinate_system(
3164 : d->getPROJContext(), subCRS);
3165 24 : proj_destroy(subCRS);
3166 24 : break;
3167 : }
3168 6 : else if (subCRS)
3169 : {
3170 6 : proj_destroy(subCRS);
3171 : }
3172 : }
3173 24 : if (coordSys == nullptr)
3174 : {
3175 0 : d->undoDemoteFromBoundCRS();
3176 0 : break;
3177 : }
3178 : }
3179 : else
3180 : {
3181 7824 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3182 7824 : d->m_pj_crs);
3183 : }
3184 :
3185 7848 : d->undoDemoteFromBoundCRS();
3186 7848 : if (!coordSys)
3187 : {
3188 0 : break;
3189 : }
3190 7848 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3191 :
3192 7848 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3193 2119 : csType != PJ_CS_TYPE_VERTICAL &&
3194 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3195 : csType != PJ_CS_TYPE_SPHERICAL)
3196 : {
3197 0 : proj_destroy(coordSys);
3198 0 : break;
3199 : }
3200 :
3201 7848 : int axis = 0;
3202 :
3203 7848 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3204 : csType == PJ_CS_TYPE_SPHERICAL)
3205 : {
3206 : const int axisCount =
3207 2119 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3208 :
3209 2119 : if (axisCount == 3)
3210 : {
3211 4 : axis = 2;
3212 : }
3213 : else
3214 : {
3215 2115 : proj_destroy(coordSys);
3216 2115 : break;
3217 : }
3218 : }
3219 :
3220 5733 : double dfConvFactor = 0.0;
3221 5733 : const char *pszUnitName = nullptr;
3222 5733 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3223 : nullptr, nullptr, nullptr, &dfConvFactor,
3224 : &pszUnitName, nullptr, nullptr))
3225 : {
3226 0 : proj_destroy(coordSys);
3227 0 : break;
3228 : }
3229 :
3230 5733 : d->m_osLinearUnits = pszUnitName;
3231 5733 : d->dfToMeter = dfConvFactor;
3232 5733 : if (ppszName)
3233 1050 : *ppszName = d->m_osLinearUnits.c_str();
3234 :
3235 5733 : proj_destroy(coordSys);
3236 5733 : return dfConvFactor;
3237 : }
3238 :
3239 2360 : d->m_osLinearUnits = "unknown";
3240 2360 : d->dfToMeter = 1.0;
3241 :
3242 2360 : if (ppszName != nullptr)
3243 2178 : *ppszName = d->m_osLinearUnits.c_str();
3244 2360 : return 1.0;
3245 : }
3246 :
3247 88 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3248 :
3249 88 : if (ppszName != nullptr)
3250 37 : *ppszName = "unknown";
3251 :
3252 88 : if (poCS == nullptr)
3253 50 : return 1.0;
3254 :
3255 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3256 : {
3257 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3258 :
3259 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3260 : {
3261 38 : if (ppszName != nullptr)
3262 37 : *ppszName = poChild->GetChild(0)->GetValue();
3263 :
3264 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3265 : }
3266 : }
3267 :
3268 0 : return 1.0;
3269 : }
3270 :
3271 : /**
3272 : * \brief Fetch linear units for target.
3273 : *
3274 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3275 : *
3276 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3277 : *
3278 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3279 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3280 : * GEOCCS and VERT_CS are looked up)
3281 : * @param ppszName a pointer to be updated with the pointer to the units name.
3282 : * The returned value remains internal to the OGRSpatialReference and should not
3283 : * be freed, or modified. It may be invalidated on the next
3284 : * OGRSpatialReference call. ppszName can be set to NULL.
3285 : *
3286 : * @return the value to multiply by linear distances to transform them to
3287 : * meters.
3288 : *
3289 : * @since GDAL 2.3.0
3290 : */
3291 :
3292 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3293 : char **ppszName) const
3294 :
3295 : {
3296 0 : return GetTargetLinearUnits(pszTargetKey,
3297 0 : const_cast<const char **>(ppszName));
3298 : }
3299 :
3300 : /************************************************************************/
3301 : /* OSRGetTargetLinearUnits() */
3302 : /************************************************************************/
3303 :
3304 : /**
3305 : * \brief Fetch linear projection units.
3306 : *
3307 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3308 : *
3309 : * @since OGR 1.9.0
3310 : */
3311 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3312 : const char *pszTargetKey, char **ppszName)
3313 :
3314 : {
3315 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3316 :
3317 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3318 4 : pszTargetKey, const_cast<const char **>(ppszName));
3319 : }
3320 :
3321 : /************************************************************************/
3322 : /* GetPrimeMeridian() */
3323 : /************************************************************************/
3324 :
3325 : /**
3326 : * \brief Fetch prime meridian info.
3327 : *
3328 : * Returns the offset of the prime meridian from greenwich in degrees,
3329 : * and the prime meridian name (if requested). If no PRIMEM value exists
3330 : * in the coordinate system definition a value of "Greenwich" and an
3331 : * offset of 0.0 is assumed.
3332 : *
3333 : * If the prime meridian name is returned, the pointer is to an internal
3334 : * copy of the name. It should not be freed, altered or depended on after
3335 : * the next OGR call.
3336 : *
3337 : * This method is the same as the C function OSRGetPrimeMeridian().
3338 : *
3339 : * @param ppszName return location for prime meridian name. If NULL, name
3340 : * is not returned.
3341 : *
3342 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3343 : * degrees.
3344 : * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3345 : */
3346 :
3347 1365 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3348 :
3349 : {
3350 2730 : TAKE_OPTIONAL_LOCK();
3351 :
3352 1365 : d->refreshProjObj();
3353 :
3354 1365 : if (!d->m_osPrimeMeridianName.empty())
3355 : {
3356 61 : if (ppszName != nullptr)
3357 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3358 61 : return d->dfFromGreenwich;
3359 : }
3360 :
3361 : while (true)
3362 : {
3363 1304 : if (!d->m_pj_crs)
3364 0 : break;
3365 :
3366 1304 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3367 1304 : if (!pm)
3368 0 : break;
3369 :
3370 1304 : d->m_osPrimeMeridianName = proj_get_name(pm);
3371 1304 : if (ppszName)
3372 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3373 1304 : double dfLongitude = 0.0;
3374 1304 : double dfConvFactor = 0.0;
3375 1304 : proj_prime_meridian_get_parameters(
3376 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3377 1304 : proj_destroy(pm);
3378 2608 : d->dfFromGreenwich =
3379 1304 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3380 1304 : return d->dfFromGreenwich;
3381 : }
3382 :
3383 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3384 0 : d->dfFromGreenwich = 0.0;
3385 0 : if (ppszName != nullptr)
3386 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3387 0 : return d->dfFromGreenwich;
3388 : }
3389 :
3390 : /**
3391 : * \brief Fetch prime meridian info.
3392 : *
3393 : * Returns the offset of the prime meridian from greenwich in degrees,
3394 : * and the prime meridian name (if requested). If no PRIMEM value exists
3395 : * in the coordinate system definition a value of "Greenwich" and an
3396 : * offset of 0.0 is assumed.
3397 : *
3398 : * If the prime meridian name is returned, the pointer is to an internal
3399 : * copy of the name. It should not be freed, altered or depended on after
3400 : * the next OGR call.
3401 : *
3402 : * This method is the same as the C function OSRGetPrimeMeridian().
3403 : *
3404 : * @param ppszName return location for prime meridian name. If NULL, name
3405 : * is not returned.
3406 : *
3407 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3408 : * degrees.
3409 : * @since GDAL 2.3.0
3410 : */
3411 :
3412 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3413 :
3414 : {
3415 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3416 : }
3417 :
3418 : /************************************************************************/
3419 : /* OSRGetPrimeMeridian() */
3420 : /************************************************************************/
3421 :
3422 : /**
3423 : * \brief Fetch prime meridian info.
3424 : *
3425 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3426 : */
3427 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3428 :
3429 : {
3430 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3431 :
3432 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3433 0 : const_cast<const char **>(ppszName));
3434 : }
3435 :
3436 : /************************************************************************/
3437 : /* SetGeogCS() */
3438 : /************************************************************************/
3439 :
3440 : /**
3441 : * \brief Set geographic coordinate system.
3442 : *
3443 : * This method is used to set the datum, ellipsoid, prime meridian and
3444 : * angular units for a geographic coordinate system. It can be used on its
3445 : * own to establish a geographic spatial reference, or applied to a
3446 : * projected coordinate system to establish the underlying geographic
3447 : * coordinate system.
3448 : *
3449 : * This method does the same as the C function OSRSetGeogCS().
3450 : *
3451 : * @param pszGeogName user visible name for the geographic coordinate system
3452 : * (not to serve as a key).
3453 : *
3454 : * @param pszDatumName key name for this datum. The OpenGIS specification
3455 : * lists some known values, and otherwise EPSG datum names with a standard
3456 : * transformation are considered legal keys.
3457 : *
3458 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3459 : *
3460 : * @param dfSemiMajor the semi major axis of the spheroid.
3461 : *
3462 : * @param dfInvFlattening the inverse flattening for the spheroid.
3463 : * This can be computed from the semi minor axis as
3464 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3465 : *
3466 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3467 : * If this is NULL a default value of "Greenwich" will be used.
3468 : *
3469 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3470 : * meridian. Always in Degrees
3471 : *
3472 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3473 : * standard names). If NULL a value of "degrees" will be assumed.
3474 : *
3475 : * @param dfConvertToRadians value to multiply angular units by to transform
3476 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3477 : * pszAngularUnits is NULL.
3478 : *
3479 : * @return OGRERR_NONE on success.
3480 : */
3481 :
3482 8345 : OGRErr OGRSpatialReference::SetGeogCS(
3483 : const char *pszGeogName, const char *pszDatumName,
3484 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3485 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3486 : double dfConvertToRadians)
3487 :
3488 : {
3489 16690 : TAKE_OPTIONAL_LOCK();
3490 :
3491 8345 : d->bNormInfoSet = FALSE;
3492 8345 : d->m_osAngularUnits.clear();
3493 8345 : d->m_dfAngularUnitToRadian = 0.0;
3494 8345 : d->m_osPrimeMeridianName.clear();
3495 8345 : d->dfFromGreenwich = 0.0;
3496 :
3497 : /* -------------------------------------------------------------------- */
3498 : /* For a geocentric coordinate system we want to set the datum */
3499 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3500 : /* temporary srs and use the copy method which has special */
3501 : /* handling for GEOCCS. */
3502 : /* -------------------------------------------------------------------- */
3503 8345 : if (IsGeocentric())
3504 : {
3505 4 : OGRSpatialReference oGCS;
3506 :
3507 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3508 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3509 : dfConvertToRadians);
3510 2 : return CopyGeogCSFrom(&oGCS);
3511 : }
3512 :
3513 8343 : auto cs = proj_create_ellipsoidal_2D_cs(
3514 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3515 : dfConvertToRadians);
3516 : // Prime meridian expressed in Degree
3517 8343 : auto obj = proj_create_geographic_crs(
3518 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3519 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3520 8343 : proj_destroy(cs);
3521 :
3522 12535 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3523 4192 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3524 : {
3525 4151 : d->setPjCRS(obj);
3526 : }
3527 4192 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3528 : {
3529 8384 : d->setPjCRS(
3530 4192 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3531 4192 : proj_destroy(obj);
3532 : }
3533 : else
3534 : {
3535 0 : proj_destroy(obj);
3536 : }
3537 :
3538 8343 : return OGRERR_NONE;
3539 : }
3540 :
3541 : /************************************************************************/
3542 : /* OSRSetGeogCS() */
3543 : /************************************************************************/
3544 :
3545 : /**
3546 : * \brief Set geographic coordinate system.
3547 : *
3548 : * This function is the same as OGRSpatialReference::SetGeogCS()
3549 : */
3550 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3551 : const char *pszDatumName, const char *pszSpheroidName,
3552 : double dfSemiMajor, double dfInvFlattening,
3553 : const char *pszPMName, double dfPMOffset,
3554 : const char *pszAngularUnits, double dfConvertToRadians)
3555 :
3556 : {
3557 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3558 :
3559 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3560 : pszSpheroidName, dfSemiMajor,
3561 : dfInvFlattening, pszPMName, dfPMOffset,
3562 18 : pszAngularUnits, dfConvertToRadians);
3563 : }
3564 :
3565 : /************************************************************************/
3566 : /* SetWellKnownGeogCS() */
3567 : /************************************************************************/
3568 :
3569 : /**
3570 : * \brief Set a GeogCS based on well known name.
3571 : *
3572 : * This may be called on an empty OGRSpatialReference to make a geographic
3573 : * coordinate system, or on something with an existing PROJCS node to
3574 : * set the underlying geographic coordinate system of a projected coordinate
3575 : * system.
3576 : *
3577 : * The following well known text values are currently supported,
3578 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3579 : * <ul>
3580 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3581 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3582 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3583 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3584 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3585 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3586 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3587 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3588 : * </ul>
3589 : *
3590 : * @param pszName name of well known geographic coordinate system.
3591 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3592 : * recognised, the target object is already initialized, or an EPSG value
3593 : * can't be successfully looked up.
3594 : */
3595 :
3596 2371 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3597 :
3598 : {
3599 4742 : TAKE_OPTIONAL_LOCK();
3600 :
3601 : /* -------------------------------------------------------------------- */
3602 : /* Check for EPSG authority numbers. */
3603 : /* -------------------------------------------------------------------- */
3604 2371 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3605 : {
3606 84 : OGRSpatialReference oSRS2;
3607 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3608 42 : if (eErr != OGRERR_NONE)
3609 0 : return eErr;
3610 :
3611 42 : if (!oSRS2.IsGeographic())
3612 0 : return OGRERR_FAILURE;
3613 :
3614 42 : return CopyGeogCSFrom(&oSRS2);
3615 : }
3616 :
3617 : /* -------------------------------------------------------------------- */
3618 : /* Check for simple names. */
3619 : /* -------------------------------------------------------------------- */
3620 2329 : const char *pszWKT = nullptr;
3621 :
3622 2329 : if (EQUAL(pszName, "WGS84"))
3623 : {
3624 2046 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3625 : }
3626 283 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3627 : {
3628 104 : pszWKT =
3629 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3630 : "ELLIPSOID[\"WGS "
3631 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3632 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3633 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3634 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3635 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3636 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3637 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3638 : "ID[\"OGC\",\"CRS84\"]]";
3639 : }
3640 179 : else if (EQUAL(pszName, "WGS72"))
3641 19 : pszWKT =
3642 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3643 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3644 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3645 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3646 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3647 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3648 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3649 :
3650 160 : else if (EQUAL(pszName, "NAD27"))
3651 132 : pszWKT =
3652 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3653 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3654 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3655 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3656 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3657 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3658 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3659 :
3660 28 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3661 0 : pszWKT =
3662 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3663 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3664 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3665 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3666 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3667 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3668 :
3669 28 : else if (EQUAL(pszName, "NAD83"))
3670 24 : pszWKT =
3671 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3672 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3673 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3674 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3675 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3676 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3677 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3678 : "\"EPSG\",\"4269\"]]";
3679 :
3680 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3681 0 : pszWKT =
3682 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3683 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3684 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3685 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3686 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3687 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3688 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3689 :
3690 : else
3691 4 : return OGRERR_FAILURE;
3692 :
3693 : /* -------------------------------------------------------------------- */
3694 : /* Import the WKT */
3695 : /* -------------------------------------------------------------------- */
3696 4650 : OGRSpatialReference oSRS2;
3697 2325 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3698 2325 : if (eErr != OGRERR_NONE)
3699 0 : return eErr;
3700 :
3701 : /* -------------------------------------------------------------------- */
3702 : /* Copy over. */
3703 : /* -------------------------------------------------------------------- */
3704 2325 : return CopyGeogCSFrom(&oSRS2);
3705 : }
3706 :
3707 : /************************************************************************/
3708 : /* OSRSetWellKnownGeogCS() */
3709 : /************************************************************************/
3710 :
3711 : /**
3712 : * \brief Set a GeogCS based on well known name.
3713 : *
3714 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3715 : */
3716 121 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3717 :
3718 : {
3719 121 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3720 :
3721 121 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3722 : }
3723 :
3724 : /************************************************************************/
3725 : /* CopyGeogCSFrom() */
3726 : /************************************************************************/
3727 :
3728 : /**
3729 : * \brief Copy GEOGCS from another OGRSpatialReference.
3730 : *
3731 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3732 : * If this object has a PROJCS root already, the GEOGCS is installed within
3733 : * it, otherwise it is installed as the root.
3734 : *
3735 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3736 : *
3737 : * @return OGRERR_NONE on success or an error code.
3738 : */
3739 :
3740 2957 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3741 :
3742 : {
3743 5914 : TAKE_OPTIONAL_LOCK();
3744 :
3745 2957 : d->bNormInfoSet = FALSE;
3746 2957 : d->m_osAngularUnits.clear();
3747 2957 : d->m_dfAngularUnitToRadian = 0.0;
3748 2957 : d->m_osPrimeMeridianName.clear();
3749 2957 : d->dfFromGreenwich = 0.0;
3750 :
3751 2957 : d->refreshProjObj();
3752 2957 : poSrcSRS->d->refreshProjObj();
3753 2957 : if (!poSrcSRS->d->m_pj_crs)
3754 : {
3755 0 : return OGRERR_FAILURE;
3756 : }
3757 : auto geodCRS =
3758 2957 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3759 2957 : if (!geodCRS)
3760 : {
3761 0 : return OGRERR_FAILURE;
3762 : }
3763 :
3764 : /* -------------------------------------------------------------------- */
3765 : /* Handle geocentric coordinate systems specially. We just */
3766 : /* want to copy the DATUM. */
3767 : /* -------------------------------------------------------------------- */
3768 2957 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3769 : {
3770 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3771 : #if PROJ_VERSION_MAJOR > 7 || \
3772 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3773 : if (datum == nullptr)
3774 : {
3775 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3776 : }
3777 : #endif
3778 3 : if (datum == nullptr)
3779 : {
3780 0 : proj_destroy(geodCRS);
3781 0 : return OGRERR_FAILURE;
3782 : }
3783 :
3784 3 : const char *pszUnitName = nullptr;
3785 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3786 :
3787 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3788 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3789 : unitConvFactor);
3790 3 : proj_destroy(datum);
3791 :
3792 3 : d->setPjCRS(pj_crs);
3793 : }
3794 :
3795 2954 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3796 : {
3797 325 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3798 325 : d->m_pj_crs, geodCRS);
3799 325 : d->setPjCRS(pj_crs);
3800 : }
3801 :
3802 : else
3803 : {
3804 2629 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3805 : }
3806 :
3807 : // Apply TOWGS84 of source CRS
3808 2957 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3809 : {
3810 : auto target =
3811 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3812 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3813 1 : poSrcSRS->d->m_pj_crs);
3814 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3815 : target, co));
3816 1 : proj_destroy(target);
3817 1 : proj_destroy(co);
3818 : }
3819 :
3820 2957 : proj_destroy(geodCRS);
3821 :
3822 2957 : return OGRERR_NONE;
3823 : }
3824 :
3825 : /************************************************************************/
3826 : /* OSRCopyGeogCSFrom() */
3827 : /************************************************************************/
3828 :
3829 : /**
3830 : * \brief Copy GEOGCS from another OGRSpatialReference.
3831 : *
3832 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3833 : */
3834 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3835 : const OGRSpatialReferenceH hSrcSRS)
3836 :
3837 : {
3838 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3839 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3840 :
3841 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3842 : }
3843 :
3844 : /************************************************************************/
3845 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3846 : /************************************************************************/
3847 :
3848 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3849 : *
3850 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3851 : */
3852 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3853 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3854 :
3855 : /**
3856 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3857 : */
3858 2433 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3859 : {
3860 2433 : return SET_FROM_USER_INPUT_LIMITATIONS;
3861 : }
3862 :
3863 : /************************************************************************/
3864 : /* RemoveIDFromMemberOfEnsembles() */
3865 : /************************************************************************/
3866 :
3867 : // cppcheck-suppress constParameterReference
3868 198 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3869 : {
3870 : // Remove "id" from members of datum ensembles for compatibility with
3871 : // older PROJ versions
3872 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3873 : // and https://github.com/OSGeo/PROJ/pull/3221
3874 198 : if (obj.GetType() == CPLJSONObject::Type::Object)
3875 : {
3876 243 : for (auto &subObj : obj.GetChildren())
3877 : {
3878 191 : RemoveIDFromMemberOfEnsembles(subObj);
3879 : }
3880 : }
3881 162 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3882 162 : obj.GetName() == "members")
3883 : {
3884 51 : for (auto &subObj : obj.ToArray())
3885 : {
3886 44 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3887 : {
3888 43 : subObj.Delete("id");
3889 : }
3890 : }
3891 : }
3892 198 : }
3893 :
3894 : /************************************************************************/
3895 : /* SetFromUserInput() */
3896 : /************************************************************************/
3897 :
3898 : /**
3899 : * \brief Set spatial reference from various text formats.
3900 : *
3901 : * This method will examine the provided input, and try to deduce the
3902 : * format, and then use it to initialize the spatial reference system. It
3903 : * may take the following forms:
3904 : *
3905 : * <ol>
3906 : * <li> Well Known Text definition - passed on to importFromWkt().
3907 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3908 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3909 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3910 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3911 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3912 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3913 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3914 : * WGS84 or WGS72.
3915 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3916 : * <li> PROJJSON (PROJ >= 6.2)
3917 : * </ol>
3918 : *
3919 : * It is expected that this method will be extended in the future to support
3920 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3921 : * State Plane definitions.
3922 : *
3923 : * This method is intended to be flexible, but by its nature it is
3924 : * imprecise as it must guess information about the format intended. When
3925 : * possible applications should call the specific method appropriate if the
3926 : * input is known to be in a particular format.
3927 : *
3928 : * This method does the same thing as the OSRSetFromUserInput() function.
3929 : *
3930 : * @param pszDefinition text definition to try to deduce SRS from.
3931 : *
3932 : * @return OGRERR_NONE on success, or an error code if the name isn't
3933 : * recognised, the definition is corrupt, or an EPSG value can't be
3934 : * successfully looked up.
3935 : */
3936 :
3937 13880 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3938 : {
3939 13880 : return SetFromUserInput(pszDefinition, nullptr);
3940 : }
3941 :
3942 : /**
3943 : * \brief Set spatial reference from various text formats.
3944 : *
3945 : * This method will examine the provided input, and try to deduce the
3946 : * format, and then use it to initialize the spatial reference system. It
3947 : * may take the following forms:
3948 : *
3949 : * <ol>
3950 : * <li> Well Known Text definition - passed on to importFromWkt().
3951 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3952 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3953 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3954 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3955 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3956 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3957 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3958 : * WGS84 or WGS72.
3959 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3960 : * <li> PROJJSON (PROJ >= 6.2)
3961 : * </ol>
3962 : *
3963 : * It is expected that this method will be extended in the future to support
3964 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3965 : * State Plane definitions.
3966 : *
3967 : * This method is intended to be flexible, but by its nature it is
3968 : * imprecise as it must guess information about the format intended. When
3969 : * possible applications should call the specific method appropriate if the
3970 : * input is known to be in a particular format.
3971 : *
3972 : * This method does the same thing as the OSRSetFromUserInput() and
3973 : * OSRSetFromUserInputEx() functions.
3974 : *
3975 : * @param pszDefinition text definition to try to deduce SRS from.
3976 : *
3977 : * @param papszOptions NULL terminated list of options, or NULL.
3978 : * <ol>
3979 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
3980 : * Whether http:// or https:// access is allowed. Defaults to YES.
3981 : * <li> ALLOW_FILE_ACCESS=YES/NO.
3982 : * Whether reading a file using the Virtual File System layer is allowed
3983 : * (can also involve network access). Defaults to YES.
3984 : * </ol>
3985 : *
3986 : * @return OGRERR_NONE on success, or an error code if the name isn't
3987 : * recognised, the definition is corrupt, or an EPSG value can't be
3988 : * successfully looked up.
3989 : */
3990 :
3991 17752 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
3992 : CSLConstList papszOptions)
3993 : {
3994 35504 : TAKE_OPTIONAL_LOCK();
3995 :
3996 : // Skip leading white space
3997 17754 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
3998 2 : pszDefinition++;
3999 :
4000 17752 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4001 : {
4002 1 : pszDefinition += 6;
4003 : }
4004 :
4005 : /* -------------------------------------------------------------------- */
4006 : /* Is it a recognised syntax? */
4007 : /* -------------------------------------------------------------------- */
4008 17752 : const char *const wktKeywords[] = {
4009 : // WKT1
4010 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4011 : // WKT2"
4012 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4013 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4014 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4015 226799 : for (const char *keyword : wktKeywords)
4016 : {
4017 216797 : if (STARTS_WITH_CI(pszDefinition, keyword))
4018 : {
4019 7750 : return importFromWkt(pszDefinition);
4020 : }
4021 : }
4022 :
4023 10002 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4024 10002 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4025 : {
4026 7661 : OGRErr eStatus = OGRERR_NONE;
4027 :
4028 7661 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4029 : {
4030 : // Use proj_create() as it allows things like EPSG:3157+4617
4031 : // that are not normally supported by the below code that
4032 : // builds manually a compound CRS
4033 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4034 62 : if (!pj)
4035 : {
4036 1 : return OGRERR_FAILURE;
4037 : }
4038 61 : Clear();
4039 61 : d->setPjCRS(pj);
4040 61 : return OGRERR_NONE;
4041 : }
4042 : else
4043 : {
4044 : eStatus =
4045 7599 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4046 : }
4047 :
4048 7599 : return eStatus;
4049 : }
4050 :
4051 2341 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4052 1673 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4053 1672 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4054 1614 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4055 1614 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4056 1614 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4057 727 : return importFromURN(pszDefinition);
4058 :
4059 1614 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4060 1612 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4061 1611 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4062 935 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4063 934 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4064 680 : return importFromCRSURL(pszDefinition);
4065 :
4066 934 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4067 1 : return importFromWMSAUTO(pszDefinition);
4068 :
4069 : // WMS/WCS OGC codes like OGC:CRS84.
4070 933 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4071 27 : return SetWellKnownGeogCS(pszDefinition + 4);
4072 :
4073 906 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4074 1 : return SetWellKnownGeogCS(pszDefinition);
4075 :
4076 905 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4077 : {
4078 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4079 0 : char *pszCode = strstr(pszFile, ",") + 1;
4080 :
4081 0 : pszCode[-1] = '\0';
4082 :
4083 0 : OGRErr err = importFromDict(pszFile, pszCode);
4084 0 : CPLFree(pszFile);
4085 :
4086 0 : return err;
4087 : }
4088 :
4089 905 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4090 901 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4091 : {
4092 265 : Clear();
4093 265 : return SetWellKnownGeogCS(pszDefinition);
4094 : }
4095 :
4096 : // PROJJSON
4097 640 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4098 53 : (strstr(pszDefinition, "GeodeticCRS") ||
4099 53 : strstr(pszDefinition, "GeographicCRS") ||
4100 21 : strstr(pszDefinition, "ProjectedCRS") ||
4101 0 : strstr(pszDefinition, "VerticalCRS") ||
4102 0 : strstr(pszDefinition, "BoundCRS") ||
4103 0 : strstr(pszDefinition, "CompoundCRS") ||
4104 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4105 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4106 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4107 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4108 0 : strstr(pszDefinition, "EngineeringCRS") ||
4109 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4110 0 : strstr(pszDefinition, "ParametricCRS") ||
4111 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4112 0 : strstr(pszDefinition, "TemporalCRS") ||
4113 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4114 : {
4115 : PJ *pj;
4116 53 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4117 : {
4118 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4119 : // a unknown id.
4120 7 : CPLJSONDocument oCRSDoc;
4121 7 : if (!oCRSDoc.LoadMemory(pszDefinition))
4122 0 : return OGRERR_CORRUPT_DATA;
4123 7 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4124 7 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4125 7 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4126 : }
4127 : else
4128 : {
4129 46 : pj = proj_create(d->getPROJContext(), pszDefinition);
4130 : }
4131 53 : if (!pj)
4132 : {
4133 2 : return OGRERR_FAILURE;
4134 : }
4135 51 : Clear();
4136 51 : d->setPjCRS(pj);
4137 51 : return OGRERR_NONE;
4138 : }
4139 :
4140 587 : if (strstr(pszDefinition, "+proj") != nullptr ||
4141 240 : strstr(pszDefinition, "+init") != nullptr)
4142 347 : return importFromProj4(pszDefinition);
4143 :
4144 240 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4145 240 : STARTS_WITH_CI(pszDefinition, "https://"))
4146 : {
4147 1 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4148 : "ALLOW_NETWORK_ACCESS", "YES")))
4149 0 : return importFromUrl(pszDefinition);
4150 :
4151 1 : CPLError(CE_Failure, CPLE_AppDefined,
4152 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4153 : pszDefinition);
4154 1 : return OGRERR_FAILURE;
4155 : }
4156 :
4157 239 : if (EQUAL(pszDefinition, "osgb:BNG"))
4158 : {
4159 13 : return importFromEPSG(27700);
4160 : }
4161 :
4162 : // Used by German CityGML files
4163 226 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4164 : {
4165 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4166 0 : return SetFromUserInput("EPSG:25832+5783");
4167 : }
4168 226 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4169 : {
4170 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4171 4 : return SetFromUserInput("EPSG:25832+7837");
4172 : }
4173 :
4174 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4175 222 : const char *pszDot = strrchr(pszDefinition, ':');
4176 222 : if (pszDot)
4177 : {
4178 109 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4179 : auto authorities =
4180 109 : proj_get_authorities_from_database(d->getPROJContext());
4181 109 : if (authorities)
4182 : {
4183 109 : std::set<std::string> aosCandidateAuthorities;
4184 236 : for (auto iter = authorities; *iter; ++iter)
4185 : {
4186 233 : if (*iter == osPrefix)
4187 : {
4188 106 : aosCandidateAuthorities.clear();
4189 106 : aosCandidateAuthorities.insert(*iter);
4190 106 : break;
4191 : }
4192 : // Deal with "IAU_2015" as authority in the list and input
4193 : // "IAU:code"
4194 127 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4195 127 : 0 &&
4196 0 : (*iter)[osPrefix.size()] == '_')
4197 : {
4198 0 : aosCandidateAuthorities.insert(*iter);
4199 : }
4200 : // Deal with "IAU_2015" as authority in the list and input
4201 : // "IAU:2015:code"
4202 254 : else if (osPrefix.find(':') != std::string::npos &&
4203 127 : osPrefix.size() == strlen(*iter) &&
4204 127 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4205 : {
4206 0 : aosCandidateAuthorities.clear();
4207 0 : aosCandidateAuthorities.insert(*iter);
4208 0 : break;
4209 : }
4210 : }
4211 :
4212 109 : proj_string_list_destroy(authorities);
4213 :
4214 109 : if (!aosCandidateAuthorities.empty())
4215 : {
4216 106 : auto obj = proj_create_from_database(
4217 : d->getPROJContext(),
4218 106 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4219 : PJ_CATEGORY_CRS, false, nullptr);
4220 106 : if (!obj)
4221 : {
4222 3 : return OGRERR_FAILURE;
4223 : }
4224 103 : Clear();
4225 103 : d->setPjCRS(obj);
4226 103 : return OGRERR_NONE;
4227 : }
4228 : }
4229 : }
4230 :
4231 : /* -------------------------------------------------------------------- */
4232 : /* Try to open it as a file. */
4233 : /* -------------------------------------------------------------------- */
4234 116 : if (!CPLTestBool(
4235 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4236 : {
4237 : VSIStatBufL sStat;
4238 24 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4239 12 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4240 : {
4241 0 : CPLError(CE_Failure, CPLE_AppDefined,
4242 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4243 : pszDefinition);
4244 0 : return OGRERR_FAILURE;
4245 : }
4246 : // We used to silently return an error without a CE_Failure message
4247 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4248 12 : return OGRERR_CORRUPT_DATA;
4249 : }
4250 :
4251 208 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4252 104 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4253 104 : if (fp == nullptr)
4254 99 : return OGRERR_CORRUPT_DATA;
4255 :
4256 5 : const size_t nBufMax = 100000;
4257 5 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4258 5 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4259 5 : VSIFCloseL(fp);
4260 :
4261 5 : if (nBytes == nBufMax - 1)
4262 : {
4263 0 : CPLDebug("OGR",
4264 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4265 : "but it is to large for our generous buffer. Is it really "
4266 : "just a WKT definition?",
4267 : pszDefinition);
4268 0 : CPLFree(pszBuffer);
4269 0 : return OGRERR_FAILURE;
4270 : }
4271 :
4272 5 : pszBuffer[nBytes] = '\0';
4273 :
4274 5 : char *pszBufPtr = pszBuffer;
4275 5 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4276 0 : pszBufPtr++;
4277 :
4278 5 : OGRErr err = OGRERR_NONE;
4279 5 : if (pszBufPtr[0] == '<')
4280 0 : err = importFromXML(pszBufPtr);
4281 5 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4282 5 : strstr(pszBuffer, "+init") != nullptr) &&
4283 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4284 0 : strstr(pszBuffer, "extension") == nullptr))
4285 0 : err = importFromProj4(pszBufPtr);
4286 : else
4287 : {
4288 5 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4289 : {
4290 3 : pszBufPtr += 6;
4291 : }
4292 :
4293 : // coverity[tainted_data]
4294 5 : err = importFromWkt(pszBufPtr);
4295 : }
4296 :
4297 5 : CPLFree(pszBuffer);
4298 :
4299 5 : return err;
4300 : }
4301 :
4302 : /************************************************************************/
4303 : /* OSRSetFromUserInput() */
4304 : /************************************************************************/
4305 :
4306 : /**
4307 : * \brief Set spatial reference from various text formats.
4308 : *
4309 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4310 : *
4311 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4312 : */
4313 265 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4314 : const char *pszDef)
4315 :
4316 : {
4317 265 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4318 :
4319 265 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4320 : }
4321 :
4322 : /************************************************************************/
4323 : /* OSRSetFromUserInputEx() */
4324 : /************************************************************************/
4325 :
4326 : /**
4327 : * \brief Set spatial reference from various text formats.
4328 : *
4329 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4330 : *
4331 : * @since GDAL 3.9
4332 : */
4333 720 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4334 : CSLConstList papszOptions)
4335 :
4336 : {
4337 720 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4338 :
4339 720 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4340 : }
4341 :
4342 : /************************************************************************/
4343 : /* ImportFromUrl() */
4344 : /************************************************************************/
4345 :
4346 : /**
4347 : * \brief Set spatial reference from a URL.
4348 : *
4349 : * This method will download the spatial reference at a given URL and
4350 : * feed it into SetFromUserInput for you.
4351 : *
4352 : * This method does the same thing as the OSRImportFromUrl() function.
4353 : *
4354 : * @param pszUrl text definition to try to deduce SRS from.
4355 : *
4356 : * @return OGRERR_NONE on success, or an error code with the curl
4357 : * error message if it is unable to download data.
4358 : */
4359 :
4360 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4361 :
4362 : {
4363 10 : TAKE_OPTIONAL_LOCK();
4364 :
4365 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4366 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4367 : {
4368 2 : CPLError(CE_Failure, CPLE_AppDefined,
4369 : "The given string is not recognized as a URL"
4370 : "starting with 'http://' -- %s",
4371 : pszUrl);
4372 2 : return OGRERR_FAILURE;
4373 : }
4374 :
4375 : /* -------------------------------------------------------------------- */
4376 : /* Fetch the result. */
4377 : /* -------------------------------------------------------------------- */
4378 3 : CPLErrorReset();
4379 :
4380 6 : std::string osUrl(pszUrl);
4381 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4382 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4383 : // to query WKT. To allow a static server to be used, rather append a
4384 : // "ogcwkt/" suffix.
4385 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4386 5 : "http://spatialreference.org/ref/"})
4387 : {
4388 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4389 : {
4390 : const CPLStringList aosTokens(
4391 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4392 3 : if (aosTokens.size() == 2)
4393 : {
4394 2 : osUrl = "https://spatialreference.org/ref/";
4395 2 : osUrl += aosTokens[0]; // authority
4396 2 : osUrl += '/';
4397 2 : osUrl += aosTokens[1]; // code
4398 2 : osUrl += "/ogcwkt/";
4399 : }
4400 3 : break;
4401 : }
4402 : }
4403 :
4404 3 : const char *pszTimeout = "TIMEOUT=10";
4405 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4406 :
4407 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4408 :
4409 : /* -------------------------------------------------------------------- */
4410 : /* Try to handle errors. */
4411 : /* -------------------------------------------------------------------- */
4412 :
4413 3 : if (psResult == nullptr)
4414 0 : return OGRERR_FAILURE;
4415 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4416 3 : psResult->pabyData == nullptr)
4417 : {
4418 0 : if (CPLGetLastErrorNo() == 0)
4419 : {
4420 0 : CPLError(CE_Failure, CPLE_AppDefined,
4421 : "No data was returned from the given URL");
4422 : }
4423 0 : CPLHTTPDestroyResult(psResult);
4424 0 : return OGRERR_FAILURE;
4425 : }
4426 :
4427 3 : if (psResult->nStatus != 0)
4428 : {
4429 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4430 : psResult->nStatus, psResult->pszErrBuf);
4431 0 : CPLHTTPDestroyResult(psResult);
4432 0 : return OGRERR_FAILURE;
4433 : }
4434 :
4435 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4436 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4437 3 : STARTS_WITH_CI(pszData, "https://"))
4438 : {
4439 0 : CPLError(CE_Failure, CPLE_AppDefined,
4440 : "The data that was downloaded also starts with 'http://' "
4441 : "and cannot be passed into SetFromUserInput. Is this "
4442 : "really a spatial reference definition? ");
4443 0 : CPLHTTPDestroyResult(psResult);
4444 0 : return OGRERR_FAILURE;
4445 : }
4446 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4447 : {
4448 0 : CPLHTTPDestroyResult(psResult);
4449 0 : return OGRERR_FAILURE;
4450 : }
4451 :
4452 3 : CPLHTTPDestroyResult(psResult);
4453 3 : return OGRERR_NONE;
4454 : }
4455 :
4456 : /************************************************************************/
4457 : /* OSRimportFromUrl() */
4458 : /************************************************************************/
4459 :
4460 : /**
4461 : * \brief Set spatial reference from a URL.
4462 : *
4463 : * This function is the same as OGRSpatialReference::importFromUrl()
4464 : */
4465 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4466 :
4467 : {
4468 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4469 :
4470 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4471 : }
4472 :
4473 : /************************************************************************/
4474 : /* importFromURNPart() */
4475 : /************************************************************************/
4476 867 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4477 : const char *pszCode,
4478 : const char *pszURN)
4479 : {
4480 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4481 : (void)this;
4482 : (void)pszAuthority;
4483 : (void)pszCode;
4484 : (void)pszURN;
4485 : return OGRERR_FAILURE;
4486 : #else
4487 : /* -------------------------------------------------------------------- */
4488 : /* Is this an EPSG code? Note that we import it with EPSG */
4489 : /* preferred axis ordering for geographic coordinate systems. */
4490 : /* -------------------------------------------------------------------- */
4491 867 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4492 788 : return importFromEPSGA(atoi(pszCode));
4493 :
4494 : /* -------------------------------------------------------------------- */
4495 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4496 : /* -------------------------------------------------------------------- */
4497 79 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4498 0 : return importFromDict("IAU2000.wkt", pszCode);
4499 :
4500 : /* -------------------------------------------------------------------- */
4501 : /* Is this an OGC code? */
4502 : /* -------------------------------------------------------------------- */
4503 79 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4504 : {
4505 1 : CPLError(CE_Failure, CPLE_AppDefined,
4506 : "URN %s has unrecognized authority.", pszURN);
4507 1 : return OGRERR_FAILURE;
4508 : }
4509 :
4510 78 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4511 68 : return SetWellKnownGeogCS(pszCode);
4512 10 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4513 0 : return SetWellKnownGeogCS(pszCode);
4514 10 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4515 0 : return SetWellKnownGeogCS(pszCode);
4516 10 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4517 8 : return SetWellKnownGeogCS("CRS84");
4518 :
4519 : /* -------------------------------------------------------------------- */
4520 : /* Handle auto codes. We need to convert from format */
4521 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4522 : /* -------------------------------------------------------------------- */
4523 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4524 : {
4525 2 : char szWMSAuto[100] = {'\0'};
4526 :
4527 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4528 0 : return OGRERR_FAILURE;
4529 :
4530 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4531 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4532 : {
4533 26 : if (szWMSAuto[i] == ':')
4534 4 : szWMSAuto[i] = ',';
4535 : }
4536 :
4537 2 : return importFromWMSAUTO(szWMSAuto);
4538 : }
4539 :
4540 : /* -------------------------------------------------------------------- */
4541 : /* Not a recognise OGC item. */
4542 : /* -------------------------------------------------------------------- */
4543 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4544 : pszURN);
4545 :
4546 0 : return OGRERR_FAILURE;
4547 : #endif
4548 : }
4549 :
4550 : /************************************************************************/
4551 : /* importFromURN() */
4552 : /* */
4553 : /* See OGC recommendation paper 06-023r1 or later for details. */
4554 : /************************************************************************/
4555 :
4556 : /**
4557 : * \brief Initialize from OGC URN.
4558 : *
4559 : * Initializes this spatial reference from a coordinate system defined
4560 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4561 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4562 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4563 : *
4564 : * This method is also support through SetFromUserInput() which can
4565 : * normally be used for URNs.
4566 : *
4567 : * @param pszURN the urn string.
4568 : *
4569 : * @return OGRERR_NONE on success or an error code.
4570 : */
4571 :
4572 788 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4573 :
4574 : {
4575 788 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4576 1492 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4577 704 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4578 : CPL_VALUE_INTEGER)
4579 : {
4580 702 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4581 : }
4582 :
4583 172 : TAKE_OPTIONAL_LOCK();
4584 :
4585 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4586 :
4587 : // PROJ 8.2.0 has support for IAU codes now.
4588 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4589 : /* -------------------------------------------------------------------- */
4590 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4591 : /* -------------------------------------------------------------------- */
4592 : const char *pszIAU = strstr(pszURN, "IAU");
4593 : if (pszIAU)
4594 : {
4595 : const char *pszCode = strchr(pszIAU, ':');
4596 : if (pszCode)
4597 : {
4598 : ++pszCode;
4599 : if (*pszCode == ':')
4600 : ++pszCode;
4601 : return importFromDict("IAU2000.wkt", pszCode);
4602 : }
4603 : }
4604 : #endif
4605 :
4606 : if (strlen(pszURN) >= 1000)
4607 : {
4608 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4609 : return OGRERR_CORRUPT_DATA;
4610 : }
4611 : auto obj = proj_create(d->getPROJContext(), pszURN);
4612 : if (!obj)
4613 : {
4614 : return OGRERR_FAILURE;
4615 : }
4616 : Clear();
4617 : d->setPjCRS(obj);
4618 : return OGRERR_NONE;
4619 : #else
4620 86 : const char *pszCur = nullptr;
4621 :
4622 86 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4623 21 : pszCur = pszURN + 16;
4624 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4625 1 : pszCur = pszURN + 20;
4626 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4627 62 : pszCur = pszURN + 18;
4628 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4629 0 : pszCur = pszURN + 16;
4630 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4631 0 : pszCur = pszURN + 20;
4632 : else
4633 : {
4634 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4635 : pszURN);
4636 2 : return OGRERR_FAILURE;
4637 : }
4638 :
4639 : /* -------------------------------------------------------------------- */
4640 : /* Clear any existing definition. */
4641 : /* -------------------------------------------------------------------- */
4642 84 : Clear();
4643 :
4644 : /* -------------------------------------------------------------------- */
4645 : /* Find code (ignoring version) out of string like: */
4646 : /* */
4647 : /* authority:[version]:code */
4648 : /* -------------------------------------------------------------------- */
4649 84 : const char *pszAuthority = pszCur;
4650 :
4651 : // skip authority
4652 403 : while (*pszCur != ':' && *pszCur)
4653 319 : pszCur++;
4654 84 : if (*pszCur == ':')
4655 84 : pszCur++;
4656 :
4657 : // skip version
4658 84 : const char *pszBeforeVersion = pszCur;
4659 380 : while (*pszCur != ':' && *pszCur)
4660 296 : pszCur++;
4661 84 : if (*pszCur == ':')
4662 56 : pszCur++;
4663 : else
4664 : // We come here in the case, the content to parse is authority:code
4665 : // (instead of authority::code) which is probably illegal according to
4666 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4667 : // for example in what is returned by GeoServer.
4668 28 : pszCur = pszBeforeVersion;
4669 :
4670 84 : const char *pszCode = pszCur;
4671 :
4672 84 : const char *pszComma = strchr(pszCur, ',');
4673 84 : if (pszComma == nullptr)
4674 83 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4675 :
4676 : // There's a second part with the vertical SRS.
4677 1 : pszCur = pszComma + 1;
4678 1 : if (!STARTS_WITH(pszCur, "crs:"))
4679 : {
4680 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4681 : pszURN);
4682 0 : return OGRERR_FAILURE;
4683 : }
4684 :
4685 1 : pszCur += 4;
4686 :
4687 1 : char *pszFirstCode = CPLStrdup(pszCode);
4688 1 : pszFirstCode[pszComma - pszCode] = '\0';
4689 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4690 1 : CPLFree(pszFirstCode);
4691 :
4692 : // Do we want to turn this into a compound definition
4693 : // with a vertical datum?
4694 1 : if (eStatus != OGRERR_NONE)
4695 0 : return eStatus;
4696 :
4697 : /* -------------------------------------------------------------------- */
4698 : /* Find code (ignoring version) out of string like: */
4699 : /* */
4700 : /* authority:[version]:code */
4701 : /* -------------------------------------------------------------------- */
4702 1 : pszAuthority = pszCur;
4703 :
4704 : // skip authority
4705 5 : while (*pszCur != ':' && *pszCur)
4706 4 : pszCur++;
4707 1 : if (*pszCur == ':')
4708 1 : pszCur++;
4709 :
4710 : // skip version
4711 1 : pszBeforeVersion = pszCur;
4712 1 : while (*pszCur != ':' && *pszCur)
4713 0 : pszCur++;
4714 1 : if (*pszCur == ':')
4715 1 : pszCur++;
4716 : else
4717 0 : pszCur = pszBeforeVersion;
4718 :
4719 1 : pszCode = pszCur;
4720 :
4721 2 : OGRSpatialReference oVertSRS;
4722 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4723 1 : if (eStatus == OGRERR_NONE)
4724 : {
4725 1 : OGRSpatialReference oHorizSRS(*this);
4726 :
4727 1 : Clear();
4728 :
4729 1 : oHorizSRS.d->refreshProjObj();
4730 1 : oVertSRS.d->refreshProjObj();
4731 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4732 0 : return OGRERR_FAILURE;
4733 :
4734 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4735 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4736 :
4737 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4738 1 : osName += " + ";
4739 1 : osName += pszVertName ? pszVertName : "";
4740 :
4741 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4742 : }
4743 :
4744 1 : return eStatus;
4745 : #endif
4746 : }
4747 :
4748 : /************************************************************************/
4749 : /* importFromCRSURL() */
4750 : /* */
4751 : /* See OGC Best Practice document 11-135 for details. */
4752 : /************************************************************************/
4753 :
4754 : /**
4755 : * \brief Initialize from OGC URL.
4756 : *
4757 : * Initializes this spatial reference from a coordinate system defined
4758 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4759 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4760 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4761 : *
4762 : * This method is also supported through SetFromUserInput() which can
4763 : * normally be used for URLs.
4764 : *
4765 : * @param pszURL the URL string.
4766 : *
4767 : * @return OGRERR_NONE on success or an error code.
4768 : */
4769 :
4770 783 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4771 :
4772 : {
4773 1566 : TAKE_OPTIONAL_LOCK();
4774 :
4775 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4776 : if (strlen(pszURL) >= 10000)
4777 : {
4778 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4779 : return OGRERR_CORRUPT_DATA;
4780 : }
4781 :
4782 : PJ *obj;
4783 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4784 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4785 : {
4786 : obj = proj_create(
4787 : d->getPROJContext(),
4788 : CPLSPrintf("IAU:%s",
4789 : pszURL +
4790 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4791 : }
4792 : else
4793 : #endif
4794 : {
4795 : obj = proj_create(d->getPROJContext(), pszURL);
4796 : }
4797 : if (!obj)
4798 : {
4799 : return OGRERR_FAILURE;
4800 : }
4801 : Clear();
4802 : d->setPjCRS(obj);
4803 : return OGRERR_NONE;
4804 : #else
4805 783 : const char *pszCur = nullptr;
4806 :
4807 783 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4808 2 : pszCur = pszURL + 26;
4809 781 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4810 1 : pszCur = pszURL + 27;
4811 780 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4812 779 : pszCur = pszURL + 30;
4813 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4814 1 : pszCur = pszURL + 31;
4815 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4816 0 : pszCur = pszURL + 23;
4817 : else
4818 : {
4819 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4820 : pszURL);
4821 0 : return OGRERR_FAILURE;
4822 : }
4823 :
4824 783 : if (*pszCur == '\0')
4825 : {
4826 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4827 0 : return OGRERR_FAILURE;
4828 : }
4829 :
4830 : /* -------------------------------------------------------------------- */
4831 : /* Clear any existing definition. */
4832 : /* -------------------------------------------------------------------- */
4833 783 : Clear();
4834 :
4835 783 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4836 : {
4837 : /* --------------------------------------------------------------------
4838 : */
4839 : /* It's a compound CRS, of the form: */
4840 : /* */
4841 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4842 : /* --------------------------------------------------------------------
4843 : */
4844 1 : pszCur += 12;
4845 :
4846 : // Extract each component CRS URL.
4847 1 : int iComponentUrl = 2;
4848 :
4849 2 : CPLString osName = "";
4850 1 : Clear();
4851 :
4852 3 : while (iComponentUrl != -1)
4853 : {
4854 2 : char searchStr[15] = {};
4855 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4856 :
4857 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
4858 :
4859 : // Figure out the next component URL.
4860 2 : char *pszComponentUrl = nullptr;
4861 :
4862 2 : if (pszUrlEnd)
4863 : {
4864 1 : size_t nLen = pszUrlEnd - pszCur;
4865 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4866 1 : strncpy(pszComponentUrl, pszCur, nLen);
4867 1 : pszComponentUrl[nLen] = '\0';
4868 :
4869 1 : ++iComponentUrl;
4870 1 : pszCur += nLen + strlen(searchStr);
4871 : }
4872 : else
4873 : {
4874 1 : if (iComponentUrl == 2)
4875 : {
4876 0 : CPLError(CE_Failure, CPLE_AppDefined,
4877 : "Compound CRS URLs must have at least two "
4878 : "component CRSs.");
4879 0 : return OGRERR_FAILURE;
4880 : }
4881 : else
4882 : {
4883 1 : pszComponentUrl = CPLStrdup(pszCur);
4884 : // no more components
4885 1 : iComponentUrl = -1;
4886 : }
4887 : }
4888 :
4889 2 : OGRSpatialReference oComponentSRS;
4890 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4891 :
4892 2 : CPLFree(pszComponentUrl);
4893 2 : pszComponentUrl = nullptr;
4894 :
4895 2 : if (eStatus == OGRERR_NONE)
4896 : {
4897 2 : if (osName.length() != 0)
4898 : {
4899 1 : osName += " + ";
4900 : }
4901 2 : osName += oComponentSRS.GetRoot()->GetValue();
4902 2 : SetNode("COMPD_CS", osName);
4903 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4904 : }
4905 : else
4906 0 : return eStatus;
4907 : }
4908 :
4909 1 : return OGRERR_NONE;
4910 : }
4911 :
4912 : /* -------------------------------------------------------------------- */
4913 : /* It's a normal CRS URL, of the form: */
4914 : /* */
4915 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
4916 : /* -------------------------------------------------------------------- */
4917 782 : ++pszCur;
4918 782 : const char *pszAuthority = pszCur;
4919 :
4920 : // skip authority
4921 103845 : while (*pszCur != '/' && *pszCur)
4922 103063 : pszCur++;
4923 782 : if (*pszCur == '/')
4924 781 : pszCur++;
4925 :
4926 : // skip version
4927 1685 : while (*pszCur != '/' && *pszCur)
4928 903 : pszCur++;
4929 782 : if (*pszCur == '/')
4930 781 : pszCur++;
4931 :
4932 782 : const char *pszCode = pszCur;
4933 :
4934 782 : return importFromURNPart(pszAuthority, pszCode, pszURL);
4935 : #endif
4936 : }
4937 :
4938 : /************************************************************************/
4939 : /* importFromWMSAUTO() */
4940 : /************************************************************************/
4941 :
4942 : /**
4943 : * \brief Initialize from WMSAUTO string.
4944 : *
4945 : * Note that the WMS 1.3 specification does not include the
4946 : * units code, while apparently earlier specs do. We try to
4947 : * guess around this.
4948 : *
4949 : * @param pszDefinition the WMSAUTO string
4950 : *
4951 : * @return OGRERR_NONE on success or an error code.
4952 : */
4953 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4954 :
4955 : {
4956 6 : TAKE_OPTIONAL_LOCK();
4957 :
4958 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4959 : if (strlen(pszDefinition) >= 10000)
4960 : {
4961 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4962 : return OGRERR_CORRUPT_DATA;
4963 : }
4964 :
4965 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
4966 : if (!obj)
4967 : {
4968 : return OGRERR_FAILURE;
4969 : }
4970 : Clear();
4971 : d->setPjCRS(obj);
4972 : return OGRERR_NONE;
4973 : #else
4974 : int nProjId, nUnitsId;
4975 3 : double dfRefLong, dfRefLat = 0.0;
4976 :
4977 : /* -------------------------------------------------------------------- */
4978 : /* Tokenize */
4979 : /* -------------------------------------------------------------------- */
4980 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4981 3 : pszDefinition += 5;
4982 :
4983 : char **papszTokens =
4984 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
4985 :
4986 3 : if (CSLCount(papszTokens) == 4)
4987 : {
4988 0 : nProjId = atoi(papszTokens[0]);
4989 0 : nUnitsId = atoi(papszTokens[1]);
4990 0 : dfRefLong = CPLAtof(papszTokens[2]);
4991 0 : dfRefLat = CPLAtof(papszTokens[3]);
4992 : }
4993 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
4994 : {
4995 0 : nProjId = atoi(papszTokens[0]);
4996 0 : nUnitsId = atoi(papszTokens[1]);
4997 0 : dfRefLong = CPLAtof(papszTokens[2]);
4998 0 : dfRefLat = 0.0;
4999 : }
5000 3 : else if (CSLCount(papszTokens) == 3)
5001 : {
5002 2 : nProjId = atoi(papszTokens[0]);
5003 2 : nUnitsId = 9001;
5004 2 : dfRefLong = CPLAtof(papszTokens[1]);
5005 2 : dfRefLat = CPLAtof(papszTokens[2]);
5006 : }
5007 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5008 : {
5009 0 : nProjId = atoi(papszTokens[0]);
5010 0 : nUnitsId = 9001;
5011 0 : dfRefLong = CPLAtof(papszTokens[1]);
5012 : }
5013 : else
5014 : {
5015 1 : CSLDestroy(papszTokens);
5016 1 : CPLError(CE_Failure, CPLE_AppDefined,
5017 : "AUTO projection has wrong number of arguments, expected\n"
5018 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5019 : "AUTO:proj_id,ref_long,ref_lat");
5020 1 : return OGRERR_FAILURE;
5021 : }
5022 :
5023 2 : CSLDestroy(papszTokens);
5024 2 : papszTokens = nullptr;
5025 :
5026 : /* -------------------------------------------------------------------- */
5027 : /* Build coordsys. */
5028 : /* -------------------------------------------------------------------- */
5029 2 : Clear();
5030 :
5031 : /* -------------------------------------------------------------------- */
5032 : /* Set WGS84. */
5033 : /* -------------------------------------------------------------------- */
5034 2 : SetWellKnownGeogCS("WGS84");
5035 :
5036 2 : switch (nProjId)
5037 : {
5038 2 : case 42001: // Auto UTM
5039 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5040 : dfRefLat >= 0.0);
5041 2 : break;
5042 :
5043 0 : case 42002: // Auto TM (strangely very UTM-like).
5044 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5045 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5046 0 : break;
5047 :
5048 0 : case 42003: // Auto Orthographic.
5049 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5050 0 : break;
5051 :
5052 0 : case 42004: // Auto Equirectangular
5053 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5054 0 : break;
5055 :
5056 0 : case 42005:
5057 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5058 0 : break;
5059 :
5060 0 : default:
5061 0 : CPLError(CE_Failure, CPLE_AppDefined,
5062 : "Unsupported projection id in importFromWMSAUTO(): %d",
5063 : nProjId);
5064 0 : return OGRERR_FAILURE;
5065 : }
5066 :
5067 : /* -------------------------------------------------------------------- */
5068 : /* Set units. */
5069 : /* -------------------------------------------------------------------- */
5070 :
5071 2 : switch (nUnitsId)
5072 : {
5073 2 : case 9001:
5074 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5075 2 : break;
5076 :
5077 0 : case 9002:
5078 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5079 0 : break;
5080 :
5081 0 : case 9003:
5082 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5083 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5084 0 : break;
5085 :
5086 0 : default:
5087 0 : CPLError(CE_Failure, CPLE_AppDefined,
5088 : "Unsupported units code (%d).", nUnitsId);
5089 0 : return OGRERR_FAILURE;
5090 : break;
5091 : }
5092 :
5093 2 : return OGRERR_NONE;
5094 : #endif
5095 : }
5096 :
5097 : /************************************************************************/
5098 : /* GetSemiMajor() */
5099 : /************************************************************************/
5100 :
5101 : /**
5102 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5103 : *
5104 : * This method does the same thing as the C function OSRGetSemiMajor().
5105 : *
5106 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5107 : * can be found.
5108 : *
5109 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5110 : */
5111 :
5112 5884 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5113 :
5114 : {
5115 11768 : TAKE_OPTIONAL_LOCK();
5116 :
5117 5884 : if (pnErr != nullptr)
5118 3166 : *pnErr = OGRERR_FAILURE;
5119 :
5120 5884 : d->refreshProjObj();
5121 5884 : if (!d->m_pj_crs)
5122 111 : return SRS_WGS84_SEMIMAJOR;
5123 :
5124 5773 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5125 5773 : if (!ellps)
5126 5 : return SRS_WGS84_SEMIMAJOR;
5127 :
5128 5768 : double dfSemiMajor = 0.0;
5129 5768 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5130 : nullptr, nullptr, nullptr);
5131 5768 : proj_destroy(ellps);
5132 :
5133 5768 : if (dfSemiMajor > 0)
5134 : {
5135 5768 : if (pnErr != nullptr)
5136 3052 : *pnErr = OGRERR_NONE;
5137 5768 : return dfSemiMajor;
5138 : }
5139 :
5140 0 : return SRS_WGS84_SEMIMAJOR;
5141 : }
5142 :
5143 : /************************************************************************/
5144 : /* OSRGetSemiMajor() */
5145 : /************************************************************************/
5146 :
5147 : /**
5148 : * \brief Get spheroid semi major axis.
5149 : *
5150 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5151 : */
5152 67 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5153 :
5154 : {
5155 67 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5156 :
5157 67 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5158 : }
5159 :
5160 : /************************************************************************/
5161 : /* GetInvFlattening() */
5162 : /************************************************************************/
5163 :
5164 : /**
5165 : * \brief Get spheroid inverse flattening.
5166 : *
5167 : * This method does the same thing as the C function OSRGetInvFlattening().
5168 : *
5169 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5170 : * can be found.
5171 : *
5172 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5173 : */
5174 :
5175 4137 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5176 :
5177 : {
5178 8274 : TAKE_OPTIONAL_LOCK();
5179 :
5180 4137 : if (pnErr != nullptr)
5181 3084 : *pnErr = OGRERR_FAILURE;
5182 :
5183 4137 : d->refreshProjObj();
5184 4137 : if (!d->m_pj_crs)
5185 111 : return SRS_WGS84_INVFLATTENING;
5186 :
5187 4026 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5188 4026 : if (!ellps)
5189 2 : return SRS_WGS84_INVFLATTENING;
5190 :
5191 4024 : double dfInvFlattening = -1.0;
5192 4024 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5193 : nullptr, &dfInvFlattening);
5194 4024 : proj_destroy(ellps);
5195 :
5196 4024 : if (dfInvFlattening >= 0.0)
5197 : {
5198 4024 : if (pnErr != nullptr)
5199 2973 : *pnErr = OGRERR_NONE;
5200 4024 : return dfInvFlattening;
5201 : }
5202 :
5203 0 : return SRS_WGS84_INVFLATTENING;
5204 : }
5205 :
5206 : /************************************************************************/
5207 : /* OSRGetInvFlattening() */
5208 : /************************************************************************/
5209 :
5210 : /**
5211 : * \brief Get spheroid inverse flattening.
5212 : *
5213 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5214 : */
5215 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5216 :
5217 : {
5218 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5219 :
5220 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5221 : }
5222 :
5223 : /************************************************************************/
5224 : /* GetEccentricity() */
5225 : /************************************************************************/
5226 :
5227 : /**
5228 : * \brief Get spheroid eccentricity
5229 : *
5230 : * @return eccentricity (or -1 in case of error)
5231 : * @since GDAL 2.3
5232 : */
5233 :
5234 0 : double OGRSpatialReference::GetEccentricity() const
5235 :
5236 : {
5237 0 : OGRErr eErr = OGRERR_NONE;
5238 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5239 0 : if (eErr != OGRERR_NONE)
5240 : {
5241 0 : return -1.0;
5242 : }
5243 0 : if (dfInvFlattening == 0.0)
5244 0 : return 0.0;
5245 0 : if (dfInvFlattening < 0.5)
5246 0 : return -1.0;
5247 0 : return sqrt(2.0 / dfInvFlattening -
5248 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5249 : }
5250 :
5251 : /************************************************************************/
5252 : /* GetSquaredEccentricity() */
5253 : /************************************************************************/
5254 :
5255 : /**
5256 : * \brief Get spheroid squared eccentricity
5257 : *
5258 : * @return squared eccentricity (or -1 in case of error)
5259 : * @since GDAL 2.3
5260 : */
5261 :
5262 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5263 :
5264 : {
5265 0 : OGRErr eErr = OGRERR_NONE;
5266 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5267 0 : if (eErr != OGRERR_NONE)
5268 : {
5269 0 : return -1.0;
5270 : }
5271 0 : if (dfInvFlattening == 0.0)
5272 0 : return 0.0;
5273 0 : if (dfInvFlattening < 0.5)
5274 0 : return -1.0;
5275 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5276 : }
5277 :
5278 : /************************************************************************/
5279 : /* GetSemiMinor() */
5280 : /************************************************************************/
5281 :
5282 : /**
5283 : * \brief Get spheroid semi minor axis.
5284 : *
5285 : * This method does the same thing as the C function OSRGetSemiMinor().
5286 : *
5287 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5288 : * can be found.
5289 : *
5290 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5291 : */
5292 :
5293 637 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5294 :
5295 : {
5296 637 : const double dfSemiMajor = GetSemiMajor(pnErr);
5297 637 : const double dfInvFlattening = GetInvFlattening(pnErr);
5298 :
5299 637 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5300 : }
5301 :
5302 : /************************************************************************/
5303 : /* OSRGetSemiMinor() */
5304 : /************************************************************************/
5305 :
5306 : /**
5307 : * \brief Get spheroid semi minor axis.
5308 : *
5309 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5310 : */
5311 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5312 :
5313 : {
5314 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5315 :
5316 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5317 : }
5318 :
5319 : /************************************************************************/
5320 : /* SetLocalCS() */
5321 : /************************************************************************/
5322 :
5323 : /**
5324 : * \brief Set the user visible LOCAL_CS name.
5325 : *
5326 : * This method is the same as the C function OSRSetLocalCS().
5327 : *
5328 : * This method will ensure a LOCAL_CS node is created as the root,
5329 : * and set the provided name on it. It must be used before SetLinearUnits().
5330 : *
5331 : * @param pszName the user visible name to assign. Not used as a key.
5332 : *
5333 : * @return OGRERR_NONE on success.
5334 : */
5335 :
5336 2877 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5337 :
5338 : {
5339 5754 : TAKE_OPTIONAL_LOCK();
5340 :
5341 2877 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5342 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5343 : {
5344 2877 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5345 : }
5346 : else
5347 : {
5348 0 : CPLDebug("OGR",
5349 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5350 : "It appears an incompatible object already exists.",
5351 : pszName);
5352 0 : return OGRERR_FAILURE;
5353 : }
5354 :
5355 2877 : return OGRERR_NONE;
5356 : }
5357 :
5358 : /************************************************************************/
5359 : /* OSRSetLocalCS() */
5360 : /************************************************************************/
5361 :
5362 : /**
5363 : * \brief Set the user visible LOCAL_CS name.
5364 : *
5365 : * This function is the same as OGRSpatialReference::SetLocalCS()
5366 : */
5367 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5368 :
5369 : {
5370 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5371 :
5372 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5373 : }
5374 :
5375 : /************************************************************************/
5376 : /* SetGeocCS() */
5377 : /************************************************************************/
5378 :
5379 : /**
5380 : * \brief Set the user visible GEOCCS name.
5381 : *
5382 : * This method is the same as the C function OSRSetGeocCS().
5383 :
5384 : * This method will ensure a GEOCCS node is created as the root,
5385 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5386 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5387 : * the GEOGCS.
5388 : *
5389 : * @param pszName the user visible name to assign. Not used as a key.
5390 : *
5391 : * @return OGRERR_NONE on success.
5392 : *
5393 : * @since OGR 1.9.0
5394 : */
5395 :
5396 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5397 :
5398 : {
5399 12 : TAKE_OPTIONAL_LOCK();
5400 :
5401 6 : OGRErr eErr = OGRERR_NONE;
5402 6 : d->refreshProjObj();
5403 6 : d->demoteFromBoundCRS();
5404 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5405 : {
5406 3 : d->setPjCRS(proj_create_geocentric_crs(
5407 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5408 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5409 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5410 : "Metre", 1.0));
5411 : }
5412 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5413 : {
5414 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5415 : }
5416 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5417 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5418 : {
5419 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5420 : #if PROJ_VERSION_MAJOR > 7 || \
5421 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5422 : if (datum == nullptr)
5423 : {
5424 : datum =
5425 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5426 : }
5427 : #endif
5428 1 : if (datum == nullptr)
5429 : {
5430 0 : d->undoDemoteFromBoundCRS();
5431 0 : return OGRERR_FAILURE;
5432 : }
5433 :
5434 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5435 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5436 : 0.0);
5437 1 : d->setPjCRS(pj_crs);
5438 :
5439 1 : proj_destroy(datum);
5440 : }
5441 : else
5442 : {
5443 1 : CPLDebug("OGR",
5444 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5445 : "It appears an incompatible object already exists.",
5446 : pszName);
5447 1 : eErr = OGRERR_FAILURE;
5448 : }
5449 6 : d->undoDemoteFromBoundCRS();
5450 :
5451 6 : return eErr;
5452 : }
5453 :
5454 : /************************************************************************/
5455 : /* OSRSetGeocCS() */
5456 : /************************************************************************/
5457 :
5458 : /**
5459 : * \brief Set the user visible PROJCS name.
5460 : *
5461 : * This function is the same as OGRSpatialReference::SetGeocCS()
5462 : *
5463 : * @since OGR 1.9.0
5464 : */
5465 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5466 :
5467 : {
5468 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5469 :
5470 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5471 : }
5472 :
5473 : /************************************************************************/
5474 : /* SetVertCS() */
5475 : /************************************************************************/
5476 :
5477 : /**
5478 : * \brief Set the user visible VERT_CS name.
5479 : *
5480 : * This method is the same as the C function OSRSetVertCS().
5481 :
5482 : * This method will ensure a VERT_CS node is created if needed. If the
5483 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5484 : * turned into a COMPD_CS.
5485 : *
5486 : * @param pszVertCSName the user visible name of the vertical coordinate
5487 : * system. Not used as a key.
5488 : *
5489 : * @param pszVertDatumName the user visible name of the vertical datum. It
5490 : * is helpful if this matches the EPSG name.
5491 : *
5492 : * @param nVertDatumType the OGC vertical datum type. Ignored
5493 : *
5494 : * @return OGRERR_NONE on success.
5495 : *
5496 : * @since OGR 1.9.0
5497 : */
5498 :
5499 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5500 : const char *pszVertDatumName,
5501 : int nVertDatumType)
5502 :
5503 : {
5504 1 : TAKE_OPTIONAL_LOCK();
5505 :
5506 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5507 :
5508 1 : d->refreshProjObj();
5509 :
5510 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5511 : pszVertDatumName, nullptr, 0.0);
5512 :
5513 : /* -------------------------------------------------------------------- */
5514 : /* Handle the case where we want to make a compound coordinate */
5515 : /* system. */
5516 : /* -------------------------------------------------------------------- */
5517 1 : if (IsProjected() || IsGeographic())
5518 : {
5519 1 : auto compoundCRS = proj_create_compound_crs(
5520 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5521 1 : proj_destroy(vertCRS);
5522 1 : d->setPjCRS(compoundCRS);
5523 : }
5524 : else
5525 : {
5526 0 : d->setPjCRS(vertCRS);
5527 : }
5528 2 : return OGRERR_NONE;
5529 : }
5530 :
5531 : /************************************************************************/
5532 : /* OSRSetVertCS() */
5533 : /************************************************************************/
5534 :
5535 : /**
5536 : * \brief Setup the vertical coordinate system.
5537 : *
5538 : * This function is the same as OGRSpatialReference::SetVertCS()
5539 : *
5540 : * @since OGR 1.9.0
5541 : */
5542 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5543 : const char *pszVertDatumName, int nVertDatumType)
5544 :
5545 : {
5546 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5547 :
5548 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5549 0 : nVertDatumType);
5550 : }
5551 :
5552 : /************************************************************************/
5553 : /* SetCompoundCS() */
5554 : /************************************************************************/
5555 :
5556 : /**
5557 : * \brief Setup a compound coordinate system.
5558 : *
5559 : * This method is the same as the C function OSRSetCompoundCS().
5560 :
5561 : * This method is replace the current SRS with a COMPD_CS coordinate system
5562 : * consisting of the passed in horizontal and vertical coordinate systems.
5563 : *
5564 : * @param pszName the name of the compound coordinate system.
5565 : *
5566 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5567 : *
5568 : * @param poVertSRS the vertical SRS (VERT_CS).
5569 : *
5570 : * @return OGRERR_NONE on success.
5571 : */
5572 :
5573 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5574 : const OGRSpatialReference *poHorizSRS,
5575 : const OGRSpatialReference *poVertSRS)
5576 :
5577 : {
5578 184 : TAKE_OPTIONAL_LOCK();
5579 :
5580 : /* -------------------------------------------------------------------- */
5581 : /* Verify these are legal horizontal and vertical coordinate */
5582 : /* systems. */
5583 : /* -------------------------------------------------------------------- */
5584 92 : if (!poVertSRS->IsVertical())
5585 : {
5586 0 : CPLError(CE_Failure, CPLE_AppDefined,
5587 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5588 0 : return OGRERR_FAILURE;
5589 : }
5590 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5591 : {
5592 0 : CPLError(CE_Failure, CPLE_AppDefined,
5593 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5594 : "GEOGCS.");
5595 0 : return OGRERR_FAILURE;
5596 : }
5597 :
5598 : /* -------------------------------------------------------------------- */
5599 : /* Replace with compound srs. */
5600 : /* -------------------------------------------------------------------- */
5601 92 : Clear();
5602 :
5603 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5604 92 : poHorizSRS->d->m_pj_crs,
5605 92 : poVertSRS->d->m_pj_crs);
5606 92 : d->setPjCRS(compoundCRS);
5607 :
5608 92 : return OGRERR_NONE;
5609 : }
5610 :
5611 : /************************************************************************/
5612 : /* OSRSetCompoundCS() */
5613 : /************************************************************************/
5614 :
5615 : /**
5616 : * \brief Setup a compound coordinate system.
5617 : *
5618 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5619 : */
5620 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5621 : OGRSpatialReferenceH hHorizSRS,
5622 : OGRSpatialReferenceH hVertSRS)
5623 :
5624 : {
5625 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5626 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5627 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5628 :
5629 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5630 16 : ToPointer(hVertSRS));
5631 : }
5632 :
5633 : /************************************************************************/
5634 : /* SetProjCS() */
5635 : /************************************************************************/
5636 :
5637 : /**
5638 : * \brief Set the user visible PROJCS name.
5639 : *
5640 : * This method is the same as the C function OSRSetProjCS().
5641 : *
5642 : * This method will ensure a PROJCS node is created as the root,
5643 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5644 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5645 : *
5646 : * @param pszName the user visible name to assign. Not used as a key.
5647 : *
5648 : * @return OGRERR_NONE on success.
5649 : */
5650 :
5651 4025 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5652 :
5653 : {
5654 4025 : TAKE_OPTIONAL_LOCK();
5655 :
5656 4025 : d->refreshProjObj();
5657 4025 : d->demoteFromBoundCRS();
5658 4025 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5659 : {
5660 476 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5661 : }
5662 : else
5663 : {
5664 3549 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5665 : nullptr, nullptr, nullptr,
5666 : nullptr, nullptr, 0, nullptr);
5667 3549 : auto cs = proj_create_cartesian_2D_cs(
5668 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5669 :
5670 3549 : auto projCRS = proj_create_projected_crs(
5671 3549 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5672 3549 : proj_destroy(dummyConv);
5673 3549 : proj_destroy(cs);
5674 :
5675 3549 : d->setPjCRS(projCRS);
5676 : }
5677 4025 : d->undoDemoteFromBoundCRS();
5678 8050 : return OGRERR_NONE;
5679 : }
5680 :
5681 : /************************************************************************/
5682 : /* OSRSetProjCS() */
5683 : /************************************************************************/
5684 :
5685 : /**
5686 : * \brief Set the user visible PROJCS name.
5687 : *
5688 : * This function is the same as OGRSpatialReference::SetProjCS()
5689 : */
5690 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5691 :
5692 : {
5693 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5694 :
5695 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5696 : }
5697 :
5698 : /************************************************************************/
5699 : /* SetProjection() */
5700 : /************************************************************************/
5701 :
5702 : /**
5703 : * \brief Set a projection name.
5704 : *
5705 : * This method is the same as the C function OSRSetProjection().
5706 : *
5707 : * @param pszProjection the projection name, which should be selected from
5708 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5709 : *
5710 : * @return OGRERR_NONE on success.
5711 : */
5712 :
5713 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5714 :
5715 : {
5716 46 : TAKE_OPTIONAL_LOCK();
5717 :
5718 23 : OGR_SRSNode *poGeogCS = nullptr;
5719 :
5720 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5721 : {
5722 4 : poGeogCS = d->m_poRoot;
5723 4 : d->m_poRoot = nullptr;
5724 : }
5725 :
5726 23 : if (!GetAttrNode("PROJCS"))
5727 : {
5728 11 : SetNode("PROJCS", "unnamed");
5729 : }
5730 :
5731 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5732 23 : if (eErr != OGRERR_NONE)
5733 0 : return eErr;
5734 :
5735 23 : if (poGeogCS != nullptr)
5736 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5737 :
5738 23 : return OGRERR_NONE;
5739 : }
5740 :
5741 : /************************************************************************/
5742 : /* OSRSetProjection() */
5743 : /************************************************************************/
5744 :
5745 : /**
5746 : * \brief Set a projection name.
5747 : *
5748 : * This function is the same as OGRSpatialReference::SetProjection()
5749 : */
5750 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5751 :
5752 : {
5753 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5754 :
5755 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5756 : }
5757 :
5758 : /************************************************************************/
5759 : /* GetWKT2ProjectionMethod() */
5760 : /************************************************************************/
5761 :
5762 : /**
5763 : * \brief Returns info on the projection method, based on WKT2 naming
5764 : * conventions.
5765 : *
5766 : * The returned strings are short lived and should be considered to be
5767 : * invalidated by any further call to the GDAL API.
5768 : *
5769 : * @param[out] ppszMethodName Pointer to a string that will receive the
5770 : * projection method name.
5771 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5772 : * receive the name of the authority that defines the projection method.
5773 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5774 : * an authority.
5775 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5776 : * receive the code that defines the projection method.
5777 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5778 : * an authority.
5779 : *
5780 : * @return OGRERR_NONE on success.
5781 : */
5782 : OGRErr
5783 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5784 : const char **ppszMethodAuthName,
5785 : const char **ppszMethodCode) const
5786 : {
5787 2 : TAKE_OPTIONAL_LOCK();
5788 :
5789 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5790 1 : if (!conv)
5791 0 : return OGRERR_FAILURE;
5792 1 : const char *pszTmpMethodName = "";
5793 1 : const char *pszTmpMethodAuthName = "";
5794 1 : const char *pszTmpMethodCode = "";
5795 1 : int ret = proj_coordoperation_get_method_info(
5796 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5797 : &pszTmpMethodCode);
5798 : // "Internalize" temporary strings returned by PROJ
5799 1 : CPLAssert(pszTmpMethodName);
5800 1 : if (ppszMethodName)
5801 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5802 1 : if (ppszMethodAuthName)
5803 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5804 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5805 0 : : nullptr;
5806 1 : if (ppszMethodCode)
5807 0 : *ppszMethodCode =
5808 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5809 1 : proj_destroy(conv);
5810 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5811 : }
5812 :
5813 : /************************************************************************/
5814 : /* SetProjParm() */
5815 : /************************************************************************/
5816 :
5817 : /**
5818 : * \brief Set a projection parameter value.
5819 : *
5820 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5821 : *
5822 : * This method is the same as the C function OSRSetProjParm().
5823 : *
5824 : * Please check https://gdal.org/proj_list pages for
5825 : * legal parameter names for specific projections.
5826 : *
5827 : *
5828 : * @param pszParamName the parameter name, which should be selected from
5829 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5830 : *
5831 : * @param dfValue value to assign.
5832 : *
5833 : * @return OGRERR_NONE on success.
5834 : */
5835 :
5836 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5837 : double dfValue)
5838 :
5839 : {
5840 258 : TAKE_OPTIONAL_LOCK();
5841 :
5842 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5843 :
5844 129 : if (poPROJCS == nullptr)
5845 3 : return OGRERR_FAILURE;
5846 :
5847 126 : char szValue[64] = {'\0'};
5848 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5849 :
5850 : /* -------------------------------------------------------------------- */
5851 : /* Try to find existing parameter with this name. */
5852 : /* -------------------------------------------------------------------- */
5853 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5854 : {
5855 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5856 :
5857 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5858 1242 : poParam->GetChildCount() == 2 &&
5859 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5860 : {
5861 39 : poParam->GetChild(1)->SetValue(szValue);
5862 39 : return OGRERR_NONE;
5863 : }
5864 : }
5865 :
5866 : /* -------------------------------------------------------------------- */
5867 : /* Otherwise create a new parameter and append. */
5868 : /* -------------------------------------------------------------------- */
5869 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5870 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
5871 87 : poParam->AddChild(new OGR_SRSNode(szValue));
5872 :
5873 87 : poPROJCS->AddChild(poParam);
5874 :
5875 87 : return OGRERR_NONE;
5876 : }
5877 :
5878 : /************************************************************************/
5879 : /* OSRSetProjParm() */
5880 : /************************************************************************/
5881 :
5882 : /**
5883 : * \brief Set a projection parameter value.
5884 : *
5885 : * This function is the same as OGRSpatialReference::SetProjParm()
5886 : */
5887 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5888 : double dfValue)
5889 :
5890 : {
5891 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5892 :
5893 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5894 : }
5895 :
5896 : /************************************************************************/
5897 : /* FindProjParm() */
5898 : /************************************************************************/
5899 :
5900 : /**
5901 : * \brief Return the child index of the named projection parameter on
5902 : * its parent PROJCS node.
5903 : *
5904 : * @param pszParameter projection parameter to look for
5905 : * @param poPROJCS projection CS node to look in. If NULL is passed,
5906 : * the PROJCS node of the SpatialReference object will be searched.
5907 : *
5908 : * @return the child index of the named projection parameter. -1 on failure
5909 : */
5910 4391 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
5911 : const OGR_SRSNode *poPROJCS) const
5912 :
5913 : {
5914 8782 : TAKE_OPTIONAL_LOCK();
5915 :
5916 4391 : if (poPROJCS == nullptr)
5917 0 : poPROJCS = GetAttrNode("PROJCS");
5918 :
5919 4391 : if (poPROJCS == nullptr)
5920 0 : return -1;
5921 :
5922 : /* -------------------------------------------------------------------- */
5923 : /* Search for requested parameter. */
5924 : /* -------------------------------------------------------------------- */
5925 4391 : bool bIsWKT2 = false;
5926 28028 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5927 : {
5928 27605 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5929 :
5930 27605 : if (poParameter->GetChildCount() >= 2)
5931 : {
5932 18864 : const char *pszValue = poParameter->GetValue();
5933 31680 : if (EQUAL(pszValue, "PARAMETER") &&
5934 12816 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5935 : pszParameter))
5936 : {
5937 3968 : return iChild;
5938 : }
5939 14896 : else if (EQUAL(pszValue, "METHOD"))
5940 : {
5941 41 : bIsWKT2 = true;
5942 : }
5943 : }
5944 : }
5945 :
5946 : /* -------------------------------------------------------------------- */
5947 : /* Try similar names, for selected parameters. */
5948 : /* -------------------------------------------------------------------- */
5949 423 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5950 : {
5951 190 : if (bIsWKT2)
5952 : {
5953 8 : int iChild = FindProjParm(
5954 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5955 8 : if (iChild == -1)
5956 3 : iChild = FindProjParm(
5957 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5958 8 : return iChild;
5959 : }
5960 182 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5961 : }
5962 :
5963 233 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5964 : {
5965 32 : if (bIsWKT2)
5966 : {
5967 9 : int iChild = FindProjParm(
5968 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5969 9 : if (iChild == -1)
5970 0 : iChild = FindProjParm(
5971 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5972 9 : return iChild;
5973 : }
5974 23 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5975 23 : if (iChild == -1)
5976 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5977 23 : return iChild;
5978 : }
5979 :
5980 201 : return -1;
5981 : }
5982 :
5983 : /************************************************************************/
5984 : /* GetProjParm() */
5985 : /************************************************************************/
5986 :
5987 : /**
5988 : * \brief Fetch a projection parameter value.
5989 : *
5990 : * NOTE: This code should be modified to translate non degree angles into
5991 : * degrees based on the GEOGCS unit. This has not yet been done.
5992 : *
5993 : * This method is the same as the C function OSRGetProjParm().
5994 : *
5995 : * @param pszName the name of the parameter to fetch, from the set of
5996 : * SRS_PP codes in ogr_srs_api.h.
5997 : *
5998 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
5999 : *
6000 : * @param pnErr place to put error code on failure. Ignored if NULL.
6001 : *
6002 : * @return value of parameter.
6003 : */
6004 :
6005 4416 : double OGRSpatialReference::GetProjParm(const char *pszName,
6006 : double dfDefaultValue,
6007 : OGRErr *pnErr) const
6008 :
6009 : {
6010 8832 : TAKE_OPTIONAL_LOCK();
6011 :
6012 4416 : d->refreshProjObj();
6013 4416 : GetRoot(); // force update of d->m_bNodesWKT2
6014 :
6015 4416 : if (pnErr != nullptr)
6016 3450 : *pnErr = OGRERR_NONE;
6017 :
6018 : /* -------------------------------------------------------------------- */
6019 : /* Find the desired parameter. */
6020 : /* -------------------------------------------------------------------- */
6021 : const OGR_SRSNode *poPROJCS =
6022 4416 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6023 4416 : if (poPROJCS == nullptr)
6024 : {
6025 250 : if (pnErr != nullptr)
6026 250 : *pnErr = OGRERR_FAILURE;
6027 250 : return dfDefaultValue;
6028 : }
6029 :
6030 4166 : const int iChild = FindProjParm(pszName, poPROJCS);
6031 4166 : if (iChild == -1)
6032 : {
6033 198 : if (IsProjected() && GetAxesCount() == 3)
6034 : {
6035 3 : OGRSpatialReference *poSRSTmp = Clone();
6036 3 : poSRSTmp->DemoteTo2D(nullptr);
6037 : const double dfRet =
6038 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6039 3 : delete poSRSTmp;
6040 3 : return dfRet;
6041 : }
6042 :
6043 195 : if (pnErr != nullptr)
6044 173 : *pnErr = OGRERR_FAILURE;
6045 195 : return dfDefaultValue;
6046 : }
6047 :
6048 3968 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6049 3968 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6050 : }
6051 :
6052 : /************************************************************************/
6053 : /* OSRGetProjParm() */
6054 : /************************************************************************/
6055 :
6056 : /**
6057 : * \brief Fetch a projection parameter value.
6058 : *
6059 : * This function is the same as OGRSpatialReference::GetProjParm()
6060 : */
6061 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6062 : double dfDefaultValue, OGRErr *pnErr)
6063 :
6064 : {
6065 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6066 :
6067 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6068 : }
6069 :
6070 : /************************************************************************/
6071 : /* GetNormProjParm() */
6072 : /************************************************************************/
6073 :
6074 : /**
6075 : * \brief Fetch a normalized projection parameter value.
6076 : *
6077 : * This method is the same as GetProjParm() except that the value of
6078 : * the parameter is "normalized" into degrees or meters depending on
6079 : * whether it is linear or angular.
6080 : *
6081 : * This method is the same as the C function OSRGetNormProjParm().
6082 : *
6083 : * @param pszName the name of the parameter to fetch, from the set of
6084 : * SRS_PP codes in ogr_srs_api.h.
6085 : *
6086 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6087 : *
6088 : * @param pnErr place to put error code on failure. Ignored if NULL.
6089 : *
6090 : * @return value of parameter.
6091 : */
6092 :
6093 3425 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6094 : double dfDefaultValue,
6095 : OGRErr *pnErr) const
6096 :
6097 : {
6098 6850 : TAKE_OPTIONAL_LOCK();
6099 :
6100 3425 : GetNormInfo();
6101 :
6102 3425 : OGRErr nError = OGRERR_NONE;
6103 3425 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6104 3425 : if (pnErr != nullptr)
6105 0 : *pnErr = nError;
6106 :
6107 : // If we got the default just return it unadjusted.
6108 3425 : if (nError != OGRERR_NONE)
6109 423 : return dfRawResult;
6110 :
6111 3002 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6112 8 : dfRawResult *= d->dfToDegrees;
6113 :
6114 3002 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6115 5 : return dfRawResult * d->dfToMeter;
6116 :
6117 2997 : return dfRawResult;
6118 : }
6119 :
6120 : /************************************************************************/
6121 : /* OSRGetNormProjParm() */
6122 : /************************************************************************/
6123 :
6124 : /**
6125 : * \brief This function is the same as OGRSpatialReference::
6126 : *
6127 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6128 : */
6129 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6130 : double dfDefaultValue, OGRErr *pnErr)
6131 :
6132 : {
6133 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6134 :
6135 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6136 : }
6137 :
6138 : /************************************************************************/
6139 : /* SetNormProjParm() */
6140 : /************************************************************************/
6141 :
6142 : /**
6143 : * \brief Set a projection parameter with a normalized value.
6144 : *
6145 : * This method is the same as SetProjParm() except that the value of
6146 : * the parameter passed in is assumed to be in "normalized" form (decimal
6147 : * degrees for angular values, meters for linear values. The values are
6148 : * converted in a form suitable for the GEOGCS and linear units in effect.
6149 : *
6150 : * This method is the same as the C function OSRSetNormProjParm().
6151 : *
6152 : * @param pszName the parameter name, which should be selected from
6153 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6154 : *
6155 : * @param dfValue value to assign.
6156 : *
6157 : * @return OGRERR_NONE on success.
6158 : */
6159 :
6160 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6161 :
6162 : {
6163 182 : TAKE_OPTIONAL_LOCK();
6164 :
6165 91 : GetNormInfo();
6166 :
6167 91 : if (d->dfToDegrees != 0.0 &&
6168 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6169 0 : IsAngularParameter(pszName))
6170 : {
6171 0 : dfValue /= d->dfToDegrees;
6172 : }
6173 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6174 4 : IsLinearParameter(pszName))
6175 4 : dfValue /= d->dfToMeter;
6176 :
6177 182 : return SetProjParm(pszName, dfValue);
6178 : }
6179 :
6180 : /************************************************************************/
6181 : /* OSRSetNormProjParm() */
6182 : /************************************************************************/
6183 :
6184 : /**
6185 : * \brief Set a projection parameter with a normalized value.
6186 : *
6187 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6188 : */
6189 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6190 : double dfValue)
6191 :
6192 : {
6193 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6194 :
6195 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6196 : }
6197 :
6198 : /************************************************************************/
6199 : /* SetTM() */
6200 : /************************************************************************/
6201 :
6202 428 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6203 : double dfScale, double dfFalseEasting,
6204 : double dfFalseNorthing)
6205 :
6206 : {
6207 856 : TAKE_OPTIONAL_LOCK();
6208 :
6209 428 : return d->replaceConversionAndUnref(
6210 : proj_create_conversion_transverse_mercator(
6211 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6212 856 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6213 : }
6214 :
6215 : /************************************************************************/
6216 : /* OSRSetTM() */
6217 : /************************************************************************/
6218 :
6219 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6220 : double dfCenterLong, double dfScale, double dfFalseEasting,
6221 : double dfFalseNorthing)
6222 :
6223 : {
6224 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6225 :
6226 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6227 1 : dfFalseEasting, dfFalseNorthing);
6228 : }
6229 :
6230 : /************************************************************************/
6231 : /* SetTMVariant() */
6232 : /************************************************************************/
6233 :
6234 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6235 : double dfCenterLat,
6236 : double dfCenterLong, double dfScale,
6237 : double dfFalseEasting,
6238 : double dfFalseNorthing)
6239 :
6240 : {
6241 0 : TAKE_OPTIONAL_LOCK();
6242 :
6243 0 : SetProjection(pszVariantName);
6244 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6245 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6246 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6247 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6248 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6249 :
6250 0 : return OGRERR_NONE;
6251 : }
6252 :
6253 : /************************************************************************/
6254 : /* OSRSetTMVariant() */
6255 : /************************************************************************/
6256 :
6257 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6258 : double dfCenterLat, double dfCenterLong, double dfScale,
6259 : double dfFalseEasting, double dfFalseNorthing)
6260 :
6261 : {
6262 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6263 :
6264 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6265 : dfCenterLong, dfScale, dfFalseEasting,
6266 0 : dfFalseNorthing);
6267 : }
6268 :
6269 : /************************************************************************/
6270 : /* SetTMSO() */
6271 : /************************************************************************/
6272 :
6273 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6274 : double dfScale, double dfFalseEasting,
6275 : double dfFalseNorthing)
6276 :
6277 : {
6278 6 : TAKE_OPTIONAL_LOCK();
6279 :
6280 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6281 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6282 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6283 :
6284 3 : const char *pszName = nullptr;
6285 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6286 3 : CPLString osName = pszName ? pszName : "";
6287 :
6288 3 : d->refreshProjObj();
6289 :
6290 3 : d->demoteFromBoundCRS();
6291 :
6292 3 : auto cs = proj_create_cartesian_2D_cs(
6293 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6294 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6295 : auto projCRS =
6296 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6297 3 : d->getGeodBaseCRS(), conv, cs);
6298 3 : proj_destroy(conv);
6299 3 : proj_destroy(cs);
6300 :
6301 3 : d->setPjCRS(projCRS);
6302 :
6303 3 : d->undoDemoteFromBoundCRS();
6304 :
6305 6 : return OGRERR_NONE;
6306 : }
6307 :
6308 : /************************************************************************/
6309 : /* OSRSetTMSO() */
6310 : /************************************************************************/
6311 :
6312 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6313 : double dfCenterLong, double dfScale, double dfFalseEasting,
6314 : double dfFalseNorthing)
6315 :
6316 : {
6317 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6318 :
6319 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6320 0 : dfFalseEasting, dfFalseNorthing);
6321 : }
6322 :
6323 : /************************************************************************/
6324 : /* SetTPED() */
6325 : /************************************************************************/
6326 :
6327 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6328 : double dfLat2, double dfLong2,
6329 : double dfFalseEasting,
6330 : double dfFalseNorthing)
6331 :
6332 : {
6333 2 : TAKE_OPTIONAL_LOCK();
6334 :
6335 1 : return d->replaceConversionAndUnref(
6336 : proj_create_conversion_two_point_equidistant(
6337 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6338 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6339 : }
6340 :
6341 : /************************************************************************/
6342 : /* OSRSetTPED() */
6343 : /************************************************************************/
6344 :
6345 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6346 : double dfLat2, double dfLong2, double dfFalseEasting,
6347 : double dfFalseNorthing)
6348 :
6349 : {
6350 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6351 :
6352 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6353 0 : dfFalseEasting, dfFalseNorthing);
6354 : }
6355 :
6356 : /************************************************************************/
6357 : /* SetTMG() */
6358 : /************************************************************************/
6359 :
6360 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6361 : double dfFalseEasting,
6362 : double dfFalseNorthing)
6363 :
6364 : {
6365 0 : TAKE_OPTIONAL_LOCK();
6366 :
6367 0 : return d->replaceConversionAndUnref(
6368 : proj_create_conversion_tunisia_mapping_grid(
6369 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6370 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6371 : }
6372 :
6373 : /************************************************************************/
6374 : /* OSRSetTMG() */
6375 : /************************************************************************/
6376 :
6377 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6378 : double dfCenterLong, double dfFalseEasting,
6379 : double dfFalseNorthing)
6380 :
6381 : {
6382 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6383 :
6384 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6385 0 : dfFalseNorthing);
6386 : }
6387 :
6388 : /************************************************************************/
6389 : /* SetACEA() */
6390 : /************************************************************************/
6391 :
6392 40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6393 : double dfCenterLat, double dfCenterLong,
6394 : double dfFalseEasting,
6395 : double dfFalseNorthing)
6396 :
6397 : {
6398 80 : TAKE_OPTIONAL_LOCK();
6399 :
6400 : // Note different order of parameters. The one in PROJ is conformant with
6401 : // EPSG
6402 40 : return d->replaceConversionAndUnref(
6403 : proj_create_conversion_albers_equal_area(
6404 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6405 80 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6406 : }
6407 :
6408 : /************************************************************************/
6409 : /* OSRSetACEA() */
6410 : /************************************************************************/
6411 :
6412 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6413 : double dfCenterLat, double dfCenterLong,
6414 : double dfFalseEasting, double dfFalseNorthing)
6415 :
6416 : {
6417 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6418 :
6419 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6420 0 : dfFalseEasting, dfFalseNorthing);
6421 : }
6422 :
6423 : /************************************************************************/
6424 : /* SetAE() */
6425 : /************************************************************************/
6426 :
6427 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6428 : double dfFalseEasting, double dfFalseNorthing)
6429 :
6430 : {
6431 42 : TAKE_OPTIONAL_LOCK();
6432 :
6433 21 : return d->replaceConversionAndUnref(
6434 : proj_create_conversion_azimuthal_equidistant(
6435 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6436 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6437 : }
6438 :
6439 : /************************************************************************/
6440 : /* OSRSetAE() */
6441 : /************************************************************************/
6442 :
6443 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6444 : double dfCenterLong, double dfFalseEasting,
6445 : double dfFalseNorthing)
6446 :
6447 : {
6448 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6449 :
6450 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6451 0 : dfFalseNorthing);
6452 : }
6453 :
6454 : /************************************************************************/
6455 : /* SetBonne() */
6456 : /************************************************************************/
6457 :
6458 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6459 : double dfFalseEasting,
6460 : double dfFalseNorthing)
6461 :
6462 : {
6463 2 : TAKE_OPTIONAL_LOCK();
6464 :
6465 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6466 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6467 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6468 : }
6469 :
6470 : /************************************************************************/
6471 : /* OSRSetBonne() */
6472 : /************************************************************************/
6473 :
6474 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6475 : double dfCentralMeridian, double dfFalseEasting,
6476 : double dfFalseNorthing)
6477 :
6478 : {
6479 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6480 :
6481 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6482 0 : dfFalseNorthing);
6483 : }
6484 :
6485 : /************************************************************************/
6486 : /* SetCEA() */
6487 : /************************************************************************/
6488 :
6489 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6490 : double dfFalseEasting,
6491 : double dfFalseNorthing)
6492 :
6493 : {
6494 8 : TAKE_OPTIONAL_LOCK();
6495 :
6496 4 : return d->replaceConversionAndUnref(
6497 : proj_create_conversion_lambert_cylindrical_equal_area(
6498 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6499 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6500 : }
6501 :
6502 : /************************************************************************/
6503 : /* OSRSetCEA() */
6504 : /************************************************************************/
6505 :
6506 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6507 : double dfCentralMeridian, double dfFalseEasting,
6508 : double dfFalseNorthing)
6509 :
6510 : {
6511 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6512 :
6513 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6514 0 : dfFalseNorthing);
6515 : }
6516 :
6517 : /************************************************************************/
6518 : /* SetCS() */
6519 : /************************************************************************/
6520 :
6521 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6522 : double dfFalseEasting, double dfFalseNorthing)
6523 :
6524 : {
6525 10 : TAKE_OPTIONAL_LOCK();
6526 :
6527 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6528 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6529 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6530 : }
6531 :
6532 : /************************************************************************/
6533 : /* OSRSetCS() */
6534 : /************************************************************************/
6535 :
6536 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6537 : double dfCenterLong, double dfFalseEasting,
6538 : double dfFalseNorthing)
6539 :
6540 : {
6541 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6542 :
6543 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6544 0 : dfFalseNorthing);
6545 : }
6546 :
6547 : /************************************************************************/
6548 : /* SetEC() */
6549 : /************************************************************************/
6550 :
6551 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6552 : double dfCenterLat, double dfCenterLong,
6553 : double dfFalseEasting, double dfFalseNorthing)
6554 :
6555 : {
6556 14 : TAKE_OPTIONAL_LOCK();
6557 :
6558 : // Note: different order of arguments
6559 7 : return d->replaceConversionAndUnref(
6560 : proj_create_conversion_equidistant_conic(
6561 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6562 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6563 : }
6564 :
6565 : /************************************************************************/
6566 : /* OSRSetEC() */
6567 : /************************************************************************/
6568 :
6569 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6570 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6571 : double dfFalseNorthing)
6572 :
6573 : {
6574 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6575 :
6576 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6577 0 : dfFalseEasting, dfFalseNorthing);
6578 : }
6579 :
6580 : /************************************************************************/
6581 : /* SetEckert() */
6582 : /************************************************************************/
6583 :
6584 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6585 : double dfCentralMeridian,
6586 : double dfFalseEasting,
6587 : double dfFalseNorthing)
6588 :
6589 : {
6590 20 : TAKE_OPTIONAL_LOCK();
6591 :
6592 : PJ *conv;
6593 10 : if (nVariation == 1)
6594 : {
6595 1 : conv = proj_create_conversion_eckert_i(
6596 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6597 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6598 : }
6599 9 : else if (nVariation == 2)
6600 : {
6601 1 : conv = proj_create_conversion_eckert_ii(
6602 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6603 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6604 : }
6605 8 : else if (nVariation == 3)
6606 : {
6607 1 : conv = proj_create_conversion_eckert_iii(
6608 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6609 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6610 : }
6611 7 : else if (nVariation == 4)
6612 : {
6613 3 : conv = proj_create_conversion_eckert_iv(
6614 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6615 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6616 : }
6617 4 : else if (nVariation == 5)
6618 : {
6619 1 : conv = proj_create_conversion_eckert_v(
6620 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6621 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6622 : }
6623 3 : else if (nVariation == 6)
6624 : {
6625 3 : conv = proj_create_conversion_eckert_vi(
6626 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6627 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6628 : }
6629 : else
6630 : {
6631 0 : CPLError(CE_Failure, CPLE_AppDefined,
6632 : "Unsupported Eckert variation (%d).", nVariation);
6633 0 : return OGRERR_UNSUPPORTED_SRS;
6634 : }
6635 :
6636 10 : return d->replaceConversionAndUnref(conv);
6637 : }
6638 :
6639 : /************************************************************************/
6640 : /* OSRSetEckert() */
6641 : /************************************************************************/
6642 :
6643 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6644 : double dfCentralMeridian, double dfFalseEasting,
6645 : double dfFalseNorthing)
6646 :
6647 : {
6648 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6649 :
6650 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6651 0 : dfFalseEasting, dfFalseNorthing);
6652 : }
6653 :
6654 : /************************************************************************/
6655 : /* SetEckertIV() */
6656 : /* */
6657 : /* Deprecated */
6658 : /************************************************************************/
6659 :
6660 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6661 : double dfFalseEasting,
6662 : double dfFalseNorthing)
6663 :
6664 : {
6665 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6666 : }
6667 :
6668 : /************************************************************************/
6669 : /* OSRSetEckertIV() */
6670 : /************************************************************************/
6671 :
6672 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6673 : double dfFalseEasting, double dfFalseNorthing)
6674 :
6675 : {
6676 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6677 :
6678 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6679 0 : dfFalseNorthing);
6680 : }
6681 :
6682 : /************************************************************************/
6683 : /* SetEckertVI() */
6684 : /* */
6685 : /* Deprecated */
6686 : /************************************************************************/
6687 :
6688 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6689 : double dfFalseEasting,
6690 : double dfFalseNorthing)
6691 :
6692 : {
6693 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6694 : }
6695 :
6696 : /************************************************************************/
6697 : /* OSRSetEckertVI() */
6698 : /************************************************************************/
6699 :
6700 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6701 : double dfFalseEasting, double dfFalseNorthing)
6702 :
6703 : {
6704 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6705 :
6706 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6707 0 : dfFalseNorthing);
6708 : }
6709 :
6710 : /************************************************************************/
6711 : /* SetEquirectangular() */
6712 : /************************************************************************/
6713 :
6714 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6715 : double dfCenterLong,
6716 : double dfFalseEasting,
6717 : double dfFalseNorthing)
6718 :
6719 : {
6720 4 : TAKE_OPTIONAL_LOCK();
6721 :
6722 2 : if (dfCenterLat == 0.0)
6723 : {
6724 0 : return d->replaceConversionAndUnref(
6725 : proj_create_conversion_equidistant_cylindrical(
6726 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6727 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6728 : }
6729 :
6730 : // Non-standard extension with non-zero latitude of origin
6731 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6732 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6733 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6734 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6735 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6736 :
6737 2 : return OGRERR_NONE;
6738 : }
6739 :
6740 : /************************************************************************/
6741 : /* OSRSetEquirectangular() */
6742 : /************************************************************************/
6743 :
6744 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6745 : double dfCenterLong, double dfFalseEasting,
6746 : double dfFalseNorthing)
6747 :
6748 : {
6749 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6750 :
6751 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6752 0 : dfFalseEasting, dfFalseNorthing);
6753 : }
6754 :
6755 : /************************************************************************/
6756 : /* SetEquirectangular2() */
6757 : /* Generalized form */
6758 : /************************************************************************/
6759 :
6760 175 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6761 : double dfCenterLong,
6762 : double dfStdParallel1,
6763 : double dfFalseEasting,
6764 : double dfFalseNorthing)
6765 :
6766 : {
6767 350 : TAKE_OPTIONAL_LOCK();
6768 :
6769 175 : if (dfCenterLat == 0.0)
6770 : {
6771 170 : return d->replaceConversionAndUnref(
6772 : proj_create_conversion_equidistant_cylindrical(
6773 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6774 170 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6775 : }
6776 :
6777 : // Non-standard extension with non-zero latitude of origin
6778 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6779 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6780 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6781 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6782 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6783 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6784 :
6785 5 : return OGRERR_NONE;
6786 : }
6787 :
6788 : /************************************************************************/
6789 : /* OSRSetEquirectangular2() */
6790 : /************************************************************************/
6791 :
6792 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6793 : double dfCenterLong, double dfStdParallel1,
6794 : double dfFalseEasting, double dfFalseNorthing)
6795 :
6796 : {
6797 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6798 :
6799 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6800 : dfStdParallel1, dfFalseEasting,
6801 3 : dfFalseNorthing);
6802 : }
6803 :
6804 : /************************************************************************/
6805 : /* SetGS() */
6806 : /************************************************************************/
6807 :
6808 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6809 : double dfFalseEasting, double dfFalseNorthing)
6810 :
6811 : {
6812 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6813 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6814 5 : nullptr, 0.0, nullptr, 0.0));
6815 : }
6816 :
6817 : /************************************************************************/
6818 : /* OSRSetGS() */
6819 : /************************************************************************/
6820 :
6821 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6822 : double dfFalseEasting, double dfFalseNorthing)
6823 :
6824 : {
6825 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6826 :
6827 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6828 2 : dfFalseNorthing);
6829 : }
6830 :
6831 : /************************************************************************/
6832 : /* SetGH() */
6833 : /************************************************************************/
6834 :
6835 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6836 : double dfFalseEasting, double dfFalseNorthing)
6837 :
6838 : {
6839 0 : TAKE_OPTIONAL_LOCK();
6840 :
6841 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6842 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6843 0 : nullptr, 0.0, nullptr, 0.0));
6844 : }
6845 :
6846 : /************************************************************************/
6847 : /* OSRSetGH() */
6848 : /************************************************************************/
6849 :
6850 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6851 : double dfFalseEasting, double dfFalseNorthing)
6852 :
6853 : {
6854 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6855 :
6856 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6857 0 : dfFalseNorthing);
6858 : }
6859 :
6860 : /************************************************************************/
6861 : /* SetIGH() */
6862 : /************************************************************************/
6863 :
6864 0 : OGRErr OGRSpatialReference::SetIGH()
6865 :
6866 : {
6867 0 : TAKE_OPTIONAL_LOCK();
6868 :
6869 0 : return d->replaceConversionAndUnref(
6870 : proj_create_conversion_interrupted_goode_homolosine(
6871 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6872 : }
6873 :
6874 : /************************************************************************/
6875 : /* OSRSetIGH() */
6876 : /************************************************************************/
6877 :
6878 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6879 :
6880 : {
6881 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6882 :
6883 0 : return ToPointer(hSRS)->SetIGH();
6884 : }
6885 :
6886 : /************************************************************************/
6887 : /* SetGEOS() */
6888 : /************************************************************************/
6889 :
6890 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6891 : double dfSatelliteHeight,
6892 : double dfFalseEasting,
6893 : double dfFalseNorthing)
6894 :
6895 : {
6896 6 : TAKE_OPTIONAL_LOCK();
6897 :
6898 3 : return d->replaceConversionAndUnref(
6899 : proj_create_conversion_geostationary_satellite_sweep_y(
6900 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6901 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6902 : }
6903 :
6904 : /************************************************************************/
6905 : /* OSRSetGEOS() */
6906 : /************************************************************************/
6907 :
6908 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6909 : double dfSatelliteHeight, double dfFalseEasting,
6910 : double dfFalseNorthing)
6911 :
6912 : {
6913 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6914 :
6915 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6916 0 : dfFalseEasting, dfFalseNorthing);
6917 : }
6918 :
6919 : /************************************************************************/
6920 : /* SetGaussSchreiberTMercator() */
6921 : /************************************************************************/
6922 :
6923 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6924 : double dfCenterLong,
6925 : double dfScale,
6926 : double dfFalseEasting,
6927 : double dfFalseNorthing)
6928 :
6929 : {
6930 0 : TAKE_OPTIONAL_LOCK();
6931 :
6932 0 : return d->replaceConversionAndUnref(
6933 : proj_create_conversion_gauss_schreiber_transverse_mercator(
6934 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6935 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6936 : }
6937 :
6938 : /************************************************************************/
6939 : /* OSRSetGaussSchreiberTMercator() */
6940 : /************************************************************************/
6941 :
6942 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6943 : double dfCenterLat, double dfCenterLong,
6944 : double dfScale, double dfFalseEasting,
6945 : double dfFalseNorthing)
6946 :
6947 : {
6948 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6949 :
6950 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6951 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6952 : }
6953 :
6954 : /************************************************************************/
6955 : /* SetGnomonic() */
6956 : /************************************************************************/
6957 :
6958 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6959 : double dfFalseEasting,
6960 : double dfFalseNorthing)
6961 :
6962 : {
6963 4 : TAKE_OPTIONAL_LOCK();
6964 :
6965 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6966 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6967 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6968 : }
6969 :
6970 : /************************************************************************/
6971 : /* OSRSetGnomonic() */
6972 : /************************************************************************/
6973 :
6974 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6975 : double dfCenterLong, double dfFalseEasting,
6976 : double dfFalseNorthing)
6977 :
6978 : {
6979 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6980 :
6981 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6982 0 : dfFalseEasting, dfFalseNorthing);
6983 : }
6984 :
6985 : /************************************************************************/
6986 : /* SetHOMAC() */
6987 : /************************************************************************/
6988 :
6989 : /**
6990 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6991 : * azimuth angle.
6992 : *
6993 : * This projection corresponds to EPSG projection method 9815, also
6994 : * sometimes known as hotine oblique mercator (variant B).
6995 : *
6996 : * This method does the same thing as the C function OSRSetHOMAC().
6997 : *
6998 : * @param dfCenterLat Latitude of the projection origin.
6999 : * @param dfCenterLong Longitude of the projection origin.
7000 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7001 : * centerline.
7002 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7003 : * @param dfScale Scale factor applies to the projection origin.
7004 : * @param dfFalseEasting False easting.
7005 : * @param dfFalseNorthing False northing.
7006 : *
7007 : * @return OGRERR_NONE on success.
7008 : */
7009 :
7010 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7011 : double dfAzimuth, double dfRectToSkew,
7012 : double dfScale, double dfFalseEasting,
7013 : double dfFalseNorthing)
7014 :
7015 : {
7016 8 : TAKE_OPTIONAL_LOCK();
7017 :
7018 4 : return d->replaceConversionAndUnref(
7019 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7020 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7021 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7022 8 : 0.0, nullptr, 0.0));
7023 : }
7024 :
7025 : /************************************************************************/
7026 : /* OSRSetHOMAC() */
7027 : /************************************************************************/
7028 :
7029 : /**
7030 : * \brief Set an Oblique Mercator projection using azimuth angle.
7031 : *
7032 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7033 : */
7034 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7035 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7036 : double dfScale, double dfFalseEasting,
7037 : double dfFalseNorthing)
7038 :
7039 : {
7040 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7041 :
7042 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7043 : dfRectToSkew, dfScale, dfFalseEasting,
7044 0 : dfFalseNorthing);
7045 : }
7046 :
7047 : /************************************************************************/
7048 : /* SetHOM() */
7049 : /************************************************************************/
7050 :
7051 : /**
7052 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7053 : *
7054 : * This projection corresponds to EPSG projection method 9812, also
7055 : * sometimes known as hotine oblique mercator (variant A)..
7056 : *
7057 : * This method does the same thing as the C function OSRSetHOM().
7058 : *
7059 : * @param dfCenterLat Latitude of the projection origin.
7060 : * @param dfCenterLong Longitude of the projection origin.
7061 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7062 : * centerline.
7063 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7064 : * @param dfScale Scale factor applies to the projection origin.
7065 : * @param dfFalseEasting False easting.
7066 : * @param dfFalseNorthing False northing.
7067 : *
7068 : * @return OGRERR_NONE on success.
7069 : */
7070 :
7071 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7072 : double dfAzimuth, double dfRectToSkew,
7073 : double dfScale, double dfFalseEasting,
7074 : double dfFalseNorthing)
7075 :
7076 : {
7077 26 : TAKE_OPTIONAL_LOCK();
7078 :
7079 13 : return d->replaceConversionAndUnref(
7080 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7081 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7082 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7083 26 : 0.0, nullptr, 0.0));
7084 : }
7085 :
7086 : /************************************************************************/
7087 : /* OSRSetHOM() */
7088 : /************************************************************************/
7089 : /**
7090 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7091 : *
7092 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7093 : */
7094 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7095 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7096 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7097 :
7098 : {
7099 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7100 :
7101 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7102 : dfRectToSkew, dfScale, dfFalseEasting,
7103 0 : dfFalseNorthing);
7104 : }
7105 :
7106 : /************************************************************************/
7107 : /* SetHOM2PNO() */
7108 : /************************************************************************/
7109 :
7110 : /**
7111 : * \brief Set a Hotine Oblique Mercator projection using two points on
7112 : * projection centerline.
7113 : *
7114 : * This method does the same thing as the C function OSRSetHOM2PNO().
7115 : *
7116 : * @param dfCenterLat Latitude of the projection origin.
7117 : * @param dfLat1 Latitude of the first point on center line.
7118 : * @param dfLong1 Longitude of the first point on center line.
7119 : * @param dfLat2 Latitude of the second point on center line.
7120 : * @param dfLong2 Longitude of the second point on center line.
7121 : * @param dfScale Scale factor applies to the projection origin.
7122 : * @param dfFalseEasting False easting.
7123 : * @param dfFalseNorthing False northing.
7124 : *
7125 : * @return OGRERR_NONE on success.
7126 : */
7127 :
7128 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7129 : double dfLong1, double dfLat2,
7130 : double dfLong2, double dfScale,
7131 : double dfFalseEasting,
7132 : double dfFalseNorthing)
7133 :
7134 : {
7135 6 : TAKE_OPTIONAL_LOCK();
7136 :
7137 3 : return d->replaceConversionAndUnref(
7138 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7139 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7140 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7141 6 : 0.0));
7142 : }
7143 :
7144 : /************************************************************************/
7145 : /* OSRSetHOM2PNO() */
7146 : /************************************************************************/
7147 : /**
7148 : * \brief Set a Hotine Oblique Mercator projection using two points on
7149 : * projection centerline.
7150 : *
7151 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7152 : */
7153 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7154 : double dfLat1, double dfLong1, double dfLat2,
7155 : double dfLong2, double dfScale, double dfFalseEasting,
7156 : double dfFalseNorthing)
7157 :
7158 : {
7159 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7160 :
7161 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7162 : dfLong2, dfScale, dfFalseEasting,
7163 0 : dfFalseNorthing);
7164 : }
7165 :
7166 : /************************************************************************/
7167 : /* SetLOM() */
7168 : /************************************************************************/
7169 :
7170 : /**
7171 : * \brief Set a Laborde Oblique Mercator projection.
7172 : *
7173 : * @param dfCenterLat Latitude of the projection origin.
7174 : * @param dfCenterLong Longitude of the projection origin.
7175 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7176 : * centerline.
7177 : * @param dfScale Scale factor on the initiali line
7178 : * @param dfFalseEasting False easting.
7179 : * @param dfFalseNorthing False northing.
7180 : *
7181 : * @return OGRERR_NONE on success.
7182 : */
7183 :
7184 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7185 : double dfAzimuth, double dfScale,
7186 : double dfFalseEasting,
7187 : double dfFalseNorthing)
7188 :
7189 : {
7190 0 : TAKE_OPTIONAL_LOCK();
7191 :
7192 0 : return d->replaceConversionAndUnref(
7193 : proj_create_conversion_laborde_oblique_mercator(
7194 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7195 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7196 : }
7197 :
7198 : /************************************************************************/
7199 : /* SetIWMPolyconic() */
7200 : /************************************************************************/
7201 :
7202 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7203 : double dfCenterLong,
7204 : double dfFalseEasting,
7205 : double dfFalseNorthing)
7206 :
7207 : {
7208 0 : TAKE_OPTIONAL_LOCK();
7209 :
7210 0 : return d->replaceConversionAndUnref(
7211 : proj_create_conversion_international_map_world_polyconic(
7212 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7213 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7214 : }
7215 :
7216 : /************************************************************************/
7217 : /* OSRSetIWMPolyconic() */
7218 : /************************************************************************/
7219 :
7220 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7221 : double dfLat2, double dfCenterLong,
7222 : double dfFalseEasting, double dfFalseNorthing)
7223 :
7224 : {
7225 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7226 :
7227 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7228 0 : dfFalseEasting, dfFalseNorthing);
7229 : }
7230 :
7231 : /************************************************************************/
7232 : /* SetKrovak() */
7233 : /************************************************************************/
7234 :
7235 : /** Krovak east-north projection.
7236 : *
7237 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7238 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7239 : */
7240 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7241 : double dfAzimuth,
7242 : double dfPseudoStdParallel1,
7243 : double dfScale, double dfFalseEasting,
7244 : double dfFalseNorthing)
7245 :
7246 : {
7247 6 : TAKE_OPTIONAL_LOCK();
7248 :
7249 3 : return d->replaceConversionAndUnref(
7250 : proj_create_conversion_krovak_north_oriented(
7251 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7252 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7253 6 : nullptr, 0.0, nullptr, 0.0));
7254 : }
7255 :
7256 : /************************************************************************/
7257 : /* OSRSetKrovak() */
7258 : /************************************************************************/
7259 :
7260 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7261 : double dfCenterLong, double dfAzimuth,
7262 : double dfPseudoStdParallel1, double dfScale,
7263 : double dfFalseEasting, double dfFalseNorthing)
7264 :
7265 : {
7266 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7267 :
7268 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7269 : dfPseudoStdParallel1, dfScale,
7270 0 : dfFalseEasting, dfFalseNorthing);
7271 : }
7272 :
7273 : /************************************************************************/
7274 : /* SetLAEA() */
7275 : /************************************************************************/
7276 :
7277 18 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7278 : double dfFalseEasting,
7279 : double dfFalseNorthing)
7280 :
7281 : {
7282 36 : TAKE_OPTIONAL_LOCK();
7283 :
7284 18 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7285 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7286 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7287 :
7288 18 : const char *pszName = nullptr;
7289 18 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7290 18 : CPLString osName = pszName ? pszName : "";
7291 :
7292 18 : d->refreshProjObj();
7293 :
7294 18 : d->demoteFromBoundCRS();
7295 :
7296 18 : auto cs = proj_create_cartesian_2D_cs(
7297 : d->getPROJContext(),
7298 18 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7299 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7300 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7301 15 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7302 : : PJ_CART2D_EASTING_NORTHING,
7303 18 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7304 : auto projCRS =
7305 18 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7306 18 : d->getGeodBaseCRS(), conv, cs);
7307 18 : proj_destroy(conv);
7308 18 : proj_destroy(cs);
7309 :
7310 18 : d->setPjCRS(projCRS);
7311 :
7312 18 : d->undoDemoteFromBoundCRS();
7313 :
7314 36 : return OGRERR_NONE;
7315 : }
7316 :
7317 : /************************************************************************/
7318 : /* OSRSetLAEA() */
7319 : /************************************************************************/
7320 :
7321 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7322 : double dfCenterLong, double dfFalseEasting,
7323 : double dfFalseNorthing)
7324 :
7325 : {
7326 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7327 :
7328 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7329 0 : dfFalseNorthing);
7330 : }
7331 :
7332 : /************************************************************************/
7333 : /* SetLCC() */
7334 : /************************************************************************/
7335 :
7336 151 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7337 : double dfCenterLat, double dfCenterLong,
7338 : double dfFalseEasting,
7339 : double dfFalseNorthing)
7340 :
7341 : {
7342 302 : TAKE_OPTIONAL_LOCK();
7343 :
7344 151 : return d->replaceConversionAndUnref(
7345 : proj_create_conversion_lambert_conic_conformal_2sp(
7346 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7347 302 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7348 : }
7349 :
7350 : /************************************************************************/
7351 : /* OSRSetLCC() */
7352 : /************************************************************************/
7353 :
7354 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7355 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7356 : double dfFalseNorthing)
7357 :
7358 : {
7359 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7360 :
7361 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7362 1 : dfFalseEasting, dfFalseNorthing);
7363 : }
7364 :
7365 : /************************************************************************/
7366 : /* SetLCC1SP() */
7367 : /************************************************************************/
7368 :
7369 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7370 : double dfScale, double dfFalseEasting,
7371 : double dfFalseNorthing)
7372 :
7373 : {
7374 20 : TAKE_OPTIONAL_LOCK();
7375 :
7376 10 : return d->replaceConversionAndUnref(
7377 : proj_create_conversion_lambert_conic_conformal_1sp(
7378 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7379 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7380 : }
7381 :
7382 : /************************************************************************/
7383 : /* OSRSetLCC1SP() */
7384 : /************************************************************************/
7385 :
7386 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7387 : double dfCenterLong, double dfScale, double dfFalseEasting,
7388 : double dfFalseNorthing)
7389 :
7390 : {
7391 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7392 :
7393 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7394 0 : dfFalseEasting, dfFalseNorthing);
7395 : }
7396 :
7397 : /************************************************************************/
7398 : /* SetLCCB() */
7399 : /************************************************************************/
7400 :
7401 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7402 : double dfCenterLat, double dfCenterLong,
7403 : double dfFalseEasting,
7404 : double dfFalseNorthing)
7405 :
7406 : {
7407 4 : TAKE_OPTIONAL_LOCK();
7408 :
7409 2 : return d->replaceConversionAndUnref(
7410 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7411 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7412 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7413 : }
7414 :
7415 : /************************************************************************/
7416 : /* OSRSetLCCB() */
7417 : /************************************************************************/
7418 :
7419 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7420 : double dfCenterLat, double dfCenterLong,
7421 : double dfFalseEasting, double dfFalseNorthing)
7422 :
7423 : {
7424 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7425 :
7426 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7427 0 : dfFalseEasting, dfFalseNorthing);
7428 : }
7429 :
7430 : /************************************************************************/
7431 : /* SetMC() */
7432 : /************************************************************************/
7433 :
7434 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7435 : double dfFalseEasting, double dfFalseNorthing)
7436 :
7437 : {
7438 8 : TAKE_OPTIONAL_LOCK();
7439 :
7440 : (void)dfCenterLat; // ignored
7441 :
7442 4 : return d->replaceConversionAndUnref(
7443 : proj_create_conversion_miller_cylindrical(
7444 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7445 8 : nullptr, 0, nullptr, 0));
7446 : }
7447 :
7448 : /************************************************************************/
7449 : /* OSRSetMC() */
7450 : /************************************************************************/
7451 :
7452 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7453 : double dfCenterLong, double dfFalseEasting,
7454 : double dfFalseNorthing)
7455 :
7456 : {
7457 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7458 :
7459 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7460 0 : dfFalseNorthing);
7461 : }
7462 :
7463 : /************************************************************************/
7464 : /* SetMercator() */
7465 : /************************************************************************/
7466 :
7467 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7468 : double dfScale, double dfFalseEasting,
7469 : double dfFalseNorthing)
7470 :
7471 : {
7472 118 : TAKE_OPTIONAL_LOCK();
7473 :
7474 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7475 : {
7476 : // Not sure this is correct, but this is how it has been used
7477 : // historically
7478 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7479 0 : dfFalseNorthing);
7480 : }
7481 59 : return d->replaceConversionAndUnref(
7482 : proj_create_conversion_mercator_variant_a(
7483 : d->getPROJContext(),
7484 : dfCenterLat, // should be zero
7485 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7486 59 : nullptr, 0));
7487 : }
7488 :
7489 : /************************************************************************/
7490 : /* OSRSetMercator() */
7491 : /************************************************************************/
7492 :
7493 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7494 : double dfCenterLong, double dfScale,
7495 : double dfFalseEasting, double dfFalseNorthing)
7496 :
7497 : {
7498 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7499 :
7500 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7501 2 : dfFalseEasting, dfFalseNorthing);
7502 : }
7503 :
7504 : /************************************************************************/
7505 : /* SetMercator2SP() */
7506 : /************************************************************************/
7507 :
7508 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7509 : double dfCenterLong,
7510 : double dfFalseEasting,
7511 : double dfFalseNorthing)
7512 :
7513 : {
7514 31 : if (dfCenterLat == 0.0)
7515 : {
7516 30 : return d->replaceConversionAndUnref(
7517 : proj_create_conversion_mercator_variant_b(
7518 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7519 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7520 : }
7521 :
7522 1 : TAKE_OPTIONAL_LOCK();
7523 :
7524 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7525 :
7526 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7527 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7528 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7529 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7530 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7531 :
7532 1 : return OGRERR_NONE;
7533 : }
7534 :
7535 : /************************************************************************/
7536 : /* OSRSetMercator2SP() */
7537 : /************************************************************************/
7538 :
7539 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7540 : double dfCenterLat, double dfCenterLong,
7541 : double dfFalseEasting, double dfFalseNorthing)
7542 :
7543 : {
7544 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7545 :
7546 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7547 1 : dfFalseEasting, dfFalseNorthing);
7548 : }
7549 :
7550 : /************************************************************************/
7551 : /* SetMollweide() */
7552 : /************************************************************************/
7553 :
7554 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7555 : double dfFalseEasting,
7556 : double dfFalseNorthing)
7557 :
7558 : {
7559 6 : TAKE_OPTIONAL_LOCK();
7560 :
7561 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7562 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7563 6 : nullptr, 0, nullptr, 0));
7564 : }
7565 :
7566 : /************************************************************************/
7567 : /* OSRSetMollweide() */
7568 : /************************************************************************/
7569 :
7570 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7571 : double dfFalseEasting, double dfFalseNorthing)
7572 :
7573 : {
7574 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7575 :
7576 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7577 0 : dfFalseNorthing);
7578 : }
7579 :
7580 : /************************************************************************/
7581 : /* SetNZMG() */
7582 : /************************************************************************/
7583 :
7584 6 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7585 : double dfFalseEasting,
7586 : double dfFalseNorthing)
7587 :
7588 : {
7589 12 : TAKE_OPTIONAL_LOCK();
7590 :
7591 6 : return d->replaceConversionAndUnref(
7592 : proj_create_conversion_new_zealand_mapping_grid(
7593 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7594 12 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7595 : }
7596 :
7597 : /************************************************************************/
7598 : /* OSRSetNZMG() */
7599 : /************************************************************************/
7600 :
7601 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7602 : double dfCenterLong, double dfFalseEasting,
7603 : double dfFalseNorthing)
7604 :
7605 : {
7606 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7607 :
7608 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7609 0 : dfFalseNorthing);
7610 : }
7611 :
7612 : /************************************************************************/
7613 : /* SetOS() */
7614 : /************************************************************************/
7615 :
7616 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7617 : double dfScale, double dfFalseEasting,
7618 : double dfFalseNorthing)
7619 :
7620 : {
7621 12 : TAKE_OPTIONAL_LOCK();
7622 :
7623 6 : return d->replaceConversionAndUnref(
7624 : proj_create_conversion_oblique_stereographic(
7625 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7626 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7627 : }
7628 :
7629 : /************************************************************************/
7630 : /* OSRSetOS() */
7631 : /************************************************************************/
7632 :
7633 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7634 : double dfCMeridian, double dfScale, double dfFalseEasting,
7635 : double dfFalseNorthing)
7636 :
7637 : {
7638 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7639 :
7640 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7641 0 : dfFalseEasting, dfFalseNorthing);
7642 : }
7643 :
7644 : /************************************************************************/
7645 : /* SetOrthographic() */
7646 : /************************************************************************/
7647 :
7648 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7649 : double dfCenterLong,
7650 : double dfFalseEasting,
7651 : double dfFalseNorthing)
7652 :
7653 : {
7654 14 : TAKE_OPTIONAL_LOCK();
7655 :
7656 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7657 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7658 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7659 : }
7660 :
7661 : /************************************************************************/
7662 : /* OSRSetOrthographic() */
7663 : /************************************************************************/
7664 :
7665 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7666 : double dfCenterLong, double dfFalseEasting,
7667 : double dfFalseNorthing)
7668 :
7669 : {
7670 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7671 :
7672 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7673 1 : dfFalseEasting, dfFalseNorthing);
7674 : }
7675 :
7676 : /************************************************************************/
7677 : /* SetPolyconic() */
7678 : /************************************************************************/
7679 :
7680 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7681 : double dfCenterLong,
7682 : double dfFalseEasting,
7683 : double dfFalseNorthing)
7684 :
7685 : {
7686 14 : TAKE_OPTIONAL_LOCK();
7687 :
7688 : // note: it seems that by some definitions this should include a
7689 : // scale_factor parameter.
7690 7 : return d->replaceConversionAndUnref(
7691 : proj_create_conversion_american_polyconic(
7692 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7693 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7694 : }
7695 :
7696 : /************************************************************************/
7697 : /* OSRSetPolyconic() */
7698 : /************************************************************************/
7699 :
7700 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7701 : double dfCenterLong, double dfFalseEasting,
7702 : double dfFalseNorthing)
7703 :
7704 : {
7705 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7706 :
7707 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7708 0 : dfFalseEasting, dfFalseNorthing);
7709 : }
7710 :
7711 : /************************************************************************/
7712 : /* SetPS() */
7713 : /************************************************************************/
7714 :
7715 : /** Sets a Polar Stereographic projection.
7716 : *
7717 : * Two variants are possible:
7718 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7719 : * interpreted as the latitude of origin, combined with the scale factor
7720 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7721 : * is interpreted as the latitude of true scale. In that situation, dfScale
7722 : * must be set to 1 (it is ignored in the projection parameters)
7723 : */
7724 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7725 : double dfScale, double dfFalseEasting,
7726 : double dfFalseNorthing)
7727 :
7728 : {
7729 60 : TAKE_OPTIONAL_LOCK();
7730 :
7731 : PJ *conv;
7732 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7733 : {
7734 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7735 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7736 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7737 : }
7738 : else
7739 : {
7740 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7741 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7742 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7743 : }
7744 :
7745 30 : const char *pszName = nullptr;
7746 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7747 30 : CPLString osName = pszName ? pszName : "";
7748 :
7749 30 : d->refreshProjObj();
7750 :
7751 30 : d->demoteFromBoundCRS();
7752 :
7753 30 : auto cs = proj_create_cartesian_2D_cs(
7754 : d->getPROJContext(),
7755 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7756 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7757 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7758 : auto projCRS =
7759 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7760 30 : d->getGeodBaseCRS(), conv, cs);
7761 30 : proj_destroy(conv);
7762 30 : proj_destroy(cs);
7763 :
7764 30 : d->setPjCRS(projCRS);
7765 :
7766 30 : d->undoDemoteFromBoundCRS();
7767 :
7768 60 : return OGRERR_NONE;
7769 : }
7770 :
7771 : /************************************************************************/
7772 : /* OSRSetPS() */
7773 : /************************************************************************/
7774 :
7775 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7776 : double dfCenterLong, double dfScale, double dfFalseEasting,
7777 : double dfFalseNorthing)
7778 :
7779 : {
7780 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7781 :
7782 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7783 1 : dfFalseEasting, dfFalseNorthing);
7784 : }
7785 :
7786 : /************************************************************************/
7787 : /* SetRobinson() */
7788 : /************************************************************************/
7789 :
7790 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7791 : double dfFalseEasting,
7792 : double dfFalseNorthing)
7793 :
7794 : {
7795 8 : TAKE_OPTIONAL_LOCK();
7796 :
7797 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7798 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7799 8 : nullptr, 0, nullptr, 0));
7800 : }
7801 :
7802 : /************************************************************************/
7803 : /* OSRSetRobinson() */
7804 : /************************************************************************/
7805 :
7806 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7807 : double dfFalseEasting, double dfFalseNorthing)
7808 :
7809 : {
7810 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7811 :
7812 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7813 0 : dfFalseNorthing);
7814 : }
7815 :
7816 : /************************************************************************/
7817 : /* SetSinusoidal() */
7818 : /************************************************************************/
7819 :
7820 33 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7821 : double dfFalseEasting,
7822 : double dfFalseNorthing)
7823 :
7824 : {
7825 66 : TAKE_OPTIONAL_LOCK();
7826 :
7827 33 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7828 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7829 66 : nullptr, 0, nullptr, 0));
7830 : }
7831 :
7832 : /************************************************************************/
7833 : /* OSRSetSinusoidal() */
7834 : /************************************************************************/
7835 :
7836 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7837 : double dfFalseEasting, double dfFalseNorthing)
7838 :
7839 : {
7840 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7841 :
7842 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7843 1 : dfFalseNorthing);
7844 : }
7845 :
7846 : /************************************************************************/
7847 : /* SetStereographic() */
7848 : /************************************************************************/
7849 :
7850 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7851 : double dfCMeridian, double dfScale,
7852 : double dfFalseEasting,
7853 : double dfFalseNorthing)
7854 :
7855 : {
7856 4 : TAKE_OPTIONAL_LOCK();
7857 :
7858 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7859 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7860 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7861 : }
7862 :
7863 : /************************************************************************/
7864 : /* OSRSetStereographic() */
7865 : /************************************************************************/
7866 :
7867 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7868 : double dfCMeridian, double dfScale,
7869 : double dfFalseEasting, double dfFalseNorthing)
7870 :
7871 : {
7872 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7873 :
7874 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7875 0 : dfFalseEasting, dfFalseNorthing);
7876 : }
7877 :
7878 : /************************************************************************/
7879 : /* SetSOC() */
7880 : /* */
7881 : /* NOTE: This definition isn't really used in practice any more */
7882 : /* and should be considered deprecated. It seems that swiss */
7883 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
7884 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7885 : /* EPSG:2056 and Bug 423. */
7886 : /************************************************************************/
7887 :
7888 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7889 : double dfCentralMeridian,
7890 : double dfFalseEasting,
7891 : double dfFalseNorthing)
7892 :
7893 : {
7894 4 : TAKE_OPTIONAL_LOCK();
7895 :
7896 2 : return d->replaceConversionAndUnref(
7897 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7898 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7899 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7900 4 : 0.0));
7901 : #if 0
7902 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7903 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7904 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7905 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7906 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7907 :
7908 : return OGRERR_NONE;
7909 : #endif
7910 : }
7911 :
7912 : /************************************************************************/
7913 : /* OSRSetSOC() */
7914 : /************************************************************************/
7915 :
7916 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7917 : double dfCentralMeridian, double dfFalseEasting,
7918 : double dfFalseNorthing)
7919 :
7920 : {
7921 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7922 :
7923 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7924 0 : dfFalseEasting, dfFalseNorthing);
7925 : }
7926 :
7927 : /************************************************************************/
7928 : /* SetVDG() */
7929 : /************************************************************************/
7930 :
7931 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7932 : double dfFalseNorthing)
7933 :
7934 : {
7935 4 : TAKE_OPTIONAL_LOCK();
7936 :
7937 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7938 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7939 4 : nullptr, 0, nullptr, 0));
7940 : }
7941 :
7942 : /************************************************************************/
7943 : /* OSRSetVDG() */
7944 : /************************************************************************/
7945 :
7946 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7947 : double dfFalseEasting, double dfFalseNorthing)
7948 :
7949 : {
7950 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7951 :
7952 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7953 0 : dfFalseNorthing);
7954 : }
7955 :
7956 : /************************************************************************/
7957 : /* SetUTM() */
7958 : /************************************************************************/
7959 :
7960 : /**
7961 : * \brief Set UTM projection definition.
7962 : *
7963 : * This will generate a projection definition with the full set of
7964 : * transverse mercator projection parameters for the given UTM zone.
7965 : * If no PROJCS[] description is set yet, one will be set to look
7966 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7967 : *
7968 : * This method is the same as the C function OSRSetUTM().
7969 : *
7970 : * @param nZone UTM zone.
7971 : *
7972 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7973 : * hemisphere.
7974 : *
7975 : * @return OGRERR_NONE on success.
7976 : */
7977 :
7978 319 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7979 :
7980 : {
7981 638 : TAKE_OPTIONAL_LOCK();
7982 :
7983 319 : if (nZone < 0 || nZone > 60)
7984 : {
7985 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7986 0 : return OGRERR_FAILURE;
7987 : }
7988 :
7989 319 : return d->replaceConversionAndUnref(
7990 319 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7991 : }
7992 :
7993 : /************************************************************************/
7994 : /* OSRSetUTM() */
7995 : /************************************************************************/
7996 :
7997 : /**
7998 : * \brief Set UTM projection definition.
7999 : *
8000 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8001 : */
8002 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8003 :
8004 : {
8005 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8006 :
8007 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8008 : }
8009 :
8010 : /************************************************************************/
8011 : /* GetUTMZone() */
8012 : /* */
8013 : /* Returns zero if it isn't UTM. */
8014 : /************************************************************************/
8015 :
8016 : /**
8017 : * \brief Get utm zone information.
8018 : *
8019 : * This is the same as the C function OSRGetUTMZone().
8020 : *
8021 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8022 : * zone which is negative in the southern hemisphere instead of having the
8023 : * pbNorth flag used in the C and C++ interface.
8024 : *
8025 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8026 : * FALSE if southern.
8027 : *
8028 : * @return UTM zone number or zero if this isn't a UTM definition.
8029 : */
8030 :
8031 589 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8032 :
8033 : {
8034 1178 : TAKE_OPTIONAL_LOCK();
8035 :
8036 589 : if (IsProjected() && GetAxesCount() == 3)
8037 : {
8038 1 : OGRSpatialReference *poSRSTmp = Clone();
8039 1 : poSRSTmp->DemoteTo2D(nullptr);
8040 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8041 1 : delete poSRSTmp;
8042 1 : return nZone;
8043 : }
8044 :
8045 588 : const char *pszProjection = GetAttrValue("PROJECTION");
8046 :
8047 588 : if (pszProjection == nullptr ||
8048 514 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8049 266 : return 0;
8050 :
8051 322 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8052 5 : return 0;
8053 :
8054 317 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8055 15 : return 0;
8056 :
8057 302 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8058 3 : return 0;
8059 :
8060 299 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8061 :
8062 299 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8063 0 : return 0;
8064 :
8065 299 : if (pbNorth != nullptr)
8066 235 : *pbNorth = (dfFalseNorthing == 0);
8067 :
8068 : const double dfCentralMeridian =
8069 299 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8070 299 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8071 :
8072 598 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8073 897 : std::isnan(dfZone) ||
8074 299 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8075 0 : return 0;
8076 :
8077 299 : return static_cast<int>(dfZone);
8078 : }
8079 :
8080 : /************************************************************************/
8081 : /* OSRGetUTMZone() */
8082 : /************************************************************************/
8083 :
8084 : /**
8085 : * \brief Get utm zone information.
8086 : *
8087 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8088 : */
8089 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8090 :
8091 : {
8092 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8093 :
8094 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8095 : }
8096 :
8097 : /************************************************************************/
8098 : /* SetWagner() */
8099 : /************************************************************************/
8100 :
8101 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8102 : double dfCenterLat, double dfFalseEasting,
8103 : double dfFalseNorthing)
8104 :
8105 : {
8106 0 : TAKE_OPTIONAL_LOCK();
8107 :
8108 : PJ *conv;
8109 0 : if (nVariation == 1)
8110 : {
8111 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8112 : dfFalseEasting, dfFalseNorthing,
8113 : nullptr, 0.0, nullptr, 0.0);
8114 : }
8115 0 : else if (nVariation == 2)
8116 : {
8117 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8118 : dfFalseEasting, dfFalseNorthing,
8119 : nullptr, 0.0, nullptr, 0.0);
8120 : }
8121 0 : else if (nVariation == 3)
8122 : {
8123 0 : conv = proj_create_conversion_wagner_iii(
8124 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8125 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8126 : }
8127 0 : else if (nVariation == 4)
8128 : {
8129 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8130 : dfFalseEasting, dfFalseNorthing,
8131 : nullptr, 0.0, nullptr, 0.0);
8132 : }
8133 0 : else if (nVariation == 5)
8134 : {
8135 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8136 : dfFalseEasting, dfFalseNorthing,
8137 : nullptr, 0.0, nullptr, 0.0);
8138 : }
8139 0 : else if (nVariation == 6)
8140 : {
8141 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8142 : dfFalseEasting, dfFalseNorthing,
8143 : nullptr, 0.0, nullptr, 0.0);
8144 : }
8145 0 : else if (nVariation == 7)
8146 : {
8147 0 : conv = proj_create_conversion_wagner_vii(
8148 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8149 : 0.0, nullptr, 0.0);
8150 : }
8151 : else
8152 : {
8153 0 : CPLError(CE_Failure, CPLE_AppDefined,
8154 : "Unsupported Wagner variation (%d).", nVariation);
8155 0 : return OGRERR_UNSUPPORTED_SRS;
8156 : }
8157 :
8158 0 : return d->replaceConversionAndUnref(conv);
8159 : }
8160 :
8161 : /************************************************************************/
8162 : /* OSRSetWagner() */
8163 : /************************************************************************/
8164 :
8165 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8166 : double dfCenterLat, double dfFalseEasting,
8167 : double dfFalseNorthing)
8168 :
8169 : {
8170 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8171 :
8172 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8173 0 : dfFalseNorthing);
8174 : }
8175 :
8176 : /************************************************************************/
8177 : /* SetQSC() */
8178 : /************************************************************************/
8179 :
8180 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8181 : {
8182 0 : TAKE_OPTIONAL_LOCK();
8183 :
8184 0 : return d->replaceConversionAndUnref(
8185 : proj_create_conversion_quadrilateralized_spherical_cube(
8186 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8187 0 : 0, nullptr, 0));
8188 : }
8189 :
8190 : /************************************************************************/
8191 : /* OSRSetQSC() */
8192 : /************************************************************************/
8193 :
8194 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8195 : double dfCenterLong)
8196 :
8197 : {
8198 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8199 :
8200 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8201 : }
8202 :
8203 : /************************************************************************/
8204 : /* SetSCH() */
8205 : /************************************************************************/
8206 :
8207 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8208 : double dfPegHeading, double dfPegHgt)
8209 :
8210 : {
8211 0 : TAKE_OPTIONAL_LOCK();
8212 :
8213 0 : return d->replaceConversionAndUnref(
8214 : proj_create_conversion_spherical_cross_track_height(
8215 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8216 0 : nullptr, 0, nullptr, 0));
8217 : }
8218 :
8219 : /************************************************************************/
8220 : /* OSRSetSCH() */
8221 : /************************************************************************/
8222 :
8223 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8224 : double dfPegHeading, double dfPegHgt)
8225 :
8226 : {
8227 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8228 :
8229 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8230 : }
8231 :
8232 : /************************************************************************/
8233 : /* SetVerticalPerspective() */
8234 : /************************************************************************/
8235 :
8236 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8237 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8238 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8239 : {
8240 6 : TAKE_OPTIONAL_LOCK();
8241 :
8242 3 : return d->replaceConversionAndUnref(
8243 : proj_create_conversion_vertical_perspective(
8244 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8245 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8246 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8247 : }
8248 :
8249 : /************************************************************************/
8250 : /* OSRSetVerticalPerspective() */
8251 : /************************************************************************/
8252 :
8253 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8254 : double dfTopoOriginLat, double dfTopoOriginLon,
8255 : double dfTopoOriginHeight,
8256 : double dfViewPointHeight,
8257 : double dfFalseEasting, double dfFalseNorthing)
8258 :
8259 : {
8260 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8261 :
8262 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8263 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8264 1 : dfFalseEasting, dfFalseNorthing);
8265 : }
8266 :
8267 : /************************************************************************/
8268 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8269 : /************************************************************************/
8270 :
8271 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8272 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8273 : double dfAxisRotation)
8274 : {
8275 4 : TAKE_OPTIONAL_LOCK();
8276 :
8277 2 : d->refreshProjObj();
8278 2 : if (!d->m_pj_crs)
8279 0 : return OGRERR_FAILURE;
8280 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8281 0 : return OGRERR_FAILURE;
8282 2 : auto ctxt = d->getPROJContext();
8283 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8284 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8285 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8286 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8287 2 : d->m_pj_crs, conv, cs));
8288 2 : proj_destroy(conv);
8289 2 : proj_destroy(cs);
8290 2 : return OGRERR_NONE;
8291 : }
8292 :
8293 : /************************************************************************/
8294 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8295 : /************************************************************************/
8296 :
8297 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8298 : const char *pszCRSName, double dfGridNorthPoleLat,
8299 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8300 : {
8301 3 : TAKE_OPTIONAL_LOCK();
8302 :
8303 : #if PROJ_VERSION_MAJOR > 8 || \
8304 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8305 : d->refreshProjObj();
8306 : if (!d->m_pj_crs)
8307 : return OGRERR_FAILURE;
8308 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8309 : return OGRERR_FAILURE;
8310 : auto ctxt = d->getPROJContext();
8311 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8312 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8313 : nullptr, 0);
8314 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8315 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8316 : d->m_pj_crs, conv, cs));
8317 : proj_destroy(conv);
8318 : proj_destroy(cs);
8319 : return OGRERR_NONE;
8320 : #else
8321 : (void)pszCRSName;
8322 3 : SetProjection("Rotated_pole");
8323 3 : SetExtension(
8324 : "PROJCS", "PROJ4",
8325 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8326 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8327 : "+to_meter=0.0174532925199433 "
8328 : "+wktext",
8329 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8330 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8331 : GetSemiMinor(nullptr)));
8332 6 : return OGRERR_NONE;
8333 : #endif
8334 : }
8335 :
8336 : /************************************************************************/
8337 : /* SetAuthority() */
8338 : /************************************************************************/
8339 :
8340 : /**
8341 : * \brief Set the authority for a node.
8342 : *
8343 : * This method is the same as the C function OSRSetAuthority().
8344 : *
8345 : * @param pszTargetKey the partial or complete path to the node to
8346 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8347 : *
8348 : * @param pszAuthority authority name, such as "EPSG".
8349 : *
8350 : * @param nCode code for value with this authority.
8351 : *
8352 : * @return OGRERR_NONE on success.
8353 : */
8354 :
8355 10329 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8356 : const char *pszAuthority, int nCode)
8357 :
8358 : {
8359 20658 : TAKE_OPTIONAL_LOCK();
8360 :
8361 10329 : d->refreshProjObj();
8362 10329 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8363 :
8364 10329 : if (pszTargetKey == nullptr)
8365 : {
8366 263 : if (!d->m_pj_crs)
8367 0 : return OGRERR_FAILURE;
8368 263 : CPLString osCode;
8369 263 : osCode.Printf("%d", nCode);
8370 263 : d->demoteFromBoundCRS();
8371 263 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8372 : pszAuthority, osCode.c_str()));
8373 263 : d->undoDemoteFromBoundCRS();
8374 263 : return OGRERR_NONE;
8375 : }
8376 :
8377 10066 : d->demoteFromBoundCRS();
8378 10066 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8379 : {
8380 3325 : CPLString osCode;
8381 3325 : osCode.Printf("%d", nCode);
8382 : auto newGeogCRS =
8383 3325 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8384 : pszAuthority, osCode.c_str());
8385 :
8386 : auto conv =
8387 3325 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8388 :
8389 3325 : auto projCRS = proj_create_projected_crs(
8390 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8391 3325 : d->getProjCRSCoordSys());
8392 :
8393 : // Preserve existing id on the PROJCRS
8394 3325 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8395 3325 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8396 3325 : if (pszProjCRSAuthName && pszProjCRSCode)
8397 : {
8398 : auto projCRSWithId =
8399 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8400 : pszProjCRSCode);
8401 0 : proj_destroy(projCRS);
8402 0 : projCRS = projCRSWithId;
8403 : }
8404 :
8405 3325 : proj_destroy(newGeogCRS);
8406 3325 : proj_destroy(conv);
8407 :
8408 3325 : d->setPjCRS(projCRS);
8409 3325 : d->undoDemoteFromBoundCRS();
8410 3325 : return OGRERR_NONE;
8411 : }
8412 6741 : d->undoDemoteFromBoundCRS();
8413 :
8414 : /* -------------------------------------------------------------------- */
8415 : /* Find the node below which the authority should be put. */
8416 : /* -------------------------------------------------------------------- */
8417 6741 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8418 :
8419 6741 : if (poNode == nullptr)
8420 0 : return OGRERR_FAILURE;
8421 :
8422 : /* -------------------------------------------------------------------- */
8423 : /* If there is an existing AUTHORITY child blow it away before */
8424 : /* trying to set a new one. */
8425 : /* -------------------------------------------------------------------- */
8426 6741 : int iOldChild = poNode->FindChild("AUTHORITY");
8427 6741 : if (iOldChild != -1)
8428 5 : poNode->DestroyChild(iOldChild);
8429 :
8430 : /* -------------------------------------------------------------------- */
8431 : /* Create a new authority node. */
8432 : /* -------------------------------------------------------------------- */
8433 6741 : char szCode[32] = {};
8434 :
8435 6741 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8436 :
8437 6741 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8438 6741 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8439 6741 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8440 :
8441 6741 : poNode->AddChild(poAuthNode);
8442 :
8443 6741 : return OGRERR_NONE;
8444 : }
8445 :
8446 : /************************************************************************/
8447 : /* OSRSetAuthority() */
8448 : /************************************************************************/
8449 :
8450 : /**
8451 : * \brief Set the authority for a node.
8452 : *
8453 : * This function is the same as OGRSpatialReference::SetAuthority().
8454 : */
8455 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8456 : const char *pszAuthority, int nCode)
8457 :
8458 : {
8459 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8460 :
8461 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8462 : }
8463 :
8464 : /************************************************************************/
8465 : /* GetAuthorityCode() */
8466 : /************************************************************************/
8467 :
8468 : /**
8469 : * \brief Get the authority code for a node.
8470 : *
8471 : * This method is used to query an AUTHORITY[] node from within the
8472 : * WKT tree, and fetch the code value.
8473 : *
8474 : * While in theory values may be non-numeric, for the EPSG authority all
8475 : * code values should be integral.
8476 : *
8477 : * This method is the same as the C function OSRGetAuthorityCode().
8478 : *
8479 : * @param pszTargetKey the partial or complete path to the node to
8480 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8481 : * search for an authority node on the root element.
8482 : *
8483 : * @return value code from authority node, or NULL on failure. The value
8484 : * returned is internal and should not be freed or modified.
8485 : */
8486 :
8487 : const char *
8488 25376 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8489 :
8490 : {
8491 50752 : TAKE_OPTIONAL_LOCK();
8492 :
8493 25376 : d->refreshProjObj();
8494 25376 : const char *pszInputTargetKey = pszTargetKey;
8495 25376 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8496 25376 : if (pszTargetKey == nullptr)
8497 : {
8498 18101 : if (!d->m_pj_crs)
8499 : {
8500 13 : return nullptr;
8501 : }
8502 18088 : d->demoteFromBoundCRS();
8503 18088 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8504 18088 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8505 : {
8506 1064 : auto ctxt = d->getPROJContext();
8507 1064 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8508 1064 : if (cs)
8509 : {
8510 1064 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8511 1064 : proj_destroy(cs);
8512 1064 : if (axisCount == 3)
8513 : {
8514 : // This might come from a COMPD_CS with a VERT_DATUM type =
8515 : // 2002 in which case, using the WKT1 representation will
8516 : // enable us to recover the EPSG code.
8517 14 : pszTargetKey = pszInputTargetKey;
8518 : }
8519 : }
8520 : }
8521 18088 : d->undoDemoteFromBoundCRS();
8522 18088 : if (ret != nullptr || pszTargetKey == nullptr)
8523 : {
8524 18088 : return ret;
8525 : }
8526 : }
8527 :
8528 : // Special key for that context
8529 7279 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8530 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8531 : {
8532 4 : auto ctxt = d->getPROJContext();
8533 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8534 4 : if (crs)
8535 : {
8536 4 : const char *ret = proj_get_id_code(crs, 0);
8537 4 : if (ret)
8538 4 : ret = CPLSPrintf("%s", ret);
8539 4 : proj_destroy(crs);
8540 4 : return ret;
8541 : }
8542 : }
8543 7275 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8544 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8545 : {
8546 4 : auto ctxt = d->getPROJContext();
8547 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8548 4 : if (crs)
8549 : {
8550 4 : const char *ret = proj_get_id_code(crs, 0);
8551 4 : if (ret)
8552 4 : ret = CPLSPrintf("%s", ret);
8553 4 : proj_destroy(crs);
8554 4 : return ret;
8555 : }
8556 : }
8557 :
8558 : /* -------------------------------------------------------------------- */
8559 : /* Find the node below which the authority should be put. */
8560 : /* -------------------------------------------------------------------- */
8561 7267 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8562 :
8563 7267 : if (poNode == nullptr)
8564 102 : return nullptr;
8565 :
8566 : /* -------------------------------------------------------------------- */
8567 : /* Fetch AUTHORITY child if there is one. */
8568 : /* -------------------------------------------------------------------- */
8569 7165 : if (poNode->FindChild("AUTHORITY") == -1)
8570 182 : return nullptr;
8571 :
8572 6983 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8573 :
8574 : /* -------------------------------------------------------------------- */
8575 : /* Create a new authority node. */
8576 : /* -------------------------------------------------------------------- */
8577 6983 : if (poNode->GetChildCount() < 2)
8578 0 : return nullptr;
8579 :
8580 6983 : return poNode->GetChild(1)->GetValue();
8581 : }
8582 :
8583 : /************************************************************************/
8584 : /* OSRGetAuthorityCode() */
8585 : /************************************************************************/
8586 :
8587 : /**
8588 : * \brief Get the authority code for a node.
8589 : *
8590 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8591 : */
8592 661 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8593 : const char *pszTargetKey)
8594 :
8595 : {
8596 661 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8597 :
8598 661 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8599 : }
8600 :
8601 : /************************************************************************/
8602 : /* GetAuthorityName() */
8603 : /************************************************************************/
8604 :
8605 : /**
8606 : * \brief Get the authority name for a node.
8607 : *
8608 : * This method is used to query an AUTHORITY[] node from within the
8609 : * WKT tree, and fetch the authority name value.
8610 : *
8611 : * The most common authority is "EPSG".
8612 : *
8613 : * This method is the same as the C function OSRGetAuthorityName().
8614 : *
8615 : * @param pszTargetKey the partial or complete path to the node to
8616 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8617 : * search for an authority node on the root element.
8618 : *
8619 : * @return value code from authority node, or NULL on failure. The value
8620 : * returned is internal and should not be freed or modified.
8621 : */
8622 :
8623 : const char *
8624 45783 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8625 :
8626 : {
8627 91566 : TAKE_OPTIONAL_LOCK();
8628 :
8629 45783 : d->refreshProjObj();
8630 45783 : const char *pszInputTargetKey = pszTargetKey;
8631 45783 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8632 45783 : if (pszTargetKey == nullptr)
8633 : {
8634 19833 : if (!d->m_pj_crs)
8635 : {
8636 14 : return nullptr;
8637 : }
8638 19819 : d->demoteFromBoundCRS();
8639 19819 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8640 19819 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8641 : {
8642 787 : auto ctxt = d->getPROJContext();
8643 787 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8644 787 : if (cs)
8645 : {
8646 787 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8647 787 : proj_destroy(cs);
8648 787 : if (axisCount == 3)
8649 : {
8650 : // This might come from a COMPD_CS with a VERT_DATUM type =
8651 : // 2002 in which case, using the WKT1 representation will
8652 : // enable us to recover the EPSG code.
8653 14 : pszTargetKey = pszInputTargetKey;
8654 : }
8655 : }
8656 : }
8657 19819 : d->undoDemoteFromBoundCRS();
8658 19819 : if (ret != nullptr || pszTargetKey == nullptr)
8659 : {
8660 19819 : return ret;
8661 : }
8662 : }
8663 :
8664 : // Special key for that context
8665 25954 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8666 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8667 : {
8668 4 : auto ctxt = d->getPROJContext();
8669 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8670 4 : if (crs)
8671 : {
8672 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8673 4 : if (ret)
8674 4 : ret = CPLSPrintf("%s", ret);
8675 4 : proj_destroy(crs);
8676 4 : return ret;
8677 : }
8678 : }
8679 25950 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8680 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8681 : {
8682 4 : auto ctxt = d->getPROJContext();
8683 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8684 4 : if (crs)
8685 : {
8686 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8687 4 : if (ret)
8688 4 : ret = CPLSPrintf("%s", ret);
8689 4 : proj_destroy(crs);
8690 4 : return ret;
8691 : }
8692 : }
8693 :
8694 : /* -------------------------------------------------------------------- */
8695 : /* Find the node below which the authority should be put. */
8696 : /* -------------------------------------------------------------------- */
8697 25942 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8698 :
8699 25942 : if (poNode == nullptr)
8700 11103 : return nullptr;
8701 :
8702 : /* -------------------------------------------------------------------- */
8703 : /* Fetch AUTHORITY child if there is one. */
8704 : /* -------------------------------------------------------------------- */
8705 14839 : if (poNode->FindChild("AUTHORITY") == -1)
8706 1441 : return nullptr;
8707 :
8708 13398 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8709 :
8710 : /* -------------------------------------------------------------------- */
8711 : /* Create a new authority node. */
8712 : /* -------------------------------------------------------------------- */
8713 13398 : if (poNode->GetChildCount() < 2)
8714 0 : return nullptr;
8715 :
8716 13398 : return poNode->GetChild(0)->GetValue();
8717 : }
8718 :
8719 : /************************************************************************/
8720 : /* OSRGetAuthorityName() */
8721 : /************************************************************************/
8722 :
8723 : /**
8724 : * \brief Get the authority name for a node.
8725 : *
8726 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8727 : */
8728 201 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8729 : const char *pszTargetKey)
8730 :
8731 : {
8732 201 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8733 :
8734 201 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8735 : }
8736 :
8737 : /************************************************************************/
8738 : /* GetOGCURN() */
8739 : /************************************************************************/
8740 :
8741 : /**
8742 : * \brief Get a OGC URN string describing the CRS, when possible
8743 : *
8744 : * This method assumes that the CRS has a top-level identifier, or is
8745 : * a compound CRS whose horizontal and vertical parts have a top-level
8746 : * identifier.
8747 : *
8748 : * @return a string to free with CPLFree(), or nullptr when no result can be
8749 : * generated
8750 : *
8751 : * @since GDAL 3.5
8752 : */
8753 :
8754 59 : char *OGRSpatialReference::GetOGCURN() const
8755 :
8756 : {
8757 118 : TAKE_OPTIONAL_LOCK();
8758 :
8759 59 : const char *pszAuthName = GetAuthorityName(nullptr);
8760 59 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8761 59 : if (pszAuthName && pszAuthCode)
8762 56 : return CPLStrdup(
8763 56 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8764 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8765 2 : return nullptr;
8766 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8767 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8768 1 : char *pszRet = nullptr;
8769 1 : if (horizCRS && vertCRS)
8770 : {
8771 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8772 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8773 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8774 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8775 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8776 : {
8777 1 : pszRet = CPLStrdup(CPLSPrintf(
8778 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8779 : horizAuthCode, vertAuthName, vertAuthCode));
8780 : }
8781 : }
8782 1 : proj_destroy(horizCRS);
8783 1 : proj_destroy(vertCRS);
8784 1 : return pszRet;
8785 : }
8786 :
8787 : /************************************************************************/
8788 : /* StripVertical() */
8789 : /************************************************************************/
8790 :
8791 : /**
8792 : * \brief Convert a compound cs into a horizontal CS.
8793 : *
8794 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8795 : * nodes are stripped resulting and only the horizontal coordinate system
8796 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8797 : *
8798 : * If this is not a compound coordinate system then nothing is changed.
8799 : *
8800 : * This method is the same as the C function OSRStripVertical().
8801 : *
8802 : * @since OGR 1.8.0
8803 : */
8804 :
8805 44 : OGRErr OGRSpatialReference::StripVertical()
8806 :
8807 : {
8808 88 : TAKE_OPTIONAL_LOCK();
8809 :
8810 44 : d->refreshProjObj();
8811 44 : d->demoteFromBoundCRS();
8812 44 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8813 : {
8814 0 : d->undoDemoteFromBoundCRS();
8815 0 : return OGRERR_NONE;
8816 : }
8817 44 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8818 44 : if (!horizCRS)
8819 : {
8820 0 : d->undoDemoteFromBoundCRS();
8821 0 : return OGRERR_FAILURE;
8822 : }
8823 :
8824 44 : bool reuseExistingBoundCRS = false;
8825 44 : if (d->m_pj_bound_crs_target)
8826 : {
8827 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8828 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8829 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8830 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8831 : }
8832 :
8833 44 : if (reuseExistingBoundCRS)
8834 : {
8835 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8836 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8837 4 : d->m_pj_bound_crs_co);
8838 4 : proj_destroy(horizCRS);
8839 4 : d->undoDemoteFromBoundCRS();
8840 4 : d->setPjCRS(newBoundCRS);
8841 : }
8842 : else
8843 : {
8844 40 : d->undoDemoteFromBoundCRS();
8845 40 : d->setPjCRS(horizCRS);
8846 : }
8847 :
8848 44 : return OGRERR_NONE;
8849 : }
8850 :
8851 : /************************************************************************/
8852 : /* OSRStripVertical() */
8853 : /************************************************************************/
8854 : /**
8855 : * \brief Convert a compound cs into a horizontal CS.
8856 : *
8857 : * This function is the same as the C++ method
8858 : * OGRSpatialReference::StripVertical().
8859 : */
8860 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8861 :
8862 : {
8863 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8864 :
8865 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8866 : }
8867 :
8868 : /************************************************************************/
8869 : /* StripTOWGS84IfKnownDatumAndAllowed() */
8870 : /************************************************************************/
8871 :
8872 : /**
8873 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8874 : * and this is allowed by the user.
8875 : *
8876 : * The default behavior is to remove TOWGS84 information if the CRS has a
8877 : * known horizontal datum. This can be disabled by setting the
8878 : * OSR_STRIP_TOWGS84 configuration option to NO.
8879 : *
8880 : * @return true if TOWGS84 has been removed.
8881 : * @since OGR 3.1.0
8882 : */
8883 :
8884 8032 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8885 : {
8886 8032 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8887 : {
8888 8029 : if (StripTOWGS84IfKnownDatum())
8889 : {
8890 71 : CPLDebug("OSR", "TOWGS84 information has been removed. "
8891 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
8892 : "configuration option to NO");
8893 71 : return true;
8894 : }
8895 : }
8896 7961 : return false;
8897 : }
8898 :
8899 : /************************************************************************/
8900 : /* StripTOWGS84IfKnownDatum() */
8901 : /************************************************************************/
8902 :
8903 : /**
8904 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8905 : *
8906 : * @return true if TOWGS84 has been removed.
8907 : * @since OGR 3.1.0
8908 : */
8909 :
8910 8035 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8911 :
8912 : {
8913 16070 : TAKE_OPTIONAL_LOCK();
8914 :
8915 8035 : d->refreshProjObj();
8916 8035 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8917 : {
8918 7944 : return false;
8919 : }
8920 91 : auto ctxt = d->getPROJContext();
8921 91 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8922 91 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8923 : {
8924 3 : proj_destroy(baseCRS);
8925 3 : return false;
8926 : }
8927 :
8928 : // Known base CRS code ? Return base CRS
8929 88 : const char *pszCode = proj_get_id_code(baseCRS, 0);
8930 88 : if (pszCode)
8931 : {
8932 2 : d->setPjCRS(baseCRS);
8933 2 : return true;
8934 : }
8935 :
8936 86 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
8937 : #if PROJ_VERSION_MAJOR > 7 || \
8938 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8939 : if (datum == nullptr)
8940 : {
8941 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8942 : }
8943 : #endif
8944 86 : if (!datum)
8945 : {
8946 0 : proj_destroy(baseCRS);
8947 0 : return false;
8948 : }
8949 :
8950 : // Known datum code ? Return base CRS
8951 86 : pszCode = proj_get_id_code(datum, 0);
8952 86 : if (pszCode)
8953 : {
8954 3 : proj_destroy(datum);
8955 3 : d->setPjCRS(baseCRS);
8956 3 : return true;
8957 : }
8958 :
8959 83 : const char *name = proj_get_name(datum);
8960 83 : if (EQUAL(name, "unknown"))
8961 : {
8962 1 : proj_destroy(datum);
8963 1 : proj_destroy(baseCRS);
8964 1 : return false;
8965 : }
8966 82 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8967 : PJ_OBJ_LIST *list =
8968 82 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8969 :
8970 82 : bool knownDatumName = false;
8971 82 : if (list)
8972 : {
8973 82 : if (proj_list_get_count(list) == 1)
8974 : {
8975 69 : knownDatumName = true;
8976 : }
8977 82 : proj_list_destroy(list);
8978 : }
8979 :
8980 82 : proj_destroy(datum);
8981 82 : if (knownDatumName)
8982 : {
8983 69 : d->setPjCRS(baseCRS);
8984 69 : return true;
8985 : }
8986 13 : proj_destroy(baseCRS);
8987 13 : return false;
8988 : }
8989 :
8990 : /************************************************************************/
8991 : /* IsCompound() */
8992 : /************************************************************************/
8993 :
8994 : /**
8995 : * \brief Check if coordinate system is compound.
8996 : *
8997 : * This method is the same as the C function OSRIsCompound().
8998 : *
8999 : * @return TRUE if this is rooted with a COMPD_CS node.
9000 : */
9001 :
9002 37870 : int OGRSpatialReference::IsCompound() const
9003 :
9004 : {
9005 37870 : TAKE_OPTIONAL_LOCK();
9006 :
9007 37870 : d->refreshProjObj();
9008 37870 : d->demoteFromBoundCRS();
9009 37870 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9010 37870 : d->undoDemoteFromBoundCRS();
9011 75740 : return isCompound;
9012 : }
9013 :
9014 : /************************************************************************/
9015 : /* OSRIsCompound() */
9016 : /************************************************************************/
9017 :
9018 : /**
9019 : * \brief Check if the coordinate system is compound.
9020 : *
9021 : * This function is the same as OGRSpatialReference::IsCompound().
9022 : */
9023 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9024 :
9025 : {
9026 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9027 :
9028 5 : return ToPointer(hSRS)->IsCompound();
9029 : }
9030 :
9031 : /************************************************************************/
9032 : /* IsProjected() */
9033 : /************************************************************************/
9034 :
9035 : /**
9036 : * \brief Check if projected coordinate system.
9037 : *
9038 : * This method is the same as the C function OSRIsProjected().
9039 : *
9040 : * @return TRUE if this contains a PROJCS node indicating a it is a
9041 : * projected coordinate system. Also if it is a CompoundCRS made of a
9042 : * ProjectedCRS
9043 : */
9044 :
9045 36154 : int OGRSpatialReference::IsProjected() const
9046 :
9047 : {
9048 36154 : TAKE_OPTIONAL_LOCK();
9049 :
9050 36154 : d->refreshProjObj();
9051 36154 : d->demoteFromBoundCRS();
9052 36154 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9053 36154 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9054 : {
9055 : auto horizCRS =
9056 142 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9057 142 : if (horizCRS)
9058 : {
9059 142 : auto horizCRSType = proj_get_type(horizCRS);
9060 142 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9061 142 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9062 : {
9063 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9064 6 : if (base)
9065 : {
9066 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9067 6 : proj_destroy(base);
9068 : }
9069 : }
9070 142 : proj_destroy(horizCRS);
9071 : }
9072 : }
9073 36154 : d->undoDemoteFromBoundCRS();
9074 72308 : return isProjected;
9075 : }
9076 :
9077 : /************************************************************************/
9078 : /* OSRIsProjected() */
9079 : /************************************************************************/
9080 : /**
9081 : * \brief Check if projected coordinate system.
9082 : *
9083 : * This function is the same as OGRSpatialReference::IsProjected().
9084 : */
9085 413 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9086 :
9087 : {
9088 413 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9089 :
9090 413 : return ToPointer(hSRS)->IsProjected();
9091 : }
9092 :
9093 : /************************************************************************/
9094 : /* IsGeocentric() */
9095 : /************************************************************************/
9096 :
9097 : /**
9098 : * \brief Check if geocentric coordinate system.
9099 : *
9100 : * This method is the same as the C function OSRIsGeocentric().
9101 : *
9102 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9103 : * geocentric coordinate system.
9104 : *
9105 : * @since OGR 1.9.0
9106 : */
9107 :
9108 15650 : int OGRSpatialReference::IsGeocentric() const
9109 :
9110 : {
9111 15650 : TAKE_OPTIONAL_LOCK();
9112 :
9113 15650 : d->refreshProjObj();
9114 15650 : d->demoteFromBoundCRS();
9115 15650 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9116 15650 : d->undoDemoteFromBoundCRS();
9117 31300 : return isGeocentric;
9118 : }
9119 :
9120 : /************************************************************************/
9121 : /* OSRIsGeocentric() */
9122 : /************************************************************************/
9123 : /**
9124 : * \brief Check if geocentric coordinate system.
9125 : *
9126 : * This function is the same as OGRSpatialReference::IsGeocentric().
9127 : *
9128 : * @since OGR 1.9.0
9129 : */
9130 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9131 :
9132 : {
9133 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9134 :
9135 2 : return ToPointer(hSRS)->IsGeocentric();
9136 : }
9137 :
9138 : /************************************************************************/
9139 : /* IsEmpty() */
9140 : /************************************************************************/
9141 :
9142 : /**
9143 : * \brief Return if the SRS is not set.
9144 : */
9145 :
9146 100419 : bool OGRSpatialReference::IsEmpty() const
9147 : {
9148 100419 : TAKE_OPTIONAL_LOCK();
9149 :
9150 100419 : d->refreshProjObj();
9151 200838 : return d->m_pj_crs == nullptr;
9152 : }
9153 :
9154 : /************************************************************************/
9155 : /* IsGeographic() */
9156 : /************************************************************************/
9157 :
9158 : /**
9159 : * \brief Check if geographic coordinate system.
9160 : *
9161 : * This method is the same as the C function OSRIsGeographic().
9162 : *
9163 : * @return TRUE if this spatial reference is geographic ... that is the
9164 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9165 : * GeographicCRS
9166 : */
9167 :
9168 48979 : int OGRSpatialReference::IsGeographic() const
9169 :
9170 : {
9171 48979 : TAKE_OPTIONAL_LOCK();
9172 :
9173 48979 : d->refreshProjObj();
9174 48979 : d->demoteFromBoundCRS();
9175 70351 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9176 21372 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9177 48979 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9178 : {
9179 : auto horizCRS =
9180 291 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9181 291 : if (horizCRS)
9182 : {
9183 291 : auto horizCRSType = proj_get_type(horizCRS);
9184 291 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9185 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9186 291 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9187 : {
9188 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9189 13 : if (base)
9190 : {
9191 13 : horizCRSType = proj_get_type(base);
9192 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9193 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9194 13 : proj_destroy(base);
9195 : }
9196 : }
9197 291 : proj_destroy(horizCRS);
9198 : }
9199 : }
9200 48979 : d->undoDemoteFromBoundCRS();
9201 97958 : return isGeog;
9202 : }
9203 :
9204 : /************************************************************************/
9205 : /* OSRIsGeographic() */
9206 : /************************************************************************/
9207 : /**
9208 : * \brief Check if geographic coordinate system.
9209 : *
9210 : * This function is the same as OGRSpatialReference::IsGeographic().
9211 : */
9212 334 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9213 :
9214 : {
9215 334 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9216 :
9217 334 : return ToPointer(hSRS)->IsGeographic();
9218 : }
9219 :
9220 : /************************************************************************/
9221 : /* IsDerivedGeographic() */
9222 : /************************************************************************/
9223 :
9224 : /**
9225 : * \brief Check if the CRS is a derived geographic coordinate system.
9226 : * (for example a rotated long/lat grid)
9227 : *
9228 : * This method is the same as the C function OSRIsDerivedGeographic().
9229 : *
9230 : * @since GDAL 3.1.0 and PROJ 6.3.0
9231 : */
9232 :
9233 14889 : int OGRSpatialReference::IsDerivedGeographic() const
9234 :
9235 : {
9236 14889 : TAKE_OPTIONAL_LOCK();
9237 :
9238 14889 : d->refreshProjObj();
9239 14889 : d->demoteFromBoundCRS();
9240 24134 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9241 9245 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9242 : const bool isDerivedGeographic =
9243 14889 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9244 14889 : d->undoDemoteFromBoundCRS();
9245 29778 : return isDerivedGeographic ? TRUE : FALSE;
9246 : }
9247 :
9248 : /************************************************************************/
9249 : /* OSRIsDerivedGeographic() */
9250 : /************************************************************************/
9251 : /**
9252 : * \brief Check if the CRS is a derived geographic coordinate system.
9253 : * (for example a rotated long/lat grid)
9254 : *
9255 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9256 : */
9257 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9258 :
9259 : {
9260 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9261 :
9262 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9263 : }
9264 :
9265 : /************************************************************************/
9266 : /* IsDerivedProjected() */
9267 : /************************************************************************/
9268 :
9269 : /**
9270 : * \brief Check if the CRS is a derived projected coordinate system.
9271 : *
9272 : * This method is the same as the C function OSRIsDerivedGeographic().
9273 : *
9274 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9275 : */
9276 :
9277 0 : int OGRSpatialReference::IsDerivedProjected() const
9278 :
9279 : {
9280 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9281 : TAKE_OPTIONAL_LOCK();
9282 : d->refreshProjObj();
9283 : d->demoteFromBoundCRS();
9284 : const bool isDerivedProjected =
9285 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9286 : d->undoDemoteFromBoundCRS();
9287 : return isDerivedProjected ? TRUE : FALSE;
9288 : #else
9289 0 : return FALSE;
9290 : #endif
9291 : }
9292 :
9293 : /************************************************************************/
9294 : /* OSRIsDerivedProjected() */
9295 : /************************************************************************/
9296 : /**
9297 : * \brief Check if the CRS is a derived projected coordinate system.
9298 : *
9299 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9300 : *
9301 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9302 : */
9303 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9304 :
9305 : {
9306 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9307 :
9308 0 : return ToPointer(hSRS)->IsDerivedProjected();
9309 : }
9310 :
9311 : /************************************************************************/
9312 : /* IsLocal() */
9313 : /************************************************************************/
9314 :
9315 : /**
9316 : * \brief Check if local coordinate system.
9317 : *
9318 : * This method is the same as the C function OSRIsLocal().
9319 : *
9320 : * @return TRUE if this spatial reference is local ... that is the
9321 : * root is a LOCAL_CS node.
9322 : */
9323 :
9324 7355 : int OGRSpatialReference::IsLocal() const
9325 :
9326 : {
9327 7355 : TAKE_OPTIONAL_LOCK();
9328 7355 : d->refreshProjObj();
9329 14710 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9330 : }
9331 :
9332 : /************************************************************************/
9333 : /* OSRIsLocal() */
9334 : /************************************************************************/
9335 : /**
9336 : * \brief Check if local coordinate system.
9337 : *
9338 : * This function is the same as OGRSpatialReference::IsLocal().
9339 : */
9340 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9341 :
9342 : {
9343 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9344 :
9345 8 : return ToPointer(hSRS)->IsLocal();
9346 : }
9347 :
9348 : /************************************************************************/
9349 : /* IsVertical() */
9350 : /************************************************************************/
9351 :
9352 : /**
9353 : * \brief Check if vertical coordinate system.
9354 : *
9355 : * This method is the same as the C function OSRIsVertical().
9356 : *
9357 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9358 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9359 : * VerticalCRS
9360 : *
9361 : * @since OGR 1.8.0
9362 : */
9363 :
9364 8122 : int OGRSpatialReference::IsVertical() const
9365 :
9366 : {
9367 8122 : TAKE_OPTIONAL_LOCK();
9368 8122 : d->refreshProjObj();
9369 8122 : d->demoteFromBoundCRS();
9370 8122 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9371 8122 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9372 : {
9373 : auto vertCRS =
9374 32 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9375 32 : if (vertCRS)
9376 : {
9377 32 : const auto vertCRSType = proj_get_type(vertCRS);
9378 32 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9379 32 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9380 : {
9381 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9382 0 : if (base)
9383 : {
9384 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9385 0 : proj_destroy(base);
9386 : }
9387 : }
9388 32 : proj_destroy(vertCRS);
9389 : }
9390 : }
9391 8122 : d->undoDemoteFromBoundCRS();
9392 16244 : return isVertical;
9393 : }
9394 :
9395 : /************************************************************************/
9396 : /* OSRIsVertical() */
9397 : /************************************************************************/
9398 : /**
9399 : * \brief Check if vertical coordinate system.
9400 : *
9401 : * This function is the same as OGRSpatialReference::IsVertical().
9402 : *
9403 : * @since OGR 1.8.0
9404 : */
9405 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9406 :
9407 : {
9408 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9409 :
9410 0 : return ToPointer(hSRS)->IsVertical();
9411 : }
9412 :
9413 : /************************************************************************/
9414 : /* IsDynamic() */
9415 : /************************************************************************/
9416 :
9417 : /**
9418 : * \brief Check if a CRS is a dynamic CRS.
9419 : *
9420 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9421 : * plate-fixed.
9422 : *
9423 : * This method is the same as the C function OSRIsDynamic().
9424 : *
9425 : * @return true if the CRS is dynamic
9426 : *
9427 : * @since OGR 3.4.0
9428 : *
9429 : * @see HasPointMotionOperation()
9430 : */
9431 :
9432 12280 : bool OGRSpatialReference::IsDynamic() const
9433 :
9434 : {
9435 12280 : TAKE_OPTIONAL_LOCK();
9436 12280 : bool isDynamic = false;
9437 12280 : d->refreshProjObj();
9438 12280 : d->demoteFromBoundCRS();
9439 12280 : auto ctxt = d->getPROJContext();
9440 12280 : PJ *horiz = nullptr;
9441 12280 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9442 : {
9443 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9444 : }
9445 12184 : else if (d->m_pj_crs)
9446 : {
9447 12103 : horiz = proj_clone(ctxt, d->m_pj_crs);
9448 : }
9449 12280 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9450 : {
9451 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9452 6 : if (baseCRS)
9453 : {
9454 6 : proj_destroy(horiz);
9455 6 : horiz = baseCRS;
9456 : }
9457 : }
9458 12280 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9459 12280 : if (datum)
9460 : {
9461 12177 : const auto type = proj_get_type(datum);
9462 12177 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9463 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9464 12177 : if (!isDynamic)
9465 : {
9466 12177 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9467 12177 : const char *code = proj_get_id_code(datum, 0);
9468 12177 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9469 11779 : EQUAL(code, "6326"))
9470 : {
9471 7257 : isDynamic = true;
9472 : }
9473 : }
9474 12177 : proj_destroy(datum);
9475 : }
9476 : #if PROJ_VERSION_MAJOR > 7 || \
9477 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9478 : else
9479 : {
9480 : auto ensemble =
9481 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9482 : if (ensemble)
9483 : {
9484 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9485 : if (member)
9486 : {
9487 : const auto type = proj_get_type(member);
9488 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9489 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9490 : proj_destroy(member);
9491 : }
9492 : proj_destroy(ensemble);
9493 : }
9494 : }
9495 : #endif
9496 12280 : proj_destroy(horiz);
9497 12280 : d->undoDemoteFromBoundCRS();
9498 24560 : return isDynamic;
9499 : }
9500 :
9501 : /************************************************************************/
9502 : /* OSRIsDynamic() */
9503 : /************************************************************************/
9504 : /**
9505 : * \brief Check if a CRS is a dynamic CRS.
9506 : *
9507 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9508 : * plate-fixed.
9509 : *
9510 : * This function is the same as OGRSpatialReference::IsDynamic().
9511 : *
9512 : * @since OGR 3.4.0
9513 : */
9514 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9515 :
9516 : {
9517 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9518 :
9519 0 : return ToPointer(hSRS)->IsDynamic();
9520 : }
9521 :
9522 : /************************************************************************/
9523 : /* HasPointMotionOperation() */
9524 : /************************************************************************/
9525 :
9526 : /**
9527 : * \brief Check if a CRS has at least an associated point motion operation.
9528 : *
9529 : * Some CRS are not formally declared as dynamic, but may behave as such
9530 : * in practice due to the presence of point motion operation, to perform
9531 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9532 : *
9533 : * @return true if the CRS has at least an associated point motion operation.
9534 : *
9535 : * @since OGR 3.8.0 and PROJ 9.4.0
9536 : *
9537 : * @see IsDynamic()
9538 : */
9539 :
9540 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9541 :
9542 : {
9543 : #if PROJ_VERSION_MAJOR > 9 || \
9544 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9545 : TAKE_OPTIONAL_LOCK();
9546 : d->refreshProjObj();
9547 : d->demoteFromBoundCRS();
9548 : auto ctxt = d->getPROJContext();
9549 : auto res =
9550 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9551 : d->undoDemoteFromBoundCRS();
9552 : return res;
9553 : #else
9554 5 : return false;
9555 : #endif
9556 : }
9557 :
9558 : /************************************************************************/
9559 : /* OSRHasPointMotionOperation() */
9560 : /************************************************************************/
9561 :
9562 : /**
9563 : * \brief Check if a CRS has at least an associated point motion operation.
9564 : *
9565 : * Some CRS are not formally declared as dynamic, but may behave as such
9566 : * in practice due to the presence of point motion operation, to perform
9567 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9568 : *
9569 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9570 : *
9571 : * @since OGR 3.8.0 and PROJ 9.4.0
9572 : */
9573 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9574 :
9575 : {
9576 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9577 :
9578 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9579 : }
9580 :
9581 : /************************************************************************/
9582 : /* CloneGeogCS() */
9583 : /************************************************************************/
9584 :
9585 : /**
9586 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9587 : * object.
9588 : *
9589 : * @return a new SRS, which becomes the responsibility of the caller.
9590 : */
9591 3579 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9592 :
9593 : {
9594 7158 : TAKE_OPTIONAL_LOCK();
9595 3579 : d->refreshProjObj();
9596 3579 : if (d->m_pj_crs)
9597 : {
9598 3579 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9599 0 : return nullptr;
9600 :
9601 : auto geodCRS =
9602 3579 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9603 3579 : if (geodCRS)
9604 : {
9605 3579 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9606 3579 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9607 : {
9608 : PJ *hub_crs =
9609 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9610 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9611 13 : d->m_pj_crs);
9612 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9613 : geodCRS, hub_crs, co);
9614 13 : proj_destroy(geodCRS);
9615 13 : geodCRS = temp;
9616 13 : proj_destroy(hub_crs);
9617 13 : proj_destroy(co);
9618 : }
9619 :
9620 : /* --------------------------------------------------------------------
9621 : */
9622 : /* We have to reconstruct the GEOGCS node for geocentric */
9623 : /* coordinate systems. */
9624 : /* --------------------------------------------------------------------
9625 : */
9626 3579 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9627 : {
9628 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9629 : #if PROJ_VERSION_MAJOR > 7 || \
9630 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9631 : if (datum == nullptr)
9632 : {
9633 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9634 : geodCRS);
9635 : }
9636 : #endif
9637 0 : if (datum)
9638 : {
9639 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9640 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9641 : nullptr, 0);
9642 0 : auto temp = proj_create_geographic_crs_from_datum(
9643 : d->getPROJContext(), "unnamed", datum, cs);
9644 0 : proj_destroy(datum);
9645 0 : proj_destroy(cs);
9646 0 : proj_destroy(geodCRS);
9647 0 : geodCRS = temp;
9648 : }
9649 : }
9650 :
9651 3579 : poNewSRS->d->setPjCRS(geodCRS);
9652 3579 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9653 2362 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9654 3579 : return poNewSRS;
9655 : }
9656 : }
9657 0 : return nullptr;
9658 : }
9659 :
9660 : /************************************************************************/
9661 : /* OSRCloneGeogCS() */
9662 : /************************************************************************/
9663 : /**
9664 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9665 : * object.
9666 : *
9667 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9668 : */
9669 150 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9670 :
9671 : {
9672 150 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9673 :
9674 150 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9675 : }
9676 :
9677 : /************************************************************************/
9678 : /* IsSameGeogCS() */
9679 : /************************************************************************/
9680 :
9681 : /**
9682 : * \brief Do the GeogCS'es match?
9683 : *
9684 : * This method is the same as the C function OSRIsSameGeogCS().
9685 : *
9686 : * @param poOther the SRS being compared against.
9687 : *
9688 : * @return TRUE if they are the same or FALSE otherwise.
9689 : */
9690 :
9691 7364 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9692 :
9693 : {
9694 7364 : return IsSameGeogCS(poOther, nullptr);
9695 : }
9696 :
9697 : /**
9698 : * \brief Do the GeogCS'es match?
9699 : *
9700 : * This method is the same as the C function OSRIsSameGeogCS().
9701 : *
9702 : * @param poOther the SRS being compared against.
9703 : * @param papszOptions options. ignored
9704 : *
9705 : * @return TRUE if they are the same or FALSE otherwise.
9706 : */
9707 :
9708 7364 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9709 : const char *const *papszOptions) const
9710 :
9711 : {
9712 14728 : TAKE_OPTIONAL_LOCK();
9713 :
9714 7364 : CPL_IGNORE_RET_VAL(papszOptions);
9715 :
9716 7364 : d->refreshProjObj();
9717 7364 : poOther->d->refreshProjObj();
9718 7364 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9719 0 : return FALSE;
9720 7364 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9721 7364 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9722 22091 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9723 7363 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9724 : {
9725 0 : return FALSE;
9726 : }
9727 :
9728 7364 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9729 : auto otherGeodCRS =
9730 7364 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9731 7364 : if (!geodCRS || !otherGeodCRS)
9732 : {
9733 0 : proj_destroy(geodCRS);
9734 0 : proj_destroy(otherGeodCRS);
9735 0 : return FALSE;
9736 : }
9737 :
9738 7364 : int ret = proj_is_equivalent_to(
9739 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9740 :
9741 7364 : proj_destroy(geodCRS);
9742 7364 : proj_destroy(otherGeodCRS);
9743 7364 : return ret;
9744 : }
9745 :
9746 : /************************************************************************/
9747 : /* OSRIsSameGeogCS() */
9748 : /************************************************************************/
9749 :
9750 : /**
9751 : * \brief Do the GeogCS'es match?
9752 : *
9753 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9754 : */
9755 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9756 :
9757 : {
9758 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9759 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9760 :
9761 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9762 : }
9763 :
9764 : /************************************************************************/
9765 : /* IsSameVertCS() */
9766 : /************************************************************************/
9767 :
9768 : /**
9769 : * \brief Do the VertCS'es match?
9770 : *
9771 : * This method is the same as the C function OSRIsSameVertCS().
9772 : *
9773 : * @param poOther the SRS being compared against.
9774 : *
9775 : * @return TRUE if they are the same or FALSE otherwise.
9776 : */
9777 :
9778 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9779 :
9780 : {
9781 0 : TAKE_OPTIONAL_LOCK();
9782 :
9783 : /* -------------------------------------------------------------------- */
9784 : /* Does the datum name match? */
9785 : /* -------------------------------------------------------------------- */
9786 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9787 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9788 :
9789 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9790 0 : !EQUAL(pszThisValue, pszOtherValue))
9791 0 : return FALSE;
9792 :
9793 : /* -------------------------------------------------------------------- */
9794 : /* Do the units match? */
9795 : /* -------------------------------------------------------------------- */
9796 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9797 0 : if (pszThisValue == nullptr)
9798 0 : pszThisValue = "1.0";
9799 :
9800 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9801 0 : if (pszOtherValue == nullptr)
9802 0 : pszOtherValue = "1.0";
9803 :
9804 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9805 0 : return FALSE;
9806 :
9807 0 : return TRUE;
9808 : }
9809 :
9810 : /************************************************************************/
9811 : /* OSRIsSameVertCS() */
9812 : /************************************************************************/
9813 :
9814 : /**
9815 : * \brief Do the VertCS'es match?
9816 : *
9817 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9818 : */
9819 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9820 :
9821 : {
9822 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9823 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9824 :
9825 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9826 : }
9827 :
9828 : /************************************************************************/
9829 : /* IsSame() */
9830 : /************************************************************************/
9831 :
9832 : /**
9833 : * \brief Do these two spatial references describe the same system ?
9834 : *
9835 : * @param poOtherSRS the SRS being compared to.
9836 : *
9837 : * @return TRUE if equivalent or FALSE otherwise.
9838 : */
9839 :
9840 4472 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9841 :
9842 : {
9843 4472 : return IsSame(poOtherSRS, nullptr);
9844 : }
9845 :
9846 : /**
9847 : * \brief Do these two spatial references describe the same system ?
9848 : *
9849 : * This also takes into account the data axis to CRS axis mapping by default
9850 : *
9851 : * @param poOtherSRS the SRS being compared to.
9852 : * @param papszOptions options. NULL or NULL terminated list of options.
9853 : * Currently supported options are:
9854 : * <ul>
9855 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9856 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9857 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9858 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9859 : * </ul>
9860 : *
9861 : * @return TRUE if equivalent or FALSE otherwise.
9862 : */
9863 :
9864 13045 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9865 : const char *const *papszOptions) const
9866 :
9867 : {
9868 26089 : TAKE_OPTIONAL_LOCK();
9869 :
9870 13045 : d->refreshProjObj();
9871 13045 : poOtherSRS->d->refreshProjObj();
9872 13045 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9873 51 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9874 12994 : if (!CPLTestBool(CSLFetchNameValueDef(
9875 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9876 : {
9877 12424 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9878 2116 : return false;
9879 : }
9880 :
9881 10878 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9882 : "IGNORE_COORDINATE_EPOCH", "NO")))
9883 : {
9884 10543 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9885 27 : return false;
9886 : }
9887 :
9888 10851 : bool reboundSelf = false;
9889 10851 : bool reboundOther = false;
9890 10902 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9891 51 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9892 : {
9893 14 : d->demoteFromBoundCRS();
9894 14 : reboundSelf = true;
9895 : }
9896 21637 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9897 10800 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9898 : {
9899 28 : poOtherSRS->d->demoteFromBoundCRS();
9900 28 : reboundOther = true;
9901 : }
9902 :
9903 10851 : PJ_COMPARISON_CRITERION criterion =
9904 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9905 10851 : const char *pszCriterion = CSLFetchNameValueDef(
9906 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9907 10851 : if (EQUAL(pszCriterion, "STRICT"))
9908 0 : criterion = PJ_COMP_STRICT;
9909 10851 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
9910 6196 : criterion = PJ_COMP_EQUIVALENT;
9911 4655 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9912 : {
9913 0 : CPLError(CE_Warning, CPLE_NotSupported,
9914 : "Unsupported value for CRITERION: %s", pszCriterion);
9915 : }
9916 : int ret =
9917 10851 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9918 10851 : if (reboundSelf)
9919 14 : d->undoDemoteFromBoundCRS();
9920 10851 : if (reboundOther)
9921 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
9922 :
9923 10850 : return ret;
9924 : }
9925 :
9926 : /************************************************************************/
9927 : /* OSRIsSame() */
9928 : /************************************************************************/
9929 :
9930 : /**
9931 : * \brief Do these two spatial references describe the same system ?
9932 : *
9933 : * This function is the same as OGRSpatialReference::IsSame().
9934 : */
9935 30 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9936 :
9937 : {
9938 30 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9939 30 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9940 :
9941 30 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9942 : }
9943 :
9944 : /************************************************************************/
9945 : /* OSRIsSameEx() */
9946 : /************************************************************************/
9947 :
9948 : /**
9949 : * \brief Do these two spatial references describe the same system ?
9950 : *
9951 : * This function is the same as OGRSpatialReference::IsSame().
9952 : */
9953 585 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9954 : const char *const *papszOptions)
9955 : {
9956 585 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9957 585 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9958 :
9959 585 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9960 : }
9961 :
9962 : /************************************************************************/
9963 : /* convertToOtherProjection() */
9964 : /************************************************************************/
9965 :
9966 : /**
9967 : * \brief Convert to another equivalent projection
9968 : *
9969 : * Currently implemented:
9970 : * <ul>
9971 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9972 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9973 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9974 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9975 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9976 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9977 : * </ul>
9978 : *
9979 : * @param pszTargetProjection target projection.
9980 : * @param papszOptions lists of options. None supported currently.
9981 : * @return a new SRS, or NULL in case of error.
9982 : *
9983 : * @since GDAL 2.3
9984 : */
9985 89 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
9986 : const char *pszTargetProjection,
9987 : CPL_UNUSED const char *const *papszOptions) const
9988 : {
9989 178 : TAKE_OPTIONAL_LOCK();
9990 :
9991 89 : if (pszTargetProjection == nullptr)
9992 1 : return nullptr;
9993 : int new_code;
9994 88 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
9995 : {
9996 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
9997 : }
9998 82 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
9999 : {
10000 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10001 : }
10002 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10003 : {
10004 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10005 : }
10006 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10007 : {
10008 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10009 : }
10010 : else
10011 : {
10012 1 : return nullptr;
10013 : }
10014 :
10015 87 : d->refreshProjObj();
10016 87 : d->demoteFromBoundCRS();
10017 87 : OGRSpatialReference *poNewSRS = nullptr;
10018 87 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10019 : {
10020 : auto conv =
10021 86 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10022 86 : auto new_conv = proj_convert_conversion_to_other_method(
10023 : d->getPROJContext(), conv, new_code, nullptr);
10024 86 : proj_destroy(conv);
10025 86 : if (new_conv)
10026 : {
10027 : auto geodCRS =
10028 72 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10029 72 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10030 72 : d->m_pj_crs);
10031 72 : if (geodCRS && cs)
10032 : {
10033 72 : auto new_proj_crs = proj_create_projected_crs(
10034 72 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10035 : new_conv, cs);
10036 72 : proj_destroy(new_conv);
10037 72 : if (new_proj_crs)
10038 : {
10039 72 : poNewSRS = new OGRSpatialReference();
10040 :
10041 72 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10042 : {
10043 9 : auto boundCRS = proj_crs_create_bound_crs(
10044 : d->getPROJContext(), new_proj_crs,
10045 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10046 9 : if (boundCRS)
10047 : {
10048 9 : proj_destroy(new_proj_crs);
10049 9 : new_proj_crs = boundCRS;
10050 : }
10051 : }
10052 :
10053 72 : poNewSRS->d->setPjCRS(new_proj_crs);
10054 : }
10055 : }
10056 72 : proj_destroy(geodCRS);
10057 72 : proj_destroy(cs);
10058 : }
10059 : }
10060 87 : d->undoDemoteFromBoundCRS();
10061 87 : return poNewSRS;
10062 : }
10063 :
10064 : /************************************************************************/
10065 : /* OSRConvertToOtherProjection() */
10066 : /************************************************************************/
10067 :
10068 : /**
10069 : * \brief Convert to another equivalent projection
10070 : *
10071 : * Currently implemented:
10072 : * <ul>
10073 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10074 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10075 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10076 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10077 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10078 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10079 : * </ul>
10080 : *
10081 : * @param hSRS source SRS
10082 : * @param pszTargetProjection target projection.
10083 : * @param papszOptions lists of options. None supported currently.
10084 : * @return a new SRS, or NULL in case of error.
10085 : *
10086 : * @since GDAL 2.3
10087 : */
10088 : OGRSpatialReferenceH
10089 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10090 : const char *pszTargetProjection,
10091 : const char *const *papszOptions)
10092 : {
10093 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10094 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10095 28 : pszTargetProjection, papszOptions));
10096 : }
10097 :
10098 : /************************************************************************/
10099 : /* OSRFindMatches() */
10100 : /************************************************************************/
10101 :
10102 : /**
10103 : * \brief Try to identify a match between the passed SRS and a related SRS
10104 : * in a catalog.
10105 : *
10106 : * Matching may be partial, or may fail.
10107 : * Returned entries will be sorted by decreasing match confidence (first
10108 : * entry has the highest match confidence).
10109 : *
10110 : * The exact way matching is done may change in future versions. Starting with
10111 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10112 : *
10113 : * This function is the same as OGRSpatialReference::FindMatches().
10114 : *
10115 : * @param hSRS SRS to match
10116 : * @param papszOptions NULL terminated list of options or NULL
10117 : * @param pnEntries Output parameter. Number of values in the returned array.
10118 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10119 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10120 : * indicate the confidence in the match. 100 is the highest confidence level.
10121 : * The array must be freed with CPLFree().
10122 : *
10123 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10124 : * with OSRFreeSRSArray()
10125 : *
10126 : * @since GDAL 2.3
10127 : */
10128 8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10129 : char **papszOptions, int *pnEntries,
10130 : int **ppanMatchConfidence)
10131 : {
10132 8 : if (pnEntries)
10133 8 : *pnEntries = 0;
10134 8 : if (ppanMatchConfidence)
10135 8 : *ppanMatchConfidence = nullptr;
10136 8 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10137 :
10138 8 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10139 8 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10140 : }
10141 :
10142 : /************************************************************************/
10143 : /* OSRFreeSRSArray() */
10144 : /************************************************************************/
10145 :
10146 : /**
10147 : * \brief Free return of OSRIdentifyMatches()
10148 : *
10149 : * @param pahSRS array of SRS (must be NULL terminated)
10150 : * @since GDAL 2.3
10151 : */
10152 183 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10153 : {
10154 183 : if (pahSRS != nullptr)
10155 : {
10156 1726 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10157 : {
10158 1561 : OSRRelease(pahSRS[i]);
10159 : }
10160 165 : CPLFree(pahSRS);
10161 : }
10162 183 : }
10163 :
10164 : /************************************************************************/
10165 : /* FindBestMatch() */
10166 : /************************************************************************/
10167 :
10168 : /**
10169 : * \brief Try to identify the best match between the passed SRS and a related
10170 : * SRS in a catalog.
10171 : *
10172 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10173 : * of filtering its output.
10174 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10175 : * will be considered. If there is a single match, it is returned.
10176 : * If there are several matches, only return the one under the
10177 : * pszPreferredAuthority, if there is a single one under that authority.
10178 : *
10179 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10180 : * 100). If set to 0, 90 is used.
10181 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10182 : * "EPSG" is used.
10183 : * @param papszOptions NULL terminated list of options or NULL. No option is
10184 : * defined at time of writing.
10185 : *
10186 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10187 : *
10188 : * @since GDAL 3.6
10189 : * @see OGRSpatialReference::FindMatches()
10190 : */
10191 : OGRSpatialReference *
10192 1249 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10193 : const char *pszPreferredAuthority,
10194 : CSLConstList papszOptions) const
10195 : {
10196 2498 : TAKE_OPTIONAL_LOCK();
10197 :
10198 1249 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10199 :
10200 1249 : if (nMinimumMatchConfidence == 0)
10201 0 : nMinimumMatchConfidence = 90;
10202 1249 : if (pszPreferredAuthority == nullptr)
10203 200 : pszPreferredAuthority = "EPSG";
10204 :
10205 : // Try to identify the CRS with the database
10206 1249 : int nEntries = 0;
10207 1249 : int *panConfidence = nullptr;
10208 : OGRSpatialReferenceH *pahSRS =
10209 1249 : FindMatches(nullptr, &nEntries, &panConfidence);
10210 1249 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10211 : {
10212 2198 : std::vector<double> adfTOWGS84(7);
10213 1099 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10214 : {
10215 1098 : adfTOWGS84.clear();
10216 : }
10217 :
10218 1099 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10219 :
10220 : auto poBaseGeogCRS =
10221 1099 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10222 :
10223 : // If the base geographic SRS of the SRS is EPSG:4326
10224 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10225 : // SRS code
10226 : // Same with EPSG:4258 (ETRS89), since it's the only known
10227 : // TOWGS84[] style transformation to WGS 84, and given the
10228 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10229 : // chance that a non-NULL TOWGS84[] will emerge.
10230 1099 : const char *pszAuthorityName = nullptr;
10231 1099 : const char *pszAuthorityCode = nullptr;
10232 1099 : const char *pszBaseAuthorityName = nullptr;
10233 1099 : const char *pszBaseAuthorityCode = nullptr;
10234 2198 : if (adfTOWGS84 == std::vector<double>(7) &&
10235 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10236 1 : EQUAL(pszAuthorityName, "EPSG") &&
10237 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10238 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10239 1 : nullptr &&
10240 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10241 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10242 2199 : nullptr &&
10243 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10244 1 : EQUAL(pszBaseAuthorityCode, "4258")))
10245 : {
10246 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10247 : }
10248 :
10249 1099 : CPLFree(pahSRS);
10250 1099 : CPLFree(panConfidence);
10251 :
10252 1099 : return poSRS;
10253 : }
10254 : else
10255 : {
10256 : // If there are several matches >= nMinimumMatchConfidence, take the
10257 : // only one that is under pszPreferredAuthority
10258 150 : int iBestEntry = -1;
10259 1661 : for (int i = 0; i < nEntries; i++)
10260 : {
10261 1511 : if (panConfidence[i] >= nMinimumMatchConfidence)
10262 : {
10263 : const char *pszAuthName =
10264 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10265 3 : ->GetAuthorityName(nullptr);
10266 3 : if (pszAuthName != nullptr &&
10267 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10268 : {
10269 3 : if (iBestEntry < 0)
10270 3 : iBestEntry = i;
10271 : else
10272 : {
10273 0 : iBestEntry = -1;
10274 0 : break;
10275 : }
10276 : }
10277 : }
10278 : }
10279 150 : if (iBestEntry >= 0)
10280 : {
10281 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10282 3 : OSRFreeSRSArray(pahSRS);
10283 3 : CPLFree(panConfidence);
10284 3 : return poRet;
10285 : }
10286 : }
10287 147 : OSRFreeSRSArray(pahSRS);
10288 147 : CPLFree(panConfidence);
10289 147 : return nullptr;
10290 : }
10291 :
10292 : /************************************************************************/
10293 : /* SetTOWGS84() */
10294 : /************************************************************************/
10295 :
10296 : /**
10297 : * \brief Set the Bursa-Wolf conversion to WGS84.
10298 : *
10299 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10300 : * if there is no existing DATUM node. It will replace
10301 : * an existing TOWGS84 node if there is one.
10302 : *
10303 : * The parameters have the same meaning as EPSG transformation 9606
10304 : * (Position Vector 7-param. transformation).
10305 : *
10306 : * This method is the same as the C function OSRSetTOWGS84().
10307 : *
10308 : * @param dfDX X child in meters.
10309 : * @param dfDY Y child in meters.
10310 : * @param dfDZ Z child in meters.
10311 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10312 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10313 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10314 : * @param dfPPM scaling factor (parts per million).
10315 : *
10316 : * @return OGRERR_NONE on success.
10317 : */
10318 :
10319 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10320 : double dfEX, double dfEY, double dfEZ,
10321 : double dfPPM)
10322 :
10323 : {
10324 206 : TAKE_OPTIONAL_LOCK();
10325 :
10326 103 : d->refreshProjObj();
10327 103 : if (d->m_pj_crs == nullptr)
10328 : {
10329 0 : return OGRERR_FAILURE;
10330 : }
10331 :
10332 : // Remove existing BoundCRS
10333 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10334 : {
10335 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10336 0 : if (!baseCRS)
10337 0 : return OGRERR_FAILURE;
10338 0 : d->setPjCRS(baseCRS);
10339 : }
10340 :
10341 : PJ_PARAM_DESCRIPTION params[7];
10342 :
10343 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10344 103 : params[0].auth_name = "EPSG";
10345 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10346 103 : params[0].value = dfDX;
10347 103 : params[0].unit_name = "metre";
10348 103 : params[0].unit_conv_factor = 1.0;
10349 103 : params[0].unit_type = PJ_UT_LINEAR;
10350 :
10351 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10352 103 : params[1].auth_name = "EPSG";
10353 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10354 103 : params[1].value = dfDY;
10355 103 : params[1].unit_name = "metre";
10356 103 : params[1].unit_conv_factor = 1.0;
10357 103 : params[1].unit_type = PJ_UT_LINEAR;
10358 :
10359 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10360 103 : params[2].auth_name = "EPSG";
10361 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10362 103 : params[2].value = dfDZ;
10363 103 : params[2].unit_name = "metre";
10364 103 : params[2].unit_conv_factor = 1.0;
10365 103 : params[2].unit_type = PJ_UT_LINEAR;
10366 :
10367 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10368 103 : params[3].auth_name = "EPSG";
10369 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10370 103 : params[3].value = dfEX;
10371 103 : params[3].unit_name = "arc-second";
10372 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10373 103 : params[3].unit_type = PJ_UT_ANGULAR;
10374 :
10375 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10376 103 : params[4].auth_name = "EPSG";
10377 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10378 103 : params[4].value = dfEY;
10379 103 : params[4].unit_name = "arc-second";
10380 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10381 103 : params[4].unit_type = PJ_UT_ANGULAR;
10382 :
10383 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10384 103 : params[5].auth_name = "EPSG";
10385 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10386 103 : params[5].value = dfEZ;
10387 103 : params[5].unit_name = "arc-second";
10388 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10389 103 : params[5].unit_type = PJ_UT_ANGULAR;
10390 :
10391 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10392 103 : params[6].auth_name = "EPSG";
10393 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10394 103 : params[6].value = dfPPM;
10395 103 : params[6].unit_name = "parts per million";
10396 103 : params[6].unit_conv_factor = 1e-6;
10397 103 : params[6].unit_type = PJ_UT_SCALE;
10398 :
10399 : auto sourceCRS =
10400 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10401 103 : if (!sourceCRS)
10402 : {
10403 0 : return OGRERR_FAILURE;
10404 : }
10405 :
10406 103 : const auto sourceType = proj_get_type(sourceCRS);
10407 :
10408 103 : auto targetCRS = proj_create_from_database(
10409 : d->getPROJContext(), "EPSG",
10410 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10411 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10412 : : "4978",
10413 : PJ_CATEGORY_CRS, false, nullptr);
10414 103 : if (!targetCRS)
10415 : {
10416 0 : proj_destroy(sourceCRS);
10417 0 : return OGRERR_FAILURE;
10418 : }
10419 :
10420 206 : CPLString osMethodCode;
10421 : osMethodCode.Printf("%d",
10422 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10423 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10424 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10425 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10426 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10427 :
10428 103 : auto transf = proj_create_transformation(
10429 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10430 : sourceCRS, targetCRS, nullptr,
10431 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10432 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10433 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10434 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10435 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10436 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10437 103 : proj_destroy(sourceCRS);
10438 103 : if (!transf)
10439 : {
10440 0 : proj_destroy(targetCRS);
10441 0 : return OGRERR_FAILURE;
10442 : }
10443 :
10444 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10445 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10446 103 : proj_destroy(transf);
10447 103 : proj_destroy(targetCRS);
10448 103 : if (!newBoundCRS)
10449 : {
10450 0 : return OGRERR_FAILURE;
10451 : }
10452 :
10453 103 : d->setPjCRS(newBoundCRS);
10454 103 : return OGRERR_NONE;
10455 : }
10456 :
10457 : /************************************************************************/
10458 : /* OSRSetTOWGS84() */
10459 : /************************************************************************/
10460 :
10461 : /**
10462 : * \brief Set the Bursa-Wolf conversion to WGS84.
10463 : *
10464 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10465 : */
10466 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10467 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10468 : double dfPPM)
10469 :
10470 : {
10471 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10472 :
10473 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10474 4 : dfPPM);
10475 : }
10476 :
10477 : /************************************************************************/
10478 : /* GetTOWGS84() */
10479 : /************************************************************************/
10480 :
10481 : /**
10482 : * \brief Fetch TOWGS84 parameters, if available.
10483 : *
10484 : * The parameters have the same meaning as EPSG transformation 9606
10485 : * (Position Vector 7-param. transformation).
10486 : *
10487 : * @param padfCoeff array into which up to 7 coefficients are placed.
10488 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10489 : *
10490 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10491 : * TOWGS84 node available.
10492 : */
10493 :
10494 4312 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10495 :
10496 : {
10497 8624 : TAKE_OPTIONAL_LOCK();
10498 :
10499 4312 : d->refreshProjObj();
10500 4312 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10501 4264 : return OGRERR_FAILURE;
10502 :
10503 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10504 :
10505 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10506 48 : int success = proj_coordoperation_get_towgs84_values(
10507 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10508 48 : proj_destroy(transf);
10509 :
10510 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10511 : }
10512 :
10513 : /************************************************************************/
10514 : /* OSRGetTOWGS84() */
10515 : /************************************************************************/
10516 :
10517 : /**
10518 : * \brief Fetch TOWGS84 parameters, if available.
10519 : *
10520 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10521 : */
10522 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10523 : int nCoeffCount)
10524 :
10525 : {
10526 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10527 :
10528 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10529 : }
10530 :
10531 : /************************************************************************/
10532 : /* IsAngularParameter() */
10533 : /************************************************************************/
10534 :
10535 : /** Is the passed projection parameter an angular one?
10536 : *
10537 : * @return TRUE or FALSE
10538 : */
10539 :
10540 : /* static */
10541 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10542 :
10543 : {
10544 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10545 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10546 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10547 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10548 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10549 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10550 8 : return TRUE;
10551 :
10552 2 : return FALSE;
10553 : }
10554 :
10555 : /************************************************************************/
10556 : /* IsLongitudeParameter() */
10557 : /************************************************************************/
10558 :
10559 : /** Is the passed projection parameter an angular longitude
10560 : * (relative to a prime meridian)?
10561 : *
10562 : * @return TRUE or FALSE
10563 : */
10564 :
10565 : /* static */
10566 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10567 :
10568 : {
10569 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10570 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10571 0 : return TRUE;
10572 :
10573 0 : return FALSE;
10574 : }
10575 :
10576 : /************************************************************************/
10577 : /* IsLinearParameter() */
10578 : /************************************************************************/
10579 :
10580 : /** Is the passed projection parameter an linear one measured in meters or
10581 : * some similar linear measure.
10582 : *
10583 : * @return TRUE or FALSE
10584 : */
10585 :
10586 : /* static */
10587 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10588 :
10589 : {
10590 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10591 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10592 9 : return TRUE;
10593 :
10594 34 : return FALSE;
10595 : }
10596 :
10597 : /************************************************************************/
10598 : /* GetNormInfo() */
10599 : /************************************************************************/
10600 :
10601 : /**
10602 : * \brief Set the internal information for normalizing linear, and angular
10603 : * values.
10604 : */
10605 3516 : void OGRSpatialReference::GetNormInfo() const
10606 :
10607 : {
10608 3516 : TAKE_OPTIONAL_LOCK();
10609 :
10610 3516 : if (d->bNormInfoSet)
10611 2466 : return;
10612 :
10613 : /* -------------------------------------------------------------------- */
10614 : /* Initialize values. */
10615 : /* -------------------------------------------------------------------- */
10616 1050 : d->bNormInfoSet = TRUE;
10617 :
10618 1050 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10619 1050 : d->dfToMeter = GetLinearUnits(nullptr);
10620 1050 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10621 1050 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10622 1047 : d->dfToDegrees = 1.0;
10623 : }
10624 :
10625 : /************************************************************************/
10626 : /* GetExtension() */
10627 : /************************************************************************/
10628 :
10629 : /**
10630 : * \brief Fetch extension value.
10631 : *
10632 : * Fetch the value of the named EXTENSION item for the identified
10633 : * target node.
10634 : *
10635 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10636 : * @param pszName the name of the extension being fetched.
10637 : * @param pszDefault the value to return if the extension is not found.
10638 : *
10639 : * @return node value if successful or pszDefault on failure.
10640 : */
10641 :
10642 9039 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10643 : const char *pszName,
10644 : const char *pszDefault) const
10645 :
10646 : {
10647 18078 : TAKE_OPTIONAL_LOCK();
10648 :
10649 : /* -------------------------------------------------------------------- */
10650 : /* Find the target node. */
10651 : /* -------------------------------------------------------------------- */
10652 : const OGR_SRSNode *poNode =
10653 9039 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10654 :
10655 9039 : if (poNode == nullptr)
10656 2186 : return nullptr;
10657 :
10658 : /* -------------------------------------------------------------------- */
10659 : /* Fetch matching EXTENSION if there is one. */
10660 : /* -------------------------------------------------------------------- */
10661 50707 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10662 : {
10663 43876 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10664 :
10665 43900 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10666 24 : poChild->GetChildCount() >= 2)
10667 : {
10668 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10669 22 : return poChild->GetChild(1)->GetValue();
10670 : }
10671 : }
10672 :
10673 6831 : return pszDefault;
10674 : }
10675 :
10676 : /************************************************************************/
10677 : /* SetExtension() */
10678 : /************************************************************************/
10679 : /**
10680 : * \brief Set extension value.
10681 : *
10682 : * Set the value of the named EXTENSION item for the identified
10683 : * target node.
10684 : *
10685 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10686 : * @param pszName the name of the extension being fetched.
10687 : * @param pszValue the value to set
10688 : *
10689 : * @return OGRERR_NONE on success
10690 : */
10691 :
10692 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10693 : const char *pszName,
10694 : const char *pszValue)
10695 :
10696 : {
10697 40 : TAKE_OPTIONAL_LOCK();
10698 :
10699 : /* -------------------------------------------------------------------- */
10700 : /* Find the target node. */
10701 : /* -------------------------------------------------------------------- */
10702 20 : OGR_SRSNode *poNode = nullptr;
10703 :
10704 20 : if (pszTargetKey == nullptr)
10705 0 : poNode = GetRoot();
10706 : else
10707 20 : poNode = GetAttrNode(pszTargetKey);
10708 :
10709 20 : if (poNode == nullptr)
10710 0 : return OGRERR_FAILURE;
10711 :
10712 : /* -------------------------------------------------------------------- */
10713 : /* Fetch matching EXTENSION if there is one. */
10714 : /* -------------------------------------------------------------------- */
10715 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10716 : {
10717 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10718 :
10719 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10720 6 : poChild->GetChildCount() >= 2)
10721 : {
10722 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10723 : {
10724 6 : poChild->GetChild(1)->SetValue(pszValue);
10725 6 : return OGRERR_NONE;
10726 : }
10727 : }
10728 : }
10729 :
10730 : /* -------------------------------------------------------------------- */
10731 : /* Create a new EXTENSION node. */
10732 : /* -------------------------------------------------------------------- */
10733 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10734 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10735 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10736 :
10737 14 : poNode->AddChild(poAuthNode);
10738 :
10739 14 : return OGRERR_NONE;
10740 : }
10741 :
10742 : /************************************************************************/
10743 : /* OSRCleanup() */
10744 : /************************************************************************/
10745 :
10746 : static void CleanupSRSWGS84Mutex();
10747 :
10748 : /**
10749 : * \brief Cleanup cached SRS related memory.
10750 : *
10751 : * This function will attempt to cleanup any cache spatial reference
10752 : * related information, such as cached tables of coordinate systems.
10753 : *
10754 : * This function should not be called concurrently with any other GDAL/OGR
10755 : * function. It is meant at being called once before process termination
10756 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10757 : * thread-specific resources before thread termination.
10758 : */
10759 927 : void OSRCleanup(void)
10760 :
10761 : {
10762 927 : OGRCTDumpStatistics();
10763 927 : CSVDeaccess(nullptr);
10764 927 : CleanupSRSWGS84Mutex();
10765 927 : OSRCTCleanCache();
10766 927 : OSRCleanupTLSContext();
10767 927 : }
10768 :
10769 : /************************************************************************/
10770 : /* GetAxesCount() */
10771 : /************************************************************************/
10772 :
10773 : /**
10774 : * \brief Return the number of axis of the coordinate system of the CRS.
10775 : *
10776 : * @since GDAL 3.0
10777 : */
10778 34495 : int OGRSpatialReference::GetAxesCount() const
10779 : {
10780 68990 : TAKE_OPTIONAL_LOCK();
10781 :
10782 34495 : int axisCount = 0;
10783 34495 : d->refreshProjObj();
10784 34495 : if (d->m_pj_crs == nullptr)
10785 : {
10786 0 : return 0;
10787 : }
10788 34495 : d->demoteFromBoundCRS();
10789 34495 : auto ctxt = d->getPROJContext();
10790 34495 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10791 : {
10792 29 : for (int i = 0;; i++)
10793 : {
10794 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10795 87 : if (!subCRS)
10796 29 : break;
10797 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10798 : {
10799 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10800 17 : if (baseCRS)
10801 : {
10802 17 : proj_destroy(subCRS);
10803 17 : subCRS = baseCRS;
10804 : }
10805 : }
10806 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10807 58 : if (cs)
10808 : {
10809 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10810 58 : proj_destroy(cs);
10811 : }
10812 58 : proj_destroy(subCRS);
10813 58 : }
10814 : }
10815 : else
10816 : {
10817 34466 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10818 34466 : if (cs)
10819 : {
10820 34466 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10821 34466 : proj_destroy(cs);
10822 : }
10823 : }
10824 34495 : d->undoDemoteFromBoundCRS();
10825 34495 : return axisCount;
10826 : }
10827 :
10828 : /************************************************************************/
10829 : /* OSRGetAxesCount() */
10830 : /************************************************************************/
10831 :
10832 : /**
10833 : * \brief Return the number of axis of the coordinate system of the CRS.
10834 : *
10835 : * This method is the equivalent of the C++ method
10836 : * OGRSpatialReference::GetAxesCount()
10837 : *
10838 : * @since GDAL 3.1
10839 : */
10840 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10841 :
10842 : {
10843 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10844 :
10845 6 : return ToPointer(hSRS)->GetAxesCount();
10846 : }
10847 :
10848 : /************************************************************************/
10849 : /* GetAxis() */
10850 : /************************************************************************/
10851 :
10852 : /**
10853 : * \brief Fetch the orientation of one axis.
10854 : *
10855 : * Fetches the request axis (iAxis - zero based) from the
10856 : * indicated portion of the coordinate system (pszTargetKey) which
10857 : * should be either "GEOGCS" or "PROJCS".
10858 : *
10859 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10860 : *
10861 : * This method is equivalent to the C function OSRGetAxis().
10862 : *
10863 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10864 : * "GEOGCS").
10865 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10866 : * @param peOrientation location into which to place the fetch orientation, may
10867 : * be NULL.
10868 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10869 : * factor. May be NULL. Only set if pszTargetKey == NULL
10870 : *
10871 : * @return the name of the axis or NULL on failure.
10872 : */
10873 :
10874 5444 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10875 : OGRAxisOrientation *peOrientation,
10876 : double *pdfConvUnit) const
10877 :
10878 : {
10879 10888 : TAKE_OPTIONAL_LOCK();
10880 :
10881 5444 : if (peOrientation != nullptr)
10882 5351 : *peOrientation = OAO_Other;
10883 5444 : if (pdfConvUnit != nullptr)
10884 85 : *pdfConvUnit = 0;
10885 :
10886 5444 : d->refreshProjObj();
10887 5444 : if (d->m_pj_crs == nullptr)
10888 : {
10889 1 : return nullptr;
10890 : }
10891 :
10892 5443 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10893 5443 : if (pszTargetKey == nullptr && iAxis <= 2)
10894 : {
10895 5443 : auto ctxt = d->getPROJContext();
10896 :
10897 5443 : int iAxisModified = iAxis;
10898 :
10899 5443 : d->demoteFromBoundCRS();
10900 :
10901 5443 : PJ *cs = nullptr;
10902 5443 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10903 : {
10904 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10905 134 : if (horizCRS)
10906 : {
10907 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10908 : {
10909 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10910 6 : if (baseCRS)
10911 : {
10912 6 : proj_destroy(horizCRS);
10913 6 : horizCRS = baseCRS;
10914 : }
10915 : }
10916 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10917 134 : proj_destroy(horizCRS);
10918 134 : if (cs)
10919 : {
10920 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10921 : {
10922 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10923 44 : proj_destroy(cs);
10924 44 : cs = nullptr;
10925 : }
10926 : }
10927 : }
10928 :
10929 134 : if (cs == nullptr)
10930 : {
10931 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10932 44 : if (vertCRS)
10933 : {
10934 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10935 : {
10936 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10937 30 : if (baseCRS)
10938 : {
10939 30 : proj_destroy(vertCRS);
10940 30 : vertCRS = baseCRS;
10941 : }
10942 : }
10943 :
10944 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10945 44 : proj_destroy(vertCRS);
10946 : }
10947 : }
10948 : }
10949 : else
10950 : {
10951 5309 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10952 : }
10953 :
10954 5443 : if (cs)
10955 : {
10956 5443 : const char *pszName = nullptr;
10957 5443 : const char *pszOrientation = nullptr;
10958 5443 : double dfConvFactor = 0.0;
10959 5443 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10960 : &pszOrientation, &dfConvFactor, nullptr,
10961 : nullptr, nullptr);
10962 :
10963 5443 : if (pdfConvUnit != nullptr)
10964 : {
10965 85 : *pdfConvUnit = dfConvFactor;
10966 : }
10967 :
10968 5443 : if (pszName && pszOrientation)
10969 : {
10970 5443 : d->m_osAxisName[iAxis] = pszName;
10971 5443 : if (peOrientation)
10972 : {
10973 5350 : if (EQUAL(pszOrientation, "NORTH"))
10974 3420 : *peOrientation = OAO_North;
10975 1930 : else if (EQUAL(pszOrientation, "EAST"))
10976 1888 : *peOrientation = OAO_East;
10977 42 : else if (EQUAL(pszOrientation, "SOUTH"))
10978 31 : *peOrientation = OAO_South;
10979 11 : else if (EQUAL(pszOrientation, "WEST"))
10980 0 : *peOrientation = OAO_West;
10981 11 : else if (EQUAL(pszOrientation, "UP"))
10982 1 : *peOrientation = OAO_Up;
10983 10 : else if (EQUAL(pszOrientation, "DOWN"))
10984 0 : *peOrientation = OAO_Down;
10985 : }
10986 5443 : proj_destroy(cs);
10987 5443 : d->undoDemoteFromBoundCRS();
10988 5443 : return d->m_osAxisName[iAxis].c_str();
10989 : }
10990 0 : proj_destroy(cs);
10991 : }
10992 0 : d->undoDemoteFromBoundCRS();
10993 : }
10994 :
10995 : /* -------------------------------------------------------------------- */
10996 : /* Find the target node. */
10997 : /* -------------------------------------------------------------------- */
10998 0 : const OGR_SRSNode *poNode = nullptr;
10999 :
11000 0 : if (pszTargetKey == nullptr)
11001 0 : poNode = GetRoot();
11002 : else
11003 0 : poNode = GetAttrNode(pszTargetKey);
11004 :
11005 0 : if (poNode == nullptr)
11006 0 : return nullptr;
11007 :
11008 : /* -------------------------------------------------------------------- */
11009 : /* Find desired child AXIS. */
11010 : /* -------------------------------------------------------------------- */
11011 0 : const OGR_SRSNode *poAxis = nullptr;
11012 0 : const int nChildCount = poNode->GetChildCount();
11013 :
11014 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11015 : {
11016 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11017 :
11018 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11019 0 : continue;
11020 :
11021 0 : if (iAxis == 0)
11022 : {
11023 0 : poAxis = poChild;
11024 0 : break;
11025 : }
11026 0 : iAxis--;
11027 : }
11028 :
11029 0 : if (poAxis == nullptr)
11030 0 : return nullptr;
11031 :
11032 0 : if (poAxis->GetChildCount() < 2)
11033 0 : return nullptr;
11034 :
11035 : /* -------------------------------------------------------------------- */
11036 : /* Extract name and orientation if possible. */
11037 : /* -------------------------------------------------------------------- */
11038 0 : if (peOrientation != nullptr)
11039 : {
11040 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11041 :
11042 0 : if (EQUAL(pszOrientation, "NORTH"))
11043 0 : *peOrientation = OAO_North;
11044 0 : else if (EQUAL(pszOrientation, "EAST"))
11045 0 : *peOrientation = OAO_East;
11046 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11047 0 : *peOrientation = OAO_South;
11048 0 : else if (EQUAL(pszOrientation, "WEST"))
11049 0 : *peOrientation = OAO_West;
11050 0 : else if (EQUAL(pszOrientation, "UP"))
11051 0 : *peOrientation = OAO_Up;
11052 0 : else if (EQUAL(pszOrientation, "DOWN"))
11053 0 : *peOrientation = OAO_Down;
11054 0 : else if (EQUAL(pszOrientation, "OTHER"))
11055 0 : *peOrientation = OAO_Other;
11056 : else
11057 : {
11058 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11059 : pszOrientation);
11060 : }
11061 : }
11062 :
11063 0 : return poAxis->GetChild(0)->GetValue();
11064 : }
11065 :
11066 : /************************************************************************/
11067 : /* OSRGetAxis() */
11068 : /************************************************************************/
11069 :
11070 : /**
11071 : * \brief Fetch the orientation of one axis.
11072 : *
11073 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11074 : */
11075 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11076 : int iAxis, OGRAxisOrientation *peOrientation)
11077 :
11078 : {
11079 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11080 :
11081 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11082 : }
11083 :
11084 : /************************************************************************/
11085 : /* OSRAxisEnumToName() */
11086 : /************************************************************************/
11087 :
11088 : /**
11089 : * \brief Return the string representation for the OGRAxisOrientation
11090 : * enumeration.
11091 : *
11092 : * For example "NORTH" for OAO_North.
11093 : *
11094 : * @return an internal string
11095 : */
11096 392 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11097 :
11098 : {
11099 392 : if (eOrientation == OAO_North)
11100 196 : return "NORTH";
11101 196 : if (eOrientation == OAO_East)
11102 196 : return "EAST";
11103 0 : if (eOrientation == OAO_South)
11104 0 : return "SOUTH";
11105 0 : if (eOrientation == OAO_West)
11106 0 : return "WEST";
11107 0 : if (eOrientation == OAO_Up)
11108 0 : return "UP";
11109 0 : if (eOrientation == OAO_Down)
11110 0 : return "DOWN";
11111 0 : if (eOrientation == OAO_Other)
11112 0 : return "OTHER";
11113 :
11114 0 : return "UNKNOWN";
11115 : }
11116 :
11117 : /************************************************************************/
11118 : /* SetAxes() */
11119 : /************************************************************************/
11120 :
11121 : /**
11122 : * \brief Set the axes for a coordinate system.
11123 : *
11124 : * Set the names, and orientations of the axes for either a projected
11125 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11126 : *
11127 : * This method is equivalent to the C function OSRSetAxes().
11128 : *
11129 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11130 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11131 : * @param eXAxisOrientation normally OAO_East.
11132 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11133 : * @param eYAxisOrientation normally OAO_North.
11134 : *
11135 : * @return OGRERR_NONE on success or an error code.
11136 : */
11137 :
11138 196 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11139 : const char *pszXAxisName,
11140 : OGRAxisOrientation eXAxisOrientation,
11141 : const char *pszYAxisName,
11142 : OGRAxisOrientation eYAxisOrientation)
11143 :
11144 : {
11145 392 : TAKE_OPTIONAL_LOCK();
11146 :
11147 : /* -------------------------------------------------------------------- */
11148 : /* Find the target node. */
11149 : /* -------------------------------------------------------------------- */
11150 196 : OGR_SRSNode *poNode = nullptr;
11151 :
11152 196 : if (pszTargetKey == nullptr)
11153 196 : poNode = GetRoot();
11154 : else
11155 0 : poNode = GetAttrNode(pszTargetKey);
11156 :
11157 196 : if (poNode == nullptr)
11158 0 : return OGRERR_FAILURE;
11159 :
11160 : /* -------------------------------------------------------------------- */
11161 : /* Strip any existing AXIS children. */
11162 : /* -------------------------------------------------------------------- */
11163 588 : while (poNode->FindChild("AXIS") >= 0)
11164 392 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11165 :
11166 : /* -------------------------------------------------------------------- */
11167 : /* Insert desired axes */
11168 : /* -------------------------------------------------------------------- */
11169 196 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11170 :
11171 196 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11172 196 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11173 :
11174 196 : poNode->AddChild(poAxis);
11175 :
11176 196 : poAxis = new OGR_SRSNode("AXIS");
11177 :
11178 196 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11179 196 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11180 :
11181 196 : poNode->AddChild(poAxis);
11182 :
11183 196 : return OGRERR_NONE;
11184 : }
11185 :
11186 : /************************************************************************/
11187 : /* OSRSetAxes() */
11188 : /************************************************************************/
11189 : /**
11190 : * \brief Set the axes for a coordinate system.
11191 : *
11192 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11193 : */
11194 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11195 : const char *pszXAxisName,
11196 : OGRAxisOrientation eXAxisOrientation,
11197 : const char *pszYAxisName,
11198 : OGRAxisOrientation eYAxisOrientation)
11199 : {
11200 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11201 :
11202 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11203 : eXAxisOrientation, pszYAxisName,
11204 0 : eYAxisOrientation);
11205 : }
11206 :
11207 : /************************************************************************/
11208 : /* OSRExportToMICoordSys() */
11209 : /************************************************************************/
11210 : /**
11211 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11212 : *
11213 : * This method is the equivalent of the C++ method
11214 : * OGRSpatialReference::exportToMICoordSys
11215 : */
11216 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11217 :
11218 : {
11219 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11220 :
11221 5 : *ppszReturn = nullptr;
11222 :
11223 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11224 : }
11225 :
11226 : /************************************************************************/
11227 : /* exportToMICoordSys() */
11228 : /************************************************************************/
11229 :
11230 : /**
11231 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11232 : *
11233 : * Note that the returned WKT string should be freed with
11234 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11235 : *
11236 : * This method is the same as the C function OSRExportToMICoordSys().
11237 : *
11238 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11239 : * definition will be assigned.
11240 : *
11241 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11242 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11243 : */
11244 :
11245 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11246 :
11247 : {
11248 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11249 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11250 7 : return OGRERR_NONE;
11251 :
11252 0 : return OGRERR_FAILURE;
11253 : }
11254 :
11255 : /************************************************************************/
11256 : /* OSRImportFromMICoordSys() */
11257 : /************************************************************************/
11258 : /**
11259 : * \brief Import Mapinfo style CoordSys definition.
11260 : *
11261 : * This method is the equivalent of the C++ method
11262 : * OGRSpatialReference::importFromMICoordSys
11263 : */
11264 :
11265 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11266 : const char *pszCoordSys)
11267 :
11268 : {
11269 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11270 :
11271 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11272 : }
11273 :
11274 : /************************************************************************/
11275 : /* importFromMICoordSys() */
11276 : /************************************************************************/
11277 :
11278 : /**
11279 : * \brief Import Mapinfo style CoordSys definition.
11280 : *
11281 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11282 : * definition string.
11283 : *
11284 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11285 : *
11286 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11287 : *
11288 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11289 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11290 : */
11291 :
11292 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11293 :
11294 : {
11295 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11296 :
11297 17 : if (poResult == nullptr)
11298 0 : return OGRERR_FAILURE;
11299 :
11300 17 : *this = *poResult;
11301 17 : delete poResult;
11302 :
11303 17 : return OGRERR_NONE;
11304 : }
11305 :
11306 : /************************************************************************/
11307 : /* OSRCalcInvFlattening() */
11308 : /************************************************************************/
11309 :
11310 : /**
11311 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11312 : *
11313 : * @param dfSemiMajor Semi-major axis length.
11314 : * @param dfSemiMinor Semi-minor axis length.
11315 : *
11316 : * @return inverse flattening, or 0 if both axis are equal.
11317 : * @since GDAL 2.0
11318 : */
11319 :
11320 7465 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11321 : {
11322 7465 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11323 27 : return 0;
11324 7438 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11325 : {
11326 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11327 : "OSRCalcInvFlattening(): Wrong input values");
11328 0 : return 0;
11329 : }
11330 :
11331 7438 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11332 : }
11333 :
11334 : /************************************************************************/
11335 : /* OSRCalcInvFlattening() */
11336 : /************************************************************************/
11337 :
11338 : /**
11339 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11340 : *
11341 : * @param dfSemiMajor Semi-major axis length.
11342 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11343 : *
11344 : * @return semi-minor axis
11345 : * @since GDAL 2.0
11346 : */
11347 :
11348 641 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11349 : double dfInvFlattening)
11350 : {
11351 641 : if (fabs(dfInvFlattening) < 0.000000000001)
11352 101 : return dfSemiMajor;
11353 540 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11354 : {
11355 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11356 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11357 0 : return dfSemiMajor;
11358 : }
11359 :
11360 540 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11361 : }
11362 :
11363 : /************************************************************************/
11364 : /* GetWGS84SRS() */
11365 : /************************************************************************/
11366 :
11367 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11368 : static CPLMutex *hMutex = nullptr;
11369 :
11370 : /**
11371 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11372 : *
11373 : * Note: the instance will have
11374 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11375 : *
11376 : * The reference counter of the returned object is not increased by this
11377 : * operation.
11378 : *
11379 : * @return instance.
11380 : * @since GDAL 2.0
11381 : */
11382 :
11383 823 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11384 : {
11385 823 : CPLMutexHolderD(&hMutex);
11386 823 : if (poSRSWGS84 == nullptr)
11387 : {
11388 4 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11389 4 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11390 : }
11391 1646 : return poSRSWGS84;
11392 : }
11393 :
11394 : /************************************************************************/
11395 : /* CleanupSRSWGS84Mutex() */
11396 : /************************************************************************/
11397 :
11398 927 : static void CleanupSRSWGS84Mutex()
11399 : {
11400 927 : if (hMutex != nullptr)
11401 : {
11402 2 : poSRSWGS84->Release();
11403 2 : poSRSWGS84 = nullptr;
11404 2 : CPLDestroyMutex(hMutex);
11405 2 : hMutex = nullptr;
11406 : }
11407 927 : }
11408 :
11409 : /************************************************************************/
11410 : /* OSRImportFromProj4() */
11411 : /************************************************************************/
11412 : /**
11413 : * \brief Import PROJ coordinate string.
11414 : *
11415 : * This function is the same as OGRSpatialReference::importFromProj4().
11416 : */
11417 186 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11418 :
11419 : {
11420 186 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11421 :
11422 186 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11423 : }
11424 :
11425 : /************************************************************************/
11426 : /* importFromProj4() */
11427 : /************************************************************************/
11428 :
11429 : /**
11430 : * \brief Import PROJ coordinate string.
11431 : *
11432 : * The OGRSpatialReference is initialized from the passed PROJs style
11433 : * coordinate system string.
11434 : *
11435 : * Example:
11436 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11437 : *
11438 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11439 : * a legacy syntax that should be avoided in the future. In particular they will
11440 : * result in CRS objects whose axis order might not correspond to the official
11441 : * EPSG axis order.
11442 : *
11443 : * This method is the equivalent of the C function OSRImportFromProj4().
11444 : *
11445 : * @param pszProj4 the PROJ style string.
11446 : *
11447 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11448 : */
11449 :
11450 627 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11451 :
11452 : {
11453 1254 : TAKE_OPTIONAL_LOCK();
11454 :
11455 627 : if (strlen(pszProj4) >= 10000)
11456 : {
11457 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11458 1 : return OGRERR_CORRUPT_DATA;
11459 : }
11460 :
11461 : /* -------------------------------------------------------------------- */
11462 : /* Clear any existing definition. */
11463 : /* -------------------------------------------------------------------- */
11464 626 : Clear();
11465 :
11466 626 : CPLString osProj4(pszProj4);
11467 626 : if (osProj4.find("type=crs") == std::string::npos)
11468 : {
11469 617 : osProj4 += " +type=crs";
11470 : }
11471 :
11472 628 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11473 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11474 : {
11475 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11476 : "+init=epsg:XXXX syntax is deprecated. It might return "
11477 : "a CRS with a non-EPSG compliant axis order.");
11478 : }
11479 626 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11480 626 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11481 626 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11482 626 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11483 : }
11484 :
11485 : /************************************************************************/
11486 : /* OSRExportToProj4() */
11487 : /************************************************************************/
11488 : /**
11489 : * \brief Export coordinate system in PROJ.4 legacy format.
11490 : *
11491 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11492 : * PROJ >= 6 is significantly different from earlier versions. In particular
11493 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11494 : * will be missing most of the time. PROJ strings to encode CRS should be
11495 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11496 : * is the recommended way.
11497 : *
11498 : * This function is the same as OGRSpatialReference::exportToProj4().
11499 : */
11500 427 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11501 : char **ppszReturn)
11502 :
11503 : {
11504 427 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11505 :
11506 427 : *ppszReturn = nullptr;
11507 :
11508 427 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11509 : }
11510 :
11511 : /************************************************************************/
11512 : /* exportToProj4() */
11513 : /************************************************************************/
11514 :
11515 : /**
11516 : * \brief Export coordinate system in PROJ.4 legacy format.
11517 : *
11518 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11519 : * PROJ >= 6 is significantly different from earlier versions. In particular
11520 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11521 : * will be missing most of the time. PROJ strings to encode CRS should be
11522 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11523 : * representation is the recommended way.
11524 : *
11525 : * Converts the loaded coordinate reference system into PROJ format
11526 : * to the extent possible. The string returned in ppszProj4 should be
11527 : * deallocated by the caller with CPLFree() when no longer needed.
11528 : *
11529 : * LOCAL_CS coordinate systems are not translatable. An empty string
11530 : * will be returned along with OGRERR_NONE.
11531 : *
11532 : * Special processing for Transverse Mercator:
11533 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11534 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11535 : * for the tmerc and utm projection methods, rather than the more accurate
11536 : * method.
11537 : *
11538 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11539 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11540 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11541 : * added. This automatic addition may be disabled by setting the
11542 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11543 : *
11544 : * This method is the equivalent of the C function OSRExportToProj4().
11545 : *
11546 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11547 : * will be assigned.
11548 : *
11549 : * @return OGRERR_NONE on success or an error code on failure.
11550 : */
11551 :
11552 1394 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11553 :
11554 : {
11555 : // In the past calling this method was thread-safe, even if we never
11556 : // guaranteed it. Now proj_as_proj_string() will cache the result
11557 : // internally, so this is no longer thread-safe.
11558 2788 : std::lock_guard oLock(d->m_mutex);
11559 :
11560 1394 : d->refreshProjObj();
11561 1394 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11562 : {
11563 25 : *ppszProj4 = CPLStrdup("");
11564 25 : return OGRERR_FAILURE;
11565 : }
11566 :
11567 : // OSR_USE_ETMERC is here just for legacy
11568 1369 : bool bForceApproxTMerc = false;
11569 1369 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11570 1369 : if (pszUseETMERC && pszUseETMERC[0])
11571 : {
11572 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11573 : "OSR_USE_ETMERC is a legacy configuration option, which "
11574 : "now has only effect when set to NO (YES is the default). "
11575 : "Use OSR_USE_APPROX_TMERC=YES instead");
11576 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11577 : }
11578 : else
11579 : {
11580 : const char *pszUseApproxTMERC =
11581 1369 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11582 1369 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11583 : {
11584 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11585 : }
11586 : }
11587 1369 : const char *options[] = {
11588 1369 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11589 :
11590 1369 : const char *projString = proj_as_proj_string(
11591 1369 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11592 :
11593 1369 : PJ *boundCRS = nullptr;
11594 2734 : if (projString &&
11595 1365 : (strstr(projString, "+datum=") == nullptr ||
11596 2744 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11597 535 : CPLTestBool(
11598 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11599 : {
11600 535 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11601 535 : d->getPROJContext(), d->m_pj_crs, true,
11602 535 : strstr(projString, "+datum=") == nullptr);
11603 535 : if (boundCRS)
11604 : {
11605 216 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11606 : PJ_PROJ_4, options);
11607 : }
11608 : }
11609 :
11610 1369 : if (projString == nullptr)
11611 : {
11612 4 : *ppszProj4 = CPLStrdup("");
11613 4 : proj_destroy(boundCRS);
11614 4 : return OGRERR_FAILURE;
11615 : }
11616 1365 : *ppszProj4 = CPLStrdup(projString);
11617 1365 : proj_destroy(boundCRS);
11618 1365 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11619 1365 : if (pszTypeCrs)
11620 1365 : *pszTypeCrs = '\0';
11621 1365 : return OGRERR_NONE;
11622 : }
11623 :
11624 : /************************************************************************/
11625 : /* morphToESRI() */
11626 : /************************************************************************/
11627 : /**
11628 : * \brief Convert in place to ESRI WKT format.
11629 : *
11630 : * The value nodes of this coordinate system are modified in various manners
11631 : * more closely map onto the ESRI concept of WKT format. This includes
11632 : * renaming a variety of projections and arguments, and stripping out
11633 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11634 : *
11635 : * \note Since GDAL 3.0, this function has only user-visible effects at
11636 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11637 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11638 : *
11639 : * This does the same as the C function OSRMorphToESRI().
11640 : *
11641 : * @return OGRERR_NONE unless something goes badly wrong.
11642 : * @deprecated
11643 : */
11644 :
11645 261 : OGRErr OGRSpatialReference::morphToESRI()
11646 :
11647 : {
11648 261 : TAKE_OPTIONAL_LOCK();
11649 :
11650 261 : d->refreshProjObj();
11651 261 : d->setMorphToESRI(true);
11652 :
11653 522 : return OGRERR_NONE;
11654 : }
11655 :
11656 : /************************************************************************/
11657 : /* OSRMorphToESRI() */
11658 : /************************************************************************/
11659 :
11660 : /**
11661 : * \brief Convert in place to ESRI WKT format.
11662 : *
11663 : * This function is the same as the C++ method
11664 : * OGRSpatialReference::morphToESRI().
11665 : */
11666 102 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11667 :
11668 : {
11669 102 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11670 :
11671 102 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11672 : }
11673 :
11674 : /************************************************************************/
11675 : /* morphFromESRI() */
11676 : /************************************************************************/
11677 :
11678 : /**
11679 : * \brief Convert in place from ESRI WKT format.
11680 : *
11681 : * The value notes of this coordinate system are modified in various manners
11682 : * to adhere more closely to the WKT standard. This mostly involves
11683 : * translating a variety of ESRI names for projections, arguments and
11684 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11685 : * translation of EPSG to WKT for the CT specification.
11686 : *
11687 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11688 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11689 : * effect is to undo the effect of a potential prior call to morphToESRI().
11690 : *
11691 : * This does the same as the C function OSRMorphFromESRI().
11692 : *
11693 : * @return OGRERR_NONE unless something goes badly wrong.
11694 : * @deprecated
11695 : */
11696 :
11697 21 : OGRErr OGRSpatialReference::morphFromESRI()
11698 :
11699 : {
11700 21 : TAKE_OPTIONAL_LOCK();
11701 :
11702 21 : d->refreshProjObj();
11703 21 : d->setMorphToESRI(false);
11704 :
11705 42 : return OGRERR_NONE;
11706 : }
11707 :
11708 : /************************************************************************/
11709 : /* OSRMorphFromESRI() */
11710 : /************************************************************************/
11711 :
11712 : /**
11713 : * \brief Convert in place from ESRI WKT format.
11714 : *
11715 : * This function is the same as the C++ method
11716 : * OGRSpatialReference::morphFromESRI().
11717 : */
11718 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11719 :
11720 : {
11721 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11722 :
11723 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11724 : }
11725 :
11726 : /************************************************************************/
11727 : /* FindMatches() */
11728 : /************************************************************************/
11729 :
11730 : /**
11731 : * \brief Try to identify a match between the passed SRS and a related SRS
11732 : * in a catalog.
11733 : *
11734 : * Matching may be partial, or may fail.
11735 : * Returned entries will be sorted by decreasing match confidence (first
11736 : * entry has the highest match confidence).
11737 : *
11738 : * The exact way matching is done may change in future versions. Starting with
11739 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11740 : *
11741 : * This method is the same as OSRFindMatches().
11742 : *
11743 : * @param papszOptions NULL terminated list of options or NULL
11744 : * @param pnEntries Output parameter. Number of values in the returned array.
11745 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11746 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11747 : * indicate the confidence in the match. 100 is the highest confidence level.
11748 : * The array must be freed with CPLFree().
11749 : *
11750 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11751 : * with OSRFreeSRSArray()
11752 : *
11753 : * @since GDAL 2.3
11754 : *
11755 : * @see OGRSpatialReference::FindBestMatch()
11756 : */
11757 : OGRSpatialReferenceH *
11758 1262 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11759 : int **ppanMatchConfidence) const
11760 : {
11761 2524 : TAKE_OPTIONAL_LOCK();
11762 :
11763 1262 : CPL_IGNORE_RET_VAL(papszOptions);
11764 :
11765 1262 : if (pnEntries)
11766 1262 : *pnEntries = 0;
11767 1262 : if (ppanMatchConfidence)
11768 1262 : *ppanMatchConfidence = nullptr;
11769 :
11770 1262 : d->refreshProjObj();
11771 1262 : if (!d->m_pj_crs)
11772 0 : return nullptr;
11773 :
11774 1262 : int *panConfidence = nullptr;
11775 1262 : auto ctxt = d->getPROJContext();
11776 : auto list =
11777 1262 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11778 1262 : if (!list)
11779 0 : return nullptr;
11780 :
11781 1262 : const int nMatches = proj_list_get_count(list);
11782 :
11783 1262 : if (pnEntries)
11784 1262 : *pnEntries = static_cast<int>(nMatches);
11785 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11786 1262 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11787 1262 : if (ppanMatchConfidence)
11788 : {
11789 1262 : *ppanMatchConfidence =
11790 1262 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11791 : }
11792 :
11793 1262 : bool bSortAgain = false;
11794 :
11795 3917 : for (int i = 0; i < nMatches; i++)
11796 : {
11797 2655 : PJ *obj = proj_list_get(ctxt, list, i);
11798 2655 : CPLAssert(obj);
11799 2655 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11800 2655 : poSRS->d->setPjCRS(obj);
11801 2655 : pahRet[i] = ToHandle(poSRS);
11802 :
11803 : // Identify matches that only differ by axis order
11804 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11805 2673 : poSRS->GetAxesCount() == 2 &&
11806 2664 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11807 : {
11808 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11809 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11810 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11811 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11812 9 : GetAxis(nullptr, 0, &eThisAxis0);
11813 9 : GetAxis(nullptr, 1, &eThisAxis1);
11814 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11815 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11816 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11817 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11818 : {
11819 : auto pj_crs_normalized =
11820 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11821 9 : if (pj_crs_normalized)
11822 : {
11823 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11824 9 : PJ_COMP_EQUIVALENT))
11825 : {
11826 3 : bSortAgain = true;
11827 3 : panConfidence[i] = 90;
11828 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11829 : }
11830 9 : proj_destroy(pj_crs_normalized);
11831 : }
11832 : }
11833 : }
11834 :
11835 2655 : if (ppanMatchConfidence)
11836 2655 : (*ppanMatchConfidence)[i] = panConfidence[i];
11837 : }
11838 :
11839 1262 : if (bSortAgain)
11840 : {
11841 3 : std::vector<int> anIndices;
11842 12 : for (int i = 0; i < nMatches; ++i)
11843 9 : anIndices.push_back(i);
11844 :
11845 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11846 9 : [&panConfidence](int i, int j)
11847 9 : { return panConfidence[i] > panConfidence[j]; });
11848 :
11849 : OGRSpatialReferenceH *pahRetSorted =
11850 : static_cast<OGRSpatialReferenceH *>(
11851 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11852 12 : for (int i = 0; i < nMatches; ++i)
11853 : {
11854 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11855 9 : if (ppanMatchConfidence)
11856 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11857 : }
11858 3 : CPLFree(pahRet);
11859 3 : pahRet = pahRetSorted;
11860 : }
11861 :
11862 1262 : pahRet[nMatches] = nullptr;
11863 1262 : proj_list_destroy(list);
11864 1262 : proj_int_list_destroy(panConfidence);
11865 :
11866 1262 : return pahRet;
11867 : }
11868 :
11869 : /************************************************************************/
11870 : /* importFromEPSGA() */
11871 : /************************************************************************/
11872 :
11873 : /**
11874 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11875 : * code.
11876 : *
11877 : * This method will initialize the spatial reference based on the
11878 : * passed in EPSG CRS code found in the PROJ database.
11879 : *
11880 : * Since GDAL 3.0, this method is identical to importFromEPSG().
11881 : *
11882 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11883 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11884 : * such method available for the CRS. This behavior might not always be
11885 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11886 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11887 : * The AddGuessedTOWGS84() method can also be used for that purpose.
11888 : *
11889 : * The method will also by default substitute a deprecated EPSG code by its
11890 : * non-deprecated replacement. If this behavior is not desired, the
11891 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11892 : *
11893 : * This method is the same as the C function OSRImportFromEPSGA().
11894 : *
11895 : * @param nCode a CRS code.
11896 : *
11897 : * @return OGRERR_NONE on success, or an error code on failure.
11898 : */
11899 :
11900 35353 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11901 :
11902 : {
11903 70706 : TAKE_OPTIONAL_LOCK();
11904 :
11905 35353 : Clear();
11906 :
11907 : const char *pszUseNonDeprecated =
11908 35353 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11909 : const bool bUseNonDeprecated =
11910 35353 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11911 35353 : const bool bAddTOWGS84 = CPLTestBool(
11912 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11913 35353 : auto tlsCache = OSRGetProjTLSCache();
11914 35353 : if (tlsCache)
11915 : {
11916 : auto cachedObj =
11917 35353 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11918 35353 : if (cachedObj)
11919 : {
11920 27311 : d->setPjCRS(cachedObj);
11921 27311 : return OGRERR_NONE;
11922 : }
11923 : }
11924 :
11925 16084 : CPLString osCode;
11926 8042 : osCode.Printf("%d", nCode);
11927 : PJ *obj;
11928 8042 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
11929 8042 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
11930 : {
11931 8038 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11932 : osCode.c_str(), PJ_CATEGORY_CRS, true,
11933 : nullptr);
11934 8038 : if (!obj)
11935 : {
11936 23 : return OGRERR_FAILURE;
11937 : }
11938 : }
11939 : else
11940 : {
11941 : // Likely to be an ESRI CRS...
11942 4 : CPLErr eLastErrorType = CE_None;
11943 4 : CPLErrorNum eLastErrorNum = CPLE_None;
11944 4 : std::string osLastErrorMsg;
11945 4 : bool bIsESRI = false;
11946 : {
11947 8 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
11948 4 : CPLErrorReset();
11949 4 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11950 : osCode.c_str(), PJ_CATEGORY_CRS,
11951 : true, nullptr);
11952 4 : if (!obj)
11953 : {
11954 1 : eLastErrorType = CPLGetLastErrorType();
11955 1 : eLastErrorNum = CPLGetLastErrorNo();
11956 1 : osLastErrorMsg = CPLGetLastErrorMsg();
11957 1 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
11958 : osCode.c_str(), PJ_CATEGORY_CRS,
11959 : true, nullptr);
11960 1 : if (obj)
11961 0 : bIsESRI = true;
11962 : }
11963 : }
11964 4 : if (!obj)
11965 : {
11966 1 : if (eLastErrorType != CE_None)
11967 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
11968 : osLastErrorMsg.c_str());
11969 1 : return OGRERR_FAILURE;
11970 : }
11971 3 : if (bIsESRI)
11972 : {
11973 0 : CPLError(CE_Warning, CPLE_AppDefined,
11974 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
11975 : "Assuming ESRI:%d was meant",
11976 : nCode, nCode, nCode);
11977 : }
11978 : }
11979 :
11980 8018 : if (bUseNonDeprecated && proj_is_deprecated(obj))
11981 : {
11982 409 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11983 409 : if (list)
11984 : {
11985 409 : const auto count = proj_list_get_count(list);
11986 409 : if (count == 1)
11987 : {
11988 : auto nonDeprecated =
11989 359 : proj_list_get(d->getPROJContext(), list, 0);
11990 359 : if (nonDeprecated)
11991 : {
11992 359 : if (pszUseNonDeprecated == nullptr)
11993 : {
11994 : const char *pszNewAuth =
11995 359 : proj_get_id_auth_name(nonDeprecated, 0);
11996 : const char *pszNewCode =
11997 359 : proj_get_id_code(nonDeprecated, 0);
11998 359 : CPLError(CE_Warning, CPLE_AppDefined,
11999 : "CRS EPSG:%d is deprecated. "
12000 : "Its non-deprecated replacement %s:%s "
12001 : "will be used instead. "
12002 : "To use the original CRS, set the "
12003 : "OSR_USE_NON_DEPRECATED "
12004 : "configuration option to NO.",
12005 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12006 : pszNewCode ? pszNewCode : "(null)");
12007 : }
12008 359 : proj_destroy(obj);
12009 359 : obj = nonDeprecated;
12010 : }
12011 : }
12012 : }
12013 409 : proj_list_destroy(list);
12014 : }
12015 :
12016 8018 : if (bAddTOWGS84)
12017 : {
12018 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12019 : obj, nullptr);
12020 1 : if (boundCRS)
12021 : {
12022 1 : proj_destroy(obj);
12023 1 : obj = boundCRS;
12024 : }
12025 : }
12026 :
12027 8018 : d->setPjCRS(obj);
12028 :
12029 8018 : if (tlsCache)
12030 : {
12031 8018 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12032 : obj);
12033 : }
12034 :
12035 8018 : return OGRERR_NONE;
12036 : }
12037 :
12038 : /************************************************************************/
12039 : /* AddGuessedTOWGS84() */
12040 : /************************************************************************/
12041 :
12042 : /**
12043 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12044 : * to WGS84.
12045 : *
12046 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12047 : * to WGS84 when there is one and only one such method available for the CRS.
12048 : * Note: this is more restrictive to how GDAL < 3 worked.
12049 : *
12050 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12051 : *
12052 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12053 : * already a transformation to WGS84 or none matching could be found).
12054 : *
12055 : * @since GDAL 3.0.3
12056 : */
12057 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12058 : {
12059 36 : TAKE_OPTIONAL_LOCK();
12060 :
12061 18 : d->refreshProjObj();
12062 18 : if (!d->m_pj_crs)
12063 0 : return OGRERR_FAILURE;
12064 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12065 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12066 18 : if (!boundCRS)
12067 : {
12068 0 : return OGRERR_FAILURE;
12069 : }
12070 18 : d->setPjCRS(boundCRS);
12071 18 : return OGRERR_NONE;
12072 : }
12073 :
12074 : /************************************************************************/
12075 : /* OSRImportFromEPSGA() */
12076 : /************************************************************************/
12077 :
12078 : /**
12079 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12080 : * to WGS84.
12081 : *
12082 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12083 : *
12084 : * @since GDAL 3.0.3
12085 : */
12086 :
12087 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12088 :
12089 : {
12090 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12091 :
12092 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12093 : }
12094 :
12095 : /************************************************************************/
12096 : /* OSRImportFromEPSGA() */
12097 : /************************************************************************/
12098 :
12099 : /**
12100 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12101 : * code.
12102 : *
12103 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12104 : */
12105 :
12106 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12107 :
12108 : {
12109 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12110 :
12111 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12112 : }
12113 :
12114 : /************************************************************************/
12115 : /* importFromEPSG() */
12116 : /************************************************************************/
12117 :
12118 : /**
12119 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12120 : * code.
12121 : *
12122 : * This method will initialize the spatial reference based on the
12123 : * passed in EPSG CRS code found in the PROJ database.
12124 : *
12125 : * This method is the same as the C function OSRImportFromEPSG().
12126 : *
12127 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12128 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12129 : * such method available for the CRS. This behavior might not always be
12130 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12131 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12132 : *
12133 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12134 : *
12135 : * @return OGRERR_NONE on success, or an error code on failure.
12136 : */
12137 :
12138 34530 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12139 :
12140 : {
12141 34530 : return importFromEPSGA(nCode);
12142 : }
12143 :
12144 : /************************************************************************/
12145 : /* OSRImportFromEPSG() */
12146 : /************************************************************************/
12147 :
12148 : /**
12149 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12150 : * code.
12151 : *
12152 : * This function is the same as OGRSpatialReference::importFromEPSG().
12153 : */
12154 :
12155 1414 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12156 :
12157 : {
12158 1414 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12159 :
12160 1414 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12161 : }
12162 :
12163 : /************************************************************************/
12164 : /* EPSGTreatsAsLatLong() */
12165 : /************************************************************************/
12166 :
12167 : /**
12168 : * \brief This method returns TRUE if this geographic coordinate
12169 : * system should be treated as having lat/long coordinate ordering.
12170 : *
12171 : * Currently this returns TRUE for all geographic coordinate systems
12172 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12173 : * also checked that the CRS had belonged to EPSG authority, but this check
12174 : * has now been removed).
12175 : *
12176 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12177 : * geographic CRS imported with importFromEPSG() would cause this method to
12178 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12179 : * is now equivalent to importFromEPSGA().
12180 : *
12181 : * FALSE will be returned for all coordinate systems that are not geographic,
12182 : * or whose axes ordering is not latitude, longitude.
12183 : *
12184 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12185 : *
12186 : * @return TRUE or FALSE.
12187 : */
12188 :
12189 712 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12190 :
12191 : {
12192 1424 : TAKE_OPTIONAL_LOCK();
12193 :
12194 712 : if (!IsGeographic())
12195 566 : return FALSE;
12196 :
12197 146 : d->demoteFromBoundCRS();
12198 :
12199 146 : bool ret = false;
12200 146 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12201 : {
12202 : auto horizCRS =
12203 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12204 3 : if (horizCRS)
12205 : {
12206 : auto cs =
12207 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12208 3 : if (cs)
12209 : {
12210 3 : const char *pszDirection = nullptr;
12211 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12212 : nullptr, &pszDirection, nullptr,
12213 3 : nullptr, nullptr, nullptr))
12214 : {
12215 3 : if (EQUAL(pszDirection, "north"))
12216 : {
12217 3 : ret = true;
12218 : }
12219 : }
12220 :
12221 3 : proj_destroy(cs);
12222 : }
12223 :
12224 3 : proj_destroy(horizCRS);
12225 : }
12226 : }
12227 : else
12228 : {
12229 : auto cs =
12230 143 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12231 143 : if (cs)
12232 : {
12233 143 : const char *pszDirection = nullptr;
12234 143 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12235 : nullptr, &pszDirection, nullptr, nullptr,
12236 143 : nullptr, nullptr))
12237 : {
12238 143 : if (EQUAL(pszDirection, "north"))
12239 : {
12240 116 : ret = true;
12241 : }
12242 : }
12243 :
12244 143 : proj_destroy(cs);
12245 : }
12246 : }
12247 146 : d->undoDemoteFromBoundCRS();
12248 :
12249 146 : return ret;
12250 : }
12251 :
12252 : /************************************************************************/
12253 : /* OSREPSGTreatsAsLatLong() */
12254 : /************************************************************************/
12255 :
12256 : /**
12257 : * \brief This function returns TRUE if this geographic coordinate
12258 : * system should be treated as having lat/long coordinate ordering.
12259 : *
12260 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12261 : */
12262 :
12263 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12264 :
12265 : {
12266 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12267 :
12268 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12269 : }
12270 :
12271 : /************************************************************************/
12272 : /* EPSGTreatsAsNorthingEasting() */
12273 : /************************************************************************/
12274 :
12275 : /**
12276 : * \brief This method returns TRUE if this projected coordinate
12277 : * system should be treated as having northing/easting coordinate ordering.
12278 : *
12279 : * Currently this returns TRUE for all projected coordinate systems
12280 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12281 : * also checked that the CRS had belonged to EPSG authority, but this check
12282 : * has now been removed).
12283 : *
12284 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12285 : * projected CRS with northing, easting axis order imported with
12286 : * importFromEPSG() would cause this method to
12287 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12288 : * is now equivalent to importFromEPSGA().
12289 : *
12290 : * FALSE will be returned for all coordinate systems that are not projected,
12291 : * or whose axes ordering is not northing, easting.
12292 : *
12293 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12294 : *
12295 : * @return TRUE or FALSE.
12296 : *
12297 : * @since OGR 1.10.0
12298 : */
12299 :
12300 603 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12301 :
12302 : {
12303 1206 : TAKE_OPTIONAL_LOCK();
12304 :
12305 603 : if (!IsProjected())
12306 24 : return FALSE;
12307 :
12308 579 : d->demoteFromBoundCRS();
12309 : PJ *projCRS;
12310 579 : const auto ctxt = d->getPROJContext();
12311 579 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12312 : {
12313 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12314 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12315 : {
12316 0 : d->undoDemoteFromBoundCRS();
12317 0 : proj_destroy(projCRS);
12318 0 : return FALSE;
12319 : }
12320 : }
12321 : else
12322 : {
12323 575 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12324 : }
12325 :
12326 579 : bool ret = false;
12327 579 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12328 579 : proj_destroy(projCRS);
12329 579 : d->undoDemoteFromBoundCRS();
12330 :
12331 579 : if (cs)
12332 : {
12333 579 : ret = isNorthEastAxisOrder(ctxt, cs);
12334 579 : proj_destroy(cs);
12335 : }
12336 :
12337 579 : return ret;
12338 : }
12339 :
12340 : /************************************************************************/
12341 : /* OSREPSGTreatsAsNorthingEasting() */
12342 : /************************************************************************/
12343 :
12344 : /**
12345 : * \brief This function returns TRUE if this projected coordinate
12346 : * system should be treated as having northing/easting coordinate ordering.
12347 : *
12348 : * This function is the same as
12349 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12350 : *
12351 : * @since OGR 1.10.0
12352 : */
12353 :
12354 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12355 :
12356 : {
12357 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12358 :
12359 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12360 : }
12361 :
12362 : /************************************************************************/
12363 : /* ImportFromESRIWisconsinWKT() */
12364 : /* */
12365 : /* Search a ESRI State Plane WKT and import it. */
12366 : /************************************************************************/
12367 :
12368 : // This is only used by the HFA driver and somewhat dubious we really need that
12369 : // Coming from an old ESRI merge
12370 :
12371 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12372 : double centralMeridian,
12373 : double latOfOrigin,
12374 : const char *unitsName,
12375 : const char *crsName)
12376 : {
12377 2 : TAKE_OPTIONAL_LOCK();
12378 :
12379 1 : if (centralMeridian < -93 || centralMeridian > -87)
12380 0 : return OGRERR_FAILURE;
12381 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12382 0 : return OGRERR_FAILURE;
12383 :
12384 : // If the CS name is known.
12385 1 : if (!prjName && !unitsName && crsName)
12386 : {
12387 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12388 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12389 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12390 0 : if (list)
12391 : {
12392 0 : if (proj_list_get_count(list) == 1)
12393 : {
12394 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12395 0 : if (crs)
12396 : {
12397 0 : Clear();
12398 0 : d->setPjCRS(crs);
12399 0 : proj_list_destroy(list);
12400 0 : return OGRERR_NONE;
12401 : }
12402 : }
12403 0 : proj_list_destroy(list);
12404 : }
12405 0 : return OGRERR_FAILURE;
12406 : }
12407 :
12408 1 : if (prjName == nullptr || unitsName == nullptr)
12409 : {
12410 0 : return OGRERR_FAILURE;
12411 : }
12412 :
12413 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12414 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12415 : "NAD_1983_HARN_WISCRS_", &type, 1,
12416 : true, 0, nullptr);
12417 1 : if (list)
12418 : {
12419 1 : const auto listSize = proj_list_get_count(list);
12420 8 : for (int i = 0; i < listSize; i++)
12421 : {
12422 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12423 8 : if (!crs)
12424 : {
12425 7 : continue;
12426 : }
12427 :
12428 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12429 8 : if (!conv)
12430 : {
12431 0 : proj_destroy(crs);
12432 0 : continue;
12433 : }
12434 8 : const char *pszMethodCode = nullptr;
12435 8 : proj_coordoperation_get_method_info(
12436 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12437 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12438 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12439 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12440 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12441 : nMethodCode ==
12442 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12443 : {
12444 3 : proj_destroy(crs);
12445 3 : proj_destroy(conv);
12446 3 : continue;
12447 : }
12448 :
12449 : auto coordSys =
12450 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12451 5 : if (!coordSys)
12452 : {
12453 0 : proj_destroy(crs);
12454 0 : proj_destroy(conv);
12455 0 : continue;
12456 : }
12457 :
12458 5 : double dfConvFactor = 0.0;
12459 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12460 : nullptr, nullptr, &dfConvFactor, nullptr,
12461 : nullptr, nullptr);
12462 5 : proj_destroy(coordSys);
12463 :
12464 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12465 1 : (!EQUAL(unitsName, "meters") &&
12466 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12467 : 1e-10))
12468 : {
12469 4 : proj_destroy(crs);
12470 4 : proj_destroy(conv);
12471 4 : continue;
12472 : }
12473 :
12474 1 : int idx_lat = proj_coordoperation_get_param_index(
12475 : d->getPROJContext(), conv,
12476 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12477 1 : double valueLat = -1000;
12478 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12479 : nullptr, nullptr, nullptr, &valueLat,
12480 : nullptr, nullptr, nullptr, nullptr,
12481 : nullptr, nullptr);
12482 1 : int idx_lon = proj_coordoperation_get_param_index(
12483 : d->getPROJContext(), conv,
12484 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12485 1 : double valueLong = -1000;
12486 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12487 : nullptr, nullptr, nullptr, &valueLong,
12488 : nullptr, nullptr, nullptr, nullptr,
12489 : nullptr, nullptr);
12490 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12491 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12492 : {
12493 1 : Clear();
12494 1 : d->setPjCRS(crs);
12495 1 : proj_list_destroy(list);
12496 1 : proj_destroy(conv);
12497 1 : return OGRERR_NONE;
12498 : }
12499 :
12500 0 : proj_destroy(crs);
12501 0 : proj_destroy(conv);
12502 : }
12503 0 : proj_list_destroy(list);
12504 : }
12505 :
12506 0 : return OGRERR_FAILURE;
12507 : }
12508 :
12509 : /************************************************************************/
12510 : /* GetAxisMappingStrategy() */
12511 : /************************************************************************/
12512 :
12513 : /** \brief Return the data axis to CRS axis mapping strategy.
12514 : *
12515 : * <ul>
12516 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12517 : * lat/long order, the data will still be long/lat ordered. Similarly for
12518 : * a projected CRS with northing/easting order, the data will still be
12519 : * easting/northing ordered.
12520 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12521 : * the CRS axis.
12522 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12523 : * SetDataAxisToSRSAxisMapping()
12524 : * </ul>
12525 : * @return the data axis to CRS axis mapping strategy.
12526 : * @since GDAL 3.0
12527 : */
12528 72 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12529 : {
12530 72 : TAKE_OPTIONAL_LOCK();
12531 :
12532 144 : return d->m_axisMappingStrategy;
12533 : }
12534 :
12535 : /************************************************************************/
12536 : /* OSRGetAxisMappingStrategy() */
12537 : /************************************************************************/
12538 :
12539 : /** \brief Return the data axis to CRS axis mapping strategy.
12540 : *
12541 : * See OGRSpatialReference::GetAxisMappingStrategy()
12542 : * @since GDAL 3.0
12543 : */
12544 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12545 : {
12546 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12547 :
12548 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12549 : }
12550 :
12551 : /************************************************************************/
12552 : /* SetAxisMappingStrategy() */
12553 : /************************************************************************/
12554 :
12555 : /** \brief Set the data axis to CRS axis mapping strategy.
12556 : *
12557 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12558 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12559 : * later being the default value when the option is not set) to control the
12560 : * value of the data axis to CRS axis mapping strategy when a
12561 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12562 : * override this default value.
12563 : *
12564 : * See OGRSpatialReference::GetAxisMappingStrategy()
12565 : * @since GDAL 3.0
12566 : */
12567 75923 : void OGRSpatialReference::SetAxisMappingStrategy(
12568 : OSRAxisMappingStrategy strategy)
12569 : {
12570 151675 : TAKE_OPTIONAL_LOCK();
12571 :
12572 75819 : d->m_axisMappingStrategy = strategy;
12573 75807 : d->refreshAxisMapping();
12574 75730 : }
12575 :
12576 : /************************************************************************/
12577 : /* OSRSetAxisMappingStrategy() */
12578 : /************************************************************************/
12579 :
12580 : /** \brief Set the data axis to CRS axis mapping strategy.
12581 : *
12582 : * See OGRSpatialReference::SetAxisMappingStrategy()
12583 : * @since GDAL 3.0
12584 : */
12585 821 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12586 : OSRAxisMappingStrategy strategy)
12587 : {
12588 821 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12589 :
12590 821 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12591 : }
12592 :
12593 : /************************************************************************/
12594 : /* GetDataAxisToSRSAxisMapping() */
12595 : /************************************************************************/
12596 :
12597 : /** \brief Return the data axis to SRS axis mapping.
12598 : *
12599 : * The number of elements of the vector will be the number of axis of the CRS.
12600 : * Values start at 1.
12601 : *
12602 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12603 : * for the first axis of the CRS.
12604 : *
12605 : * @since GDAL 3.0
12606 : */
12607 3687850 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12608 : {
12609 3687850 : TAKE_OPTIONAL_LOCK();
12610 :
12611 7375650 : return d->m_axisMapping;
12612 : }
12613 :
12614 : /************************************************************************/
12615 : /* OSRGetDataAxisToSRSAxisMapping() */
12616 : /************************************************************************/
12617 :
12618 : /** \brief Return the data axis to SRS axis mapping.
12619 : *
12620 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12621 : *
12622 : * @since GDAL 3.0
12623 : */
12624 200 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12625 : int *pnCount)
12626 : {
12627 200 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12628 200 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12629 :
12630 : const auto &v =
12631 200 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12632 200 : *pnCount = static_cast<int>(v.size());
12633 200 : return v.data();
12634 : }
12635 :
12636 : /************************************************************************/
12637 : /* SetDataAxisToSRSAxisMapping() */
12638 : /************************************************************************/
12639 :
12640 : /** \brief Set a custom data axis to CRS axis mapping.
12641 : *
12642 : * The number of elements of the mapping vector should be the number of axis
12643 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12644 : * check that, beyond checking there are at least 2 elements, so that this
12645 : * method and setting the CRS can be done in any order).
12646 : * This is taken into account by OGRCoordinateTransformation to transform the
12647 : * order of coordinates to the order expected by the CRS before
12648 : * transformation, and back to the data order after transformation.
12649 : *
12650 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12651 : * axis of the CRS. A negative value can also be used to ask for a sign
12652 : * reversal during coordinate transformation (to deal with northing vs southing,
12653 : * easting vs westing, heights vs depths).
12654 : *
12655 : * When used with OGRCoordinateTransformation,
12656 : * - the only valid values for mapping[0] (data axis number for the first axis
12657 : * of the CRS) are 1, 2, -1, -2.
12658 : * - the only valid values for mapping[1] (data axis number for the second axis
12659 : * of the CRS) are 1, 2, -1, -2.
12660 : * - the only valid values mapping[2] are 3 or -3.
12661 : * Note: this method does not validate the values of mapping[].
12662 : *
12663 : * mapping=[2,1] typically expresses the inversion of axis between the data
12664 : * axis and the CRS axis for a 2D CRS.
12665 : *
12666 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12667 : *
12668 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12669 : *
12670 : * @param mapping The new data axis to CRS axis mapping.
12671 : *
12672 : * @since GDAL 3.0
12673 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12674 : */
12675 6605 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12676 : const std::vector<int> &mapping)
12677 : {
12678 13210 : TAKE_OPTIONAL_LOCK();
12679 :
12680 6605 : if (mapping.size() < 2)
12681 0 : return OGRERR_FAILURE;
12682 6605 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12683 6605 : d->m_axisMapping = mapping;
12684 6605 : return OGRERR_NONE;
12685 : }
12686 :
12687 : /************************************************************************/
12688 : /* OSRSetDataAxisToSRSAxisMapping() */
12689 : /************************************************************************/
12690 :
12691 : /** \brief Set a custom data axis to CRS axis mapping.
12692 : *
12693 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12694 : *
12695 : * This is the same as the C++ method
12696 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12697 : *
12698 : * @since GDAL 3.1
12699 : */
12700 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12701 : int nMappingSize, const int *panMapping)
12702 : {
12703 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12704 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12705 : OGRERR_FAILURE);
12706 :
12707 15 : if (nMappingSize < 0)
12708 0 : return OGRERR_FAILURE;
12709 :
12710 30 : std::vector<int> mapping(nMappingSize);
12711 15 : if (nMappingSize)
12712 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12713 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12714 15 : mapping);
12715 : }
12716 :
12717 : /************************************************************************/
12718 : /* GetAreaOfUse() */
12719 : /************************************************************************/
12720 :
12721 : /** \brief Return the area of use of the CRS.
12722 : *
12723 : * This method is the same as the OSRGetAreaOfUse() function.
12724 : *
12725 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12726 : * longitude, expressed in degree. Might be NULL. If the returned value is
12727 : * -1000, the bounding box is unknown.
12728 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12729 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12730 : * the bounding box is unknown.
12731 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12732 : * longitude, expressed in degree. Might be NULL. If the returned value is
12733 : * -1000, the bounding box is unknown.
12734 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12735 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12736 : * the bounding box is unknown.
12737 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12738 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12739 : * invalidated by further calls.
12740 : * @return true in case of success
12741 : * @since GDAL 3.0
12742 : */
12743 32 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12744 : double *pdfSouthLatitudeDeg,
12745 : double *pdfEastLongitudeDeg,
12746 : double *pdfNorthLatitudeDeg,
12747 : const char **ppszAreaName) const
12748 : {
12749 64 : TAKE_OPTIONAL_LOCK();
12750 :
12751 32 : d->refreshProjObj();
12752 32 : if (!d->m_pj_crs)
12753 : {
12754 0 : return false;
12755 : }
12756 32 : d->demoteFromBoundCRS();
12757 32 : const char *pszAreaName = nullptr;
12758 32 : int bSuccess = proj_get_area_of_use(
12759 32 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12760 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12761 : &pszAreaName);
12762 32 : d->undoDemoteFromBoundCRS();
12763 32 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12764 32 : if (ppszAreaName)
12765 1 : *ppszAreaName = d->m_osAreaName.c_str();
12766 32 : return CPL_TO_BOOL(bSuccess);
12767 : }
12768 :
12769 : /************************************************************************/
12770 : /* GetAreaOfUse() */
12771 : /************************************************************************/
12772 :
12773 : /** \brief Return the area of use of the CRS.
12774 : *
12775 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12776 : *
12777 : * @since GDAL 3.0
12778 : */
12779 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12780 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12781 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12782 : {
12783 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12784 :
12785 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12786 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12787 1 : pdfNorthLatitudeDeg, ppszAreaName);
12788 : }
12789 :
12790 : /************************************************************************/
12791 : /* OSRGetCRSInfoListFromDatabase() */
12792 : /************************************************************************/
12793 :
12794 : /** \brief Enumerate CRS objects from the database.
12795 : *
12796 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12797 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12798 : *
12799 : * @param pszAuthName Authority name, used to restrict the search.
12800 : * Or NULL for all authorities.
12801 : * @param params Additional criteria. Must be set to NULL for now.
12802 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12803 : * the size of the result list. Might be NULL
12804 : * @return an array of OSRCRSInfo* pointers to be freed with
12805 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12806 : *
12807 : * @since GDAL 3.0
12808 : */
12809 : OSRCRSInfo **
12810 12 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12811 : CPL_UNUSED const OSRCRSListParameters *params,
12812 : int *pnOutResultCount)
12813 : {
12814 12 : int nResultCount = 0;
12815 12 : auto projList = proj_get_crs_info_list_from_database(
12816 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12817 12 : if (pnOutResultCount)
12818 12 : *pnOutResultCount = nResultCount;
12819 12 : if (!projList)
12820 : {
12821 0 : return nullptr;
12822 : }
12823 12 : auto res = new OSRCRSInfo *[nResultCount + 1];
12824 56167 : for (int i = 0; i < nResultCount; i++)
12825 : {
12826 56155 : res[i] = new OSRCRSInfo;
12827 112310 : res[i]->pszAuthName = projList[i]->auth_name
12828 56155 : ? CPLStrdup(projList[i]->auth_name)
12829 : : nullptr;
12830 56155 : res[i]->pszCode =
12831 56155 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12832 56155 : res[i]->pszName =
12833 56155 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12834 56155 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12835 56155 : switch (projList[i]->type)
12836 : {
12837 5273 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12838 5273 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12839 5273 : break;
12840 1821 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12841 1821 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12842 1821 : break;
12843 1910 : case PJ_TYPE_GEOCENTRIC_CRS:
12844 1910 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12845 1910 : break;
12846 42888 : case PJ_TYPE_PROJECTED_CRS:
12847 42888 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12848 42888 : break;
12849 1820 : case PJ_TYPE_VERTICAL_CRS:
12850 1820 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12851 1820 : break;
12852 2443 : case PJ_TYPE_COMPOUND_CRS:
12853 2443 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12854 2443 : break;
12855 0 : default:
12856 0 : break;
12857 : }
12858 56155 : res[i]->bDeprecated = projList[i]->deprecated;
12859 56155 : res[i]->bBboxValid = projList[i]->bbox_valid;
12860 56155 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12861 56155 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12862 56155 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12863 56155 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12864 112310 : res[i]->pszAreaName = projList[i]->area_name
12865 56155 : ? CPLStrdup(projList[i]->area_name)
12866 : : nullptr;
12867 56155 : res[i]->pszProjectionMethod =
12868 56155 : projList[i]->projection_method_name
12869 56155 : ? CPLStrdup(projList[i]->projection_method_name)
12870 : : nullptr;
12871 : }
12872 12 : res[nResultCount] = nullptr;
12873 12 : proj_crs_info_list_destroy(projList);
12874 12 : return res;
12875 : }
12876 :
12877 : /************************************************************************/
12878 : /* OSRDestroyCRSInfoList() */
12879 : /************************************************************************/
12880 :
12881 : /** \brief Destroy the result returned by
12882 : * OSRGetCRSInfoListFromDatabase().
12883 : *
12884 : * @since GDAL 3.0
12885 : */
12886 12 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12887 : {
12888 12 : if (list)
12889 : {
12890 56167 : for (int i = 0; list[i] != nullptr; i++)
12891 : {
12892 56155 : CPLFree(list[i]->pszAuthName);
12893 56155 : CPLFree(list[i]->pszCode);
12894 56155 : CPLFree(list[i]->pszName);
12895 56155 : CPLFree(list[i]->pszAreaName);
12896 56155 : CPLFree(list[i]->pszProjectionMethod);
12897 56155 : delete list[i];
12898 : }
12899 12 : delete[] list;
12900 : }
12901 12 : }
12902 :
12903 : /************************************************************************/
12904 : /* OSRGetAuthorityListFromDatabase() */
12905 : /************************************************************************/
12906 :
12907 : /** \brief Return the list of CRS authorities used in the PROJ database.
12908 : *
12909 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
12910 : *
12911 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
12912 : *
12913 : * @return nullptr in case of error, or a NULL terminated list of strings to
12914 : * free with CSLDestroy()
12915 : * @since GDAL 3.10
12916 : */
12917 2 : char **OSRGetAuthorityListFromDatabase()
12918 : {
12919 : PROJ_STRING_LIST list =
12920 2 : proj_get_authorities_from_database(OSRGetProjTLSContext());
12921 2 : if (!list)
12922 : {
12923 0 : return nullptr;
12924 : }
12925 2 : int count = 0;
12926 12 : while (list[count])
12927 10 : ++count;
12928 2 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
12929 12 : for (int i = 0; i < count; ++i)
12930 10 : res[i] = CPLStrdup(list[i]);
12931 2 : proj_string_list_destroy(list);
12932 2 : return res;
12933 : }
12934 :
12935 : /************************************************************************/
12936 : /* UpdateCoordinateSystemFromGeogCRS() */
12937 : /************************************************************************/
12938 :
12939 : /*! @cond Doxygen_Suppress */
12940 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12941 : *
12942 : * @since GDAL 3.1
12943 : */
12944 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12945 : {
12946 1 : TAKE_OPTIONAL_LOCK();
12947 :
12948 1 : d->refreshProjObj();
12949 1 : if (!d->m_pj_crs)
12950 0 : return;
12951 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12952 0 : return;
12953 1 : if (GetAxesCount() == 3)
12954 0 : return;
12955 1 : auto ctxt = d->getPROJContext();
12956 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12957 1 : if (!baseCRS)
12958 0 : return;
12959 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12960 1 : if (!baseCRSCS)
12961 : {
12962 0 : proj_destroy(baseCRS);
12963 0 : return;
12964 : }
12965 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12966 : {
12967 0 : proj_destroy(baseCRSCS);
12968 0 : proj_destroy(baseCRS);
12969 0 : return;
12970 : }
12971 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12972 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12973 : {
12974 0 : proj_destroy(baseCRSCS);
12975 0 : proj_destroy(baseCRS);
12976 0 : proj_destroy(projCS);
12977 0 : return;
12978 : }
12979 :
12980 : PJ_AXIS_DESCRIPTION axis[3];
12981 4 : for (int i = 0; i < 3; i++)
12982 : {
12983 3 : const char *name = nullptr;
12984 3 : const char *abbreviation = nullptr;
12985 3 : const char *direction = nullptr;
12986 3 : double unit_conv_factor = 0;
12987 3 : const char *unit_name = nullptr;
12988 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
12989 : &abbreviation, &direction, &unit_conv_factor,
12990 : &unit_name, nullptr, nullptr);
12991 3 : axis[i].name = CPLStrdup(name);
12992 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
12993 3 : axis[i].direction = CPLStrdup(direction);
12994 3 : axis[i].unit_name = CPLStrdup(unit_name);
12995 3 : axis[i].unit_conv_factor = unit_conv_factor;
12996 3 : axis[i].unit_type = PJ_UT_LINEAR;
12997 : }
12998 1 : proj_destroy(baseCRSCS);
12999 1 : proj_destroy(projCS);
13000 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13001 4 : for (int i = 0; i < 3; i++)
13002 : {
13003 3 : CPLFree(axis[i].name);
13004 3 : CPLFree(axis[i].abbreviation);
13005 3 : CPLFree(axis[i].direction);
13006 3 : CPLFree(axis[i].unit_name);
13007 : }
13008 1 : if (!cs)
13009 : {
13010 0 : proj_destroy(baseCRS);
13011 0 : return;
13012 : }
13013 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13014 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13015 : conversion, cs);
13016 1 : proj_destroy(baseCRS);
13017 1 : proj_destroy(conversion);
13018 1 : proj_destroy(cs);
13019 1 : d->setPjCRS(crs);
13020 : }
13021 :
13022 : /*! @endcond */
13023 :
13024 : /************************************************************************/
13025 : /* PromoteTo3D() */
13026 : /************************************************************************/
13027 :
13028 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13029 : *
13030 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13031 : * units.
13032 : *
13033 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13034 : * be used.
13035 : * @return OGRERR_NONE if no error occurred.
13036 : * @since GDAL 3.1 and PROJ 6.3
13037 : */
13038 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13039 : {
13040 84 : TAKE_OPTIONAL_LOCK();
13041 :
13042 42 : d->refreshProjObj();
13043 42 : if (!d->m_pj_crs)
13044 0 : return OGRERR_FAILURE;
13045 : auto newPj =
13046 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13047 42 : if (!newPj)
13048 0 : return OGRERR_FAILURE;
13049 42 : d->setPjCRS(newPj);
13050 42 : return OGRERR_NONE;
13051 : }
13052 :
13053 : /************************************************************************/
13054 : /* OSRPromoteTo3D() */
13055 : /************************************************************************/
13056 :
13057 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13058 : *
13059 : * See OGRSpatialReference::PromoteTo3D()
13060 : *
13061 : * @since GDAL 3.1 and PROJ 6.3
13062 : */
13063 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13064 : {
13065 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13066 :
13067 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13068 : }
13069 :
13070 : /************************************************************************/
13071 : /* DemoteTo2D() */
13072 : /************************************************************************/
13073 :
13074 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13075 : *
13076 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13077 : * be used.
13078 : * @return OGRERR_NONE if no error occurred.
13079 : * @since GDAL 3.2 and PROJ 6.3
13080 : */
13081 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13082 : {
13083 90 : TAKE_OPTIONAL_LOCK();
13084 :
13085 45 : d->refreshProjObj();
13086 45 : if (!d->m_pj_crs)
13087 0 : return OGRERR_FAILURE;
13088 : auto newPj =
13089 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13090 45 : if (!newPj)
13091 0 : return OGRERR_FAILURE;
13092 45 : d->setPjCRS(newPj);
13093 45 : return OGRERR_NONE;
13094 : }
13095 :
13096 : /************************************************************************/
13097 : /* OSRDemoteTo2D() */
13098 : /************************************************************************/
13099 :
13100 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13101 : *
13102 : * See OGRSpatialReference::DemoteTo2D()
13103 : *
13104 : * @since GDAL 3.2 and PROJ 6.3
13105 : */
13106 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13107 : {
13108 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13109 :
13110 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13111 : }
13112 :
13113 : /************************************************************************/
13114 : /* GetEPSGGeogCS() */
13115 : /************************************************************************/
13116 :
13117 : /** Try to establish what the EPSG code for this coordinate systems
13118 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13119 : *
13120 : * @return EPSG code
13121 : */
13122 :
13123 335 : int OGRSpatialReference::GetEPSGGeogCS() const
13124 :
13125 : {
13126 670 : TAKE_OPTIONAL_LOCK();
13127 :
13128 : /* -------------------------------------------------------------------- */
13129 : /* Check axis order. */
13130 : /* -------------------------------------------------------------------- */
13131 670 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13132 335 : if (!poGeogCRS)
13133 0 : return -1;
13134 :
13135 335 : bool ret = false;
13136 335 : poGeogCRS->d->demoteFromBoundCRS();
13137 335 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13138 335 : poGeogCRS->d->m_pj_crs);
13139 335 : poGeogCRS->d->undoDemoteFromBoundCRS();
13140 335 : if (cs)
13141 : {
13142 335 : const char *pszDirection = nullptr;
13143 335 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13144 : &pszDirection, nullptr, nullptr, nullptr,
13145 335 : nullptr))
13146 : {
13147 335 : if (EQUAL(pszDirection, "north"))
13148 : {
13149 136 : ret = true;
13150 : }
13151 : }
13152 :
13153 335 : proj_destroy(cs);
13154 : }
13155 335 : if (!ret)
13156 199 : return -1;
13157 :
13158 : /* -------------------------------------------------------------------- */
13159 : /* Do we already have it? */
13160 : /* -------------------------------------------------------------------- */
13161 136 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13162 136 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13163 61 : return atoi(GetAuthorityCode("GEOGCS"));
13164 :
13165 : /* -------------------------------------------------------------------- */
13166 : /* Get the datum and geogcs names. */
13167 : /* -------------------------------------------------------------------- */
13168 :
13169 75 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13170 75 : const char *pszDatum = GetAttrValue("DATUM");
13171 :
13172 : // We can only operate on coordinate systems with a geogcs.
13173 150 : OGRSpatialReference oSRSTmp;
13174 75 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13175 : {
13176 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13177 : // export to WKT1, so try to extract the geographic CRS through PROJ
13178 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13179 1 : oSRSTmp.CopyGeogCSFrom(this);
13180 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13181 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13182 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13183 : {
13184 0 : return -1;
13185 : }
13186 : }
13187 :
13188 : // Lookup geographic CRS name
13189 75 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13190 75 : PJ_OBJ_LIST *list = proj_create_from_name(
13191 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13192 75 : if (list)
13193 : {
13194 75 : const auto listSize = proj_list_get_count(list);
13195 75 : if (listSize == 1)
13196 : {
13197 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13198 49 : if (crs)
13199 : {
13200 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13201 49 : const char *pszCode = proj_get_id_code(crs, 0);
13202 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13203 : {
13204 47 : const int nCode = atoi(pszCode);
13205 47 : proj_destroy(crs);
13206 47 : proj_list_destroy(list);
13207 47 : return nCode;
13208 : }
13209 2 : proj_destroy(crs);
13210 : }
13211 : }
13212 28 : proj_list_destroy(list);
13213 : }
13214 :
13215 : /* -------------------------------------------------------------------- */
13216 : /* Is this a "well known" geographic coordinate system? */
13217 : /* -------------------------------------------------------------------- */
13218 84 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13219 28 : strstr(pszDatum, "WGS") ||
13220 28 : strstr(pszGEOGCS, "World Geodetic System") ||
13221 28 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13222 84 : strstr(pszDatum, "World Geodetic System") ||
13223 28 : strstr(pszDatum, "World_Geodetic_System");
13224 :
13225 84 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13226 28 : strstr(pszDatum, "NAD") ||
13227 28 : strstr(pszGEOGCS, "North American") ||
13228 28 : strstr(pszGEOGCS, "North_American") ||
13229 84 : strstr(pszDatum, "North American") ||
13230 28 : strstr(pszDatum, "North_American");
13231 :
13232 28 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13233 0 : return 4326;
13234 :
13235 28 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13236 0 : return 4322;
13237 :
13238 : // This is questionable as there are several 'flavors' of NAD83 that
13239 : // are not the same as 4269
13240 28 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13241 0 : return 4269;
13242 :
13243 28 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13244 0 : return 4267;
13245 :
13246 : /* -------------------------------------------------------------------- */
13247 : /* If we know the datum, associate the most likely GCS with */
13248 : /* it. */
13249 : /* -------------------------------------------------------------------- */
13250 28 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13251 28 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13252 28 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13253 0 : GetPrimeMeridian() == 0.0)
13254 : {
13255 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13256 :
13257 0 : if (nDatum >= 6000 && nDatum <= 6999)
13258 0 : return nDatum - 2000;
13259 : }
13260 :
13261 28 : return -1;
13262 : }
13263 :
13264 : /************************************************************************/
13265 : /* SetCoordinateEpoch() */
13266 : /************************************************************************/
13267 :
13268 : /** Set the coordinate epoch, as decimal year.
13269 : *
13270 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13271 : * change with time. To be unambiguous the coordinates must always be qualified
13272 : * with the epoch at which they are valid. The coordinate epoch is not
13273 : * necessarily the epoch at which the observation was collected.
13274 : *
13275 : * Pedantically the coordinate epoch of an observation belongs to the
13276 : * observation, and not to the CRS, however it is often more practical to
13277 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13278 : * CRS (see IsDynamic())
13279 : *
13280 : * This method is the same as the OSRSetCoordinateEpoch() function.
13281 : *
13282 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13283 : * @since OGR 3.4
13284 : */
13285 :
13286 802 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13287 : {
13288 802 : d->m_coordinateEpoch = dfCoordinateEpoch;
13289 802 : }
13290 :
13291 : /************************************************************************/
13292 : /* OSRSetCoordinateEpoch() */
13293 : /************************************************************************/
13294 :
13295 : /** \brief Set the coordinate epoch, as decimal year.
13296 : *
13297 : * See OGRSpatialReference::SetCoordinateEpoch()
13298 : *
13299 : * @since OGR 3.4
13300 : */
13301 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13302 : {
13303 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13304 :
13305 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13306 31 : dfCoordinateEpoch);
13307 : }
13308 :
13309 : /************************************************************************/
13310 : /* GetCoordinateEpoch() */
13311 : /************************************************************************/
13312 :
13313 : /** Return the coordinate epoch, as decimal year.
13314 : *
13315 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13316 : * change with time. To be unambiguous the coordinates must always be qualified
13317 : * with the epoch at which they are valid. The coordinate epoch is not
13318 : * necessarily the epoch at which the observation was collected.
13319 : *
13320 : * Pedantically the coordinate epoch of an observation belongs to the
13321 : * observation, and not to the CRS, however it is often more practical to
13322 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13323 : * CRS (see IsDynamic())
13324 : *
13325 : * This method is the same as the OSRGetCoordinateEpoch() function.
13326 : *
13327 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13328 : * if not set, or relevant.
13329 : * @since OGR 3.4
13330 : */
13331 :
13332 10620 : double OGRSpatialReference::GetCoordinateEpoch() const
13333 : {
13334 10620 : return d->m_coordinateEpoch;
13335 : }
13336 :
13337 : /************************************************************************/
13338 : /* OSRGetCoordinateEpoch() */
13339 : /************************************************************************/
13340 :
13341 : /** \brief Get the coordinate epoch, as decimal year.
13342 : *
13343 : * See OGRSpatialReference::GetCoordinateEpoch()
13344 : *
13345 : * @since OGR 3.4
13346 : */
13347 622 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13348 : {
13349 622 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13350 :
13351 622 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13352 : }
|