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 : bool GDALThreadLocalDatasetCacheIsInDestruction();
49 :
50 : // Exists since 8.0.1
51 : #ifndef PROJ_AT_LEAST_VERSION
52 : #define PROJ_COMPUTE_VERSION(maj, min, patch) \
53 : ((maj) * 10000 + (min) * 100 + (patch))
54 : #define PROJ_VERSION_NUMBER \
55 : PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, \
56 : PROJ_VERSION_PATCH)
57 : #define PROJ_AT_LEAST_VERSION(maj, min, patch) \
58 : (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
59 : #endif
60 :
61 : #define STRINGIFY(s) #s
62 : #define XSTRINGIFY(s) STRINGIFY(s)
63 :
64 : struct OGRSpatialReference::Private
65 : {
66 : struct Listener final : public OGR_SRSNode::Listener
67 : {
68 : OGRSpatialReference::Private *m_poObj = nullptr;
69 :
70 268814 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 268814 : }
73 :
74 : Listener(const Listener &) = delete;
75 : Listener &operator=(const Listener &) = delete;
76 :
77 : void notifyChange(OGR_SRSNode *) override;
78 : };
79 :
80 : OGRSpatialReference *m_poSelf = nullptr;
81 : PJ *m_pj_crs = nullptr;
82 :
83 : // Temporary state used for object construction
84 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85 : CPLString m_osPrimeMeridianName{};
86 : CPLString m_osAngularUnits{};
87 : CPLString m_osLinearUnits{};
88 : CPLString m_osAxisName[3]{};
89 :
90 : std::vector<std::string> m_wktImportWarnings{};
91 : std::vector<std::string> m_wktImportErrors{};
92 : CPLString m_osAreaName{};
93 : CPLString m_celestialBodyName{};
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(bool bForceWKT2);
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 1263710 : PJ_CONTEXT *getPROJContext()
159 : {
160 1263710 : 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 18428200 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 18428200 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 18428200 : }
184 :
185 18428200 : ~OptionalLockGuard()
186 18428200 : {
187 18428200 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 18428200 : }
190 : };
191 :
192 18428200 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 18428200 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2381310 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2381310 : m_poObj->nodesChanged();
201 2381310 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 268814 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 268814 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 268814 : if (pszDefaultAMS)
212 : {
213 1 : if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
214 0 : return OAMS_AUTHORITY_COMPLIANT;
215 1 : else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
216 1 : return OAMS_TRADITIONAL_GIS_ORDER;
217 : else
218 : {
219 0 : CPLError(CE_Failure, CPLE_AppDefined,
220 : "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
221 : pszDefaultAMS);
222 : }
223 : }
224 268813 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 268814 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 268814 : : m_poSelf(poSelf), m_poListener(std::make_shared<Listener>(this))
229 : {
230 : // Get the default value for m_axisMappingStrategy from the
231 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
232 268814 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
233 268814 : }
234 :
235 1069290 : OGRSpatialReference::Private::~Private()
236 : {
237 : // In case we destroy the object not in the thread that created it,
238 : // we need to reassign the PROJ context. Having the context bundled inside
239 : // PJ* deeply sucks...
240 267322 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
241 : PJ_CONTEXT *ctxt;
242 267322 : if (GDALThreadLocalDatasetCacheIsInDestruction())
243 : {
244 181 : pj_context_to_destroy = proj_context_create();
245 181 : ctxt = pj_context_to_destroy;
246 : }
247 : else
248 : {
249 267141 : ctxt = getPROJContext();
250 : }
251 :
252 267322 : proj_assign_context(m_pj_crs, ctxt);
253 267322 : proj_destroy(m_pj_crs);
254 :
255 267322 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
256 267322 : proj_destroy(m_pj_geod_base_crs_temp);
257 :
258 267322 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
259 267322 : proj_destroy(m_pj_proj_crs_cs_temp);
260 :
261 267322 : proj_assign_context(m_pj_bound_crs_target, ctxt);
262 267322 : proj_destroy(m_pj_bound_crs_target);
263 :
264 267322 : proj_assign_context(m_pj_bound_crs_co, ctxt);
265 267322 : proj_destroy(m_pj_bound_crs_co);
266 :
267 267322 : proj_assign_context(m_pj_crs_backup, ctxt);
268 267322 : proj_destroy(m_pj_crs_backup);
269 :
270 267322 : delete m_poRootBackup;
271 267322 : delete m_poRoot;
272 267322 : proj_context_destroy(pj_context_to_destroy);
273 267322 : }
274 :
275 132702 : void OGRSpatialReference::Private::clear()
276 : {
277 132702 : proj_assign_context(m_pj_crs, getPROJContext());
278 132702 : proj_destroy(m_pj_crs);
279 132702 : m_pj_crs = nullptr;
280 :
281 132702 : delete m_poRoot;
282 132702 : m_poRoot = nullptr;
283 132702 : m_bNodesChanged = false;
284 :
285 132702 : m_wktImportWarnings.clear();
286 132702 : m_wktImportErrors.clear();
287 :
288 132702 : m_pj_crs_modified_during_demote = false;
289 132702 : m_pjType = PJ_TYPE_UNKNOWN;
290 132702 : m_osPrimeMeridianName.clear();
291 132702 : m_osAngularUnits.clear();
292 132702 : m_osLinearUnits.clear();
293 :
294 132702 : bNormInfoSet = FALSE;
295 132702 : dfFromGreenwich = 1.0;
296 132702 : dfToMeter = 1.0;
297 132702 : dfToDegrees = 1.0;
298 132702 : m_dfAngularUnitToRadian = 0.0;
299 :
300 132702 : m_bMorphToESRI = false;
301 132702 : m_bHasCenterLong = false;
302 :
303 132702 : m_coordinateEpoch = 0.0;
304 132702 : }
305 :
306 29719 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
307 : {
308 29719 : m_poRoot = poRoot;
309 29719 : if (m_poRoot)
310 : {
311 29719 : m_poRoot->RegisterListener(m_poListener);
312 : }
313 29719 : nodesChanged();
314 29719 : }
315 :
316 195274 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
317 : bool doRefreshAxisMapping)
318 : {
319 195274 : auto ctxt = getPROJContext();
320 :
321 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
322 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
323 : {
324 : const double dfEpoch =
325 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
326 : if (!std::isnan(dfEpoch))
327 : {
328 : m_poSelf->SetCoordinateEpoch(dfEpoch);
329 : }
330 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
331 : proj_destroy(pj_crsIn);
332 : pj_crsIn = crs;
333 : }
334 : #endif
335 :
336 195274 : proj_assign_context(m_pj_crs, ctxt);
337 195274 : proj_destroy(m_pj_crs);
338 195274 : m_pj_crs = pj_crsIn;
339 195274 : if (m_pj_crs)
340 : {
341 195223 : m_pjType = proj_get_type(m_pj_crs);
342 : }
343 195274 : if (m_pj_crs_backup)
344 : {
345 21 : m_pj_crs_modified_during_demote = true;
346 : }
347 195274 : invalidateNodes();
348 195274 : if (doRefreshAxisMapping)
349 : {
350 195254 : refreshAxisMapping();
351 : }
352 195274 : }
353 :
354 791889 : void OGRSpatialReference::Private::refreshProjObj()
355 : {
356 791889 : if (m_bNodesChanged && m_poRoot)
357 : {
358 8850 : char *pszWKT = nullptr;
359 8850 : m_poRoot->exportToWkt(&pszWKT);
360 8850 : auto poRootBackup = m_poRoot;
361 8850 : m_poRoot = nullptr;
362 8850 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
363 8850 : clear();
364 8850 : m_coordinateEpoch = dfCoordinateEpochBackup;
365 8850 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
366 :
367 8850 : const char *const options[] = {
368 : "STRICT=NO",
369 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
370 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
371 : #endif
372 : nullptr};
373 8850 : PROJ_STRING_LIST warnings = nullptr;
374 8850 : PROJ_STRING_LIST errors = nullptr;
375 8850 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
376 : &warnings, &errors));
377 17316 : for (auto iter = warnings; iter && *iter; ++iter)
378 : {
379 8466 : m_wktImportWarnings.push_back(*iter);
380 : }
381 9066 : for (auto iter = errors; iter && *iter; ++iter)
382 : {
383 216 : m_wktImportErrors.push_back(*iter);
384 : }
385 8850 : proj_string_list_destroy(warnings);
386 8850 : proj_string_list_destroy(errors);
387 :
388 8850 : CPLFree(pszWKT);
389 :
390 8850 : m_poRoot = poRootBackup;
391 8850 : m_bNodesChanged = false;
392 : }
393 791889 : }
394 :
395 31846 : void OGRSpatialReference::Private::refreshRootFromProjObj(bool bForceWKT2)
396 : {
397 31846 : CPLAssert(m_poRoot == nullptr);
398 :
399 31846 : if (m_pj_crs)
400 : {
401 59350 : CPLStringList aosOptions;
402 29675 : if (!m_bMorphToESRI)
403 : {
404 29671 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
405 29671 : aosOptions.SetNameValue("MULTILINE", "NO");
406 : }
407 29675 : aosOptions.SetNameValue("STRICT", "NO");
408 :
409 29675 : const char *pszWKT = nullptr;
410 29675 : if (!bForceWKT2)
411 : {
412 29662 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
413 29662 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
414 29662 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
415 29662 : aosOptions.List());
416 29662 : m_bNodesWKT2 = false;
417 : }
418 29675 : if (!m_bMorphToESRI && pszWKT == nullptr)
419 : {
420 109 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
421 109 : aosOptions.List());
422 109 : m_bNodesWKT2 = true;
423 : }
424 29675 : if (pszWKT)
425 : {
426 29675 : auto root = new OGR_SRSNode();
427 29675 : setRoot(root);
428 29675 : root->importFromWkt(&pszWKT);
429 29675 : m_bNodesChanged = false;
430 : }
431 : }
432 31846 : }
433 :
434 228252 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
435 : {
436 228252 : const char *pszName1 = nullptr;
437 228252 : const char *pszDirection1 = nullptr;
438 228252 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
439 : nullptr, nullptr, nullptr, nullptr);
440 228252 : const char *pszName2 = nullptr;
441 228252 : const char *pszDirection2 = nullptr;
442 228252 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
443 : nullptr, nullptr, nullptr, nullptr);
444 228252 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
445 100850 : EQUAL(pszDirection2, "east"))
446 : {
447 99271 : return true;
448 : }
449 128981 : if (pszDirection1 && pszDirection2 &&
450 128981 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
451 127419 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
452 3261 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
453 1320 : STARTS_WITH_CI(pszName2, "easting"))
454 : {
455 1320 : return true;
456 : }
457 127661 : return false;
458 : }
459 :
460 288200 : void OGRSpatialReference::Private::refreshAxisMapping()
461 : {
462 288200 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
463 60601 : return;
464 :
465 227599 : bool doUndoDemote = false;
466 227599 : if (m_pj_crs_backup == nullptr)
467 : {
468 227578 : doUndoDemote = true;
469 227578 : demoteFromBoundCRS();
470 : }
471 227599 : const auto ctxt = getPROJContext();
472 227599 : PJ *horizCRS = nullptr;
473 227599 : int axisCount = 0;
474 227599 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
475 : {
476 218 : axisCount = 1;
477 : }
478 227381 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
479 : {
480 1101 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
481 1101 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
482 : {
483 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
484 222 : if (baseCRS)
485 : {
486 222 : proj_destroy(horizCRS);
487 222 : horizCRS = baseCRS;
488 : }
489 : }
490 :
491 1101 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
492 1101 : if (vertCRS)
493 : {
494 1098 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
495 : {
496 398 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
497 398 : if (baseCRS)
498 : {
499 398 : proj_destroy(vertCRS);
500 398 : vertCRS = baseCRS;
501 : }
502 : }
503 :
504 1098 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
505 1098 : if (cs)
506 : {
507 1098 : axisCount += proj_cs_get_axis_count(ctxt, cs);
508 1098 : proj_destroy(cs);
509 : }
510 1098 : proj_destroy(vertCRS);
511 : }
512 : }
513 : else
514 : {
515 226280 : horizCRS = m_pj_crs;
516 : }
517 :
518 227599 : bool bSwitchForGisFriendlyOrder = false;
519 227599 : if (horizCRS)
520 : {
521 227378 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
522 227378 : if (cs)
523 : {
524 227378 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
525 227378 : axisCount += nHorizCSAxisCount;
526 227378 : if (nHorizCSAxisCount >= 2)
527 : {
528 227368 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
529 : }
530 227378 : proj_destroy(cs);
531 : }
532 : }
533 227599 : if (horizCRS != m_pj_crs)
534 : {
535 1319 : proj_destroy(horizCRS);
536 : }
537 227599 : if (doUndoDemote)
538 : {
539 227578 : undoDemoteFromBoundCRS();
540 : }
541 :
542 227599 : m_axisMapping.resize(axisCount);
543 227599 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
544 68055 : !bSwitchForGisFriendlyOrder)
545 : {
546 590810 : for (int i = 0; i < axisCount; i++)
547 : {
548 394388 : m_axisMapping[i] = i + 1;
549 196422 : }
550 : }
551 : else
552 : {
553 31177 : m_axisMapping[0] = 2;
554 31177 : m_axisMapping[1] = 1;
555 31177 : if (axisCount == 3)
556 : {
557 415 : m_axisMapping[2] = 3;
558 : }
559 : }
560 : }
561 :
562 2411030 : void OGRSpatialReference::Private::nodesChanged()
563 : {
564 2411030 : m_bNodesChanged = true;
565 2411030 : }
566 :
567 195543 : void OGRSpatialReference::Private::invalidateNodes()
568 : {
569 195543 : delete m_poRoot;
570 195543 : m_poRoot = nullptr;
571 195543 : m_bNodesChanged = false;
572 195543 : }
573 :
574 256 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
575 : {
576 256 : invalidateNodes();
577 256 : m_bMorphToESRI = b;
578 256 : }
579 :
580 670462 : void OGRSpatialReference::Private::demoteFromBoundCRS()
581 : {
582 670462 : CPLAssert(m_pj_bound_crs_target == nullptr);
583 670462 : CPLAssert(m_pj_bound_crs_co == nullptr);
584 670462 : CPLAssert(m_poRootBackup == nullptr);
585 670462 : CPLAssert(m_pj_crs_backup == nullptr);
586 :
587 670462 : m_pj_crs_modified_during_demote = false;
588 :
589 670462 : if (m_pjType == PJ_TYPE_BOUND_CRS)
590 : {
591 2752 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
592 2752 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
593 2752 : m_pj_bound_crs_co =
594 2752 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
595 :
596 2752 : m_poRootBackup = m_poRoot;
597 2752 : m_poRoot = nullptr;
598 2752 : m_pj_crs_backup = m_pj_crs;
599 2752 : m_pj_crs = baseCRS;
600 2752 : m_pjType = proj_get_type(m_pj_crs);
601 : }
602 670462 : }
603 :
604 670462 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
605 : {
606 670462 : if (m_pj_bound_crs_target)
607 : {
608 2752 : CPLAssert(m_poRoot == nullptr);
609 2752 : CPLAssert(m_pj_crs);
610 2752 : if (!m_pj_crs_modified_during_demote)
611 : {
612 2732 : proj_destroy(m_pj_crs);
613 2732 : m_pj_crs = m_pj_crs_backup;
614 2732 : m_pjType = proj_get_type(m_pj_crs);
615 2732 : m_poRoot = m_poRootBackup;
616 : }
617 : else
618 : {
619 20 : delete m_poRootBackup;
620 20 : m_poRootBackup = nullptr;
621 20 : proj_destroy(m_pj_crs_backup);
622 20 : m_pj_crs_backup = nullptr;
623 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
624 20 : m_pj_bound_crs_target,
625 20 : m_pj_bound_crs_co),
626 : false);
627 : }
628 : }
629 :
630 670462 : m_poRootBackup = nullptr;
631 670462 : m_pj_crs_backup = nullptr;
632 670462 : proj_destroy(m_pj_bound_crs_target);
633 670462 : m_pj_bound_crs_target = nullptr;
634 670462 : proj_destroy(m_pj_bound_crs_co);
635 670462 : m_pj_bound_crs_co = nullptr;
636 670462 : m_pj_crs_modified_during_demote = false;
637 670462 : }
638 :
639 170088 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
640 : const char *pszTargetKey)
641 : {
642 170088 : if (pszTargetKey)
643 : {
644 61471 : demoteFromBoundCRS();
645 61471 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
646 32325 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
647 29224 : EQUAL(pszTargetKey, "GEOGCS"))
648 : {
649 7190 : pszTargetKey = nullptr;
650 : }
651 54281 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
652 20 : EQUAL(pszTargetKey, "GEOCCS"))
653 : {
654 0 : pszTargetKey = nullptr;
655 : }
656 54281 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
657 30936 : EQUAL(pszTargetKey, "PROJCS"))
658 : {
659 4538 : pszTargetKey = nullptr;
660 : }
661 49743 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
662 4 : EQUAL(pszTargetKey, "VERT_CS"))
663 : {
664 2 : pszTargetKey = nullptr;
665 : }
666 61471 : undoDemoteFromBoundCRS();
667 : }
668 170088 : return pszTargetKey;
669 : }
670 :
671 9876 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
672 : {
673 9876 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
674 9823 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
675 : {
676 53 : return m_pj_crs;
677 : }
678 :
679 9823 : auto ctxt = getPROJContext();
680 9823 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
681 : {
682 4532 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
683 4532 : proj_destroy(m_pj_geod_base_crs_temp);
684 4532 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
685 4532 : return m_pj_geod_base_crs_temp;
686 : }
687 :
688 5291 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
689 5291 : proj_destroy(m_pj_geod_base_crs_temp);
690 5291 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
691 : nullptr, 0);
692 5291 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
693 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
694 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
695 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
696 5291 : proj_destroy(cs);
697 :
698 5291 : return m_pj_geod_base_crs_temp;
699 : }
700 :
701 5493 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
702 : {
703 5493 : auto ctxt = getPROJContext();
704 5493 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
705 : {
706 4518 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
707 4518 : proj_destroy(m_pj_proj_crs_cs_temp);
708 4518 : m_pj_proj_crs_cs_temp =
709 4518 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
710 4518 : return m_pj_proj_crs_cs_temp;
711 : }
712 :
713 975 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
714 975 : proj_destroy(m_pj_proj_crs_cs_temp);
715 975 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
716 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
717 975 : return m_pj_proj_crs_cs_temp;
718 : }
719 :
720 5544 : const char *OGRSpatialReference::Private::getProjCRSName()
721 : {
722 5544 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
723 : {
724 4533 : return proj_get_name(m_pj_crs);
725 : }
726 :
727 1011 : return "unnamed";
728 : }
729 :
730 1384 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
731 : {
732 1384 : refreshProjObj();
733 :
734 1384 : demoteFromBoundCRS();
735 :
736 : auto projCRS =
737 1384 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
738 1384 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
739 1384 : proj_destroy(conv);
740 :
741 1384 : setPjCRS(projCRS);
742 :
743 1384 : undoDemoteFromBoundCRS();
744 1384 : return OGRERR_NONE;
745 : }
746 :
747 : /************************************************************************/
748 : /* ToPointer() */
749 : /************************************************************************/
750 :
751 27104 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
752 : {
753 27104 : return OGRSpatialReference::FromHandle(hSRS);
754 : }
755 :
756 : /************************************************************************/
757 : /* ToHandle() */
758 : /************************************************************************/
759 :
760 4823 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
761 : {
762 4823 : return OGRSpatialReference::ToHandle(poSRS);
763 : }
764 :
765 : /************************************************************************/
766 : /* OGRsnPrintDouble() */
767 : /************************************************************************/
768 :
769 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
770 :
771 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
772 :
773 : {
774 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
775 :
776 126 : const size_t nLen = strlen(pszStrBuf);
777 :
778 : // The following hack is intended to truncate some "precision" in cases
779 : // that appear to be roundoff error.
780 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
781 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
782 : {
783 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
784 : }
785 :
786 : // Force to user periods regardless of locale.
787 126 : if (strchr(pszStrBuf, ',') != nullptr)
788 : {
789 0 : char *const pszDelim = strchr(pszStrBuf, ',');
790 0 : *pszDelim = '.';
791 : }
792 126 : }
793 :
794 : /************************************************************************/
795 : /* OGRSpatialReference() */
796 : /************************************************************************/
797 :
798 : /**
799 : * \brief Constructor.
800 : *
801 : * This constructor takes an optional string argument which if passed
802 : * should be a WKT representation of an SRS. Passing this is equivalent
803 : * to not passing it, and then calling importFromWkt() with the WKT string.
804 : *
805 : * Note that newly created objects are given a reference count of one.
806 : *
807 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
808 : * object are assumed to be in the order of the axis of the CRS definition
809 : (which
810 : * for example means latitude first, longitude second for geographic CRS
811 : belonging
812 : * to the EPSG authority). It is possible to define a data axis to CRS axis
813 : * mapping strategy with the SetAxisMappingStrategy() method.
814 : *
815 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
816 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
817 : later
818 : * being the default value when the option is not set) to control the value of
819 : the
820 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
821 : * created. Calling SetAxisMappingStrategy() will override this default value.
822 :
823 : * The C function OSRNewSpatialReference() does the same thing as this
824 : * constructor.
825 : *
826 : * @param pszWKT well known text definition to which the object should
827 : * be initialized, or NULL (the default).
828 : */
829 :
830 266181 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
831 266181 : : d(new Private(this))
832 : {
833 266181 : if (pszWKT != nullptr)
834 1101 : importFromWkt(pszWKT);
835 266181 : }
836 :
837 : /************************************************************************/
838 : /* OSRNewSpatialReference() */
839 : /************************************************************************/
840 :
841 : /**
842 : * \brief Constructor.
843 : *
844 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
845 : * object are assumed to be in the order of the axis of the CRS definition
846 : * (which for example means latitude first, longitude second for geographic CRS
847 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
848 : * axis mapping strategy with the SetAxisMappingStrategy() method.
849 : *
850 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
851 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
852 : * later being the default value when the option is not set) to control the
853 : * value of the data axis to CRS axis mapping strategy when a
854 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
855 : * override this default value.
856 : *
857 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
858 : */
859 3340 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
860 :
861 : {
862 3340 : OGRSpatialReference *poSRS = new OGRSpatialReference();
863 :
864 3340 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
865 : {
866 65 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
867 : {
868 0 : delete poSRS;
869 0 : poSRS = nullptr;
870 : }
871 : }
872 :
873 3340 : return ToHandle(poSRS);
874 : }
875 :
876 : /************************************************************************/
877 : /* OGRSpatialReference() */
878 : /************************************************************************/
879 :
880 : /** Copy constructor. See also Clone().
881 : * @param oOther other spatial reference
882 : */
883 2633 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
884 2633 : : d(new Private(this))
885 : {
886 2633 : *this = oOther;
887 2633 : }
888 :
889 : /************************************************************************/
890 : /* OGRSpatialReference() */
891 : /************************************************************************/
892 :
893 : /** Move constructor.
894 : * @param oOther other spatial reference
895 : */
896 1 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
897 1 : : d(std::move(oOther.d))
898 : {
899 1 : }
900 :
901 : /************************************************************************/
902 : /* ~OGRSpatialReference() */
903 : /************************************************************************/
904 :
905 : /**
906 : * \brief OGRSpatialReference destructor.
907 : *
908 : * The C function OSRDestroySpatialReference() does the same thing as this
909 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
910 : *
911 : * @deprecated
912 : */
913 :
914 325551 : OGRSpatialReference::~OGRSpatialReference()
915 :
916 : {
917 325551 : }
918 :
919 : /************************************************************************/
920 : /* DestroySpatialReference() */
921 : /************************************************************************/
922 :
923 : /**
924 : * \brief OGRSpatialReference destructor.
925 : *
926 : * This static method will destroy a OGRSpatialReference. It is
927 : * equivalent to calling delete on the object, but it ensures that the
928 : * deallocation is properly executed within the OGR libraries heap on
929 : * platforms where this can matter (win32).
930 : *
931 : * This function is the same as OSRDestroySpatialReference()
932 : *
933 : * @param poSRS the object to delete
934 : *
935 : */
936 :
937 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
938 : {
939 0 : delete poSRS;
940 0 : }
941 :
942 : /************************************************************************/
943 : /* OSRDestroySpatialReference() */
944 : /************************************************************************/
945 :
946 : /**
947 : * \brief OGRSpatialReference destructor.
948 : *
949 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
950 : * and OGRSpatialReference::DestroySpatialReference()
951 : *
952 : * @param hSRS the object to delete
953 : */
954 9681 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
955 :
956 : {
957 9681 : delete ToPointer(hSRS);
958 9681 : }
959 :
960 : /************************************************************************/
961 : /* Clear() */
962 : /************************************************************************/
963 :
964 : /**
965 : * \brief Wipe current definition.
966 : *
967 : * Returns OGRSpatialReference to a state with no definition, as it
968 : * exists when first created. It does not affect reference counts.
969 : */
970 :
971 123852 : void OGRSpatialReference::Clear()
972 :
973 : {
974 123852 : d->clear();
975 123852 : }
976 :
977 : /************************************************************************/
978 : /* operator=() */
979 : /************************************************************************/
980 :
981 : /** Assignment operator.
982 : * @param oSource SRS to assign to *this
983 : * @return *this
984 : */
985 : OGRSpatialReference &
986 28049 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
987 :
988 : {
989 28049 : if (&oSource != this)
990 : {
991 28048 : Clear();
992 : #ifdef CPPCHECK
993 : // Otherwise cppcheck would protest that nRefCount isn't modified
994 : d->nRefCount = (d->nRefCount + 1) - 1;
995 : #endif
996 :
997 28048 : oSource.d->refreshProjObj();
998 28048 : if (oSource.d->m_pj_crs)
999 27603 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1000 28048 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1001 13264 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1002 14784 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1003 124 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1004 :
1005 28048 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1006 : }
1007 :
1008 28049 : return *this;
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* operator=() */
1013 : /************************************************************************/
1014 :
1015 : /** Move assignment operator.
1016 : * @param oSource SRS to assign to *this
1017 : * @return *this
1018 : */
1019 : OGRSpatialReference &
1020 4857 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1021 :
1022 : {
1023 4857 : if (&oSource != this)
1024 : {
1025 4856 : d = std::move(oSource.d);
1026 : }
1027 :
1028 4857 : return *this;
1029 : }
1030 :
1031 : /************************************************************************/
1032 : /* AssignAndSetThreadSafe() */
1033 : /************************************************************************/
1034 :
1035 : /** Assignment method, with thread-safety.
1036 : *
1037 : * Same as an assignment operator, but asking also that the *this instance
1038 : * becomes thread-safe.
1039 : *
1040 : * @param oSource SRS to assign to *this
1041 : * @return *this
1042 : * @since 3.10
1043 : */
1044 :
1045 : OGRSpatialReference &
1046 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1047 : {
1048 2 : *this = oSource;
1049 2 : d->SetThreadSafe();
1050 2 : return *this;
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* Reference() */
1055 : /************************************************************************/
1056 :
1057 : /**
1058 : * \brief Increments the reference count by one.
1059 : *
1060 : * The reference count is used keep track of the number of OGRGeometry objects
1061 : * referencing this SRS.
1062 : *
1063 : * The method does the same thing as the C function OSRReference().
1064 : *
1065 : * @return the updated reference count.
1066 : */
1067 :
1068 3647050 : int OGRSpatialReference::Reference()
1069 :
1070 : {
1071 3647050 : return CPLAtomicInc(&d->nRefCount);
1072 : }
1073 :
1074 : /************************************************************************/
1075 : /* OSRReference() */
1076 : /************************************************************************/
1077 :
1078 : /**
1079 : * \brief Increments the reference count by one.
1080 : *
1081 : * This function is the same as OGRSpatialReference::Reference()
1082 : */
1083 1027 : int OSRReference(OGRSpatialReferenceH hSRS)
1084 :
1085 : {
1086 1027 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1087 :
1088 1027 : return ToPointer(hSRS)->Reference();
1089 : }
1090 :
1091 : /************************************************************************/
1092 : /* Dereference() */
1093 : /************************************************************************/
1094 :
1095 : /**
1096 : * \brief Decrements the reference count by one.
1097 : *
1098 : * The method does the same thing as the C function OSRDereference().
1099 : *
1100 : * @return the updated reference count.
1101 : */
1102 :
1103 3688420 : int OGRSpatialReference::Dereference()
1104 :
1105 : {
1106 3688420 : if (d->nRefCount <= 0)
1107 0 : CPLDebug("OSR",
1108 : "Dereference() called on an object with refcount %d,"
1109 : "likely already destroyed!",
1110 0 : d->nRefCount);
1111 3688420 : return CPLAtomicDec(&d->nRefCount);
1112 : }
1113 :
1114 : /************************************************************************/
1115 : /* OSRDereference() */
1116 : /************************************************************************/
1117 :
1118 : /**
1119 : * \brief Decrements the reference count by one.
1120 : *
1121 : * This function is the same as OGRSpatialReference::Dereference()
1122 : */
1123 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1124 :
1125 : {
1126 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1127 :
1128 0 : return ToPointer(hSRS)->Dereference();
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* GetReferenceCount() */
1133 : /************************************************************************/
1134 :
1135 : /**
1136 : * \brief Fetch current reference count.
1137 : *
1138 : * @return the current reference count.
1139 : */
1140 180 : int OGRSpatialReference::GetReferenceCount() const
1141 : {
1142 180 : return d->nRefCount;
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* Release() */
1147 : /************************************************************************/
1148 :
1149 : /**
1150 : * \brief Decrements the reference count by one, and destroy if zero.
1151 : *
1152 : * The method does the same thing as the C function OSRRelease().
1153 : */
1154 :
1155 3688160 : void OGRSpatialReference::Release()
1156 :
1157 : {
1158 3688160 : if (Dereference() <= 0)
1159 41311 : delete this;
1160 3688160 : }
1161 :
1162 : /************************************************************************/
1163 : /* OSRRelease() */
1164 : /************************************************************************/
1165 :
1166 : /**
1167 : * \brief Decrements the reference count by one, and destroy if zero.
1168 : *
1169 : * This function is the same as OGRSpatialReference::Release()
1170 : */
1171 6772 : void OSRRelease(OGRSpatialReferenceH hSRS)
1172 :
1173 : {
1174 6772 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1175 :
1176 6772 : ToPointer(hSRS)->Release();
1177 : }
1178 :
1179 94011 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1180 : {
1181 94011 : TAKE_OPTIONAL_LOCK();
1182 :
1183 94011 : if (!d->m_poRoot)
1184 : {
1185 28534 : d->refreshRootFromProjObj(false);
1186 : }
1187 188022 : return d->m_poRoot;
1188 : }
1189 :
1190 8654 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1191 : {
1192 8654 : TAKE_OPTIONAL_LOCK();
1193 :
1194 8654 : if (!d->m_poRoot)
1195 : {
1196 3299 : d->refreshRootFromProjObj(false);
1197 : }
1198 17308 : return d->m_poRoot;
1199 : }
1200 :
1201 : /************************************************************************/
1202 : /* SetRoot() */
1203 : /************************************************************************/
1204 :
1205 : /**
1206 : * \brief Set the root SRS node.
1207 : *
1208 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1209 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1210 : * is assumed by the OGRSpatialReference.
1211 : *
1212 : * @param poNewRoot object to assign as root.
1213 : */
1214 :
1215 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1216 :
1217 : {
1218 44 : if (d->m_poRoot != poNewRoot)
1219 : {
1220 44 : delete d->m_poRoot;
1221 44 : d->setRoot(poNewRoot);
1222 : }
1223 44 : }
1224 :
1225 : /************************************************************************/
1226 : /* GetAttrNode() */
1227 : /************************************************************************/
1228 :
1229 : /**
1230 : * \brief Find named node in tree.
1231 : *
1232 : * This method does a pre-order traversal of the node tree searching for
1233 : * a node with this exact value (case insensitive), and returns it. Leaf
1234 : * nodes are not considered, under the assumption that they are just
1235 : * attribute value nodes.
1236 : *
1237 : * If a node appears more than once in the tree (such as UNIT for instance),
1238 : * the first encountered will be returned. Use GetNode() on a subtree to be
1239 : * more specific.
1240 : *
1241 : * @param pszNodePath the name of the node to search for. May contain multiple
1242 : * components such as "GEOGCS|UNIT".
1243 : *
1244 : * @return a pointer to the node found, or NULL if none.
1245 : */
1246 :
1247 90877 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1248 :
1249 : {
1250 90877 : if (strstr(pszNodePath, "CONVERSION") && !d->m_bNodesWKT2)
1251 : {
1252 13 : d->invalidateNodes();
1253 13 : d->refreshRootFromProjObj(/* bForceWKT2 = */ true);
1254 : }
1255 :
1256 90877 : if (strchr(pszNodePath, '|') == nullptr)
1257 : {
1258 : // Fast path
1259 50521 : OGR_SRSNode *poNode = GetRoot();
1260 50521 : if (poNode)
1261 49314 : poNode = poNode->GetNode(pszNodePath);
1262 50521 : return poNode;
1263 : }
1264 :
1265 : char **papszPathTokens =
1266 40356 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1267 :
1268 40356 : if (CSLCount(papszPathTokens) < 1)
1269 : {
1270 0 : CSLDestroy(papszPathTokens);
1271 0 : return nullptr;
1272 : }
1273 :
1274 40356 : OGR_SRSNode *poNode = GetRoot();
1275 122740 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1276 : {
1277 82384 : poNode = poNode->GetNode(papszPathTokens[i]);
1278 : }
1279 :
1280 40356 : CSLDestroy(papszPathTokens);
1281 :
1282 40356 : return poNode;
1283 : }
1284 :
1285 : /**
1286 : * \brief Find named node in tree.
1287 : *
1288 : * This method does a pre-order traversal of the node tree searching for
1289 : * a node with this exact value (case insensitive), and returns it. Leaf
1290 : * nodes are not considered, under the assumption that they are just
1291 : * attribute value nodes.
1292 : *
1293 : * If a node appears more than once in the tree (such as UNIT for instance),
1294 : * the first encountered will be returned. Use GetNode() on a subtree to be
1295 : * more specific.
1296 : *
1297 : * @param pszNodePath the name of the node to search for. May contain multiple
1298 : * components such as "GEOGCS|UNIT".
1299 : *
1300 : * @return a pointer to the node found, or NULL if none.
1301 : */
1302 :
1303 : const OGR_SRSNode *
1304 82276 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1305 :
1306 : {
1307 : OGR_SRSNode *poNode =
1308 82276 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1309 :
1310 82276 : return poNode;
1311 : }
1312 :
1313 : /************************************************************************/
1314 : /* GetAttrValue() */
1315 : /************************************************************************/
1316 :
1317 : /**
1318 : * \brief Fetch indicated attribute of named node.
1319 : *
1320 : * This method uses GetAttrNode() to find the named node, and then extracts
1321 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1322 : * would return the second child of the UNIT node, which is normally the
1323 : * length of the linear unit in meters.
1324 : *
1325 : * This method does the same thing as the C function OSRGetAttrValue().
1326 : *
1327 : * @param pszNodeName the tree node to look for (case insensitive).
1328 : * @param iAttr the child of the node to fetch (zero based).
1329 : *
1330 : * @return the requested value, or NULL if it fails for any reason.
1331 : */
1332 :
1333 24728 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1334 : int iAttr) const
1335 :
1336 : {
1337 24728 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1338 24728 : if (poNode == nullptr)
1339 : {
1340 10564 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1341 : {
1342 14 : return GetAttrValue("METHOD", iAttr);
1343 : }
1344 10550 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1345 : {
1346 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1347 : }
1348 10550 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1349 : {
1350 1 : return GetAttrValue("PROJCRS", iAttr);
1351 : }
1352 10549 : return nullptr;
1353 : }
1354 :
1355 14164 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1356 0 : return nullptr;
1357 :
1358 14164 : return poNode->GetChild(iAttr)->GetValue();
1359 : }
1360 :
1361 : /************************************************************************/
1362 : /* OSRGetAttrValue() */
1363 : /************************************************************************/
1364 :
1365 : /**
1366 : * \brief Fetch indicated attribute of named node.
1367 : *
1368 : * This function is the same as OGRSpatialReference::GetAttrValue()
1369 : */
1370 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1371 : const char *pszKey, int iChild)
1372 :
1373 : {
1374 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1375 :
1376 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* GetName() */
1381 : /************************************************************************/
1382 :
1383 : /**
1384 : * \brief Return the CRS name.
1385 : *
1386 : * The returned value is only short lived and should not be used after other
1387 : * calls to methods on this object.
1388 : *
1389 : * @since GDAL 3.0
1390 : */
1391 :
1392 8494 : const char *OGRSpatialReference::GetName() const
1393 : {
1394 16988 : TAKE_OPTIONAL_LOCK();
1395 :
1396 8494 : d->refreshProjObj();
1397 8494 : if (!d->m_pj_crs)
1398 113 : return nullptr;
1399 8381 : const char *pszName = proj_get_name(d->m_pj_crs);
1400 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1401 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1402 : {
1403 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1404 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1405 : if (baseCRS)
1406 : {
1407 : pszName = proj_get_name(baseCRS);
1408 : // pszName still remains valid after proj_destroy(), since
1409 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1410 : proj_destroy(baseCRS);
1411 : }
1412 : }
1413 : #endif
1414 8381 : return pszName;
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* OSRGetName() */
1419 : /************************************************************************/
1420 :
1421 : /**
1422 : * \brief Return the CRS name.
1423 : *
1424 : * The returned value is only short lived and should not be used after other
1425 : * calls to methods on this object.
1426 : *
1427 : * @since GDAL 3.0
1428 : */
1429 46 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1430 :
1431 : {
1432 46 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1433 :
1434 46 : return ToPointer(hSRS)->GetName();
1435 : }
1436 :
1437 : /************************************************************************/
1438 : /* GetCelestialBodyName() */
1439 : /************************************************************************/
1440 :
1441 : /**
1442 : * \brief Return the name of the celestial body of this CRS.
1443 : *
1444 : * e.g. "Earth" for an Earth CRS
1445 : *
1446 : * The returned value is only short lived and should not be used after other
1447 : * calls to methods on this object.
1448 : *
1449 : * @since GDAL 3.12 and PROJ 8.1
1450 : */
1451 :
1452 4 : const char *OGRSpatialReference::GetCelestialBodyName() const
1453 : {
1454 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1455 :
1456 : TAKE_OPTIONAL_LOCK();
1457 :
1458 : d->refreshProjObj();
1459 : if (!d->m_pj_crs)
1460 : return nullptr;
1461 : d->demoteFromBoundCRS();
1462 : const char *name =
1463 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1464 : if (name)
1465 : {
1466 : d->m_celestialBodyName = name;
1467 : }
1468 : d->undoDemoteFromBoundCRS();
1469 : return d->m_celestialBodyName.c_str();
1470 : #else
1471 4 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1472 : 0.05 * SRS_WGS84_SEMIMAJOR)
1473 4 : return "Earth";
1474 0 : const char *pszAuthName = GetAuthorityName(nullptr);
1475 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1476 0 : return "Earth";
1477 0 : return nullptr;
1478 : #endif
1479 : }
1480 :
1481 : /************************************************************************/
1482 : /* OSRGetCelestialBodyName() */
1483 : /************************************************************************/
1484 :
1485 : /**
1486 : * \brief Return the name of the celestial body of this CRS.
1487 : *
1488 : * e.g. "Earth" for an Earth CRS
1489 : *
1490 : * The returned value is only short lived and should not be used after other
1491 : * calls to methods on this object.
1492 : *
1493 : * @since GDAL 3.12 and PROJ 8.1
1494 : */
1495 :
1496 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1497 :
1498 : {
1499 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1500 :
1501 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1502 : }
1503 :
1504 : /************************************************************************/
1505 : /* Clone() */
1506 : /************************************************************************/
1507 :
1508 : /**
1509 : * \brief Make a duplicate of this OGRSpatialReference.
1510 : *
1511 : * This method is the same as the C function OSRClone().
1512 : *
1513 : * @return a new SRS, which becomes the responsibility of the caller.
1514 : */
1515 :
1516 31470 : OGRSpatialReference *OGRSpatialReference::Clone() const
1517 :
1518 : {
1519 31470 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1520 :
1521 31470 : TAKE_OPTIONAL_LOCK();
1522 :
1523 31470 : d->refreshProjObj();
1524 31470 : if (d->m_pj_crs != nullptr)
1525 31426 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1526 31470 : if (d->m_bHasCenterLong && d->m_poRoot)
1527 : {
1528 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1529 : }
1530 31470 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1531 31470 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1532 31470 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1533 62940 : return poNewRef;
1534 : }
1535 :
1536 : /************************************************************************/
1537 : /* OSRClone() */
1538 : /************************************************************************/
1539 :
1540 : /**
1541 : * \brief Make a duplicate of this OGRSpatialReference.
1542 : *
1543 : * This function is the same as OGRSpatialReference::Clone()
1544 : */
1545 1329 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1546 :
1547 : {
1548 1329 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1549 :
1550 1329 : return ToHandle(ToPointer(hSRS)->Clone());
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* dumpReadable() */
1555 : /************************************************************************/
1556 :
1557 : /** Dump pretty wkt to stdout, mostly for debugging.
1558 : */
1559 0 : void OGRSpatialReference::dumpReadable()
1560 :
1561 : {
1562 0 : char *pszPrettyWkt = nullptr;
1563 :
1564 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1565 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1566 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1567 0 : CPLFree(pszPrettyWkt);
1568 0 : }
1569 :
1570 : /************************************************************************/
1571 : /* exportToPrettyWkt() */
1572 : /************************************************************************/
1573 :
1574 : /**
1575 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1576 : * person.
1577 : *
1578 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1579 : * Issues</a> page for implementation details of WKT 1 in OGR.
1580 : *
1581 : * Note that the returned WKT string should be freed with
1582 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1583 : *
1584 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1585 : * option. Valid values are the one of the FORMAT option of
1586 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1587 : *
1588 : * This method is the same as the C function OSRExportToPrettyWkt().
1589 : *
1590 : * @param ppszResult the resulting string is returned in this pointer.
1591 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1592 : * stripped off.
1593 : *
1594 : * @return OGRERR_NONE if successful.
1595 : */
1596 :
1597 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1598 : int bSimplify) const
1599 :
1600 : {
1601 116 : CPLStringList aosOptions;
1602 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1603 58 : if (bSimplify)
1604 : {
1605 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1606 : }
1607 116 : return exportToWkt(ppszResult, aosOptions.List());
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* OSRExportToPrettyWkt() */
1612 : /************************************************************************/
1613 :
1614 : /**
1615 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1616 : * person.
1617 : *
1618 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1619 : * option. Valid values are the one of the FORMAT option of
1620 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1621 : *
1622 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1623 : */
1624 :
1625 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1626 : char **ppszReturn, int bSimplify)
1627 :
1628 : {
1629 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1630 :
1631 56 : *ppszReturn = nullptr;
1632 :
1633 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1634 : }
1635 :
1636 : /************************************************************************/
1637 : /* exportToWkt() */
1638 : /************************************************************************/
1639 :
1640 : /**
1641 : * \brief Convert this SRS into WKT 1 format.
1642 : *
1643 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1644 : * Issues</a> page for implementation details of WKT 1 in OGR.
1645 : *
1646 : * Note that the returned WKT string should be freed with
1647 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1648 : *
1649 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1650 : * option. Valid values are the one of the FORMAT option of
1651 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1652 : *
1653 : * This method is the same as the C function OSRExportToWkt().
1654 : *
1655 : * @param ppszResult the resulting string is returned in this pointer.
1656 : *
1657 : * @return OGRERR_NONE if successful.
1658 : */
1659 :
1660 12467 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1661 :
1662 : {
1663 12467 : return exportToWkt(ppszResult, nullptr);
1664 : }
1665 :
1666 : /************************************************************************/
1667 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1668 : /************************************************************************/
1669 :
1670 531 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1671 : bool onlyIfEPSGCode,
1672 : bool canModifyHorizPart)
1673 : {
1674 531 : PJ *ret = nullptr;
1675 531 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1676 : {
1677 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1678 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1679 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1680 26 : vertCRS &&
1681 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1682 : {
1683 : auto boundHoriz =
1684 : canModifyHorizPart
1685 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1686 3 : : proj_clone(ctx, horizCRS);
1687 : auto boundVert =
1688 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1689 3 : if (boundHoriz && boundVert)
1690 : {
1691 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1692 : boundHoriz, boundVert);
1693 : }
1694 3 : proj_destroy(boundHoriz);
1695 3 : proj_destroy(boundVert);
1696 : }
1697 13 : proj_destroy(horizCRS);
1698 13 : proj_destroy(vertCRS);
1699 : }
1700 996 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1701 478 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1702 : {
1703 173 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1704 : }
1705 531 : return ret;
1706 : }
1707 :
1708 : /************************************************************************/
1709 : /* exportToWkt() */
1710 : /************************************************************************/
1711 :
1712 : /**
1713 : * Convert this SRS into a WKT string.
1714 : *
1715 : * Note that the returned WKT string should be freed with
1716 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1717 : *
1718 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1719 : * Issues</a> page for implementation details of WKT 1 in OGR.
1720 : *
1721 : * @param ppszResult the resulting string is returned in this pointer.
1722 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1723 : * supported options are
1724 : * <ul>
1725 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1726 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1727 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1728 : * node is returned.
1729 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1730 : * node is returned.
1731 : * WKT1 is an alias of WKT1_GDAL.
1732 : * WKT2 will default to the latest revision implemented (currently
1733 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1734 : * </li>
1735 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1736 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1737 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1738 : * height (for example for use with LAS 1.4 WKT1).
1739 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1740 : * </ul>
1741 : *
1742 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1743 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1744 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1745 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1746 : * TOWGS84[] node may be added.
1747 : *
1748 : * @return OGRERR_NONE if successful.
1749 : * @since GDAL 3.0
1750 : */
1751 :
1752 17408 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1753 : const char *const *papszOptions) const
1754 : {
1755 : // In the past calling this method was thread-safe, even if we never
1756 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1757 : // so this is no longer thread-safe.
1758 34816 : std::lock_guard oLock(d->m_mutex);
1759 :
1760 17408 : d->refreshProjObj();
1761 17408 : if (!d->m_pj_crs)
1762 : {
1763 21 : *ppszResult = CPLStrdup("");
1764 21 : return OGRERR_FAILURE;
1765 : }
1766 :
1767 17387 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1768 : {
1769 0 : return d->m_poRoot->exportToWkt(ppszResult);
1770 : }
1771 :
1772 17387 : auto ctxt = d->getPROJContext();
1773 17387 : auto wktFormat = PJ_WKT1_GDAL;
1774 : const char *pszFormat =
1775 17387 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1776 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1777 17387 : if (EQUAL(pszFormat, "DEFAULT"))
1778 14608 : pszFormat = "";
1779 :
1780 17387 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1781 : {
1782 658 : wktFormat = PJ_WKT1_ESRI;
1783 : }
1784 16729 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1785 16026 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1786 : {
1787 708 : wktFormat = PJ_WKT1_GDAL;
1788 : }
1789 16021 : else if (EQUAL(pszFormat, "WKT2_2015"))
1790 : {
1791 347 : wktFormat = PJ_WKT2_2015;
1792 : }
1793 15674 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1794 15287 : EQUAL(pszFormat, "WKT2_2019"))
1795 : {
1796 1295 : wktFormat = PJ_WKT2_2018;
1797 : }
1798 14379 : else if (pszFormat[0] == '\0')
1799 : {
1800 : // cppcheck-suppress knownConditionTrueFalse
1801 14379 : if (IsDerivedGeographic())
1802 : {
1803 2 : wktFormat = PJ_WKT2_2018;
1804 : }
1805 28121 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1806 13744 : GetAxesCount() == 3)
1807 : {
1808 80 : wktFormat = PJ_WKT2_2018;
1809 : }
1810 : }
1811 : else
1812 : {
1813 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1814 0 : *ppszResult = CPLStrdup("");
1815 0 : return OGRERR_FAILURE;
1816 : }
1817 :
1818 34774 : CPLStringList aosOptions;
1819 17387 : if (wktFormat != PJ_WKT1_ESRI)
1820 : {
1821 16729 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1822 : }
1823 : aosOptions.SetNameValue(
1824 17387 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1825 :
1826 17387 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1827 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1828 17387 : if (pszAllowEllpsHeightAsVertCS)
1829 : {
1830 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1831 0 : pszAllowEllpsHeightAsVertCS);
1832 : }
1833 :
1834 17387 : PJ *boundCRS = nullptr;
1835 32392 : if (wktFormat == PJ_WKT1_GDAL &&
1836 15005 : CPLTestBool(CSLFetchNameValueDef(
1837 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1838 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1839 : {
1840 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1841 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1842 : }
1843 :
1844 34774 : CPLErrorAccumulator oErrorAccumulator;
1845 : const char *pszWKT;
1846 : {
1847 17387 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1848 17387 : CPL_IGNORE_RET_VAL(oAccumulator);
1849 17387 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1850 17387 : aosOptions.List());
1851 : }
1852 17389 : for (const auto &oError : oErrorAccumulator.GetErrors())
1853 : {
1854 32 : if (pszFormat[0] == '\0' &&
1855 14 : (oError.msg.find("Unsupported conversion method") !=
1856 2 : std::string::npos ||
1857 2 : oError.msg.find("can only be exported to WKT2") !=
1858 0 : std::string::npos ||
1859 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1860 : std::string::npos))
1861 : {
1862 14 : CPLErrorReset();
1863 : // If we cannot export in the default mode (WKT1), retry with WKT2
1864 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1865 14 : PJ_WKT2_2018, aosOptions.List());
1866 14 : break;
1867 : }
1868 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1869 : }
1870 :
1871 17387 : if (!pszWKT)
1872 : {
1873 2 : *ppszResult = CPLStrdup("");
1874 2 : proj_destroy(boundCRS);
1875 2 : return OGRERR_FAILURE;
1876 : }
1877 :
1878 17385 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1879 : {
1880 5 : OGR_SRSNode oRoot;
1881 5 : oRoot.importFromWkt(&pszWKT);
1882 5 : oRoot.StripNodes("AXIS");
1883 5 : if (EQUAL(pszFormat, "SFSQL"))
1884 : {
1885 3 : oRoot.StripNodes("TOWGS84");
1886 : }
1887 5 : oRoot.StripNodes("AUTHORITY");
1888 5 : oRoot.StripNodes("EXTENSION");
1889 : OGRErr eErr;
1890 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1891 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1892 : else
1893 3 : eErr = oRoot.exportToWkt(ppszResult);
1894 5 : proj_destroy(boundCRS);
1895 5 : return eErr;
1896 : }
1897 :
1898 17380 : *ppszResult = CPLStrdup(pszWKT);
1899 :
1900 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1901 17380 : if (wktFormat == PJ_WKT2_2018)
1902 : {
1903 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1904 : // related to a wrong EPSG code assigned to UTM South conversions
1905 1377 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1906 1377 : if (pszPtr)
1907 : {
1908 337 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1909 337 : const int nZone = atoi(pszPtr);
1910 1010 : while (*pszPtr >= '0' && *pszPtr <= '9')
1911 673 : ++pszPtr;
1912 337 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1913 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1914 : {
1915 1 : pszPtr += 3;
1916 1 : int nLevel = 0;
1917 1 : bool bInString = false;
1918 : // Find the ID node corresponding to this CONVERSION node
1919 480 : while (*pszPtr)
1920 : {
1921 480 : if (bInString)
1922 : {
1923 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1924 : {
1925 0 : ++pszPtr;
1926 : }
1927 197 : else if (*pszPtr == '"')
1928 : {
1929 17 : bInString = false;
1930 : }
1931 : }
1932 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1933 : {
1934 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1935 : 17000 + nZone)))
1936 : {
1937 1 : CPLAssert(pszPtr[11] == '7');
1938 1 : CPLAssert(pszPtr[12] == '0');
1939 1 : pszPtr[11] = '6';
1940 1 : pszPtr[12] = '1';
1941 : }
1942 1 : break;
1943 : }
1944 282 : else if (*pszPtr == '"')
1945 : {
1946 17 : bInString = true;
1947 : }
1948 265 : else if (*pszPtr == '[')
1949 : {
1950 17 : ++nLevel;
1951 : }
1952 248 : else if (*pszPtr == ']')
1953 : {
1954 17 : --nLevel;
1955 : }
1956 :
1957 479 : ++pszPtr;
1958 : }
1959 : }
1960 : }
1961 : }
1962 : #endif
1963 :
1964 17380 : proj_destroy(boundCRS);
1965 17380 : return OGRERR_NONE;
1966 : }
1967 :
1968 : /************************************************************************/
1969 : /* exportToWkt() */
1970 : /************************************************************************/
1971 :
1972 : /**
1973 : * Convert this SRS into a WKT string.
1974 : *
1975 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1976 : * Issues</a> page for implementation details of WKT 1 in OGR.
1977 : *
1978 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1979 : * supported options are
1980 : * <ul>
1981 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1982 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1983 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1984 : * node is returned.
1985 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1986 : * node is returned.
1987 : * WKT1 is an alias of WKT1_GDAL.
1988 : * WKT2 will default to the latest revision implemented (currently
1989 : * WKT2_2019)
1990 : * </li>
1991 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1992 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1993 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1994 : * height (for example for use with LAS 1.4 WKT1).
1995 : * Requires PROJ 7.2.1.</li>
1996 : * </ul>
1997 : *
1998 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1999 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
2000 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
2001 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
2002 : * TOWGS84[] node may be added.
2003 : *
2004 : * @return a non-empty string if successful.
2005 : * @since GDAL 3.9
2006 : */
2007 :
2008 : std::string
2009 531 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2010 : {
2011 531 : std::string osWKT;
2012 531 : char *pszWKT = nullptr;
2013 531 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2014 531 : osWKT = pszWKT;
2015 531 : CPLFree(pszWKT);
2016 1062 : return osWKT;
2017 : }
2018 :
2019 : /************************************************************************/
2020 : /* OSRExportToWkt() */
2021 : /************************************************************************/
2022 :
2023 : /**
2024 : * \brief Convert this SRS into WKT 1 format.
2025 : *
2026 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2027 : * Issues</a> page for implementation details of WKT in OGR.
2028 : *
2029 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2030 : * option. Valid values are the one of the FORMAT option of
2031 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2032 : *
2033 : * This function is the same as OGRSpatialReference::exportToWkt().
2034 : */
2035 :
2036 1048 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2037 :
2038 : {
2039 1048 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2040 :
2041 1048 : *ppszReturn = nullptr;
2042 :
2043 1048 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2044 : }
2045 :
2046 : /************************************************************************/
2047 : /* OSRExportToWktEx() */
2048 : /************************************************************************/
2049 :
2050 : /**
2051 : * \brief Convert this SRS into WKT format.
2052 : *
2053 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2054 : * ppszResult,const char* const* papszOptions ) const
2055 : *
2056 : * @since GDAL 3.0
2057 : */
2058 :
2059 1308 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2060 : const char *const *papszOptions)
2061 : {
2062 1308 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2063 :
2064 1308 : *ppszReturn = nullptr;
2065 :
2066 1308 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2067 : }
2068 :
2069 : /************************************************************************/
2070 : /* exportToPROJJSON() */
2071 : /************************************************************************/
2072 :
2073 : /**
2074 : * Convert this SRS into a PROJJSON string.
2075 : *
2076 : * Note that the returned JSON string should be freed with
2077 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2078 : *
2079 : * @param ppszResult the resulting string is returned in this pointer.
2080 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2081 : * supported options are
2082 : * <ul>
2083 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2084 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2085 : * on).</li>
2086 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2087 : * disable it.</li>
2088 : * </ul>
2089 : *
2090 : * @return OGRERR_NONE if successful.
2091 : * @since GDAL 3.1 and PROJ 6.2
2092 : */
2093 :
2094 2852 : OGRErr OGRSpatialReference::exportToPROJJSON(
2095 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2096 : {
2097 5704 : TAKE_OPTIONAL_LOCK();
2098 :
2099 2852 : d->refreshProjObj();
2100 2852 : if (!d->m_pj_crs)
2101 : {
2102 1 : *ppszResult = nullptr;
2103 1 : return OGRERR_FAILURE;
2104 : }
2105 :
2106 : const char *pszPROJJSON =
2107 2851 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2108 :
2109 2851 : if (!pszPROJJSON)
2110 : {
2111 0 : *ppszResult = CPLStrdup("");
2112 0 : return OGRERR_FAILURE;
2113 : }
2114 :
2115 2851 : *ppszResult = CPLStrdup(pszPROJJSON);
2116 :
2117 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2118 : {
2119 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2120 : // related to a wrong EPSG code assigned to UTM South conversions
2121 2851 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2122 2851 : if (pszPtr)
2123 : {
2124 257 : pszPtr += strlen("\"name\": \"UTM zone ");
2125 257 : const int nZone = atoi(pszPtr);
2126 770 : while (*pszPtr >= '0' && *pszPtr <= '9')
2127 513 : ++pszPtr;
2128 257 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2129 : {
2130 4 : pszPtr += 2;
2131 4 : int nLevel = 0;
2132 4 : bool bInString = false;
2133 : // Find the id node corresponding to this conversion node
2134 5299 : while (*pszPtr)
2135 : {
2136 5299 : if (bInString)
2137 : {
2138 1950 : if (*pszPtr == '\\')
2139 : {
2140 0 : ++pszPtr;
2141 : }
2142 1950 : else if (*pszPtr == '"')
2143 : {
2144 244 : bInString = false;
2145 : }
2146 : }
2147 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2148 : {
2149 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2150 : const char *pszAuthEPSG =
2151 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2152 4 : char *pszCode = strstr(
2153 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2154 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2155 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2156 4 : pszNextEndCurl - pszCode > 0)
2157 : {
2158 4 : CPLAssert(pszCode[9] == '7');
2159 4 : CPLAssert(pszCode[10] == '0');
2160 4 : pszCode[9] = '6';
2161 4 : pszCode[10] = '1';
2162 : }
2163 4 : break;
2164 : }
2165 3345 : else if (*pszPtr == '"')
2166 : {
2167 244 : bInString = true;
2168 : }
2169 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2170 : {
2171 60 : ++nLevel;
2172 : }
2173 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2174 : {
2175 60 : --nLevel;
2176 : }
2177 :
2178 5295 : ++pszPtr;
2179 : }
2180 : }
2181 : }
2182 : }
2183 : #endif
2184 :
2185 2851 : return OGRERR_NONE;
2186 : }
2187 :
2188 : /************************************************************************/
2189 : /* OSRExportToPROJJSON() */
2190 : /************************************************************************/
2191 :
2192 : /**
2193 : * \brief Convert this SRS into PROJJSON format.
2194 : *
2195 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2196 : *
2197 : * @since GDAL 3.1 and PROJ 6.2
2198 : */
2199 :
2200 2 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2201 : const char *const *papszOptions)
2202 : {
2203 2 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2204 :
2205 2 : *ppszReturn = nullptr;
2206 :
2207 2 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2208 : }
2209 :
2210 : /************************************************************************/
2211 : /* importFromWkt() */
2212 : /************************************************************************/
2213 :
2214 : /**
2215 : * \brief Import from WKT string.
2216 : *
2217 : * This method will wipe the existing SRS definition, and
2218 : * reassign it based on the contents of the passed WKT string. Only as
2219 : * much of the input string as needed to construct this SRS is consumed from
2220 : * the input string, and the input string pointer
2221 : * is then updated to point to the remaining (unused) input.
2222 : *
2223 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2224 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2225 : * and the coordinate epoch potentially present used as the coordinate epoch
2226 : * property of the OGRSpatialReference object.
2227 : *
2228 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2229 : * Issues</a> page for implementation details of WKT in OGR.
2230 : *
2231 : * This method is the same as the C function OSRImportFromWkt().
2232 : *
2233 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2234 : * point to remaining unused input text.
2235 : *
2236 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2237 : * fails for any reason.
2238 : */
2239 :
2240 20266 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2241 :
2242 : {
2243 20266 : return importFromWkt(ppszInput, nullptr);
2244 : }
2245 :
2246 : /************************************************************************/
2247 : /* importFromWkt() */
2248 : /************************************************************************/
2249 :
2250 : /*! @cond Doxygen_Suppress */
2251 :
2252 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2253 : CSLConstList papszOptions)
2254 :
2255 : {
2256 21 : return importFromWkt(&pszInput, papszOptions);
2257 : }
2258 :
2259 20287 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2260 : CSLConstList papszOptions)
2261 :
2262 : {
2263 40574 : TAKE_OPTIONAL_LOCK();
2264 :
2265 20287 : if (!ppszInput || !*ppszInput)
2266 0 : return OGRERR_FAILURE;
2267 :
2268 20287 : if (strlen(*ppszInput) > 100 * 1000 &&
2269 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2270 : {
2271 0 : CPLError(CE_Failure, CPLE_NotSupported,
2272 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2273 : "You can remove this limitation by definition the "
2274 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2275 0 : return OGRERR_FAILURE;
2276 : }
2277 :
2278 20287 : Clear();
2279 :
2280 20287 : bool canCache = false;
2281 20287 : auto tlsCache = OSRGetProjTLSCache();
2282 40574 : std::string osWkt;
2283 20287 : if (**ppszInput)
2284 : {
2285 19731 : osWkt = *ppszInput;
2286 19731 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2287 19731 : if (cachedObj)
2288 : {
2289 17745 : d->setPjCRS(cachedObj);
2290 : }
2291 : else
2292 : {
2293 3972 : CPLStringList aosOptions(papszOptions);
2294 1986 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2295 1986 : aosOptions.SetNameValue("STRICT", "NO");
2296 1986 : PROJ_STRING_LIST warnings = nullptr;
2297 1986 : PROJ_STRING_LIST errors = nullptr;
2298 1986 : auto ctxt = d->getPROJContext();
2299 1986 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2300 : &warnings, &errors);
2301 1986 : d->setPjCRS(pj);
2302 :
2303 2039 : for (auto iter = warnings; iter && *iter; ++iter)
2304 : {
2305 53 : d->m_wktImportWarnings.push_back(*iter);
2306 : }
2307 2223 : for (auto iter = errors; iter && *iter; ++iter)
2308 : {
2309 237 : d->m_wktImportErrors.push_back(*iter);
2310 237 : if (!d->m_pj_crs)
2311 : {
2312 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2313 : }
2314 : }
2315 1986 : if (warnings == nullptr && errors == nullptr)
2316 : {
2317 1705 : canCache = true;
2318 : }
2319 1986 : proj_string_list_destroy(warnings);
2320 1986 : proj_string_list_destroy(errors);
2321 : }
2322 : }
2323 20287 : if (!d->m_pj_crs)
2324 590 : return OGRERR_CORRUPT_DATA;
2325 :
2326 : // Only accept CRS objects
2327 19697 : if (!proj_is_crs(d->m_pj_crs))
2328 : {
2329 0 : Clear();
2330 0 : return OGRERR_CORRUPT_DATA;
2331 : }
2332 :
2333 19697 : if (canCache)
2334 : {
2335 1705 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2336 : }
2337 :
2338 19697 : if (strstr(*ppszInput, "CENTER_LONG"))
2339 : {
2340 0 : auto poRoot = new OGR_SRSNode();
2341 0 : d->setRoot(poRoot);
2342 0 : const char *pszTmp = *ppszInput;
2343 0 : poRoot->importFromWkt(&pszTmp);
2344 0 : d->m_bHasCenterLong = true;
2345 : }
2346 :
2347 : // TODO? we don't really update correctly since we assume that the
2348 : // passed string is only WKT.
2349 19697 : *ppszInput += strlen(*ppszInput);
2350 19697 : return OGRERR_NONE;
2351 :
2352 : #if no_longer_implemented_for_now
2353 : /* -------------------------------------------------------------------- */
2354 : /* The following seems to try and detect and unconsumed */
2355 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2356 : /* import and attach it to the existing root. Likely we will */
2357 : /* need to extend this somewhat to bring it into an acceptable */
2358 : /* OGRSpatialReference organization at some point. */
2359 : /* -------------------------------------------------------------------- */
2360 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2361 : {
2362 : if (((*ppszInput)[0]) == ',')
2363 : (*ppszInput)++;
2364 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2365 : poRoot->AddChild(poNewChild);
2366 : return poNewChild->importFromWkt(ppszInput);
2367 : }
2368 : #endif
2369 : }
2370 :
2371 : /*! @endcond */
2372 :
2373 : /**
2374 : * \brief Import from WKT string.
2375 : *
2376 : * This method will wipe the existing SRS definition, and
2377 : * reassign it based on the contents of the passed WKT string. Only as
2378 : * much of the input string as needed to construct this SRS is consumed from
2379 : * the input string, and the input string pointer
2380 : * is then updated to point to the remaining (unused) input.
2381 : *
2382 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2383 : * Issues</a> page for implementation details of WKT in OGR.
2384 : *
2385 : * This method is the same as the C function OSRImportFromWkt().
2386 : *
2387 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2388 : * point to remaining unused input text.
2389 : *
2390 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2391 : * fails for any reason.
2392 : * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2393 : * char*)
2394 : */
2395 :
2396 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2397 :
2398 : {
2399 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2400 : }
2401 :
2402 : /**
2403 : * \brief Import from WKT string.
2404 : *
2405 : * This method will wipe the existing SRS definition, and
2406 : * reassign it based on the contents of the passed WKT string. Only as
2407 : * much of the input string as needed to construct this SRS is consumed from
2408 : * the input string, and the input string pointer
2409 : * is then updated to point to the remaining (unused) input.
2410 : *
2411 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2412 : * Issues</a> page for implementation details of WKT in OGR.
2413 : *
2414 : * @param pszInput Input WKT
2415 : *
2416 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2417 : * fails for any reason.
2418 : */
2419 :
2420 20001 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2421 : {
2422 20001 : return importFromWkt(&pszInput);
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* Validate() */
2427 : /************************************************************************/
2428 :
2429 : /**
2430 : * \brief Validate CRS imported with importFromWkt() or with modified with
2431 : * direct node manipulations. Otherwise the CRS should be always valid.
2432 : *
2433 : * This method attempts to verify that the spatial reference system is
2434 : * well formed, and consists of known tokens. The validation is not
2435 : * comprehensive.
2436 : *
2437 : * This method is the same as the C function OSRValidate().
2438 : *
2439 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2440 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2441 : * but contains non-standard PROJECTION[] values.
2442 : */
2443 :
2444 116 : OGRErr OGRSpatialReference::Validate() const
2445 :
2446 : {
2447 232 : TAKE_OPTIONAL_LOCK();
2448 :
2449 154 : for (const auto &str : d->m_wktImportErrors)
2450 : {
2451 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2452 : }
2453 116 : for (const auto &str : d->m_wktImportWarnings)
2454 : {
2455 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2456 : }
2457 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2458 : {
2459 37 : return OGRERR_CORRUPT_DATA;
2460 : }
2461 79 : if (!d->m_wktImportWarnings.empty())
2462 : {
2463 0 : return OGRERR_UNSUPPORTED_SRS;
2464 : }
2465 79 : return OGRERR_NONE;
2466 : }
2467 :
2468 : /************************************************************************/
2469 : /* OSRValidate() */
2470 : /************************************************************************/
2471 : /**
2472 : * \brief Validate SRS tokens.
2473 : *
2474 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2475 : */
2476 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2477 :
2478 : {
2479 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2480 :
2481 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2482 : }
2483 :
2484 : /************************************************************************/
2485 : /* OSRImportFromWkt() */
2486 : /************************************************************************/
2487 :
2488 : /**
2489 : * \brief Import from WKT string.
2490 : *
2491 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2492 : * Issues</a> page for implementation details of WKT in OGR.
2493 : *
2494 : * This function is the same as OGRSpatialReference::importFromWkt().
2495 : */
2496 :
2497 265 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2498 :
2499 : {
2500 265 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2501 :
2502 265 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2503 : }
2504 :
2505 : /************************************************************************/
2506 : /* SetNode() */
2507 : /************************************************************************/
2508 :
2509 : /**
2510 : * \brief Set attribute value in spatial reference.
2511 : *
2512 : * Missing intermediate nodes in the path will be created if not already
2513 : * in existence. If the attribute has no children one will be created and
2514 : * assigned the value otherwise the zeroth child will be assigned the value.
2515 : *
2516 : * This method does the same as the C function OSRSetAttrValue().
2517 : *
2518 : * @param pszNodePath full path to attribute to be set. For instance
2519 : * "PROJCS|GEOGCS|UNIT".
2520 : *
2521 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2522 : * This may be NULL if you just want to force creation of the intermediate
2523 : * path.
2524 : *
2525 : * @return OGRERR_NONE on success.
2526 : */
2527 :
2528 583 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2529 : const char *pszNewNodeValue)
2530 :
2531 : {
2532 1166 : TAKE_OPTIONAL_LOCK();
2533 :
2534 : char **papszPathTokens =
2535 583 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2536 :
2537 583 : if (CSLCount(papszPathTokens) < 1)
2538 : {
2539 0 : CSLDestroy(papszPathTokens);
2540 0 : return OGRERR_FAILURE;
2541 : }
2542 :
2543 1018 : if (GetRoot() == nullptr ||
2544 435 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2545 : {
2546 268 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2547 116 : CSLCount(papszPathTokens) == 1)
2548 : {
2549 116 : CSLDestroy(papszPathTokens);
2550 116 : return SetProjCS(pszNewNodeValue);
2551 : }
2552 : else
2553 : {
2554 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2555 : }
2556 : }
2557 :
2558 467 : OGR_SRSNode *poNode = GetRoot();
2559 725 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2560 : {
2561 258 : int j = 0; // Used after for.
2562 :
2563 645 : for (; j < poNode->GetChildCount(); j++)
2564 : {
2565 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2566 : {
2567 198 : poNode = poNode->GetChild(j);
2568 198 : j = -1;
2569 198 : break;
2570 : }
2571 : }
2572 :
2573 258 : if (j != -1)
2574 : {
2575 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2576 60 : poNode->AddChild(poNewNode);
2577 60 : poNode = poNewNode;
2578 : }
2579 : }
2580 :
2581 467 : CSLDestroy(papszPathTokens);
2582 :
2583 467 : if (pszNewNodeValue != nullptr)
2584 : {
2585 467 : if (poNode->GetChildCount() > 0)
2586 371 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2587 : else
2588 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2589 : };
2590 467 : return OGRERR_NONE;
2591 : }
2592 :
2593 : /************************************************************************/
2594 : /* OSRSetAttrValue() */
2595 : /************************************************************************/
2596 :
2597 : /**
2598 : * \brief Set attribute value in spatial reference.
2599 : *
2600 : * This function is the same as OGRSpatialReference::SetNode()
2601 : */
2602 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2603 : const char *pszPath, const char *pszValue)
2604 :
2605 : {
2606 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2607 :
2608 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2609 : }
2610 :
2611 : /************************************************************************/
2612 : /* SetNode() */
2613 : /************************************************************************/
2614 :
2615 : /**
2616 : * \brief Set attribute value in spatial reference.
2617 : *
2618 : * Missing intermediate nodes in the path will be created if not already
2619 : * in existence. If the attribute has no children one will be created and
2620 : * assigned the value otherwise the zeroth child will be assigned the value.
2621 : *
2622 : * This method does the same as the C function OSRSetAttrValue().
2623 : *
2624 : * @param pszNodePath full path to attribute to be set. For instance
2625 : * "PROJCS|GEOGCS|UNIT".
2626 : *
2627 : * @param dfValue value to be assigned to node.
2628 : *
2629 : * @return OGRERR_NONE on success.
2630 : */
2631 :
2632 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2633 :
2634 : {
2635 0 : char szValue[64] = {'\0'};
2636 :
2637 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2638 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2639 : else
2640 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2641 :
2642 0 : return SetNode(pszNodePath, szValue);
2643 : }
2644 :
2645 : /************************************************************************/
2646 : /* SetAngularUnits() */
2647 : /************************************************************************/
2648 :
2649 : /**
2650 : * \brief Set the angular units for the geographic coordinate system.
2651 : *
2652 : * This method creates a UNIT subnode with the specified values as a
2653 : * child of the GEOGCS node.
2654 : *
2655 : * This method does the same as the C function OSRSetAngularUnits().
2656 : *
2657 : * @param pszUnitsName the units name to be used. Some preferred units
2658 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2659 : *
2660 : * @param dfInRadians the value to multiple by an angle in the indicated
2661 : * units to transform to radians. Some standard conversion factors can
2662 : * be found in ogr_srs_api.h.
2663 : *
2664 : * @return OGRERR_NONE on success.
2665 : */
2666 :
2667 1521 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2668 : double dfInRadians)
2669 :
2670 : {
2671 3042 : TAKE_OPTIONAL_LOCK();
2672 :
2673 1521 : d->bNormInfoSet = FALSE;
2674 :
2675 1521 : d->refreshProjObj();
2676 1521 : if (!d->m_pj_crs)
2677 0 : return OGRERR_FAILURE;
2678 1521 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2679 1521 : if (!geodCRS)
2680 0 : return OGRERR_FAILURE;
2681 1521 : proj_destroy(geodCRS);
2682 1521 : d->demoteFromBoundCRS();
2683 1521 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2684 : pszUnitsName, dfInRadians,
2685 : nullptr, nullptr));
2686 1521 : d->undoDemoteFromBoundCRS();
2687 :
2688 1521 : d->m_osAngularUnits = pszUnitsName;
2689 1521 : d->m_dfAngularUnitToRadian = dfInRadians;
2690 :
2691 1521 : return OGRERR_NONE;
2692 : }
2693 :
2694 : /************************************************************************/
2695 : /* OSRSetAngularUnits() */
2696 : /************************************************************************/
2697 :
2698 : /**
2699 : * \brief Set the angular units for the geographic coordinate system.
2700 : *
2701 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2702 : */
2703 48 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2704 : double dfInRadians)
2705 :
2706 : {
2707 48 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2708 :
2709 48 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2710 : }
2711 :
2712 : /************************************************************************/
2713 : /* GetAngularUnits() */
2714 : /************************************************************************/
2715 :
2716 : /**
2717 : * \brief Fetch angular geographic coordinate system units.
2718 : *
2719 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2720 : * will be assumed. This method only checks directly under the GEOGCS node
2721 : * for units.
2722 : *
2723 : * This method does the same thing as the C function OSRGetAngularUnits().
2724 : *
2725 : * @param ppszName a pointer to be updated with the pointer to the units name.
2726 : * The returned value remains internal to the OGRSpatialReference and should
2727 : * not be freed, or modified. It may be invalidated on the next
2728 : * OGRSpatialReference call.
2729 : *
2730 : * @return the value to multiply by angular distances to transform them to
2731 : * radians.
2732 : */
2733 :
2734 8642 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2735 :
2736 : {
2737 17284 : TAKE_OPTIONAL_LOCK();
2738 :
2739 8642 : d->refreshProjObj();
2740 :
2741 8642 : if (!d->m_osAngularUnits.empty())
2742 : {
2743 2379 : if (ppszName != nullptr)
2744 182 : *ppszName = d->m_osAngularUnits.c_str();
2745 2379 : return d->m_dfAngularUnitToRadian;
2746 : }
2747 :
2748 : do
2749 : {
2750 6263 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2751 : {
2752 113 : break;
2753 : }
2754 :
2755 : auto geodCRS =
2756 6152 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2757 6152 : if (!geodCRS)
2758 : {
2759 0 : break;
2760 : }
2761 : auto coordSys =
2762 6152 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2763 6152 : proj_destroy(geodCRS);
2764 6152 : if (!coordSys)
2765 : {
2766 0 : break;
2767 : }
2768 6152 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2769 : PJ_CS_TYPE_ELLIPSOIDAL)
2770 : {
2771 2 : proj_destroy(coordSys);
2772 2 : break;
2773 : }
2774 :
2775 6150 : double dfConvFactor = 0.0;
2776 6150 : const char *pszUnitName = nullptr;
2777 6150 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2778 : nullptr, nullptr, &dfConvFactor,
2779 : &pszUnitName, nullptr, nullptr))
2780 : {
2781 0 : proj_destroy(coordSys);
2782 0 : break;
2783 : }
2784 :
2785 6150 : d->m_osAngularUnits = pszUnitName;
2786 :
2787 6150 : proj_destroy(coordSys);
2788 6150 : d->m_dfAngularUnitToRadian = dfConvFactor;
2789 : } while (false);
2790 :
2791 6263 : if (d->m_osAngularUnits.empty())
2792 : {
2793 113 : d->m_osAngularUnits = "degree";
2794 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2795 : }
2796 :
2797 6263 : if (ppszName != nullptr)
2798 3218 : *ppszName = d->m_osAngularUnits.c_str();
2799 6263 : return d->m_dfAngularUnitToRadian;
2800 : }
2801 :
2802 : /**
2803 : * \brief Fetch angular geographic coordinate system units.
2804 : *
2805 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2806 : * will be assumed. This method only checks directly under the GEOGCS node
2807 : * for units.
2808 : *
2809 : * This method does the same thing as the C function OSRGetAngularUnits().
2810 : *
2811 : * @param ppszName a pointer to be updated with the pointer to the units name.
2812 : * The returned value remains internal to the OGRSpatialReference and should
2813 : * not be freed, or modified. It may be invalidated on the next
2814 : * OGRSpatialReference call.
2815 : *
2816 : * @return the value to multiply by angular distances to transform them to
2817 : * radians.
2818 : * @deprecated Use GetAngularUnits(const char**) const.
2819 : */
2820 :
2821 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2822 :
2823 : {
2824 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2825 : }
2826 :
2827 : /************************************************************************/
2828 : /* OSRGetAngularUnits() */
2829 : /************************************************************************/
2830 :
2831 : /**
2832 : * \brief Fetch angular geographic coordinate system units.
2833 : *
2834 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2835 : */
2836 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2837 :
2838 : {
2839 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2840 :
2841 1 : return ToPointer(hSRS)->GetAngularUnits(
2842 1 : const_cast<const char **>(ppszName));
2843 : }
2844 :
2845 : /************************************************************************/
2846 : /* SetLinearUnitsAndUpdateParameters() */
2847 : /************************************************************************/
2848 :
2849 : /**
2850 : * \brief Set the linear units for the projection.
2851 : *
2852 : * This method creates a UNIT subnode with the specified values as a
2853 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2854 : * SetLinearUnits() method, but it also updates all existing linear
2855 : * projection parameter values from the old units to the new units.
2856 : *
2857 : * @param pszName the units name to be used. Some preferred units
2858 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2859 : * and SRS_UL_US_FOOT.
2860 : *
2861 : * @param dfInMeters the value to multiple by a length in the indicated
2862 : * units to transform to meters. Some standard conversion factors can
2863 : * be found in ogr_srs_api.h.
2864 : *
2865 : * @param pszUnitAuthority Unit authority name. Or nullptr
2866 : *
2867 : * @param pszUnitCode Unit code. Or nullptr
2868 : *
2869 : * @return OGRERR_NONE on success.
2870 : */
2871 :
2872 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2873 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2874 : const char *pszUnitCode)
2875 :
2876 : {
2877 78 : TAKE_OPTIONAL_LOCK();
2878 :
2879 39 : if (dfInMeters <= 0.0)
2880 0 : return OGRERR_FAILURE;
2881 :
2882 39 : d->refreshProjObj();
2883 39 : if (!d->m_pj_crs)
2884 0 : return OGRERR_FAILURE;
2885 :
2886 39 : d->demoteFromBoundCRS();
2887 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2888 : {
2889 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2890 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2891 : pszUnitAuthority, pszUnitCode, true));
2892 : }
2893 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2894 : pszName, dfInMeters,
2895 : pszUnitAuthority, pszUnitCode));
2896 39 : d->undoDemoteFromBoundCRS();
2897 :
2898 39 : d->m_osLinearUnits = pszName;
2899 39 : d->dfToMeter = dfInMeters;
2900 :
2901 39 : return OGRERR_NONE;
2902 : }
2903 :
2904 : /************************************************************************/
2905 : /* OSRSetLinearUnitsAndUpdateParameters() */
2906 : /************************************************************************/
2907 :
2908 : /**
2909 : * \brief Set the linear units for the projection.
2910 : *
2911 : * This function is the same as
2912 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2913 : */
2914 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2915 : const char *pszUnits,
2916 : double dfInMeters)
2917 :
2918 : {
2919 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2920 : OGRERR_FAILURE);
2921 :
2922 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2923 1 : dfInMeters);
2924 : }
2925 :
2926 : /************************************************************************/
2927 : /* SetLinearUnits() */
2928 : /************************************************************************/
2929 :
2930 : /**
2931 : * \brief Set the linear units for the projection.
2932 : *
2933 : * This method creates a UNIT subnode with the specified values as a
2934 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2935 : * Geographic 3D CRS the vertical axis units will be set.
2936 : *
2937 : * This method does the same as the C function OSRSetLinearUnits().
2938 : *
2939 : * @param pszUnitsName the units name to be used. Some preferred units
2940 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2941 : * and SRS_UL_US_FOOT.
2942 : *
2943 : * @param dfInMeters the value to multiple by a length in the indicated
2944 : * units to transform to meters. Some standard conversion factors can
2945 : * be found in ogr_srs_api.h.
2946 : *
2947 : * @return OGRERR_NONE on success.
2948 : */
2949 :
2950 7404 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2951 : double dfInMeters)
2952 :
2953 : {
2954 7404 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2955 : }
2956 :
2957 : /************************************************************************/
2958 : /* OSRSetLinearUnits() */
2959 : /************************************************************************/
2960 :
2961 : /**
2962 : * \brief Set the linear units for the projection.
2963 : *
2964 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2965 : */
2966 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2967 : double dfInMeters)
2968 :
2969 : {
2970 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2971 :
2972 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2973 : }
2974 :
2975 : /************************************************************************/
2976 : /* SetTargetLinearUnits() */
2977 : /************************************************************************/
2978 :
2979 : /**
2980 : * \brief Set the linear units for the projection.
2981 : *
2982 : * This method creates a UNIT subnode with the specified values as a
2983 : * child of the target node.
2984 : *
2985 : * This method does the same as the C function OSRSetTargetLinearUnits().
2986 : *
2987 : * @param pszTargetKey the keyword to set the linear units for.
2988 : * i.e. "PROJCS" or "VERT_CS"
2989 : *
2990 : * @param pszUnitsName the units name to be used. Some preferred units
2991 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2992 : * and SRS_UL_US_FOOT.
2993 : *
2994 : * @param dfInMeters the value to multiple by a length in the indicated
2995 : * units to transform to meters. Some standard conversion factors can
2996 : * be found in ogr_srs_api.h.
2997 : *
2998 : * @param pszUnitAuthority Unit authority name. Or nullptr
2999 : *
3000 : * @param pszUnitCode Unit code. Or nullptr
3001 : *
3002 : * @return OGRERR_NONE on success.
3003 : *
3004 : */
3005 :
3006 11856 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3007 : const char *pszUnitsName,
3008 : double dfInMeters,
3009 : const char *pszUnitAuthority,
3010 : const char *pszUnitCode)
3011 :
3012 : {
3013 23712 : TAKE_OPTIONAL_LOCK();
3014 :
3015 11856 : if (dfInMeters <= 0.0)
3016 0 : return OGRERR_FAILURE;
3017 :
3018 11856 : d->refreshProjObj();
3019 11856 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3020 11856 : if (pszTargetKey == nullptr)
3021 : {
3022 11856 : if (!d->m_pj_crs)
3023 0 : return OGRERR_FAILURE;
3024 :
3025 11856 : d->demoteFromBoundCRS();
3026 11856 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3027 : {
3028 17914 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3029 8957 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3030 : pszUnitAuthority, pszUnitCode, false));
3031 : }
3032 23712 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3033 11856 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3034 : pszUnitAuthority, pszUnitCode));
3035 11856 : d->undoDemoteFromBoundCRS();
3036 :
3037 11856 : d->m_osLinearUnits = pszUnitsName;
3038 11856 : d->dfToMeter = dfInMeters;
3039 :
3040 11856 : return OGRERR_NONE;
3041 : }
3042 :
3043 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3044 :
3045 0 : if (poCS == nullptr)
3046 0 : return OGRERR_FAILURE;
3047 :
3048 0 : char szValue[128] = {'\0'};
3049 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3050 0 : dfInMeters > std::numeric_limits<int>::min() &&
3051 0 : dfInMeters == static_cast<int>(dfInMeters))
3052 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3053 : else
3054 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3055 :
3056 0 : OGR_SRSNode *poUnits = nullptr;
3057 0 : if (poCS->FindChild("UNIT") >= 0)
3058 : {
3059 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3060 0 : if (poUnits->GetChildCount() < 2)
3061 0 : return OGRERR_FAILURE;
3062 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3063 0 : poUnits->GetChild(1)->SetValue(szValue);
3064 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3065 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3066 : }
3067 : else
3068 : {
3069 0 : poUnits = new OGR_SRSNode("UNIT");
3070 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3071 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3072 :
3073 0 : poCS->AddChild(poUnits);
3074 : }
3075 :
3076 0 : return OGRERR_NONE;
3077 : }
3078 :
3079 : /************************************************************************/
3080 : /* OSRSetLinearUnits() */
3081 : /************************************************************************/
3082 :
3083 : /**
3084 : * \brief Set the linear units for the target node.
3085 : *
3086 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3087 : *
3088 : */
3089 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3090 : const char *pszTargetKey, const char *pszUnits,
3091 : double dfInMeters)
3092 :
3093 : {
3094 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3095 :
3096 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3097 1 : dfInMeters);
3098 : }
3099 :
3100 : /************************************************************************/
3101 : /* GetLinearUnits() */
3102 : /************************************************************************/
3103 :
3104 : /**
3105 : * \brief Fetch linear projection units.
3106 : *
3107 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3108 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3109 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3110 : * axis units will be returned.
3111 : *
3112 : * This method does the same thing as the C function OSRGetLinearUnits()
3113 : *
3114 : * @param ppszName a pointer to be updated with the pointer to the units name.
3115 : * The returned value remains internal to the OGRSpatialReference and should
3116 : * not be freed, or modified. It may be invalidated on the next
3117 : * OGRSpatialReference call.
3118 : *
3119 : * @return the value to multiply by linear distances to transform them to
3120 : * meters.
3121 : * @deprecated Use GetLinearUnits(const char**) const.
3122 : */
3123 :
3124 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3125 :
3126 : {
3127 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3128 : }
3129 :
3130 : /**
3131 : * \brief Fetch linear projection units.
3132 : *
3133 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3134 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3135 : * for units.
3136 : *
3137 : * This method does the same thing as the C function OSRGetLinearUnits()
3138 : *
3139 : * @param ppszName a pointer to be updated with the pointer to the units name.
3140 : * The returned value remains internal to the OGRSpatialReference and should
3141 : * not be freed, or modified. It may be invalidated on the next
3142 : * OGRSpatialReference call.
3143 : *
3144 : * @return the value to multiply by linear distances to transform them to
3145 : * meters.
3146 : */
3147 :
3148 21345 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3149 :
3150 : {
3151 21345 : return GetTargetLinearUnits(nullptr, ppszName);
3152 : }
3153 :
3154 : /************************************************************************/
3155 : /* OSRGetLinearUnits() */
3156 : /************************************************************************/
3157 :
3158 : /**
3159 : * \brief Fetch linear projection units.
3160 : *
3161 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3162 : */
3163 255 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3164 :
3165 : {
3166 255 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3167 :
3168 255 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3169 : }
3170 :
3171 : /************************************************************************/
3172 : /* GetTargetLinearUnits() */
3173 : /************************************************************************/
3174 :
3175 : /**
3176 : * \brief Fetch linear units for target.
3177 : *
3178 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3179 : *
3180 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3181 : *
3182 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3183 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3184 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3185 : * @param ppszName a pointer to be updated with the pointer to the units name.
3186 : * The returned value remains internal to the OGRSpatialReference and should not
3187 : * be freed, or modified. It may be invalidated on the next
3188 : * OGRSpatialReference call. ppszName can be set to NULL.
3189 : *
3190 : * @return the value to multiply by linear distances to transform them to
3191 : * meters.
3192 : *
3193 : * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3194 : * const.
3195 : */
3196 :
3197 21496 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3198 : const char **ppszName) const
3199 :
3200 : {
3201 42992 : TAKE_OPTIONAL_LOCK();
3202 :
3203 21496 : d->refreshProjObj();
3204 :
3205 21496 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3206 21496 : if (pszTargetKey == nullptr)
3207 : {
3208 : // Use cached result if available
3209 21405 : if (!d->m_osLinearUnits.empty())
3210 : {
3211 9105 : if (ppszName)
3212 8248 : *ppszName = d->m_osLinearUnits.c_str();
3213 9105 : return d->dfToMeter;
3214 : }
3215 :
3216 : while (true)
3217 : {
3218 12300 : if (d->m_pj_crs == nullptr)
3219 : {
3220 242 : break;
3221 : }
3222 :
3223 12058 : d->demoteFromBoundCRS();
3224 12058 : PJ *coordSys = nullptr;
3225 12058 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3226 : {
3227 37 : for (int iComponent = 0; iComponent < 2; iComponent++)
3228 : {
3229 37 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3230 37 : d->m_pj_crs, iComponent);
3231 37 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3232 : {
3233 : auto temp =
3234 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3235 0 : proj_destroy(subCRS);
3236 0 : subCRS = temp;
3237 : }
3238 74 : if (subCRS &&
3239 37 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3240 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3241 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3242 : {
3243 31 : coordSys = proj_crs_get_coordinate_system(
3244 : d->getPROJContext(), subCRS);
3245 31 : proj_destroy(subCRS);
3246 31 : break;
3247 : }
3248 6 : else if (subCRS)
3249 : {
3250 6 : proj_destroy(subCRS);
3251 : }
3252 : }
3253 31 : if (coordSys == nullptr)
3254 : {
3255 0 : d->undoDemoteFromBoundCRS();
3256 0 : break;
3257 : }
3258 : }
3259 : else
3260 : {
3261 12027 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3262 12027 : d->m_pj_crs);
3263 : }
3264 :
3265 12058 : d->undoDemoteFromBoundCRS();
3266 12058 : if (!coordSys)
3267 : {
3268 0 : break;
3269 : }
3270 12058 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3271 :
3272 12058 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3273 2252 : csType != PJ_CS_TYPE_VERTICAL &&
3274 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3275 : csType != PJ_CS_TYPE_SPHERICAL)
3276 : {
3277 0 : proj_destroy(coordSys);
3278 0 : break;
3279 : }
3280 :
3281 12058 : int axis = 0;
3282 :
3283 12058 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3284 : csType == PJ_CS_TYPE_SPHERICAL)
3285 : {
3286 : const int axisCount =
3287 2252 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3288 :
3289 2252 : if (axisCount == 3)
3290 : {
3291 4 : axis = 2;
3292 : }
3293 : else
3294 : {
3295 2248 : proj_destroy(coordSys);
3296 2248 : break;
3297 : }
3298 : }
3299 :
3300 9810 : double dfConvFactor = 0.0;
3301 9810 : const char *pszUnitName = nullptr;
3302 9810 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3303 : nullptr, nullptr, nullptr, &dfConvFactor,
3304 : &pszUnitName, nullptr, nullptr))
3305 : {
3306 0 : proj_destroy(coordSys);
3307 0 : break;
3308 : }
3309 :
3310 9810 : d->m_osLinearUnits = pszUnitName;
3311 9810 : d->dfToMeter = dfConvFactor;
3312 9810 : if (ppszName)
3313 1264 : *ppszName = d->m_osLinearUnits.c_str();
3314 :
3315 9810 : proj_destroy(coordSys);
3316 9810 : return dfConvFactor;
3317 : }
3318 :
3319 2490 : d->m_osLinearUnits = "unknown";
3320 2490 : d->dfToMeter = 1.0;
3321 :
3322 2490 : if (ppszName != nullptr)
3323 2304 : *ppszName = d->m_osLinearUnits.c_str();
3324 2490 : return 1.0;
3325 : }
3326 :
3327 91 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3328 :
3329 91 : if (ppszName != nullptr)
3330 37 : *ppszName = "unknown";
3331 :
3332 91 : if (poCS == nullptr)
3333 53 : return 1.0;
3334 :
3335 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3336 : {
3337 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3338 :
3339 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3340 : {
3341 38 : if (ppszName != nullptr)
3342 37 : *ppszName = poChild->GetChild(0)->GetValue();
3343 :
3344 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3345 : }
3346 : }
3347 :
3348 0 : return 1.0;
3349 : }
3350 :
3351 : /**
3352 : * \brief Fetch linear units for target.
3353 : *
3354 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3355 : *
3356 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3357 : *
3358 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3359 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3360 : * GEOCCS and VERT_CS are looked up)
3361 : * @param ppszName a pointer to be updated with the pointer to the units name.
3362 : * The returned value remains internal to the OGRSpatialReference and should not
3363 : * be freed, or modified. It may be invalidated on the next
3364 : * OGRSpatialReference call. ppszName can be set to NULL.
3365 : *
3366 : * @return the value to multiply by linear distances to transform them to
3367 : * meters.
3368 : *
3369 : */
3370 :
3371 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3372 : char **ppszName) const
3373 :
3374 : {
3375 0 : return GetTargetLinearUnits(pszTargetKey,
3376 0 : const_cast<const char **>(ppszName));
3377 : }
3378 :
3379 : /************************************************************************/
3380 : /* OSRGetTargetLinearUnits() */
3381 : /************************************************************************/
3382 :
3383 : /**
3384 : * \brief Fetch linear projection units.
3385 : *
3386 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3387 : *
3388 : */
3389 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3390 : const char *pszTargetKey, char **ppszName)
3391 :
3392 : {
3393 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3394 :
3395 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3396 4 : pszTargetKey, const_cast<const char **>(ppszName));
3397 : }
3398 :
3399 : /************************************************************************/
3400 : /* GetPrimeMeridian() */
3401 : /************************************************************************/
3402 :
3403 : /**
3404 : * \brief Fetch prime meridian info.
3405 : *
3406 : * Returns the offset of the prime meridian from greenwich in degrees,
3407 : * and the prime meridian name (if requested). If no PRIMEM value exists
3408 : * in the coordinate system definition a value of "Greenwich" and an
3409 : * offset of 0.0 is assumed.
3410 : *
3411 : * If the prime meridian name is returned, the pointer is to an internal
3412 : * copy of the name. It should not be freed, altered or depended on after
3413 : * the next OGR call.
3414 : *
3415 : * This method is the same as the C function OSRGetPrimeMeridian().
3416 : *
3417 : * @param ppszName return location for prime meridian name. If NULL, name
3418 : * is not returned.
3419 : *
3420 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3421 : * degrees.
3422 : * @deprecated Use GetPrimeMeridian(const char**) const.
3423 : */
3424 :
3425 1536 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3426 :
3427 : {
3428 3072 : TAKE_OPTIONAL_LOCK();
3429 :
3430 1536 : d->refreshProjObj();
3431 :
3432 1536 : if (!d->m_osPrimeMeridianName.empty())
3433 : {
3434 87 : if (ppszName != nullptr)
3435 11 : *ppszName = d->m_osPrimeMeridianName.c_str();
3436 87 : return d->dfFromGreenwich;
3437 : }
3438 :
3439 : while (true)
3440 : {
3441 1449 : if (!d->m_pj_crs)
3442 0 : break;
3443 :
3444 1449 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3445 1449 : if (!pm)
3446 0 : break;
3447 :
3448 1449 : d->m_osPrimeMeridianName = proj_get_name(pm);
3449 1449 : if (ppszName)
3450 20 : *ppszName = d->m_osPrimeMeridianName.c_str();
3451 1449 : double dfLongitude = 0.0;
3452 1449 : double dfConvFactor = 0.0;
3453 1449 : proj_prime_meridian_get_parameters(
3454 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3455 1449 : proj_destroy(pm);
3456 2898 : d->dfFromGreenwich =
3457 1449 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3458 1449 : return d->dfFromGreenwich;
3459 : }
3460 :
3461 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3462 0 : d->dfFromGreenwich = 0.0;
3463 0 : if (ppszName != nullptr)
3464 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3465 0 : return d->dfFromGreenwich;
3466 : }
3467 :
3468 : /**
3469 : * \brief Fetch prime meridian info.
3470 : *
3471 : * Returns the offset of the prime meridian from greenwich in degrees,
3472 : * and the prime meridian name (if requested). If no PRIMEM value exists
3473 : * in the coordinate system definition a value of "Greenwich" and an
3474 : * offset of 0.0 is assumed.
3475 : *
3476 : * If the prime meridian name is returned, the pointer is to an internal
3477 : * copy of the name. It should not be freed, altered or depended on after
3478 : * the next OGR call.
3479 : *
3480 : * This method is the same as the C function OSRGetPrimeMeridian().
3481 : *
3482 : * @param ppszName return location for prime meridian name. If NULL, name
3483 : * is not returned.
3484 : *
3485 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3486 : * degrees.
3487 : */
3488 :
3489 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3490 :
3491 : {
3492 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3493 : }
3494 :
3495 : /************************************************************************/
3496 : /* OSRGetPrimeMeridian() */
3497 : /************************************************************************/
3498 :
3499 : /**
3500 : * \brief Fetch prime meridian info.
3501 : *
3502 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3503 : */
3504 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3505 :
3506 : {
3507 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3508 :
3509 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3510 0 : const_cast<const char **>(ppszName));
3511 : }
3512 :
3513 : /************************************************************************/
3514 : /* SetGeogCS() */
3515 : /************************************************************************/
3516 :
3517 : /**
3518 : * \brief Set geographic coordinate system.
3519 : *
3520 : * This method is used to set the datum, ellipsoid, prime meridian and
3521 : * angular units for a geographic coordinate system. It can be used on its
3522 : * own to establish a geographic spatial reference, or applied to a
3523 : * projected coordinate system to establish the underlying geographic
3524 : * coordinate system.
3525 : *
3526 : * This method does the same as the C function OSRSetGeogCS().
3527 : *
3528 : * @param pszGeogName user visible name for the geographic coordinate system
3529 : * (not to serve as a key).
3530 : *
3531 : * @param pszDatumName key name for this datum. The OpenGIS specification
3532 : * lists some known values, and otherwise EPSG datum names with a standard
3533 : * transformation are considered legal keys.
3534 : *
3535 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3536 : *
3537 : * @param dfSemiMajor the semi major axis of the spheroid.
3538 : *
3539 : * @param dfInvFlattening the inverse flattening for the spheroid.
3540 : * This can be computed from the semi minor axis as
3541 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3542 : *
3543 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3544 : * If this is NULL a default value of "Greenwich" will be used.
3545 : *
3546 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3547 : * meridian. Always in Degrees
3548 : *
3549 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3550 : * standard names). If NULL a value of "degrees" will be assumed.
3551 : *
3552 : * @param dfConvertToRadians value to multiply angular units by to transform
3553 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3554 : * pszAngularUnits is NULL.
3555 : *
3556 : * @return OGRERR_NONE on success.
3557 : */
3558 :
3559 9714 : OGRErr OGRSpatialReference::SetGeogCS(
3560 : const char *pszGeogName, const char *pszDatumName,
3561 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3562 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3563 : double dfConvertToRadians)
3564 :
3565 : {
3566 19428 : TAKE_OPTIONAL_LOCK();
3567 :
3568 9714 : d->bNormInfoSet = FALSE;
3569 9714 : d->m_osAngularUnits.clear();
3570 9714 : d->m_dfAngularUnitToRadian = 0.0;
3571 9714 : d->m_osPrimeMeridianName.clear();
3572 9714 : d->dfFromGreenwich = 0.0;
3573 :
3574 : /* -------------------------------------------------------------------- */
3575 : /* For a geocentric coordinate system we want to set the datum */
3576 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3577 : /* temporary srs and use the copy method which has special */
3578 : /* handling for GEOCCS. */
3579 : /* -------------------------------------------------------------------- */
3580 9714 : if (IsGeocentric())
3581 : {
3582 4 : OGRSpatialReference oGCS;
3583 :
3584 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3585 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3586 : dfConvertToRadians);
3587 2 : return CopyGeogCSFrom(&oGCS);
3588 : }
3589 :
3590 9712 : auto cs = proj_create_ellipsoidal_2D_cs(
3591 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3592 : dfConvertToRadians);
3593 : // Prime meridian expressed in Degree
3594 9712 : auto obj = proj_create_geographic_crs(
3595 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3596 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3597 9712 : proj_destroy(cs);
3598 :
3599 14707 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3600 4995 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3601 : {
3602 4717 : d->setPjCRS(obj);
3603 : }
3604 4995 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3605 : {
3606 9990 : d->setPjCRS(
3607 4995 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3608 4995 : proj_destroy(obj);
3609 : }
3610 : else
3611 : {
3612 0 : proj_destroy(obj);
3613 : }
3614 :
3615 9712 : return OGRERR_NONE;
3616 : }
3617 :
3618 : /************************************************************************/
3619 : /* OSRSetGeogCS() */
3620 : /************************************************************************/
3621 :
3622 : /**
3623 : * \brief Set geographic coordinate system.
3624 : *
3625 : * This function is the same as OGRSpatialReference::SetGeogCS()
3626 : */
3627 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3628 : const char *pszDatumName, const char *pszSpheroidName,
3629 : double dfSemiMajor, double dfInvFlattening,
3630 : const char *pszPMName, double dfPMOffset,
3631 : const char *pszAngularUnits, double dfConvertToRadians)
3632 :
3633 : {
3634 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3635 :
3636 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3637 : pszSpheroidName, dfSemiMajor,
3638 : dfInvFlattening, pszPMName, dfPMOffset,
3639 18 : pszAngularUnits, dfConvertToRadians);
3640 : }
3641 :
3642 : /************************************************************************/
3643 : /* SetWellKnownGeogCS() */
3644 : /************************************************************************/
3645 :
3646 : /**
3647 : * \brief Set a GeogCS based on well known name.
3648 : *
3649 : * This may be called on an empty OGRSpatialReference to make a geographic
3650 : * coordinate system, or on something with an existing PROJCS node to
3651 : * set the underlying geographic coordinate system of a projected coordinate
3652 : * system.
3653 : *
3654 : * The following well known text values are currently supported,
3655 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3656 : * <ul>
3657 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3658 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3659 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3660 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3661 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3662 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3663 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3664 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3665 : * </ul>
3666 : *
3667 : * @param pszName name of well known geographic coordinate system.
3668 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3669 : * recognised, the target object is already initialized, or an EPSG value
3670 : * can't be successfully looked up.
3671 : */
3672 :
3673 4579 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3674 :
3675 : {
3676 9158 : TAKE_OPTIONAL_LOCK();
3677 :
3678 : /* -------------------------------------------------------------------- */
3679 : /* Check for EPSG authority numbers. */
3680 : /* -------------------------------------------------------------------- */
3681 4579 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3682 : {
3683 84 : OGRSpatialReference oSRS2;
3684 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3685 42 : if (eErr != OGRERR_NONE)
3686 0 : return eErr;
3687 :
3688 42 : if (!oSRS2.IsGeographic())
3689 0 : return OGRERR_FAILURE;
3690 :
3691 42 : return CopyGeogCSFrom(&oSRS2);
3692 : }
3693 :
3694 : /* -------------------------------------------------------------------- */
3695 : /* Check for simple names. */
3696 : /* -------------------------------------------------------------------- */
3697 4537 : const char *pszWKT = nullptr;
3698 :
3699 4537 : if (EQUAL(pszName, "WGS84"))
3700 : {
3701 2669 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3702 : }
3703 1868 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3704 : {
3705 1072 : pszWKT =
3706 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3707 : "ELLIPSOID[\"WGS "
3708 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3709 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3710 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3711 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3712 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3713 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3714 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3715 : "ID[\"OGC\",\"CRS84\"]]";
3716 : }
3717 796 : else if (EQUAL(pszName, "WGS72"))
3718 19 : pszWKT =
3719 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3720 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3721 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3722 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3723 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3724 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3725 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3726 :
3727 777 : else if (EQUAL(pszName, "NAD27"))
3728 136 : pszWKT =
3729 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3730 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3731 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3732 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3733 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3734 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3735 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3736 :
3737 641 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3738 0 : pszWKT =
3739 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3740 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3741 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3742 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3743 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3744 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3745 :
3746 641 : else if (EQUAL(pszName, "NAD83"))
3747 637 : pszWKT =
3748 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3749 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3750 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3751 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3752 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3753 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3754 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3755 : "\"EPSG\",\"4269\"]]";
3756 :
3757 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3758 0 : pszWKT =
3759 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3760 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3761 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3762 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3763 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3764 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3765 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3766 :
3767 : else
3768 4 : return OGRERR_FAILURE;
3769 :
3770 : /* -------------------------------------------------------------------- */
3771 : /* Import the WKT */
3772 : /* -------------------------------------------------------------------- */
3773 9066 : OGRSpatialReference oSRS2;
3774 4533 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3775 4533 : if (eErr != OGRERR_NONE)
3776 0 : return eErr;
3777 :
3778 : /* -------------------------------------------------------------------- */
3779 : /* Copy over. */
3780 : /* -------------------------------------------------------------------- */
3781 4533 : return CopyGeogCSFrom(&oSRS2);
3782 : }
3783 :
3784 : /************************************************************************/
3785 : /* OSRSetWellKnownGeogCS() */
3786 : /************************************************************************/
3787 :
3788 : /**
3789 : * \brief Set a GeogCS based on well known name.
3790 : *
3791 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3792 : */
3793 155 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3794 :
3795 : {
3796 155 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3797 :
3798 155 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3799 : }
3800 :
3801 : /************************************************************************/
3802 : /* CopyGeogCSFrom() */
3803 : /************************************************************************/
3804 :
3805 : /**
3806 : * \brief Copy GEOGCS from another OGRSpatialReference.
3807 : *
3808 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3809 : * If this object has a PROJCS root already, the GEOGCS is installed within
3810 : * it, otherwise it is installed as the root.
3811 : *
3812 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3813 : *
3814 : * @return OGRERR_NONE on success or an error code.
3815 : */
3816 :
3817 5187 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3818 :
3819 : {
3820 10374 : TAKE_OPTIONAL_LOCK();
3821 :
3822 5187 : d->bNormInfoSet = FALSE;
3823 5187 : d->m_osAngularUnits.clear();
3824 5187 : d->m_dfAngularUnitToRadian = 0.0;
3825 5187 : d->m_osPrimeMeridianName.clear();
3826 5187 : d->dfFromGreenwich = 0.0;
3827 :
3828 5187 : d->refreshProjObj();
3829 5187 : poSrcSRS->d->refreshProjObj();
3830 5187 : if (!poSrcSRS->d->m_pj_crs)
3831 : {
3832 1 : return OGRERR_FAILURE;
3833 : }
3834 : auto geodCRS =
3835 5186 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3836 5186 : if (!geodCRS)
3837 : {
3838 0 : return OGRERR_FAILURE;
3839 : }
3840 :
3841 : /* -------------------------------------------------------------------- */
3842 : /* Handle geocentric coordinate systems specially. We just */
3843 : /* want to copy the DATUM. */
3844 : /* -------------------------------------------------------------------- */
3845 5186 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3846 : {
3847 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3848 : #if PROJ_VERSION_MAJOR > 7 || \
3849 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3850 : if (datum == nullptr)
3851 : {
3852 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3853 : }
3854 : #endif
3855 3 : if (datum == nullptr)
3856 : {
3857 0 : proj_destroy(geodCRS);
3858 0 : return OGRERR_FAILURE;
3859 : }
3860 :
3861 3 : const char *pszUnitName = nullptr;
3862 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3863 :
3864 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3865 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3866 : unitConvFactor);
3867 3 : proj_destroy(datum);
3868 :
3869 3 : d->setPjCRS(pj_crs);
3870 : }
3871 :
3872 5183 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3873 : {
3874 320 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3875 320 : d->m_pj_crs, geodCRS);
3876 320 : d->setPjCRS(pj_crs);
3877 : }
3878 :
3879 : else
3880 : {
3881 4863 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3882 : }
3883 :
3884 : // Apply TOWGS84 of source CRS
3885 5186 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3886 : {
3887 : auto target =
3888 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3889 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3890 1 : poSrcSRS->d->m_pj_crs);
3891 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3892 : target, co));
3893 1 : proj_destroy(target);
3894 1 : proj_destroy(co);
3895 : }
3896 :
3897 5186 : proj_destroy(geodCRS);
3898 :
3899 5186 : return OGRERR_NONE;
3900 : }
3901 :
3902 : /************************************************************************/
3903 : /* OSRCopyGeogCSFrom() */
3904 : /************************************************************************/
3905 :
3906 : /**
3907 : * \brief Copy GEOGCS from another OGRSpatialReference.
3908 : *
3909 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3910 : */
3911 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3912 : const OGRSpatialReferenceH hSrcSRS)
3913 :
3914 : {
3915 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3916 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3917 :
3918 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3919 : }
3920 :
3921 : /************************************************************************/
3922 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3923 : /************************************************************************/
3924 :
3925 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3926 : *
3927 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3928 : */
3929 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3930 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3931 :
3932 : /**
3933 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3934 : */
3935 2755 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3936 : {
3937 2755 : return SET_FROM_USER_INPUT_LIMITATIONS;
3938 : }
3939 :
3940 : /************************************************************************/
3941 : /* RemoveIDFromMemberOfEnsembles() */
3942 : /************************************************************************/
3943 :
3944 : // cppcheck-suppress constParameterReference
3945 243 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3946 : {
3947 : // Remove "id" from members of datum ensembles for compatibility with
3948 : // older PROJ versions
3949 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3950 : // and https://github.com/OSGeo/PROJ/pull/3221
3951 243 : if (obj.GetType() == CPLJSONObject::Type::Object)
3952 : {
3953 300 : for (auto &subObj : obj.GetChildren())
3954 : {
3955 235 : RemoveIDFromMemberOfEnsembles(subObj);
3956 : }
3957 : }
3958 198 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3959 198 : obj.GetName() == "members")
3960 : {
3961 60 : for (auto &subObj : obj.ToArray())
3962 : {
3963 52 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3964 : {
3965 51 : subObj.Delete("id");
3966 : }
3967 : }
3968 : }
3969 243 : }
3970 :
3971 : /************************************************************************/
3972 : /* SetFromUserInput() */
3973 : /************************************************************************/
3974 :
3975 : /**
3976 : * \brief Set spatial reference from various text formats.
3977 : *
3978 : * This method will examine the provided input, and try to deduce the
3979 : * format, and then use it to initialize the spatial reference system. It
3980 : * may take the following forms:
3981 : *
3982 : * <ol>
3983 : * <li> Well Known Text definition - passed on to importFromWkt().
3984 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3985 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3986 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3987 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3988 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3989 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3990 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3991 : * WGS84 or WGS72.
3992 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3993 : * <li> PROJJSON (PROJ >= 6.2)
3994 : * </ol>
3995 : *
3996 : * It is expected that this method will be extended in the future to support
3997 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3998 : * State Plane definitions.
3999 : *
4000 : * This method is intended to be flexible, but by its nature it is
4001 : * imprecise as it must guess information about the format intended. When
4002 : * possible applications should call the specific method appropriate if the
4003 : * input is known to be in a particular format.
4004 : *
4005 : * This method does the same thing as the OSRSetFromUserInput() function.
4006 : *
4007 : * @param pszDefinition text definition to try to deduce SRS from.
4008 : *
4009 : * @return OGRERR_NONE on success, or an error code if the name isn't
4010 : * recognised, the definition is corrupt, or an EPSG value can't be
4011 : * successfully looked up.
4012 : */
4013 :
4014 22468 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4015 : {
4016 22468 : return SetFromUserInput(pszDefinition, nullptr);
4017 : }
4018 :
4019 : /**
4020 : * \brief Set spatial reference from various text formats.
4021 : *
4022 : * This method will examine the provided input, and try to deduce the
4023 : * format, and then use it to initialize the spatial reference system. It
4024 : * may take the following forms:
4025 : *
4026 : * <ol>
4027 : * <li> Well Known Text definition - passed on to importFromWkt().
4028 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4029 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4030 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4031 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4032 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4033 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4034 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4035 : * WGS84 or WGS72.
4036 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4037 : * <li> PROJJSON (PROJ >= 6.2)
4038 : * </ol>
4039 : *
4040 : * It is expected that this method will be extended in the future to support
4041 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4042 : * State Plane definitions.
4043 : *
4044 : * This method is intended to be flexible, but by its nature it is
4045 : * imprecise as it must guess information about the format intended. When
4046 : * possible applications should call the specific method appropriate if the
4047 : * input is known to be in a particular format.
4048 : *
4049 : * This method does the same thing as the OSRSetFromUserInput() and
4050 : * OSRSetFromUserInputEx() functions.
4051 : *
4052 : * @param pszDefinition text definition to try to deduce SRS from.
4053 : *
4054 : * @param papszOptions NULL terminated list of options, or NULL.
4055 : * <ol>
4056 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4057 : * Whether http:// or https:// access is allowed. Defaults to YES.
4058 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4059 : * Whether reading a file using the Virtual File System layer is allowed
4060 : * (can also involve network access). Defaults to YES.
4061 : * </ol>
4062 : *
4063 : * @return OGRERR_NONE on success, or an error code if the name isn't
4064 : * recognised, the definition is corrupt, or an EPSG value can't be
4065 : * successfully looked up.
4066 : */
4067 :
4068 29943 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4069 : CSLConstList papszOptions)
4070 : {
4071 59886 : TAKE_OPTIONAL_LOCK();
4072 :
4073 : // Skip leading white space
4074 29945 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4075 2 : pszDefinition++;
4076 :
4077 29943 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4078 : {
4079 1 : pszDefinition += 6;
4080 : }
4081 :
4082 : /* -------------------------------------------------------------------- */
4083 : /* Is it a recognised syntax? */
4084 : /* -------------------------------------------------------------------- */
4085 29943 : const char *const wktKeywords[] = {
4086 : // WKT1
4087 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4088 : // WKT2"
4089 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4090 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4091 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4092 459337 : for (const char *keyword : wktKeywords)
4093 : {
4094 438545 : if (STARTS_WITH_CI(pszDefinition, keyword))
4095 : {
4096 9151 : return importFromWkt(pszDefinition);
4097 : }
4098 : }
4099 :
4100 20792 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4101 20792 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4102 : {
4103 11756 : OGRErr eStatus = OGRERR_NONE;
4104 :
4105 11756 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4106 : {
4107 : // Use proj_create() as it allows things like EPSG:3157+4617
4108 : // that are not normally supported by the below code that
4109 : // builds manually a compound CRS
4110 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4111 62 : if (!pj)
4112 : {
4113 1 : return OGRERR_FAILURE;
4114 : }
4115 61 : Clear();
4116 61 : d->setPjCRS(pj);
4117 61 : return OGRERR_NONE;
4118 : }
4119 : else
4120 : {
4121 : eStatus =
4122 11694 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4123 : }
4124 :
4125 11694 : return eStatus;
4126 : }
4127 :
4128 9036 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4129 8311 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4130 8310 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4131 8252 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4132 8252 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4133 8252 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4134 784 : return importFromURN(pszDefinition);
4135 :
4136 8252 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4137 8250 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4138 8249 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4139 1649 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4140 1648 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4141 6604 : return importFromCRSURL(pszDefinition);
4142 :
4143 1648 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4144 1 : return importFromWMSAUTO(pszDefinition);
4145 :
4146 : // WMS/WCS OGC codes like OGC:CRS84.
4147 1647 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4148 79 : return SetWellKnownGeogCS(pszDefinition + 4);
4149 :
4150 1568 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4151 1 : return SetWellKnownGeogCS(pszDefinition);
4152 :
4153 1567 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4154 : {
4155 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4156 0 : char *pszCode = strstr(pszFile, ",") + 1;
4157 :
4158 0 : pszCode[-1] = '\0';
4159 :
4160 0 : OGRErr err = importFromDict(pszFile, pszCode);
4161 0 : CPLFree(pszFile);
4162 :
4163 0 : return err;
4164 : }
4165 :
4166 1567 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4167 1562 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4168 : {
4169 521 : Clear();
4170 521 : return SetWellKnownGeogCS(pszDefinition);
4171 : }
4172 :
4173 : // PROJJSON
4174 1046 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4175 179 : (strstr(pszDefinition, "GeodeticCRS") ||
4176 179 : strstr(pszDefinition, "GeographicCRS") ||
4177 129 : strstr(pszDefinition, "ProjectedCRS") ||
4178 0 : strstr(pszDefinition, "VerticalCRS") ||
4179 0 : strstr(pszDefinition, "BoundCRS") ||
4180 0 : strstr(pszDefinition, "CompoundCRS") ||
4181 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4182 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4183 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4184 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4185 0 : strstr(pszDefinition, "EngineeringCRS") ||
4186 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4187 0 : strstr(pszDefinition, "ParametricCRS") ||
4188 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4189 0 : strstr(pszDefinition, "TemporalCRS") ||
4190 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4191 : {
4192 : PJ *pj;
4193 179 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4194 : {
4195 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4196 : // a unknown id.
4197 8 : CPLJSONDocument oCRSDoc;
4198 8 : if (!oCRSDoc.LoadMemory(pszDefinition))
4199 0 : return OGRERR_CORRUPT_DATA;
4200 8 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4201 8 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4202 8 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4203 : }
4204 : else
4205 : {
4206 171 : pj = proj_create(d->getPROJContext(), pszDefinition);
4207 : }
4208 179 : if (!pj)
4209 : {
4210 2 : return OGRERR_FAILURE;
4211 : }
4212 177 : Clear();
4213 177 : d->setPjCRS(pj);
4214 177 : return OGRERR_NONE;
4215 : }
4216 :
4217 867 : if (strstr(pszDefinition, "+proj") != nullptr ||
4218 407 : strstr(pszDefinition, "+init") != nullptr)
4219 460 : return importFromProj4(pszDefinition);
4220 :
4221 407 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4222 382 : STARTS_WITH_CI(pszDefinition, "https://"))
4223 : {
4224 26 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4225 : "ALLOW_NETWORK_ACCESS", "YES")))
4226 0 : return importFromUrl(pszDefinition);
4227 :
4228 26 : CPLError(CE_Failure, CPLE_AppDefined,
4229 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4230 : pszDefinition);
4231 26 : return OGRERR_FAILURE;
4232 : }
4233 :
4234 381 : if (EQUAL(pszDefinition, "osgb:BNG"))
4235 : {
4236 8 : return importFromEPSG(27700);
4237 : }
4238 :
4239 : // Used by German CityGML files
4240 373 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4241 : {
4242 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4243 0 : return SetFromUserInput("EPSG:25832+5783");
4244 : }
4245 373 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4246 : {
4247 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4248 3 : return SetFromUserInput("EPSG:25832+7837");
4249 : }
4250 :
4251 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4252 370 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4253 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4254 370 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4255 8 : return importFromEPSG(6668); // JGD2011
4256 362 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4257 : {
4258 : // FIXME when EPSG attributes a CRS code
4259 3 : return importFromWkt(
4260 : "GEOGCRS[\"JGD2024\",\n"
4261 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4262 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4263 : " LENGTHUNIT[\"metre\",1]]],\n"
4264 : " PRIMEM[\"Greenwich\",0,\n"
4265 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4266 : " CS[ellipsoidal,2],\n"
4267 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4268 : " ORDER[1],\n"
4269 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4270 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4271 : " ORDER[2],\n"
4272 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4273 : " USAGE[\n"
4274 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4275 : " AREA[\"Japan - onshore and offshore.\"],\n"
4276 3 : " BBOX[17.09,122.38,46.05,157.65]]]");
4277 : }
4278 :
4279 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4280 359 : const char *pszDot = strrchr(pszDefinition, ':');
4281 359 : if (pszDot)
4282 : {
4283 125 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4284 : auto authorities =
4285 125 : proj_get_authorities_from_database(d->getPROJContext());
4286 125 : if (authorities)
4287 : {
4288 125 : std::set<std::string> aosCandidateAuthorities;
4289 290 : for (auto iter = authorities; *iter; ++iter)
4290 : {
4291 288 : if (*iter == osPrefix)
4292 : {
4293 123 : aosCandidateAuthorities.clear();
4294 123 : aosCandidateAuthorities.insert(*iter);
4295 123 : break;
4296 : }
4297 : // Deal with "IAU_2015" as authority in the list and input
4298 : // "IAU:code"
4299 165 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4300 165 : 0 &&
4301 0 : (*iter)[osPrefix.size()] == '_')
4302 : {
4303 0 : aosCandidateAuthorities.insert(*iter);
4304 : }
4305 : // Deal with "IAU_2015" as authority in the list and input
4306 : // "IAU:2015:code"
4307 330 : else if (osPrefix.find(':') != std::string::npos &&
4308 165 : osPrefix.size() == strlen(*iter) &&
4309 165 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4310 : {
4311 0 : aosCandidateAuthorities.clear();
4312 0 : aosCandidateAuthorities.insert(*iter);
4313 0 : break;
4314 : }
4315 : }
4316 :
4317 125 : proj_string_list_destroy(authorities);
4318 :
4319 125 : if (!aosCandidateAuthorities.empty())
4320 : {
4321 123 : auto obj = proj_create_from_database(
4322 : d->getPROJContext(),
4323 123 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4324 : PJ_CATEGORY_CRS, false, nullptr);
4325 123 : if (!obj)
4326 : {
4327 16 : return OGRERR_FAILURE;
4328 : }
4329 107 : Clear();
4330 107 : d->setPjCRS(obj);
4331 107 : return OGRERR_NONE;
4332 : }
4333 : }
4334 : }
4335 :
4336 : /* -------------------------------------------------------------------- */
4337 : /* Try to open it as a file. */
4338 : /* -------------------------------------------------------------------- */
4339 236 : if (!CPLTestBool(
4340 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4341 : {
4342 : VSIStatBufL sStat;
4343 40 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4344 20 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4345 : {
4346 0 : CPLError(CE_Failure, CPLE_AppDefined,
4347 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4348 : pszDefinition);
4349 0 : return OGRERR_FAILURE;
4350 : }
4351 : // We used to silently return an error without a CE_Failure message
4352 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4353 20 : return OGRERR_CORRUPT_DATA;
4354 : }
4355 :
4356 432 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4357 216 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4358 216 : if (fp == nullptr)
4359 213 : return OGRERR_CORRUPT_DATA;
4360 :
4361 3 : const size_t nBufMax = 100000;
4362 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4363 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4364 3 : VSIFCloseL(fp);
4365 :
4366 3 : if (nBytes == nBufMax - 1)
4367 : {
4368 0 : CPLDebug("OGR",
4369 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4370 : "but it is to large for our generous buffer. Is it really "
4371 : "just a WKT definition?",
4372 : pszDefinition);
4373 0 : CPLFree(pszBuffer);
4374 0 : return OGRERR_FAILURE;
4375 : }
4376 :
4377 3 : pszBuffer[nBytes] = '\0';
4378 :
4379 3 : char *pszBufPtr = pszBuffer;
4380 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4381 0 : pszBufPtr++;
4382 :
4383 3 : OGRErr err = OGRERR_NONE;
4384 3 : if (pszBufPtr[0] == '<')
4385 0 : err = importFromXML(pszBufPtr);
4386 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4387 3 : strstr(pszBuffer, "+init") != nullptr) &&
4388 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4389 0 : strstr(pszBuffer, "extension") == nullptr))
4390 0 : err = importFromProj4(pszBufPtr);
4391 : else
4392 : {
4393 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4394 : {
4395 0 : pszBufPtr += 6;
4396 : }
4397 :
4398 : // coverity[tainted_data]
4399 3 : err = importFromWkt(pszBufPtr);
4400 : }
4401 :
4402 3 : CPLFree(pszBuffer);
4403 :
4404 3 : return err;
4405 : }
4406 :
4407 : /************************************************************************/
4408 : /* OSRSetFromUserInput() */
4409 : /************************************************************************/
4410 :
4411 : /**
4412 : * \brief Set spatial reference from various text formats.
4413 : *
4414 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4415 : *
4416 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4417 : */
4418 365 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4419 : const char *pszDef)
4420 :
4421 : {
4422 365 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4423 :
4424 365 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4425 : }
4426 :
4427 : /************************************************************************/
4428 : /* OSRSetFromUserInputEx() */
4429 : /************************************************************************/
4430 :
4431 : /**
4432 : * \brief Set spatial reference from various text formats.
4433 : *
4434 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4435 : *
4436 : * @since GDAL 3.9
4437 : */
4438 1204 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4439 : CSLConstList papszOptions)
4440 :
4441 : {
4442 1204 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4443 :
4444 1204 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4445 : }
4446 :
4447 : /************************************************************************/
4448 : /* ImportFromUrl() */
4449 : /************************************************************************/
4450 :
4451 : /**
4452 : * \brief Set spatial reference from a URL.
4453 : *
4454 : * This method will download the spatial reference at a given URL and
4455 : * feed it into SetFromUserInput for you.
4456 : *
4457 : * This method does the same thing as the OSRImportFromUrl() function.
4458 : *
4459 : * @param pszUrl text definition to try to deduce SRS from.
4460 : *
4461 : * @return OGRERR_NONE on success, or an error code with the curl
4462 : * error message if it is unable to download data.
4463 : */
4464 :
4465 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4466 :
4467 : {
4468 10 : TAKE_OPTIONAL_LOCK();
4469 :
4470 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4471 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4472 : {
4473 2 : CPLError(CE_Failure, CPLE_AppDefined,
4474 : "The given string is not recognized as a URL"
4475 : "starting with 'http://' -- %s",
4476 : pszUrl);
4477 2 : return OGRERR_FAILURE;
4478 : }
4479 :
4480 : /* -------------------------------------------------------------------- */
4481 : /* Fetch the result. */
4482 : /* -------------------------------------------------------------------- */
4483 3 : CPLErrorReset();
4484 :
4485 6 : std::string osUrl(pszUrl);
4486 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4487 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4488 : // to query WKT. To allow a static server to be used, rather append a
4489 : // "ogcwkt/" suffix.
4490 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4491 5 : "http://spatialreference.org/ref/"})
4492 : {
4493 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4494 : {
4495 : const CPLStringList aosTokens(
4496 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4497 3 : if (aosTokens.size() == 2)
4498 : {
4499 2 : osUrl = "https://spatialreference.org/ref/";
4500 2 : osUrl += aosTokens[0]; // authority
4501 2 : osUrl += '/';
4502 2 : osUrl += aosTokens[1]; // code
4503 2 : osUrl += "/ogcwkt/";
4504 : }
4505 3 : break;
4506 : }
4507 : }
4508 :
4509 3 : const char *pszTimeout = "TIMEOUT=10";
4510 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4511 :
4512 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4513 :
4514 : /* -------------------------------------------------------------------- */
4515 : /* Try to handle errors. */
4516 : /* -------------------------------------------------------------------- */
4517 :
4518 3 : if (psResult == nullptr)
4519 0 : return OGRERR_FAILURE;
4520 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4521 3 : psResult->pabyData == nullptr)
4522 : {
4523 0 : if (CPLGetLastErrorNo() == 0)
4524 : {
4525 0 : CPLError(CE_Failure, CPLE_AppDefined,
4526 : "No data was returned from the given URL");
4527 : }
4528 0 : CPLHTTPDestroyResult(psResult);
4529 0 : return OGRERR_FAILURE;
4530 : }
4531 :
4532 3 : if (psResult->nStatus != 0)
4533 : {
4534 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4535 : psResult->nStatus, psResult->pszErrBuf);
4536 0 : CPLHTTPDestroyResult(psResult);
4537 0 : return OGRERR_FAILURE;
4538 : }
4539 :
4540 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4541 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4542 3 : STARTS_WITH_CI(pszData, "https://"))
4543 : {
4544 0 : CPLError(CE_Failure, CPLE_AppDefined,
4545 : "The data that was downloaded also starts with 'http://' "
4546 : "and cannot be passed into SetFromUserInput. Is this "
4547 : "really a spatial reference definition? ");
4548 0 : CPLHTTPDestroyResult(psResult);
4549 0 : return OGRERR_FAILURE;
4550 : }
4551 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4552 : {
4553 0 : CPLHTTPDestroyResult(psResult);
4554 0 : return OGRERR_FAILURE;
4555 : }
4556 :
4557 3 : CPLHTTPDestroyResult(psResult);
4558 3 : return OGRERR_NONE;
4559 : }
4560 :
4561 : /************************************************************************/
4562 : /* OSRimportFromUrl() */
4563 : /************************************************************************/
4564 :
4565 : /**
4566 : * \brief Set spatial reference from a URL.
4567 : *
4568 : * This function is the same as OGRSpatialReference::importFromUrl()
4569 : */
4570 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4571 :
4572 : {
4573 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4574 :
4575 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4576 : }
4577 :
4578 : /************************************************************************/
4579 : /* importFromURNPart() */
4580 : /************************************************************************/
4581 6815 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4582 : const char *pszCode,
4583 : const char *pszURN)
4584 : {
4585 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4586 : (void)this;
4587 : (void)pszAuthority;
4588 : (void)pszCode;
4589 : (void)pszURN;
4590 : return OGRERR_FAILURE;
4591 : #else
4592 : /* -------------------------------------------------------------------- */
4593 : /* Is this an EPSG code? Note that we import it with EPSG */
4594 : /* preferred axis ordering for geographic coordinate systems. */
4595 : /* -------------------------------------------------------------------- */
4596 6815 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4597 5820 : return importFromEPSGA(atoi(pszCode));
4598 :
4599 : /* -------------------------------------------------------------------- */
4600 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4601 : /* -------------------------------------------------------------------- */
4602 995 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4603 0 : return importFromDict("IAU2000.wkt", pszCode);
4604 :
4605 : /* -------------------------------------------------------------------- */
4606 : /* Is this an OGC code? */
4607 : /* -------------------------------------------------------------------- */
4608 995 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4609 : {
4610 1 : CPLError(CE_Failure, CPLE_AppDefined,
4611 : "URN %s has unrecognized authority.", pszURN);
4612 1 : return OGRERR_FAILURE;
4613 : }
4614 :
4615 994 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4616 982 : return SetWellKnownGeogCS(pszCode);
4617 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4618 0 : return SetWellKnownGeogCS(pszCode);
4619 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4620 0 : return SetWellKnownGeogCS(pszCode);
4621 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4622 10 : return SetWellKnownGeogCS("CRS84");
4623 :
4624 : /* -------------------------------------------------------------------- */
4625 : /* Handle auto codes. We need to convert from format */
4626 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4627 : /* -------------------------------------------------------------------- */
4628 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4629 : {
4630 2 : char szWMSAuto[100] = {'\0'};
4631 :
4632 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4633 0 : return OGRERR_FAILURE;
4634 :
4635 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4636 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4637 : {
4638 26 : if (szWMSAuto[i] == ':')
4639 4 : szWMSAuto[i] = ',';
4640 : }
4641 :
4642 2 : return importFromWMSAUTO(szWMSAuto);
4643 : }
4644 :
4645 : /* -------------------------------------------------------------------- */
4646 : /* Not a recognise OGC item. */
4647 : /* -------------------------------------------------------------------- */
4648 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4649 : pszURN);
4650 :
4651 0 : return OGRERR_FAILURE;
4652 : #endif
4653 : }
4654 :
4655 : /************************************************************************/
4656 : /* importFromURN() */
4657 : /* */
4658 : /* See OGC recommendation paper 06-023r1 or later for details. */
4659 : /************************************************************************/
4660 :
4661 : /**
4662 : * \brief Initialize from OGC URN.
4663 : *
4664 : * Initializes this spatial reference from a coordinate system defined
4665 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4666 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4667 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4668 : *
4669 : * This method is also support through SetFromUserInput() which can
4670 : * normally be used for URNs.
4671 : *
4672 : * @param pszURN the urn string.
4673 : *
4674 : * @return OGRERR_NONE on success or an error code.
4675 : */
4676 :
4677 845 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4678 :
4679 : {
4680 845 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4681 1605 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4682 760 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4683 : CPL_VALUE_INTEGER)
4684 : {
4685 757 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4686 : }
4687 :
4688 176 : TAKE_OPTIONAL_LOCK();
4689 :
4690 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4691 :
4692 : // PROJ 8.2.0 has support for IAU codes now.
4693 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4694 : /* -------------------------------------------------------------------- */
4695 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4696 : /* -------------------------------------------------------------------- */
4697 : const char *pszIAU = strstr(pszURN, "IAU");
4698 : if (pszIAU)
4699 : {
4700 : const char *pszCode = strchr(pszIAU, ':');
4701 : if (pszCode)
4702 : {
4703 : ++pszCode;
4704 : if (*pszCode == ':')
4705 : ++pszCode;
4706 : return importFromDict("IAU2000.wkt", pszCode);
4707 : }
4708 : }
4709 : #endif
4710 :
4711 : if (strlen(pszURN) >= 1000)
4712 : {
4713 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4714 : return OGRERR_CORRUPT_DATA;
4715 : }
4716 : auto obj = proj_create(d->getPROJContext(), pszURN);
4717 : if (!obj)
4718 : {
4719 : return OGRERR_FAILURE;
4720 : }
4721 : Clear();
4722 : d->setPjCRS(obj);
4723 : return OGRERR_NONE;
4724 : #else
4725 88 : const char *pszCur = nullptr;
4726 :
4727 88 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4728 23 : pszCur = pszURN + 16;
4729 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4730 1 : pszCur = pszURN + 20;
4731 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4732 62 : pszCur = pszURN + 18;
4733 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4734 0 : pszCur = pszURN + 16;
4735 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4736 0 : pszCur = pszURN + 20;
4737 : else
4738 : {
4739 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4740 : pszURN);
4741 2 : return OGRERR_FAILURE;
4742 : }
4743 :
4744 : /* -------------------------------------------------------------------- */
4745 : /* Clear any existing definition. */
4746 : /* -------------------------------------------------------------------- */
4747 86 : Clear();
4748 :
4749 : /* -------------------------------------------------------------------- */
4750 : /* Find code (ignoring version) out of string like: */
4751 : /* */
4752 : /* authority:[version]:code */
4753 : /* -------------------------------------------------------------------- */
4754 86 : const char *pszAuthority = pszCur;
4755 :
4756 : // skip authority
4757 414 : while (*pszCur != ':' && *pszCur)
4758 328 : pszCur++;
4759 86 : if (*pszCur == ':')
4760 86 : pszCur++;
4761 :
4762 : // skip version
4763 86 : const char *pszBeforeVersion = pszCur;
4764 387 : while (*pszCur != ':' && *pszCur)
4765 301 : pszCur++;
4766 86 : if (*pszCur == ':')
4767 58 : pszCur++;
4768 : else
4769 : // We come here in the case, the content to parse is authority:code
4770 : // (instead of authority::code) which is probably illegal according to
4771 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4772 : // for example in what is returned by GeoServer.
4773 28 : pszCur = pszBeforeVersion;
4774 :
4775 86 : const char *pszCode = pszCur;
4776 :
4777 86 : const char *pszComma = strchr(pszCur, ',');
4778 86 : if (pszComma == nullptr)
4779 85 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4780 :
4781 : // There's a second part with the vertical SRS.
4782 1 : pszCur = pszComma + 1;
4783 1 : if (!STARTS_WITH(pszCur, "crs:"))
4784 : {
4785 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4786 : pszURN);
4787 0 : return OGRERR_FAILURE;
4788 : }
4789 :
4790 1 : pszCur += 4;
4791 :
4792 1 : char *pszFirstCode = CPLStrdup(pszCode);
4793 1 : pszFirstCode[pszComma - pszCode] = '\0';
4794 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4795 1 : CPLFree(pszFirstCode);
4796 :
4797 : // Do we want to turn this into a compound definition
4798 : // with a vertical datum?
4799 1 : if (eStatus != OGRERR_NONE)
4800 0 : return eStatus;
4801 :
4802 : /* -------------------------------------------------------------------- */
4803 : /* Find code (ignoring version) out of string like: */
4804 : /* */
4805 : /* authority:[version]:code */
4806 : /* -------------------------------------------------------------------- */
4807 1 : pszAuthority = pszCur;
4808 :
4809 : // skip authority
4810 5 : while (*pszCur != ':' && *pszCur)
4811 4 : pszCur++;
4812 1 : if (*pszCur == ':')
4813 1 : pszCur++;
4814 :
4815 : // skip version
4816 1 : pszBeforeVersion = pszCur;
4817 1 : while (*pszCur != ':' && *pszCur)
4818 0 : pszCur++;
4819 1 : if (*pszCur == ':')
4820 1 : pszCur++;
4821 : else
4822 0 : pszCur = pszBeforeVersion;
4823 :
4824 1 : pszCode = pszCur;
4825 :
4826 2 : OGRSpatialReference oVertSRS;
4827 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4828 1 : if (eStatus == OGRERR_NONE)
4829 : {
4830 1 : OGRSpatialReference oHorizSRS(*this);
4831 :
4832 1 : Clear();
4833 :
4834 1 : oHorizSRS.d->refreshProjObj();
4835 1 : oVertSRS.d->refreshProjObj();
4836 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4837 0 : return OGRERR_FAILURE;
4838 :
4839 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4840 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4841 :
4842 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4843 1 : osName += " + ";
4844 1 : osName += pszVertName ? pszVertName : "";
4845 :
4846 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4847 : }
4848 :
4849 1 : return eStatus;
4850 : #endif
4851 : }
4852 :
4853 : /************************************************************************/
4854 : /* importFromCRSURL() */
4855 : /* */
4856 : /* See OGC Best Practice document 11-135 for details. */
4857 : /************************************************************************/
4858 :
4859 : /**
4860 : * \brief Initialize from OGC URL.
4861 : *
4862 : * Initializes this spatial reference from a coordinate system defined
4863 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4864 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4865 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4866 : *
4867 : * This method is also supported through SetFromUserInput() which can
4868 : * normally be used for URLs.
4869 : *
4870 : * @param pszURL the URL string.
4871 : *
4872 : * @return OGRERR_NONE on success or an error code.
4873 : */
4874 :
4875 6741 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4876 :
4877 : {
4878 13482 : TAKE_OPTIONAL_LOCK();
4879 :
4880 : #if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4881 6741 : if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4882 : {
4883 12 : PJ *obj = proj_create(
4884 : d->getPROJContext(),
4885 : "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4886 : " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4887 : " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4888 : " MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4889 : " MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4890 : " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4891 : " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4892 : " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4893 : " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4894 : " MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4895 : " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4896 : " LENGTHUNIT[\"metre\",1]],\n"
4897 : " ENSEMBLEACCURACY[2.0]],\n"
4898 : " PRIMEM[\"Greenwich\",0,\n"
4899 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4900 : " CS[ellipsoidal,3],\n"
4901 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4902 : " ORDER[1],\n"
4903 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4904 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4905 : " ORDER[2],\n"
4906 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4907 : " AXIS[\"ellipsoidal height (h)\",up,\n"
4908 : " ORDER[3],\n"
4909 : " LENGTHUNIT[\"metre\",1]],\n"
4910 : " USAGE[\n"
4911 : " SCOPE[\"3D system frequently used in GIS, Web APIs and "
4912 : "Web applications\"],\n"
4913 : " AREA[\"World.\"],\n"
4914 : " BBOX[-90,-180,90,180]],\n"
4915 : " ID[\"OGC\",\"CRS84h\"]]");
4916 12 : if (!obj)
4917 : {
4918 0 : return OGRERR_FAILURE;
4919 : }
4920 12 : Clear();
4921 12 : d->setPjCRS(obj);
4922 12 : return OGRERR_NONE;
4923 : }
4924 : #endif
4925 :
4926 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4927 : if (strlen(pszURL) >= 10000)
4928 : {
4929 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4930 : return OGRERR_CORRUPT_DATA;
4931 : }
4932 :
4933 : PJ *obj;
4934 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4935 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4936 : {
4937 : obj = proj_create(
4938 : d->getPROJContext(),
4939 : CPLSPrintf("IAU:%s",
4940 : pszURL +
4941 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4942 : }
4943 : else
4944 : #endif
4945 : {
4946 : obj = proj_create(d->getPROJContext(), pszURL);
4947 : }
4948 : if (!obj)
4949 : {
4950 : return OGRERR_FAILURE;
4951 : }
4952 : Clear();
4953 : d->setPjCRS(obj);
4954 : return OGRERR_NONE;
4955 : #else
4956 6729 : const char *pszCur = nullptr;
4957 :
4958 6729 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4959 2 : pszCur = pszURL + 26;
4960 6727 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4961 1 : pszCur = pszURL + 27;
4962 6726 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4963 6725 : pszCur = pszURL + 30;
4964 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4965 1 : pszCur = pszURL + 31;
4966 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4967 0 : pszCur = pszURL + 23;
4968 : else
4969 : {
4970 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4971 : pszURL);
4972 0 : return OGRERR_FAILURE;
4973 : }
4974 :
4975 6729 : if (*pszCur == '\0')
4976 : {
4977 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4978 0 : return OGRERR_FAILURE;
4979 : }
4980 :
4981 : /* -------------------------------------------------------------------- */
4982 : /* Clear any existing definition. */
4983 : /* -------------------------------------------------------------------- */
4984 6729 : Clear();
4985 :
4986 6729 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4987 : {
4988 : /* --------------------------------------------------------------------
4989 : */
4990 : /* It's a compound CRS, of the form: */
4991 : /* */
4992 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4993 : /* --------------------------------------------------------------------
4994 : */
4995 1 : pszCur += 12;
4996 :
4997 : // Extract each component CRS URL.
4998 1 : int iComponentUrl = 2;
4999 :
5000 2 : CPLString osName = "";
5001 1 : Clear();
5002 :
5003 3 : while (iComponentUrl != -1)
5004 : {
5005 2 : char searchStr[15] = {};
5006 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5007 :
5008 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
5009 :
5010 : // Figure out the next component URL.
5011 2 : char *pszComponentUrl = nullptr;
5012 :
5013 2 : if (pszUrlEnd)
5014 : {
5015 1 : size_t nLen = pszUrlEnd - pszCur;
5016 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5017 1 : strncpy(pszComponentUrl, pszCur, nLen);
5018 1 : pszComponentUrl[nLen] = '\0';
5019 :
5020 1 : ++iComponentUrl;
5021 1 : pszCur += nLen + strlen(searchStr);
5022 : }
5023 : else
5024 : {
5025 1 : if (iComponentUrl == 2)
5026 : {
5027 0 : CPLError(CE_Failure, CPLE_AppDefined,
5028 : "Compound CRS URLs must have at least two "
5029 : "component CRSs.");
5030 0 : return OGRERR_FAILURE;
5031 : }
5032 : else
5033 : {
5034 1 : pszComponentUrl = CPLStrdup(pszCur);
5035 : // no more components
5036 1 : iComponentUrl = -1;
5037 : }
5038 : }
5039 :
5040 2 : OGRSpatialReference oComponentSRS;
5041 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5042 :
5043 2 : CPLFree(pszComponentUrl);
5044 2 : pszComponentUrl = nullptr;
5045 :
5046 2 : if (eStatus == OGRERR_NONE)
5047 : {
5048 2 : if (osName.length() != 0)
5049 : {
5050 1 : osName += " + ";
5051 : }
5052 2 : osName += oComponentSRS.GetRoot()->GetValue();
5053 2 : SetNode("COMPD_CS", osName);
5054 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5055 : }
5056 : else
5057 0 : return eStatus;
5058 : }
5059 :
5060 1 : return OGRERR_NONE;
5061 : }
5062 :
5063 : /* -------------------------------------------------------------------- */
5064 : /* It's a normal CRS URL, of the form: */
5065 : /* */
5066 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5067 : /* -------------------------------------------------------------------- */
5068 6728 : ++pszCur;
5069 6728 : const char *pszAuthority = pszCur;
5070 :
5071 : // skip authority
5072 132658 : while (*pszCur != '/' && *pszCur)
5073 125930 : pszCur++;
5074 6728 : if (*pszCur == '/')
5075 6727 : pszCur++;
5076 :
5077 : // skip version
5078 15383 : while (*pszCur != '/' && *pszCur)
5079 8655 : pszCur++;
5080 6728 : if (*pszCur == '/')
5081 6727 : pszCur++;
5082 :
5083 6728 : const char *pszCode = pszCur;
5084 :
5085 6728 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5086 : #endif
5087 : }
5088 :
5089 : /************************************************************************/
5090 : /* importFromWMSAUTO() */
5091 : /************************************************************************/
5092 :
5093 : /**
5094 : * \brief Initialize from WMSAUTO string.
5095 : *
5096 : * Note that the WMS 1.3 specification does not include the
5097 : * units code, while apparently earlier specs do. We try to
5098 : * guess around this.
5099 : *
5100 : * @param pszDefinition the WMSAUTO string
5101 : *
5102 : * @return OGRERR_NONE on success or an error code.
5103 : */
5104 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5105 :
5106 : {
5107 6 : TAKE_OPTIONAL_LOCK();
5108 :
5109 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5110 : if (strlen(pszDefinition) >= 10000)
5111 : {
5112 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5113 : return OGRERR_CORRUPT_DATA;
5114 : }
5115 :
5116 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5117 : if (!obj)
5118 : {
5119 : return OGRERR_FAILURE;
5120 : }
5121 : Clear();
5122 : d->setPjCRS(obj);
5123 : return OGRERR_NONE;
5124 : #else
5125 : int nProjId, nUnitsId;
5126 3 : double dfRefLong, dfRefLat = 0.0;
5127 :
5128 : /* -------------------------------------------------------------------- */
5129 : /* Tokenize */
5130 : /* -------------------------------------------------------------------- */
5131 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5132 3 : pszDefinition += 5;
5133 :
5134 : char **papszTokens =
5135 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5136 :
5137 3 : if (CSLCount(papszTokens) == 4)
5138 : {
5139 0 : nProjId = atoi(papszTokens[0]);
5140 0 : nUnitsId = atoi(papszTokens[1]);
5141 0 : dfRefLong = CPLAtof(papszTokens[2]);
5142 0 : dfRefLat = CPLAtof(papszTokens[3]);
5143 : }
5144 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5145 : {
5146 0 : nProjId = atoi(papszTokens[0]);
5147 0 : nUnitsId = atoi(papszTokens[1]);
5148 0 : dfRefLong = CPLAtof(papszTokens[2]);
5149 0 : dfRefLat = 0.0;
5150 : }
5151 3 : else if (CSLCount(papszTokens) == 3)
5152 : {
5153 2 : nProjId = atoi(papszTokens[0]);
5154 2 : nUnitsId = 9001;
5155 2 : dfRefLong = CPLAtof(papszTokens[1]);
5156 2 : dfRefLat = CPLAtof(papszTokens[2]);
5157 : }
5158 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5159 : {
5160 0 : nProjId = atoi(papszTokens[0]);
5161 0 : nUnitsId = 9001;
5162 0 : dfRefLong = CPLAtof(papszTokens[1]);
5163 : }
5164 : else
5165 : {
5166 1 : CSLDestroy(papszTokens);
5167 1 : CPLError(CE_Failure, CPLE_AppDefined,
5168 : "AUTO projection has wrong number of arguments, expected\n"
5169 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5170 : "AUTO:proj_id,ref_long,ref_lat");
5171 1 : return OGRERR_FAILURE;
5172 : }
5173 :
5174 2 : CSLDestroy(papszTokens);
5175 2 : papszTokens = nullptr;
5176 :
5177 : /* -------------------------------------------------------------------- */
5178 : /* Build coordsys. */
5179 : /* -------------------------------------------------------------------- */
5180 2 : Clear();
5181 :
5182 : /* -------------------------------------------------------------------- */
5183 : /* Set WGS84. */
5184 : /* -------------------------------------------------------------------- */
5185 2 : SetWellKnownGeogCS("WGS84");
5186 :
5187 2 : switch (nProjId)
5188 : {
5189 2 : case 42001: // Auto UTM
5190 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5191 : dfRefLat >= 0.0);
5192 2 : break;
5193 :
5194 0 : case 42002: // Auto TM (strangely very UTM-like).
5195 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5196 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5197 0 : break;
5198 :
5199 0 : case 42003: // Auto Orthographic.
5200 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5201 0 : break;
5202 :
5203 0 : case 42004: // Auto Equirectangular
5204 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5205 0 : break;
5206 :
5207 0 : case 42005:
5208 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5209 0 : break;
5210 :
5211 0 : default:
5212 0 : CPLError(CE_Failure, CPLE_AppDefined,
5213 : "Unsupported projection id in importFromWMSAUTO(): %d",
5214 : nProjId);
5215 0 : return OGRERR_FAILURE;
5216 : }
5217 :
5218 : /* -------------------------------------------------------------------- */
5219 : /* Set units. */
5220 : /* -------------------------------------------------------------------- */
5221 :
5222 2 : switch (nUnitsId)
5223 : {
5224 2 : case 9001:
5225 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5226 2 : break;
5227 :
5228 0 : case 9002:
5229 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5230 0 : break;
5231 :
5232 0 : case 9003:
5233 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5234 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5235 0 : break;
5236 :
5237 0 : default:
5238 0 : CPLError(CE_Failure, CPLE_AppDefined,
5239 : "Unsupported units code (%d).", nUnitsId);
5240 0 : return OGRERR_FAILURE;
5241 : break;
5242 : }
5243 :
5244 2 : return OGRERR_NONE;
5245 : #endif
5246 : }
5247 :
5248 : /************************************************************************/
5249 : /* GetSemiMajor() */
5250 : /************************************************************************/
5251 :
5252 : /**
5253 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5254 : *
5255 : * This method does the same thing as the C function OSRGetSemiMajor().
5256 : *
5257 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5258 : * can be found.
5259 : *
5260 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5261 : */
5262 :
5263 6855 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5264 :
5265 : {
5266 13710 : TAKE_OPTIONAL_LOCK();
5267 :
5268 6855 : if (pnErr != nullptr)
5269 3558 : *pnErr = OGRERR_FAILURE;
5270 :
5271 6855 : d->refreshProjObj();
5272 6855 : if (!d->m_pj_crs)
5273 111 : return SRS_WGS84_SEMIMAJOR;
5274 :
5275 6744 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5276 6744 : if (!ellps)
5277 5 : return SRS_WGS84_SEMIMAJOR;
5278 :
5279 6739 : double dfSemiMajor = 0.0;
5280 6739 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5281 : nullptr, nullptr, nullptr);
5282 6739 : proj_destroy(ellps);
5283 :
5284 6739 : if (dfSemiMajor > 0)
5285 : {
5286 6739 : if (pnErr != nullptr)
5287 3444 : *pnErr = OGRERR_NONE;
5288 6739 : return dfSemiMajor;
5289 : }
5290 :
5291 0 : return SRS_WGS84_SEMIMAJOR;
5292 : }
5293 :
5294 : /************************************************************************/
5295 : /* OSRGetSemiMajor() */
5296 : /************************************************************************/
5297 :
5298 : /**
5299 : * \brief Get spheroid semi major axis.
5300 : *
5301 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5302 : */
5303 87 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5304 :
5305 : {
5306 87 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5307 :
5308 87 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5309 : }
5310 :
5311 : /************************************************************************/
5312 : /* GetInvFlattening() */
5313 : /************************************************************************/
5314 :
5315 : /**
5316 : * \brief Get spheroid inverse flattening.
5317 : *
5318 : * This method does the same thing as the C function OSRGetInvFlattening().
5319 : *
5320 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5321 : * can be found.
5322 : *
5323 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5324 : */
5325 :
5326 4528 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5327 :
5328 : {
5329 9056 : TAKE_OPTIONAL_LOCK();
5330 :
5331 4528 : if (pnErr != nullptr)
5332 3448 : *pnErr = OGRERR_FAILURE;
5333 :
5334 4528 : d->refreshProjObj();
5335 4528 : if (!d->m_pj_crs)
5336 111 : return SRS_WGS84_INVFLATTENING;
5337 :
5338 4417 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5339 4417 : if (!ellps)
5340 2 : return SRS_WGS84_INVFLATTENING;
5341 :
5342 4415 : double dfInvFlattening = -1.0;
5343 4415 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5344 : nullptr, &dfInvFlattening);
5345 4415 : proj_destroy(ellps);
5346 :
5347 4415 : if (dfInvFlattening >= 0.0)
5348 : {
5349 4415 : if (pnErr != nullptr)
5350 3337 : *pnErr = OGRERR_NONE;
5351 4415 : return dfInvFlattening;
5352 : }
5353 :
5354 0 : return SRS_WGS84_INVFLATTENING;
5355 : }
5356 :
5357 : /************************************************************************/
5358 : /* OSRGetInvFlattening() */
5359 : /************************************************************************/
5360 :
5361 : /**
5362 : * \brief Get spheroid inverse flattening.
5363 : *
5364 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5365 : */
5366 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5367 :
5368 : {
5369 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5370 :
5371 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5372 : }
5373 :
5374 : /************************************************************************/
5375 : /* GetEccentricity() */
5376 : /************************************************************************/
5377 :
5378 : /**
5379 : * \brief Get spheroid eccentricity
5380 : *
5381 : * @return eccentricity (or -1 in case of error)
5382 : */
5383 :
5384 0 : double OGRSpatialReference::GetEccentricity() const
5385 :
5386 : {
5387 0 : OGRErr eErr = OGRERR_NONE;
5388 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5389 0 : if (eErr != OGRERR_NONE)
5390 : {
5391 0 : return -1.0;
5392 : }
5393 0 : if (dfInvFlattening == 0.0)
5394 0 : return 0.0;
5395 0 : if (dfInvFlattening < 0.5)
5396 0 : return -1.0;
5397 0 : return sqrt(2.0 / dfInvFlattening -
5398 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5399 : }
5400 :
5401 : /************************************************************************/
5402 : /* GetSquaredEccentricity() */
5403 : /************************************************************************/
5404 :
5405 : /**
5406 : * \brief Get spheroid squared eccentricity
5407 : *
5408 : * @return squared eccentricity (or -1 in case of error)
5409 : */
5410 :
5411 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5412 :
5413 : {
5414 0 : OGRErr eErr = OGRERR_NONE;
5415 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5416 0 : if (eErr != OGRERR_NONE)
5417 : {
5418 0 : return -1.0;
5419 : }
5420 0 : if (dfInvFlattening == 0.0)
5421 0 : return 0.0;
5422 0 : if (dfInvFlattening < 0.5)
5423 0 : return -1.0;
5424 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5425 : }
5426 :
5427 : /************************************************************************/
5428 : /* GetSemiMinor() */
5429 : /************************************************************************/
5430 :
5431 : /**
5432 : * \brief Get spheroid semi minor axis.
5433 : *
5434 : * This method does the same thing as the C function OSRGetSemiMinor().
5435 : *
5436 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5437 : * can be found.
5438 : *
5439 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5440 : */
5441 :
5442 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5443 :
5444 : {
5445 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5446 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5447 :
5448 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5449 : }
5450 :
5451 : /************************************************************************/
5452 : /* OSRGetSemiMinor() */
5453 : /************************************************************************/
5454 :
5455 : /**
5456 : * \brief Get spheroid semi minor axis.
5457 : *
5458 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5459 : */
5460 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5461 :
5462 : {
5463 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5464 :
5465 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5466 : }
5467 :
5468 : /************************************************************************/
5469 : /* SetLocalCS() */
5470 : /************************************************************************/
5471 :
5472 : /**
5473 : * \brief Set the user visible LOCAL_CS name.
5474 : *
5475 : * This method is the same as the C function OSRSetLocalCS().
5476 : *
5477 : * This method will ensure a LOCAL_CS node is created as the root,
5478 : * and set the provided name on it. It must be used before SetLinearUnits().
5479 : *
5480 : * @param pszName the user visible name to assign. Not used as a key.
5481 : *
5482 : * @return OGRERR_NONE on success.
5483 : */
5484 :
5485 2898 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5486 :
5487 : {
5488 5796 : TAKE_OPTIONAL_LOCK();
5489 :
5490 2898 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5491 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5492 : {
5493 2898 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5494 : }
5495 : else
5496 : {
5497 0 : CPLDebug("OGR",
5498 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5499 : "It appears an incompatible object already exists.",
5500 : pszName);
5501 0 : return OGRERR_FAILURE;
5502 : }
5503 :
5504 2898 : return OGRERR_NONE;
5505 : }
5506 :
5507 : /************************************************************************/
5508 : /* OSRSetLocalCS() */
5509 : /************************************************************************/
5510 :
5511 : /**
5512 : * \brief Set the user visible LOCAL_CS name.
5513 : *
5514 : * This function is the same as OGRSpatialReference::SetLocalCS()
5515 : */
5516 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5517 :
5518 : {
5519 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5520 :
5521 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5522 : }
5523 :
5524 : /************************************************************************/
5525 : /* SetGeocCS() */
5526 : /************************************************************************/
5527 :
5528 : /**
5529 : * \brief Set the user visible GEOCCS name.
5530 : *
5531 : * This method is the same as the C function OSRSetGeocCS().
5532 :
5533 : * This method will ensure a GEOCCS node is created as the root,
5534 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5535 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5536 : * the GEOGCS.
5537 : *
5538 : * @param pszName the user visible name to assign. Not used as a key.
5539 : *
5540 : * @return OGRERR_NONE on success.
5541 : *
5542 : */
5543 :
5544 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5545 :
5546 : {
5547 12 : TAKE_OPTIONAL_LOCK();
5548 :
5549 6 : OGRErr eErr = OGRERR_NONE;
5550 6 : d->refreshProjObj();
5551 6 : d->demoteFromBoundCRS();
5552 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5553 : {
5554 3 : d->setPjCRS(proj_create_geocentric_crs(
5555 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5556 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5557 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5558 : "Metre", 1.0));
5559 : }
5560 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5561 : {
5562 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5563 : }
5564 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5565 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5566 : {
5567 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5568 : #if PROJ_VERSION_MAJOR > 7 || \
5569 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5570 : if (datum == nullptr)
5571 : {
5572 : datum =
5573 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5574 : }
5575 : #endif
5576 1 : if (datum == nullptr)
5577 : {
5578 0 : d->undoDemoteFromBoundCRS();
5579 0 : return OGRERR_FAILURE;
5580 : }
5581 :
5582 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5583 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5584 : 0.0);
5585 1 : d->setPjCRS(pj_crs);
5586 :
5587 1 : proj_destroy(datum);
5588 : }
5589 : else
5590 : {
5591 1 : CPLDebug("OGR",
5592 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5593 : "It appears an incompatible object already exists.",
5594 : pszName);
5595 1 : eErr = OGRERR_FAILURE;
5596 : }
5597 6 : d->undoDemoteFromBoundCRS();
5598 :
5599 6 : return eErr;
5600 : }
5601 :
5602 : /************************************************************************/
5603 : /* OSRSetGeocCS() */
5604 : /************************************************************************/
5605 :
5606 : /**
5607 : * \brief Set the user visible PROJCS name.
5608 : *
5609 : * This function is the same as OGRSpatialReference::SetGeocCS()
5610 : *
5611 : */
5612 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5613 :
5614 : {
5615 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5616 :
5617 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5618 : }
5619 :
5620 : /************************************************************************/
5621 : /* SetVertCS() */
5622 : /************************************************************************/
5623 :
5624 : /**
5625 : * \brief Set the user visible VERT_CS name.
5626 : *
5627 : * This method is the same as the C function OSRSetVertCS().
5628 :
5629 : * This method will ensure a VERT_CS node is created if needed. If the
5630 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5631 : * turned into a COMPD_CS.
5632 : *
5633 : * @param pszVertCSName the user visible name of the vertical coordinate
5634 : * system. Not used as a key.
5635 : *
5636 : * @param pszVertDatumName the user visible name of the vertical datum. It
5637 : * is helpful if this matches the EPSG name.
5638 : *
5639 : * @param nVertDatumType the OGC vertical datum type. Ignored
5640 : *
5641 : * @return OGRERR_NONE on success.
5642 : *
5643 : */
5644 :
5645 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5646 : const char *pszVertDatumName,
5647 : int nVertDatumType)
5648 :
5649 : {
5650 1 : TAKE_OPTIONAL_LOCK();
5651 :
5652 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5653 :
5654 1 : d->refreshProjObj();
5655 :
5656 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5657 : pszVertDatumName, nullptr, 0.0);
5658 :
5659 : /* -------------------------------------------------------------------- */
5660 : /* Handle the case where we want to make a compound coordinate */
5661 : /* system. */
5662 : /* -------------------------------------------------------------------- */
5663 1 : if (IsProjected() || IsGeographic())
5664 : {
5665 1 : auto compoundCRS = proj_create_compound_crs(
5666 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5667 1 : proj_destroy(vertCRS);
5668 1 : d->setPjCRS(compoundCRS);
5669 : }
5670 : else
5671 : {
5672 0 : d->setPjCRS(vertCRS);
5673 : }
5674 2 : return OGRERR_NONE;
5675 : }
5676 :
5677 : /************************************************************************/
5678 : /* OSRSetVertCS() */
5679 : /************************************************************************/
5680 :
5681 : /**
5682 : * \brief Setup the vertical coordinate system.
5683 : *
5684 : * This function is the same as OGRSpatialReference::SetVertCS()
5685 : *
5686 : */
5687 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5688 : const char *pszVertDatumName, int nVertDatumType)
5689 :
5690 : {
5691 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5692 :
5693 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5694 0 : nVertDatumType);
5695 : }
5696 :
5697 : /************************************************************************/
5698 : /* SetCompoundCS() */
5699 : /************************************************************************/
5700 :
5701 : /**
5702 : * \brief Setup a compound coordinate system.
5703 : *
5704 : * This method is the same as the C function OSRSetCompoundCS().
5705 :
5706 : * This method is replace the current SRS with a COMPD_CS coordinate system
5707 : * consisting of the passed in horizontal and vertical coordinate systems.
5708 : *
5709 : * @param pszName the name of the compound coordinate system.
5710 : *
5711 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5712 : *
5713 : * @param poVertSRS the vertical SRS (VERT_CS).
5714 : *
5715 : * @return OGRERR_NONE on success.
5716 : */
5717 :
5718 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5719 : const OGRSpatialReference *poHorizSRS,
5720 : const OGRSpatialReference *poVertSRS)
5721 :
5722 : {
5723 184 : TAKE_OPTIONAL_LOCK();
5724 :
5725 : /* -------------------------------------------------------------------- */
5726 : /* Verify these are legal horizontal and vertical coordinate */
5727 : /* systems. */
5728 : /* -------------------------------------------------------------------- */
5729 92 : if (!poVertSRS->IsVertical())
5730 : {
5731 0 : CPLError(CE_Failure, CPLE_AppDefined,
5732 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5733 0 : return OGRERR_FAILURE;
5734 : }
5735 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5736 : {
5737 0 : CPLError(CE_Failure, CPLE_AppDefined,
5738 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5739 : "GEOGCS.");
5740 0 : return OGRERR_FAILURE;
5741 : }
5742 :
5743 : /* -------------------------------------------------------------------- */
5744 : /* Replace with compound srs. */
5745 : /* -------------------------------------------------------------------- */
5746 92 : Clear();
5747 :
5748 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5749 92 : poHorizSRS->d->m_pj_crs,
5750 92 : poVertSRS->d->m_pj_crs);
5751 92 : d->setPjCRS(compoundCRS);
5752 :
5753 92 : return OGRERR_NONE;
5754 : }
5755 :
5756 : /************************************************************************/
5757 : /* OSRSetCompoundCS() */
5758 : /************************************************************************/
5759 :
5760 : /**
5761 : * \brief Setup a compound coordinate system.
5762 : *
5763 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5764 : */
5765 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5766 : OGRSpatialReferenceH hHorizSRS,
5767 : OGRSpatialReferenceH hVertSRS)
5768 :
5769 : {
5770 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5771 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5772 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5773 :
5774 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5775 16 : ToPointer(hVertSRS));
5776 : }
5777 :
5778 : /************************************************************************/
5779 : /* SetProjCS() */
5780 : /************************************************************************/
5781 :
5782 : /**
5783 : * \brief Set the user visible PROJCS name.
5784 : *
5785 : * This method is the same as the C function OSRSetProjCS().
5786 : *
5787 : * This method will ensure a PROJCS node is created as the root,
5788 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5789 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5790 : *
5791 : * @param pszName the user visible name to assign. Not used as a key.
5792 : *
5793 : * @return OGRERR_NONE on success.
5794 : */
5795 :
5796 4822 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5797 :
5798 : {
5799 4822 : TAKE_OPTIONAL_LOCK();
5800 :
5801 4822 : d->refreshProjObj();
5802 4822 : d->demoteFromBoundCRS();
5803 4822 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5804 : {
5805 489 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5806 : }
5807 : else
5808 : {
5809 4333 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5810 : nullptr, nullptr, nullptr,
5811 : nullptr, nullptr, 0, nullptr);
5812 4333 : auto cs = proj_create_cartesian_2D_cs(
5813 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5814 :
5815 4333 : auto projCRS = proj_create_projected_crs(
5816 4333 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5817 4333 : proj_destroy(dummyConv);
5818 4333 : proj_destroy(cs);
5819 :
5820 4333 : d->setPjCRS(projCRS);
5821 : }
5822 4822 : d->undoDemoteFromBoundCRS();
5823 9644 : return OGRERR_NONE;
5824 : }
5825 :
5826 : /************************************************************************/
5827 : /* OSRSetProjCS() */
5828 : /************************************************************************/
5829 :
5830 : /**
5831 : * \brief Set the user visible PROJCS name.
5832 : *
5833 : * This function is the same as OGRSpatialReference::SetProjCS()
5834 : */
5835 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5836 :
5837 : {
5838 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5839 :
5840 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5841 : }
5842 :
5843 : /************************************************************************/
5844 : /* SetProjection() */
5845 : /************************************************************************/
5846 :
5847 : /**
5848 : * \brief Set a projection name.
5849 : *
5850 : * This method is the same as the C function OSRSetProjection().
5851 : *
5852 : * @param pszProjection the projection name, which should be selected from
5853 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5854 : *
5855 : * @return OGRERR_NONE on success.
5856 : */
5857 :
5858 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5859 :
5860 : {
5861 46 : TAKE_OPTIONAL_LOCK();
5862 :
5863 23 : OGR_SRSNode *poGeogCS = nullptr;
5864 :
5865 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5866 : {
5867 4 : poGeogCS = d->m_poRoot;
5868 4 : d->m_poRoot = nullptr;
5869 : }
5870 :
5871 23 : if (!GetAttrNode("PROJCS"))
5872 : {
5873 11 : SetNode("PROJCS", "unnamed");
5874 : }
5875 :
5876 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5877 23 : if (eErr != OGRERR_NONE)
5878 0 : return eErr;
5879 :
5880 23 : if (poGeogCS != nullptr)
5881 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5882 :
5883 23 : return OGRERR_NONE;
5884 : }
5885 :
5886 : /************************************************************************/
5887 : /* OSRSetProjection() */
5888 : /************************************************************************/
5889 :
5890 : /**
5891 : * \brief Set a projection name.
5892 : *
5893 : * This function is the same as OGRSpatialReference::SetProjection()
5894 : */
5895 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5896 :
5897 : {
5898 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5899 :
5900 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5901 : }
5902 :
5903 : /************************************************************************/
5904 : /* GetWKT2ProjectionMethod() */
5905 : /************************************************************************/
5906 :
5907 : /**
5908 : * \brief Returns info on the projection method, based on WKT2 naming
5909 : * conventions.
5910 : *
5911 : * The returned strings are short lived and should be considered to be
5912 : * invalidated by any further call to the GDAL API.
5913 : *
5914 : * @param[out] ppszMethodName Pointer to a string that will receive the
5915 : * projection method name.
5916 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5917 : * receive the name of the authority that defines the projection method.
5918 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5919 : * an authority.
5920 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5921 : * receive the code that defines the projection method.
5922 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5923 : * an authority.
5924 : *
5925 : * @return OGRERR_NONE on success.
5926 : */
5927 : OGRErr
5928 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5929 : const char **ppszMethodAuthName,
5930 : const char **ppszMethodCode) const
5931 : {
5932 2 : TAKE_OPTIONAL_LOCK();
5933 :
5934 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5935 1 : if (!conv)
5936 0 : return OGRERR_FAILURE;
5937 1 : const char *pszTmpMethodName = "";
5938 1 : const char *pszTmpMethodAuthName = "";
5939 1 : const char *pszTmpMethodCode = "";
5940 1 : int ret = proj_coordoperation_get_method_info(
5941 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5942 : &pszTmpMethodCode);
5943 : // "Internalize" temporary strings returned by PROJ
5944 1 : CPLAssert(pszTmpMethodName);
5945 1 : if (ppszMethodName)
5946 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5947 1 : if (ppszMethodAuthName)
5948 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5949 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5950 0 : : nullptr;
5951 1 : if (ppszMethodCode)
5952 0 : *ppszMethodCode =
5953 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5954 1 : proj_destroy(conv);
5955 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5956 : }
5957 :
5958 : /************************************************************************/
5959 : /* SetProjParm() */
5960 : /************************************************************************/
5961 :
5962 : /**
5963 : * \brief Set a projection parameter value.
5964 : *
5965 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5966 : *
5967 : * This method is the same as the C function OSRSetProjParm().
5968 : *
5969 : * Please check https://gdal.org/proj_list pages for
5970 : * legal parameter names for specific projections.
5971 : *
5972 : *
5973 : * @param pszParamName the parameter name, which should be selected from
5974 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5975 : *
5976 : * @param dfValue value to assign.
5977 : *
5978 : * @return OGRERR_NONE on success.
5979 : */
5980 :
5981 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5982 : double dfValue)
5983 :
5984 : {
5985 258 : TAKE_OPTIONAL_LOCK();
5986 :
5987 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5988 :
5989 129 : if (poPROJCS == nullptr)
5990 3 : return OGRERR_FAILURE;
5991 :
5992 126 : char szValue[64] = {'\0'};
5993 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5994 :
5995 : /* -------------------------------------------------------------------- */
5996 : /* Try to find existing parameter with this name. */
5997 : /* -------------------------------------------------------------------- */
5998 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5999 : {
6000 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
6001 :
6002 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
6003 1242 : poParam->GetChildCount() == 2 &&
6004 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
6005 : {
6006 39 : poParam->GetChild(1)->SetValue(szValue);
6007 39 : return OGRERR_NONE;
6008 : }
6009 : }
6010 :
6011 : /* -------------------------------------------------------------------- */
6012 : /* Otherwise create a new parameter and append. */
6013 : /* -------------------------------------------------------------------- */
6014 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6015 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
6016 87 : poParam->AddChild(new OGR_SRSNode(szValue));
6017 :
6018 87 : poPROJCS->AddChild(poParam);
6019 :
6020 87 : return OGRERR_NONE;
6021 : }
6022 :
6023 : /************************************************************************/
6024 : /* OSRSetProjParm() */
6025 : /************************************************************************/
6026 :
6027 : /**
6028 : * \brief Set a projection parameter value.
6029 : *
6030 : * This function is the same as OGRSpatialReference::SetProjParm()
6031 : */
6032 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6033 : double dfValue)
6034 :
6035 : {
6036 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6037 :
6038 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6039 : }
6040 :
6041 : /************************************************************************/
6042 : /* FindProjParm() */
6043 : /************************************************************************/
6044 :
6045 : /**
6046 : * \brief Return the child index of the named projection parameter on
6047 : * its parent PROJCS node.
6048 : *
6049 : * @param pszParameter projection parameter to look for
6050 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6051 : * the PROJCS node of the SpatialReference object will be searched.
6052 : *
6053 : * @return the child index of the named projection parameter. -1 on failure
6054 : */
6055 5152 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6056 : const OGR_SRSNode *poPROJCS) const
6057 :
6058 : {
6059 10304 : TAKE_OPTIONAL_LOCK();
6060 :
6061 5152 : if (poPROJCS == nullptr)
6062 0 : poPROJCS = GetAttrNode("PROJCS");
6063 :
6064 5152 : if (poPROJCS == nullptr)
6065 0 : return -1;
6066 :
6067 : /* -------------------------------------------------------------------- */
6068 : /* Search for requested parameter. */
6069 : /* -------------------------------------------------------------------- */
6070 5152 : bool bIsWKT2 = false;
6071 33875 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6072 : {
6073 33204 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6074 :
6075 33204 : if (poParameter->GetChildCount() >= 2)
6076 : {
6077 22941 : const char *pszValue = poParameter->GetValue();
6078 37857 : if (EQUAL(pszValue, "PARAMETER") &&
6079 14916 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6080 : pszParameter))
6081 : {
6082 4481 : return iChild;
6083 : }
6084 18460 : else if (EQUAL(pszValue, "METHOD"))
6085 : {
6086 41 : bIsWKT2 = true;
6087 : }
6088 : }
6089 : }
6090 :
6091 : /* -------------------------------------------------------------------- */
6092 : /* Try similar names, for selected parameters. */
6093 : /* -------------------------------------------------------------------- */
6094 671 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6095 : {
6096 314 : if (bIsWKT2)
6097 : {
6098 8 : int iChild = FindProjParm(
6099 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6100 8 : if (iChild == -1)
6101 3 : iChild = FindProjParm(
6102 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6103 8 : return iChild;
6104 : }
6105 306 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6106 : }
6107 :
6108 357 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6109 : {
6110 38 : if (bIsWKT2)
6111 : {
6112 9 : int iChild = FindProjParm(
6113 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6114 9 : if (iChild == -1)
6115 0 : iChild = FindProjParm(
6116 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6117 9 : return iChild;
6118 : }
6119 29 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6120 29 : if (iChild == -1)
6121 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6122 29 : return iChild;
6123 : }
6124 :
6125 319 : return -1;
6126 : }
6127 :
6128 : /************************************************************************/
6129 : /* GetProjParm() */
6130 : /************************************************************************/
6131 :
6132 : /**
6133 : * \brief Fetch a projection parameter value.
6134 : *
6135 : * NOTE: This code should be modified to translate non degree angles into
6136 : * degrees based on the GEOGCS unit. This has not yet been done.
6137 : *
6138 : * This method is the same as the C function OSRGetProjParm().
6139 : *
6140 : * @param pszName the name of the parameter to fetch, from the set of
6141 : * SRS_PP codes in ogr_srs_api.h.
6142 : *
6143 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6144 : *
6145 : * @param pnErr place to put error code on failure. Ignored if NULL.
6146 : *
6147 : * @return value of parameter.
6148 : */
6149 :
6150 5055 : double OGRSpatialReference::GetProjParm(const char *pszName,
6151 : double dfDefaultValue,
6152 : OGRErr *pnErr) const
6153 :
6154 : {
6155 10110 : TAKE_OPTIONAL_LOCK();
6156 :
6157 5055 : d->refreshProjObj();
6158 5055 : GetRoot(); // force update of d->m_bNodesWKT2
6159 :
6160 5055 : if (pnErr != nullptr)
6161 4055 : *pnErr = OGRERR_NONE;
6162 :
6163 : /* -------------------------------------------------------------------- */
6164 : /* Find the desired parameter. */
6165 : /* -------------------------------------------------------------------- */
6166 : const OGR_SRSNode *poPROJCS =
6167 5055 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6168 5055 : if (poPROJCS == nullptr)
6169 : {
6170 258 : if (pnErr != nullptr)
6171 258 : *pnErr = OGRERR_FAILURE;
6172 258 : return dfDefaultValue;
6173 : }
6174 :
6175 4797 : const int iChild = FindProjParm(pszName, poPROJCS);
6176 4797 : if (iChild == -1)
6177 : {
6178 316 : if (IsProjected() && GetAxesCount() == 3)
6179 : {
6180 3 : OGRSpatialReference *poSRSTmp = Clone();
6181 3 : poSRSTmp->DemoteTo2D(nullptr);
6182 : const double dfRet =
6183 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6184 3 : delete poSRSTmp;
6185 3 : return dfRet;
6186 : }
6187 :
6188 313 : if (pnErr != nullptr)
6189 291 : *pnErr = OGRERR_FAILURE;
6190 313 : return dfDefaultValue;
6191 : }
6192 :
6193 4481 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6194 4481 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6195 : }
6196 :
6197 : /************************************************************************/
6198 : /* OSRGetProjParm() */
6199 : /************************************************************************/
6200 :
6201 : /**
6202 : * \brief Fetch a projection parameter value.
6203 : *
6204 : * This function is the same as OGRSpatialReference::GetProjParm()
6205 : */
6206 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6207 : double dfDefaultValue, OGRErr *pnErr)
6208 :
6209 : {
6210 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6211 :
6212 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6213 : }
6214 :
6215 : /************************************************************************/
6216 : /* GetNormProjParm() */
6217 : /************************************************************************/
6218 :
6219 : /**
6220 : * \brief Fetch a normalized projection parameter value.
6221 : *
6222 : * This method is the same as GetProjParm() except that the value of
6223 : * the parameter is "normalized" into degrees or meters depending on
6224 : * whether it is linear or angular.
6225 : *
6226 : * This method is the same as the C function OSRGetNormProjParm().
6227 : *
6228 : * @param pszName the name of the parameter to fetch, from the set of
6229 : * SRS_PP codes in ogr_srs_api.h.
6230 : *
6231 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6232 : *
6233 : * @param pnErr place to put error code on failure. Ignored if NULL.
6234 : *
6235 : * @return value of parameter.
6236 : */
6237 :
6238 4030 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6239 : double dfDefaultValue,
6240 : OGRErr *pnErr) const
6241 :
6242 : {
6243 8060 : TAKE_OPTIONAL_LOCK();
6244 :
6245 4030 : GetNormInfo();
6246 :
6247 4030 : OGRErr nError = OGRERR_NONE;
6248 4030 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6249 4030 : if (pnErr != nullptr)
6250 0 : *pnErr = nError;
6251 :
6252 : // If we got the default just return it unadjusted.
6253 4030 : if (nError != OGRERR_NONE)
6254 549 : return dfRawResult;
6255 :
6256 3481 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6257 8 : dfRawResult *= d->dfToDegrees;
6258 :
6259 3481 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6260 5 : return dfRawResult * d->dfToMeter;
6261 :
6262 3476 : return dfRawResult;
6263 : }
6264 :
6265 : /************************************************************************/
6266 : /* OSRGetNormProjParm() */
6267 : /************************************************************************/
6268 :
6269 : /**
6270 : * \brief This function is the same as OGRSpatialReference::
6271 : *
6272 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6273 : */
6274 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6275 : double dfDefaultValue, OGRErr *pnErr)
6276 :
6277 : {
6278 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6279 :
6280 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6281 : }
6282 :
6283 : /************************************************************************/
6284 : /* SetNormProjParm() */
6285 : /************************************************************************/
6286 :
6287 : /**
6288 : * \brief Set a projection parameter with a normalized value.
6289 : *
6290 : * This method is the same as SetProjParm() except that the value of
6291 : * the parameter passed in is assumed to be in "normalized" form (decimal
6292 : * degrees for angular values, meters for linear values. The values are
6293 : * converted in a form suitable for the GEOGCS and linear units in effect.
6294 : *
6295 : * This method is the same as the C function OSRSetNormProjParm().
6296 : *
6297 : * @param pszName the parameter name, which should be selected from
6298 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6299 : *
6300 : * @param dfValue value to assign.
6301 : *
6302 : * @return OGRERR_NONE on success.
6303 : */
6304 :
6305 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6306 :
6307 : {
6308 182 : TAKE_OPTIONAL_LOCK();
6309 :
6310 91 : GetNormInfo();
6311 :
6312 91 : if (d->dfToDegrees != 0.0 &&
6313 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6314 0 : IsAngularParameter(pszName))
6315 : {
6316 0 : dfValue /= d->dfToDegrees;
6317 : }
6318 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6319 4 : IsLinearParameter(pszName))
6320 4 : dfValue /= d->dfToMeter;
6321 :
6322 182 : return SetProjParm(pszName, dfValue);
6323 : }
6324 :
6325 : /************************************************************************/
6326 : /* OSRSetNormProjParm() */
6327 : /************************************************************************/
6328 :
6329 : /**
6330 : * \brief Set a projection parameter with a normalized value.
6331 : *
6332 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6333 : */
6334 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6335 : double dfValue)
6336 :
6337 : {
6338 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6339 :
6340 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6341 : }
6342 :
6343 : /************************************************************************/
6344 : /* SetTM() */
6345 : /************************************************************************/
6346 :
6347 443 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6348 : double dfScale, double dfFalseEasting,
6349 : double dfFalseNorthing)
6350 :
6351 : {
6352 886 : TAKE_OPTIONAL_LOCK();
6353 :
6354 443 : return d->replaceConversionAndUnref(
6355 : proj_create_conversion_transverse_mercator(
6356 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6357 886 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6358 : }
6359 :
6360 : /************************************************************************/
6361 : /* OSRSetTM() */
6362 : /************************************************************************/
6363 :
6364 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6365 : double dfCenterLong, double dfScale, double dfFalseEasting,
6366 : double dfFalseNorthing)
6367 :
6368 : {
6369 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6370 :
6371 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6372 1 : dfFalseEasting, dfFalseNorthing);
6373 : }
6374 :
6375 : /************************************************************************/
6376 : /* SetTMVariant() */
6377 : /************************************************************************/
6378 :
6379 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6380 : double dfCenterLat,
6381 : double dfCenterLong, double dfScale,
6382 : double dfFalseEasting,
6383 : double dfFalseNorthing)
6384 :
6385 : {
6386 0 : TAKE_OPTIONAL_LOCK();
6387 :
6388 0 : SetProjection(pszVariantName);
6389 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6390 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6391 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6392 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6393 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6394 :
6395 0 : return OGRERR_NONE;
6396 : }
6397 :
6398 : /************************************************************************/
6399 : /* OSRSetTMVariant() */
6400 : /************************************************************************/
6401 :
6402 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6403 : double dfCenterLat, double dfCenterLong, double dfScale,
6404 : double dfFalseEasting, double dfFalseNorthing)
6405 :
6406 : {
6407 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6408 :
6409 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6410 : dfCenterLong, dfScale, dfFalseEasting,
6411 0 : dfFalseNorthing);
6412 : }
6413 :
6414 : /************************************************************************/
6415 : /* SetTMSO() */
6416 : /************************************************************************/
6417 :
6418 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6419 : double dfScale, double dfFalseEasting,
6420 : double dfFalseNorthing)
6421 :
6422 : {
6423 6 : TAKE_OPTIONAL_LOCK();
6424 :
6425 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6426 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6427 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6428 :
6429 3 : const char *pszName = nullptr;
6430 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6431 3 : CPLString osName = pszName ? pszName : "";
6432 :
6433 3 : d->refreshProjObj();
6434 :
6435 3 : d->demoteFromBoundCRS();
6436 :
6437 3 : auto cs = proj_create_cartesian_2D_cs(
6438 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6439 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6440 : auto projCRS =
6441 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6442 3 : d->getGeodBaseCRS(), conv, cs);
6443 3 : proj_destroy(conv);
6444 3 : proj_destroy(cs);
6445 :
6446 3 : d->setPjCRS(projCRS);
6447 :
6448 3 : d->undoDemoteFromBoundCRS();
6449 :
6450 6 : return OGRERR_NONE;
6451 : }
6452 :
6453 : /************************************************************************/
6454 : /* OSRSetTMSO() */
6455 : /************************************************************************/
6456 :
6457 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6458 : double dfCenterLong, double dfScale, double dfFalseEasting,
6459 : double dfFalseNorthing)
6460 :
6461 : {
6462 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6463 :
6464 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6465 0 : dfFalseEasting, dfFalseNorthing);
6466 : }
6467 :
6468 : /************************************************************************/
6469 : /* SetTPED() */
6470 : /************************************************************************/
6471 :
6472 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6473 : double dfLat2, double dfLong2,
6474 : double dfFalseEasting,
6475 : double dfFalseNorthing)
6476 :
6477 : {
6478 2 : TAKE_OPTIONAL_LOCK();
6479 :
6480 1 : return d->replaceConversionAndUnref(
6481 : proj_create_conversion_two_point_equidistant(
6482 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6483 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6484 : }
6485 :
6486 : /************************************************************************/
6487 : /* OSRSetTPED() */
6488 : /************************************************************************/
6489 :
6490 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6491 : double dfLat2, double dfLong2, double dfFalseEasting,
6492 : double dfFalseNorthing)
6493 :
6494 : {
6495 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6496 :
6497 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6498 0 : dfFalseEasting, dfFalseNorthing);
6499 : }
6500 :
6501 : /************************************************************************/
6502 : /* SetTMG() */
6503 : /************************************************************************/
6504 :
6505 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6506 : double dfFalseEasting,
6507 : double dfFalseNorthing)
6508 :
6509 : {
6510 0 : TAKE_OPTIONAL_LOCK();
6511 :
6512 0 : return d->replaceConversionAndUnref(
6513 : proj_create_conversion_tunisia_mapping_grid(
6514 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6515 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6516 : }
6517 :
6518 : /************************************************************************/
6519 : /* OSRSetTMG() */
6520 : /************************************************************************/
6521 :
6522 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6523 : double dfCenterLong, double dfFalseEasting,
6524 : double dfFalseNorthing)
6525 :
6526 : {
6527 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6528 :
6529 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6530 0 : dfFalseNorthing);
6531 : }
6532 :
6533 : /************************************************************************/
6534 : /* SetACEA() */
6535 : /************************************************************************/
6536 :
6537 39 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6538 : double dfCenterLat, double dfCenterLong,
6539 : double dfFalseEasting,
6540 : double dfFalseNorthing)
6541 :
6542 : {
6543 78 : TAKE_OPTIONAL_LOCK();
6544 :
6545 : // Note different order of parameters. The one in PROJ is conformant with
6546 : // EPSG
6547 39 : return d->replaceConversionAndUnref(
6548 : proj_create_conversion_albers_equal_area(
6549 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6550 78 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6551 : }
6552 :
6553 : /************************************************************************/
6554 : /* OSRSetACEA() */
6555 : /************************************************************************/
6556 :
6557 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6558 : double dfCenterLat, double dfCenterLong,
6559 : double dfFalseEasting, double dfFalseNorthing)
6560 :
6561 : {
6562 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6563 :
6564 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6565 0 : dfFalseEasting, dfFalseNorthing);
6566 : }
6567 :
6568 : /************************************************************************/
6569 : /* SetAE() */
6570 : /************************************************************************/
6571 :
6572 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6573 : double dfFalseEasting, double dfFalseNorthing)
6574 :
6575 : {
6576 42 : TAKE_OPTIONAL_LOCK();
6577 :
6578 21 : return d->replaceConversionAndUnref(
6579 : proj_create_conversion_azimuthal_equidistant(
6580 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6581 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6582 : }
6583 :
6584 : /************************************************************************/
6585 : /* OSRSetAE() */
6586 : /************************************************************************/
6587 :
6588 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6589 : double dfCenterLong, double dfFalseEasting,
6590 : double dfFalseNorthing)
6591 :
6592 : {
6593 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6594 :
6595 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6596 0 : dfFalseNorthing);
6597 : }
6598 :
6599 : /************************************************************************/
6600 : /* SetBonne() */
6601 : /************************************************************************/
6602 :
6603 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6604 : double dfFalseEasting,
6605 : double dfFalseNorthing)
6606 :
6607 : {
6608 2 : TAKE_OPTIONAL_LOCK();
6609 :
6610 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6611 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6612 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6613 : }
6614 :
6615 : /************************************************************************/
6616 : /* OSRSetBonne() */
6617 : /************************************************************************/
6618 :
6619 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6620 : double dfCentralMeridian, double dfFalseEasting,
6621 : double dfFalseNorthing)
6622 :
6623 : {
6624 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6625 :
6626 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6627 0 : dfFalseNorthing);
6628 : }
6629 :
6630 : /************************************************************************/
6631 : /* SetCEA() */
6632 : /************************************************************************/
6633 :
6634 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6635 : double dfFalseEasting,
6636 : double dfFalseNorthing)
6637 :
6638 : {
6639 8 : TAKE_OPTIONAL_LOCK();
6640 :
6641 4 : return d->replaceConversionAndUnref(
6642 : proj_create_conversion_lambert_cylindrical_equal_area(
6643 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6644 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6645 : }
6646 :
6647 : /************************************************************************/
6648 : /* OSRSetCEA() */
6649 : /************************************************************************/
6650 :
6651 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6652 : double dfCentralMeridian, double dfFalseEasting,
6653 : double dfFalseNorthing)
6654 :
6655 : {
6656 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6657 :
6658 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6659 0 : dfFalseNorthing);
6660 : }
6661 :
6662 : /************************************************************************/
6663 : /* SetCS() */
6664 : /************************************************************************/
6665 :
6666 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6667 : double dfFalseEasting, double dfFalseNorthing)
6668 :
6669 : {
6670 10 : TAKE_OPTIONAL_LOCK();
6671 :
6672 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6673 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6674 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6675 : }
6676 :
6677 : /************************************************************************/
6678 : /* OSRSetCS() */
6679 : /************************************************************************/
6680 :
6681 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6682 : double dfCenterLong, double dfFalseEasting,
6683 : double dfFalseNorthing)
6684 :
6685 : {
6686 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6687 :
6688 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6689 0 : dfFalseNorthing);
6690 : }
6691 :
6692 : /************************************************************************/
6693 : /* SetEC() */
6694 : /************************************************************************/
6695 :
6696 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6697 : double dfCenterLat, double dfCenterLong,
6698 : double dfFalseEasting, double dfFalseNorthing)
6699 :
6700 : {
6701 14 : TAKE_OPTIONAL_LOCK();
6702 :
6703 : // Note: different order of arguments
6704 7 : return d->replaceConversionAndUnref(
6705 : proj_create_conversion_equidistant_conic(
6706 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6707 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6708 : }
6709 :
6710 : /************************************************************************/
6711 : /* OSRSetEC() */
6712 : /************************************************************************/
6713 :
6714 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6715 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6716 : double dfFalseNorthing)
6717 :
6718 : {
6719 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6720 :
6721 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6722 0 : dfFalseEasting, dfFalseNorthing);
6723 : }
6724 :
6725 : /************************************************************************/
6726 : /* SetEckert() */
6727 : /************************************************************************/
6728 :
6729 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6730 : double dfCentralMeridian,
6731 : double dfFalseEasting,
6732 : double dfFalseNorthing)
6733 :
6734 : {
6735 20 : TAKE_OPTIONAL_LOCK();
6736 :
6737 : PJ *conv;
6738 10 : if (nVariation == 1)
6739 : {
6740 1 : conv = proj_create_conversion_eckert_i(
6741 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6742 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6743 : }
6744 9 : else if (nVariation == 2)
6745 : {
6746 1 : conv = proj_create_conversion_eckert_ii(
6747 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6748 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6749 : }
6750 8 : else if (nVariation == 3)
6751 : {
6752 1 : conv = proj_create_conversion_eckert_iii(
6753 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6754 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6755 : }
6756 7 : else if (nVariation == 4)
6757 : {
6758 3 : conv = proj_create_conversion_eckert_iv(
6759 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6760 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6761 : }
6762 4 : else if (nVariation == 5)
6763 : {
6764 1 : conv = proj_create_conversion_eckert_v(
6765 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6766 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6767 : }
6768 3 : else if (nVariation == 6)
6769 : {
6770 3 : conv = proj_create_conversion_eckert_vi(
6771 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6772 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6773 : }
6774 : else
6775 : {
6776 0 : CPLError(CE_Failure, CPLE_AppDefined,
6777 : "Unsupported Eckert variation (%d).", nVariation);
6778 0 : return OGRERR_UNSUPPORTED_SRS;
6779 : }
6780 :
6781 10 : return d->replaceConversionAndUnref(conv);
6782 : }
6783 :
6784 : /************************************************************************/
6785 : /* OSRSetEckert() */
6786 : /************************************************************************/
6787 :
6788 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6789 : double dfCentralMeridian, double dfFalseEasting,
6790 : double dfFalseNorthing)
6791 :
6792 : {
6793 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6794 :
6795 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6796 0 : dfFalseEasting, dfFalseNorthing);
6797 : }
6798 :
6799 : /************************************************************************/
6800 : /* SetEckertIV() */
6801 : /* */
6802 : /* Deprecated */
6803 : /************************************************************************/
6804 :
6805 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6806 : double dfFalseEasting,
6807 : double dfFalseNorthing)
6808 :
6809 : {
6810 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6811 : }
6812 :
6813 : /************************************************************************/
6814 : /* OSRSetEckertIV() */
6815 : /************************************************************************/
6816 :
6817 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6818 : double dfFalseEasting, double dfFalseNorthing)
6819 :
6820 : {
6821 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6822 :
6823 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6824 0 : dfFalseNorthing);
6825 : }
6826 :
6827 : /************************************************************************/
6828 : /* SetEckertVI() */
6829 : /* */
6830 : /* Deprecated */
6831 : /************************************************************************/
6832 :
6833 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6834 : double dfFalseEasting,
6835 : double dfFalseNorthing)
6836 :
6837 : {
6838 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6839 : }
6840 :
6841 : /************************************************************************/
6842 : /* OSRSetEckertVI() */
6843 : /************************************************************************/
6844 :
6845 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6846 : double dfFalseEasting, double dfFalseNorthing)
6847 :
6848 : {
6849 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6850 :
6851 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6852 0 : dfFalseNorthing);
6853 : }
6854 :
6855 : /************************************************************************/
6856 : /* SetEquirectangular() */
6857 : /************************************************************************/
6858 :
6859 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6860 : double dfCenterLong,
6861 : double dfFalseEasting,
6862 : double dfFalseNorthing)
6863 :
6864 : {
6865 4 : TAKE_OPTIONAL_LOCK();
6866 :
6867 2 : if (dfCenterLat == 0.0)
6868 : {
6869 0 : return d->replaceConversionAndUnref(
6870 : proj_create_conversion_equidistant_cylindrical(
6871 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6872 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6873 : }
6874 :
6875 : // Non-standard extension with non-zero latitude of origin
6876 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6877 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6878 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6879 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6880 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6881 :
6882 2 : return OGRERR_NONE;
6883 : }
6884 :
6885 : /************************************************************************/
6886 : /* OSRSetEquirectangular() */
6887 : /************************************************************************/
6888 :
6889 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6890 : double dfCenterLong, double dfFalseEasting,
6891 : double dfFalseNorthing)
6892 :
6893 : {
6894 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6895 :
6896 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6897 0 : dfFalseEasting, dfFalseNorthing);
6898 : }
6899 :
6900 : /************************************************************************/
6901 : /* SetEquirectangular2() */
6902 : /* Generalized form */
6903 : /************************************************************************/
6904 :
6905 179 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6906 : double dfCenterLong,
6907 : double dfStdParallel1,
6908 : double dfFalseEasting,
6909 : double dfFalseNorthing)
6910 :
6911 : {
6912 358 : TAKE_OPTIONAL_LOCK();
6913 :
6914 179 : if (dfCenterLat == 0.0)
6915 : {
6916 174 : return d->replaceConversionAndUnref(
6917 : proj_create_conversion_equidistant_cylindrical(
6918 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6919 174 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6920 : }
6921 :
6922 : // Non-standard extension with non-zero latitude of origin
6923 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6924 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6925 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6926 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6927 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6928 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6929 :
6930 5 : return OGRERR_NONE;
6931 : }
6932 :
6933 : /************************************************************************/
6934 : /* OSRSetEquirectangular2() */
6935 : /************************************************************************/
6936 :
6937 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6938 : double dfCenterLong, double dfStdParallel1,
6939 : double dfFalseEasting, double dfFalseNorthing)
6940 :
6941 : {
6942 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6943 :
6944 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6945 : dfStdParallel1, dfFalseEasting,
6946 3 : dfFalseNorthing);
6947 : }
6948 :
6949 : /************************************************************************/
6950 : /* SetGS() */
6951 : /************************************************************************/
6952 :
6953 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6954 : double dfFalseEasting, double dfFalseNorthing)
6955 :
6956 : {
6957 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6958 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6959 5 : nullptr, 0.0, nullptr, 0.0));
6960 : }
6961 :
6962 : /************************************************************************/
6963 : /* OSRSetGS() */
6964 : /************************************************************************/
6965 :
6966 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6967 : double dfFalseEasting, double dfFalseNorthing)
6968 :
6969 : {
6970 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6971 :
6972 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6973 2 : dfFalseNorthing);
6974 : }
6975 :
6976 : /************************************************************************/
6977 : /* SetGH() */
6978 : /************************************************************************/
6979 :
6980 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6981 : double dfFalseEasting, double dfFalseNorthing)
6982 :
6983 : {
6984 0 : TAKE_OPTIONAL_LOCK();
6985 :
6986 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6987 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6988 0 : nullptr, 0.0, nullptr, 0.0));
6989 : }
6990 :
6991 : /************************************************************************/
6992 : /* OSRSetGH() */
6993 : /************************************************************************/
6994 :
6995 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6996 : double dfFalseEasting, double dfFalseNorthing)
6997 :
6998 : {
6999 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
7000 :
7001 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
7002 0 : dfFalseNorthing);
7003 : }
7004 :
7005 : /************************************************************************/
7006 : /* SetIGH() */
7007 : /************************************************************************/
7008 :
7009 0 : OGRErr OGRSpatialReference::SetIGH()
7010 :
7011 : {
7012 0 : TAKE_OPTIONAL_LOCK();
7013 :
7014 0 : return d->replaceConversionAndUnref(
7015 : proj_create_conversion_interrupted_goode_homolosine(
7016 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7017 : }
7018 :
7019 : /************************************************************************/
7020 : /* OSRSetIGH() */
7021 : /************************************************************************/
7022 :
7023 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7024 :
7025 : {
7026 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7027 :
7028 0 : return ToPointer(hSRS)->SetIGH();
7029 : }
7030 :
7031 : /************************************************************************/
7032 : /* SetGEOS() */
7033 : /************************************************************************/
7034 :
7035 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7036 : double dfSatelliteHeight,
7037 : double dfFalseEasting,
7038 : double dfFalseNorthing)
7039 :
7040 : {
7041 6 : TAKE_OPTIONAL_LOCK();
7042 :
7043 3 : return d->replaceConversionAndUnref(
7044 : proj_create_conversion_geostationary_satellite_sweep_y(
7045 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7046 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7047 : }
7048 :
7049 : /************************************************************************/
7050 : /* OSRSetGEOS() */
7051 : /************************************************************************/
7052 :
7053 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7054 : double dfSatelliteHeight, double dfFalseEasting,
7055 : double dfFalseNorthing)
7056 :
7057 : {
7058 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7059 :
7060 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7061 0 : dfFalseEasting, dfFalseNorthing);
7062 : }
7063 :
7064 : /************************************************************************/
7065 : /* SetGaussSchreiberTMercator() */
7066 : /************************************************************************/
7067 :
7068 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7069 : double dfCenterLong,
7070 : double dfScale,
7071 : double dfFalseEasting,
7072 : double dfFalseNorthing)
7073 :
7074 : {
7075 0 : TAKE_OPTIONAL_LOCK();
7076 :
7077 0 : return d->replaceConversionAndUnref(
7078 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7079 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7080 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7081 : }
7082 :
7083 : /************************************************************************/
7084 : /* OSRSetGaussSchreiberTMercator() */
7085 : /************************************************************************/
7086 :
7087 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7088 : double dfCenterLat, double dfCenterLong,
7089 : double dfScale, double dfFalseEasting,
7090 : double dfFalseNorthing)
7091 :
7092 : {
7093 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7094 :
7095 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7096 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7097 : }
7098 :
7099 : /************************************************************************/
7100 : /* SetGnomonic() */
7101 : /************************************************************************/
7102 :
7103 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7104 : double dfFalseEasting,
7105 : double dfFalseNorthing)
7106 :
7107 : {
7108 4 : TAKE_OPTIONAL_LOCK();
7109 :
7110 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7111 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7112 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7113 : }
7114 :
7115 : /************************************************************************/
7116 : /* OSRSetGnomonic() */
7117 : /************************************************************************/
7118 :
7119 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7120 : double dfCenterLong, double dfFalseEasting,
7121 : double dfFalseNorthing)
7122 :
7123 : {
7124 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7125 :
7126 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7127 0 : dfFalseEasting, dfFalseNorthing);
7128 : }
7129 :
7130 : /************************************************************************/
7131 : /* SetHOMAC() */
7132 : /************************************************************************/
7133 :
7134 : /**
7135 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7136 : * azimuth angle.
7137 : *
7138 : * This projection corresponds to EPSG projection method 9815, also
7139 : * sometimes known as hotine oblique mercator (variant B).
7140 : *
7141 : * This method does the same thing as the C function OSRSetHOMAC().
7142 : *
7143 : * @param dfCenterLat Latitude of the projection origin.
7144 : * @param dfCenterLong Longitude of the projection origin.
7145 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7146 : * centerline.
7147 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7148 : * @param dfScale Scale factor applies to the projection origin.
7149 : * @param dfFalseEasting False easting.
7150 : * @param dfFalseNorthing False northing.
7151 : *
7152 : * @return OGRERR_NONE on success.
7153 : */
7154 :
7155 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7156 : double dfAzimuth, double dfRectToSkew,
7157 : double dfScale, double dfFalseEasting,
7158 : double dfFalseNorthing)
7159 :
7160 : {
7161 8 : TAKE_OPTIONAL_LOCK();
7162 :
7163 4 : return d->replaceConversionAndUnref(
7164 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7165 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7166 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7167 8 : 0.0, nullptr, 0.0));
7168 : }
7169 :
7170 : /************************************************************************/
7171 : /* OSRSetHOMAC() */
7172 : /************************************************************************/
7173 :
7174 : /**
7175 : * \brief Set an Oblique Mercator projection using azimuth angle.
7176 : *
7177 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7178 : */
7179 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7180 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7181 : double dfScale, double dfFalseEasting,
7182 : double dfFalseNorthing)
7183 :
7184 : {
7185 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7186 :
7187 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7188 : dfRectToSkew, dfScale, dfFalseEasting,
7189 0 : dfFalseNorthing);
7190 : }
7191 :
7192 : /************************************************************************/
7193 : /* SetHOM() */
7194 : /************************************************************************/
7195 :
7196 : /**
7197 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7198 : *
7199 : * This projection corresponds to EPSG projection method 9812, also
7200 : * sometimes known as hotine oblique mercator (variant A)..
7201 : *
7202 : * This method does the same thing as the C function OSRSetHOM().
7203 : *
7204 : * @param dfCenterLat Latitude of the projection origin.
7205 : * @param dfCenterLong Longitude of the projection origin.
7206 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7207 : * centerline.
7208 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7209 : * @param dfScale Scale factor applies to the projection origin.
7210 : * @param dfFalseEasting False easting.
7211 : * @param dfFalseNorthing False northing.
7212 : *
7213 : * @return OGRERR_NONE on success.
7214 : */
7215 :
7216 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7217 : double dfAzimuth, double dfRectToSkew,
7218 : double dfScale, double dfFalseEasting,
7219 : double dfFalseNorthing)
7220 :
7221 : {
7222 26 : TAKE_OPTIONAL_LOCK();
7223 :
7224 13 : return d->replaceConversionAndUnref(
7225 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7226 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7227 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7228 26 : 0.0, nullptr, 0.0));
7229 : }
7230 :
7231 : /************************************************************************/
7232 : /* OSRSetHOM() */
7233 : /************************************************************************/
7234 : /**
7235 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7236 : *
7237 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7238 : */
7239 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7240 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7241 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7242 :
7243 : {
7244 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7245 :
7246 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7247 : dfRectToSkew, dfScale, dfFalseEasting,
7248 0 : dfFalseNorthing);
7249 : }
7250 :
7251 : /************************************************************************/
7252 : /* SetHOM2PNO() */
7253 : /************************************************************************/
7254 :
7255 : /**
7256 : * \brief Set a Hotine Oblique Mercator projection using two points on
7257 : * projection centerline.
7258 : *
7259 : * This method does the same thing as the C function OSRSetHOM2PNO().
7260 : *
7261 : * @param dfCenterLat Latitude of the projection origin.
7262 : * @param dfLat1 Latitude of the first point on center line.
7263 : * @param dfLong1 Longitude of the first point on center line.
7264 : * @param dfLat2 Latitude of the second point on center line.
7265 : * @param dfLong2 Longitude of the second point on center line.
7266 : * @param dfScale Scale factor applies to the projection origin.
7267 : * @param dfFalseEasting False easting.
7268 : * @param dfFalseNorthing False northing.
7269 : *
7270 : * @return OGRERR_NONE on success.
7271 : */
7272 :
7273 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7274 : double dfLong1, double dfLat2,
7275 : double dfLong2, double dfScale,
7276 : double dfFalseEasting,
7277 : double dfFalseNorthing)
7278 :
7279 : {
7280 6 : TAKE_OPTIONAL_LOCK();
7281 :
7282 3 : return d->replaceConversionAndUnref(
7283 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7284 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7285 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7286 6 : 0.0));
7287 : }
7288 :
7289 : /************************************************************************/
7290 : /* OSRSetHOM2PNO() */
7291 : /************************************************************************/
7292 : /**
7293 : * \brief Set a Hotine Oblique Mercator projection using two points on
7294 : * projection centerline.
7295 : *
7296 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7297 : */
7298 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7299 : double dfLat1, double dfLong1, double dfLat2,
7300 : double dfLong2, double dfScale, double dfFalseEasting,
7301 : double dfFalseNorthing)
7302 :
7303 : {
7304 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7305 :
7306 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7307 : dfLong2, dfScale, dfFalseEasting,
7308 0 : dfFalseNorthing);
7309 : }
7310 :
7311 : /************************************************************************/
7312 : /* SetLOM() */
7313 : /************************************************************************/
7314 :
7315 : /**
7316 : * \brief Set a Laborde Oblique Mercator projection.
7317 : *
7318 : * @param dfCenterLat Latitude of the projection origin.
7319 : * @param dfCenterLong Longitude of the projection origin.
7320 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7321 : * centerline.
7322 : * @param dfScale Scale factor on the initial line
7323 : * @param dfFalseEasting False easting.
7324 : * @param dfFalseNorthing False northing.
7325 : *
7326 : * @return OGRERR_NONE on success.
7327 : */
7328 :
7329 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7330 : double dfAzimuth, double dfScale,
7331 : double dfFalseEasting,
7332 : double dfFalseNorthing)
7333 :
7334 : {
7335 0 : TAKE_OPTIONAL_LOCK();
7336 :
7337 0 : return d->replaceConversionAndUnref(
7338 : proj_create_conversion_laborde_oblique_mercator(
7339 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7340 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7341 : }
7342 :
7343 : /************************************************************************/
7344 : /* SetIWMPolyconic() */
7345 : /************************************************************************/
7346 :
7347 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7348 : double dfCenterLong,
7349 : double dfFalseEasting,
7350 : double dfFalseNorthing)
7351 :
7352 : {
7353 0 : TAKE_OPTIONAL_LOCK();
7354 :
7355 0 : return d->replaceConversionAndUnref(
7356 : proj_create_conversion_international_map_world_polyconic(
7357 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7358 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7359 : }
7360 :
7361 : /************************************************************************/
7362 : /* OSRSetIWMPolyconic() */
7363 : /************************************************************************/
7364 :
7365 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7366 : double dfLat2, double dfCenterLong,
7367 : double dfFalseEasting, double dfFalseNorthing)
7368 :
7369 : {
7370 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7371 :
7372 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7373 0 : dfFalseEasting, dfFalseNorthing);
7374 : }
7375 :
7376 : /************************************************************************/
7377 : /* SetKrovak() */
7378 : /************************************************************************/
7379 :
7380 : /** Krovak east-north projection.
7381 : *
7382 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7383 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7384 : */
7385 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7386 : double dfAzimuth,
7387 : double dfPseudoStdParallel1,
7388 : double dfScale, double dfFalseEasting,
7389 : double dfFalseNorthing)
7390 :
7391 : {
7392 6 : TAKE_OPTIONAL_LOCK();
7393 :
7394 3 : return d->replaceConversionAndUnref(
7395 : proj_create_conversion_krovak_north_oriented(
7396 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7397 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7398 6 : nullptr, 0.0, nullptr, 0.0));
7399 : }
7400 :
7401 : /************************************************************************/
7402 : /* OSRSetKrovak() */
7403 : /************************************************************************/
7404 :
7405 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7406 : double dfCenterLong, double dfAzimuth,
7407 : double dfPseudoStdParallel1, double dfScale,
7408 : double dfFalseEasting, double dfFalseNorthing)
7409 :
7410 : {
7411 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7412 :
7413 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7414 : dfPseudoStdParallel1, dfScale,
7415 0 : dfFalseEasting, dfFalseNorthing);
7416 : }
7417 :
7418 : /************************************************************************/
7419 : /* SetLAEA() */
7420 : /************************************************************************/
7421 :
7422 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7423 : double dfFalseEasting,
7424 : double dfFalseNorthing)
7425 :
7426 : {
7427 34 : TAKE_OPTIONAL_LOCK();
7428 :
7429 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7430 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7431 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7432 :
7433 17 : const char *pszName = nullptr;
7434 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7435 17 : CPLString osName = pszName ? pszName : "";
7436 :
7437 17 : d->refreshProjObj();
7438 :
7439 17 : d->demoteFromBoundCRS();
7440 :
7441 17 : auto cs = proj_create_cartesian_2D_cs(
7442 : d->getPROJContext(),
7443 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7444 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7445 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7446 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7447 : : PJ_CART2D_EASTING_NORTHING,
7448 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7449 : auto projCRS =
7450 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7451 17 : d->getGeodBaseCRS(), conv, cs);
7452 17 : proj_destroy(conv);
7453 17 : proj_destroy(cs);
7454 :
7455 17 : d->setPjCRS(projCRS);
7456 :
7457 17 : d->undoDemoteFromBoundCRS();
7458 :
7459 34 : return OGRERR_NONE;
7460 : }
7461 :
7462 : /************************************************************************/
7463 : /* OSRSetLAEA() */
7464 : /************************************************************************/
7465 :
7466 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7467 : double dfCenterLong, double dfFalseEasting,
7468 : double dfFalseNorthing)
7469 :
7470 : {
7471 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7472 :
7473 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7474 0 : dfFalseNorthing);
7475 : }
7476 :
7477 : /************************************************************************/
7478 : /* SetLCC() */
7479 : /************************************************************************/
7480 :
7481 148 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7482 : double dfCenterLat, double dfCenterLong,
7483 : double dfFalseEasting,
7484 : double dfFalseNorthing)
7485 :
7486 : {
7487 296 : TAKE_OPTIONAL_LOCK();
7488 :
7489 148 : return d->replaceConversionAndUnref(
7490 : proj_create_conversion_lambert_conic_conformal_2sp(
7491 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7492 296 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7493 : }
7494 :
7495 : /************************************************************************/
7496 : /* OSRSetLCC() */
7497 : /************************************************************************/
7498 :
7499 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7500 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7501 : double dfFalseNorthing)
7502 :
7503 : {
7504 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7505 :
7506 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7507 1 : dfFalseEasting, dfFalseNorthing);
7508 : }
7509 :
7510 : /************************************************************************/
7511 : /* SetLCC1SP() */
7512 : /************************************************************************/
7513 :
7514 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7515 : double dfScale, double dfFalseEasting,
7516 : double dfFalseNorthing)
7517 :
7518 : {
7519 20 : TAKE_OPTIONAL_LOCK();
7520 :
7521 10 : return d->replaceConversionAndUnref(
7522 : proj_create_conversion_lambert_conic_conformal_1sp(
7523 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7524 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7525 : }
7526 :
7527 : /************************************************************************/
7528 : /* OSRSetLCC1SP() */
7529 : /************************************************************************/
7530 :
7531 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7532 : double dfCenterLong, double dfScale, double dfFalseEasting,
7533 : double dfFalseNorthing)
7534 :
7535 : {
7536 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7537 :
7538 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7539 0 : dfFalseEasting, dfFalseNorthing);
7540 : }
7541 :
7542 : /************************************************************************/
7543 : /* SetLCCB() */
7544 : /************************************************************************/
7545 :
7546 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7547 : double dfCenterLat, double dfCenterLong,
7548 : double dfFalseEasting,
7549 : double dfFalseNorthing)
7550 :
7551 : {
7552 4 : TAKE_OPTIONAL_LOCK();
7553 :
7554 2 : return d->replaceConversionAndUnref(
7555 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7556 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7557 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7558 : }
7559 :
7560 : /************************************************************************/
7561 : /* OSRSetLCCB() */
7562 : /************************************************************************/
7563 :
7564 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7565 : double dfCenterLat, double dfCenterLong,
7566 : double dfFalseEasting, double dfFalseNorthing)
7567 :
7568 : {
7569 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7570 :
7571 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7572 0 : dfFalseEasting, dfFalseNorthing);
7573 : }
7574 :
7575 : /************************************************************************/
7576 : /* SetMC() */
7577 : /************************************************************************/
7578 :
7579 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7580 : double dfFalseEasting, double dfFalseNorthing)
7581 :
7582 : {
7583 8 : TAKE_OPTIONAL_LOCK();
7584 :
7585 : (void)dfCenterLat; // ignored
7586 :
7587 4 : return d->replaceConversionAndUnref(
7588 : proj_create_conversion_miller_cylindrical(
7589 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7590 8 : nullptr, 0, nullptr, 0));
7591 : }
7592 :
7593 : /************************************************************************/
7594 : /* OSRSetMC() */
7595 : /************************************************************************/
7596 :
7597 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7598 : double dfCenterLong, double dfFalseEasting,
7599 : double dfFalseNorthing)
7600 :
7601 : {
7602 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7603 :
7604 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7605 0 : dfFalseNorthing);
7606 : }
7607 :
7608 : /************************************************************************/
7609 : /* SetMercator() */
7610 : /************************************************************************/
7611 :
7612 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7613 : double dfScale, double dfFalseEasting,
7614 : double dfFalseNorthing)
7615 :
7616 : {
7617 118 : TAKE_OPTIONAL_LOCK();
7618 :
7619 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7620 : {
7621 : // Not sure this is correct, but this is how it has been used
7622 : // historically
7623 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7624 0 : dfFalseNorthing);
7625 : }
7626 59 : return d->replaceConversionAndUnref(
7627 : proj_create_conversion_mercator_variant_a(
7628 : d->getPROJContext(),
7629 : dfCenterLat, // should be zero
7630 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7631 59 : nullptr, 0));
7632 : }
7633 :
7634 : /************************************************************************/
7635 : /* OSRSetMercator() */
7636 : /************************************************************************/
7637 :
7638 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7639 : double dfCenterLong, double dfScale,
7640 : double dfFalseEasting, double dfFalseNorthing)
7641 :
7642 : {
7643 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7644 :
7645 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7646 2 : dfFalseEasting, dfFalseNorthing);
7647 : }
7648 :
7649 : /************************************************************************/
7650 : /* SetMercator2SP() */
7651 : /************************************************************************/
7652 :
7653 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7654 : double dfCenterLong,
7655 : double dfFalseEasting,
7656 : double dfFalseNorthing)
7657 :
7658 : {
7659 31 : if (dfCenterLat == 0.0)
7660 : {
7661 30 : return d->replaceConversionAndUnref(
7662 : proj_create_conversion_mercator_variant_b(
7663 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7664 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7665 : }
7666 :
7667 1 : TAKE_OPTIONAL_LOCK();
7668 :
7669 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7670 :
7671 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7672 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7673 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7674 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7675 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7676 :
7677 1 : return OGRERR_NONE;
7678 : }
7679 :
7680 : /************************************************************************/
7681 : /* OSRSetMercator2SP() */
7682 : /************************************************************************/
7683 :
7684 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7685 : double dfCenterLat, double dfCenterLong,
7686 : double dfFalseEasting, double dfFalseNorthing)
7687 :
7688 : {
7689 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7690 :
7691 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7692 1 : dfFalseEasting, dfFalseNorthing);
7693 : }
7694 :
7695 : /************************************************************************/
7696 : /* SetMollweide() */
7697 : /************************************************************************/
7698 :
7699 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7700 : double dfFalseEasting,
7701 : double dfFalseNorthing)
7702 :
7703 : {
7704 6 : TAKE_OPTIONAL_LOCK();
7705 :
7706 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7707 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7708 6 : nullptr, 0, nullptr, 0));
7709 : }
7710 :
7711 : /************************************************************************/
7712 : /* OSRSetMollweide() */
7713 : /************************************************************************/
7714 :
7715 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7716 : double dfFalseEasting, double dfFalseNorthing)
7717 :
7718 : {
7719 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7720 :
7721 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7722 0 : dfFalseNorthing);
7723 : }
7724 :
7725 : /************************************************************************/
7726 : /* SetNZMG() */
7727 : /************************************************************************/
7728 :
7729 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7730 : double dfFalseEasting,
7731 : double dfFalseNorthing)
7732 :
7733 : {
7734 14 : TAKE_OPTIONAL_LOCK();
7735 :
7736 7 : return d->replaceConversionAndUnref(
7737 : proj_create_conversion_new_zealand_mapping_grid(
7738 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7739 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7740 : }
7741 :
7742 : /************************************************************************/
7743 : /* OSRSetNZMG() */
7744 : /************************************************************************/
7745 :
7746 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7747 : double dfCenterLong, double dfFalseEasting,
7748 : double dfFalseNorthing)
7749 :
7750 : {
7751 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7752 :
7753 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7754 0 : dfFalseNorthing);
7755 : }
7756 :
7757 : /************************************************************************/
7758 : /* SetOS() */
7759 : /************************************************************************/
7760 :
7761 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7762 : double dfScale, double dfFalseEasting,
7763 : double dfFalseNorthing)
7764 :
7765 : {
7766 12 : TAKE_OPTIONAL_LOCK();
7767 :
7768 6 : return d->replaceConversionAndUnref(
7769 : proj_create_conversion_oblique_stereographic(
7770 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7771 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7772 : }
7773 :
7774 : /************************************************************************/
7775 : /* OSRSetOS() */
7776 : /************************************************************************/
7777 :
7778 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7779 : double dfCMeridian, double dfScale, double dfFalseEasting,
7780 : double dfFalseNorthing)
7781 :
7782 : {
7783 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7784 :
7785 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7786 0 : dfFalseEasting, dfFalseNorthing);
7787 : }
7788 :
7789 : /************************************************************************/
7790 : /* SetOrthographic() */
7791 : /************************************************************************/
7792 :
7793 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7794 : double dfCenterLong,
7795 : double dfFalseEasting,
7796 : double dfFalseNorthing)
7797 :
7798 : {
7799 14 : TAKE_OPTIONAL_LOCK();
7800 :
7801 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7802 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7803 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7804 : }
7805 :
7806 : /************************************************************************/
7807 : /* OSRSetOrthographic() */
7808 : /************************************************************************/
7809 :
7810 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7811 : double dfCenterLong, double dfFalseEasting,
7812 : double dfFalseNorthing)
7813 :
7814 : {
7815 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7816 :
7817 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7818 1 : dfFalseEasting, dfFalseNorthing);
7819 : }
7820 :
7821 : /************************************************************************/
7822 : /* SetPolyconic() */
7823 : /************************************************************************/
7824 :
7825 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7826 : double dfCenterLong,
7827 : double dfFalseEasting,
7828 : double dfFalseNorthing)
7829 :
7830 : {
7831 14 : TAKE_OPTIONAL_LOCK();
7832 :
7833 : // note: it seems that by some definitions this should include a
7834 : // scale_factor parameter.
7835 7 : return d->replaceConversionAndUnref(
7836 : proj_create_conversion_american_polyconic(
7837 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7838 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7839 : }
7840 :
7841 : /************************************************************************/
7842 : /* OSRSetPolyconic() */
7843 : /************************************************************************/
7844 :
7845 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7846 : double dfCenterLong, double dfFalseEasting,
7847 : double dfFalseNorthing)
7848 :
7849 : {
7850 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7851 :
7852 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7853 0 : dfFalseEasting, dfFalseNorthing);
7854 : }
7855 :
7856 : /************************************************************************/
7857 : /* SetPS() */
7858 : /************************************************************************/
7859 :
7860 : /** Sets a Polar Stereographic projection.
7861 : *
7862 : * Two variants are possible:
7863 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7864 : * interpreted as the latitude of origin, combined with the scale factor
7865 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7866 : * is interpreted as the latitude of true scale. In that situation, dfScale
7867 : * must be set to 1 (it is ignored in the projection parameters)
7868 : */
7869 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7870 : double dfScale, double dfFalseEasting,
7871 : double dfFalseNorthing)
7872 :
7873 : {
7874 60 : TAKE_OPTIONAL_LOCK();
7875 :
7876 : PJ *conv;
7877 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7878 : {
7879 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7880 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7881 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7882 : }
7883 : else
7884 : {
7885 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7886 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7887 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7888 : }
7889 :
7890 30 : const char *pszName = nullptr;
7891 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7892 30 : CPLString osName = pszName ? pszName : "";
7893 :
7894 30 : d->refreshProjObj();
7895 :
7896 30 : d->demoteFromBoundCRS();
7897 :
7898 30 : auto cs = proj_create_cartesian_2D_cs(
7899 : d->getPROJContext(),
7900 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7901 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7902 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7903 : auto projCRS =
7904 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7905 30 : d->getGeodBaseCRS(), conv, cs);
7906 30 : proj_destroy(conv);
7907 30 : proj_destroy(cs);
7908 :
7909 30 : d->setPjCRS(projCRS);
7910 :
7911 30 : d->undoDemoteFromBoundCRS();
7912 :
7913 60 : return OGRERR_NONE;
7914 : }
7915 :
7916 : /************************************************************************/
7917 : /* OSRSetPS() */
7918 : /************************************************************************/
7919 :
7920 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7921 : double dfCenterLong, double dfScale, double dfFalseEasting,
7922 : double dfFalseNorthing)
7923 :
7924 : {
7925 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7926 :
7927 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7928 1 : dfFalseEasting, dfFalseNorthing);
7929 : }
7930 :
7931 : /************************************************************************/
7932 : /* SetRobinson() */
7933 : /************************************************************************/
7934 :
7935 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7936 : double dfFalseEasting,
7937 : double dfFalseNorthing)
7938 :
7939 : {
7940 8 : TAKE_OPTIONAL_LOCK();
7941 :
7942 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7943 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7944 8 : nullptr, 0, nullptr, 0));
7945 : }
7946 :
7947 : /************************************************************************/
7948 : /* OSRSetRobinson() */
7949 : /************************************************************************/
7950 :
7951 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7952 : double dfFalseEasting, double dfFalseNorthing)
7953 :
7954 : {
7955 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7956 :
7957 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7958 0 : dfFalseNorthing);
7959 : }
7960 :
7961 : /************************************************************************/
7962 : /* SetSinusoidal() */
7963 : /************************************************************************/
7964 :
7965 36 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7966 : double dfFalseEasting,
7967 : double dfFalseNorthing)
7968 :
7969 : {
7970 72 : TAKE_OPTIONAL_LOCK();
7971 :
7972 36 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7973 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7974 72 : nullptr, 0, nullptr, 0));
7975 : }
7976 :
7977 : /************************************************************************/
7978 : /* OSRSetSinusoidal() */
7979 : /************************************************************************/
7980 :
7981 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7982 : double dfFalseEasting, double dfFalseNorthing)
7983 :
7984 : {
7985 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7986 :
7987 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7988 1 : dfFalseNorthing);
7989 : }
7990 :
7991 : /************************************************************************/
7992 : /* SetStereographic() */
7993 : /************************************************************************/
7994 :
7995 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7996 : double dfCMeridian, double dfScale,
7997 : double dfFalseEasting,
7998 : double dfFalseNorthing)
7999 :
8000 : {
8001 4 : TAKE_OPTIONAL_LOCK();
8002 :
8003 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
8004 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
8005 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8006 : }
8007 :
8008 : /************************************************************************/
8009 : /* OSRSetStereographic() */
8010 : /************************************************************************/
8011 :
8012 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8013 : double dfCMeridian, double dfScale,
8014 : double dfFalseEasting, double dfFalseNorthing)
8015 :
8016 : {
8017 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8018 :
8019 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8020 0 : dfFalseEasting, dfFalseNorthing);
8021 : }
8022 :
8023 : /************************************************************************/
8024 : /* SetSOC() */
8025 : /* */
8026 : /* NOTE: This definition isn't really used in practice any more */
8027 : /* and should be considered deprecated. It seems that swiss */
8028 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
8029 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
8030 : /* EPSG:2056 and Bug 423. */
8031 : /************************************************************************/
8032 :
8033 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8034 : double dfCentralMeridian,
8035 : double dfFalseEasting,
8036 : double dfFalseNorthing)
8037 :
8038 : {
8039 4 : TAKE_OPTIONAL_LOCK();
8040 :
8041 2 : return d->replaceConversionAndUnref(
8042 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8043 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8044 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8045 4 : 0.0));
8046 : #if 0
8047 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8048 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8049 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8050 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8051 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8052 :
8053 : return OGRERR_NONE;
8054 : #endif
8055 : }
8056 :
8057 : /************************************************************************/
8058 : /* OSRSetSOC() */
8059 : /************************************************************************/
8060 :
8061 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8062 : double dfCentralMeridian, double dfFalseEasting,
8063 : double dfFalseNorthing)
8064 :
8065 : {
8066 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8067 :
8068 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8069 0 : dfFalseEasting, dfFalseNorthing);
8070 : }
8071 :
8072 : /************************************************************************/
8073 : /* SetVDG() */
8074 : /************************************************************************/
8075 :
8076 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8077 : double dfFalseNorthing)
8078 :
8079 : {
8080 4 : TAKE_OPTIONAL_LOCK();
8081 :
8082 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8083 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8084 4 : nullptr, 0, nullptr, 0));
8085 : }
8086 :
8087 : /************************************************************************/
8088 : /* OSRSetVDG() */
8089 : /************************************************************************/
8090 :
8091 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8092 : double dfFalseEasting, double dfFalseNorthing)
8093 :
8094 : {
8095 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8096 :
8097 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8098 0 : dfFalseNorthing);
8099 : }
8100 :
8101 : /************************************************************************/
8102 : /* SetUTM() */
8103 : /************************************************************************/
8104 :
8105 : /**
8106 : * \brief Set UTM projection definition.
8107 : *
8108 : * This will generate a projection definition with the full set of
8109 : * transverse mercator projection parameters for the given UTM zone.
8110 : * If no PROJCS[] description is set yet, one will be set to look
8111 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8112 : *
8113 : * This method is the same as the C function OSRSetUTM().
8114 : *
8115 : * @param nZone UTM zone.
8116 : *
8117 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8118 : * hemisphere.
8119 : *
8120 : * @return OGRERR_NONE on success.
8121 : */
8122 :
8123 314 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8124 :
8125 : {
8126 628 : TAKE_OPTIONAL_LOCK();
8127 :
8128 314 : if (nZone < 0 || nZone > 60)
8129 : {
8130 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8131 0 : return OGRERR_FAILURE;
8132 : }
8133 :
8134 314 : return d->replaceConversionAndUnref(
8135 314 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8136 : }
8137 :
8138 : /************************************************************************/
8139 : /* OSRSetUTM() */
8140 : /************************************************************************/
8141 :
8142 : /**
8143 : * \brief Set UTM projection definition.
8144 : *
8145 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8146 : */
8147 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8148 :
8149 : {
8150 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8151 :
8152 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8153 : }
8154 :
8155 : /************************************************************************/
8156 : /* GetUTMZone() */
8157 : /* */
8158 : /* Returns zero if it isn't UTM. */
8159 : /************************************************************************/
8160 :
8161 : /**
8162 : * \brief Get utm zone information.
8163 : *
8164 : * This is the same as the C function OSRGetUTMZone().
8165 : *
8166 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8167 : * zone which is negative in the southern hemisphere instead of having the
8168 : * pbNorth flag used in the C and C++ interface.
8169 : *
8170 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8171 : * FALSE if southern.
8172 : *
8173 : * @return UTM zone number or zero if this isn't a UTM definition.
8174 : */
8175 :
8176 611 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8177 :
8178 : {
8179 1222 : TAKE_OPTIONAL_LOCK();
8180 :
8181 611 : if (IsProjected() && GetAxesCount() == 3)
8182 : {
8183 1 : OGRSpatialReference *poSRSTmp = Clone();
8184 1 : poSRSTmp->DemoteTo2D(nullptr);
8185 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8186 1 : delete poSRSTmp;
8187 1 : return nZone;
8188 : }
8189 :
8190 610 : const char *pszProjection = GetAttrValue("PROJECTION");
8191 :
8192 610 : if (pszProjection == nullptr ||
8193 530 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8194 278 : return 0;
8195 :
8196 332 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8197 5 : return 0;
8198 :
8199 327 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8200 15 : return 0;
8201 :
8202 312 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8203 6 : return 0;
8204 :
8205 306 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8206 :
8207 306 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8208 0 : return 0;
8209 :
8210 306 : if (pbNorth != nullptr)
8211 240 : *pbNorth = (dfFalseNorthing == 0);
8212 :
8213 : const double dfCentralMeridian =
8214 306 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8215 306 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8216 :
8217 612 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8218 918 : std::isnan(dfZone) ||
8219 306 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8220 0 : return 0;
8221 :
8222 306 : return static_cast<int>(dfZone);
8223 : }
8224 :
8225 : /************************************************************************/
8226 : /* OSRGetUTMZone() */
8227 : /************************************************************************/
8228 :
8229 : /**
8230 : * \brief Get utm zone information.
8231 : *
8232 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8233 : */
8234 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8235 :
8236 : {
8237 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8238 :
8239 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8240 : }
8241 :
8242 : /************************************************************************/
8243 : /* SetWagner() */
8244 : /************************************************************************/
8245 :
8246 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8247 : double dfCenterLat, double dfFalseEasting,
8248 : double dfFalseNorthing)
8249 :
8250 : {
8251 0 : TAKE_OPTIONAL_LOCK();
8252 :
8253 : PJ *conv;
8254 0 : if (nVariation == 1)
8255 : {
8256 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8257 : dfFalseEasting, dfFalseNorthing,
8258 : nullptr, 0.0, nullptr, 0.0);
8259 : }
8260 0 : else if (nVariation == 2)
8261 : {
8262 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8263 : dfFalseEasting, dfFalseNorthing,
8264 : nullptr, 0.0, nullptr, 0.0);
8265 : }
8266 0 : else if (nVariation == 3)
8267 : {
8268 0 : conv = proj_create_conversion_wagner_iii(
8269 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8270 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8271 : }
8272 0 : else if (nVariation == 4)
8273 : {
8274 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8275 : dfFalseEasting, dfFalseNorthing,
8276 : nullptr, 0.0, nullptr, 0.0);
8277 : }
8278 0 : else if (nVariation == 5)
8279 : {
8280 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8281 : dfFalseEasting, dfFalseNorthing,
8282 : nullptr, 0.0, nullptr, 0.0);
8283 : }
8284 0 : else if (nVariation == 6)
8285 : {
8286 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8287 : dfFalseEasting, dfFalseNorthing,
8288 : nullptr, 0.0, nullptr, 0.0);
8289 : }
8290 0 : else if (nVariation == 7)
8291 : {
8292 0 : conv = proj_create_conversion_wagner_vii(
8293 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8294 : 0.0, nullptr, 0.0);
8295 : }
8296 : else
8297 : {
8298 0 : CPLError(CE_Failure, CPLE_AppDefined,
8299 : "Unsupported Wagner variation (%d).", nVariation);
8300 0 : return OGRERR_UNSUPPORTED_SRS;
8301 : }
8302 :
8303 0 : return d->replaceConversionAndUnref(conv);
8304 : }
8305 :
8306 : /************************************************************************/
8307 : /* OSRSetWagner() */
8308 : /************************************************************************/
8309 :
8310 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8311 : double dfCenterLat, double dfFalseEasting,
8312 : double dfFalseNorthing)
8313 :
8314 : {
8315 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8316 :
8317 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8318 0 : dfFalseNorthing);
8319 : }
8320 :
8321 : /************************************************************************/
8322 : /* SetQSC() */
8323 : /************************************************************************/
8324 :
8325 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8326 : {
8327 0 : TAKE_OPTIONAL_LOCK();
8328 :
8329 0 : return d->replaceConversionAndUnref(
8330 : proj_create_conversion_quadrilateralized_spherical_cube(
8331 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8332 0 : 0, nullptr, 0));
8333 : }
8334 :
8335 : /************************************************************************/
8336 : /* OSRSetQSC() */
8337 : /************************************************************************/
8338 :
8339 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8340 : double dfCenterLong)
8341 :
8342 : {
8343 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8344 :
8345 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8346 : }
8347 :
8348 : /************************************************************************/
8349 : /* SetSCH() */
8350 : /************************************************************************/
8351 :
8352 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8353 : double dfPegHeading, double dfPegHgt)
8354 :
8355 : {
8356 0 : TAKE_OPTIONAL_LOCK();
8357 :
8358 0 : return d->replaceConversionAndUnref(
8359 : proj_create_conversion_spherical_cross_track_height(
8360 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8361 0 : nullptr, 0, nullptr, 0));
8362 : }
8363 :
8364 : /************************************************************************/
8365 : /* OSRSetSCH() */
8366 : /************************************************************************/
8367 :
8368 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8369 : double dfPegHeading, double dfPegHgt)
8370 :
8371 : {
8372 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8373 :
8374 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8375 : }
8376 :
8377 : /************************************************************************/
8378 : /* SetVerticalPerspective() */
8379 : /************************************************************************/
8380 :
8381 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8382 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8383 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8384 : {
8385 6 : TAKE_OPTIONAL_LOCK();
8386 :
8387 3 : return d->replaceConversionAndUnref(
8388 : proj_create_conversion_vertical_perspective(
8389 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8390 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8391 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8392 : }
8393 :
8394 : /************************************************************************/
8395 : /* OSRSetVerticalPerspective() */
8396 : /************************************************************************/
8397 :
8398 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8399 : double dfTopoOriginLat, double dfTopoOriginLon,
8400 : double dfTopoOriginHeight,
8401 : double dfViewPointHeight,
8402 : double dfFalseEasting, double dfFalseNorthing)
8403 :
8404 : {
8405 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8406 :
8407 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8408 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8409 1 : dfFalseEasting, dfFalseNorthing);
8410 : }
8411 :
8412 : /************************************************************************/
8413 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8414 : /************************************************************************/
8415 :
8416 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8417 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8418 : double dfAxisRotation)
8419 : {
8420 4 : TAKE_OPTIONAL_LOCK();
8421 :
8422 2 : d->refreshProjObj();
8423 2 : if (!d->m_pj_crs)
8424 0 : return OGRERR_FAILURE;
8425 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8426 0 : return OGRERR_FAILURE;
8427 2 : auto ctxt = d->getPROJContext();
8428 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8429 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8430 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8431 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8432 2 : d->m_pj_crs, conv, cs));
8433 2 : proj_destroy(conv);
8434 2 : proj_destroy(cs);
8435 2 : return OGRERR_NONE;
8436 : }
8437 :
8438 : /************************************************************************/
8439 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8440 : /************************************************************************/
8441 :
8442 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8443 : const char *pszCRSName, double dfGridNorthPoleLat,
8444 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8445 : {
8446 3 : TAKE_OPTIONAL_LOCK();
8447 :
8448 : #if PROJ_VERSION_MAJOR > 8 || \
8449 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8450 : d->refreshProjObj();
8451 : if (!d->m_pj_crs)
8452 : return OGRERR_FAILURE;
8453 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8454 : return OGRERR_FAILURE;
8455 : auto ctxt = d->getPROJContext();
8456 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8457 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8458 : nullptr, 0);
8459 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8460 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8461 : d->m_pj_crs, conv, cs));
8462 : proj_destroy(conv);
8463 : proj_destroy(cs);
8464 : return OGRERR_NONE;
8465 : #else
8466 : (void)pszCRSName;
8467 3 : SetProjection("Rotated_pole");
8468 3 : SetExtension(
8469 : "PROJCS", "PROJ4",
8470 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8471 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8472 : "+to_meter=0.0174532925199433 "
8473 : "+wktext",
8474 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8475 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8476 : GetSemiMinor(nullptr)));
8477 6 : return OGRERR_NONE;
8478 : #endif
8479 : }
8480 :
8481 : /************************************************************************/
8482 : /* SetAuthority() */
8483 : /************************************************************************/
8484 :
8485 : /**
8486 : * \brief Set the authority for a node.
8487 : *
8488 : * This method is the same as the C function OSRSetAuthority().
8489 : *
8490 : * @param pszTargetKey the partial or complete path to the node to
8491 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8492 : *
8493 : * @param pszAuthority authority name, such as "EPSG".
8494 : *
8495 : * @param nCode code for value with this authority.
8496 : *
8497 : * @return OGRERR_NONE on success.
8498 : */
8499 :
8500 12690 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8501 : const char *pszAuthority, int nCode)
8502 :
8503 : {
8504 25380 : TAKE_OPTIONAL_LOCK();
8505 :
8506 12690 : d->refreshProjObj();
8507 12690 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8508 :
8509 12690 : if (pszTargetKey == nullptr)
8510 : {
8511 265 : if (!d->m_pj_crs)
8512 0 : return OGRERR_FAILURE;
8513 265 : CPLString osCode;
8514 265 : osCode.Printf("%d", nCode);
8515 265 : d->demoteFromBoundCRS();
8516 265 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8517 : pszAuthority, osCode.c_str()));
8518 265 : d->undoDemoteFromBoundCRS();
8519 265 : return OGRERR_NONE;
8520 : }
8521 :
8522 12425 : d->demoteFromBoundCRS();
8523 12425 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8524 : {
8525 4109 : CPLString osCode;
8526 4109 : osCode.Printf("%d", nCode);
8527 : auto newGeogCRS =
8528 4109 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8529 : pszAuthority, osCode.c_str());
8530 :
8531 : auto conv =
8532 4109 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8533 :
8534 4109 : auto projCRS = proj_create_projected_crs(
8535 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8536 4109 : d->getProjCRSCoordSys());
8537 :
8538 : // Preserve existing id on the PROJCRS
8539 4109 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8540 4109 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8541 4109 : if (pszProjCRSAuthName && pszProjCRSCode)
8542 : {
8543 : auto projCRSWithId =
8544 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8545 : pszProjCRSCode);
8546 0 : proj_destroy(projCRS);
8547 0 : projCRS = projCRSWithId;
8548 : }
8549 :
8550 4109 : proj_destroy(newGeogCRS);
8551 4109 : proj_destroy(conv);
8552 :
8553 4109 : d->setPjCRS(projCRS);
8554 4109 : d->undoDemoteFromBoundCRS();
8555 4109 : return OGRERR_NONE;
8556 : }
8557 8316 : d->undoDemoteFromBoundCRS();
8558 :
8559 : /* -------------------------------------------------------------------- */
8560 : /* Find the node below which the authority should be put. */
8561 : /* -------------------------------------------------------------------- */
8562 8316 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8563 :
8564 8316 : if (poNode == nullptr)
8565 0 : return OGRERR_FAILURE;
8566 :
8567 : /* -------------------------------------------------------------------- */
8568 : /* If there is an existing AUTHORITY child blow it away before */
8569 : /* trying to set a new one. */
8570 : /* -------------------------------------------------------------------- */
8571 8316 : int iOldChild = poNode->FindChild("AUTHORITY");
8572 8316 : if (iOldChild != -1)
8573 5 : poNode->DestroyChild(iOldChild);
8574 :
8575 : /* -------------------------------------------------------------------- */
8576 : /* Create a new authority node. */
8577 : /* -------------------------------------------------------------------- */
8578 8316 : char szCode[32] = {};
8579 :
8580 8316 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8581 :
8582 8316 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8583 8316 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8584 8316 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8585 :
8586 8316 : poNode->AddChild(poAuthNode);
8587 :
8588 8316 : return OGRERR_NONE;
8589 : }
8590 :
8591 : /************************************************************************/
8592 : /* OSRSetAuthority() */
8593 : /************************************************************************/
8594 :
8595 : /**
8596 : * \brief Set the authority for a node.
8597 : *
8598 : * This function is the same as OGRSpatialReference::SetAuthority().
8599 : */
8600 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8601 : const char *pszAuthority, int nCode)
8602 :
8603 : {
8604 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8605 :
8606 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8607 : }
8608 :
8609 : /************************************************************************/
8610 : /* GetAuthorityCode() */
8611 : /************************************************************************/
8612 :
8613 : /**
8614 : * \brief Get the authority code for a node.
8615 : *
8616 : * This method is used to query an AUTHORITY[] node from within the
8617 : * WKT tree, and fetch the code value.
8618 : *
8619 : * While in theory values may be non-numeric, for the EPSG authority all
8620 : * code values should be integral.
8621 : *
8622 : * This method is the same as the C function OSRGetAuthorityCode().
8623 : *
8624 : * @param pszTargetKey the partial or complete path to the node to
8625 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8626 : * search for an authority node on the root element.
8627 : *
8628 : * @return value code from authority node, or NULL on failure. The value
8629 : * returned is internal and should not be freed or modified.
8630 : */
8631 :
8632 : const char *
8633 50821 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8634 :
8635 : {
8636 101642 : TAKE_OPTIONAL_LOCK();
8637 :
8638 50821 : d->refreshProjObj();
8639 50821 : const char *pszInputTargetKey = pszTargetKey;
8640 50821 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8641 50821 : if (pszTargetKey == nullptr)
8642 : {
8643 42506 : if (!d->m_pj_crs)
8644 : {
8645 17 : return nullptr;
8646 : }
8647 42489 : d->demoteFromBoundCRS();
8648 42489 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8649 42489 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8650 : {
8651 1389 : auto ctxt = d->getPROJContext();
8652 1389 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8653 1389 : if (cs)
8654 : {
8655 1389 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8656 1389 : proj_destroy(cs);
8657 1389 : if (axisCount == 3)
8658 : {
8659 : // This might come from a COMPD_CS with a VERT_DATUM type =
8660 : // 2002 in which case, using the WKT1 representation will
8661 : // enable us to recover the EPSG code.
8662 14 : pszTargetKey = pszInputTargetKey;
8663 : }
8664 : }
8665 : }
8666 42489 : d->undoDemoteFromBoundCRS();
8667 42489 : if (ret != nullptr || pszTargetKey == nullptr)
8668 : {
8669 42489 : return ret;
8670 : }
8671 : }
8672 :
8673 : // Special key for that context
8674 8319 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8675 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8676 : {
8677 4 : auto ctxt = d->getPROJContext();
8678 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8679 4 : if (crs)
8680 : {
8681 4 : const char *ret = proj_get_id_code(crs, 0);
8682 4 : if (ret)
8683 4 : ret = CPLSPrintf("%s", ret);
8684 4 : proj_destroy(crs);
8685 4 : return ret;
8686 : }
8687 : }
8688 8315 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8689 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8690 : {
8691 4 : auto ctxt = d->getPROJContext();
8692 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8693 4 : if (crs)
8694 : {
8695 4 : const char *ret = proj_get_id_code(crs, 0);
8696 4 : if (ret)
8697 4 : ret = CPLSPrintf("%s", ret);
8698 4 : proj_destroy(crs);
8699 4 : return ret;
8700 : }
8701 : }
8702 :
8703 : /* -------------------------------------------------------------------- */
8704 : /* Find the node below which the authority should be put. */
8705 : /* -------------------------------------------------------------------- */
8706 8307 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8707 :
8708 8307 : if (poNode == nullptr)
8709 106 : return nullptr;
8710 :
8711 : /* -------------------------------------------------------------------- */
8712 : /* Fetch AUTHORITY child if there is one. */
8713 : /* -------------------------------------------------------------------- */
8714 8201 : if (poNode->FindChild("AUTHORITY") == -1)
8715 197 : return nullptr;
8716 :
8717 8004 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8718 :
8719 : /* -------------------------------------------------------------------- */
8720 : /* Create a new authority node. */
8721 : /* -------------------------------------------------------------------- */
8722 8004 : if (poNode->GetChildCount() < 2)
8723 0 : return nullptr;
8724 :
8725 8004 : return poNode->GetChild(1)->GetValue();
8726 : }
8727 :
8728 : /************************************************************************/
8729 : /* OSRGetAuthorityCode() */
8730 : /************************************************************************/
8731 :
8732 : /**
8733 : * \brief Get the authority code for a node.
8734 : *
8735 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8736 : */
8737 799 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8738 : const char *pszTargetKey)
8739 :
8740 : {
8741 799 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8742 :
8743 799 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8744 : }
8745 :
8746 : /************************************************************************/
8747 : /* GetAuthorityName() */
8748 : /************************************************************************/
8749 :
8750 : /**
8751 : * \brief Get the authority name for a node.
8752 : *
8753 : * This method is used to query an AUTHORITY[] node from within the
8754 : * WKT tree, and fetch the authority name value.
8755 : *
8756 : * The most common authority is "EPSG".
8757 : *
8758 : * This method is the same as the C function OSRGetAuthorityName().
8759 : *
8760 : * @param pszTargetKey the partial or complete path to the node to
8761 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8762 : * search for an authority node on the root element.
8763 : *
8764 : * @return value code from authority node, or NULL on failure. The value
8765 : * returned is internal and should not be freed or modified.
8766 : */
8767 :
8768 : const char *
8769 64550 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8770 :
8771 : {
8772 129100 : TAKE_OPTIONAL_LOCK();
8773 :
8774 64550 : d->refreshProjObj();
8775 64550 : const char *pszInputTargetKey = pszTargetKey;
8776 64550 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8777 64550 : if (pszTargetKey == nullptr)
8778 : {
8779 35640 : if (!d->m_pj_crs)
8780 : {
8781 18 : return nullptr;
8782 : }
8783 35622 : d->demoteFromBoundCRS();
8784 35622 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8785 35622 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8786 : {
8787 1011 : auto ctxt = d->getPROJContext();
8788 1011 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8789 1011 : if (cs)
8790 : {
8791 1011 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8792 1011 : proj_destroy(cs);
8793 1011 : if (axisCount == 3)
8794 : {
8795 : // This might come from a COMPD_CS with a VERT_DATUM type =
8796 : // 2002 in which case, using the WKT1 representation will
8797 : // enable us to recover the EPSG code.
8798 14 : pszTargetKey = pszInputTargetKey;
8799 : }
8800 : }
8801 : }
8802 35622 : d->undoDemoteFromBoundCRS();
8803 35622 : if (ret != nullptr || pszTargetKey == nullptr)
8804 : {
8805 35622 : return ret;
8806 : }
8807 : }
8808 :
8809 : // Special key for that context
8810 28914 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8811 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8812 : {
8813 4 : auto ctxt = d->getPROJContext();
8814 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8815 4 : if (crs)
8816 : {
8817 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8818 4 : if (ret)
8819 4 : ret = CPLSPrintf("%s", ret);
8820 4 : proj_destroy(crs);
8821 4 : return ret;
8822 : }
8823 : }
8824 28910 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8825 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8826 : {
8827 4 : auto ctxt = d->getPROJContext();
8828 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8829 4 : if (crs)
8830 : {
8831 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8832 4 : if (ret)
8833 4 : ret = CPLSPrintf("%s", ret);
8834 4 : proj_destroy(crs);
8835 4 : return ret;
8836 : }
8837 : }
8838 :
8839 : /* -------------------------------------------------------------------- */
8840 : /* Find the node below which the authority should be put. */
8841 : /* -------------------------------------------------------------------- */
8842 28902 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8843 :
8844 28902 : if (poNode == nullptr)
8845 11910 : return nullptr;
8846 :
8847 : /* -------------------------------------------------------------------- */
8848 : /* Fetch AUTHORITY child if there is one. */
8849 : /* -------------------------------------------------------------------- */
8850 16992 : if (poNode->FindChild("AUTHORITY") == -1)
8851 1595 : return nullptr;
8852 :
8853 15397 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8854 :
8855 : /* -------------------------------------------------------------------- */
8856 : /* Create a new authority node. */
8857 : /* -------------------------------------------------------------------- */
8858 15397 : if (poNode->GetChildCount() < 2)
8859 0 : return nullptr;
8860 :
8861 15397 : return poNode->GetChild(0)->GetValue();
8862 : }
8863 :
8864 : /************************************************************************/
8865 : /* OSRGetAuthorityName() */
8866 : /************************************************************************/
8867 :
8868 : /**
8869 : * \brief Get the authority name for a node.
8870 : *
8871 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8872 : */
8873 94 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8874 : const char *pszTargetKey)
8875 :
8876 : {
8877 94 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8878 :
8879 94 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8880 : }
8881 :
8882 : /************************************************************************/
8883 : /* GetOGCURN() */
8884 : /************************************************************************/
8885 :
8886 : /**
8887 : * \brief Get a OGC URN string describing the CRS, when possible
8888 : *
8889 : * This method assumes that the CRS has a top-level identifier, or is
8890 : * a compound CRS whose horizontal and vertical parts have a top-level
8891 : * identifier.
8892 : *
8893 : * @return a string to free with CPLFree(), or nullptr when no result can be
8894 : * generated
8895 : *
8896 : * @since GDAL 3.5
8897 : */
8898 :
8899 62 : char *OGRSpatialReference::GetOGCURN() const
8900 :
8901 : {
8902 124 : TAKE_OPTIONAL_LOCK();
8903 :
8904 62 : const char *pszAuthName = GetAuthorityName(nullptr);
8905 62 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8906 62 : if (pszAuthName && pszAuthCode)
8907 59 : return CPLStrdup(
8908 59 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8909 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8910 2 : return nullptr;
8911 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8912 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8913 1 : char *pszRet = nullptr;
8914 1 : if (horizCRS && vertCRS)
8915 : {
8916 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8917 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8918 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8919 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8920 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8921 : {
8922 1 : pszRet = CPLStrdup(CPLSPrintf(
8923 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8924 : horizAuthCode, vertAuthName, vertAuthCode));
8925 : }
8926 : }
8927 1 : proj_destroy(horizCRS);
8928 1 : proj_destroy(vertCRS);
8929 1 : return pszRet;
8930 : }
8931 :
8932 : /************************************************************************/
8933 : /* StripVertical() */
8934 : /************************************************************************/
8935 :
8936 : /**
8937 : * \brief Convert a compound cs into a horizontal CS.
8938 : *
8939 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8940 : * nodes are stripped resulting and only the horizontal coordinate system
8941 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8942 : *
8943 : * If this is not a compound coordinate system then nothing is changed.
8944 : *
8945 : * This method is the same as the C function OSRStripVertical().
8946 : *
8947 : */
8948 :
8949 46 : OGRErr OGRSpatialReference::StripVertical()
8950 :
8951 : {
8952 92 : TAKE_OPTIONAL_LOCK();
8953 :
8954 46 : d->refreshProjObj();
8955 46 : d->demoteFromBoundCRS();
8956 46 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8957 : {
8958 0 : d->undoDemoteFromBoundCRS();
8959 0 : return OGRERR_NONE;
8960 : }
8961 46 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8962 46 : if (!horizCRS)
8963 : {
8964 0 : d->undoDemoteFromBoundCRS();
8965 0 : return OGRERR_FAILURE;
8966 : }
8967 :
8968 46 : bool reuseExistingBoundCRS = false;
8969 46 : if (d->m_pj_bound_crs_target)
8970 : {
8971 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8972 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8973 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8974 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8975 : }
8976 :
8977 46 : if (reuseExistingBoundCRS)
8978 : {
8979 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8980 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8981 4 : d->m_pj_bound_crs_co);
8982 4 : proj_destroy(horizCRS);
8983 4 : d->undoDemoteFromBoundCRS();
8984 4 : d->setPjCRS(newBoundCRS);
8985 : }
8986 : else
8987 : {
8988 42 : d->undoDemoteFromBoundCRS();
8989 42 : d->setPjCRS(horizCRS);
8990 : }
8991 :
8992 46 : return OGRERR_NONE;
8993 : }
8994 :
8995 : /************************************************************************/
8996 : /* OSRStripVertical() */
8997 : /************************************************************************/
8998 : /**
8999 : * \brief Convert a compound cs into a horizontal CS.
9000 : *
9001 : * This function is the same as the C++ method
9002 : * OGRSpatialReference::StripVertical().
9003 : */
9004 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
9005 :
9006 : {
9007 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9008 :
9009 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9010 : }
9011 :
9012 : /************************************************************************/
9013 : /* StripTOWGS84IfKnownDatumAndAllowed() */
9014 : /************************************************************************/
9015 :
9016 : /**
9017 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9018 : * and this is allowed by the user.
9019 : *
9020 : * The default behavior is to remove TOWGS84 information if the CRS has a
9021 : * known horizontal datum. This can be disabled by setting the
9022 : * OSR_STRIP_TOWGS84 configuration option to NO.
9023 : *
9024 : * @return true if TOWGS84 has been removed.
9025 : * @since OGR 3.1.0
9026 : */
9027 :
9028 9514 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9029 : {
9030 9514 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9031 : {
9032 9511 : if (StripTOWGS84IfKnownDatum())
9033 : {
9034 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9035 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9036 : "configuration option to NO");
9037 72 : return true;
9038 : }
9039 : }
9040 9442 : return false;
9041 : }
9042 :
9043 : /************************************************************************/
9044 : /* StripTOWGS84IfKnownDatum() */
9045 : /************************************************************************/
9046 :
9047 : /**
9048 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9049 : *
9050 : * @return true if TOWGS84 has been removed.
9051 : * @since OGR 3.1.0
9052 : */
9053 :
9054 9517 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9055 :
9056 : {
9057 19034 : TAKE_OPTIONAL_LOCK();
9058 :
9059 9517 : d->refreshProjObj();
9060 9517 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9061 : {
9062 9425 : return false;
9063 : }
9064 92 : auto ctxt = d->getPROJContext();
9065 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9066 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9067 : {
9068 3 : proj_destroy(baseCRS);
9069 3 : return false;
9070 : }
9071 :
9072 : // Known base CRS code ? Return base CRS
9073 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9074 89 : if (pszCode)
9075 : {
9076 2 : d->setPjCRS(baseCRS);
9077 2 : return true;
9078 : }
9079 :
9080 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9081 : #if PROJ_VERSION_MAJOR > 7 || \
9082 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9083 : if (datum == nullptr)
9084 : {
9085 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9086 : }
9087 : #endif
9088 87 : if (!datum)
9089 : {
9090 0 : proj_destroy(baseCRS);
9091 0 : return false;
9092 : }
9093 :
9094 : // Known datum code ? Return base CRS
9095 87 : pszCode = proj_get_id_code(datum, 0);
9096 87 : if (pszCode)
9097 : {
9098 3 : proj_destroy(datum);
9099 3 : d->setPjCRS(baseCRS);
9100 3 : return true;
9101 : }
9102 :
9103 84 : const char *name = proj_get_name(datum);
9104 84 : if (EQUAL(name, "unknown"))
9105 : {
9106 1 : proj_destroy(datum);
9107 1 : proj_destroy(baseCRS);
9108 1 : return false;
9109 : }
9110 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9111 : PJ_OBJ_LIST *list =
9112 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9113 :
9114 83 : bool knownDatumName = false;
9115 83 : if (list)
9116 : {
9117 83 : if (proj_list_get_count(list) == 1)
9118 : {
9119 70 : knownDatumName = true;
9120 : }
9121 83 : proj_list_destroy(list);
9122 : }
9123 :
9124 83 : proj_destroy(datum);
9125 83 : if (knownDatumName)
9126 : {
9127 70 : d->setPjCRS(baseCRS);
9128 70 : return true;
9129 : }
9130 13 : proj_destroy(baseCRS);
9131 13 : return false;
9132 : }
9133 :
9134 : /************************************************************************/
9135 : /* IsCompound() */
9136 : /************************************************************************/
9137 :
9138 : /**
9139 : * \brief Check if coordinate system is compound.
9140 : *
9141 : * This method is the same as the C function OSRIsCompound().
9142 : *
9143 : * @return TRUE if this is rooted with a COMPD_CS node.
9144 : */
9145 :
9146 42166 : int OGRSpatialReference::IsCompound() const
9147 :
9148 : {
9149 42166 : TAKE_OPTIONAL_LOCK();
9150 :
9151 42166 : d->refreshProjObj();
9152 42166 : d->demoteFromBoundCRS();
9153 42166 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9154 42166 : d->undoDemoteFromBoundCRS();
9155 84332 : return isCompound;
9156 : }
9157 :
9158 : /************************************************************************/
9159 : /* OSRIsCompound() */
9160 : /************************************************************************/
9161 :
9162 : /**
9163 : * \brief Check if the coordinate system is compound.
9164 : *
9165 : * This function is the same as OGRSpatialReference::IsCompound().
9166 : */
9167 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9168 :
9169 : {
9170 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9171 :
9172 5 : return ToPointer(hSRS)->IsCompound();
9173 : }
9174 :
9175 : /************************************************************************/
9176 : /* IsProjected() */
9177 : /************************************************************************/
9178 :
9179 : /**
9180 : * \brief Check if projected coordinate system.
9181 : *
9182 : * This method is the same as the C function OSRIsProjected().
9183 : *
9184 : * @return TRUE if this contains a PROJCS node indicating a it is a
9185 : * projected coordinate system. Also if it is a CompoundCRS made of a
9186 : * ProjectedCRS
9187 : */
9188 :
9189 45492 : int OGRSpatialReference::IsProjected() const
9190 :
9191 : {
9192 45492 : TAKE_OPTIONAL_LOCK();
9193 :
9194 45492 : d->refreshProjObj();
9195 45492 : d->demoteFromBoundCRS();
9196 45492 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9197 45492 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9198 : {
9199 : auto horizCRS =
9200 150 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9201 150 : if (horizCRS)
9202 : {
9203 150 : auto horizCRSType = proj_get_type(horizCRS);
9204 150 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9205 150 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9206 : {
9207 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9208 6 : if (base)
9209 : {
9210 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9211 6 : proj_destroy(base);
9212 : }
9213 : }
9214 150 : proj_destroy(horizCRS);
9215 : }
9216 : }
9217 45492 : d->undoDemoteFromBoundCRS();
9218 90984 : return isProjected;
9219 : }
9220 :
9221 : /************************************************************************/
9222 : /* OSRIsProjected() */
9223 : /************************************************************************/
9224 : /**
9225 : * \brief Check if projected coordinate system.
9226 : *
9227 : * This function is the same as OGRSpatialReference::IsProjected().
9228 : */
9229 441 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9230 :
9231 : {
9232 441 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9233 :
9234 441 : return ToPointer(hSRS)->IsProjected();
9235 : }
9236 :
9237 : /************************************************************************/
9238 : /* IsGeocentric() */
9239 : /************************************************************************/
9240 :
9241 : /**
9242 : * \brief Check if geocentric coordinate system.
9243 : *
9244 : * This method is the same as the C function OSRIsGeocentric().
9245 : *
9246 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9247 : * geocentric coordinate system.
9248 : *
9249 : */
9250 :
9251 17747 : int OGRSpatialReference::IsGeocentric() const
9252 :
9253 : {
9254 17747 : TAKE_OPTIONAL_LOCK();
9255 :
9256 17747 : d->refreshProjObj();
9257 17747 : d->demoteFromBoundCRS();
9258 17747 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9259 17747 : d->undoDemoteFromBoundCRS();
9260 35494 : return isGeocentric;
9261 : }
9262 :
9263 : /************************************************************************/
9264 : /* OSRIsGeocentric() */
9265 : /************************************************************************/
9266 : /**
9267 : * \brief Check if geocentric coordinate system.
9268 : *
9269 : * This function is the same as OGRSpatialReference::IsGeocentric().
9270 : *
9271 : */
9272 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9273 :
9274 : {
9275 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9276 :
9277 2 : return ToPointer(hSRS)->IsGeocentric();
9278 : }
9279 :
9280 : /************************************************************************/
9281 : /* IsEmpty() */
9282 : /************************************************************************/
9283 :
9284 : /**
9285 : * \brief Return if the SRS is not set.
9286 : */
9287 :
9288 118647 : bool OGRSpatialReference::IsEmpty() const
9289 : {
9290 118647 : TAKE_OPTIONAL_LOCK();
9291 :
9292 118647 : d->refreshProjObj();
9293 237294 : return d->m_pj_crs == nullptr;
9294 : }
9295 :
9296 : /************************************************************************/
9297 : /* IsGeographic() */
9298 : /************************************************************************/
9299 :
9300 : /**
9301 : * \brief Check if geographic coordinate system.
9302 : *
9303 : * This method is the same as the C function OSRIsGeographic().
9304 : *
9305 : * @return TRUE if this spatial reference is geographic ... that is the
9306 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9307 : * GeographicCRS
9308 : */
9309 :
9310 62716 : int OGRSpatialReference::IsGeographic() const
9311 :
9312 : {
9313 62716 : TAKE_OPTIONAL_LOCK();
9314 :
9315 62716 : d->refreshProjObj();
9316 62716 : d->demoteFromBoundCRS();
9317 87872 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9318 25156 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9319 62716 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9320 : {
9321 : auto horizCRS =
9322 300 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9323 300 : if (horizCRS)
9324 : {
9325 300 : auto horizCRSType = proj_get_type(horizCRS);
9326 300 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9327 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9328 300 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9329 : {
9330 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9331 13 : if (base)
9332 : {
9333 13 : horizCRSType = proj_get_type(base);
9334 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9335 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9336 13 : proj_destroy(base);
9337 : }
9338 : }
9339 300 : proj_destroy(horizCRS);
9340 : }
9341 : }
9342 62716 : d->undoDemoteFromBoundCRS();
9343 125432 : return isGeog;
9344 : }
9345 :
9346 : /************************************************************************/
9347 : /* OSRIsGeographic() */
9348 : /************************************************************************/
9349 : /**
9350 : * \brief Check if geographic coordinate system.
9351 : *
9352 : * This function is the same as OGRSpatialReference::IsGeographic().
9353 : */
9354 286 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9355 :
9356 : {
9357 286 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9358 :
9359 286 : return ToPointer(hSRS)->IsGeographic();
9360 : }
9361 :
9362 : /************************************************************************/
9363 : /* IsDerivedGeographic() */
9364 : /************************************************************************/
9365 :
9366 : /**
9367 : * \brief Check if the CRS is a derived geographic coordinate system.
9368 : * (for example a rotated long/lat grid)
9369 : *
9370 : * This method is the same as the C function OSRIsDerivedGeographic().
9371 : *
9372 : * @since GDAL 3.1.0 and PROJ 6.3.0
9373 : */
9374 :
9375 15527 : int OGRSpatialReference::IsDerivedGeographic() const
9376 :
9377 : {
9378 15527 : TAKE_OPTIONAL_LOCK();
9379 :
9380 15527 : d->refreshProjObj();
9381 15527 : d->demoteFromBoundCRS();
9382 25335 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9383 9808 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9384 : const bool isDerivedGeographic =
9385 15527 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9386 15527 : d->undoDemoteFromBoundCRS();
9387 31054 : return isDerivedGeographic ? TRUE : FALSE;
9388 : }
9389 :
9390 : /************************************************************************/
9391 : /* OSRIsDerivedGeographic() */
9392 : /************************************************************************/
9393 : /**
9394 : * \brief Check if the CRS is a derived geographic coordinate system.
9395 : * (for example a rotated long/lat grid)
9396 : *
9397 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9398 : */
9399 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9400 :
9401 : {
9402 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9403 :
9404 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9405 : }
9406 :
9407 : /************************************************************************/
9408 : /* IsDerivedProjected() */
9409 : /************************************************************************/
9410 :
9411 : /**
9412 : * \brief Check if the CRS is a derived projected coordinate system.
9413 : *
9414 : * This method is the same as the C function OSRIsDerivedGeographic().
9415 : *
9416 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9417 : */
9418 :
9419 0 : int OGRSpatialReference::IsDerivedProjected() const
9420 :
9421 : {
9422 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9423 : TAKE_OPTIONAL_LOCK();
9424 : d->refreshProjObj();
9425 : d->demoteFromBoundCRS();
9426 : const bool isDerivedProjected =
9427 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9428 : d->undoDemoteFromBoundCRS();
9429 : return isDerivedProjected ? TRUE : FALSE;
9430 : #else
9431 0 : return FALSE;
9432 : #endif
9433 : }
9434 :
9435 : /************************************************************************/
9436 : /* OSRIsDerivedProjected() */
9437 : /************************************************************************/
9438 : /**
9439 : * \brief Check if the CRS is a derived projected coordinate system.
9440 : *
9441 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9442 : *
9443 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9444 : */
9445 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9446 :
9447 : {
9448 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9449 :
9450 0 : return ToPointer(hSRS)->IsDerivedProjected();
9451 : }
9452 :
9453 : /************************************************************************/
9454 : /* IsLocal() */
9455 : /************************************************************************/
9456 :
9457 : /**
9458 : * \brief Check if local coordinate system.
9459 : *
9460 : * This method is the same as the C function OSRIsLocal().
9461 : *
9462 : * @return TRUE if this spatial reference is local ... that is the
9463 : * root is a LOCAL_CS node.
9464 : */
9465 :
9466 8173 : int OGRSpatialReference::IsLocal() const
9467 :
9468 : {
9469 8173 : TAKE_OPTIONAL_LOCK();
9470 8173 : d->refreshProjObj();
9471 16346 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9472 : }
9473 :
9474 : /************************************************************************/
9475 : /* OSRIsLocal() */
9476 : /************************************************************************/
9477 : /**
9478 : * \brief Check if local coordinate system.
9479 : *
9480 : * This function is the same as OGRSpatialReference::IsLocal().
9481 : */
9482 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9483 :
9484 : {
9485 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9486 :
9487 8 : return ToPointer(hSRS)->IsLocal();
9488 : }
9489 :
9490 : /************************************************************************/
9491 : /* IsVertical() */
9492 : /************************************************************************/
9493 :
9494 : /**
9495 : * \brief Check if vertical coordinate system.
9496 : *
9497 : * This method is the same as the C function OSRIsVertical().
9498 : *
9499 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9500 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9501 : * VerticalCRS
9502 : *
9503 : */
9504 :
9505 9048 : int OGRSpatialReference::IsVertical() const
9506 :
9507 : {
9508 9048 : TAKE_OPTIONAL_LOCK();
9509 9048 : d->refreshProjObj();
9510 9048 : d->demoteFromBoundCRS();
9511 9048 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9512 9048 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9513 : {
9514 : auto vertCRS =
9515 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9516 33 : if (vertCRS)
9517 : {
9518 33 : const auto vertCRSType = proj_get_type(vertCRS);
9519 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9520 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9521 : {
9522 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9523 0 : if (base)
9524 : {
9525 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9526 0 : proj_destroy(base);
9527 : }
9528 : }
9529 33 : proj_destroy(vertCRS);
9530 : }
9531 : }
9532 9048 : d->undoDemoteFromBoundCRS();
9533 18096 : return isVertical;
9534 : }
9535 :
9536 : /************************************************************************/
9537 : /* OSRIsVertical() */
9538 : /************************************************************************/
9539 : /**
9540 : * \brief Check if vertical coordinate system.
9541 : *
9542 : * This function is the same as OGRSpatialReference::IsVertical().
9543 : *
9544 : */
9545 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9546 :
9547 : {
9548 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9549 :
9550 0 : return ToPointer(hSRS)->IsVertical();
9551 : }
9552 :
9553 : /************************************************************************/
9554 : /* IsDynamic() */
9555 : /************************************************************************/
9556 :
9557 : /**
9558 : * \brief Check if a CRS is a dynamic CRS.
9559 : *
9560 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9561 : * plate-fixed.
9562 : *
9563 : * This method is the same as the C function OSRIsDynamic().
9564 : *
9565 : * @return true if the CRS is dynamic
9566 : *
9567 : * @since OGR 3.4.0
9568 : *
9569 : * @see HasPointMotionOperation()
9570 : */
9571 :
9572 16759 : bool OGRSpatialReference::IsDynamic() const
9573 :
9574 : {
9575 16759 : TAKE_OPTIONAL_LOCK();
9576 16759 : bool isDynamic = false;
9577 16759 : d->refreshProjObj();
9578 16759 : d->demoteFromBoundCRS();
9579 16759 : auto ctxt = d->getPROJContext();
9580 16759 : PJ *horiz = nullptr;
9581 16759 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9582 : {
9583 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9584 : }
9585 16663 : else if (d->m_pj_crs)
9586 : {
9587 16469 : horiz = proj_clone(ctxt, d->m_pj_crs);
9588 : }
9589 16759 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9590 : {
9591 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9592 6 : if (baseCRS)
9593 : {
9594 6 : proj_destroy(horiz);
9595 6 : horiz = baseCRS;
9596 : }
9597 : }
9598 16759 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9599 16759 : if (datum)
9600 : {
9601 16530 : const auto type = proj_get_type(datum);
9602 16530 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9603 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9604 16530 : if (!isDynamic)
9605 : {
9606 16530 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9607 16530 : const char *code = proj_get_id_code(datum, 0);
9608 16530 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9609 16012 : EQUAL(code, "6326"))
9610 : {
9611 10457 : isDynamic = true;
9612 : }
9613 : }
9614 16530 : proj_destroy(datum);
9615 : }
9616 : #if PROJ_VERSION_MAJOR > 7 || \
9617 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9618 : else
9619 : {
9620 : auto ensemble =
9621 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9622 : if (ensemble)
9623 : {
9624 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9625 : if (member)
9626 : {
9627 : const auto type = proj_get_type(member);
9628 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9629 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9630 : proj_destroy(member);
9631 : }
9632 : proj_destroy(ensemble);
9633 : }
9634 : }
9635 : #endif
9636 16759 : proj_destroy(horiz);
9637 16759 : d->undoDemoteFromBoundCRS();
9638 33518 : return isDynamic;
9639 : }
9640 :
9641 : /************************************************************************/
9642 : /* OSRIsDynamic() */
9643 : /************************************************************************/
9644 : /**
9645 : * \brief Check if a CRS is a dynamic CRS.
9646 : *
9647 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9648 : * plate-fixed.
9649 : *
9650 : * This function is the same as OGRSpatialReference::IsDynamic().
9651 : *
9652 : * @since OGR 3.4.0
9653 : */
9654 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9655 :
9656 : {
9657 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9658 :
9659 0 : return ToPointer(hSRS)->IsDynamic();
9660 : }
9661 :
9662 : /************************************************************************/
9663 : /* HasPointMotionOperation() */
9664 : /************************************************************************/
9665 :
9666 : /**
9667 : * \brief Check if a CRS has at least an associated point motion operation.
9668 : *
9669 : * Some CRS are not formally declared as dynamic, but may behave as such
9670 : * in practice due to the presence of point motion operation, to perform
9671 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9672 : *
9673 : * @return true if the CRS has at least an associated point motion operation.
9674 : *
9675 : * @since OGR 3.8.0 and PROJ 9.4.0
9676 : *
9677 : * @see IsDynamic()
9678 : */
9679 :
9680 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9681 :
9682 : {
9683 : #if PROJ_VERSION_MAJOR > 9 || \
9684 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9685 : TAKE_OPTIONAL_LOCK();
9686 : d->refreshProjObj();
9687 : d->demoteFromBoundCRS();
9688 : auto ctxt = d->getPROJContext();
9689 : auto res =
9690 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9691 : d->undoDemoteFromBoundCRS();
9692 : return res;
9693 : #else
9694 5 : return false;
9695 : #endif
9696 : }
9697 :
9698 : /************************************************************************/
9699 : /* OSRHasPointMotionOperation() */
9700 : /************************************************************************/
9701 :
9702 : /**
9703 : * \brief Check if a CRS has at least an associated point motion operation.
9704 : *
9705 : * Some CRS are not formally declared as dynamic, but may behave as such
9706 : * in practice due to the presence of point motion operation, to perform
9707 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9708 : *
9709 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9710 : *
9711 : * @since OGR 3.8.0 and PROJ 9.4.0
9712 : */
9713 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9714 :
9715 : {
9716 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9717 :
9718 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9719 : }
9720 :
9721 : /************************************************************************/
9722 : /* CloneGeogCS() */
9723 : /************************************************************************/
9724 :
9725 : /**
9726 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9727 : * object.
9728 : *
9729 : * @return a new SRS, which becomes the responsibility of the caller.
9730 : */
9731 4481 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9732 :
9733 : {
9734 8962 : TAKE_OPTIONAL_LOCK();
9735 4481 : d->refreshProjObj();
9736 4481 : if (d->m_pj_crs)
9737 : {
9738 4481 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9739 0 : return nullptr;
9740 :
9741 : auto geodCRS =
9742 4481 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9743 4481 : if (geodCRS)
9744 : {
9745 4481 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9746 4481 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9747 : {
9748 : PJ *hub_crs =
9749 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9750 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9751 13 : d->m_pj_crs);
9752 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9753 : geodCRS, hub_crs, co);
9754 13 : proj_destroy(geodCRS);
9755 13 : geodCRS = temp;
9756 13 : proj_destroy(hub_crs);
9757 13 : proj_destroy(co);
9758 : }
9759 :
9760 : /* --------------------------------------------------------------------
9761 : */
9762 : /* We have to reconstruct the GEOGCS node for geocentric */
9763 : /* coordinate systems. */
9764 : /* --------------------------------------------------------------------
9765 : */
9766 4481 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9767 : {
9768 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9769 : #if PROJ_VERSION_MAJOR > 7 || \
9770 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9771 : if (datum == nullptr)
9772 : {
9773 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9774 : geodCRS);
9775 : }
9776 : #endif
9777 0 : if (datum)
9778 : {
9779 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9780 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9781 : nullptr, 0);
9782 0 : auto temp = proj_create_geographic_crs_from_datum(
9783 : d->getPROJContext(), "unnamed", datum, cs);
9784 0 : proj_destroy(datum);
9785 0 : proj_destroy(cs);
9786 0 : proj_destroy(geodCRS);
9787 0 : geodCRS = temp;
9788 : }
9789 : }
9790 :
9791 4481 : poNewSRS->d->setPjCRS(geodCRS);
9792 4481 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9793 2975 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9794 4481 : return poNewSRS;
9795 : }
9796 : }
9797 0 : return nullptr;
9798 : }
9799 :
9800 : /************************************************************************/
9801 : /* OSRCloneGeogCS() */
9802 : /************************************************************************/
9803 : /**
9804 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9805 : * object.
9806 : *
9807 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9808 : */
9809 126 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9810 :
9811 : {
9812 126 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9813 :
9814 126 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9815 : }
9816 :
9817 : /************************************************************************/
9818 : /* IsSameGeogCS() */
9819 : /************************************************************************/
9820 :
9821 : /**
9822 : * \brief Do the GeogCS'es match?
9823 : *
9824 : * This method is the same as the C function OSRIsSameGeogCS().
9825 : *
9826 : * @param poOther the SRS being compared against.
9827 : *
9828 : * @return TRUE if they are the same or FALSE otherwise.
9829 : */
9830 :
9831 8744 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9832 :
9833 : {
9834 8744 : return IsSameGeogCS(poOther, nullptr);
9835 : }
9836 :
9837 : /**
9838 : * \brief Do the GeogCS'es match?
9839 : *
9840 : * This method is the same as the C function OSRIsSameGeogCS().
9841 : *
9842 : * @param poOther the SRS being compared against.
9843 : * @param papszOptions options. ignored
9844 : *
9845 : * @return TRUE if they are the same or FALSE otherwise.
9846 : */
9847 :
9848 8744 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9849 : const char *const *papszOptions) const
9850 :
9851 : {
9852 17488 : TAKE_OPTIONAL_LOCK();
9853 :
9854 8744 : CPL_IGNORE_RET_VAL(papszOptions);
9855 :
9856 8744 : d->refreshProjObj();
9857 8744 : poOther->d->refreshProjObj();
9858 8744 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9859 0 : return FALSE;
9860 8744 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9861 8744 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9862 26232 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9863 8744 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9864 : {
9865 0 : return FALSE;
9866 : }
9867 :
9868 8744 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9869 : auto otherGeodCRS =
9870 8744 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9871 8744 : if (!geodCRS || !otherGeodCRS)
9872 : {
9873 0 : proj_destroy(geodCRS);
9874 0 : proj_destroy(otherGeodCRS);
9875 0 : return FALSE;
9876 : }
9877 :
9878 8744 : int ret = proj_is_equivalent_to(
9879 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9880 :
9881 8744 : proj_destroy(geodCRS);
9882 8744 : proj_destroy(otherGeodCRS);
9883 8744 : return ret;
9884 : }
9885 :
9886 : /************************************************************************/
9887 : /* OSRIsSameGeogCS() */
9888 : /************************************************************************/
9889 :
9890 : /**
9891 : * \brief Do the GeogCS'es match?
9892 : *
9893 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9894 : */
9895 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9896 :
9897 : {
9898 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9899 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9900 :
9901 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9902 : }
9903 :
9904 : /************************************************************************/
9905 : /* IsSameVertCS() */
9906 : /************************************************************************/
9907 :
9908 : /**
9909 : * \brief Do the VertCS'es match?
9910 : *
9911 : * This method is the same as the C function OSRIsSameVertCS().
9912 : *
9913 : * @param poOther the SRS being compared against.
9914 : *
9915 : * @return TRUE if they are the same or FALSE otherwise.
9916 : */
9917 :
9918 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9919 :
9920 : {
9921 0 : TAKE_OPTIONAL_LOCK();
9922 :
9923 : /* -------------------------------------------------------------------- */
9924 : /* Does the datum name match? */
9925 : /* -------------------------------------------------------------------- */
9926 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9927 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9928 :
9929 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9930 0 : !EQUAL(pszThisValue, pszOtherValue))
9931 0 : return FALSE;
9932 :
9933 : /* -------------------------------------------------------------------- */
9934 : /* Do the units match? */
9935 : /* -------------------------------------------------------------------- */
9936 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9937 0 : if (pszThisValue == nullptr)
9938 0 : pszThisValue = "1.0";
9939 :
9940 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9941 0 : if (pszOtherValue == nullptr)
9942 0 : pszOtherValue = "1.0";
9943 :
9944 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9945 0 : return FALSE;
9946 :
9947 0 : return TRUE;
9948 : }
9949 :
9950 : /************************************************************************/
9951 : /* OSRIsSameVertCS() */
9952 : /************************************************************************/
9953 :
9954 : /**
9955 : * \brief Do the VertCS'es match?
9956 : *
9957 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9958 : */
9959 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9960 :
9961 : {
9962 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9963 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9964 :
9965 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9966 : }
9967 :
9968 : /************************************************************************/
9969 : /* IsSame() */
9970 : /************************************************************************/
9971 :
9972 : /**
9973 : * \brief Do these two spatial references describe the same system ?
9974 : *
9975 : * @param poOtherSRS the SRS being compared to.
9976 : *
9977 : * @return TRUE if equivalent or FALSE otherwise.
9978 : */
9979 :
9980 23465 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9981 :
9982 : {
9983 23465 : return IsSame(poOtherSRS, nullptr);
9984 : }
9985 :
9986 : /**
9987 : * \brief Do these two spatial references describe the same system ?
9988 : *
9989 : * This also takes into account the data axis to CRS axis mapping by default
9990 : *
9991 : * @param poOtherSRS the SRS being compared to.
9992 : * @param papszOptions options. NULL or NULL terminated list of options.
9993 : * Currently supported options are:
9994 : * <ul>
9995 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9996 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9997 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9998 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9999 : * </ul>
10000 : *
10001 : * @return TRUE if equivalent or FALSE otherwise.
10002 : */
10003 :
10004 36755 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
10005 : const char *const *papszOptions) const
10006 :
10007 : {
10008 73510 : TAKE_OPTIONAL_LOCK();
10009 :
10010 36755 : d->refreshProjObj();
10011 36755 : poOtherSRS->d->refreshProjObj();
10012 36755 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10013 51 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10014 36704 : if (!CPLTestBool(CSLFetchNameValueDef(
10015 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10016 : {
10017 35981 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10018 3545 : return false;
10019 : }
10020 :
10021 33159 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10022 : "IGNORE_COORDINATE_EPOCH", "NO")))
10023 : {
10024 32695 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10025 27 : return false;
10026 : }
10027 :
10028 33132 : bool reboundSelf = false;
10029 33132 : bool reboundOther = false;
10030 33184 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10031 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10032 : {
10033 14 : d->demoteFromBoundCRS();
10034 14 : reboundSelf = true;
10035 : }
10036 66198 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10037 33080 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10038 : {
10039 28 : poOtherSRS->d->demoteFromBoundCRS();
10040 28 : reboundOther = true;
10041 : }
10042 :
10043 33132 : PJ_COMPARISON_CRITERION criterion =
10044 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10045 33132 : const char *pszCriterion = CSLFetchNameValueDef(
10046 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10047 33132 : if (EQUAL(pszCriterion, "STRICT"))
10048 0 : criterion = PJ_COMP_STRICT;
10049 33132 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10050 9942 : criterion = PJ_COMP_EQUIVALENT;
10051 23190 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10052 : {
10053 0 : CPLError(CE_Warning, CPLE_NotSupported,
10054 : "Unsupported value for CRITERION: %s", pszCriterion);
10055 : }
10056 : int ret =
10057 33132 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10058 33132 : if (reboundSelf)
10059 14 : d->undoDemoteFromBoundCRS();
10060 33132 : if (reboundOther)
10061 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10062 :
10063 33132 : return ret;
10064 : }
10065 :
10066 : /************************************************************************/
10067 : /* OSRIsSame() */
10068 : /************************************************************************/
10069 :
10070 : /**
10071 : * \brief Do these two spatial references describe the same system ?
10072 : *
10073 : * This function is the same as OGRSpatialReference::IsSame().
10074 : */
10075 35 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10076 :
10077 : {
10078 35 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10079 35 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10080 :
10081 35 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10082 : }
10083 :
10084 : /************************************************************************/
10085 : /* OSRIsSameEx() */
10086 : /************************************************************************/
10087 :
10088 : /**
10089 : * \brief Do these two spatial references describe the same system ?
10090 : *
10091 : * This function is the same as OGRSpatialReference::IsSame().
10092 : */
10093 649 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10094 : const char *const *papszOptions)
10095 : {
10096 649 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10097 649 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10098 :
10099 649 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10100 : }
10101 :
10102 : /************************************************************************/
10103 : /* convertToOtherProjection() */
10104 : /************************************************************************/
10105 :
10106 : /**
10107 : * \brief Convert to another equivalent projection
10108 : *
10109 : * Currently implemented:
10110 : * <ul>
10111 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10112 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10113 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10114 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10115 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10116 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10117 : * </ul>
10118 : *
10119 : * @param pszTargetProjection target projection.
10120 : * @param papszOptions lists of options. None supported currently.
10121 : * @return a new SRS, or NULL in case of error.
10122 : *
10123 : */
10124 91 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10125 : const char *pszTargetProjection,
10126 : CPL_UNUSED const char *const *papszOptions) const
10127 : {
10128 182 : TAKE_OPTIONAL_LOCK();
10129 :
10130 91 : if (pszTargetProjection == nullptr)
10131 1 : return nullptr;
10132 : int new_code;
10133 90 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10134 : {
10135 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10136 : }
10137 84 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10138 : {
10139 7 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10140 : }
10141 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10142 : {
10143 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10144 : }
10145 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10146 : {
10147 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10148 : }
10149 : else
10150 : {
10151 1 : return nullptr;
10152 : }
10153 :
10154 89 : d->refreshProjObj();
10155 89 : d->demoteFromBoundCRS();
10156 89 : OGRSpatialReference *poNewSRS = nullptr;
10157 89 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10158 : {
10159 : auto conv =
10160 88 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10161 88 : auto new_conv = proj_convert_conversion_to_other_method(
10162 : d->getPROJContext(), conv, new_code, nullptr);
10163 88 : proj_destroy(conv);
10164 88 : if (new_conv)
10165 : {
10166 : auto geodCRS =
10167 74 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10168 74 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10169 74 : d->m_pj_crs);
10170 74 : if (geodCRS && cs)
10171 : {
10172 74 : auto new_proj_crs = proj_create_projected_crs(
10173 74 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10174 : new_conv, cs);
10175 74 : proj_destroy(new_conv);
10176 74 : if (new_proj_crs)
10177 : {
10178 74 : poNewSRS = new OGRSpatialReference();
10179 :
10180 74 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10181 : {
10182 9 : auto boundCRS = proj_crs_create_bound_crs(
10183 : d->getPROJContext(), new_proj_crs,
10184 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10185 9 : if (boundCRS)
10186 : {
10187 9 : proj_destroy(new_proj_crs);
10188 9 : new_proj_crs = boundCRS;
10189 : }
10190 : }
10191 :
10192 74 : poNewSRS->d->setPjCRS(new_proj_crs);
10193 : }
10194 : }
10195 74 : proj_destroy(geodCRS);
10196 74 : proj_destroy(cs);
10197 : }
10198 : }
10199 89 : d->undoDemoteFromBoundCRS();
10200 89 : return poNewSRS;
10201 : }
10202 :
10203 : /************************************************************************/
10204 : /* OSRConvertToOtherProjection() */
10205 : /************************************************************************/
10206 :
10207 : /**
10208 : * \brief Convert to another equivalent projection
10209 : *
10210 : * Currently implemented:
10211 : * <ul>
10212 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10213 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10214 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10215 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10216 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10217 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10218 : * </ul>
10219 : *
10220 : * @param hSRS source SRS
10221 : * @param pszTargetProjection target projection.
10222 : * @param papszOptions lists of options. None supported currently.
10223 : * @return a new SRS, or NULL in case of error.
10224 : *
10225 : */
10226 : OGRSpatialReferenceH
10227 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10228 : const char *pszTargetProjection,
10229 : const char *const *papszOptions)
10230 : {
10231 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10232 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10233 28 : pszTargetProjection, papszOptions));
10234 : }
10235 :
10236 : /************************************************************************/
10237 : /* OSRFindMatches() */
10238 : /************************************************************************/
10239 :
10240 : /**
10241 : * \brief Try to identify a match between the passed SRS and a related SRS
10242 : * in a catalog.
10243 : *
10244 : * Matching may be partial, or may fail.
10245 : * Returned entries will be sorted by decreasing match confidence (first
10246 : * entry has the highest match confidence).
10247 : *
10248 : * The exact way matching is done may change in future versions. Starting with
10249 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10250 : *
10251 : * This function is the same as OGRSpatialReference::FindMatches().
10252 : *
10253 : * @param hSRS SRS to match
10254 : * @param papszOptions NULL terminated list of options or NULL
10255 : * @param pnEntries Output parameter. Number of values in the returned array.
10256 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10257 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10258 : * indicate the confidence in the match. 100 is the highest confidence level.
10259 : * The array must be freed with CPLFree().
10260 : *
10261 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10262 : * with OSRFreeSRSArray()
10263 : *
10264 : */
10265 10 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10266 : CSLConstList papszOptions, int *pnEntries,
10267 : int **ppanMatchConfidence)
10268 : {
10269 10 : if (pnEntries)
10270 10 : *pnEntries = 0;
10271 10 : if (ppanMatchConfidence)
10272 10 : *ppanMatchConfidence = nullptr;
10273 10 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10274 :
10275 10 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10276 10 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10277 : }
10278 :
10279 : /************************************************************************/
10280 : /* OSRFreeSRSArray() */
10281 : /************************************************************************/
10282 :
10283 : /**
10284 : * \brief Free return of OSRIdentifyMatches()
10285 : *
10286 : * @param pahSRS array of SRS (must be NULL terminated)
10287 : */
10288 197 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10289 : {
10290 197 : if (pahSRS != nullptr)
10291 : {
10292 1744 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10293 : {
10294 1565 : OSRRelease(pahSRS[i]);
10295 : }
10296 179 : CPLFree(pahSRS);
10297 : }
10298 197 : }
10299 :
10300 : /************************************************************************/
10301 : /* FindBestMatch() */
10302 : /************************************************************************/
10303 :
10304 : /**
10305 : * \brief Try to identify the best match between the passed SRS and a related
10306 : * SRS in a catalog.
10307 : *
10308 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10309 : * of filtering its output.
10310 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10311 : * will be considered. If there is a single match, it is returned.
10312 : * If there are several matches, only return the one under the
10313 : * pszPreferredAuthority, if there is a single one under that authority.
10314 : *
10315 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10316 : * 100). If set to 0, 90 is used.
10317 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10318 : * "EPSG" is used.
10319 : * @param papszOptions NULL terminated list of options or NULL. No option is
10320 : * defined at time of writing.
10321 : *
10322 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10323 : *
10324 : * @since GDAL 3.6
10325 : * @see OGRSpatialReference::FindMatches()
10326 : */
10327 : OGRSpatialReference *
10328 1499 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10329 : const char *pszPreferredAuthority,
10330 : CSLConstList papszOptions) const
10331 : {
10332 2998 : TAKE_OPTIONAL_LOCK();
10333 :
10334 1499 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10335 :
10336 1499 : if (nMinimumMatchConfidence == 0)
10337 0 : nMinimumMatchConfidence = 90;
10338 1499 : if (pszPreferredAuthority == nullptr)
10339 201 : pszPreferredAuthority = "EPSG";
10340 :
10341 : // Try to identify the CRS with the database
10342 1499 : int nEntries = 0;
10343 1499 : int *panConfidence = nullptr;
10344 : OGRSpatialReferenceH *pahSRS =
10345 1499 : FindMatches(nullptr, &nEntries, &panConfidence);
10346 1499 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10347 : {
10348 2670 : std::vector<double> adfTOWGS84(7);
10349 1335 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10350 : {
10351 1334 : adfTOWGS84.clear();
10352 : }
10353 :
10354 1335 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10355 :
10356 : auto poBaseGeogCRS =
10357 1335 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10358 1335 : if (poBaseGeogCRS)
10359 : {
10360 : // If the base geographic SRS of the SRS is EPSG:4326
10361 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10362 : // SRS code
10363 : // Same with EPSG:4258 (ETRS89), since it's the only known
10364 : // TOWGS84[] style transformation to WGS 84, and given the
10365 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10366 : // chance that a non-NULL TOWGS84[] will emerge.
10367 1335 : const char *pszAuthorityName = nullptr;
10368 1335 : const char *pszAuthorityCode = nullptr;
10369 1335 : const char *pszBaseAuthorityName = nullptr;
10370 1335 : const char *pszBaseAuthorityCode = nullptr;
10371 1335 : const char *pszBaseName = poBaseGeogCRS->GetName();
10372 2670 : if (adfTOWGS84 == std::vector<double>(7) &&
10373 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) !=
10374 1 : nullptr &&
10375 1 : EQUAL(pszAuthorityName, "EPSG") &&
10376 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) !=
10377 1 : nullptr &&
10378 : (pszBaseAuthorityName =
10379 1 : poBaseGeogCRS->GetAuthorityName(nullptr)) != nullptr &&
10380 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10381 : (pszBaseAuthorityCode =
10382 2672 : poBaseGeogCRS->GetAuthorityCode(nullptr)) != nullptr &&
10383 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10384 1 : EQUAL(pszBaseAuthorityCode, "4258") ||
10385 : // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10386 0 : (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10387 : {
10388 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10389 : }
10390 : }
10391 :
10392 1335 : CPLFree(pahSRS);
10393 1335 : CPLFree(panConfidence);
10394 :
10395 1335 : return poSRS;
10396 : }
10397 : else
10398 : {
10399 : // If there are several matches >= nMinimumMatchConfidence, take the
10400 : // only one that is under pszPreferredAuthority
10401 164 : int iBestEntry = -1;
10402 1679 : for (int i = 0; i < nEntries; i++)
10403 : {
10404 1515 : if (panConfidence[i] >= nMinimumMatchConfidence)
10405 : {
10406 : const char *pszAuthName =
10407 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10408 3 : ->GetAuthorityName(nullptr);
10409 3 : if (pszAuthName != nullptr &&
10410 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10411 : {
10412 3 : if (iBestEntry < 0)
10413 3 : iBestEntry = i;
10414 : else
10415 : {
10416 0 : iBestEntry = -1;
10417 0 : break;
10418 : }
10419 : }
10420 : }
10421 : }
10422 164 : if (iBestEntry >= 0)
10423 : {
10424 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10425 3 : OSRFreeSRSArray(pahSRS);
10426 3 : CPLFree(panConfidence);
10427 3 : return poRet;
10428 : }
10429 : }
10430 161 : OSRFreeSRSArray(pahSRS);
10431 161 : CPLFree(panConfidence);
10432 161 : return nullptr;
10433 : }
10434 :
10435 : /************************************************************************/
10436 : /* SetTOWGS84() */
10437 : /************************************************************************/
10438 :
10439 : /**
10440 : * \brief Set the Bursa-Wolf conversion to WGS84.
10441 : *
10442 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10443 : * if there is no existing DATUM node. It will replace
10444 : * an existing TOWGS84 node if there is one.
10445 : *
10446 : * The parameters have the same meaning as EPSG transformation 9606
10447 : * (Position Vector 7-param. transformation).
10448 : *
10449 : * This method is the same as the C function OSRSetTOWGS84().
10450 : *
10451 : * @param dfDX X child in meters.
10452 : * @param dfDY Y child in meters.
10453 : * @param dfDZ Z child in meters.
10454 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10455 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10456 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10457 : * @param dfPPM scaling factor (parts per million).
10458 : *
10459 : * @return OGRERR_NONE on success.
10460 : */
10461 :
10462 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10463 : double dfEX, double dfEY, double dfEZ,
10464 : double dfPPM)
10465 :
10466 : {
10467 206 : TAKE_OPTIONAL_LOCK();
10468 :
10469 103 : d->refreshProjObj();
10470 103 : if (d->m_pj_crs == nullptr)
10471 : {
10472 0 : return OGRERR_FAILURE;
10473 : }
10474 :
10475 : // Remove existing BoundCRS
10476 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10477 : {
10478 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10479 0 : if (!baseCRS)
10480 0 : return OGRERR_FAILURE;
10481 0 : d->setPjCRS(baseCRS);
10482 : }
10483 :
10484 : PJ_PARAM_DESCRIPTION params[7];
10485 :
10486 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10487 103 : params[0].auth_name = "EPSG";
10488 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10489 103 : params[0].value = dfDX;
10490 103 : params[0].unit_name = "metre";
10491 103 : params[0].unit_conv_factor = 1.0;
10492 103 : params[0].unit_type = PJ_UT_LINEAR;
10493 :
10494 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10495 103 : params[1].auth_name = "EPSG";
10496 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10497 103 : params[1].value = dfDY;
10498 103 : params[1].unit_name = "metre";
10499 103 : params[1].unit_conv_factor = 1.0;
10500 103 : params[1].unit_type = PJ_UT_LINEAR;
10501 :
10502 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10503 103 : params[2].auth_name = "EPSG";
10504 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10505 103 : params[2].value = dfDZ;
10506 103 : params[2].unit_name = "metre";
10507 103 : params[2].unit_conv_factor = 1.0;
10508 103 : params[2].unit_type = PJ_UT_LINEAR;
10509 :
10510 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10511 103 : params[3].auth_name = "EPSG";
10512 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10513 103 : params[3].value = dfEX;
10514 103 : params[3].unit_name = "arc-second";
10515 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10516 103 : params[3].unit_type = PJ_UT_ANGULAR;
10517 :
10518 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10519 103 : params[4].auth_name = "EPSG";
10520 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10521 103 : params[4].value = dfEY;
10522 103 : params[4].unit_name = "arc-second";
10523 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10524 103 : params[4].unit_type = PJ_UT_ANGULAR;
10525 :
10526 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10527 103 : params[5].auth_name = "EPSG";
10528 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10529 103 : params[5].value = dfEZ;
10530 103 : params[5].unit_name = "arc-second";
10531 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10532 103 : params[5].unit_type = PJ_UT_ANGULAR;
10533 :
10534 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10535 103 : params[6].auth_name = "EPSG";
10536 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10537 103 : params[6].value = dfPPM;
10538 103 : params[6].unit_name = "parts per million";
10539 103 : params[6].unit_conv_factor = 1e-6;
10540 103 : params[6].unit_type = PJ_UT_SCALE;
10541 :
10542 : auto sourceCRS =
10543 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10544 103 : if (!sourceCRS)
10545 : {
10546 0 : return OGRERR_FAILURE;
10547 : }
10548 :
10549 103 : const auto sourceType = proj_get_type(sourceCRS);
10550 :
10551 103 : auto targetCRS = proj_create_from_database(
10552 : d->getPROJContext(), "EPSG",
10553 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10554 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10555 : : "4978",
10556 : PJ_CATEGORY_CRS, false, nullptr);
10557 103 : if (!targetCRS)
10558 : {
10559 0 : proj_destroy(sourceCRS);
10560 0 : return OGRERR_FAILURE;
10561 : }
10562 :
10563 206 : CPLString osMethodCode;
10564 : osMethodCode.Printf("%d",
10565 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10566 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10567 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10568 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10569 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10570 :
10571 103 : auto transf = proj_create_transformation(
10572 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10573 : sourceCRS, targetCRS, nullptr,
10574 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10575 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10576 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10577 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10578 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10579 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10580 103 : proj_destroy(sourceCRS);
10581 103 : if (!transf)
10582 : {
10583 0 : proj_destroy(targetCRS);
10584 0 : return OGRERR_FAILURE;
10585 : }
10586 :
10587 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10588 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10589 103 : proj_destroy(transf);
10590 103 : proj_destroy(targetCRS);
10591 103 : if (!newBoundCRS)
10592 : {
10593 0 : return OGRERR_FAILURE;
10594 : }
10595 :
10596 103 : d->setPjCRS(newBoundCRS);
10597 103 : return OGRERR_NONE;
10598 : }
10599 :
10600 : /************************************************************************/
10601 : /* OSRSetTOWGS84() */
10602 : /************************************************************************/
10603 :
10604 : /**
10605 : * \brief Set the Bursa-Wolf conversion to WGS84.
10606 : *
10607 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10608 : */
10609 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10610 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10611 : double dfPPM)
10612 :
10613 : {
10614 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10615 :
10616 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10617 4 : dfPPM);
10618 : }
10619 :
10620 : /************************************************************************/
10621 : /* GetTOWGS84() */
10622 : /************************************************************************/
10623 :
10624 : /**
10625 : * \brief Fetch TOWGS84 parameters, if available.
10626 : *
10627 : * The parameters have the same meaning as EPSG transformation 9606
10628 : * (Position Vector 7-param. transformation).
10629 : *
10630 : * @param padfCoeff array into which up to 7 coefficients are placed.
10631 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10632 : *
10633 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10634 : * TOWGS84 node available.
10635 : */
10636 :
10637 4911 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10638 :
10639 : {
10640 9822 : TAKE_OPTIONAL_LOCK();
10641 :
10642 4911 : d->refreshProjObj();
10643 4911 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10644 4863 : return OGRERR_FAILURE;
10645 :
10646 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10647 :
10648 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10649 48 : int success = proj_coordoperation_get_towgs84_values(
10650 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10651 48 : proj_destroy(transf);
10652 :
10653 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10654 : }
10655 :
10656 : /************************************************************************/
10657 : /* OSRGetTOWGS84() */
10658 : /************************************************************************/
10659 :
10660 : /**
10661 : * \brief Fetch TOWGS84 parameters, if available.
10662 : *
10663 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10664 : */
10665 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10666 : int nCoeffCount)
10667 :
10668 : {
10669 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10670 :
10671 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10672 : }
10673 :
10674 : /************************************************************************/
10675 : /* IsAngularParameter() */
10676 : /************************************************************************/
10677 :
10678 : /** Is the passed projection parameter an angular one?
10679 : *
10680 : * @return TRUE or FALSE
10681 : */
10682 :
10683 : /* static */
10684 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10685 :
10686 : {
10687 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10688 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10689 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10690 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10691 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10692 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10693 8 : return TRUE;
10694 :
10695 2 : return FALSE;
10696 : }
10697 :
10698 : /************************************************************************/
10699 : /* IsLongitudeParameter() */
10700 : /************************************************************************/
10701 :
10702 : /** Is the passed projection parameter an angular longitude
10703 : * (relative to a prime meridian)?
10704 : *
10705 : * @return TRUE or FALSE
10706 : */
10707 :
10708 : /* static */
10709 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10710 :
10711 : {
10712 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10713 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10714 0 : return TRUE;
10715 :
10716 0 : return FALSE;
10717 : }
10718 :
10719 : /************************************************************************/
10720 : /* IsLinearParameter() */
10721 : /************************************************************************/
10722 :
10723 : /** Is the passed projection parameter an linear one measured in meters or
10724 : * some similar linear measure.
10725 : *
10726 : * @return TRUE or FALSE
10727 : */
10728 :
10729 : /* static */
10730 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10731 :
10732 : {
10733 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10734 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10735 9 : return TRUE;
10736 :
10737 34 : return FALSE;
10738 : }
10739 :
10740 : /************************************************************************/
10741 : /* GetNormInfo() */
10742 : /************************************************************************/
10743 :
10744 : /**
10745 : * \brief Set the internal information for normalizing linear, and angular
10746 : * values.
10747 : */
10748 4121 : void OGRSpatialReference::GetNormInfo() const
10749 :
10750 : {
10751 4121 : TAKE_OPTIONAL_LOCK();
10752 :
10753 4121 : if (d->bNormInfoSet)
10754 2943 : return;
10755 :
10756 : /* -------------------------------------------------------------------- */
10757 : /* Initialize values. */
10758 : /* -------------------------------------------------------------------- */
10759 1178 : d->bNormInfoSet = TRUE;
10760 :
10761 1178 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10762 1178 : d->dfToMeter = GetLinearUnits(nullptr);
10763 1178 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10764 1178 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10765 1175 : d->dfToDegrees = 1.0;
10766 : }
10767 :
10768 : /************************************************************************/
10769 : /* GetExtension() */
10770 : /************************************************************************/
10771 :
10772 : /**
10773 : * \brief Fetch extension value.
10774 : *
10775 : * Fetch the value of the named EXTENSION item for the identified
10776 : * target node.
10777 : *
10778 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10779 : * @param pszName the name of the extension being fetched.
10780 : * @param pszDefault the value to return if the extension is not found.
10781 : *
10782 : * @return node value if successful or pszDefault on failure.
10783 : */
10784 :
10785 13082 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10786 : const char *pszName,
10787 : const char *pszDefault) const
10788 :
10789 : {
10790 26164 : TAKE_OPTIONAL_LOCK();
10791 :
10792 : /* -------------------------------------------------------------------- */
10793 : /* Find the target node. */
10794 : /* -------------------------------------------------------------------- */
10795 : const OGR_SRSNode *poNode =
10796 13082 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10797 :
10798 13082 : if (poNode == nullptr)
10799 2364 : return nullptr;
10800 :
10801 : /* -------------------------------------------------------------------- */
10802 : /* Fetch matching EXTENSION if there is one. */
10803 : /* -------------------------------------------------------------------- */
10804 79139 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10805 : {
10806 68445 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10807 :
10808 68471 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10809 26 : poChild->GetChildCount() >= 2)
10810 : {
10811 26 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10812 24 : return poChild->GetChild(1)->GetValue();
10813 : }
10814 : }
10815 :
10816 10694 : return pszDefault;
10817 : }
10818 :
10819 : /************************************************************************/
10820 : /* SetExtension() */
10821 : /************************************************************************/
10822 : /**
10823 : * \brief Set extension value.
10824 : *
10825 : * Set the value of the named EXTENSION item for the identified
10826 : * target node.
10827 : *
10828 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10829 : * @param pszName the name of the extension being fetched.
10830 : * @param pszValue the value to set
10831 : *
10832 : * @return OGRERR_NONE on success
10833 : */
10834 :
10835 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10836 : const char *pszName,
10837 : const char *pszValue)
10838 :
10839 : {
10840 40 : TAKE_OPTIONAL_LOCK();
10841 :
10842 : /* -------------------------------------------------------------------- */
10843 : /* Find the target node. */
10844 : /* -------------------------------------------------------------------- */
10845 20 : OGR_SRSNode *poNode = nullptr;
10846 :
10847 20 : if (pszTargetKey == nullptr)
10848 0 : poNode = GetRoot();
10849 : else
10850 20 : poNode = GetAttrNode(pszTargetKey);
10851 :
10852 20 : if (poNode == nullptr)
10853 0 : return OGRERR_FAILURE;
10854 :
10855 : /* -------------------------------------------------------------------- */
10856 : /* Fetch matching EXTENSION if there is one. */
10857 : /* -------------------------------------------------------------------- */
10858 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10859 : {
10860 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10861 :
10862 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10863 6 : poChild->GetChildCount() >= 2)
10864 : {
10865 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10866 : {
10867 6 : poChild->GetChild(1)->SetValue(pszValue);
10868 6 : return OGRERR_NONE;
10869 : }
10870 : }
10871 : }
10872 :
10873 : /* -------------------------------------------------------------------- */
10874 : /* Create a new EXTENSION node. */
10875 : /* -------------------------------------------------------------------- */
10876 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10877 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10878 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10879 :
10880 14 : poNode->AddChild(poAuthNode);
10881 :
10882 14 : return OGRERR_NONE;
10883 : }
10884 :
10885 : /************************************************************************/
10886 : /* OSRCleanup() */
10887 : /************************************************************************/
10888 :
10889 : static void CleanupSRSWGS84Mutex();
10890 :
10891 : /**
10892 : * \brief Cleanup cached SRS related memory.
10893 : *
10894 : * This function will attempt to cleanup any cache spatial reference
10895 : * related information, such as cached tables of coordinate systems.
10896 : *
10897 : * This function should not be called concurrently with any other GDAL/OGR
10898 : * function. It is meant at being called once before process termination
10899 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10900 : * thread-specific resources before thread termination.
10901 : */
10902 1141 : void OSRCleanup(void)
10903 :
10904 : {
10905 1141 : OGRCTDumpStatistics();
10906 1141 : CSVDeaccess(nullptr);
10907 1141 : CleanupSRSWGS84Mutex();
10908 1141 : OSRCTCleanCache();
10909 1141 : OSRCleanupTLSContext();
10910 1141 : }
10911 :
10912 : /************************************************************************/
10913 : /* GetAxesCount() */
10914 : /************************************************************************/
10915 :
10916 : /**
10917 : * \brief Return the number of axis of the coordinate system of the CRS.
10918 : *
10919 : * @since GDAL 3.0
10920 : */
10921 38924 : int OGRSpatialReference::GetAxesCount() const
10922 : {
10923 77848 : TAKE_OPTIONAL_LOCK();
10924 :
10925 38924 : int axisCount = 0;
10926 38924 : d->refreshProjObj();
10927 38924 : if (d->m_pj_crs == nullptr)
10928 : {
10929 0 : return 0;
10930 : }
10931 38924 : d->demoteFromBoundCRS();
10932 38924 : auto ctxt = d->getPROJContext();
10933 38924 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10934 : {
10935 38 : for (int i = 0;; i++)
10936 : {
10937 114 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10938 114 : if (!subCRS)
10939 38 : break;
10940 76 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10941 : {
10942 18 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10943 18 : if (baseCRS)
10944 : {
10945 18 : proj_destroy(subCRS);
10946 18 : subCRS = baseCRS;
10947 : }
10948 : }
10949 76 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10950 76 : if (cs)
10951 : {
10952 76 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10953 76 : proj_destroy(cs);
10954 : }
10955 76 : proj_destroy(subCRS);
10956 76 : }
10957 : }
10958 : else
10959 : {
10960 38886 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10961 38886 : if (cs)
10962 : {
10963 38886 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10964 38886 : proj_destroy(cs);
10965 : }
10966 : }
10967 38924 : d->undoDemoteFromBoundCRS();
10968 38924 : return axisCount;
10969 : }
10970 :
10971 : /************************************************************************/
10972 : /* OSRGetAxesCount() */
10973 : /************************************************************************/
10974 :
10975 : /**
10976 : * \brief Return the number of axis of the coordinate system of the CRS.
10977 : *
10978 : * This method is the equivalent of the C++ method
10979 : * OGRSpatialReference::GetAxesCount()
10980 : *
10981 : * @since GDAL 3.1
10982 : */
10983 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10984 :
10985 : {
10986 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10987 :
10988 6 : return ToPointer(hSRS)->GetAxesCount();
10989 : }
10990 :
10991 : /************************************************************************/
10992 : /* GetAxis() */
10993 : /************************************************************************/
10994 :
10995 : /**
10996 : * \brief Fetch the orientation of one axis.
10997 : *
10998 : * Fetches the request axis (iAxis - zero based) from the
10999 : * indicated portion of the coordinate system (pszTargetKey) which
11000 : * should be either "GEOGCS" or "PROJCS".
11001 : *
11002 : * No CPLError is issued on routine failures (such as not finding the AXIS).
11003 : *
11004 : * This method is equivalent to the C function OSRGetAxis().
11005 : *
11006 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11007 : * "GEOGCS").
11008 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11009 : * @param peOrientation location into which to place the fetch orientation, may
11010 : * be NULL.
11011 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11012 : * factor. May be NULL. Only set if pszTargetKey == NULL
11013 : *
11014 : * @return the name of the axis or NULL on failure.
11015 : */
11016 :
11017 8678 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11018 : OGRAxisOrientation *peOrientation,
11019 : double *pdfConvUnit) const
11020 :
11021 : {
11022 17356 : TAKE_OPTIONAL_LOCK();
11023 :
11024 8678 : if (peOrientation != nullptr)
11025 8569 : *peOrientation = OAO_Other;
11026 8678 : if (pdfConvUnit != nullptr)
11027 101 : *pdfConvUnit = 0;
11028 :
11029 8678 : d->refreshProjObj();
11030 8678 : if (d->m_pj_crs == nullptr)
11031 : {
11032 3 : return nullptr;
11033 : }
11034 :
11035 8675 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11036 8675 : if (pszTargetKey == nullptr && iAxis <= 2)
11037 : {
11038 8675 : auto ctxt = d->getPROJContext();
11039 :
11040 8675 : int iAxisModified = iAxis;
11041 :
11042 8675 : d->demoteFromBoundCRS();
11043 :
11044 8675 : PJ *cs = nullptr;
11045 8675 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11046 : {
11047 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11048 134 : if (horizCRS)
11049 : {
11050 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11051 : {
11052 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11053 6 : if (baseCRS)
11054 : {
11055 6 : proj_destroy(horizCRS);
11056 6 : horizCRS = baseCRS;
11057 : }
11058 : }
11059 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11060 134 : proj_destroy(horizCRS);
11061 134 : if (cs)
11062 : {
11063 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11064 : {
11065 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11066 44 : proj_destroy(cs);
11067 44 : cs = nullptr;
11068 : }
11069 : }
11070 : }
11071 :
11072 134 : if (cs == nullptr)
11073 : {
11074 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11075 44 : if (vertCRS)
11076 : {
11077 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11078 : {
11079 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11080 30 : if (baseCRS)
11081 : {
11082 30 : proj_destroy(vertCRS);
11083 30 : vertCRS = baseCRS;
11084 : }
11085 : }
11086 :
11087 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11088 44 : proj_destroy(vertCRS);
11089 : }
11090 : }
11091 : }
11092 : else
11093 : {
11094 8541 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11095 : }
11096 :
11097 8675 : if (cs)
11098 : {
11099 8675 : const char *pszName = nullptr;
11100 8675 : const char *pszOrientation = nullptr;
11101 8675 : double dfConvFactor = 0.0;
11102 8675 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11103 : &pszOrientation, &dfConvFactor, nullptr,
11104 : nullptr, nullptr);
11105 :
11106 8675 : if (pdfConvUnit != nullptr)
11107 : {
11108 101 : *pdfConvUnit = dfConvFactor;
11109 : }
11110 :
11111 8675 : if (pszName && pszOrientation)
11112 : {
11113 8675 : d->m_osAxisName[iAxis] = pszName;
11114 8675 : if (peOrientation)
11115 : {
11116 8566 : if (EQUAL(pszOrientation, "NORTH"))
11117 5465 : *peOrientation = OAO_North;
11118 3101 : else if (EQUAL(pszOrientation, "EAST"))
11119 3023 : *peOrientation = OAO_East;
11120 78 : else if (EQUAL(pszOrientation, "SOUTH"))
11121 67 : *peOrientation = OAO_South;
11122 11 : else if (EQUAL(pszOrientation, "WEST"))
11123 0 : *peOrientation = OAO_West;
11124 11 : else if (EQUAL(pszOrientation, "UP"))
11125 1 : *peOrientation = OAO_Up;
11126 10 : else if (EQUAL(pszOrientation, "DOWN"))
11127 0 : *peOrientation = OAO_Down;
11128 : }
11129 8675 : proj_destroy(cs);
11130 8675 : d->undoDemoteFromBoundCRS();
11131 8675 : return d->m_osAxisName[iAxis].c_str();
11132 : }
11133 0 : proj_destroy(cs);
11134 : }
11135 0 : d->undoDemoteFromBoundCRS();
11136 : }
11137 :
11138 : /* -------------------------------------------------------------------- */
11139 : /* Find the target node. */
11140 : /* -------------------------------------------------------------------- */
11141 0 : const OGR_SRSNode *poNode = nullptr;
11142 :
11143 0 : if (pszTargetKey == nullptr)
11144 0 : poNode = GetRoot();
11145 : else
11146 0 : poNode = GetAttrNode(pszTargetKey);
11147 :
11148 0 : if (poNode == nullptr)
11149 0 : return nullptr;
11150 :
11151 : /* -------------------------------------------------------------------- */
11152 : /* Find desired child AXIS. */
11153 : /* -------------------------------------------------------------------- */
11154 0 : const OGR_SRSNode *poAxis = nullptr;
11155 0 : const int nChildCount = poNode->GetChildCount();
11156 :
11157 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11158 : {
11159 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11160 :
11161 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11162 0 : continue;
11163 :
11164 0 : if (iAxis == 0)
11165 : {
11166 0 : poAxis = poChild;
11167 0 : break;
11168 : }
11169 0 : iAxis--;
11170 : }
11171 :
11172 0 : if (poAxis == nullptr)
11173 0 : return nullptr;
11174 :
11175 0 : if (poAxis->GetChildCount() < 2)
11176 0 : return nullptr;
11177 :
11178 : /* -------------------------------------------------------------------- */
11179 : /* Extract name and orientation if possible. */
11180 : /* -------------------------------------------------------------------- */
11181 0 : if (peOrientation != nullptr)
11182 : {
11183 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11184 :
11185 0 : if (EQUAL(pszOrientation, "NORTH"))
11186 0 : *peOrientation = OAO_North;
11187 0 : else if (EQUAL(pszOrientation, "EAST"))
11188 0 : *peOrientation = OAO_East;
11189 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11190 0 : *peOrientation = OAO_South;
11191 0 : else if (EQUAL(pszOrientation, "WEST"))
11192 0 : *peOrientation = OAO_West;
11193 0 : else if (EQUAL(pszOrientation, "UP"))
11194 0 : *peOrientation = OAO_Up;
11195 0 : else if (EQUAL(pszOrientation, "DOWN"))
11196 0 : *peOrientation = OAO_Down;
11197 0 : else if (EQUAL(pszOrientation, "OTHER"))
11198 0 : *peOrientation = OAO_Other;
11199 : else
11200 : {
11201 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11202 : pszOrientation);
11203 : }
11204 : }
11205 :
11206 0 : return poAxis->GetChild(0)->GetValue();
11207 : }
11208 :
11209 : /************************************************************************/
11210 : /* OSRGetAxis() */
11211 : /************************************************************************/
11212 :
11213 : /**
11214 : * \brief Fetch the orientation of one axis.
11215 : *
11216 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11217 : */
11218 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11219 : int iAxis, OGRAxisOrientation *peOrientation)
11220 :
11221 : {
11222 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11223 :
11224 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11225 : }
11226 :
11227 : /************************************************************************/
11228 : /* OSRAxisEnumToName() */
11229 : /************************************************************************/
11230 :
11231 : /**
11232 : * \brief Return the string representation for the OGRAxisOrientation
11233 : * enumeration.
11234 : *
11235 : * For example "NORTH" for OAO_North.
11236 : *
11237 : * @return an internal string
11238 : */
11239 400 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11240 :
11241 : {
11242 400 : if (eOrientation == OAO_North)
11243 200 : return "NORTH";
11244 200 : if (eOrientation == OAO_East)
11245 200 : return "EAST";
11246 0 : if (eOrientation == OAO_South)
11247 0 : return "SOUTH";
11248 0 : if (eOrientation == OAO_West)
11249 0 : return "WEST";
11250 0 : if (eOrientation == OAO_Up)
11251 0 : return "UP";
11252 0 : if (eOrientation == OAO_Down)
11253 0 : return "DOWN";
11254 0 : if (eOrientation == OAO_Other)
11255 0 : return "OTHER";
11256 :
11257 0 : return "UNKNOWN";
11258 : }
11259 :
11260 : /************************************************************************/
11261 : /* SetAxes() */
11262 : /************************************************************************/
11263 :
11264 : /**
11265 : * \brief Set the axes for a coordinate system.
11266 : *
11267 : * Set the names, and orientations of the axes for either a projected
11268 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11269 : *
11270 : * This method is equivalent to the C function OSRSetAxes().
11271 : *
11272 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11273 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11274 : * @param eXAxisOrientation normally OAO_East.
11275 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11276 : * @param eYAxisOrientation normally OAO_North.
11277 : *
11278 : * @return OGRERR_NONE on success or an error code.
11279 : */
11280 :
11281 200 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11282 : const char *pszXAxisName,
11283 : OGRAxisOrientation eXAxisOrientation,
11284 : const char *pszYAxisName,
11285 : OGRAxisOrientation eYAxisOrientation)
11286 :
11287 : {
11288 400 : TAKE_OPTIONAL_LOCK();
11289 :
11290 : /* -------------------------------------------------------------------- */
11291 : /* Find the target node. */
11292 : /* -------------------------------------------------------------------- */
11293 200 : OGR_SRSNode *poNode = nullptr;
11294 :
11295 200 : if (pszTargetKey == nullptr)
11296 200 : poNode = GetRoot();
11297 : else
11298 0 : poNode = GetAttrNode(pszTargetKey);
11299 :
11300 200 : if (poNode == nullptr)
11301 0 : return OGRERR_FAILURE;
11302 :
11303 : /* -------------------------------------------------------------------- */
11304 : /* Strip any existing AXIS children. */
11305 : /* -------------------------------------------------------------------- */
11306 600 : while (poNode->FindChild("AXIS") >= 0)
11307 400 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11308 :
11309 : /* -------------------------------------------------------------------- */
11310 : /* Insert desired axes */
11311 : /* -------------------------------------------------------------------- */
11312 200 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11313 :
11314 200 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11315 200 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11316 :
11317 200 : poNode->AddChild(poAxis);
11318 :
11319 200 : poAxis = new OGR_SRSNode("AXIS");
11320 :
11321 200 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11322 200 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11323 :
11324 200 : poNode->AddChild(poAxis);
11325 :
11326 200 : return OGRERR_NONE;
11327 : }
11328 :
11329 : /************************************************************************/
11330 : /* OSRSetAxes() */
11331 : /************************************************************************/
11332 : /**
11333 : * \brief Set the axes for a coordinate system.
11334 : *
11335 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11336 : */
11337 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11338 : const char *pszXAxisName,
11339 : OGRAxisOrientation eXAxisOrientation,
11340 : const char *pszYAxisName,
11341 : OGRAxisOrientation eYAxisOrientation)
11342 : {
11343 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11344 :
11345 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11346 : eXAxisOrientation, pszYAxisName,
11347 0 : eYAxisOrientation);
11348 : }
11349 :
11350 : /************************************************************************/
11351 : /* OSRExportToMICoordSys() */
11352 : /************************************************************************/
11353 : /**
11354 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11355 : *
11356 : * This method is the equivalent of the C++ method
11357 : * OGRSpatialReference::exportToMICoordSys
11358 : */
11359 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11360 :
11361 : {
11362 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11363 :
11364 5 : *ppszReturn = nullptr;
11365 :
11366 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11367 : }
11368 :
11369 : /************************************************************************/
11370 : /* exportToMICoordSys() */
11371 : /************************************************************************/
11372 :
11373 : /**
11374 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11375 : *
11376 : * Note that the returned WKT string should be freed with
11377 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11378 : *
11379 : * This method is the same as the C function OSRExportToMICoordSys().
11380 : *
11381 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11382 : * definition will be assigned.
11383 : *
11384 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11385 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11386 : */
11387 :
11388 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11389 :
11390 : {
11391 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11392 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11393 7 : return OGRERR_NONE;
11394 :
11395 0 : return OGRERR_FAILURE;
11396 : }
11397 :
11398 : /************************************************************************/
11399 : /* OSRImportFromMICoordSys() */
11400 : /************************************************************************/
11401 : /**
11402 : * \brief Import Mapinfo style CoordSys definition.
11403 : *
11404 : * This method is the equivalent of the C++ method
11405 : * OGRSpatialReference::importFromMICoordSys
11406 : */
11407 :
11408 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11409 : const char *pszCoordSys)
11410 :
11411 : {
11412 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11413 :
11414 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11415 : }
11416 :
11417 : /************************************************************************/
11418 : /* importFromMICoordSys() */
11419 : /************************************************************************/
11420 :
11421 : /**
11422 : * \brief Import Mapinfo style CoordSys definition.
11423 : *
11424 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11425 : * definition string.
11426 : *
11427 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11428 : *
11429 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11430 : *
11431 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11432 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11433 : */
11434 :
11435 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11436 :
11437 : {
11438 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11439 :
11440 17 : if (poResult == nullptr)
11441 0 : return OGRERR_FAILURE;
11442 :
11443 17 : *this = *poResult;
11444 17 : delete poResult;
11445 :
11446 17 : return OGRERR_NONE;
11447 : }
11448 :
11449 : /************************************************************************/
11450 : /* OSRCalcInvFlattening() */
11451 : /************************************************************************/
11452 :
11453 : /**
11454 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11455 : *
11456 : * @param dfSemiMajor Semi-major axis length.
11457 : * @param dfSemiMinor Semi-minor axis length.
11458 : *
11459 : * @return inverse flattening, or 0 if both axis are equal.
11460 : */
11461 :
11462 8814 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11463 : {
11464 8814 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11465 27 : return 0;
11466 8787 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11467 : {
11468 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11469 : "OSRCalcInvFlattening(): Wrong input values");
11470 0 : return 0;
11471 : }
11472 :
11473 8787 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11474 : }
11475 :
11476 : /************************************************************************/
11477 : /* OSRCalcInvFlattening() */
11478 : /************************************************************************/
11479 :
11480 : /**
11481 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11482 : *
11483 : * @param dfSemiMajor Semi-major axis length.
11484 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11485 : *
11486 : * @return semi-minor axis
11487 : */
11488 :
11489 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11490 : double dfInvFlattening)
11491 : {
11492 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11493 103 : return dfSemiMajor;
11494 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11495 : {
11496 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11497 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11498 0 : return dfSemiMajor;
11499 : }
11500 :
11501 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11502 : }
11503 :
11504 : /************************************************************************/
11505 : /* GetWGS84SRS() */
11506 : /************************************************************************/
11507 :
11508 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11509 : static CPLMutex *hMutex = nullptr;
11510 :
11511 : /**
11512 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11513 : *
11514 : * Note: the instance will have
11515 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11516 : *
11517 : * The reference counter of the returned object is not increased by this
11518 : * operation.
11519 : *
11520 : * @return instance.
11521 : */
11522 :
11523 1003 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11524 : {
11525 1003 : CPLMutexHolderD(&hMutex);
11526 1003 : if (poSRSWGS84 == nullptr)
11527 : {
11528 5 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11529 5 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11530 : }
11531 2006 : return poSRSWGS84;
11532 : }
11533 :
11534 : /************************************************************************/
11535 : /* CleanupSRSWGS84Mutex() */
11536 : /************************************************************************/
11537 :
11538 1141 : static void CleanupSRSWGS84Mutex()
11539 : {
11540 1141 : if (hMutex != nullptr)
11541 : {
11542 3 : poSRSWGS84->Release();
11543 3 : poSRSWGS84 = nullptr;
11544 3 : CPLDestroyMutex(hMutex);
11545 3 : hMutex = nullptr;
11546 : }
11547 1141 : }
11548 :
11549 : /************************************************************************/
11550 : /* OSRImportFromProj4() */
11551 : /************************************************************************/
11552 : /**
11553 : * \brief Import PROJ coordinate string.
11554 : *
11555 : * This function is the same as OGRSpatialReference::importFromProj4().
11556 : */
11557 220 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11558 :
11559 : {
11560 220 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11561 :
11562 220 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11563 : }
11564 :
11565 : /************************************************************************/
11566 : /* importFromProj4() */
11567 : /************************************************************************/
11568 :
11569 : /**
11570 : * \brief Import PROJ coordinate string.
11571 : *
11572 : * The OGRSpatialReference is initialized from the passed PROJs style
11573 : * coordinate system string.
11574 : *
11575 : * Example:
11576 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11577 : *
11578 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11579 : * a legacy syntax that should be avoided in the future. In particular they will
11580 : * result in CRS objects whose axis order might not correspond to the official
11581 : * EPSG axis order.
11582 : *
11583 : * This method is the equivalent of the C function OSRImportFromProj4().
11584 : *
11585 : * @param pszProj4 the PROJ style string.
11586 : *
11587 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11588 : */
11589 :
11590 774 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11591 :
11592 : {
11593 1548 : TAKE_OPTIONAL_LOCK();
11594 :
11595 774 : if (strlen(pszProj4) >= 10000)
11596 : {
11597 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11598 1 : return OGRERR_CORRUPT_DATA;
11599 : }
11600 :
11601 : /* -------------------------------------------------------------------- */
11602 : /* Clear any existing definition. */
11603 : /* -------------------------------------------------------------------- */
11604 773 : Clear();
11605 :
11606 773 : CPLString osProj4(pszProj4);
11607 773 : if (osProj4.find("type=crs") == std::string::npos)
11608 : {
11609 764 : osProj4 += " +type=crs";
11610 : }
11611 :
11612 775 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11613 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11614 : {
11615 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11616 : "+init=epsg:XXXX syntax is deprecated. It might return "
11617 : "a CRS with a non-EPSG compliant axis order.");
11618 : }
11619 773 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11620 773 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11621 773 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11622 773 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11623 : }
11624 :
11625 : /************************************************************************/
11626 : /* OSRExportToProj4() */
11627 : /************************************************************************/
11628 : /**
11629 : * \brief Export coordinate system in PROJ.4 legacy format.
11630 : *
11631 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11632 : * PROJ >= 6 is significantly different from earlier versions. In particular
11633 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11634 : * will be missing most of the time. PROJ strings to encode CRS should be
11635 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11636 : * is the recommended way.
11637 : *
11638 : * This function is the same as OGRSpatialReference::exportToProj4().
11639 : */
11640 311 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11641 : char **ppszReturn)
11642 :
11643 : {
11644 311 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11645 :
11646 311 : *ppszReturn = nullptr;
11647 :
11648 311 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11649 : }
11650 :
11651 : /************************************************************************/
11652 : /* exportToProj4() */
11653 : /************************************************************************/
11654 :
11655 : /**
11656 : * \brief Export coordinate system in PROJ.4 legacy format.
11657 : *
11658 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11659 : * PROJ >= 6 is significantly different from earlier versions. In particular
11660 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11661 : * will be missing most of the time. PROJ strings to encode CRS should be
11662 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11663 : * representation is the recommended way.
11664 : *
11665 : * Converts the loaded coordinate reference system into PROJ format
11666 : * to the extent possible. The string returned in ppszProj4 should be
11667 : * deallocated by the caller with CPLFree() when no longer needed.
11668 : *
11669 : * LOCAL_CS coordinate systems are not translatable. An empty string
11670 : * will be returned along with OGRERR_NONE.
11671 : *
11672 : * Special processing for Transverse Mercator:
11673 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11674 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11675 : * for the tmerc and utm projection methods, rather than the more accurate
11676 : * method.
11677 : *
11678 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11679 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11680 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11681 : * added. This automatic addition may be disabled by setting the
11682 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11683 : *
11684 : * This method is the equivalent of the C function OSRExportToProj4().
11685 : *
11686 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11687 : * will be assigned.
11688 : *
11689 : * @return OGRERR_NONE on success or an error code on failure.
11690 : */
11691 :
11692 1412 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11693 :
11694 : {
11695 : // In the past calling this method was thread-safe, even if we never
11696 : // guaranteed it. Now proj_as_proj_string() will cache the result
11697 : // internally, so this is no longer thread-safe.
11698 2824 : std::lock_guard oLock(d->m_mutex);
11699 :
11700 1412 : d->refreshProjObj();
11701 1412 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11702 : {
11703 4 : *ppszProj4 = CPLStrdup("");
11704 4 : return OGRERR_FAILURE;
11705 : }
11706 :
11707 : // OSR_USE_ETMERC is here just for legacy
11708 1408 : bool bForceApproxTMerc = false;
11709 1408 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11710 1408 : if (pszUseETMERC && pszUseETMERC[0])
11711 : {
11712 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11713 : "OSR_USE_ETMERC is a legacy configuration option, which "
11714 : "now has only effect when set to NO (YES is the default). "
11715 : "Use OSR_USE_APPROX_TMERC=YES instead");
11716 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11717 : }
11718 : else
11719 : {
11720 : const char *pszUseApproxTMERC =
11721 1408 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11722 1408 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11723 : {
11724 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11725 : }
11726 : }
11727 1408 : const char *options[] = {
11728 1408 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11729 :
11730 1408 : const char *projString = proj_as_proj_string(
11731 1408 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11732 :
11733 1408 : PJ *boundCRS = nullptr;
11734 2812 : if (projString &&
11735 1404 : (strstr(projString, "+datum=") == nullptr ||
11736 2822 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11737 513 : CPLTestBool(
11738 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11739 : {
11740 513 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11741 513 : d->getPROJContext(), d->m_pj_crs, true,
11742 513 : strstr(projString, "+datum=") == nullptr);
11743 513 : if (boundCRS)
11744 : {
11745 158 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11746 : PJ_PROJ_4, options);
11747 : }
11748 : }
11749 :
11750 1408 : if (projString == nullptr)
11751 : {
11752 4 : *ppszProj4 = CPLStrdup("");
11753 4 : proj_destroy(boundCRS);
11754 4 : return OGRERR_FAILURE;
11755 : }
11756 1404 : *ppszProj4 = CPLStrdup(projString);
11757 1404 : proj_destroy(boundCRS);
11758 1404 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11759 1404 : if (pszTypeCrs)
11760 1404 : *pszTypeCrs = '\0';
11761 1404 : return OGRERR_NONE;
11762 : }
11763 :
11764 : /************************************************************************/
11765 : /* morphToESRI() */
11766 : /************************************************************************/
11767 : /**
11768 : * \brief Convert in place to ESRI WKT format.
11769 : *
11770 : * The value nodes of this coordinate system are modified in various manners
11771 : * more closely map onto the ESRI concept of WKT format. This includes
11772 : * renaming a variety of projections and arguments, and stripping out
11773 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11774 : *
11775 : * \note Since GDAL 3.0, this function has only user-visible effects at
11776 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11777 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11778 : *
11779 : * This does the same as the C function OSRMorphToESRI().
11780 : *
11781 : * @return OGRERR_NONE unless something goes badly wrong.
11782 : * @deprecated
11783 : */
11784 :
11785 235 : OGRErr OGRSpatialReference::morphToESRI()
11786 :
11787 : {
11788 235 : TAKE_OPTIONAL_LOCK();
11789 :
11790 235 : d->refreshProjObj();
11791 235 : d->setMorphToESRI(true);
11792 :
11793 470 : return OGRERR_NONE;
11794 : }
11795 :
11796 : /************************************************************************/
11797 : /* OSRMorphToESRI() */
11798 : /************************************************************************/
11799 :
11800 : /**
11801 : * \brief Convert in place to ESRI WKT format.
11802 : *
11803 : * This function is the same as the C++ method
11804 : * OGRSpatialReference::morphToESRI().
11805 : */
11806 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11807 :
11808 : {
11809 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11810 :
11811 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11812 : }
11813 :
11814 : /************************************************************************/
11815 : /* morphFromESRI() */
11816 : /************************************************************************/
11817 :
11818 : /**
11819 : * \brief Convert in place from ESRI WKT format.
11820 : *
11821 : * The value notes of this coordinate system are modified in various manners
11822 : * to adhere more closely to the WKT standard. This mostly involves
11823 : * translating a variety of ESRI names for projections, arguments and
11824 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11825 : * translation of EPSG to WKT for the CT specification.
11826 : *
11827 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11828 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11829 : * effect is to undo the effect of a potential prior call to morphToESRI().
11830 : *
11831 : * This does the same as the C function OSRMorphFromESRI().
11832 : *
11833 : * @return OGRERR_NONE unless something goes badly wrong.
11834 : * @deprecated
11835 : */
11836 :
11837 21 : OGRErr OGRSpatialReference::morphFromESRI()
11838 :
11839 : {
11840 21 : TAKE_OPTIONAL_LOCK();
11841 :
11842 21 : d->refreshProjObj();
11843 21 : d->setMorphToESRI(false);
11844 :
11845 42 : return OGRERR_NONE;
11846 : }
11847 :
11848 : /************************************************************************/
11849 : /* OSRMorphFromESRI() */
11850 : /************************************************************************/
11851 :
11852 : /**
11853 : * \brief Convert in place from ESRI WKT format.
11854 : *
11855 : * This function is the same as the C++ method
11856 : * OGRSpatialReference::morphFromESRI().
11857 : */
11858 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11859 :
11860 : {
11861 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11862 :
11863 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11864 : }
11865 :
11866 : /************************************************************************/
11867 : /* FindMatches() */
11868 : /************************************************************************/
11869 :
11870 : /**
11871 : * \brief Try to identify a match between the passed SRS and a related SRS
11872 : * in a catalog.
11873 : *
11874 : * Matching may be partial, or may fail.
11875 : * Returned entries will be sorted by decreasing match confidence (first
11876 : * entry has the highest match confidence).
11877 : *
11878 : * The exact way matching is done may change in future versions. Starting with
11879 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11880 : *
11881 : * This method is the same as OSRFindMatches().
11882 : *
11883 : * @param papszOptions NULL terminated list of options or NULL
11884 : * @param pnEntries Output parameter. Number of values in the returned array.
11885 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11886 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11887 : * indicate the confidence in the match. 100 is the highest confidence level.
11888 : * The array must be freed with CPLFree().
11889 : *
11890 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11891 : * with OSRFreeSRSArray()
11892 : *
11893 : *
11894 : * @see OGRSpatialReference::FindBestMatch()
11895 : */
11896 : OGRSpatialReferenceH *
11897 1514 : OGRSpatialReference::FindMatches(CSLConstList papszOptions, int *pnEntries,
11898 : int **ppanMatchConfidence) const
11899 : {
11900 3028 : TAKE_OPTIONAL_LOCK();
11901 :
11902 1514 : CPL_IGNORE_RET_VAL(papszOptions);
11903 :
11904 1514 : if (pnEntries)
11905 1514 : *pnEntries = 0;
11906 1514 : if (ppanMatchConfidence)
11907 1514 : *ppanMatchConfidence = nullptr;
11908 :
11909 1514 : d->refreshProjObj();
11910 1514 : if (!d->m_pj_crs)
11911 0 : return nullptr;
11912 :
11913 1514 : int *panConfidence = nullptr;
11914 1514 : auto ctxt = d->getPROJContext();
11915 : auto list =
11916 1514 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11917 1514 : if (!list)
11918 0 : return nullptr;
11919 :
11920 1514 : const int nMatches = proj_list_get_count(list);
11921 :
11922 1514 : if (pnEntries)
11923 1514 : *pnEntries = static_cast<int>(nMatches);
11924 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11925 1514 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11926 1514 : if (ppanMatchConfidence)
11927 : {
11928 1514 : *ppanMatchConfidence =
11929 1514 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11930 : }
11931 :
11932 1514 : bool bSortAgain = false;
11933 :
11934 4414 : for (int i = 0; i < nMatches; i++)
11935 : {
11936 2900 : PJ *obj = proj_list_get(ctxt, list, i);
11937 2900 : CPLAssert(obj);
11938 2900 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11939 2900 : poSRS->d->setPjCRS(obj);
11940 2900 : pahRet[i] = ToHandle(poSRS);
11941 :
11942 : // Identify matches that only differ by axis order
11943 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11944 2918 : poSRS->GetAxesCount() == 2 &&
11945 2909 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11946 : {
11947 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11948 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11949 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11950 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11951 9 : GetAxis(nullptr, 0, &eThisAxis0);
11952 9 : GetAxis(nullptr, 1, &eThisAxis1);
11953 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11954 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11955 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11956 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11957 : {
11958 : auto pj_crs_normalized =
11959 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11960 9 : if (pj_crs_normalized)
11961 : {
11962 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11963 9 : PJ_COMP_EQUIVALENT))
11964 : {
11965 3 : bSortAgain = true;
11966 3 : panConfidence[i] = 90;
11967 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11968 : }
11969 9 : proj_destroy(pj_crs_normalized);
11970 : }
11971 : }
11972 : }
11973 :
11974 2900 : if (ppanMatchConfidence)
11975 2900 : (*ppanMatchConfidence)[i] = panConfidence[i];
11976 : }
11977 :
11978 1514 : if (bSortAgain)
11979 : {
11980 3 : std::vector<int> anIndices;
11981 12 : for (int i = 0; i < nMatches; ++i)
11982 9 : anIndices.push_back(i);
11983 :
11984 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11985 9 : [&panConfidence](int i, int j)
11986 9 : { return panConfidence[i] > panConfidence[j]; });
11987 :
11988 : OGRSpatialReferenceH *pahRetSorted =
11989 : static_cast<OGRSpatialReferenceH *>(
11990 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11991 12 : for (int i = 0; i < nMatches; ++i)
11992 : {
11993 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11994 9 : if (ppanMatchConfidence)
11995 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11996 : }
11997 3 : CPLFree(pahRet);
11998 3 : pahRet = pahRetSorted;
11999 : }
12000 :
12001 1514 : pahRet[nMatches] = nullptr;
12002 1514 : proj_list_destroy(list);
12003 1514 : proj_int_list_destroy(panConfidence);
12004 :
12005 1514 : return pahRet;
12006 : }
12007 :
12008 : /************************************************************************/
12009 : /* importFromEPSGA() */
12010 : /************************************************************************/
12011 :
12012 : /**
12013 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12014 : * code.
12015 : *
12016 : * This method will initialize the spatial reference based on the
12017 : * passed in EPSG CRS code found in the PROJ database.
12018 : *
12019 : * Since GDAL 3.0, this method is identical to importFromEPSG().
12020 : *
12021 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12022 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12023 : * such method available for the CRS. This behavior might not always be
12024 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12025 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12026 : * The AddGuessedTOWGS84() method can also be used for that purpose.
12027 : *
12028 : * The method will also by default substitute a deprecated EPSG code by its
12029 : * non-deprecated replacement. If this behavior is not desired, the
12030 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12031 : *
12032 : * This method is the same as the C function OSRImportFromEPSGA().
12033 : *
12034 : * @param nCode a CRS code.
12035 : *
12036 : * @return OGRERR_NONE on success, or an error code on failure.
12037 : */
12038 :
12039 47812 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12040 :
12041 : {
12042 95624 : TAKE_OPTIONAL_LOCK();
12043 :
12044 47812 : Clear();
12045 :
12046 : const char *pszUseNonDeprecated =
12047 47812 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12048 : const bool bUseNonDeprecated =
12049 47812 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12050 47812 : const bool bAddTOWGS84 = CPLTestBool(
12051 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12052 47812 : auto tlsCache = OSRGetProjTLSCache();
12053 47812 : if (tlsCache)
12054 : {
12055 : auto cachedObj =
12056 47812 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12057 47812 : if (cachedObj)
12058 : {
12059 37509 : d->setPjCRS(cachedObj);
12060 37509 : return OGRERR_NONE;
12061 : }
12062 : }
12063 :
12064 20606 : CPLString osCode;
12065 10303 : osCode.Printf("%d", nCode);
12066 : PJ *obj;
12067 10303 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12068 10303 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12069 : {
12070 10298 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12071 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12072 : nullptr);
12073 10298 : if (!obj)
12074 : {
12075 24 : return OGRERR_FAILURE;
12076 : }
12077 : }
12078 : else
12079 : {
12080 : // Likely to be an ESRI CRS...
12081 5 : CPLErr eLastErrorType = CE_None;
12082 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12083 5 : std::string osLastErrorMsg;
12084 5 : bool bIsESRI = false;
12085 : {
12086 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12087 5 : CPLErrorReset();
12088 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12089 : osCode.c_str(), PJ_CATEGORY_CRS,
12090 : true, nullptr);
12091 5 : if (!obj)
12092 : {
12093 2 : eLastErrorType = CPLGetLastErrorType();
12094 2 : eLastErrorNum = CPLGetLastErrorNo();
12095 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12096 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12097 : osCode.c_str(), PJ_CATEGORY_CRS,
12098 : true, nullptr);
12099 2 : if (obj)
12100 1 : bIsESRI = true;
12101 : }
12102 : }
12103 5 : if (!obj)
12104 : {
12105 1 : if (eLastErrorType != CE_None)
12106 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12107 : osLastErrorMsg.c_str());
12108 1 : return OGRERR_FAILURE;
12109 : }
12110 4 : if (bIsESRI)
12111 : {
12112 1 : CPLError(CE_Warning, CPLE_AppDefined,
12113 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12114 : "Assuming ESRI:%d was meant",
12115 : nCode, nCode, nCode);
12116 : }
12117 : }
12118 :
12119 10278 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12120 : {
12121 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12122 410 : if (list)
12123 : {
12124 410 : const auto count = proj_list_get_count(list);
12125 410 : if (count == 1)
12126 : {
12127 : auto nonDeprecated =
12128 359 : proj_list_get(d->getPROJContext(), list, 0);
12129 359 : if (nonDeprecated)
12130 : {
12131 359 : if (pszUseNonDeprecated == nullptr)
12132 : {
12133 : const char *pszNewAuth =
12134 359 : proj_get_id_auth_name(nonDeprecated, 0);
12135 : const char *pszNewCode =
12136 359 : proj_get_id_code(nonDeprecated, 0);
12137 359 : CPLError(CE_Warning, CPLE_AppDefined,
12138 : "CRS EPSG:%d is deprecated. "
12139 : "Its non-deprecated replacement %s:%s "
12140 : "will be used instead. "
12141 : "To use the original CRS, set the "
12142 : "OSR_USE_NON_DEPRECATED "
12143 : "configuration option to NO.",
12144 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12145 : pszNewCode ? pszNewCode : "(null)");
12146 : }
12147 359 : proj_destroy(obj);
12148 359 : obj = nonDeprecated;
12149 : }
12150 : }
12151 : }
12152 410 : proj_list_destroy(list);
12153 : }
12154 :
12155 10278 : if (bAddTOWGS84)
12156 : {
12157 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12158 : obj, nullptr);
12159 1 : if (boundCRS)
12160 : {
12161 1 : proj_destroy(obj);
12162 1 : obj = boundCRS;
12163 : }
12164 : }
12165 :
12166 10278 : d->setPjCRS(obj);
12167 :
12168 10278 : if (tlsCache)
12169 : {
12170 10278 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12171 : obj);
12172 : }
12173 :
12174 10278 : return OGRERR_NONE;
12175 : }
12176 :
12177 : /************************************************************************/
12178 : /* AddGuessedTOWGS84() */
12179 : /************************************************************************/
12180 :
12181 : /**
12182 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12183 : * to WGS84.
12184 : *
12185 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12186 : * to WGS84 when there is one and only one such method available for the CRS.
12187 : * Note: this is more restrictive to how GDAL < 3 worked.
12188 : *
12189 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12190 : *
12191 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12192 : * already a transformation to WGS84 or none matching could be found).
12193 : *
12194 : * @since GDAL 3.0.3
12195 : */
12196 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12197 : {
12198 36 : TAKE_OPTIONAL_LOCK();
12199 :
12200 18 : d->refreshProjObj();
12201 18 : if (!d->m_pj_crs)
12202 0 : return OGRERR_FAILURE;
12203 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12204 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12205 18 : if (!boundCRS)
12206 : {
12207 0 : return OGRERR_FAILURE;
12208 : }
12209 18 : d->setPjCRS(boundCRS);
12210 18 : return OGRERR_NONE;
12211 : }
12212 :
12213 : /************************************************************************/
12214 : /* OSRImportFromEPSGA() */
12215 : /************************************************************************/
12216 :
12217 : /**
12218 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12219 : * to WGS84.
12220 : *
12221 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12222 : *
12223 : * @since GDAL 3.0.3
12224 : */
12225 :
12226 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12227 :
12228 : {
12229 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12230 :
12231 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12232 : }
12233 :
12234 : /************************************************************************/
12235 : /* OSRImportFromEPSGA() */
12236 : /************************************************************************/
12237 :
12238 : /**
12239 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12240 : * code.
12241 : *
12242 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12243 : */
12244 :
12245 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12246 :
12247 : {
12248 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12249 :
12250 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12251 : }
12252 :
12253 : /************************************************************************/
12254 : /* importFromEPSG() */
12255 : /************************************************************************/
12256 :
12257 : /**
12258 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12259 : * code.
12260 : *
12261 : * This method will initialize the spatial reference based on the
12262 : * passed in EPSG CRS code found in the PROJ database.
12263 : *
12264 : * This method is the same as the C function OSRImportFromEPSG().
12265 : *
12266 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12267 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12268 : * such method available for the CRS. This behavior might not always be
12269 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12270 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12271 : *
12272 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12273 : *
12274 : * @return OGRERR_NONE on success, or an error code on failure.
12275 : */
12276 :
12277 41957 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12278 :
12279 : {
12280 41957 : return importFromEPSGA(nCode);
12281 : }
12282 :
12283 : /************************************************************************/
12284 : /* OSRImportFromEPSG() */
12285 : /************************************************************************/
12286 :
12287 : /**
12288 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12289 : * code.
12290 : *
12291 : * This function is the same as OGRSpatialReference::importFromEPSG().
12292 : */
12293 :
12294 1515 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12295 :
12296 : {
12297 1515 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12298 :
12299 1515 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12300 : }
12301 :
12302 : /************************************************************************/
12303 : /* EPSGTreatsAsLatLong() */
12304 : /************************************************************************/
12305 :
12306 : /**
12307 : * \brief This method returns TRUE if this geographic coordinate
12308 : * system should be treated as having lat/long coordinate ordering.
12309 : *
12310 : * Currently this returns TRUE for all geographic coordinate systems
12311 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12312 : * also checked that the CRS had belonged to EPSG authority, but this check
12313 : * has now been removed).
12314 : *
12315 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12316 : * geographic CRS imported with importFromEPSG() would cause this method to
12317 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12318 : * is now equivalent to importFromEPSGA().
12319 : *
12320 : * FALSE will be returned for all coordinate systems that are not geographic,
12321 : * or whose axes ordering is not latitude, longitude.
12322 : *
12323 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12324 : *
12325 : * @return TRUE or FALSE.
12326 : */
12327 :
12328 1188 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12329 :
12330 : {
12331 2376 : TAKE_OPTIONAL_LOCK();
12332 :
12333 1188 : if (!IsGeographic())
12334 846 : return FALSE;
12335 :
12336 342 : d->demoteFromBoundCRS();
12337 :
12338 342 : bool ret = false;
12339 342 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12340 : {
12341 : auto horizCRS =
12342 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12343 3 : if (horizCRS)
12344 : {
12345 : auto cs =
12346 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12347 3 : if (cs)
12348 : {
12349 3 : const char *pszDirection = nullptr;
12350 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12351 : nullptr, &pszDirection, nullptr,
12352 3 : nullptr, nullptr, nullptr))
12353 : {
12354 3 : if (EQUAL(pszDirection, "north"))
12355 : {
12356 3 : ret = true;
12357 : }
12358 : }
12359 :
12360 3 : proj_destroy(cs);
12361 : }
12362 :
12363 3 : proj_destroy(horizCRS);
12364 : }
12365 : }
12366 : else
12367 : {
12368 : auto cs =
12369 339 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12370 339 : if (cs)
12371 : {
12372 339 : const char *pszDirection = nullptr;
12373 339 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12374 : nullptr, &pszDirection, nullptr, nullptr,
12375 339 : nullptr, nullptr))
12376 : {
12377 339 : if (EQUAL(pszDirection, "north"))
12378 : {
12379 292 : ret = true;
12380 : }
12381 : }
12382 :
12383 339 : proj_destroy(cs);
12384 : }
12385 : }
12386 342 : d->undoDemoteFromBoundCRS();
12387 :
12388 342 : return ret;
12389 : }
12390 :
12391 : /************************************************************************/
12392 : /* OSREPSGTreatsAsLatLong() */
12393 : /************************************************************************/
12394 :
12395 : /**
12396 : * \brief This function returns TRUE if this geographic coordinate
12397 : * system should be treated as having lat/long coordinate ordering.
12398 : *
12399 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12400 : */
12401 :
12402 208 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12403 :
12404 : {
12405 208 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12406 :
12407 208 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12408 : }
12409 :
12410 : /************************************************************************/
12411 : /* EPSGTreatsAsNorthingEasting() */
12412 : /************************************************************************/
12413 :
12414 : /**
12415 : * \brief This method returns TRUE if this projected coordinate
12416 : * system should be treated as having northing/easting coordinate ordering.
12417 : *
12418 : * Currently this returns TRUE for all projected coordinate systems
12419 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12420 : * also checked that the CRS had belonged to EPSG authority, but this check
12421 : * has now been removed).
12422 : *
12423 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12424 : * projected CRS with northing, easting axis order imported with
12425 : * importFromEPSG() would cause this method to
12426 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12427 : * is now equivalent to importFromEPSGA().
12428 : *
12429 : * FALSE will be returned for all coordinate systems that are not projected,
12430 : * or whose axes ordering is not northing, easting.
12431 : *
12432 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12433 : *
12434 : * @return TRUE or FALSE.
12435 : *
12436 : */
12437 :
12438 933 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12439 :
12440 : {
12441 1866 : TAKE_OPTIONAL_LOCK();
12442 :
12443 933 : if (!IsProjected())
12444 49 : return FALSE;
12445 :
12446 884 : d->demoteFromBoundCRS();
12447 : PJ *projCRS;
12448 884 : const auto ctxt = d->getPROJContext();
12449 884 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12450 : {
12451 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12452 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12453 : {
12454 0 : d->undoDemoteFromBoundCRS();
12455 0 : proj_destroy(projCRS);
12456 0 : return FALSE;
12457 : }
12458 : }
12459 : else
12460 : {
12461 880 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12462 : }
12463 :
12464 884 : bool ret = false;
12465 884 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12466 884 : proj_destroy(projCRS);
12467 884 : d->undoDemoteFromBoundCRS();
12468 :
12469 884 : if (cs)
12470 : {
12471 884 : ret = isNorthEastAxisOrder(ctxt, cs);
12472 884 : proj_destroy(cs);
12473 : }
12474 :
12475 884 : return ret;
12476 : }
12477 :
12478 : /************************************************************************/
12479 : /* OSREPSGTreatsAsNorthingEasting() */
12480 : /************************************************************************/
12481 :
12482 : /**
12483 : * \brief This function returns TRUE if this projected coordinate
12484 : * system should be treated as having northing/easting coordinate ordering.
12485 : *
12486 : * This function is the same as
12487 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12488 : *
12489 : */
12490 :
12491 215 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12492 :
12493 : {
12494 215 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12495 :
12496 215 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12497 : }
12498 :
12499 : /************************************************************************/
12500 : /* ImportFromESRIWisconsinWKT() */
12501 : /* */
12502 : /* Search a ESRI State Plane WKT and import it. */
12503 : /************************************************************************/
12504 :
12505 : // This is only used by the HFA driver and somewhat dubious we really need that
12506 : // Coming from an old ESRI merge
12507 :
12508 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12509 : double centralMeridian,
12510 : double latOfOrigin,
12511 : const char *unitsName,
12512 : const char *crsName)
12513 : {
12514 2 : TAKE_OPTIONAL_LOCK();
12515 :
12516 1 : if (centralMeridian < -93 || centralMeridian > -87)
12517 0 : return OGRERR_FAILURE;
12518 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12519 0 : return OGRERR_FAILURE;
12520 :
12521 : // If the CS name is known.
12522 1 : if (!prjName && !unitsName && crsName)
12523 : {
12524 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12525 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12526 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12527 0 : if (list)
12528 : {
12529 0 : if (proj_list_get_count(list) == 1)
12530 : {
12531 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12532 0 : if (crs)
12533 : {
12534 0 : Clear();
12535 0 : d->setPjCRS(crs);
12536 0 : proj_list_destroy(list);
12537 0 : return OGRERR_NONE;
12538 : }
12539 : }
12540 0 : proj_list_destroy(list);
12541 : }
12542 0 : return OGRERR_FAILURE;
12543 : }
12544 :
12545 1 : if (prjName == nullptr || unitsName == nullptr)
12546 : {
12547 0 : return OGRERR_FAILURE;
12548 : }
12549 :
12550 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12551 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12552 : "NAD_1983_HARN_WISCRS_", &type, 1,
12553 : true, 0, nullptr);
12554 1 : if (list)
12555 : {
12556 1 : const auto listSize = proj_list_get_count(list);
12557 8 : for (int i = 0; i < listSize; i++)
12558 : {
12559 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12560 8 : if (!crs)
12561 : {
12562 7 : continue;
12563 : }
12564 :
12565 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12566 8 : if (!conv)
12567 : {
12568 0 : proj_destroy(crs);
12569 0 : continue;
12570 : }
12571 8 : const char *pszMethodCode = nullptr;
12572 8 : proj_coordoperation_get_method_info(
12573 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12574 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12575 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12576 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12577 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12578 : nMethodCode ==
12579 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12580 : {
12581 3 : proj_destroy(crs);
12582 3 : proj_destroy(conv);
12583 3 : continue;
12584 : }
12585 :
12586 : auto coordSys =
12587 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12588 5 : if (!coordSys)
12589 : {
12590 0 : proj_destroy(crs);
12591 0 : proj_destroy(conv);
12592 0 : continue;
12593 : }
12594 :
12595 5 : double dfConvFactor = 0.0;
12596 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12597 : nullptr, nullptr, &dfConvFactor, nullptr,
12598 : nullptr, nullptr);
12599 5 : proj_destroy(coordSys);
12600 :
12601 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12602 1 : (!EQUAL(unitsName, "meters") &&
12603 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12604 : 1e-10))
12605 : {
12606 4 : proj_destroy(crs);
12607 4 : proj_destroy(conv);
12608 4 : continue;
12609 : }
12610 :
12611 1 : int idx_lat = proj_coordoperation_get_param_index(
12612 : d->getPROJContext(), conv,
12613 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12614 1 : double valueLat = -1000;
12615 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12616 : nullptr, nullptr, nullptr, &valueLat,
12617 : nullptr, nullptr, nullptr, nullptr,
12618 : nullptr, nullptr);
12619 1 : int idx_lon = proj_coordoperation_get_param_index(
12620 : d->getPROJContext(), conv,
12621 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12622 1 : double valueLong = -1000;
12623 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12624 : nullptr, nullptr, nullptr, &valueLong,
12625 : nullptr, nullptr, nullptr, nullptr,
12626 : nullptr, nullptr);
12627 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12628 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12629 : {
12630 1 : Clear();
12631 1 : d->setPjCRS(crs);
12632 1 : proj_list_destroy(list);
12633 1 : proj_destroy(conv);
12634 1 : return OGRERR_NONE;
12635 : }
12636 :
12637 0 : proj_destroy(crs);
12638 0 : proj_destroy(conv);
12639 : }
12640 0 : proj_list_destroy(list);
12641 : }
12642 :
12643 0 : return OGRERR_FAILURE;
12644 : }
12645 :
12646 : /************************************************************************/
12647 : /* GetAxisMappingStrategy() */
12648 : /************************************************************************/
12649 :
12650 : /** \brief Return the data axis to CRS axis mapping strategy.
12651 : *
12652 : * <ul>
12653 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12654 : * lat/long order, the data will still be long/lat ordered. Similarly for
12655 : * a projected CRS with northing/easting order, the data will still be
12656 : * easting/northing ordered.
12657 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12658 : * the CRS axis.
12659 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12660 : * SetDataAxisToSRSAxisMapping()
12661 : * </ul>
12662 : * @return the data axis to CRS axis mapping strategy.
12663 : * @since GDAL 3.0
12664 : */
12665 93 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12666 : {
12667 93 : TAKE_OPTIONAL_LOCK();
12668 :
12669 186 : return d->m_axisMappingStrategy;
12670 : }
12671 :
12672 : /************************************************************************/
12673 : /* OSRGetAxisMappingStrategy() */
12674 : /************************************************************************/
12675 :
12676 : /** \brief Return the data axis to CRS axis mapping strategy.
12677 : *
12678 : * See OGRSpatialReference::GetAxisMappingStrategy()
12679 : * @since GDAL 3.0
12680 : */
12681 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12682 : {
12683 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12684 :
12685 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12686 : }
12687 :
12688 : /************************************************************************/
12689 : /* SetAxisMappingStrategy() */
12690 : /************************************************************************/
12691 :
12692 : /** \brief Set the data axis to CRS axis mapping strategy.
12693 : *
12694 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12695 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12696 : * later being the default value when the option is not set) to control the
12697 : * value of the data axis to CRS axis mapping strategy when a
12698 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12699 : * override this default value.
12700 : *
12701 : * See OGRSpatialReference::GetAxisMappingStrategy()
12702 : * @since GDAL 3.0
12703 : */
12704 92946 : void OGRSpatialReference::SetAxisMappingStrategy(
12705 : OSRAxisMappingStrategy strategy)
12706 : {
12707 185892 : TAKE_OPTIONAL_LOCK();
12708 :
12709 92946 : d->m_axisMappingStrategy = strategy;
12710 92946 : d->refreshAxisMapping();
12711 92946 : }
12712 :
12713 : /************************************************************************/
12714 : /* OSRSetAxisMappingStrategy() */
12715 : /************************************************************************/
12716 :
12717 : /** \brief Set the data axis to CRS axis mapping strategy.
12718 : *
12719 : * See OGRSpatialReference::SetAxisMappingStrategy()
12720 : * @since GDAL 3.0
12721 : */
12722 813 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12723 : OSRAxisMappingStrategy strategy)
12724 : {
12725 813 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12726 :
12727 813 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12728 : }
12729 :
12730 : /************************************************************************/
12731 : /* GetDataAxisToSRSAxisMapping() */
12732 : /************************************************************************/
12733 :
12734 : /** \brief Return the data axis to SRS axis mapping.
12735 : *
12736 : * The number of elements of the vector will be the number of axis of the CRS.
12737 : * Values start at 1.
12738 : *
12739 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12740 : * for the first axis of the CRS.
12741 : *
12742 : * @since GDAL 3.0
12743 : */
12744 17373000 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12745 : {
12746 17373000 : TAKE_OPTIONAL_LOCK();
12747 :
12748 34746000 : return d->m_axisMapping;
12749 : }
12750 :
12751 : /************************************************************************/
12752 : /* OSRGetDataAxisToSRSAxisMapping() */
12753 : /************************************************************************/
12754 :
12755 : /** \brief Return the data axis to SRS axis mapping.
12756 : *
12757 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12758 : *
12759 : * @since GDAL 3.0
12760 : */
12761 105 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12762 : int *pnCount)
12763 : {
12764 105 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12765 105 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12766 :
12767 : const auto &v =
12768 105 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12769 105 : *pnCount = static_cast<int>(v.size());
12770 105 : return v.data();
12771 : }
12772 :
12773 : /************************************************************************/
12774 : /* SetDataAxisToSRSAxisMapping() */
12775 : /************************************************************************/
12776 :
12777 : /** \brief Set a custom data axis to CRS axis mapping.
12778 : *
12779 : * The number of elements of the mapping vector should be the number of axis
12780 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12781 : * check that, beyond checking there are at least 2 elements, so that this
12782 : * method and setting the CRS can be done in any order).
12783 : * This is taken into account by OGRCoordinateTransformation to transform the
12784 : * order of coordinates to the order expected by the CRS before
12785 : * transformation, and back to the data order after transformation.
12786 : *
12787 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12788 : * axis of the CRS. A negative value can also be used to ask for a sign
12789 : * reversal during coordinate transformation (to deal with northing vs southing,
12790 : * easting vs westing, heights vs depths).
12791 : *
12792 : * When used with OGRCoordinateTransformation,
12793 : * - the only valid values for mapping[0] (data axis number for the first axis
12794 : * of the CRS) are 1, 2, -1, -2.
12795 : * - the only valid values for mapping[1] (data axis number for the second axis
12796 : * of the CRS) are 1, 2, -1, -2.
12797 : * - the only valid values mapping[2] are 3 or -3.
12798 : * Note: this method does not validate the values of mapping[].
12799 : *
12800 : * mapping=[2,1] typically expresses the inversion of axis between the data
12801 : * axis and the CRS axis for a 2D CRS.
12802 : *
12803 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12804 : *
12805 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12806 : *
12807 : * @param mapping The new data axis to CRS axis mapping.
12808 : *
12809 : * @since GDAL 3.0
12810 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12811 : */
12812 10030 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12813 : const std::vector<int> &mapping)
12814 : {
12815 20060 : TAKE_OPTIONAL_LOCK();
12816 :
12817 10030 : if (mapping.size() < 2)
12818 0 : return OGRERR_FAILURE;
12819 10030 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12820 10030 : d->m_axisMapping = mapping;
12821 10030 : return OGRERR_NONE;
12822 : }
12823 :
12824 : /************************************************************************/
12825 : /* OSRSetDataAxisToSRSAxisMapping() */
12826 : /************************************************************************/
12827 :
12828 : /** \brief Set a custom data axis to CRS axis mapping.
12829 : *
12830 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12831 : *
12832 : * This is the same as the C++ method
12833 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12834 : *
12835 : * @since GDAL 3.1
12836 : */
12837 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12838 : int nMappingSize, const int *panMapping)
12839 : {
12840 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12841 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12842 : OGRERR_FAILURE);
12843 :
12844 15 : if (nMappingSize < 0)
12845 0 : return OGRERR_FAILURE;
12846 :
12847 30 : std::vector<int> mapping(nMappingSize);
12848 15 : if (nMappingSize)
12849 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12850 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12851 15 : mapping);
12852 : }
12853 :
12854 : /************************************************************************/
12855 : /* GetAreaOfUse() */
12856 : /************************************************************************/
12857 :
12858 : /** \brief Return the area of use of the CRS.
12859 : *
12860 : * This method is the same as the OSRGetAreaOfUse() function.
12861 : *
12862 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12863 : * longitude, expressed in degree. Might be NULL. If the returned value is
12864 : * -1000, the bounding box is unknown.
12865 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12866 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12867 : * the bounding box is unknown.
12868 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12869 : * longitude, expressed in degree. Might be NULL. If the returned value is
12870 : * -1000, the bounding box is unknown.
12871 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12872 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12873 : * the bounding box is unknown.
12874 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12875 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12876 : * invalidated by further calls.
12877 : * @return true in case of success
12878 : * @since GDAL 3.0
12879 : */
12880 75 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12881 : double *pdfSouthLatitudeDeg,
12882 : double *pdfEastLongitudeDeg,
12883 : double *pdfNorthLatitudeDeg,
12884 : const char **ppszAreaName) const
12885 : {
12886 150 : TAKE_OPTIONAL_LOCK();
12887 :
12888 75 : d->refreshProjObj();
12889 75 : if (!d->m_pj_crs)
12890 : {
12891 0 : return false;
12892 : }
12893 75 : d->demoteFromBoundCRS();
12894 75 : const char *pszAreaName = nullptr;
12895 75 : int bSuccess = proj_get_area_of_use(
12896 75 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12897 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12898 : &pszAreaName);
12899 75 : d->undoDemoteFromBoundCRS();
12900 75 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12901 75 : if (ppszAreaName)
12902 23 : *ppszAreaName = d->m_osAreaName.c_str();
12903 75 : return CPL_TO_BOOL(bSuccess);
12904 : }
12905 :
12906 : /************************************************************************/
12907 : /* GetAreaOfUse() */
12908 : /************************************************************************/
12909 :
12910 : /** \brief Return the area of use of the CRS.
12911 : *
12912 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12913 : *
12914 : * @since GDAL 3.0
12915 : */
12916 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12917 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12918 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12919 : {
12920 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12921 :
12922 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12923 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12924 1 : pdfNorthLatitudeDeg, ppszAreaName);
12925 : }
12926 :
12927 : /************************************************************************/
12928 : /* OSRGetCRSInfoListFromDatabase() */
12929 : /************************************************************************/
12930 :
12931 : /** \brief Enumerate CRS objects from the database.
12932 : *
12933 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12934 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12935 : *
12936 : * @param pszAuthName Authority name, used to restrict the search.
12937 : * Or NULL for all authorities.
12938 : * @param params Additional criteria. Must be set to NULL for now.
12939 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12940 : * the size of the result list. Might be NULL
12941 : * @return an array of OSRCRSInfo* pointers to be freed with
12942 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12943 : *
12944 : * @since GDAL 3.0
12945 : */
12946 : OSRCRSInfo **
12947 24 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12948 : CPL_UNUSED const OSRCRSListParameters *params,
12949 : int *pnOutResultCount)
12950 : {
12951 24 : int nResultCount = 0;
12952 24 : auto projList = proj_get_crs_info_list_from_database(
12953 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12954 24 : if (pnOutResultCount)
12955 24 : *pnOutResultCount = nResultCount;
12956 24 : if (!projList)
12957 : {
12958 0 : return nullptr;
12959 : }
12960 24 : auto res = new OSRCRSInfo *[nResultCount + 1];
12961 89181 : for (int i = 0; i < nResultCount; i++)
12962 : {
12963 89157 : res[i] = new OSRCRSInfo;
12964 178314 : res[i]->pszAuthName = projList[i]->auth_name
12965 89157 : ? CPLStrdup(projList[i]->auth_name)
12966 : : nullptr;
12967 89157 : res[i]->pszCode =
12968 89157 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12969 89157 : res[i]->pszName =
12970 89157 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12971 89157 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12972 89157 : switch (projList[i]->type)
12973 : {
12974 8727 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12975 8727 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12976 8727 : break;
12977 2811 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12978 2811 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12979 2811 : break;
12980 3066 : case PJ_TYPE_GEOCENTRIC_CRS:
12981 3066 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12982 3066 : break;
12983 67740 : case PJ_TYPE_PROJECTED_CRS:
12984 67740 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12985 67740 : break;
12986 2808 : case PJ_TYPE_VERTICAL_CRS:
12987 2808 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12988 2808 : break;
12989 4005 : case PJ_TYPE_COMPOUND_CRS:
12990 4005 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12991 4005 : break;
12992 0 : default:
12993 0 : break;
12994 : }
12995 89157 : res[i]->bDeprecated = projList[i]->deprecated;
12996 89157 : res[i]->bBboxValid = projList[i]->bbox_valid;
12997 89157 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12998 89157 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12999 89157 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
13000 89157 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
13001 178314 : res[i]->pszAreaName = projList[i]->area_name
13002 89157 : ? CPLStrdup(projList[i]->area_name)
13003 : : nullptr;
13004 89157 : res[i]->pszProjectionMethod =
13005 89157 : projList[i]->projection_method_name
13006 89157 : ? CPLStrdup(projList[i]->projection_method_name)
13007 : : nullptr;
13008 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
13009 : res[i]->pszCelestialBodyName =
13010 : projList[i]->celestial_body_name
13011 : ? CPLStrdup(projList[i]->celestial_body_name)
13012 : : nullptr;
13013 : #else
13014 89157 : res[i]->pszCelestialBodyName =
13015 89157 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13016 178314 : ? CPLStrdup("Earth")
13017 : : nullptr;
13018 : #endif
13019 : }
13020 24 : res[nResultCount] = nullptr;
13021 24 : proj_crs_info_list_destroy(projList);
13022 24 : return res;
13023 : }
13024 :
13025 : /************************************************************************/
13026 : /* OSRDestroyCRSInfoList() */
13027 : /************************************************************************/
13028 :
13029 : /** \brief Destroy the result returned by
13030 : * OSRGetCRSInfoListFromDatabase().
13031 : *
13032 : * @since GDAL 3.0
13033 : */
13034 24 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13035 : {
13036 24 : if (list)
13037 : {
13038 89181 : for (int i = 0; list[i] != nullptr; i++)
13039 : {
13040 89157 : CPLFree(list[i]->pszAuthName);
13041 89157 : CPLFree(list[i]->pszCode);
13042 89157 : CPLFree(list[i]->pszName);
13043 89157 : CPLFree(list[i]->pszAreaName);
13044 89157 : CPLFree(list[i]->pszProjectionMethod);
13045 89157 : CPLFree(list[i]->pszCelestialBodyName);
13046 89157 : delete list[i];
13047 : }
13048 24 : delete[] list;
13049 : }
13050 24 : }
13051 :
13052 : /************************************************************************/
13053 : /* OSRGetAuthorityListFromDatabase() */
13054 : /************************************************************************/
13055 :
13056 : /** \brief Return the list of CRS authorities used in the PROJ database.
13057 : *
13058 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13059 : *
13060 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13061 : *
13062 : * @return nullptr in case of error, or a NULL terminated list of strings to
13063 : * free with CSLDestroy()
13064 : * @since GDAL 3.10
13065 : */
13066 4 : char **OSRGetAuthorityListFromDatabase()
13067 : {
13068 : PROJ_STRING_LIST list =
13069 4 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13070 4 : if (!list)
13071 : {
13072 0 : return nullptr;
13073 : }
13074 4 : int count = 0;
13075 24 : while (list[count])
13076 20 : ++count;
13077 4 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13078 24 : for (int i = 0; i < count; ++i)
13079 20 : res[i] = CPLStrdup(list[i]);
13080 4 : proj_string_list_destroy(list);
13081 4 : return res;
13082 : }
13083 :
13084 : /************************************************************************/
13085 : /* UpdateCoordinateSystemFromGeogCRS() */
13086 : /************************************************************************/
13087 :
13088 : /*! @cond Doxygen_Suppress */
13089 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13090 : *
13091 : * @since GDAL 3.1
13092 : */
13093 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13094 : {
13095 1 : TAKE_OPTIONAL_LOCK();
13096 :
13097 1 : d->refreshProjObj();
13098 1 : if (!d->m_pj_crs)
13099 0 : return;
13100 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13101 0 : return;
13102 1 : if (GetAxesCount() == 3)
13103 0 : return;
13104 1 : auto ctxt = d->getPROJContext();
13105 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13106 1 : if (!baseCRS)
13107 0 : return;
13108 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13109 1 : if (!baseCRSCS)
13110 : {
13111 0 : proj_destroy(baseCRS);
13112 0 : return;
13113 : }
13114 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13115 : {
13116 0 : proj_destroy(baseCRSCS);
13117 0 : proj_destroy(baseCRS);
13118 0 : return;
13119 : }
13120 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13121 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13122 : {
13123 0 : proj_destroy(baseCRSCS);
13124 0 : proj_destroy(baseCRS);
13125 0 : proj_destroy(projCS);
13126 0 : return;
13127 : }
13128 :
13129 : PJ_AXIS_DESCRIPTION axis[3];
13130 4 : for (int i = 0; i < 3; i++)
13131 : {
13132 3 : const char *name = nullptr;
13133 3 : const char *abbreviation = nullptr;
13134 3 : const char *direction = nullptr;
13135 3 : double unit_conv_factor = 0;
13136 3 : const char *unit_name = nullptr;
13137 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13138 : &abbreviation, &direction, &unit_conv_factor,
13139 : &unit_name, nullptr, nullptr);
13140 3 : axis[i].name = CPLStrdup(name);
13141 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13142 3 : axis[i].direction = CPLStrdup(direction);
13143 3 : axis[i].unit_name = CPLStrdup(unit_name);
13144 3 : axis[i].unit_conv_factor = unit_conv_factor;
13145 3 : axis[i].unit_type = PJ_UT_LINEAR;
13146 : }
13147 1 : proj_destroy(baseCRSCS);
13148 1 : proj_destroy(projCS);
13149 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13150 4 : for (int i = 0; i < 3; i++)
13151 : {
13152 3 : CPLFree(axis[i].name);
13153 3 : CPLFree(axis[i].abbreviation);
13154 3 : CPLFree(axis[i].direction);
13155 3 : CPLFree(axis[i].unit_name);
13156 : }
13157 1 : if (!cs)
13158 : {
13159 0 : proj_destroy(baseCRS);
13160 0 : return;
13161 : }
13162 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13163 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13164 : conversion, cs);
13165 1 : proj_destroy(baseCRS);
13166 1 : proj_destroy(conversion);
13167 1 : proj_destroy(cs);
13168 1 : d->setPjCRS(crs);
13169 : }
13170 :
13171 : /*! @endcond */
13172 :
13173 : /************************************************************************/
13174 : /* PromoteTo3D() */
13175 : /************************************************************************/
13176 :
13177 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13178 : *
13179 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13180 : * units.
13181 : *
13182 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13183 : * be used.
13184 : * @return OGRERR_NONE if no error occurred.
13185 : * @since GDAL 3.1 and PROJ 6.3
13186 : */
13187 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13188 : {
13189 84 : TAKE_OPTIONAL_LOCK();
13190 :
13191 42 : d->refreshProjObj();
13192 42 : if (!d->m_pj_crs)
13193 0 : return OGRERR_FAILURE;
13194 : auto newPj =
13195 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13196 42 : if (!newPj)
13197 0 : return OGRERR_FAILURE;
13198 42 : d->setPjCRS(newPj);
13199 42 : return OGRERR_NONE;
13200 : }
13201 :
13202 : /************************************************************************/
13203 : /* OSRPromoteTo3D() */
13204 : /************************************************************************/
13205 :
13206 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13207 : *
13208 : * See OGRSpatialReference::PromoteTo3D()
13209 : *
13210 : * @since GDAL 3.1 and PROJ 6.3
13211 : */
13212 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13213 : {
13214 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13215 :
13216 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13217 : }
13218 :
13219 : /************************************************************************/
13220 : /* DemoteTo2D() */
13221 : /************************************************************************/
13222 :
13223 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13224 : *
13225 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13226 : * be used.
13227 : * @return OGRERR_NONE if no error occurred.
13228 : * @since GDAL 3.2 and PROJ 6.3
13229 : */
13230 47 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13231 : {
13232 94 : TAKE_OPTIONAL_LOCK();
13233 :
13234 47 : d->refreshProjObj();
13235 47 : if (!d->m_pj_crs)
13236 0 : return OGRERR_FAILURE;
13237 : auto newPj =
13238 47 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13239 47 : if (!newPj)
13240 0 : return OGRERR_FAILURE;
13241 47 : d->setPjCRS(newPj);
13242 47 : return OGRERR_NONE;
13243 : }
13244 :
13245 : /************************************************************************/
13246 : /* OSRDemoteTo2D() */
13247 : /************************************************************************/
13248 :
13249 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13250 : *
13251 : * See OGRSpatialReference::DemoteTo2D()
13252 : *
13253 : * @since GDAL 3.2 and PROJ 6.3
13254 : */
13255 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13256 : {
13257 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13258 :
13259 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13260 : }
13261 :
13262 : /************************************************************************/
13263 : /* GetEPSGGeogCS() */
13264 : /************************************************************************/
13265 :
13266 : /** Try to establish what the EPSG code for this coordinate systems
13267 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13268 : *
13269 : * @return EPSG code
13270 : */
13271 :
13272 344 : int OGRSpatialReference::GetEPSGGeogCS() const
13273 :
13274 : {
13275 688 : TAKE_OPTIONAL_LOCK();
13276 :
13277 : /* -------------------------------------------------------------------- */
13278 : /* Check axis order. */
13279 : /* -------------------------------------------------------------------- */
13280 688 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13281 344 : if (!poGeogCRS)
13282 0 : return -1;
13283 :
13284 344 : bool ret = false;
13285 344 : poGeogCRS->d->demoteFromBoundCRS();
13286 344 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13287 344 : poGeogCRS->d->m_pj_crs);
13288 344 : poGeogCRS->d->undoDemoteFromBoundCRS();
13289 344 : if (cs)
13290 : {
13291 344 : const char *pszDirection = nullptr;
13292 344 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13293 : &pszDirection, nullptr, nullptr, nullptr,
13294 344 : nullptr))
13295 : {
13296 344 : if (EQUAL(pszDirection, "north"))
13297 : {
13298 143 : ret = true;
13299 : }
13300 : }
13301 :
13302 344 : proj_destroy(cs);
13303 : }
13304 344 : if (!ret)
13305 201 : return -1;
13306 :
13307 : /* -------------------------------------------------------------------- */
13308 : /* Do we already have it? */
13309 : /* -------------------------------------------------------------------- */
13310 143 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13311 143 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13312 66 : return atoi(GetAuthorityCode("GEOGCS"));
13313 :
13314 : /* -------------------------------------------------------------------- */
13315 : /* Get the datum and geogcs names. */
13316 : /* -------------------------------------------------------------------- */
13317 :
13318 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13319 77 : const char *pszDatum = GetAttrValue("DATUM");
13320 :
13321 : // We can only operate on coordinate systems with a geogcs.
13322 154 : OGRSpatialReference oSRSTmp;
13323 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13324 : {
13325 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13326 : // export to WKT1, so try to extract the geographic CRS through PROJ
13327 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13328 1 : oSRSTmp.CopyGeogCSFrom(this);
13329 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13330 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13331 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13332 : {
13333 0 : return -1;
13334 : }
13335 : }
13336 :
13337 : // Lookup geographic CRS name
13338 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13339 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13340 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13341 77 : if (list)
13342 : {
13343 77 : const auto listSize = proj_list_get_count(list);
13344 77 : if (listSize == 1)
13345 : {
13346 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13347 49 : if (crs)
13348 : {
13349 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13350 49 : const char *pszCode = proj_get_id_code(crs, 0);
13351 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13352 : {
13353 47 : const int nCode = atoi(pszCode);
13354 47 : proj_destroy(crs);
13355 47 : proj_list_destroy(list);
13356 47 : return nCode;
13357 : }
13358 2 : proj_destroy(crs);
13359 : }
13360 : }
13361 30 : proj_list_destroy(list);
13362 : }
13363 :
13364 : /* -------------------------------------------------------------------- */
13365 : /* Is this a "well known" geographic coordinate system? */
13366 : /* -------------------------------------------------------------------- */
13367 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13368 30 : strstr(pszDatum, "WGS") ||
13369 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13370 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13371 90 : strstr(pszDatum, "World Geodetic System") ||
13372 30 : strstr(pszDatum, "World_Geodetic_System");
13373 :
13374 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13375 30 : strstr(pszDatum, "NAD") ||
13376 30 : strstr(pszGEOGCS, "North American") ||
13377 30 : strstr(pszGEOGCS, "North_American") ||
13378 90 : strstr(pszDatum, "North American") ||
13379 30 : strstr(pszDatum, "North_American");
13380 :
13381 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13382 0 : return 4326;
13383 :
13384 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13385 0 : return 4322;
13386 :
13387 : // This is questionable as there are several 'flavors' of NAD83 that
13388 : // are not the same as 4269
13389 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13390 0 : return 4269;
13391 :
13392 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13393 0 : return 4267;
13394 :
13395 : /* -------------------------------------------------------------------- */
13396 : /* If we know the datum, associate the most likely GCS with */
13397 : /* it. */
13398 : /* -------------------------------------------------------------------- */
13399 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13400 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13401 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13402 0 : GetPrimeMeridian() == 0.0)
13403 : {
13404 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13405 :
13406 0 : if (nDatum >= 6000 && nDatum <= 6999)
13407 0 : return nDatum - 2000;
13408 : }
13409 :
13410 30 : return -1;
13411 : }
13412 :
13413 : /************************************************************************/
13414 : /* SetCoordinateEpoch() */
13415 : /************************************************************************/
13416 :
13417 : /** Set the coordinate epoch, as decimal year.
13418 : *
13419 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13420 : * change with time. To be unambiguous the coordinates must always be qualified
13421 : * with the epoch at which they are valid. The coordinate epoch is not
13422 : * necessarily the epoch at which the observation was collected.
13423 : *
13424 : * Pedantically the coordinate epoch of an observation belongs to the
13425 : * observation, and not to the CRS, however it is often more practical to
13426 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13427 : * CRS (see IsDynamic())
13428 : *
13429 : * This method is the same as the OSRSetCoordinateEpoch() function.
13430 : *
13431 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13432 : * @since OGR 3.4
13433 : */
13434 :
13435 932 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13436 : {
13437 932 : d->m_coordinateEpoch = dfCoordinateEpoch;
13438 932 : }
13439 :
13440 : /************************************************************************/
13441 : /* OSRSetCoordinateEpoch() */
13442 : /************************************************************************/
13443 :
13444 : /** \brief Set the coordinate epoch, as decimal year.
13445 : *
13446 : * See OGRSpatialReference::SetCoordinateEpoch()
13447 : *
13448 : * @since OGR 3.4
13449 : */
13450 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13451 : {
13452 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13453 :
13454 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13455 31 : dfCoordinateEpoch);
13456 : }
13457 :
13458 : /************************************************************************/
13459 : /* GetCoordinateEpoch() */
13460 : /************************************************************************/
13461 :
13462 : /** Return the coordinate epoch, as decimal year.
13463 : *
13464 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13465 : * change with time. To be unambiguous the coordinates must always be qualified
13466 : * with the epoch at which they are valid. The coordinate epoch is not
13467 : * necessarily the epoch at which the observation was collected.
13468 : *
13469 : * Pedantically the coordinate epoch of an observation belongs to the
13470 : * observation, and not to the CRS, however it is often more practical to
13471 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13472 : * CRS (see IsDynamic())
13473 : *
13474 : * This method is the same as the OSRGetCoordinateEpoch() function.
13475 : *
13476 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13477 : * if not set, or relevant.
13478 : * @since OGR 3.4
13479 : */
13480 :
13481 14652 : double OGRSpatialReference::GetCoordinateEpoch() const
13482 : {
13483 14652 : return d->m_coordinateEpoch;
13484 : }
13485 :
13486 : /************************************************************************/
13487 : /* OSRGetCoordinateEpoch() */
13488 : /************************************************************************/
13489 :
13490 : /** \brief Get the coordinate epoch, as decimal year.
13491 : *
13492 : * See OGRSpatialReference::GetCoordinateEpoch()
13493 : *
13494 : * @since OGR 3.4
13495 : */
13496 601 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13497 : {
13498 601 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13499 :
13500 601 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13501 : }
|