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 231384 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 231247 : }
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();
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 1152320 : PJ_CONTEXT *getPROJContext()
159 : {
160 1152320 : 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 5189030 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 5189030 : if (m_private.m_bIsThreadSafe)
182 3797 : m_private.m_mutex.lock();
183 5189030 : }
184 :
185 5190030 : ~OptionalLockGuard()
186 5190030 : {
187 5190030 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 5190030 : }
190 : };
191 :
192 5189260 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 5189260 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2134550 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2134550 : m_poObj->nodesChanged();
201 2134550 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 231396 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 231396 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 231451 : 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 231450 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 231397 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 : : m_poSelf(poSelf),
229 231397 : m_poListener(std::shared_ptr<Listener>(new Listener(this)))
230 : {
231 : // Get the default value for m_axisMappingStrategy from the
232 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
233 231216 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
234 231451 : }
235 :
236 920757 : OGRSpatialReference::Private::~Private()
237 : {
238 : // In case we destroy the object not in the thread that created it,
239 : // we need to reassign the PROJ context. Having the context bundled inside
240 : // PJ* deeply sucks...
241 230235 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
242 : PJ_CONTEXT *ctxt;
243 230235 : if (GDALThreadLocalDatasetCacheIsInDestruction())
244 : {
245 186 : pj_context_to_destroy = proj_context_create();
246 186 : ctxt = pj_context_to_destroy;
247 : }
248 : else
249 : {
250 230036 : ctxt = getPROJContext();
251 : }
252 :
253 230235 : proj_assign_context(m_pj_crs, ctxt);
254 230234 : proj_destroy(m_pj_crs);
255 :
256 230234 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
257 230234 : proj_destroy(m_pj_geod_base_crs_temp);
258 :
259 230234 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
260 230234 : proj_destroy(m_pj_proj_crs_cs_temp);
261 :
262 230234 : proj_assign_context(m_pj_bound_crs_target, ctxt);
263 230235 : proj_destroy(m_pj_bound_crs_target);
264 :
265 230234 : proj_assign_context(m_pj_bound_crs_co, ctxt);
266 230234 : proj_destroy(m_pj_bound_crs_co);
267 :
268 230234 : proj_assign_context(m_pj_crs_backup, ctxt);
269 230234 : proj_destroy(m_pj_crs_backup);
270 :
271 230234 : delete m_poRootBackup;
272 230230 : delete m_poRoot;
273 230230 : proj_context_destroy(pj_context_to_destroy);
274 230187 : }
275 :
276 120012 : void OGRSpatialReference::Private::clear()
277 : {
278 120012 : proj_assign_context(m_pj_crs, getPROJContext());
279 120012 : proj_destroy(m_pj_crs);
280 120012 : m_pj_crs = nullptr;
281 :
282 120012 : delete m_poRoot;
283 120012 : m_poRoot = nullptr;
284 120012 : m_bNodesChanged = false;
285 :
286 120012 : m_wktImportWarnings.clear();
287 120011 : m_wktImportErrors.clear();
288 :
289 120012 : m_pj_crs_modified_during_demote = false;
290 120012 : m_pjType = PJ_TYPE_UNKNOWN;
291 120012 : m_osPrimeMeridianName.clear();
292 120011 : m_osAngularUnits.clear();
293 120012 : m_osLinearUnits.clear();
294 :
295 120012 : bNormInfoSet = FALSE;
296 120012 : dfFromGreenwich = 1.0;
297 120012 : dfToMeter = 1.0;
298 120012 : dfToDegrees = 1.0;
299 120012 : m_dfAngularUnitToRadian = 0.0;
300 :
301 120012 : m_bMorphToESRI = false;
302 120012 : m_bHasCenterLong = false;
303 :
304 120012 : m_coordinateEpoch = 0.0;
305 120012 : }
306 :
307 26795 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
308 : {
309 26795 : m_poRoot = poRoot;
310 26795 : if (m_poRoot)
311 : {
312 26795 : m_poRoot->RegisterListener(m_poListener);
313 : }
314 26795 : nodesChanged();
315 26795 : }
316 :
317 181865 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
318 : bool doRefreshAxisMapping)
319 : {
320 181865 : auto ctxt = getPROJContext();
321 :
322 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
323 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
324 : {
325 : const double dfEpoch =
326 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
327 : if (!std::isnan(dfEpoch))
328 : {
329 : m_poSelf->SetCoordinateEpoch(dfEpoch);
330 : }
331 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
332 : proj_destroy(pj_crsIn);
333 : pj_crsIn = crs;
334 : }
335 : #endif
336 :
337 181866 : proj_assign_context(m_pj_crs, ctxt);
338 181866 : proj_destroy(m_pj_crs);
339 181865 : m_pj_crs = pj_crsIn;
340 181865 : if (m_pj_crs)
341 : {
342 181814 : m_pjType = proj_get_type(m_pj_crs);
343 : }
344 181865 : if (m_pj_crs_backup)
345 : {
346 21 : m_pj_crs_modified_during_demote = true;
347 : }
348 181865 : invalidateNodes();
349 181866 : if (doRefreshAxisMapping)
350 : {
351 181846 : refreshAxisMapping();
352 : }
353 181866 : }
354 :
355 733620 : void OGRSpatialReference::Private::refreshProjObj()
356 : {
357 733620 : if (m_bNodesChanged && m_poRoot)
358 : {
359 8283 : char *pszWKT = nullptr;
360 8283 : m_poRoot->exportToWkt(&pszWKT);
361 8283 : auto poRootBackup = m_poRoot;
362 8283 : m_poRoot = nullptr;
363 8283 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
364 8283 : clear();
365 8283 : m_coordinateEpoch = dfCoordinateEpochBackup;
366 8283 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
367 :
368 8283 : const char *const options[] = {
369 : "STRICT=NO",
370 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
371 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
372 : #endif
373 : nullptr
374 : };
375 8283 : PROJ_STRING_LIST warnings = nullptr;
376 8283 : PROJ_STRING_LIST errors = nullptr;
377 8283 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
378 : &warnings, &errors));
379 16171 : for (auto iter = warnings; iter && *iter; ++iter)
380 : {
381 7888 : m_wktImportWarnings.push_back(*iter);
382 : }
383 8498 : for (auto iter = errors; iter && *iter; ++iter)
384 : {
385 215 : m_wktImportErrors.push_back(*iter);
386 : }
387 8283 : proj_string_list_destroy(warnings);
388 8283 : proj_string_list_destroy(errors);
389 :
390 8283 : CPLFree(pszWKT);
391 :
392 8283 : m_poRoot = poRootBackup;
393 8283 : m_bNodesChanged = false;
394 : }
395 733620 : }
396 :
397 28918 : void OGRSpatialReference::Private::refreshRootFromProjObj()
398 : {
399 28918 : CPLAssert(m_poRoot == nullptr);
400 :
401 28918 : if (m_pj_crs)
402 : {
403 53502 : CPLStringList aosOptions;
404 26751 : if (!m_bMorphToESRI)
405 : {
406 26747 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
407 26747 : aosOptions.SetNameValue("MULTILINE", "NO");
408 : }
409 26751 : aosOptions.SetNameValue("STRICT", "NO");
410 :
411 : const char *pszWKT;
412 : {
413 26751 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
414 26751 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
415 26751 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
416 26751 : aosOptions.List());
417 26751 : m_bNodesWKT2 = false;
418 : }
419 26751 : if (!m_bMorphToESRI && pszWKT == nullptr)
420 : {
421 96 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
422 96 : aosOptions.List());
423 96 : m_bNodesWKT2 = true;
424 : }
425 26751 : if (pszWKT)
426 : {
427 26751 : auto root = new OGR_SRSNode();
428 26751 : setRoot(root);
429 26751 : root->importFromWkt(&pszWKT);
430 26751 : m_bNodesChanged = false;
431 : }
432 : }
433 28918 : }
434 :
435 210355 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
436 : {
437 210355 : const char *pszName1 = nullptr;
438 210355 : const char *pszDirection1 = nullptr;
439 210355 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
440 : nullptr, nullptr, nullptr, nullptr);
441 210352 : const char *pszName2 = nullptr;
442 210352 : const char *pszDirection2 = nullptr;
443 210352 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
444 : nullptr, nullptr, nullptr, nullptr);
445 210353 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
446 93282 : EQUAL(pszDirection2, "east"))
447 : {
448 91893 : return true;
449 : }
450 118460 : if (pszDirection1 && pszDirection2 &&
451 118460 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
452 117088 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
453 2741 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
454 1065 : STARTS_WITH_CI(pszName2, "easting"))
455 : {
456 1065 : return true;
457 : }
458 117395 : return false;
459 : }
460 :
461 267864 : void OGRSpatialReference::Private::refreshAxisMapping()
462 : {
463 267864 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
464 57987 : return;
465 :
466 209877 : bool doUndoDemote = false;
467 209877 : if (m_pj_crs_backup == nullptr)
468 : {
469 209854 : doUndoDemote = true;
470 209854 : demoteFromBoundCRS();
471 : }
472 209878 : const auto ctxt = getPROJContext();
473 209878 : PJ *horizCRS = nullptr;
474 209878 : int axisCount = 0;
475 209878 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
476 : {
477 218 : axisCount = 1;
478 : }
479 209660 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
480 : {
481 1100 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
482 1100 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
483 : {
484 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
485 222 : if (baseCRS)
486 : {
487 222 : proj_destroy(horizCRS);
488 222 : horizCRS = baseCRS;
489 : }
490 : }
491 :
492 1100 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
493 1100 : if (vertCRS)
494 : {
495 1097 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
496 : {
497 391 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
498 391 : if (baseCRS)
499 : {
500 391 : proj_destroy(vertCRS);
501 391 : vertCRS = baseCRS;
502 : }
503 : }
504 :
505 1097 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
506 1097 : if (cs)
507 : {
508 1097 : axisCount += proj_cs_get_axis_count(ctxt, cs);
509 1097 : proj_destroy(cs);
510 : }
511 1097 : proj_destroy(vertCRS);
512 : }
513 : }
514 : else
515 : {
516 208560 : horizCRS = m_pj_crs;
517 : }
518 :
519 209878 : bool bSwitchForGisFriendlyOrder = false;
520 209878 : if (horizCRS)
521 : {
522 209657 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
523 209657 : if (cs)
524 : {
525 209657 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
526 209657 : axisCount += nHorizCSAxisCount;
527 209657 : if (nHorizCSAxisCount >= 2)
528 : {
529 209646 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
530 : }
531 209657 : proj_destroy(cs);
532 : }
533 : }
534 209877 : if (horizCRS != m_pj_crs)
535 : {
536 1318 : proj_destroy(horizCRS);
537 : }
538 209877 : if (doUndoDemote)
539 : {
540 209855 : undoDemoteFromBoundCRS();
541 : }
542 :
543 209876 : m_axisMapping.resize(axisCount);
544 209876 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
545 61835 : !bSwitchForGisFriendlyOrder)
546 : {
547 546842 : for (int i = 0; i < axisCount; i++)
548 : {
549 365068 : m_axisMapping[i] = i + 1;
550 181774 : }
551 : }
552 : else
553 : {
554 28103 : m_axisMapping[0] = 2;
555 28103 : m_axisMapping[1] = 1;
556 28103 : if (axisCount == 3)
557 : {
558 338 : m_axisMapping[2] = 3;
559 : }
560 : }
561 : }
562 :
563 2161350 : void OGRSpatialReference::Private::nodesChanged()
564 : {
565 2161350 : m_bNodesChanged = true;
566 2161350 : }
567 :
568 182122 : void OGRSpatialReference::Private::invalidateNodes()
569 : {
570 182122 : delete m_poRoot;
571 182122 : m_poRoot = nullptr;
572 182122 : m_bNodesChanged = false;
573 182122 : }
574 :
575 256 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
576 : {
577 256 : invalidateNodes();
578 256 : m_bMorphToESRI = b;
579 256 : }
580 :
581 617049 : void OGRSpatialReference::Private::demoteFromBoundCRS()
582 : {
583 617049 : CPLAssert(m_pj_bound_crs_target == nullptr);
584 617049 : CPLAssert(m_pj_bound_crs_co == nullptr);
585 617049 : CPLAssert(m_poRootBackup == nullptr);
586 617049 : CPLAssert(m_pj_crs_backup == nullptr);
587 :
588 617049 : m_pj_crs_modified_during_demote = false;
589 :
590 617049 : if (m_pjType == PJ_TYPE_BOUND_CRS)
591 : {
592 2758 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
593 2758 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
594 2758 : m_pj_bound_crs_co =
595 2758 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
596 :
597 2758 : m_poRootBackup = m_poRoot;
598 2758 : m_poRoot = nullptr;
599 2758 : m_pj_crs_backup = m_pj_crs;
600 2758 : m_pj_crs = baseCRS;
601 2758 : m_pjType = proj_get_type(m_pj_crs);
602 : }
603 617049 : }
604 :
605 617050 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
606 : {
607 617050 : if (m_pj_bound_crs_target)
608 : {
609 2758 : CPLAssert(m_poRoot == nullptr);
610 2758 : CPLAssert(m_pj_crs);
611 2758 : if (!m_pj_crs_modified_during_demote)
612 : {
613 2738 : proj_destroy(m_pj_crs);
614 2738 : m_pj_crs = m_pj_crs_backup;
615 2738 : m_pjType = proj_get_type(m_pj_crs);
616 2738 : m_poRoot = m_poRootBackup;
617 : }
618 : else
619 : {
620 20 : delete m_poRootBackup;
621 20 : m_poRootBackup = nullptr;
622 20 : proj_destroy(m_pj_crs_backup);
623 20 : m_pj_crs_backup = nullptr;
624 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
625 20 : m_pj_bound_crs_target,
626 20 : m_pj_bound_crs_co),
627 : false);
628 : }
629 : }
630 :
631 617050 : m_poRootBackup = nullptr;
632 617050 : m_pj_crs_backup = nullptr;
633 617050 : proj_destroy(m_pj_bound_crs_target);
634 617048 : m_pj_bound_crs_target = nullptr;
635 617048 : proj_destroy(m_pj_bound_crs_co);
636 617046 : m_pj_bound_crs_co = nullptr;
637 617046 : m_pj_crs_modified_during_demote = false;
638 617046 : }
639 :
640 153656 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
641 : const char *pszTargetKey)
642 : {
643 153656 : if (pszTargetKey)
644 : {
645 57924 : demoteFromBoundCRS();
646 57924 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
647 29867 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
648 28135 : EQUAL(pszTargetKey, "GEOGCS"))
649 : {
650 6979 : pszTargetKey = nullptr;
651 : }
652 50945 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
653 20 : EQUAL(pszTargetKey, "GEOCCS"))
654 : {
655 0 : pszTargetKey = nullptr;
656 : }
657 50945 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
658 28482 : EQUAL(pszTargetKey, "PROJCS"))
659 : {
660 3994 : pszTargetKey = nullptr;
661 : }
662 46951 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
663 4 : EQUAL(pszTargetKey, "VERT_CS"))
664 : {
665 2 : pszTargetKey = nullptr;
666 : }
667 57924 : undoDemoteFromBoundCRS();
668 : }
669 153656 : return pszTargetKey;
670 : }
671 :
672 9301 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
673 : {
674 9301 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
675 9248 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
676 : {
677 53 : return m_pj_crs;
678 : }
679 :
680 9248 : auto ctxt = getPROJContext();
681 9248 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
682 : {
683 4246 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
684 4246 : proj_destroy(m_pj_geod_base_crs_temp);
685 4246 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
686 4246 : return m_pj_geod_base_crs_temp;
687 : }
688 :
689 5002 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
690 5002 : proj_destroy(m_pj_geod_base_crs_temp);
691 5002 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
692 : nullptr, 0);
693 5002 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
694 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
695 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
696 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
697 5002 : proj_destroy(cs);
698 :
699 5002 : return m_pj_geod_base_crs_temp;
700 : }
701 :
702 5203 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
703 : {
704 5203 : auto ctxt = getPROJContext();
705 5203 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
706 : {
707 4232 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
708 4232 : proj_destroy(m_pj_proj_crs_cs_temp);
709 4232 : m_pj_proj_crs_cs_temp =
710 4232 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
711 4232 : return m_pj_proj_crs_cs_temp;
712 : }
713 :
714 971 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
715 971 : proj_destroy(m_pj_proj_crs_cs_temp);
716 971 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
717 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
718 971 : return m_pj_proj_crs_cs_temp;
719 : }
720 :
721 5254 : const char *OGRSpatialReference::Private::getProjCRSName()
722 : {
723 5254 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
724 : {
725 4247 : return proj_get_name(m_pj_crs);
726 : }
727 :
728 1007 : return "unnamed";
729 : }
730 :
731 1378 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
732 : {
733 1378 : refreshProjObj();
734 :
735 1378 : demoteFromBoundCRS();
736 :
737 : auto projCRS =
738 1378 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
739 1378 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
740 1378 : proj_destroy(conv);
741 :
742 1378 : setPjCRS(projCRS);
743 :
744 1378 : undoDemoteFromBoundCRS();
745 1378 : return OGRERR_NONE;
746 : }
747 :
748 : /************************************************************************/
749 : /* ToPointer() */
750 : /************************************************************************/
751 :
752 25589 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
753 : {
754 25589 : return OGRSpatialReference::FromHandle(hSRS);
755 : }
756 :
757 : /************************************************************************/
758 : /* ToHandle() */
759 : /************************************************************************/
760 :
761 4315 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
762 : {
763 4315 : return OGRSpatialReference::ToHandle(poSRS);
764 : }
765 :
766 : /************************************************************************/
767 : /* OGRsnPrintDouble() */
768 : /************************************************************************/
769 :
770 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
771 :
772 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
773 :
774 : {
775 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
776 :
777 126 : const size_t nLen = strlen(pszStrBuf);
778 :
779 : // The following hack is intended to truncate some "precision" in cases
780 : // that appear to be roundoff error.
781 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
782 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
783 : {
784 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
785 : }
786 :
787 : // Force to user periods regardless of locale.
788 126 : if (strchr(pszStrBuf, ',') != nullptr)
789 : {
790 0 : char *const pszDelim = strchr(pszStrBuf, ',');
791 0 : *pszDelim = '.';
792 : }
793 126 : }
794 :
795 : /************************************************************************/
796 : /* OGRSpatialReference() */
797 : /************************************************************************/
798 :
799 : /**
800 : * \brief Constructor.
801 : *
802 : * This constructor takes an optional string argument which if passed
803 : * should be a WKT representation of an SRS. Passing this is equivalent
804 : * to not passing it, and then calling importFromWkt() with the WKT string.
805 : *
806 : * Note that newly created objects are given a reference count of one.
807 : *
808 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
809 : * object are assumed to be in the order of the axis of the CRS definition
810 : (which
811 : * for example means latitude first, longitude second for geographic CRS
812 : belonging
813 : * to the EPSG authority). It is possible to define a data axis to CRS axis
814 : * mapping strategy with the SetAxisMappingStrategy() method.
815 : *
816 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
817 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
818 : later
819 : * being the default value when the option is not set) to control the value of
820 : the
821 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
822 : * created. Calling SetAxisMappingStrategy() will override this default value.
823 :
824 : * The C function OSRNewSpatialReference() does the same thing as this
825 : * constructor.
826 : *
827 : * @param pszWKT well known text definition to which the object should
828 : * be initialized, or NULL (the default).
829 : */
830 :
831 228957 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
832 228957 : : d(new Private(this))
833 : {
834 228784 : if (pszWKT != nullptr)
835 281 : importFromWkt(pszWKT);
836 228784 : }
837 :
838 : /************************************************************************/
839 : /* OSRNewSpatialReference() */
840 : /************************************************************************/
841 :
842 : /**
843 : * \brief Constructor.
844 : *
845 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
846 : * object are assumed to be in the order of the axis of the CRS definition
847 : * (which for example means latitude first, longitude second for geographic CRS
848 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
849 : * axis mapping strategy with the SetAxisMappingStrategy() method.
850 : *
851 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
852 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
853 : * later being the default value when the option is not set) to control the
854 : * value of the data axis to CRS axis mapping strategy when a
855 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
856 : * override this default value.
857 : *
858 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
859 : */
860 3043 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
861 :
862 : {
863 3043 : OGRSpatialReference *poSRS = new OGRSpatialReference();
864 :
865 3043 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
866 : {
867 65 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
868 : {
869 0 : delete poSRS;
870 0 : poSRS = nullptr;
871 : }
872 : }
873 :
874 3043 : return ToHandle(poSRS);
875 : }
876 :
877 : /************************************************************************/
878 : /* OGRSpatialReference() */
879 : /************************************************************************/
880 :
881 : /** Copy constructor. See also Clone().
882 : * @param oOther other spatial reference
883 : */
884 2477 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
885 2477 : : d(new Private(this))
886 : {
887 2477 : *this = oOther;
888 2477 : }
889 :
890 : /************************************************************************/
891 : /* OGRSpatialReference() */
892 : /************************************************************************/
893 :
894 : /** Move constructor.
895 : * @param oOther other spatial reference
896 : */
897 29 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
898 29 : : d(std::move(oOther.d))
899 : {
900 29 : }
901 :
902 : /************************************************************************/
903 : /* ~OGRSpatialReference() */
904 : /************************************************************************/
905 :
906 : /**
907 : * \brief OGRSpatialReference destructor.
908 : *
909 : * The C function OSRDestroySpatialReference() does the same thing as this
910 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
911 : *
912 : * @deprecated
913 : */
914 :
915 286892 : OGRSpatialReference::~OGRSpatialReference()
916 :
917 : {
918 286867 : }
919 :
920 : /************************************************************************/
921 : /* DestroySpatialReference() */
922 : /************************************************************************/
923 :
924 : /**
925 : * \brief OGRSpatialReference destructor.
926 : *
927 : * This static method will destroy a OGRSpatialReference. It is
928 : * equivalent to calling delete on the object, but it ensures that the
929 : * deallocation is properly executed within the OGR libraries heap on
930 : * platforms where this can matter (win32).
931 : *
932 : * This function is the same as OSRDestroySpatialReference()
933 : *
934 : * @param poSRS the object to delete
935 : *
936 : */
937 :
938 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
939 : {
940 0 : delete poSRS;
941 0 : }
942 :
943 : /************************************************************************/
944 : /* OSRDestroySpatialReference() */
945 : /************************************************************************/
946 :
947 : /**
948 : * \brief OGRSpatialReference destructor.
949 : *
950 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
951 : * and OGRSpatialReference::DestroySpatialReference()
952 : *
953 : * @param hSRS the object to delete
954 : */
955 9187 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
956 :
957 : {
958 9187 : delete ToPointer(hSRS);
959 9187 : }
960 :
961 : /************************************************************************/
962 : /* Clear() */
963 : /************************************************************************/
964 :
965 : /**
966 : * \brief Wipe current definition.
967 : *
968 : * Returns OGRSpatialReference to a state with no definition, as it
969 : * exists when first created. It does not affect reference counts.
970 : */
971 :
972 111728 : void OGRSpatialReference::Clear()
973 :
974 : {
975 111728 : d->clear();
976 111729 : }
977 :
978 : /************************************************************************/
979 : /* operator=() */
980 : /************************************************************************/
981 :
982 : /** Assignment operator.
983 : * @param oSource SRS to assign to *this
984 : * @return *this
985 : */
986 : OGRSpatialReference &
987 26197 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
988 :
989 : {
990 26197 : if (&oSource != this)
991 : {
992 26196 : Clear();
993 : #ifdef CPPCHECK
994 : // Otherwise cppcheck would protest that nRefCount isn't modified
995 : d->nRefCount = (d->nRefCount + 1) - 1;
996 : #endif
997 :
998 26197 : oSource.d->refreshProjObj();
999 26197 : if (oSource.d->m_pj_crs)
1000 25907 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1001 26196 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1002 12024 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1003 14172 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1004 124 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1005 :
1006 26197 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1007 : }
1008 :
1009 26197 : return *this;
1010 : }
1011 :
1012 : /************************************************************************/
1013 : /* operator=() */
1014 : /************************************************************************/
1015 :
1016 : /** Move assignment operator.
1017 : * @param oSource SRS to assign to *this
1018 : * @return *this
1019 : */
1020 : OGRSpatialReference &
1021 4550 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1022 :
1023 : {
1024 4550 : if (&oSource != this)
1025 : {
1026 4549 : d = std::move(oSource.d);
1027 : }
1028 :
1029 4550 : return *this;
1030 : }
1031 :
1032 : /************************************************************************/
1033 : /* AssignAndSetThreadSafe() */
1034 : /************************************************************************/
1035 :
1036 : /** Assignment method, with thread-safety.
1037 : *
1038 : * Same as an assignment operator, but asking also that the *this instance
1039 : * becomes thread-safe.
1040 : *
1041 : * @param oSource SRS to assign to *this
1042 : * @return *this
1043 : * @since 3.10
1044 : */
1045 :
1046 : OGRSpatialReference &
1047 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1048 : {
1049 2 : *this = oSource;
1050 2 : d->SetThreadSafe();
1051 2 : return *this;
1052 : }
1053 :
1054 : /************************************************************************/
1055 : /* Reference() */
1056 : /************************************************************************/
1057 :
1058 : /**
1059 : * \brief Increments the reference count by one.
1060 : *
1061 : * The reference count is used keep track of the number of OGRGeometry objects
1062 : * referencing this SRS.
1063 : *
1064 : * The method does the same thing as the C function OSRReference().
1065 : *
1066 : * @return the updated reference count.
1067 : */
1068 :
1069 4131340 : int OGRSpatialReference::Reference()
1070 :
1071 : {
1072 4131340 : return CPLAtomicInc(&d->nRefCount);
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* OSRReference() */
1077 : /************************************************************************/
1078 :
1079 : /**
1080 : * \brief Increments the reference count by one.
1081 : *
1082 : * This function is the same as OGRSpatialReference::Reference()
1083 : */
1084 994 : int OSRReference(OGRSpatialReferenceH hSRS)
1085 :
1086 : {
1087 994 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1088 :
1089 994 : return ToPointer(hSRS)->Reference();
1090 : }
1091 :
1092 : /************************************************************************/
1093 : /* Dereference() */
1094 : /************************************************************************/
1095 :
1096 : /**
1097 : * \brief Decrements the reference count by one.
1098 : *
1099 : * The method does the same thing as the C function OSRDereference().
1100 : *
1101 : * @return the updated reference count.
1102 : */
1103 :
1104 4172050 : int OGRSpatialReference::Dereference()
1105 :
1106 : {
1107 4172050 : if (d->nRefCount <= 0)
1108 0 : CPLDebug("OSR",
1109 : "Dereference() called on an object with refcount %d,"
1110 : "likely already destroyed!",
1111 0 : d->nRefCount);
1112 4172050 : return CPLAtomicDec(&d->nRefCount);
1113 : }
1114 :
1115 : /************************************************************************/
1116 : /* OSRDereference() */
1117 : /************************************************************************/
1118 :
1119 : /**
1120 : * \brief Decrements the reference count by one.
1121 : *
1122 : * This function is the same as OGRSpatialReference::Dereference()
1123 : */
1124 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1125 :
1126 : {
1127 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1128 :
1129 0 : return ToPointer(hSRS)->Dereference();
1130 : }
1131 :
1132 : /************************************************************************/
1133 : /* GetReferenceCount() */
1134 : /************************************************************************/
1135 :
1136 : /**
1137 : * \brief Fetch current reference count.
1138 : *
1139 : * @return the current reference count.
1140 : */
1141 180 : int OGRSpatialReference::GetReferenceCount() const
1142 : {
1143 180 : return d->nRefCount;
1144 : }
1145 :
1146 : /************************************************************************/
1147 : /* Release() */
1148 : /************************************************************************/
1149 :
1150 : /**
1151 : * \brief Decrements the reference count by one, and destroy if zero.
1152 : *
1153 : * The method does the same thing as the C function OSRRelease().
1154 : */
1155 :
1156 4169270 : void OGRSpatialReference::Release()
1157 :
1158 : {
1159 4169270 : if (Dereference() <= 0)
1160 40652 : delete this;
1161 4169270 : }
1162 :
1163 : /************************************************************************/
1164 : /* OSRRelease() */
1165 : /************************************************************************/
1166 :
1167 : /**
1168 : * \brief Decrements the reference count by one, and destroy if zero.
1169 : *
1170 : * This function is the same as OGRSpatialReference::Release()
1171 : */
1172 6315 : void OSRRelease(OGRSpatialReferenceH hSRS)
1173 :
1174 : {
1175 6315 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1176 :
1177 6315 : ToPointer(hSRS)->Release();
1178 : }
1179 :
1180 87739 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1181 : {
1182 87739 : TAKE_OPTIONAL_LOCK();
1183 :
1184 87739 : if (!d->m_poRoot)
1185 : {
1186 25948 : d->refreshRootFromProjObj();
1187 : }
1188 175478 : return d->m_poRoot;
1189 : }
1190 :
1191 7906 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1192 : {
1193 7906 : TAKE_OPTIONAL_LOCK();
1194 :
1195 7906 : if (!d->m_poRoot)
1196 : {
1197 2970 : d->refreshRootFromProjObj();
1198 : }
1199 15812 : return d->m_poRoot;
1200 : }
1201 :
1202 : /************************************************************************/
1203 : /* SetRoot() */
1204 : /************************************************************************/
1205 :
1206 : /**
1207 : * \brief Set the root SRS node.
1208 : *
1209 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1210 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1211 : * is assumed by the OGRSpatialReference.
1212 : *
1213 : * @param poNewRoot object to assign as root.
1214 : */
1215 :
1216 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1217 :
1218 : {
1219 44 : if (d->m_poRoot != poNewRoot)
1220 : {
1221 44 : delete d->m_poRoot;
1222 44 : d->setRoot(poNewRoot);
1223 : }
1224 44 : }
1225 :
1226 : /************************************************************************/
1227 : /* GetAttrNode() */
1228 : /************************************************************************/
1229 :
1230 : /**
1231 : * \brief Find named node in tree.
1232 : *
1233 : * This method does a pre-order traversal of the node tree searching for
1234 : * a node with this exact value (case insensitive), and returns it. Leaf
1235 : * nodes are not considered, under the assumption that they are just
1236 : * attribute value nodes.
1237 : *
1238 : * If a node appears more than once in the tree (such as UNIT for instance),
1239 : * the first encountered will be returned. Use GetNode() on a subtree to be
1240 : * more specific.
1241 : *
1242 : * @param pszNodePath the name of the node to search for. May contain multiple
1243 : * components such as "GEOGCS|UNIT".
1244 : *
1245 : * @return a pointer to the node found, or NULL if none.
1246 : */
1247 :
1248 84607 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1249 :
1250 : {
1251 84607 : if (strchr(pszNodePath, '|') == nullptr)
1252 : {
1253 : // Fast path
1254 46447 : OGR_SRSNode *poNode = GetRoot();
1255 46447 : if (poNode)
1256 45244 : poNode = poNode->GetNode(pszNodePath);
1257 46447 : return poNode;
1258 : }
1259 :
1260 : char **papszPathTokens =
1261 38160 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1262 :
1263 38160 : if (CSLCount(papszPathTokens) < 1)
1264 : {
1265 0 : CSLDestroy(papszPathTokens);
1266 0 : return nullptr;
1267 : }
1268 :
1269 38160 : OGR_SRSNode *poNode = GetRoot();
1270 115739 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1271 : {
1272 77579 : poNode = poNode->GetNode(papszPathTokens[i]);
1273 : }
1274 :
1275 38160 : CSLDestroy(papszPathTokens);
1276 :
1277 38160 : return poNode;
1278 : }
1279 :
1280 : /**
1281 : * \brief Find named node in tree.
1282 : *
1283 : * This method does a pre-order traversal of the node tree searching for
1284 : * a node with this exact value (case insensitive), and returns it. Leaf
1285 : * nodes are not considered, under the assumption that they are just
1286 : * attribute value nodes.
1287 : *
1288 : * If a node appears more than once in the tree (such as UNIT for instance),
1289 : * the first encountered will be returned. Use GetNode() on a subtree to be
1290 : * more specific.
1291 : *
1292 : * @param pszNodePath the name of the node to search for. May contain multiple
1293 : * components such as "GEOGCS|UNIT".
1294 : *
1295 : * @return a pointer to the node found, or NULL if none.
1296 : */
1297 :
1298 : const OGR_SRSNode *
1299 76583 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1300 :
1301 : {
1302 : OGR_SRSNode *poNode =
1303 76583 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1304 :
1305 76583 : return poNode;
1306 : }
1307 :
1308 : /************************************************************************/
1309 : /* GetAttrValue() */
1310 : /************************************************************************/
1311 :
1312 : /**
1313 : * \brief Fetch indicated attribute of named node.
1314 : *
1315 : * This method uses GetAttrNode() to find the named node, and then extracts
1316 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1317 : * would return the second child of the UNIT node, which is normally the
1318 : * length of the linear unit in meters.
1319 : *
1320 : * This method does the same thing as the C function OSRGetAttrValue().
1321 : *
1322 : * @param pszNodeName the tree node to look for (case insensitive).
1323 : * @param iAttr the child of the node to fetch (zero based).
1324 : *
1325 : * @return the requested value, or NULL if it fails for any reason.
1326 : */
1327 :
1328 23256 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1329 : int iAttr) const
1330 :
1331 : {
1332 23256 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1333 23256 : if (poNode == nullptr)
1334 : {
1335 10058 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1336 : {
1337 14 : return GetAttrValue("METHOD", iAttr);
1338 : }
1339 10044 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1340 : {
1341 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1342 : }
1343 10044 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1344 : {
1345 1 : return GetAttrValue("PROJCRS", iAttr);
1346 : }
1347 10043 : return nullptr;
1348 : }
1349 :
1350 13198 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1351 0 : return nullptr;
1352 :
1353 13198 : return poNode->GetChild(iAttr)->GetValue();
1354 : }
1355 :
1356 : /************************************************************************/
1357 : /* OSRGetAttrValue() */
1358 : /************************************************************************/
1359 :
1360 : /**
1361 : * \brief Fetch indicated attribute of named node.
1362 : *
1363 : * This function is the same as OGRSpatialReference::GetAttrValue()
1364 : */
1365 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1366 : const char *pszKey, int iChild)
1367 :
1368 : {
1369 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1370 :
1371 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1372 : }
1373 :
1374 : /************************************************************************/
1375 : /* GetName() */
1376 : /************************************************************************/
1377 :
1378 : /**
1379 : * \brief Return the CRS name.
1380 : *
1381 : * The returned value is only short lived and should not be used after other
1382 : * calls to methods on this object.
1383 : *
1384 : * @since GDAL 3.0
1385 : */
1386 :
1387 5706 : const char *OGRSpatialReference::GetName() const
1388 : {
1389 11412 : TAKE_OPTIONAL_LOCK();
1390 :
1391 5706 : d->refreshProjObj();
1392 5706 : if (!d->m_pj_crs)
1393 113 : return nullptr;
1394 5593 : const char *pszName = proj_get_name(d->m_pj_crs);
1395 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1396 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1397 : {
1398 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1399 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1400 : if (baseCRS)
1401 : {
1402 : pszName = proj_get_name(baseCRS);
1403 : // pszName still remains valid after proj_destroy(), since
1404 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1405 : proj_destroy(baseCRS);
1406 : }
1407 : }
1408 : #endif
1409 5593 : return pszName;
1410 : }
1411 :
1412 : /************************************************************************/
1413 : /* OSRGetName() */
1414 : /************************************************************************/
1415 :
1416 : /**
1417 : * \brief Return the CRS name.
1418 : *
1419 : * The returned value is only short lived and should not be used after other
1420 : * calls to methods on this object.
1421 : *
1422 : * @since GDAL 3.0
1423 : */
1424 44 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1425 :
1426 : {
1427 44 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1428 :
1429 44 : return ToPointer(hSRS)->GetName();
1430 : }
1431 :
1432 : /************************************************************************/
1433 : /* GetCelestialBodyName() */
1434 : /************************************************************************/
1435 :
1436 : /**
1437 : * \brief Return the name of the celestial body of this CRS.
1438 : *
1439 : * e.g. "Earth" for an Earth CRS
1440 : *
1441 : * The returned value is only short lived and should not be used after other
1442 : * calls to methods on this object.
1443 : *
1444 : * @since GDAL 3.12 and PROJ 8.1
1445 : */
1446 :
1447 3 : const char *OGRSpatialReference::GetCelestialBodyName() const
1448 : {
1449 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1450 :
1451 : TAKE_OPTIONAL_LOCK();
1452 :
1453 : d->refreshProjObj();
1454 : if (!d->m_pj_crs)
1455 : return nullptr;
1456 : d->demoteFromBoundCRS();
1457 : const char *name =
1458 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1459 : if (name)
1460 : {
1461 : d->m_celestialBodyName = name;
1462 : }
1463 : d->undoDemoteFromBoundCRS();
1464 : return d->m_celestialBodyName.c_str();
1465 : #else
1466 3 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1467 : 0.05 * SRS_WGS84_SEMIMAJOR)
1468 3 : return "Earth";
1469 0 : const char *pszAuthName = GetAuthorityName(nullptr);
1470 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1471 0 : return "Earth";
1472 0 : return nullptr;
1473 : #endif
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* OSRGetCelestialBodyName() */
1478 : /************************************************************************/
1479 :
1480 : /**
1481 : * \brief Return the name of the celestial body of this CRS.
1482 : *
1483 : * e.g. "Earth" for an Earth CRS
1484 : *
1485 : * The returned value is only short lived and should not be used after other
1486 : * calls to methods on this object.
1487 : *
1488 : * @since GDAL 3.12 and PROJ 8.1
1489 : */
1490 :
1491 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1492 :
1493 : {
1494 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1495 :
1496 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1497 : }
1498 :
1499 : /************************************************************************/
1500 : /* Clone() */
1501 : /************************************************************************/
1502 :
1503 : /**
1504 : * \brief Make a duplicate of this OGRSpatialReference.
1505 : *
1506 : * This method is the same as the C function OSRClone().
1507 : *
1508 : * @return a new SRS, which becomes the responsibility of the caller.
1509 : */
1510 :
1511 32270 : OGRSpatialReference *OGRSpatialReference::Clone() const
1512 :
1513 : {
1514 32270 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1515 :
1516 32272 : TAKE_OPTIONAL_LOCK();
1517 :
1518 32272 : d->refreshProjObj();
1519 32272 : if (d->m_pj_crs != nullptr)
1520 32212 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1521 32271 : if (d->m_bHasCenterLong && d->m_poRoot)
1522 : {
1523 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1524 : }
1525 32271 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1526 32271 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1527 32270 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1528 64542 : return poNewRef;
1529 : }
1530 :
1531 : /************************************************************************/
1532 : /* OSRClone() */
1533 : /************************************************************************/
1534 :
1535 : /**
1536 : * \brief Make a duplicate of this OGRSpatialReference.
1537 : *
1538 : * This function is the same as OGRSpatialReference::Clone()
1539 : */
1540 1120 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1541 :
1542 : {
1543 1120 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1544 :
1545 1120 : return ToHandle(ToPointer(hSRS)->Clone());
1546 : }
1547 :
1548 : /************************************************************************/
1549 : /* dumpReadable() */
1550 : /************************************************************************/
1551 :
1552 : /** Dump pretty wkt to stdout, mostly for debugging.
1553 : */
1554 0 : void OGRSpatialReference::dumpReadable()
1555 :
1556 : {
1557 0 : char *pszPrettyWkt = nullptr;
1558 :
1559 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1560 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1561 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1562 0 : CPLFree(pszPrettyWkt);
1563 0 : }
1564 :
1565 : /************************************************************************/
1566 : /* exportToPrettyWkt() */
1567 : /************************************************************************/
1568 :
1569 : /**
1570 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1571 : * person.
1572 : *
1573 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1574 : * Issues</a> page for implementation details of WKT 1 in OGR.
1575 : *
1576 : * Note that the returned WKT string should be freed with
1577 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1578 : *
1579 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1580 : * option. Valid values are the one of the FORMAT option of
1581 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1582 : *
1583 : * This method is the same as the C function OSRExportToPrettyWkt().
1584 : *
1585 : * @param ppszResult the resulting string is returned in this pointer.
1586 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1587 : * stripped off.
1588 : *
1589 : * @return OGRERR_NONE if successful.
1590 : */
1591 :
1592 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1593 : int bSimplify) const
1594 :
1595 : {
1596 116 : CPLStringList aosOptions;
1597 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1598 58 : if (bSimplify)
1599 : {
1600 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1601 : }
1602 116 : return exportToWkt(ppszResult, aosOptions.List());
1603 : }
1604 :
1605 : /************************************************************************/
1606 : /* OSRExportToPrettyWkt() */
1607 : /************************************************************************/
1608 :
1609 : /**
1610 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1611 : * person.
1612 : *
1613 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1614 : * option. Valid values are the one of the FORMAT option of
1615 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1616 : *
1617 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1618 : */
1619 :
1620 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1621 : char **ppszReturn, int bSimplify)
1622 :
1623 : {
1624 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1625 :
1626 56 : *ppszReturn = nullptr;
1627 :
1628 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1629 : }
1630 :
1631 : /************************************************************************/
1632 : /* exportToWkt() */
1633 : /************************************************************************/
1634 :
1635 : /**
1636 : * \brief Convert this SRS into WKT 1 format.
1637 : *
1638 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1639 : * Issues</a> page for implementation details of WKT 1 in OGR.
1640 : *
1641 : * Note that the returned WKT string should be freed with
1642 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1643 : *
1644 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1645 : * option. Valid values are the one of the FORMAT option of
1646 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1647 : *
1648 : * This method is the same as the C function OSRExportToWkt().
1649 : *
1650 : * @param ppszResult the resulting string is returned in this pointer.
1651 : *
1652 : * @return OGRERR_NONE if successful.
1653 : */
1654 :
1655 12399 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1656 :
1657 : {
1658 12399 : return exportToWkt(ppszResult, nullptr);
1659 : }
1660 :
1661 : /************************************************************************/
1662 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1663 : /************************************************************************/
1664 :
1665 573 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1666 : bool onlyIfEPSGCode,
1667 : bool canModifyHorizPart)
1668 : {
1669 573 : PJ *ret = nullptr;
1670 573 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1671 : {
1672 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1673 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1674 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1675 26 : vertCRS &&
1676 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1677 : {
1678 : auto boundHoriz =
1679 : canModifyHorizPart
1680 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1681 3 : : proj_clone(ctx, horizCRS);
1682 : auto boundVert =
1683 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1684 3 : if (boundHoriz && boundVert)
1685 : {
1686 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1687 : boundHoriz, boundVert);
1688 : }
1689 3 : proj_destroy(boundHoriz);
1690 3 : proj_destroy(boundVert);
1691 : }
1692 13 : proj_destroy(horizCRS);
1693 13 : proj_destroy(vertCRS);
1694 : }
1695 1080 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1696 520 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1697 : {
1698 242 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1699 : }
1700 573 : return ret;
1701 : }
1702 :
1703 : /************************************************************************/
1704 : /* exportToWkt() */
1705 : /************************************************************************/
1706 :
1707 : /**
1708 : * Convert this SRS into a WKT string.
1709 : *
1710 : * Note that the returned WKT string should be freed with
1711 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1712 : *
1713 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1714 : * Issues</a> page for implementation details of WKT 1 in OGR.
1715 : *
1716 : * @param ppszResult the resulting string is returned in this pointer.
1717 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1718 : * supported options are
1719 : * <ul>
1720 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1721 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1722 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1723 : * node is returned.
1724 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1725 : * node is returned.
1726 : * WKT1 is an alias of WKT1_GDAL.
1727 : * WKT2 will default to the latest revision implemented (currently
1728 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1729 : * </li>
1730 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1731 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1732 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1733 : * height (for example for use with LAS 1.4 WKT1).
1734 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1735 : * </ul>
1736 : *
1737 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1738 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1739 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1740 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1741 : * TOWGS84[] node may be added.
1742 : *
1743 : * @return OGRERR_NONE if successful.
1744 : * @since GDAL 3.0
1745 : */
1746 :
1747 16954 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1748 : const char *const *papszOptions) const
1749 : {
1750 : // In the past calling this method was thread-safe, even if we never
1751 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1752 : // so this is no longer thread-safe.
1753 33908 : std::lock_guard oLock(d->m_mutex);
1754 :
1755 16954 : d->refreshProjObj();
1756 16954 : if (!d->m_pj_crs)
1757 : {
1758 21 : *ppszResult = CPLStrdup("");
1759 21 : return OGRERR_FAILURE;
1760 : }
1761 :
1762 16933 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1763 : {
1764 0 : return d->m_poRoot->exportToWkt(ppszResult);
1765 : }
1766 :
1767 16933 : auto ctxt = d->getPROJContext();
1768 16933 : auto wktFormat = PJ_WKT1_GDAL;
1769 : const char *pszFormat =
1770 16933 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1771 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1772 16933 : if (EQUAL(pszFormat, "DEFAULT"))
1773 14313 : pszFormat = "";
1774 :
1775 16933 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1776 : {
1777 644 : wktFormat = PJ_WKT1_ESRI;
1778 : }
1779 16289 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1780 15571 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1781 : {
1782 723 : wktFormat = PJ_WKT1_GDAL;
1783 : }
1784 15566 : else if (EQUAL(pszFormat, "WKT2_2015"))
1785 : {
1786 297 : wktFormat = PJ_WKT2_2015;
1787 : }
1788 15269 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1789 14917 : EQUAL(pszFormat, "WKT2_2019"))
1790 : {
1791 1185 : wktFormat = PJ_WKT2_2018;
1792 : }
1793 14084 : else if (pszFormat[0] == '\0')
1794 : {
1795 : // cppcheck-suppress knownConditionTrueFalse
1796 14084 : if (IsDerivedGeographic())
1797 : {
1798 2 : wktFormat = PJ_WKT2_2018;
1799 : }
1800 27534 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1801 13452 : GetAxesCount() == 3)
1802 : {
1803 56 : wktFormat = PJ_WKT2_2018;
1804 : }
1805 : }
1806 : else
1807 : {
1808 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1809 0 : *ppszResult = CPLStrdup("");
1810 0 : return OGRERR_FAILURE;
1811 : }
1812 :
1813 33866 : CPLStringList aosOptions;
1814 16933 : if (wktFormat != PJ_WKT1_ESRI)
1815 : {
1816 16289 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1817 : }
1818 : aosOptions.SetNameValue(
1819 16933 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1820 :
1821 16933 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1822 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1823 16933 : if (pszAllowEllpsHeightAsVertCS)
1824 : {
1825 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1826 0 : pszAllowEllpsHeightAsVertCS);
1827 : }
1828 :
1829 16933 : PJ *boundCRS = nullptr;
1830 31682 : if (wktFormat == PJ_WKT1_GDAL &&
1831 14749 : CPLTestBool(CSLFetchNameValueDef(
1832 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1833 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1834 : {
1835 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1836 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1837 : }
1838 :
1839 33866 : CPLErrorAccumulator oErrorAccumulator;
1840 : const char *pszWKT;
1841 : {
1842 16933 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1843 16933 : CPL_IGNORE_RET_VAL(oAccumulator);
1844 16933 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1845 16933 : aosOptions.List());
1846 : }
1847 16935 : for (const auto &oError : oErrorAccumulator.GetErrors())
1848 : {
1849 32 : if (pszFormat[0] == '\0' &&
1850 14 : (oError.msg.find("Unsupported conversion method") !=
1851 2 : std::string::npos ||
1852 2 : oError.msg.find("can only be exported to WKT2") !=
1853 0 : std::string::npos ||
1854 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1855 : std::string::npos))
1856 : {
1857 14 : CPLErrorReset();
1858 : // If we cannot export in the default mode (WKT1), retry with WKT2
1859 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1860 14 : PJ_WKT2_2018, aosOptions.List());
1861 14 : break;
1862 : }
1863 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1864 : }
1865 :
1866 16933 : if (!pszWKT)
1867 : {
1868 2 : *ppszResult = CPLStrdup("");
1869 2 : proj_destroy(boundCRS);
1870 2 : return OGRERR_FAILURE;
1871 : }
1872 :
1873 16931 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1874 : {
1875 5 : OGR_SRSNode oRoot;
1876 5 : oRoot.importFromWkt(&pszWKT);
1877 5 : oRoot.StripNodes("AXIS");
1878 5 : if (EQUAL(pszFormat, "SFSQL"))
1879 : {
1880 3 : oRoot.StripNodes("TOWGS84");
1881 : }
1882 5 : oRoot.StripNodes("AUTHORITY");
1883 5 : oRoot.StripNodes("EXTENSION");
1884 : OGRErr eErr;
1885 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1886 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1887 : else
1888 3 : eErr = oRoot.exportToWkt(ppszResult);
1889 5 : proj_destroy(boundCRS);
1890 5 : return eErr;
1891 : }
1892 :
1893 16926 : *ppszResult = CPLStrdup(pszWKT);
1894 :
1895 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1896 16926 : if (wktFormat == PJ_WKT2_2018)
1897 : {
1898 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1899 : // related to a wrong EPSG code assigned to UTM South conversions
1900 1243 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1901 1243 : if (pszPtr)
1902 : {
1903 303 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1904 303 : const int nZone = atoi(pszPtr);
1905 908 : while (*pszPtr >= '0' && *pszPtr <= '9')
1906 605 : ++pszPtr;
1907 303 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1908 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1909 : {
1910 1 : pszPtr += 3;
1911 1 : int nLevel = 0;
1912 1 : bool bInString = false;
1913 : // Find the ID node corresponding to this CONVERSION node
1914 480 : while (*pszPtr)
1915 : {
1916 480 : if (bInString)
1917 : {
1918 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1919 : {
1920 0 : ++pszPtr;
1921 : }
1922 197 : else if (*pszPtr == '"')
1923 : {
1924 17 : bInString = false;
1925 : }
1926 : }
1927 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1928 : {
1929 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1930 : 17000 + nZone)))
1931 : {
1932 1 : CPLAssert(pszPtr[11] == '7');
1933 1 : CPLAssert(pszPtr[12] == '0');
1934 1 : pszPtr[11] = '6';
1935 1 : pszPtr[12] = '1';
1936 : }
1937 1 : break;
1938 : }
1939 282 : else if (*pszPtr == '"')
1940 : {
1941 17 : bInString = true;
1942 : }
1943 265 : else if (*pszPtr == '[')
1944 : {
1945 17 : ++nLevel;
1946 : }
1947 248 : else if (*pszPtr == ']')
1948 : {
1949 17 : --nLevel;
1950 : }
1951 :
1952 479 : ++pszPtr;
1953 : }
1954 : }
1955 : }
1956 : }
1957 : #endif
1958 :
1959 16926 : proj_destroy(boundCRS);
1960 16926 : return OGRERR_NONE;
1961 : }
1962 :
1963 : /************************************************************************/
1964 : /* exportToWkt() */
1965 : /************************************************************************/
1966 :
1967 : /**
1968 : * Convert this SRS into a WKT string.
1969 : *
1970 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1971 : * Issues</a> page for implementation details of WKT 1 in OGR.
1972 : *
1973 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1974 : * supported options are
1975 : * <ul>
1976 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1977 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1978 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1979 : * node is returned.
1980 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1981 : * node is returned.
1982 : * WKT1 is an alias of WKT1_GDAL.
1983 : * WKT2 will default to the latest revision implemented (currently
1984 : * WKT2_2019)
1985 : * </li>
1986 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1987 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1988 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1989 : * height (for example for use with LAS 1.4 WKT1).
1990 : * Requires PROJ 7.2.1.</li>
1991 : * </ul>
1992 : *
1993 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1994 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1995 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1996 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1997 : * TOWGS84[] node may be added.
1998 : *
1999 : * @return a non-empty string if successful.
2000 : * @since GDAL 3.9
2001 : */
2002 :
2003 : std::string
2004 287 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2005 : {
2006 287 : std::string osWKT;
2007 287 : char *pszWKT = nullptr;
2008 287 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2009 287 : osWKT = pszWKT;
2010 287 : CPLFree(pszWKT);
2011 574 : return osWKT;
2012 : }
2013 :
2014 : /************************************************************************/
2015 : /* OSRExportToWkt() */
2016 : /************************************************************************/
2017 :
2018 : /**
2019 : * \brief Convert this SRS into WKT 1 format.
2020 : *
2021 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2022 : * Issues</a> page for implementation details of WKT in OGR.
2023 : *
2024 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2025 : * option. Valid values are the one of the FORMAT option of
2026 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2027 : *
2028 : * This function is the same as OGRSpatialReference::exportToWkt().
2029 : */
2030 :
2031 915 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2032 :
2033 : {
2034 915 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2035 :
2036 915 : *ppszReturn = nullptr;
2037 :
2038 915 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2039 : }
2040 :
2041 : /************************************************************************/
2042 : /* OSRExportToWktEx() */
2043 : /************************************************************************/
2044 :
2045 : /**
2046 : * \brief Convert this SRS into WKT format.
2047 : *
2048 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2049 : * ppszResult,const char* const* papszOptions ) const
2050 : *
2051 : * @since GDAL 3.0
2052 : */
2053 :
2054 1336 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2055 : const char *const *papszOptions)
2056 : {
2057 1336 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2058 :
2059 1336 : *ppszReturn = nullptr;
2060 :
2061 1336 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2062 : }
2063 :
2064 : /************************************************************************/
2065 : /* exportToPROJJSON() */
2066 : /************************************************************************/
2067 :
2068 : /**
2069 : * Convert this SRS into a PROJJSON string.
2070 : *
2071 : * Note that the returned JSON string should be freed with
2072 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2073 : *
2074 : * @param ppszResult the resulting string is returned in this pointer.
2075 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2076 : * supported options are
2077 : * <ul>
2078 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2079 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2080 : * on).</li>
2081 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2082 : * disable it.</li>
2083 : * </ul>
2084 : *
2085 : * @return OGRERR_NONE if successful.
2086 : * @since GDAL 3.1 and PROJ 6.2
2087 : */
2088 :
2089 2511 : OGRErr OGRSpatialReference::exportToPROJJSON(
2090 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2091 : {
2092 5022 : TAKE_OPTIONAL_LOCK();
2093 :
2094 2511 : d->refreshProjObj();
2095 2511 : if (!d->m_pj_crs)
2096 : {
2097 1 : *ppszResult = nullptr;
2098 1 : return OGRERR_FAILURE;
2099 : }
2100 :
2101 : const char *pszPROJJSON =
2102 2510 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2103 :
2104 2510 : if (!pszPROJJSON)
2105 : {
2106 0 : *ppszResult = CPLStrdup("");
2107 0 : return OGRERR_FAILURE;
2108 : }
2109 :
2110 2510 : *ppszResult = CPLStrdup(pszPROJJSON);
2111 :
2112 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2113 : {
2114 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2115 : // related to a wrong EPSG code assigned to UTM South conversions
2116 2510 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2117 2510 : if (pszPtr)
2118 : {
2119 238 : pszPtr += strlen("\"name\": \"UTM zone ");
2120 238 : const int nZone = atoi(pszPtr);
2121 713 : while (*pszPtr >= '0' && *pszPtr <= '9')
2122 475 : ++pszPtr;
2123 238 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2124 : {
2125 4 : pszPtr += 2;
2126 4 : int nLevel = 0;
2127 4 : bool bInString = false;
2128 : // Find the id node corresponding to this conversion node
2129 5299 : while (*pszPtr)
2130 : {
2131 5299 : if (bInString)
2132 : {
2133 1950 : if (*pszPtr == '\\')
2134 : {
2135 0 : ++pszPtr;
2136 : }
2137 1950 : else if (*pszPtr == '"')
2138 : {
2139 244 : bInString = false;
2140 : }
2141 : }
2142 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2143 : {
2144 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2145 : const char *pszAuthEPSG =
2146 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2147 4 : char *pszCode = strstr(
2148 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2149 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2150 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2151 4 : pszNextEndCurl - pszCode > 0)
2152 : {
2153 4 : CPLAssert(pszCode[9] == '7');
2154 4 : CPLAssert(pszCode[10] == '0');
2155 4 : pszCode[9] = '6';
2156 4 : pszCode[10] = '1';
2157 : }
2158 4 : break;
2159 : }
2160 3345 : else if (*pszPtr == '"')
2161 : {
2162 244 : bInString = true;
2163 : }
2164 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2165 : {
2166 60 : ++nLevel;
2167 : }
2168 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2169 : {
2170 60 : --nLevel;
2171 : }
2172 :
2173 5295 : ++pszPtr;
2174 : }
2175 : }
2176 : }
2177 : }
2178 : #endif
2179 :
2180 2510 : return OGRERR_NONE;
2181 : }
2182 :
2183 : /************************************************************************/
2184 : /* OSRExportToPROJJSON() */
2185 : /************************************************************************/
2186 :
2187 : /**
2188 : * \brief Convert this SRS into PROJJSON format.
2189 : *
2190 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2191 : *
2192 : * @since GDAL 3.1 and PROJ 6.2
2193 : */
2194 :
2195 73 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2196 : const char *const *papszOptions)
2197 : {
2198 73 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2199 :
2200 73 : *ppszReturn = nullptr;
2201 :
2202 73 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2203 : }
2204 :
2205 : /************************************************************************/
2206 : /* importFromWkt() */
2207 : /************************************************************************/
2208 :
2209 : /**
2210 : * \brief Import from WKT string.
2211 : *
2212 : * This method will wipe the existing SRS definition, and
2213 : * reassign it based on the contents of the passed WKT string. Only as
2214 : * much of the input string as needed to construct this SRS is consumed from
2215 : * the input string, and the input string pointer
2216 : * is then updated to point to the remaining (unused) input.
2217 : *
2218 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2219 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2220 : * and the coordinate epoch potentially present used as the coordinate epoch
2221 : * property of the OGRSpatialReference object.
2222 : *
2223 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2224 : * Issues</a> page for implementation details of WKT in OGR.
2225 : *
2226 : * This method is the same as the C function OSRImportFromWkt().
2227 : *
2228 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2229 : * point to remaining unused input text.
2230 : *
2231 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2232 : * fails for any reason.
2233 : */
2234 :
2235 17240 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2236 :
2237 : {
2238 17240 : return importFromWkt(ppszInput, nullptr);
2239 : }
2240 :
2241 : /************************************************************************/
2242 : /* importFromWkt() */
2243 : /************************************************************************/
2244 :
2245 : /*! @cond Doxygen_Suppress */
2246 :
2247 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2248 : CSLConstList papszOptions)
2249 :
2250 : {
2251 21 : return importFromWkt(&pszInput, papszOptions);
2252 : }
2253 :
2254 17261 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2255 : CSLConstList papszOptions)
2256 :
2257 : {
2258 34522 : TAKE_OPTIONAL_LOCK();
2259 :
2260 17261 : if (!ppszInput || !*ppszInput)
2261 0 : return OGRERR_FAILURE;
2262 :
2263 17261 : if (strlen(*ppszInput) > 100 * 1000 &&
2264 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2265 : {
2266 0 : CPLError(CE_Failure, CPLE_NotSupported,
2267 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2268 : "You can remove this limitation by definition the "
2269 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2270 0 : return OGRERR_FAILURE;
2271 : }
2272 :
2273 17261 : Clear();
2274 :
2275 17261 : bool canCache = false;
2276 17261 : auto tlsCache = OSRGetProjTLSCache();
2277 34522 : std::string osWkt;
2278 17261 : if (**ppszInput)
2279 : {
2280 16705 : osWkt = *ppszInput;
2281 16705 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2282 16705 : if (cachedObj)
2283 : {
2284 14890 : d->setPjCRS(cachedObj);
2285 : }
2286 : else
2287 : {
2288 3630 : CPLStringList aosOptions(papszOptions);
2289 1815 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2290 1815 : aosOptions.SetNameValue("STRICT", "NO");
2291 1815 : PROJ_STRING_LIST warnings = nullptr;
2292 1815 : PROJ_STRING_LIST errors = nullptr;
2293 1815 : auto ctxt = d->getPROJContext();
2294 1815 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2295 : &warnings, &errors);
2296 1815 : d->setPjCRS(pj);
2297 :
2298 1868 : for (auto iter = warnings; iter && *iter; ++iter)
2299 : {
2300 53 : d->m_wktImportWarnings.push_back(*iter);
2301 : }
2302 2051 : for (auto iter = errors; iter && *iter; ++iter)
2303 : {
2304 236 : d->m_wktImportErrors.push_back(*iter);
2305 236 : if (!d->m_pj_crs)
2306 : {
2307 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2308 : }
2309 : }
2310 1815 : if (warnings == nullptr && errors == nullptr)
2311 : {
2312 1535 : canCache = true;
2313 : }
2314 1815 : proj_string_list_destroy(warnings);
2315 1815 : proj_string_list_destroy(errors);
2316 : }
2317 : }
2318 17261 : if (!d->m_pj_crs)
2319 590 : return OGRERR_CORRUPT_DATA;
2320 :
2321 : // Only accept CRS objects
2322 16671 : if (!proj_is_crs(d->m_pj_crs))
2323 : {
2324 0 : Clear();
2325 0 : return OGRERR_CORRUPT_DATA;
2326 : }
2327 :
2328 16671 : if (canCache)
2329 : {
2330 1535 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2331 : }
2332 :
2333 16671 : if (strstr(*ppszInput, "CENTER_LONG"))
2334 : {
2335 0 : auto poRoot = new OGR_SRSNode();
2336 0 : d->setRoot(poRoot);
2337 0 : const char *pszTmp = *ppszInput;
2338 0 : poRoot->importFromWkt(&pszTmp);
2339 0 : d->m_bHasCenterLong = true;
2340 : }
2341 :
2342 : // TODO? we don't really update correctly since we assume that the
2343 : // passed string is only WKT.
2344 16671 : *ppszInput += strlen(*ppszInput);
2345 16671 : return OGRERR_NONE;
2346 :
2347 : #if no_longer_implemented_for_now
2348 : /* -------------------------------------------------------------------- */
2349 : /* The following seems to try and detect and unconsumed */
2350 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2351 : /* import and attach it to the existing root. Likely we will */
2352 : /* need to extend this somewhat to bring it into an acceptable */
2353 : /* OGRSpatialReference organization at some point. */
2354 : /* -------------------------------------------------------------------- */
2355 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2356 : {
2357 : if (((*ppszInput)[0]) == ',')
2358 : (*ppszInput)++;
2359 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2360 : poRoot->AddChild(poNewChild);
2361 : return poNewChild->importFromWkt(ppszInput);
2362 : }
2363 : #endif
2364 : }
2365 :
2366 : /*! @endcond */
2367 :
2368 : /**
2369 : * \brief Import from WKT string.
2370 : *
2371 : * This method will wipe the existing SRS definition, and
2372 : * reassign it based on the contents of the passed WKT string. Only as
2373 : * much of the input string as needed to construct this SRS is consumed from
2374 : * the input string, and the input string pointer
2375 : * is then updated to point to the remaining (unused) input.
2376 : *
2377 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2378 : * Issues</a> page for implementation details of WKT in OGR.
2379 : *
2380 : * This method is the same as the C function OSRImportFromWkt().
2381 : *
2382 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2383 : * point to remaining unused input text.
2384 : *
2385 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2386 : * fails for any reason.
2387 : * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2388 : * char*)
2389 : */
2390 :
2391 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2392 :
2393 : {
2394 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2395 : }
2396 :
2397 : /**
2398 : * \brief Import from WKT string.
2399 : *
2400 : * This method will wipe the existing SRS definition, and
2401 : * reassign it based on the contents of the passed WKT string. Only as
2402 : * much of the input string as needed to construct this SRS is consumed from
2403 : * the input string, and the input string pointer
2404 : * is then updated to point to the remaining (unused) input.
2405 : *
2406 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2407 : * Issues</a> page for implementation details of WKT in OGR.
2408 : *
2409 : * @param pszInput Input WKT
2410 : *
2411 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2412 : * fails for any reason.
2413 : */
2414 :
2415 16950 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2416 : {
2417 16950 : return importFromWkt(&pszInput);
2418 : }
2419 :
2420 : /************************************************************************/
2421 : /* Validate() */
2422 : /************************************************************************/
2423 :
2424 : /**
2425 : * \brief Validate CRS imported with importFromWkt() or with modified with
2426 : * direct node manipulations. Otherwise the CRS should be always valid.
2427 : *
2428 : * This method attempts to verify that the spatial reference system is
2429 : * well formed, and consists of known tokens. The validation is not
2430 : * comprehensive.
2431 : *
2432 : * This method is the same as the C function OSRValidate().
2433 : *
2434 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2435 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2436 : * but contains non-standard PROJECTION[] values.
2437 : */
2438 :
2439 116 : OGRErr OGRSpatialReference::Validate() const
2440 :
2441 : {
2442 232 : TAKE_OPTIONAL_LOCK();
2443 :
2444 154 : for (const auto &str : d->m_wktImportErrors)
2445 : {
2446 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2447 : }
2448 116 : for (const auto &str : d->m_wktImportWarnings)
2449 : {
2450 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2451 : }
2452 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2453 : {
2454 37 : return OGRERR_CORRUPT_DATA;
2455 : }
2456 79 : if (!d->m_wktImportWarnings.empty())
2457 : {
2458 0 : return OGRERR_UNSUPPORTED_SRS;
2459 : }
2460 79 : return OGRERR_NONE;
2461 : }
2462 :
2463 : /************************************************************************/
2464 : /* OSRValidate() */
2465 : /************************************************************************/
2466 : /**
2467 : * \brief Validate SRS tokens.
2468 : *
2469 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2470 : */
2471 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2472 :
2473 : {
2474 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2475 :
2476 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2477 : }
2478 :
2479 : /************************************************************************/
2480 : /* OSRImportFromWkt() */
2481 : /************************************************************************/
2482 :
2483 : /**
2484 : * \brief Import from WKT string.
2485 : *
2486 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2487 : * Issues</a> page for implementation details of WKT in OGR.
2488 : *
2489 : * This function is the same as OGRSpatialReference::importFromWkt().
2490 : */
2491 :
2492 290 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2493 :
2494 : {
2495 290 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2496 :
2497 290 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2498 : }
2499 :
2500 : /************************************************************************/
2501 : /* SetNode() */
2502 : /************************************************************************/
2503 :
2504 : /**
2505 : * \brief Set attribute value in spatial reference.
2506 : *
2507 : * Missing intermediate nodes in the path will be created if not already
2508 : * in existence. If the attribute has no children one will be created and
2509 : * assigned the value otherwise the zeroth child will be assigned the value.
2510 : *
2511 : * This method does the same as the C function OSRSetAttrValue().
2512 : *
2513 : * @param pszNodePath full path to attribute to be set. For instance
2514 : * "PROJCS|GEOGCS|UNIT".
2515 : *
2516 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2517 : * This may be NULL if you just want to force creation of the intermediate
2518 : * path.
2519 : *
2520 : * @return OGRERR_NONE on success.
2521 : */
2522 :
2523 583 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2524 : const char *pszNewNodeValue)
2525 :
2526 : {
2527 1166 : TAKE_OPTIONAL_LOCK();
2528 :
2529 : char **papszPathTokens =
2530 583 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2531 :
2532 583 : if (CSLCount(papszPathTokens) < 1)
2533 : {
2534 0 : CSLDestroy(papszPathTokens);
2535 0 : return OGRERR_FAILURE;
2536 : }
2537 :
2538 1018 : if (GetRoot() == nullptr ||
2539 435 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2540 : {
2541 268 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2542 116 : CSLCount(papszPathTokens) == 1)
2543 : {
2544 116 : CSLDestroy(papszPathTokens);
2545 116 : return SetProjCS(pszNewNodeValue);
2546 : }
2547 : else
2548 : {
2549 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2550 : }
2551 : }
2552 :
2553 467 : OGR_SRSNode *poNode = GetRoot();
2554 725 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2555 : {
2556 258 : int j = 0; // Used after for.
2557 :
2558 645 : for (; j < poNode->GetChildCount(); j++)
2559 : {
2560 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2561 : {
2562 198 : poNode = poNode->GetChild(j);
2563 198 : j = -1;
2564 198 : break;
2565 : }
2566 : }
2567 :
2568 258 : if (j != -1)
2569 : {
2570 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2571 60 : poNode->AddChild(poNewNode);
2572 60 : poNode = poNewNode;
2573 : }
2574 : }
2575 :
2576 467 : CSLDestroy(papszPathTokens);
2577 :
2578 467 : if (pszNewNodeValue != nullptr)
2579 : {
2580 467 : if (poNode->GetChildCount() > 0)
2581 371 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2582 : else
2583 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2584 : };
2585 467 : return OGRERR_NONE;
2586 : }
2587 :
2588 : /************************************************************************/
2589 : /* OSRSetAttrValue() */
2590 : /************************************************************************/
2591 :
2592 : /**
2593 : * \brief Set attribute value in spatial reference.
2594 : *
2595 : * This function is the same as OGRSpatialReference::SetNode()
2596 : */
2597 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2598 : const char *pszPath, const char *pszValue)
2599 :
2600 : {
2601 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2602 :
2603 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2604 : }
2605 :
2606 : /************************************************************************/
2607 : /* SetNode() */
2608 : /************************************************************************/
2609 :
2610 : /**
2611 : * \brief Set attribute value in spatial reference.
2612 : *
2613 : * Missing intermediate nodes in the path will be created if not already
2614 : * in existence. If the attribute has no children one will be created and
2615 : * assigned the value otherwise the zeroth child will be assigned the value.
2616 : *
2617 : * This method does the same as the C function OSRSetAttrValue().
2618 : *
2619 : * @param pszNodePath full path to attribute to be set. For instance
2620 : * "PROJCS|GEOGCS|UNIT".
2621 : *
2622 : * @param dfValue value to be assigned to node.
2623 : *
2624 : * @return OGRERR_NONE on success.
2625 : */
2626 :
2627 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2628 :
2629 : {
2630 0 : char szValue[64] = {'\0'};
2631 :
2632 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2633 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2634 : else
2635 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2636 :
2637 0 : return SetNode(pszNodePath, szValue);
2638 : }
2639 :
2640 : /************************************************************************/
2641 : /* SetAngularUnits() */
2642 : /************************************************************************/
2643 :
2644 : /**
2645 : * \brief Set the angular units for the geographic coordinate system.
2646 : *
2647 : * This method creates a UNIT subnode with the specified values as a
2648 : * child of the GEOGCS node.
2649 : *
2650 : * This method does the same as the C function OSRSetAngularUnits().
2651 : *
2652 : * @param pszUnitsName the units name to be used. Some preferred units
2653 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2654 : *
2655 : * @param dfInRadians the value to multiple by an angle in the indicated
2656 : * units to transform to radians. Some standard conversion factors can
2657 : * be found in ogr_srs_api.h.
2658 : *
2659 : * @return OGRERR_NONE on success.
2660 : */
2661 :
2662 1359 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2663 : double dfInRadians)
2664 :
2665 : {
2666 2718 : TAKE_OPTIONAL_LOCK();
2667 :
2668 1359 : d->bNormInfoSet = FALSE;
2669 :
2670 1359 : d->refreshProjObj();
2671 1359 : if (!d->m_pj_crs)
2672 0 : return OGRERR_FAILURE;
2673 1359 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2674 1359 : if (!geodCRS)
2675 0 : return OGRERR_FAILURE;
2676 1359 : proj_destroy(geodCRS);
2677 1359 : d->demoteFromBoundCRS();
2678 1359 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2679 : pszUnitsName, dfInRadians,
2680 : nullptr, nullptr));
2681 1359 : d->undoDemoteFromBoundCRS();
2682 :
2683 1359 : d->m_osAngularUnits = pszUnitsName;
2684 1359 : d->m_dfAngularUnitToRadian = dfInRadians;
2685 :
2686 1359 : return OGRERR_NONE;
2687 : }
2688 :
2689 : /************************************************************************/
2690 : /* OSRSetAngularUnits() */
2691 : /************************************************************************/
2692 :
2693 : /**
2694 : * \brief Set the angular units for the geographic coordinate system.
2695 : *
2696 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2697 : */
2698 46 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2699 : double dfInRadians)
2700 :
2701 : {
2702 46 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2703 :
2704 46 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2705 : }
2706 :
2707 : /************************************************************************/
2708 : /* GetAngularUnits() */
2709 : /************************************************************************/
2710 :
2711 : /**
2712 : * \brief Fetch angular geographic coordinate system units.
2713 : *
2714 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2715 : * will be assumed. This method only checks directly under the GEOGCS node
2716 : * for units.
2717 : *
2718 : * This method does the same thing as the C function OSRGetAngularUnits().
2719 : *
2720 : * @param ppszName a pointer to be updated with the pointer to the units name.
2721 : * The returned value remains internal to the OGRSpatialReference and should
2722 : * not be freed, or modified. It may be invalidated on the next
2723 : * OGRSpatialReference call.
2724 : *
2725 : * @return the value to multiply by angular distances to transform them to
2726 : * radians.
2727 : */
2728 :
2729 7644 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2730 :
2731 : {
2732 15288 : TAKE_OPTIONAL_LOCK();
2733 :
2734 7644 : d->refreshProjObj();
2735 :
2736 7644 : if (!d->m_osAngularUnits.empty())
2737 : {
2738 2153 : if (ppszName != nullptr)
2739 148 : *ppszName = d->m_osAngularUnits.c_str();
2740 2153 : return d->m_dfAngularUnitToRadian;
2741 : }
2742 :
2743 : do
2744 : {
2745 5491 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2746 : {
2747 113 : break;
2748 : }
2749 :
2750 : auto geodCRS =
2751 5380 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2752 5380 : if (!geodCRS)
2753 : {
2754 0 : break;
2755 : }
2756 : auto coordSys =
2757 5380 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2758 5380 : proj_destroy(geodCRS);
2759 5380 : if (!coordSys)
2760 : {
2761 0 : break;
2762 : }
2763 5380 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2764 : PJ_CS_TYPE_ELLIPSOIDAL)
2765 : {
2766 2 : proj_destroy(coordSys);
2767 2 : break;
2768 : }
2769 :
2770 5378 : double dfConvFactor = 0.0;
2771 5378 : const char *pszUnitName = nullptr;
2772 5378 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2773 : nullptr, nullptr, &dfConvFactor,
2774 : &pszUnitName, nullptr, nullptr))
2775 : {
2776 0 : proj_destroy(coordSys);
2777 0 : break;
2778 : }
2779 :
2780 5378 : d->m_osAngularUnits = pszUnitName;
2781 :
2782 5378 : proj_destroy(coordSys);
2783 5378 : d->m_dfAngularUnitToRadian = dfConvFactor;
2784 : } while (false);
2785 :
2786 5491 : if (d->m_osAngularUnits.empty())
2787 : {
2788 113 : d->m_osAngularUnits = "degree";
2789 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2790 : }
2791 :
2792 5491 : if (ppszName != nullptr)
2793 3070 : *ppszName = d->m_osAngularUnits.c_str();
2794 5491 : return d->m_dfAngularUnitToRadian;
2795 : }
2796 :
2797 : /**
2798 : * \brief Fetch angular geographic coordinate system units.
2799 : *
2800 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2801 : * will be assumed. This method only checks directly under the GEOGCS node
2802 : * for units.
2803 : *
2804 : * This method does the same thing as the C function OSRGetAngularUnits().
2805 : *
2806 : * @param ppszName a pointer to be updated with the pointer to the units name.
2807 : * The returned value remains internal to the OGRSpatialReference and should
2808 : * not be freed, or modified. It may be invalidated on the next
2809 : * OGRSpatialReference call.
2810 : *
2811 : * @return the value to multiply by angular distances to transform them to
2812 : * radians.
2813 : * @deprecated Use GetAngularUnits(const char**) const.
2814 : */
2815 :
2816 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2817 :
2818 : {
2819 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2820 : }
2821 :
2822 : /************************************************************************/
2823 : /* OSRGetAngularUnits() */
2824 : /************************************************************************/
2825 :
2826 : /**
2827 : * \brief Fetch angular geographic coordinate system units.
2828 : *
2829 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2830 : */
2831 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2832 :
2833 : {
2834 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2835 :
2836 1 : return ToPointer(hSRS)->GetAngularUnits(
2837 1 : const_cast<const char **>(ppszName));
2838 : }
2839 :
2840 : /************************************************************************/
2841 : /* SetLinearUnitsAndUpdateParameters() */
2842 : /************************************************************************/
2843 :
2844 : /**
2845 : * \brief Set the linear units for the projection.
2846 : *
2847 : * This method creates a UNIT subnode with the specified values as a
2848 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2849 : * SetLinearUnits() method, but it also updates all existing linear
2850 : * projection parameter values from the old units to the new units.
2851 : *
2852 : * @param pszName the units name to be used. Some preferred units
2853 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2854 : * and SRS_UL_US_FOOT.
2855 : *
2856 : * @param dfInMeters the value to multiple by a length in the indicated
2857 : * units to transform to meters. Some standard conversion factors can
2858 : * be found in ogr_srs_api.h.
2859 : *
2860 : * @param pszUnitAuthority Unit authority name. Or nullptr
2861 : *
2862 : * @param pszUnitCode Unit code. Or nullptr
2863 : *
2864 : * @return OGRERR_NONE on success.
2865 : */
2866 :
2867 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2868 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2869 : const char *pszUnitCode)
2870 :
2871 : {
2872 78 : TAKE_OPTIONAL_LOCK();
2873 :
2874 39 : if (dfInMeters <= 0.0)
2875 0 : return OGRERR_FAILURE;
2876 :
2877 39 : d->refreshProjObj();
2878 39 : if (!d->m_pj_crs)
2879 0 : return OGRERR_FAILURE;
2880 :
2881 39 : d->demoteFromBoundCRS();
2882 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2883 : {
2884 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2885 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2886 : pszUnitAuthority, pszUnitCode, true));
2887 : }
2888 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2889 : pszName, dfInMeters,
2890 : pszUnitAuthority, pszUnitCode));
2891 39 : d->undoDemoteFromBoundCRS();
2892 :
2893 39 : d->m_osLinearUnits = pszName;
2894 39 : d->dfToMeter = dfInMeters;
2895 :
2896 39 : return OGRERR_NONE;
2897 : }
2898 :
2899 : /************************************************************************/
2900 : /* OSRSetLinearUnitsAndUpdateParameters() */
2901 : /************************************************************************/
2902 :
2903 : /**
2904 : * \brief Set the linear units for the projection.
2905 : *
2906 : * This function is the same as
2907 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2908 : */
2909 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2910 : const char *pszUnits,
2911 : double dfInMeters)
2912 :
2913 : {
2914 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2915 : OGRERR_FAILURE);
2916 :
2917 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2918 1 : dfInMeters);
2919 : }
2920 :
2921 : /************************************************************************/
2922 : /* SetLinearUnits() */
2923 : /************************************************************************/
2924 :
2925 : /**
2926 : * \brief Set the linear units for the projection.
2927 : *
2928 : * This method creates a UNIT subnode with the specified values as a
2929 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2930 : * Geographic 3D CRS the vertical axis units will be set.
2931 : *
2932 : * This method does the same as the C function OSRSetLinearUnits().
2933 : *
2934 : * @param pszUnitsName the units name to be used. Some preferred units
2935 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2936 : * and SRS_UL_US_FOOT.
2937 : *
2938 : * @param dfInMeters the value to multiple by a length in the indicated
2939 : * units to transform to meters. Some standard conversion factors can
2940 : * be found in ogr_srs_api.h.
2941 : *
2942 : * @return OGRERR_NONE on success.
2943 : */
2944 :
2945 7112 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2946 : double dfInMeters)
2947 :
2948 : {
2949 7112 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2950 : }
2951 :
2952 : /************************************************************************/
2953 : /* OSRSetLinearUnits() */
2954 : /************************************************************************/
2955 :
2956 : /**
2957 : * \brief Set the linear units for the projection.
2958 : *
2959 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2960 : */
2961 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2962 : double dfInMeters)
2963 :
2964 : {
2965 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2966 :
2967 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2968 : }
2969 :
2970 : /************************************************************************/
2971 : /* SetTargetLinearUnits() */
2972 : /************************************************************************/
2973 :
2974 : /**
2975 : * \brief Set the linear units for the projection.
2976 : *
2977 : * This method creates a UNIT subnode with the specified values as a
2978 : * child of the target node.
2979 : *
2980 : * This method does the same as the C function OSRSetTargetLinearUnits().
2981 : *
2982 : * @param pszTargetKey the keyword to set the linear units for.
2983 : * i.e. "PROJCS" or "VERT_CS"
2984 : *
2985 : * @param pszUnitsName the units name to be used. Some preferred units
2986 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2987 : * and SRS_UL_US_FOOT.
2988 : *
2989 : * @param dfInMeters the value to multiple by a length in the indicated
2990 : * units to transform to meters. Some standard conversion factors can
2991 : * be found in ogr_srs_api.h.
2992 : *
2993 : * @param pszUnitAuthority Unit authority name. Or nullptr
2994 : *
2995 : * @param pszUnitCode Unit code. Or nullptr
2996 : *
2997 : * @return OGRERR_NONE on success.
2998 : *
2999 : */
3000 :
3001 11280 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3002 : const char *pszUnitsName,
3003 : double dfInMeters,
3004 : const char *pszUnitAuthority,
3005 : const char *pszUnitCode)
3006 :
3007 : {
3008 22560 : TAKE_OPTIONAL_LOCK();
3009 :
3010 11280 : if (dfInMeters <= 0.0)
3011 0 : return OGRERR_FAILURE;
3012 :
3013 11280 : d->refreshProjObj();
3014 11280 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3015 11280 : if (pszTargetKey == nullptr)
3016 : {
3017 11280 : if (!d->m_pj_crs)
3018 0 : return OGRERR_FAILURE;
3019 :
3020 11280 : d->demoteFromBoundCRS();
3021 11280 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3022 : {
3023 16774 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3024 8387 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3025 : pszUnitAuthority, pszUnitCode, false));
3026 : }
3027 22560 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3028 11280 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3029 : pszUnitAuthority, pszUnitCode));
3030 11280 : d->undoDemoteFromBoundCRS();
3031 :
3032 11280 : d->m_osLinearUnits = pszUnitsName;
3033 11280 : d->dfToMeter = dfInMeters;
3034 :
3035 11280 : return OGRERR_NONE;
3036 : }
3037 :
3038 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3039 :
3040 0 : if (poCS == nullptr)
3041 0 : return OGRERR_FAILURE;
3042 :
3043 0 : char szValue[128] = {'\0'};
3044 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3045 0 : dfInMeters > std::numeric_limits<int>::min() &&
3046 0 : dfInMeters == static_cast<int>(dfInMeters))
3047 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3048 : else
3049 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3050 :
3051 0 : OGR_SRSNode *poUnits = nullptr;
3052 0 : if (poCS->FindChild("UNIT") >= 0)
3053 : {
3054 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3055 0 : if (poUnits->GetChildCount() < 2)
3056 0 : return OGRERR_FAILURE;
3057 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3058 0 : poUnits->GetChild(1)->SetValue(szValue);
3059 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3060 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3061 : }
3062 : else
3063 : {
3064 0 : poUnits = new OGR_SRSNode("UNIT");
3065 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3066 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3067 :
3068 0 : poCS->AddChild(poUnits);
3069 : }
3070 :
3071 0 : return OGRERR_NONE;
3072 : }
3073 :
3074 : /************************************************************************/
3075 : /* OSRSetLinearUnits() */
3076 : /************************************************************************/
3077 :
3078 : /**
3079 : * \brief Set the linear units for the target node.
3080 : *
3081 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3082 : *
3083 : */
3084 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3085 : const char *pszTargetKey, const char *pszUnits,
3086 : double dfInMeters)
3087 :
3088 : {
3089 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3090 :
3091 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3092 1 : dfInMeters);
3093 : }
3094 :
3095 : /************************************************************************/
3096 : /* GetLinearUnits() */
3097 : /************************************************************************/
3098 :
3099 : /**
3100 : * \brief Fetch linear projection units.
3101 : *
3102 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3103 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3104 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3105 : * axis units will be returned.
3106 : *
3107 : * This method does the same thing as the C function OSRGetLinearUnits()
3108 : *
3109 : * @param ppszName a pointer to be updated with the pointer to the units name.
3110 : * The returned value remains internal to the OGRSpatialReference and should
3111 : * not be freed, or modified. It may be invalidated on the next
3112 : * OGRSpatialReference call.
3113 : *
3114 : * @return the value to multiply by linear distances to transform them to
3115 : * meters.
3116 : * @deprecated Use GetLinearUnits(const char**) const.
3117 : */
3118 :
3119 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3120 :
3121 : {
3122 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3123 : }
3124 :
3125 : /**
3126 : * \brief Fetch linear projection units.
3127 : *
3128 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3129 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3130 : * for units.
3131 : *
3132 : * This method does the same thing as the C function OSRGetLinearUnits()
3133 : *
3134 : * @param ppszName a pointer to be updated with the pointer to the units name.
3135 : * The returned value remains internal to the OGRSpatialReference and should
3136 : * not be freed, or modified. It may be invalidated on the next
3137 : * OGRSpatialReference call.
3138 : *
3139 : * @return the value to multiply by linear distances to transform them to
3140 : * meters.
3141 : */
3142 :
3143 19692 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3144 :
3145 : {
3146 19692 : return GetTargetLinearUnits(nullptr, ppszName);
3147 : }
3148 :
3149 : /************************************************************************/
3150 : /* OSRGetLinearUnits() */
3151 : /************************************************************************/
3152 :
3153 : /**
3154 : * \brief Fetch linear projection units.
3155 : *
3156 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3157 : */
3158 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3159 :
3160 : {
3161 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3162 :
3163 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3164 : }
3165 :
3166 : /************************************************************************/
3167 : /* GetTargetLinearUnits() */
3168 : /************************************************************************/
3169 :
3170 : /**
3171 : * \brief Fetch linear units for target.
3172 : *
3173 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3174 : *
3175 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3176 : *
3177 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3178 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3179 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3180 : * @param ppszName a pointer to be updated with the pointer to the units name.
3181 : * The returned value remains internal to the OGRSpatialReference and should not
3182 : * be freed, or modified. It may be invalidated on the next
3183 : * OGRSpatialReference call. ppszName can be set to NULL.
3184 : *
3185 : * @return the value to multiply by linear distances to transform them to
3186 : * meters.
3187 : *
3188 : * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3189 : * const.
3190 : */
3191 :
3192 19843 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3193 : const char **ppszName) const
3194 :
3195 : {
3196 39686 : TAKE_OPTIONAL_LOCK();
3197 :
3198 19843 : d->refreshProjObj();
3199 :
3200 19843 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3201 19843 : if (pszTargetKey == nullptr)
3202 : {
3203 : // Use cached result if available
3204 19752 : if (!d->m_osLinearUnits.empty())
3205 : {
3206 8429 : if (ppszName)
3207 7650 : *ppszName = d->m_osLinearUnits.c_str();
3208 8429 : return d->dfToMeter;
3209 : }
3210 :
3211 : while (true)
3212 : {
3213 11323 : if (d->m_pj_crs == nullptr)
3214 : {
3215 245 : break;
3216 : }
3217 :
3218 11078 : d->demoteFromBoundCRS();
3219 11078 : PJ *coordSys = nullptr;
3220 11078 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3221 : {
3222 30 : for (int iComponent = 0; iComponent < 2; iComponent++)
3223 : {
3224 30 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3225 30 : d->m_pj_crs, iComponent);
3226 30 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3227 : {
3228 : auto temp =
3229 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3230 0 : proj_destroy(subCRS);
3231 0 : subCRS = temp;
3232 : }
3233 60 : if (subCRS &&
3234 30 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3235 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3236 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3237 : {
3238 24 : coordSys = proj_crs_get_coordinate_system(
3239 : d->getPROJContext(), subCRS);
3240 24 : proj_destroy(subCRS);
3241 24 : break;
3242 : }
3243 6 : else if (subCRS)
3244 : {
3245 6 : proj_destroy(subCRS);
3246 : }
3247 : }
3248 24 : if (coordSys == nullptr)
3249 : {
3250 0 : d->undoDemoteFromBoundCRS();
3251 0 : break;
3252 : }
3253 : }
3254 : else
3255 : {
3256 11054 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3257 11054 : d->m_pj_crs);
3258 : }
3259 :
3260 11078 : d->undoDemoteFromBoundCRS();
3261 11078 : if (!coordSys)
3262 : {
3263 0 : break;
3264 : }
3265 11078 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3266 :
3267 11078 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3268 2182 : csType != PJ_CS_TYPE_VERTICAL &&
3269 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3270 : csType != PJ_CS_TYPE_SPHERICAL)
3271 : {
3272 0 : proj_destroy(coordSys);
3273 0 : break;
3274 : }
3275 :
3276 11078 : int axis = 0;
3277 :
3278 11078 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3279 : csType == PJ_CS_TYPE_SPHERICAL)
3280 : {
3281 : const int axisCount =
3282 2182 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3283 :
3284 2182 : if (axisCount == 3)
3285 : {
3286 4 : axis = 2;
3287 : }
3288 : else
3289 : {
3290 2178 : proj_destroy(coordSys);
3291 2178 : break;
3292 : }
3293 : }
3294 :
3295 8900 : double dfConvFactor = 0.0;
3296 8900 : const char *pszUnitName = nullptr;
3297 8900 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3298 : nullptr, nullptr, nullptr, &dfConvFactor,
3299 : &pszUnitName, nullptr, nullptr))
3300 : {
3301 0 : proj_destroy(coordSys);
3302 0 : break;
3303 : }
3304 :
3305 8900 : d->m_osLinearUnits = pszUnitName;
3306 8900 : d->dfToMeter = dfConvFactor;
3307 8900 : if (ppszName)
3308 1166 : *ppszName = d->m_osLinearUnits.c_str();
3309 :
3310 8900 : proj_destroy(coordSys);
3311 8900 : return dfConvFactor;
3312 : }
3313 :
3314 2423 : d->m_osLinearUnits = "unknown";
3315 2423 : d->dfToMeter = 1.0;
3316 :
3317 2423 : if (ppszName != nullptr)
3318 2234 : *ppszName = d->m_osLinearUnits.c_str();
3319 2423 : return 1.0;
3320 : }
3321 :
3322 91 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3323 :
3324 91 : if (ppszName != nullptr)
3325 37 : *ppszName = "unknown";
3326 :
3327 91 : if (poCS == nullptr)
3328 53 : return 1.0;
3329 :
3330 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3331 : {
3332 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3333 :
3334 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3335 : {
3336 38 : if (ppszName != nullptr)
3337 37 : *ppszName = poChild->GetChild(0)->GetValue();
3338 :
3339 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3340 : }
3341 : }
3342 :
3343 0 : return 1.0;
3344 : }
3345 :
3346 : /**
3347 : * \brief Fetch linear units for target.
3348 : *
3349 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3350 : *
3351 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3352 : *
3353 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3354 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3355 : * GEOCCS and VERT_CS are looked up)
3356 : * @param ppszName a pointer to be updated with the pointer to the units name.
3357 : * The returned value remains internal to the OGRSpatialReference and should not
3358 : * be freed, or modified. It may be invalidated on the next
3359 : * OGRSpatialReference call. ppszName can be set to NULL.
3360 : *
3361 : * @return the value to multiply by linear distances to transform them to
3362 : * meters.
3363 : *
3364 : */
3365 :
3366 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3367 : char **ppszName) const
3368 :
3369 : {
3370 0 : return GetTargetLinearUnits(pszTargetKey,
3371 0 : const_cast<const char **>(ppszName));
3372 : }
3373 :
3374 : /************************************************************************/
3375 : /* OSRGetTargetLinearUnits() */
3376 : /************************************************************************/
3377 :
3378 : /**
3379 : * \brief Fetch linear projection units.
3380 : *
3381 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3382 : *
3383 : */
3384 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3385 : const char *pszTargetKey, char **ppszName)
3386 :
3387 : {
3388 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3389 :
3390 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3391 4 : pszTargetKey, const_cast<const char **>(ppszName));
3392 : }
3393 :
3394 : /************************************************************************/
3395 : /* GetPrimeMeridian() */
3396 : /************************************************************************/
3397 :
3398 : /**
3399 : * \brief Fetch prime meridian info.
3400 : *
3401 : * Returns the offset of the prime meridian from greenwich in degrees,
3402 : * and the prime meridian name (if requested). If no PRIMEM value exists
3403 : * in the coordinate system definition a value of "Greenwich" and an
3404 : * offset of 0.0 is assumed.
3405 : *
3406 : * If the prime meridian name is returned, the pointer is to an internal
3407 : * copy of the name. It should not be freed, altered or depended on after
3408 : * the next OGR call.
3409 : *
3410 : * This method is the same as the C function OSRGetPrimeMeridian().
3411 : *
3412 : * @param ppszName return location for prime meridian name. If NULL, name
3413 : * is not returned.
3414 : *
3415 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3416 : * degrees.
3417 : * @deprecated Use GetPrimeMeridian(const char**) const.
3418 : */
3419 :
3420 1437 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3421 :
3422 : {
3423 2874 : TAKE_OPTIONAL_LOCK();
3424 :
3425 1437 : d->refreshProjObj();
3426 :
3427 1437 : if (!d->m_osPrimeMeridianName.empty())
3428 : {
3429 60 : if (ppszName != nullptr)
3430 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3431 60 : return d->dfFromGreenwich;
3432 : }
3433 :
3434 : while (true)
3435 : {
3436 1377 : if (!d->m_pj_crs)
3437 0 : break;
3438 :
3439 1377 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3440 1377 : if (!pm)
3441 0 : break;
3442 :
3443 1377 : d->m_osPrimeMeridianName = proj_get_name(pm);
3444 1377 : if (ppszName)
3445 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3446 1377 : double dfLongitude = 0.0;
3447 1377 : double dfConvFactor = 0.0;
3448 1377 : proj_prime_meridian_get_parameters(
3449 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3450 1377 : proj_destroy(pm);
3451 2754 : d->dfFromGreenwich =
3452 1377 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3453 1377 : return d->dfFromGreenwich;
3454 : }
3455 :
3456 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3457 0 : d->dfFromGreenwich = 0.0;
3458 0 : if (ppszName != nullptr)
3459 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3460 0 : return d->dfFromGreenwich;
3461 : }
3462 :
3463 : /**
3464 : * \brief Fetch prime meridian info.
3465 : *
3466 : * Returns the offset of the prime meridian from greenwich in degrees,
3467 : * and the prime meridian name (if requested). If no PRIMEM value exists
3468 : * in the coordinate system definition a value of "Greenwich" and an
3469 : * offset of 0.0 is assumed.
3470 : *
3471 : * If the prime meridian name is returned, the pointer is to an internal
3472 : * copy of the name. It should not be freed, altered or depended on after
3473 : * the next OGR call.
3474 : *
3475 : * This method is the same as the C function OSRGetPrimeMeridian().
3476 : *
3477 : * @param ppszName return location for prime meridian name. If NULL, name
3478 : * is not returned.
3479 : *
3480 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3481 : * degrees.
3482 : */
3483 :
3484 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3485 :
3486 : {
3487 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3488 : }
3489 :
3490 : /************************************************************************/
3491 : /* OSRGetPrimeMeridian() */
3492 : /************************************************************************/
3493 :
3494 : /**
3495 : * \brief Fetch prime meridian info.
3496 : *
3497 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3498 : */
3499 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3500 :
3501 : {
3502 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3503 :
3504 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3505 0 : const_cast<const char **>(ppszName));
3506 : }
3507 :
3508 : /************************************************************************/
3509 : /* SetGeogCS() */
3510 : /************************************************************************/
3511 :
3512 : /**
3513 : * \brief Set geographic coordinate system.
3514 : *
3515 : * This method is used to set the datum, ellipsoid, prime meridian and
3516 : * angular units for a geographic coordinate system. It can be used on its
3517 : * own to establish a geographic spatial reference, or applied to a
3518 : * projected coordinate system to establish the underlying geographic
3519 : * coordinate system.
3520 : *
3521 : * This method does the same as the C function OSRSetGeogCS().
3522 : *
3523 : * @param pszGeogName user visible name for the geographic coordinate system
3524 : * (not to serve as a key).
3525 : *
3526 : * @param pszDatumName key name for this datum. The OpenGIS specification
3527 : * lists some known values, and otherwise EPSG datum names with a standard
3528 : * transformation are considered legal keys.
3529 : *
3530 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3531 : *
3532 : * @param dfSemiMajor the semi major axis of the spheroid.
3533 : *
3534 : * @param dfInvFlattening the inverse flattening for the spheroid.
3535 : * This can be computed from the semi minor axis as
3536 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3537 : *
3538 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3539 : * If this is NULL a default value of "Greenwich" will be used.
3540 : *
3541 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3542 : * meridian. Always in Degrees
3543 : *
3544 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3545 : * standard names). If NULL a value of "degrees" will be assumed.
3546 : *
3547 : * @param dfConvertToRadians value to multiply angular units by to transform
3548 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3549 : * pszAngularUnits is NULL.
3550 : *
3551 : * @return OGRERR_NONE on success.
3552 : */
3553 :
3554 9300 : OGRErr OGRSpatialReference::SetGeogCS(
3555 : const char *pszGeogName, const char *pszDatumName,
3556 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3557 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3558 : double dfConvertToRadians)
3559 :
3560 : {
3561 18600 : TAKE_OPTIONAL_LOCK();
3562 :
3563 9300 : d->bNormInfoSet = FALSE;
3564 9300 : d->m_osAngularUnits.clear();
3565 9300 : d->m_dfAngularUnitToRadian = 0.0;
3566 9300 : d->m_osPrimeMeridianName.clear();
3567 9300 : d->dfFromGreenwich = 0.0;
3568 :
3569 : /* -------------------------------------------------------------------- */
3570 : /* For a geocentric coordinate system we want to set the datum */
3571 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3572 : /* temporary srs and use the copy method which has special */
3573 : /* handling for GEOCCS. */
3574 : /* -------------------------------------------------------------------- */
3575 9300 : if (IsGeocentric())
3576 : {
3577 4 : OGRSpatialReference oGCS;
3578 :
3579 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3580 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3581 : dfConvertToRadians);
3582 2 : return CopyGeogCSFrom(&oGCS);
3583 : }
3584 :
3585 9298 : auto cs = proj_create_ellipsoidal_2D_cs(
3586 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3587 : dfConvertToRadians);
3588 : // Prime meridian expressed in Degree
3589 9298 : auto obj = proj_create_geographic_crs(
3590 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3591 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3592 9298 : proj_destroy(cs);
3593 :
3594 14005 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3595 4707 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3596 : {
3597 4591 : d->setPjCRS(obj);
3598 : }
3599 4707 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3600 : {
3601 9414 : d->setPjCRS(
3602 4707 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3603 4707 : proj_destroy(obj);
3604 : }
3605 : else
3606 : {
3607 0 : proj_destroy(obj);
3608 : }
3609 :
3610 9298 : return OGRERR_NONE;
3611 : }
3612 :
3613 : /************************************************************************/
3614 : /* OSRSetGeogCS() */
3615 : /************************************************************************/
3616 :
3617 : /**
3618 : * \brief Set geographic coordinate system.
3619 : *
3620 : * This function is the same as OGRSpatialReference::SetGeogCS()
3621 : */
3622 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3623 : const char *pszDatumName, const char *pszSpheroidName,
3624 : double dfSemiMajor, double dfInvFlattening,
3625 : const char *pszPMName, double dfPMOffset,
3626 : const char *pszAngularUnits, double dfConvertToRadians)
3627 :
3628 : {
3629 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3630 :
3631 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3632 : pszSpheroidName, dfSemiMajor,
3633 : dfInvFlattening, pszPMName, dfPMOffset,
3634 18 : pszAngularUnits, dfConvertToRadians);
3635 : }
3636 :
3637 : /************************************************************************/
3638 : /* SetWellKnownGeogCS() */
3639 : /************************************************************************/
3640 :
3641 : /**
3642 : * \brief Set a GeogCS based on well known name.
3643 : *
3644 : * This may be called on an empty OGRSpatialReference to make a geographic
3645 : * coordinate system, or on something with an existing PROJCS node to
3646 : * set the underlying geographic coordinate system of a projected coordinate
3647 : * system.
3648 : *
3649 : * The following well known text values are currently supported,
3650 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3651 : * <ul>
3652 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3653 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3654 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3655 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3656 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3657 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3658 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3659 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3660 : * </ul>
3661 : *
3662 : * @param pszName name of well known geographic coordinate system.
3663 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3664 : * recognised, the target object is already initialized, or an EPSG value
3665 : * can't be successfully looked up.
3666 : */
3667 :
3668 3001 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3669 :
3670 : {
3671 6002 : TAKE_OPTIONAL_LOCK();
3672 :
3673 : /* -------------------------------------------------------------------- */
3674 : /* Check for EPSG authority numbers. */
3675 : /* -------------------------------------------------------------------- */
3676 3001 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3677 : {
3678 84 : OGRSpatialReference oSRS2;
3679 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3680 42 : if (eErr != OGRERR_NONE)
3681 0 : return eErr;
3682 :
3683 42 : if (!oSRS2.IsGeographic())
3684 0 : return OGRERR_FAILURE;
3685 :
3686 42 : return CopyGeogCSFrom(&oSRS2);
3687 : }
3688 :
3689 : /* -------------------------------------------------------------------- */
3690 : /* Check for simple names. */
3691 : /* -------------------------------------------------------------------- */
3692 2959 : const char *pszWKT = nullptr;
3693 :
3694 2959 : if (EQUAL(pszName, "WGS84"))
3695 : {
3696 2163 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3697 : }
3698 796 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3699 : {
3700 614 : pszWKT =
3701 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3702 : "ELLIPSOID[\"WGS "
3703 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3704 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3705 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3706 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3707 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3708 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3709 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3710 : "ID[\"OGC\",\"CRS84\"]]";
3711 : }
3712 182 : else if (EQUAL(pszName, "WGS72"))
3713 19 : pszWKT =
3714 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3715 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3716 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3717 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3718 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3719 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3720 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3721 :
3722 163 : else if (EQUAL(pszName, "NAD27"))
3723 135 : pszWKT =
3724 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3725 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3726 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3727 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3728 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3729 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3730 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3731 :
3732 28 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3733 0 : pszWKT =
3734 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3735 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3736 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3737 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3738 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3739 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3740 :
3741 28 : else if (EQUAL(pszName, "NAD83"))
3742 24 : pszWKT =
3743 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3744 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3745 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3746 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3747 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3748 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3749 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3750 : "\"EPSG\",\"4269\"]]";
3751 :
3752 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3753 0 : pszWKT =
3754 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3755 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3756 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3757 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3758 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3759 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3760 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3761 :
3762 : else
3763 4 : return OGRERR_FAILURE;
3764 :
3765 : /* -------------------------------------------------------------------- */
3766 : /* Import the WKT */
3767 : /* -------------------------------------------------------------------- */
3768 5910 : OGRSpatialReference oSRS2;
3769 2955 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3770 2955 : if (eErr != OGRERR_NONE)
3771 0 : return eErr;
3772 :
3773 : /* -------------------------------------------------------------------- */
3774 : /* Copy over. */
3775 : /* -------------------------------------------------------------------- */
3776 2955 : return CopyGeogCSFrom(&oSRS2);
3777 : }
3778 :
3779 : /************************************************************************/
3780 : /* OSRSetWellKnownGeogCS() */
3781 : /************************************************************************/
3782 :
3783 : /**
3784 : * \brief Set a GeogCS based on well known name.
3785 : *
3786 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3787 : */
3788 135 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3789 :
3790 : {
3791 135 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3792 :
3793 135 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3794 : }
3795 :
3796 : /************************************************************************/
3797 : /* CopyGeogCSFrom() */
3798 : /************************************************************************/
3799 :
3800 : /**
3801 : * \brief Copy GEOGCS from another OGRSpatialReference.
3802 : *
3803 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3804 : * If this object has a PROJCS root already, the GEOGCS is installed within
3805 : * it, otherwise it is installed as the root.
3806 : *
3807 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3808 : *
3809 : * @return OGRERR_NONE on success or an error code.
3810 : */
3811 :
3812 3592 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3813 :
3814 : {
3815 7184 : TAKE_OPTIONAL_LOCK();
3816 :
3817 3592 : d->bNormInfoSet = FALSE;
3818 3592 : d->m_osAngularUnits.clear();
3819 3592 : d->m_dfAngularUnitToRadian = 0.0;
3820 3592 : d->m_osPrimeMeridianName.clear();
3821 3592 : d->dfFromGreenwich = 0.0;
3822 :
3823 3592 : d->refreshProjObj();
3824 3592 : poSrcSRS->d->refreshProjObj();
3825 3592 : if (!poSrcSRS->d->m_pj_crs)
3826 : {
3827 1 : return OGRERR_FAILURE;
3828 : }
3829 : auto geodCRS =
3830 3591 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3831 3591 : if (!geodCRS)
3832 : {
3833 0 : return OGRERR_FAILURE;
3834 : }
3835 :
3836 : /* -------------------------------------------------------------------- */
3837 : /* Handle geocentric coordinate systems specially. We just */
3838 : /* want to copy the DATUM. */
3839 : /* -------------------------------------------------------------------- */
3840 3591 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3841 : {
3842 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3843 : #if PROJ_VERSION_MAJOR > 7 || \
3844 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3845 : if (datum == nullptr)
3846 : {
3847 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3848 : }
3849 : #endif
3850 3 : if (datum == nullptr)
3851 : {
3852 0 : proj_destroy(geodCRS);
3853 0 : return OGRERR_FAILURE;
3854 : }
3855 :
3856 3 : const char *pszUnitName = nullptr;
3857 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3858 :
3859 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3860 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3861 : unitConvFactor);
3862 3 : proj_destroy(datum);
3863 :
3864 3 : d->setPjCRS(pj_crs);
3865 : }
3866 :
3867 3588 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3868 : {
3869 319 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3870 319 : d->m_pj_crs, geodCRS);
3871 319 : d->setPjCRS(pj_crs);
3872 : }
3873 :
3874 : else
3875 : {
3876 3269 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3877 : }
3878 :
3879 : // Apply TOWGS84 of source CRS
3880 3591 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3881 : {
3882 : auto target =
3883 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3884 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3885 1 : poSrcSRS->d->m_pj_crs);
3886 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3887 : target, co));
3888 1 : proj_destroy(target);
3889 1 : proj_destroy(co);
3890 : }
3891 :
3892 3591 : proj_destroy(geodCRS);
3893 :
3894 3591 : return OGRERR_NONE;
3895 : }
3896 :
3897 : /************************************************************************/
3898 : /* OSRCopyGeogCSFrom() */
3899 : /************************************************************************/
3900 :
3901 : /**
3902 : * \brief Copy GEOGCS from another OGRSpatialReference.
3903 : *
3904 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3905 : */
3906 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3907 : const OGRSpatialReferenceH hSrcSRS)
3908 :
3909 : {
3910 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3911 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3912 :
3913 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3914 : }
3915 :
3916 : /************************************************************************/
3917 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3918 : /************************************************************************/
3919 :
3920 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3921 : *
3922 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3923 : */
3924 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3925 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3926 :
3927 : /**
3928 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3929 : */
3930 2696 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3931 : {
3932 2696 : return SET_FROM_USER_INPUT_LIMITATIONS;
3933 : }
3934 :
3935 : /************************************************************************/
3936 : /* RemoveIDFromMemberOfEnsembles() */
3937 : /************************************************************************/
3938 :
3939 : // cppcheck-suppress constParameterReference
3940 198 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3941 : {
3942 : // Remove "id" from members of datum ensembles for compatibility with
3943 : // older PROJ versions
3944 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3945 : // and https://github.com/OSGeo/PROJ/pull/3221
3946 198 : if (obj.GetType() == CPLJSONObject::Type::Object)
3947 : {
3948 243 : for (auto &subObj : obj.GetChildren())
3949 : {
3950 191 : RemoveIDFromMemberOfEnsembles(subObj);
3951 : }
3952 : }
3953 162 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3954 162 : obj.GetName() == "members")
3955 : {
3956 51 : for (auto &subObj : obj.ToArray())
3957 : {
3958 44 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3959 : {
3960 43 : subObj.Delete("id");
3961 : }
3962 : }
3963 : }
3964 198 : }
3965 :
3966 : /************************************************************************/
3967 : /* SetFromUserInput() */
3968 : /************************************************************************/
3969 :
3970 : /**
3971 : * \brief Set spatial reference from various text formats.
3972 : *
3973 : * This method will examine the provided input, and try to deduce the
3974 : * format, and then use it to initialize the spatial reference system. It
3975 : * may take the following forms:
3976 : *
3977 : * <ol>
3978 : * <li> Well Known Text definition - passed on to importFromWkt().
3979 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3980 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3981 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3982 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3983 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3984 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3985 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3986 : * WGS84 or WGS72.
3987 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3988 : * <li> PROJJSON (PROJ >= 6.2)
3989 : * </ol>
3990 : *
3991 : * It is expected that this method will be extended in the future to support
3992 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3993 : * State Plane definitions.
3994 : *
3995 : * This method is intended to be flexible, but by its nature it is
3996 : * imprecise as it must guess information about the format intended. When
3997 : * possible applications should call the specific method appropriate if the
3998 : * input is known to be in a particular format.
3999 : *
4000 : * This method does the same thing as the OSRSetFromUserInput() function.
4001 : *
4002 : * @param pszDefinition text definition to try to deduce SRS from.
4003 : *
4004 : * @return OGRERR_NONE on success, or an error code if the name isn't
4005 : * recognised, the definition is corrupt, or an EPSG value can't be
4006 : * successfully looked up.
4007 : */
4008 :
4009 18617 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4010 : {
4011 18617 : return SetFromUserInput(pszDefinition, nullptr);
4012 : }
4013 :
4014 : /**
4015 : * \brief Set spatial reference from various text formats.
4016 : *
4017 : * This method will examine the provided input, and try to deduce the
4018 : * format, and then use it to initialize the spatial reference system. It
4019 : * may take the following forms:
4020 : *
4021 : * <ol>
4022 : * <li> Well Known Text definition - passed on to importFromWkt().
4023 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4024 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4025 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4026 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4027 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4028 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4029 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4030 : * WGS84 or WGS72.
4031 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4032 : * <li> PROJJSON (PROJ >= 6.2)
4033 : * </ol>
4034 : *
4035 : * It is expected that this method will be extended in the future to support
4036 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4037 : * State Plane definitions.
4038 : *
4039 : * This method is intended to be flexible, but by its nature it is
4040 : * imprecise as it must guess information about the format intended. When
4041 : * possible applications should call the specific method appropriate if the
4042 : * input is known to be in a particular format.
4043 : *
4044 : * This method does the same thing as the OSRSetFromUserInput() and
4045 : * OSRSetFromUserInputEx() functions.
4046 : *
4047 : * @param pszDefinition text definition to try to deduce SRS from.
4048 : *
4049 : * @param papszOptions NULL terminated list of options, or NULL.
4050 : * <ol>
4051 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4052 : * Whether http:// or https:// access is allowed. Defaults to YES.
4053 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4054 : * Whether reading a file using the Virtual File System layer is allowed
4055 : * (can also involve network access). Defaults to YES.
4056 : * </ol>
4057 : *
4058 : * @return OGRERR_NONE on success, or an error code if the name isn't
4059 : * recognised, the definition is corrupt, or an EPSG value can't be
4060 : * successfully looked up.
4061 : */
4062 :
4063 25531 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4064 : CSLConstList papszOptions)
4065 : {
4066 51062 : TAKE_OPTIONAL_LOCK();
4067 :
4068 : // Skip leading white space
4069 25533 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4070 2 : pszDefinition++;
4071 :
4072 25531 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4073 : {
4074 1 : pszDefinition += 6;
4075 : }
4076 :
4077 : /* -------------------------------------------------------------------- */
4078 : /* Is it a recognised syntax? */
4079 : /* -------------------------------------------------------------------- */
4080 25531 : const char *const wktKeywords[] = {
4081 : // WKT1
4082 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4083 : // WKT2"
4084 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4085 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4086 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4087 375336 : for (const char *keyword : wktKeywords)
4088 : {
4089 358410 : if (STARTS_WITH_CI(pszDefinition, keyword))
4090 : {
4091 8605 : return importFromWkt(pszDefinition);
4092 : }
4093 : }
4094 :
4095 16926 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4096 16926 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4097 : {
4098 9867 : OGRErr eStatus = OGRERR_NONE;
4099 :
4100 9867 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4101 : {
4102 : // Use proj_create() as it allows things like EPSG:3157+4617
4103 : // that are not normally supported by the below code that
4104 : // builds manually a compound CRS
4105 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4106 62 : if (!pj)
4107 : {
4108 1 : return OGRERR_FAILURE;
4109 : }
4110 61 : Clear();
4111 61 : d->setPjCRS(pj);
4112 61 : return OGRERR_NONE;
4113 : }
4114 : else
4115 : {
4116 : eStatus =
4117 9805 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4118 : }
4119 :
4120 9805 : return eStatus;
4121 : }
4122 :
4123 7059 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4124 6354 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4125 6353 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4126 6295 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4127 6295 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4128 6295 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4129 764 : return importFromURN(pszDefinition);
4130 :
4131 6295 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4132 6293 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4133 6292 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4134 1220 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4135 1219 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4136 5076 : return importFromCRSURL(pszDefinition);
4137 :
4138 1219 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4139 1 : return importFromWMSAUTO(pszDefinition);
4140 :
4141 : // WMS/WCS OGC codes like OGC:CRS84.
4142 1218 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4143 90 : return SetWellKnownGeogCS(pszDefinition + 4);
4144 :
4145 1128 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4146 1 : return SetWellKnownGeogCS(pszDefinition);
4147 :
4148 1127 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4149 : {
4150 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4151 0 : char *pszCode = strstr(pszFile, ",") + 1;
4152 :
4153 0 : pszCode[-1] = '\0';
4154 :
4155 0 : OGRErr err = importFromDict(pszFile, pszCode);
4156 0 : CPLFree(pszFile);
4157 :
4158 0 : return err;
4159 : }
4160 :
4161 1127 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4162 1122 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4163 : {
4164 318 : Clear();
4165 318 : return SetWellKnownGeogCS(pszDefinition);
4166 : }
4167 :
4168 : // PROJJSON
4169 809 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4170 71 : (strstr(pszDefinition, "GeodeticCRS") ||
4171 71 : strstr(pszDefinition, "GeographicCRS") ||
4172 37 : strstr(pszDefinition, "ProjectedCRS") ||
4173 0 : strstr(pszDefinition, "VerticalCRS") ||
4174 0 : strstr(pszDefinition, "BoundCRS") ||
4175 0 : strstr(pszDefinition, "CompoundCRS") ||
4176 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4177 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4178 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4179 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4180 0 : strstr(pszDefinition, "EngineeringCRS") ||
4181 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4182 0 : strstr(pszDefinition, "ParametricCRS") ||
4183 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4184 0 : strstr(pszDefinition, "TemporalCRS") ||
4185 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4186 : {
4187 : PJ *pj;
4188 71 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4189 : {
4190 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4191 : // a unknown id.
4192 7 : CPLJSONDocument oCRSDoc;
4193 7 : if (!oCRSDoc.LoadMemory(pszDefinition))
4194 0 : return OGRERR_CORRUPT_DATA;
4195 7 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4196 7 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4197 7 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4198 : }
4199 : else
4200 : {
4201 64 : pj = proj_create(d->getPROJContext(), pszDefinition);
4202 : }
4203 71 : if (!pj)
4204 : {
4205 2 : return OGRERR_FAILURE;
4206 : }
4207 69 : Clear();
4208 69 : d->setPjCRS(pj);
4209 69 : return OGRERR_NONE;
4210 : }
4211 :
4212 738 : if (strstr(pszDefinition, "+proj") != nullptr ||
4213 293 : strstr(pszDefinition, "+init") != nullptr)
4214 445 : return importFromProj4(pszDefinition);
4215 :
4216 293 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4217 293 : STARTS_WITH_CI(pszDefinition, "https://"))
4218 : {
4219 1 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4220 : "ALLOW_NETWORK_ACCESS", "YES")))
4221 0 : return importFromUrl(pszDefinition);
4222 :
4223 1 : CPLError(CE_Failure, CPLE_AppDefined,
4224 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4225 : pszDefinition);
4226 1 : return OGRERR_FAILURE;
4227 : }
4228 :
4229 292 : if (EQUAL(pszDefinition, "osgb:BNG"))
4230 : {
4231 13 : return importFromEPSG(27700);
4232 : }
4233 :
4234 : // Used by German CityGML files
4235 279 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4236 : {
4237 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4238 0 : return SetFromUserInput("EPSG:25832+5783");
4239 : }
4240 279 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4241 : {
4242 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4243 4 : return SetFromUserInput("EPSG:25832+7837");
4244 : }
4245 :
4246 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4247 275 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4248 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4249 275 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4250 10 : return importFromEPSG(6668); // JGD2011
4251 265 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4252 : {
4253 : // FIXME when EPSG attributes a CRS code
4254 4 : return importFromWkt(
4255 : "GEOGCRS[\"JGD2024\",\n"
4256 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4257 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4258 : " LENGTHUNIT[\"metre\",1]]],\n"
4259 : " PRIMEM[\"Greenwich\",0,\n"
4260 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4261 : " CS[ellipsoidal,2],\n"
4262 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4263 : " ORDER[1],\n"
4264 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4265 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4266 : " ORDER[2],\n"
4267 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4268 : " USAGE[\n"
4269 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4270 : " AREA[\"Japan - onshore and offshore.\"],\n"
4271 4 : " BBOX[17.09,122.38,46.05,157.65]]]");
4272 : }
4273 :
4274 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4275 261 : const char *pszDot = strrchr(pszDefinition, ':');
4276 261 : if (pszDot)
4277 : {
4278 124 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4279 : auto authorities =
4280 124 : proj_get_authorities_from_database(d->getPROJContext());
4281 124 : if (authorities)
4282 : {
4283 124 : std::set<std::string> aosCandidateAuthorities;
4284 284 : for (auto iter = authorities; *iter; ++iter)
4285 : {
4286 283 : if (*iter == osPrefix)
4287 : {
4288 123 : aosCandidateAuthorities.clear();
4289 123 : aosCandidateAuthorities.insert(*iter);
4290 123 : break;
4291 : }
4292 : // Deal with "IAU_2015" as authority in the list and input
4293 : // "IAU:code"
4294 160 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4295 160 : 0 &&
4296 0 : (*iter)[osPrefix.size()] == '_')
4297 : {
4298 0 : aosCandidateAuthorities.insert(*iter);
4299 : }
4300 : // Deal with "IAU_2015" as authority in the list and input
4301 : // "IAU:2015:code"
4302 320 : else if (osPrefix.find(':') != std::string::npos &&
4303 160 : osPrefix.size() == strlen(*iter) &&
4304 160 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4305 : {
4306 0 : aosCandidateAuthorities.clear();
4307 0 : aosCandidateAuthorities.insert(*iter);
4308 0 : break;
4309 : }
4310 : }
4311 :
4312 124 : proj_string_list_destroy(authorities);
4313 :
4314 124 : if (!aosCandidateAuthorities.empty())
4315 : {
4316 123 : auto obj = proj_create_from_database(
4317 : d->getPROJContext(),
4318 123 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4319 : PJ_CATEGORY_CRS, false, nullptr);
4320 123 : if (!obj)
4321 : {
4322 16 : return OGRERR_FAILURE;
4323 : }
4324 107 : Clear();
4325 107 : d->setPjCRS(obj);
4326 107 : return OGRERR_NONE;
4327 : }
4328 : }
4329 : }
4330 :
4331 : /* -------------------------------------------------------------------- */
4332 : /* Try to open it as a file. */
4333 : /* -------------------------------------------------------------------- */
4334 138 : if (!CPLTestBool(
4335 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4336 : {
4337 : VSIStatBufL sStat;
4338 20 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4339 10 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4340 : {
4341 0 : CPLError(CE_Failure, CPLE_AppDefined,
4342 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4343 : pszDefinition);
4344 0 : return OGRERR_FAILURE;
4345 : }
4346 : // We used to silently return an error without a CE_Failure message
4347 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4348 10 : return OGRERR_CORRUPT_DATA;
4349 : }
4350 :
4351 256 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4352 128 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4353 128 : if (fp == nullptr)
4354 125 : return OGRERR_CORRUPT_DATA;
4355 :
4356 3 : const size_t nBufMax = 100000;
4357 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4358 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4359 3 : VSIFCloseL(fp);
4360 :
4361 3 : if (nBytes == nBufMax - 1)
4362 : {
4363 0 : CPLDebug("OGR",
4364 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4365 : "but it is to large for our generous buffer. Is it really "
4366 : "just a WKT definition?",
4367 : pszDefinition);
4368 0 : CPLFree(pszBuffer);
4369 0 : return OGRERR_FAILURE;
4370 : }
4371 :
4372 3 : pszBuffer[nBytes] = '\0';
4373 :
4374 3 : char *pszBufPtr = pszBuffer;
4375 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4376 0 : pszBufPtr++;
4377 :
4378 3 : OGRErr err = OGRERR_NONE;
4379 3 : if (pszBufPtr[0] == '<')
4380 0 : err = importFromXML(pszBufPtr);
4381 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4382 3 : strstr(pszBuffer, "+init") != nullptr) &&
4383 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4384 0 : strstr(pszBuffer, "extension") == nullptr))
4385 0 : err = importFromProj4(pszBufPtr);
4386 : else
4387 : {
4388 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4389 : {
4390 0 : pszBufPtr += 6;
4391 : }
4392 :
4393 : // coverity[tainted_data]
4394 3 : err = importFromWkt(pszBufPtr);
4395 : }
4396 :
4397 3 : CPLFree(pszBuffer);
4398 :
4399 3 : return err;
4400 : }
4401 :
4402 : /************************************************************************/
4403 : /* OSRSetFromUserInput() */
4404 : /************************************************************************/
4405 :
4406 : /**
4407 : * \brief Set spatial reference from various text formats.
4408 : *
4409 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4410 : *
4411 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4412 : */
4413 299 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4414 : const char *pszDef)
4415 :
4416 : {
4417 299 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4418 :
4419 299 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4420 : }
4421 :
4422 : /************************************************************************/
4423 : /* OSRSetFromUserInputEx() */
4424 : /************************************************************************/
4425 :
4426 : /**
4427 : * \brief Set spatial reference from various text formats.
4428 : *
4429 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4430 : *
4431 : * @since GDAL 3.9
4432 : */
4433 1058 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4434 : CSLConstList papszOptions)
4435 :
4436 : {
4437 1058 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4438 :
4439 1058 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4440 : }
4441 :
4442 : /************************************************************************/
4443 : /* ImportFromUrl() */
4444 : /************************************************************************/
4445 :
4446 : /**
4447 : * \brief Set spatial reference from a URL.
4448 : *
4449 : * This method will download the spatial reference at a given URL and
4450 : * feed it into SetFromUserInput for you.
4451 : *
4452 : * This method does the same thing as the OSRImportFromUrl() function.
4453 : *
4454 : * @param pszUrl text definition to try to deduce SRS from.
4455 : *
4456 : * @return OGRERR_NONE on success, or an error code with the curl
4457 : * error message if it is unable to download data.
4458 : */
4459 :
4460 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4461 :
4462 : {
4463 10 : TAKE_OPTIONAL_LOCK();
4464 :
4465 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4466 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4467 : {
4468 2 : CPLError(CE_Failure, CPLE_AppDefined,
4469 : "The given string is not recognized as a URL"
4470 : "starting with 'http://' -- %s",
4471 : pszUrl);
4472 2 : return OGRERR_FAILURE;
4473 : }
4474 :
4475 : /* -------------------------------------------------------------------- */
4476 : /* Fetch the result. */
4477 : /* -------------------------------------------------------------------- */
4478 3 : CPLErrorReset();
4479 :
4480 6 : std::string osUrl(pszUrl);
4481 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4482 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4483 : // to query WKT. To allow a static server to be used, rather append a
4484 : // "ogcwkt/" suffix.
4485 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4486 5 : "http://spatialreference.org/ref/"})
4487 : {
4488 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4489 : {
4490 : const CPLStringList aosTokens(
4491 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4492 3 : if (aosTokens.size() == 2)
4493 : {
4494 2 : osUrl = "https://spatialreference.org/ref/";
4495 2 : osUrl += aosTokens[0]; // authority
4496 2 : osUrl += '/';
4497 2 : osUrl += aosTokens[1]; // code
4498 2 : osUrl += "/ogcwkt/";
4499 : }
4500 3 : break;
4501 : }
4502 : }
4503 :
4504 3 : const char *pszTimeout = "TIMEOUT=10";
4505 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4506 :
4507 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4508 :
4509 : /* -------------------------------------------------------------------- */
4510 : /* Try to handle errors. */
4511 : /* -------------------------------------------------------------------- */
4512 :
4513 3 : if (psResult == nullptr)
4514 0 : return OGRERR_FAILURE;
4515 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4516 3 : psResult->pabyData == nullptr)
4517 : {
4518 0 : if (CPLGetLastErrorNo() == 0)
4519 : {
4520 0 : CPLError(CE_Failure, CPLE_AppDefined,
4521 : "No data was returned from the given URL");
4522 : }
4523 0 : CPLHTTPDestroyResult(psResult);
4524 0 : return OGRERR_FAILURE;
4525 : }
4526 :
4527 3 : if (psResult->nStatus != 0)
4528 : {
4529 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4530 : psResult->nStatus, psResult->pszErrBuf);
4531 0 : CPLHTTPDestroyResult(psResult);
4532 0 : return OGRERR_FAILURE;
4533 : }
4534 :
4535 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4536 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4537 3 : STARTS_WITH_CI(pszData, "https://"))
4538 : {
4539 0 : CPLError(CE_Failure, CPLE_AppDefined,
4540 : "The data that was downloaded also starts with 'http://' "
4541 : "and cannot be passed into SetFromUserInput. Is this "
4542 : "really a spatial reference definition? ");
4543 0 : CPLHTTPDestroyResult(psResult);
4544 0 : return OGRERR_FAILURE;
4545 : }
4546 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4547 : {
4548 0 : CPLHTTPDestroyResult(psResult);
4549 0 : return OGRERR_FAILURE;
4550 : }
4551 :
4552 3 : CPLHTTPDestroyResult(psResult);
4553 3 : return OGRERR_NONE;
4554 : }
4555 :
4556 : /************************************************************************/
4557 : /* OSRimportFromUrl() */
4558 : /************************************************************************/
4559 :
4560 : /**
4561 : * \brief Set spatial reference from a URL.
4562 : *
4563 : * This function is the same as OGRSpatialReference::importFromUrl()
4564 : */
4565 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4566 :
4567 : {
4568 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4569 :
4570 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4571 : }
4572 :
4573 : /************************************************************************/
4574 : /* importFromURNPart() */
4575 : /************************************************************************/
4576 5288 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4577 : const char *pszCode,
4578 : const char *pszURN)
4579 : {
4580 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4581 : (void)this;
4582 : (void)pszAuthority;
4583 : (void)pszCode;
4584 : (void)pszURN;
4585 : return OGRERR_FAILURE;
4586 : #else
4587 : /* -------------------------------------------------------------------- */
4588 : /* Is this an EPSG code? Note that we import it with EPSG */
4589 : /* preferred axis ordering for geographic coordinate systems. */
4590 : /* -------------------------------------------------------------------- */
4591 5288 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4592 4762 : return importFromEPSGA(atoi(pszCode));
4593 :
4594 : /* -------------------------------------------------------------------- */
4595 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4596 : /* -------------------------------------------------------------------- */
4597 526 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4598 0 : return importFromDict("IAU2000.wkt", pszCode);
4599 :
4600 : /* -------------------------------------------------------------------- */
4601 : /* Is this an OGC code? */
4602 : /* -------------------------------------------------------------------- */
4603 526 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4604 : {
4605 1 : CPLError(CE_Failure, CPLE_AppDefined,
4606 : "URN %s has unrecognized authority.", pszURN);
4607 1 : return OGRERR_FAILURE;
4608 : }
4609 :
4610 525 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4611 513 : return SetWellKnownGeogCS(pszCode);
4612 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4613 0 : return SetWellKnownGeogCS(pszCode);
4614 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4615 0 : return SetWellKnownGeogCS(pszCode);
4616 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4617 10 : return SetWellKnownGeogCS("CRS84");
4618 :
4619 : /* -------------------------------------------------------------------- */
4620 : /* Handle auto codes. We need to convert from format */
4621 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4622 : /* -------------------------------------------------------------------- */
4623 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4624 : {
4625 2 : char szWMSAuto[100] = {'\0'};
4626 :
4627 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4628 0 : return OGRERR_FAILURE;
4629 :
4630 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4631 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4632 : {
4633 26 : if (szWMSAuto[i] == ':')
4634 4 : szWMSAuto[i] = ',';
4635 : }
4636 :
4637 2 : return importFromWMSAUTO(szWMSAuto);
4638 : }
4639 :
4640 : /* -------------------------------------------------------------------- */
4641 : /* Not a recognise OGC item. */
4642 : /* -------------------------------------------------------------------- */
4643 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4644 : pszURN);
4645 :
4646 0 : return OGRERR_FAILURE;
4647 : #endif
4648 : }
4649 :
4650 : /************************************************************************/
4651 : /* importFromURN() */
4652 : /* */
4653 : /* See OGC recommendation paper 06-023r1 or later for details. */
4654 : /************************************************************************/
4655 :
4656 : /**
4657 : * \brief Initialize from OGC URN.
4658 : *
4659 : * Initializes this spatial reference from a coordinate system defined
4660 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4661 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4662 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4663 : *
4664 : * This method is also support through SetFromUserInput() which can
4665 : * normally be used for URNs.
4666 : *
4667 : * @param pszURN the urn string.
4668 : *
4669 : * @return OGRERR_NONE on success or an error code.
4670 : */
4671 :
4672 825 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4673 :
4674 : {
4675 825 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4676 1562 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4677 737 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4678 : CPL_VALUE_INTEGER)
4679 : {
4680 735 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4681 : }
4682 :
4683 180 : TAKE_OPTIONAL_LOCK();
4684 :
4685 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4686 :
4687 : // PROJ 8.2.0 has support for IAU codes now.
4688 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4689 : /* -------------------------------------------------------------------- */
4690 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4691 : /* -------------------------------------------------------------------- */
4692 : const char *pszIAU = strstr(pszURN, "IAU");
4693 : if (pszIAU)
4694 : {
4695 : const char *pszCode = strchr(pszIAU, ':');
4696 : if (pszCode)
4697 : {
4698 : ++pszCode;
4699 : if (*pszCode == ':')
4700 : ++pszCode;
4701 : return importFromDict("IAU2000.wkt", pszCode);
4702 : }
4703 : }
4704 : #endif
4705 :
4706 : if (strlen(pszURN) >= 1000)
4707 : {
4708 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4709 : return OGRERR_CORRUPT_DATA;
4710 : }
4711 : auto obj = proj_create(d->getPROJContext(), pszURN);
4712 : if (!obj)
4713 : {
4714 : return OGRERR_FAILURE;
4715 : }
4716 : Clear();
4717 : d->setPjCRS(obj);
4718 : return OGRERR_NONE;
4719 : #else
4720 90 : const char *pszCur = nullptr;
4721 :
4722 90 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4723 25 : pszCur = pszURN + 16;
4724 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4725 1 : pszCur = pszURN + 20;
4726 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4727 62 : pszCur = pszURN + 18;
4728 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4729 0 : pszCur = pszURN + 16;
4730 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4731 0 : pszCur = pszURN + 20;
4732 : else
4733 : {
4734 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4735 : pszURN);
4736 2 : return OGRERR_FAILURE;
4737 : }
4738 :
4739 : /* -------------------------------------------------------------------- */
4740 : /* Clear any existing definition. */
4741 : /* -------------------------------------------------------------------- */
4742 88 : Clear();
4743 :
4744 : /* -------------------------------------------------------------------- */
4745 : /* Find code (ignoring version) out of string like: */
4746 : /* */
4747 : /* authority:[version]:code */
4748 : /* -------------------------------------------------------------------- */
4749 88 : const char *pszAuthority = pszCur;
4750 :
4751 : // skip authority
4752 421 : while (*pszCur != ':' && *pszCur)
4753 333 : pszCur++;
4754 88 : if (*pszCur == ':')
4755 88 : pszCur++;
4756 :
4757 : // skip version
4758 88 : const char *pszBeforeVersion = pszCur;
4759 398 : while (*pszCur != ':' && *pszCur)
4760 310 : pszCur++;
4761 88 : if (*pszCur == ':')
4762 60 : pszCur++;
4763 : else
4764 : // We come here in the case, the content to parse is authority:code
4765 : // (instead of authority::code) which is probably illegal according to
4766 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4767 : // for example in what is returned by GeoServer.
4768 28 : pszCur = pszBeforeVersion;
4769 :
4770 88 : const char *pszCode = pszCur;
4771 :
4772 88 : const char *pszComma = strchr(pszCur, ',');
4773 88 : if (pszComma == nullptr)
4774 87 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4775 :
4776 : // There's a second part with the vertical SRS.
4777 1 : pszCur = pszComma + 1;
4778 1 : if (!STARTS_WITH(pszCur, "crs:"))
4779 : {
4780 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4781 : pszURN);
4782 0 : return OGRERR_FAILURE;
4783 : }
4784 :
4785 1 : pszCur += 4;
4786 :
4787 1 : char *pszFirstCode = CPLStrdup(pszCode);
4788 1 : pszFirstCode[pszComma - pszCode] = '\0';
4789 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4790 1 : CPLFree(pszFirstCode);
4791 :
4792 : // Do we want to turn this into a compound definition
4793 : // with a vertical datum?
4794 1 : if (eStatus != OGRERR_NONE)
4795 0 : return eStatus;
4796 :
4797 : /* -------------------------------------------------------------------- */
4798 : /* Find code (ignoring version) out of string like: */
4799 : /* */
4800 : /* authority:[version]:code */
4801 : /* -------------------------------------------------------------------- */
4802 1 : pszAuthority = pszCur;
4803 :
4804 : // skip authority
4805 5 : while (*pszCur != ':' && *pszCur)
4806 4 : pszCur++;
4807 1 : if (*pszCur == ':')
4808 1 : pszCur++;
4809 :
4810 : // skip version
4811 1 : pszBeforeVersion = pszCur;
4812 1 : while (*pszCur != ':' && *pszCur)
4813 0 : pszCur++;
4814 1 : if (*pszCur == ':')
4815 1 : pszCur++;
4816 : else
4817 0 : pszCur = pszBeforeVersion;
4818 :
4819 1 : pszCode = pszCur;
4820 :
4821 2 : OGRSpatialReference oVertSRS;
4822 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4823 1 : if (eStatus == OGRERR_NONE)
4824 : {
4825 1 : OGRSpatialReference oHorizSRS(*this);
4826 :
4827 1 : Clear();
4828 :
4829 1 : oHorizSRS.d->refreshProjObj();
4830 1 : oVertSRS.d->refreshProjObj();
4831 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4832 0 : return OGRERR_FAILURE;
4833 :
4834 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4835 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4836 :
4837 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4838 1 : osName += " + ";
4839 1 : osName += pszVertName ? pszVertName : "";
4840 :
4841 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4842 : }
4843 :
4844 1 : return eStatus;
4845 : #endif
4846 : }
4847 :
4848 : /************************************************************************/
4849 : /* importFromCRSURL() */
4850 : /* */
4851 : /* See OGC Best Practice document 11-135 for details. */
4852 : /************************************************************************/
4853 :
4854 : /**
4855 : * \brief Initialize from OGC URL.
4856 : *
4857 : * Initializes this spatial reference from a coordinate system defined
4858 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4859 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4860 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4861 : *
4862 : * This method is also supported through SetFromUserInput() which can
4863 : * normally be used for URLs.
4864 : *
4865 : * @param pszURL the URL string.
4866 : *
4867 : * @return OGRERR_NONE on success or an error code.
4868 : */
4869 :
4870 5212 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4871 :
4872 : {
4873 10424 : TAKE_OPTIONAL_LOCK();
4874 :
4875 : #if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4876 5212 : if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4877 : {
4878 12 : PJ *obj = proj_create(
4879 : d->getPROJContext(),
4880 : "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4881 : " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4882 : " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4883 : " MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4884 : " MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4885 : " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4886 : " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4887 : " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4888 : " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4889 : " MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4890 : " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4891 : " LENGTHUNIT[\"metre\",1]],\n"
4892 : " ENSEMBLEACCURACY[2.0]],\n"
4893 : " PRIMEM[\"Greenwich\",0,\n"
4894 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4895 : " CS[ellipsoidal,3],\n"
4896 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4897 : " ORDER[1],\n"
4898 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4899 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4900 : " ORDER[2],\n"
4901 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4902 : " AXIS[\"ellipsoidal height (h)\",up,\n"
4903 : " ORDER[3],\n"
4904 : " LENGTHUNIT[\"metre\",1]],\n"
4905 : " USAGE[\n"
4906 : " SCOPE[\"3D system frequently used in GIS, Web APIs and "
4907 : "Web applications\"],\n"
4908 : " AREA[\"World.\"],\n"
4909 : " BBOX[-90,-180,90,180]],\n"
4910 : " ID[\"OGC\",\"CRS84h\"]]");
4911 12 : if (!obj)
4912 : {
4913 0 : return OGRERR_FAILURE;
4914 : }
4915 12 : Clear();
4916 12 : d->setPjCRS(obj);
4917 12 : return OGRERR_NONE;
4918 : }
4919 : #endif
4920 :
4921 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4922 : if (strlen(pszURL) >= 10000)
4923 : {
4924 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4925 : return OGRERR_CORRUPT_DATA;
4926 : }
4927 :
4928 : PJ *obj;
4929 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4930 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4931 : {
4932 : obj = proj_create(
4933 : d->getPROJContext(),
4934 : CPLSPrintf("IAU:%s",
4935 : pszURL +
4936 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4937 : }
4938 : else
4939 : #endif
4940 : {
4941 : obj = proj_create(d->getPROJContext(), pszURL);
4942 : }
4943 : if (!obj)
4944 : {
4945 : return OGRERR_FAILURE;
4946 : }
4947 : Clear();
4948 : d->setPjCRS(obj);
4949 : return OGRERR_NONE;
4950 : #else
4951 5200 : const char *pszCur = nullptr;
4952 :
4953 5200 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4954 2 : pszCur = pszURL + 26;
4955 5198 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4956 1 : pszCur = pszURL + 27;
4957 5197 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4958 5196 : pszCur = pszURL + 30;
4959 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4960 1 : pszCur = pszURL + 31;
4961 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4962 0 : pszCur = pszURL + 23;
4963 : else
4964 : {
4965 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4966 : pszURL);
4967 0 : return OGRERR_FAILURE;
4968 : }
4969 :
4970 5200 : if (*pszCur == '\0')
4971 : {
4972 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4973 0 : return OGRERR_FAILURE;
4974 : }
4975 :
4976 : /* -------------------------------------------------------------------- */
4977 : /* Clear any existing definition. */
4978 : /* -------------------------------------------------------------------- */
4979 5200 : Clear();
4980 :
4981 5200 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4982 : {
4983 : /* --------------------------------------------------------------------
4984 : */
4985 : /* It's a compound CRS, of the form: */
4986 : /* */
4987 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4988 : /* --------------------------------------------------------------------
4989 : */
4990 1 : pszCur += 12;
4991 :
4992 : // Extract each component CRS URL.
4993 1 : int iComponentUrl = 2;
4994 :
4995 2 : CPLString osName = "";
4996 1 : Clear();
4997 :
4998 3 : while (iComponentUrl != -1)
4999 : {
5000 2 : char searchStr[15] = {};
5001 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5002 :
5003 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
5004 :
5005 : // Figure out the next component URL.
5006 2 : char *pszComponentUrl = nullptr;
5007 :
5008 2 : if (pszUrlEnd)
5009 : {
5010 1 : size_t nLen = pszUrlEnd - pszCur;
5011 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5012 1 : strncpy(pszComponentUrl, pszCur, nLen);
5013 1 : pszComponentUrl[nLen] = '\0';
5014 :
5015 1 : ++iComponentUrl;
5016 1 : pszCur += nLen + strlen(searchStr);
5017 : }
5018 : else
5019 : {
5020 1 : if (iComponentUrl == 2)
5021 : {
5022 0 : CPLError(CE_Failure, CPLE_AppDefined,
5023 : "Compound CRS URLs must have at least two "
5024 : "component CRSs.");
5025 0 : return OGRERR_FAILURE;
5026 : }
5027 : else
5028 : {
5029 1 : pszComponentUrl = CPLStrdup(pszCur);
5030 : // no more components
5031 1 : iComponentUrl = -1;
5032 : }
5033 : }
5034 :
5035 2 : OGRSpatialReference oComponentSRS;
5036 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5037 :
5038 2 : CPLFree(pszComponentUrl);
5039 2 : pszComponentUrl = nullptr;
5040 :
5041 2 : if (eStatus == OGRERR_NONE)
5042 : {
5043 2 : if (osName.length() != 0)
5044 : {
5045 1 : osName += " + ";
5046 : }
5047 2 : osName += oComponentSRS.GetRoot()->GetValue();
5048 2 : SetNode("COMPD_CS", osName);
5049 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5050 : }
5051 : else
5052 0 : return eStatus;
5053 : }
5054 :
5055 1 : return OGRERR_NONE;
5056 : }
5057 :
5058 : /* -------------------------------------------------------------------- */
5059 : /* It's a normal CRS URL, of the form: */
5060 : /* */
5061 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5062 : /* -------------------------------------------------------------------- */
5063 5199 : ++pszCur;
5064 5199 : const char *pszAuthority = pszCur;
5065 :
5066 : // skip authority
5067 125485 : while (*pszCur != '/' && *pszCur)
5068 120286 : pszCur++;
5069 5199 : if (*pszCur == '/')
5070 5198 : pszCur++;
5071 :
5072 : // skip version
5073 11381 : while (*pszCur != '/' && *pszCur)
5074 6182 : pszCur++;
5075 5199 : if (*pszCur == '/')
5076 5198 : pszCur++;
5077 :
5078 5199 : const char *pszCode = pszCur;
5079 :
5080 5199 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5081 : #endif
5082 : }
5083 :
5084 : /************************************************************************/
5085 : /* importFromWMSAUTO() */
5086 : /************************************************************************/
5087 :
5088 : /**
5089 : * \brief Initialize from WMSAUTO string.
5090 : *
5091 : * Note that the WMS 1.3 specification does not include the
5092 : * units code, while apparently earlier specs do. We try to
5093 : * guess around this.
5094 : *
5095 : * @param pszDefinition the WMSAUTO string
5096 : *
5097 : * @return OGRERR_NONE on success or an error code.
5098 : */
5099 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5100 :
5101 : {
5102 6 : TAKE_OPTIONAL_LOCK();
5103 :
5104 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5105 : if (strlen(pszDefinition) >= 10000)
5106 : {
5107 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5108 : return OGRERR_CORRUPT_DATA;
5109 : }
5110 :
5111 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5112 : if (!obj)
5113 : {
5114 : return OGRERR_FAILURE;
5115 : }
5116 : Clear();
5117 : d->setPjCRS(obj);
5118 : return OGRERR_NONE;
5119 : #else
5120 : int nProjId, nUnitsId;
5121 3 : double dfRefLong, dfRefLat = 0.0;
5122 :
5123 : /* -------------------------------------------------------------------- */
5124 : /* Tokenize */
5125 : /* -------------------------------------------------------------------- */
5126 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5127 3 : pszDefinition += 5;
5128 :
5129 : char **papszTokens =
5130 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5131 :
5132 3 : if (CSLCount(papszTokens) == 4)
5133 : {
5134 0 : nProjId = atoi(papszTokens[0]);
5135 0 : nUnitsId = atoi(papszTokens[1]);
5136 0 : dfRefLong = CPLAtof(papszTokens[2]);
5137 0 : dfRefLat = CPLAtof(papszTokens[3]);
5138 : }
5139 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5140 : {
5141 0 : nProjId = atoi(papszTokens[0]);
5142 0 : nUnitsId = atoi(papszTokens[1]);
5143 0 : dfRefLong = CPLAtof(papszTokens[2]);
5144 0 : dfRefLat = 0.0;
5145 : }
5146 3 : else if (CSLCount(papszTokens) == 3)
5147 : {
5148 2 : nProjId = atoi(papszTokens[0]);
5149 2 : nUnitsId = 9001;
5150 2 : dfRefLong = CPLAtof(papszTokens[1]);
5151 2 : dfRefLat = CPLAtof(papszTokens[2]);
5152 : }
5153 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5154 : {
5155 0 : nProjId = atoi(papszTokens[0]);
5156 0 : nUnitsId = 9001;
5157 0 : dfRefLong = CPLAtof(papszTokens[1]);
5158 : }
5159 : else
5160 : {
5161 1 : CSLDestroy(papszTokens);
5162 1 : CPLError(CE_Failure, CPLE_AppDefined,
5163 : "AUTO projection has wrong number of arguments, expected\n"
5164 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5165 : "AUTO:proj_id,ref_long,ref_lat");
5166 1 : return OGRERR_FAILURE;
5167 : }
5168 :
5169 2 : CSLDestroy(papszTokens);
5170 2 : papszTokens = nullptr;
5171 :
5172 : /* -------------------------------------------------------------------- */
5173 : /* Build coordsys. */
5174 : /* -------------------------------------------------------------------- */
5175 2 : Clear();
5176 :
5177 : /* -------------------------------------------------------------------- */
5178 : /* Set WGS84. */
5179 : /* -------------------------------------------------------------------- */
5180 2 : SetWellKnownGeogCS("WGS84");
5181 :
5182 2 : switch (nProjId)
5183 : {
5184 2 : case 42001: // Auto UTM
5185 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5186 : dfRefLat >= 0.0);
5187 2 : break;
5188 :
5189 0 : case 42002: // Auto TM (strangely very UTM-like).
5190 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5191 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5192 0 : break;
5193 :
5194 0 : case 42003: // Auto Orthographic.
5195 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5196 0 : break;
5197 :
5198 0 : case 42004: // Auto Equirectangular
5199 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5200 0 : break;
5201 :
5202 0 : case 42005:
5203 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5204 0 : break;
5205 :
5206 0 : default:
5207 0 : CPLError(CE_Failure, CPLE_AppDefined,
5208 : "Unsupported projection id in importFromWMSAUTO(): %d",
5209 : nProjId);
5210 0 : return OGRERR_FAILURE;
5211 : }
5212 :
5213 : /* -------------------------------------------------------------------- */
5214 : /* Set units. */
5215 : /* -------------------------------------------------------------------- */
5216 :
5217 2 : switch (nUnitsId)
5218 : {
5219 2 : case 9001:
5220 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5221 2 : break;
5222 :
5223 0 : case 9002:
5224 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5225 0 : break;
5226 :
5227 0 : case 9003:
5228 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5229 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5230 0 : break;
5231 :
5232 0 : default:
5233 0 : CPLError(CE_Failure, CPLE_AppDefined,
5234 : "Unsupported units code (%d).", nUnitsId);
5235 0 : return OGRERR_FAILURE;
5236 : break;
5237 : }
5238 :
5239 2 : return OGRERR_NONE;
5240 : #endif
5241 : }
5242 :
5243 : /************************************************************************/
5244 : /* GetSemiMajor() */
5245 : /************************************************************************/
5246 :
5247 : /**
5248 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5249 : *
5250 : * This method does the same thing as the C function OSRGetSemiMajor().
5251 : *
5252 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5253 : * can be found.
5254 : *
5255 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5256 : */
5257 :
5258 6318 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5259 :
5260 : {
5261 12636 : TAKE_OPTIONAL_LOCK();
5262 :
5263 6318 : if (pnErr != nullptr)
5264 3357 : *pnErr = OGRERR_FAILURE;
5265 :
5266 6318 : d->refreshProjObj();
5267 6318 : if (!d->m_pj_crs)
5268 111 : return SRS_WGS84_SEMIMAJOR;
5269 :
5270 6207 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5271 6207 : if (!ellps)
5272 5 : return SRS_WGS84_SEMIMAJOR;
5273 :
5274 6202 : double dfSemiMajor = 0.0;
5275 6202 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5276 : nullptr, nullptr, nullptr);
5277 6202 : proj_destroy(ellps);
5278 :
5279 6202 : if (dfSemiMajor > 0)
5280 : {
5281 6202 : if (pnErr != nullptr)
5282 3243 : *pnErr = OGRERR_NONE;
5283 6202 : return dfSemiMajor;
5284 : }
5285 :
5286 0 : return SRS_WGS84_SEMIMAJOR;
5287 : }
5288 :
5289 : /************************************************************************/
5290 : /* OSRGetSemiMajor() */
5291 : /************************************************************************/
5292 :
5293 : /**
5294 : * \brief Get spheroid semi major axis.
5295 : *
5296 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5297 : */
5298 78 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5299 :
5300 : {
5301 78 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5302 :
5303 78 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5304 : }
5305 :
5306 : /************************************************************************/
5307 : /* GetInvFlattening() */
5308 : /************************************************************************/
5309 :
5310 : /**
5311 : * \brief Get spheroid inverse flattening.
5312 : *
5313 : * This method does the same thing as the C function OSRGetInvFlattening().
5314 : *
5315 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5316 : * can be found.
5317 : *
5318 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5319 : */
5320 :
5321 4330 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5322 :
5323 : {
5324 8660 : TAKE_OPTIONAL_LOCK();
5325 :
5326 4330 : if (pnErr != nullptr)
5327 3260 : *pnErr = OGRERR_FAILURE;
5328 :
5329 4330 : d->refreshProjObj();
5330 4330 : if (!d->m_pj_crs)
5331 111 : return SRS_WGS84_INVFLATTENING;
5332 :
5333 4219 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5334 4219 : if (!ellps)
5335 2 : return SRS_WGS84_INVFLATTENING;
5336 :
5337 4217 : double dfInvFlattening = -1.0;
5338 4217 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5339 : nullptr, &dfInvFlattening);
5340 4217 : proj_destroy(ellps);
5341 :
5342 4217 : if (dfInvFlattening >= 0.0)
5343 : {
5344 4217 : if (pnErr != nullptr)
5345 3149 : *pnErr = OGRERR_NONE;
5346 4217 : return dfInvFlattening;
5347 : }
5348 :
5349 0 : return SRS_WGS84_INVFLATTENING;
5350 : }
5351 :
5352 : /************************************************************************/
5353 : /* OSRGetInvFlattening() */
5354 : /************************************************************************/
5355 :
5356 : /**
5357 : * \brief Get spheroid inverse flattening.
5358 : *
5359 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5360 : */
5361 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5362 :
5363 : {
5364 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5365 :
5366 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5367 : }
5368 :
5369 : /************************************************************************/
5370 : /* GetEccentricity() */
5371 : /************************************************************************/
5372 :
5373 : /**
5374 : * \brief Get spheroid eccentricity
5375 : *
5376 : * @return eccentricity (or -1 in case of error)
5377 : */
5378 :
5379 0 : double OGRSpatialReference::GetEccentricity() const
5380 :
5381 : {
5382 0 : OGRErr eErr = OGRERR_NONE;
5383 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5384 0 : if (eErr != OGRERR_NONE)
5385 : {
5386 0 : return -1.0;
5387 : }
5388 0 : if (dfInvFlattening == 0.0)
5389 0 : return 0.0;
5390 0 : if (dfInvFlattening < 0.5)
5391 0 : return -1.0;
5392 0 : return sqrt(2.0 / dfInvFlattening -
5393 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5394 : }
5395 :
5396 : /************************************************************************/
5397 : /* GetSquaredEccentricity() */
5398 : /************************************************************************/
5399 :
5400 : /**
5401 : * \brief Get spheroid squared eccentricity
5402 : *
5403 : * @return squared eccentricity (or -1 in case of error)
5404 : */
5405 :
5406 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5407 :
5408 : {
5409 0 : OGRErr eErr = OGRERR_NONE;
5410 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5411 0 : if (eErr != OGRERR_NONE)
5412 : {
5413 0 : return -1.0;
5414 : }
5415 0 : if (dfInvFlattening == 0.0)
5416 0 : return 0.0;
5417 0 : if (dfInvFlattening < 0.5)
5418 0 : return -1.0;
5419 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5420 : }
5421 :
5422 : /************************************************************************/
5423 : /* GetSemiMinor() */
5424 : /************************************************************************/
5425 :
5426 : /**
5427 : * \brief Get spheroid semi minor axis.
5428 : *
5429 : * This method does the same thing as the C function OSRGetSemiMinor().
5430 : *
5431 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5432 : * can be found.
5433 : *
5434 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5435 : */
5436 :
5437 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5438 :
5439 : {
5440 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5441 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5442 :
5443 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5444 : }
5445 :
5446 : /************************************************************************/
5447 : /* OSRGetSemiMinor() */
5448 : /************************************************************************/
5449 :
5450 : /**
5451 : * \brief Get spheroid semi minor axis.
5452 : *
5453 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5454 : */
5455 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5456 :
5457 : {
5458 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5459 :
5460 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5461 : }
5462 :
5463 : /************************************************************************/
5464 : /* SetLocalCS() */
5465 : /************************************************************************/
5466 :
5467 : /**
5468 : * \brief Set the user visible LOCAL_CS name.
5469 : *
5470 : * This method is the same as the C function OSRSetLocalCS().
5471 : *
5472 : * This method will ensure a LOCAL_CS node is created as the root,
5473 : * and set the provided name on it. It must be used before SetLinearUnits().
5474 : *
5475 : * @param pszName the user visible name to assign. Not used as a key.
5476 : *
5477 : * @return OGRERR_NONE on success.
5478 : */
5479 :
5480 2892 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5481 :
5482 : {
5483 5784 : TAKE_OPTIONAL_LOCK();
5484 :
5485 2892 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5486 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5487 : {
5488 2892 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5489 : }
5490 : else
5491 : {
5492 0 : CPLDebug("OGR",
5493 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5494 : "It appears an incompatible object already exists.",
5495 : pszName);
5496 0 : return OGRERR_FAILURE;
5497 : }
5498 :
5499 2892 : return OGRERR_NONE;
5500 : }
5501 :
5502 : /************************************************************************/
5503 : /* OSRSetLocalCS() */
5504 : /************************************************************************/
5505 :
5506 : /**
5507 : * \brief Set the user visible LOCAL_CS name.
5508 : *
5509 : * This function is the same as OGRSpatialReference::SetLocalCS()
5510 : */
5511 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5512 :
5513 : {
5514 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5515 :
5516 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5517 : }
5518 :
5519 : /************************************************************************/
5520 : /* SetGeocCS() */
5521 : /************************************************************************/
5522 :
5523 : /**
5524 : * \brief Set the user visible GEOCCS name.
5525 : *
5526 : * This method is the same as the C function OSRSetGeocCS().
5527 :
5528 : * This method will ensure a GEOCCS node is created as the root,
5529 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5530 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5531 : * the GEOGCS.
5532 : *
5533 : * @param pszName the user visible name to assign. Not used as a key.
5534 : *
5535 : * @return OGRERR_NONE on success.
5536 : *
5537 : */
5538 :
5539 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5540 :
5541 : {
5542 12 : TAKE_OPTIONAL_LOCK();
5543 :
5544 6 : OGRErr eErr = OGRERR_NONE;
5545 6 : d->refreshProjObj();
5546 6 : d->demoteFromBoundCRS();
5547 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5548 : {
5549 3 : d->setPjCRS(proj_create_geocentric_crs(
5550 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5551 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5552 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5553 : "Metre", 1.0));
5554 : }
5555 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5556 : {
5557 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5558 : }
5559 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5560 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5561 : {
5562 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5563 : #if PROJ_VERSION_MAJOR > 7 || \
5564 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5565 : if (datum == nullptr)
5566 : {
5567 : datum =
5568 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5569 : }
5570 : #endif
5571 1 : if (datum == nullptr)
5572 : {
5573 0 : d->undoDemoteFromBoundCRS();
5574 0 : return OGRERR_FAILURE;
5575 : }
5576 :
5577 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5578 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5579 : 0.0);
5580 1 : d->setPjCRS(pj_crs);
5581 :
5582 1 : proj_destroy(datum);
5583 : }
5584 : else
5585 : {
5586 1 : CPLDebug("OGR",
5587 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5588 : "It appears an incompatible object already exists.",
5589 : pszName);
5590 1 : eErr = OGRERR_FAILURE;
5591 : }
5592 6 : d->undoDemoteFromBoundCRS();
5593 :
5594 6 : return eErr;
5595 : }
5596 :
5597 : /************************************************************************/
5598 : /* OSRSetGeocCS() */
5599 : /************************************************************************/
5600 :
5601 : /**
5602 : * \brief Set the user visible PROJCS name.
5603 : *
5604 : * This function is the same as OGRSpatialReference::SetGeocCS()
5605 : *
5606 : */
5607 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5608 :
5609 : {
5610 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5611 :
5612 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5613 : }
5614 :
5615 : /************************************************************************/
5616 : /* SetVertCS() */
5617 : /************************************************************************/
5618 :
5619 : /**
5620 : * \brief Set the user visible VERT_CS name.
5621 : *
5622 : * This method is the same as the C function OSRSetVertCS().
5623 :
5624 : * This method will ensure a VERT_CS node is created if needed. If the
5625 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5626 : * turned into a COMPD_CS.
5627 : *
5628 : * @param pszVertCSName the user visible name of the vertical coordinate
5629 : * system. Not used as a key.
5630 : *
5631 : * @param pszVertDatumName the user visible name of the vertical datum. It
5632 : * is helpful if this matches the EPSG name.
5633 : *
5634 : * @param nVertDatumType the OGC vertical datum type. Ignored
5635 : *
5636 : * @return OGRERR_NONE on success.
5637 : *
5638 : */
5639 :
5640 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5641 : const char *pszVertDatumName,
5642 : int nVertDatumType)
5643 :
5644 : {
5645 1 : TAKE_OPTIONAL_LOCK();
5646 :
5647 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5648 :
5649 1 : d->refreshProjObj();
5650 :
5651 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5652 : pszVertDatumName, nullptr, 0.0);
5653 :
5654 : /* -------------------------------------------------------------------- */
5655 : /* Handle the case where we want to make a compound coordinate */
5656 : /* system. */
5657 : /* -------------------------------------------------------------------- */
5658 1 : if (IsProjected() || IsGeographic())
5659 : {
5660 1 : auto compoundCRS = proj_create_compound_crs(
5661 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5662 1 : proj_destroy(vertCRS);
5663 1 : d->setPjCRS(compoundCRS);
5664 : }
5665 : else
5666 : {
5667 0 : d->setPjCRS(vertCRS);
5668 : }
5669 2 : return OGRERR_NONE;
5670 : }
5671 :
5672 : /************************************************************************/
5673 : /* OSRSetVertCS() */
5674 : /************************************************************************/
5675 :
5676 : /**
5677 : * \brief Setup the vertical coordinate system.
5678 : *
5679 : * This function is the same as OGRSpatialReference::SetVertCS()
5680 : *
5681 : */
5682 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5683 : const char *pszVertDatumName, int nVertDatumType)
5684 :
5685 : {
5686 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5687 :
5688 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5689 0 : nVertDatumType);
5690 : }
5691 :
5692 : /************************************************************************/
5693 : /* SetCompoundCS() */
5694 : /************************************************************************/
5695 :
5696 : /**
5697 : * \brief Setup a compound coordinate system.
5698 : *
5699 : * This method is the same as the C function OSRSetCompoundCS().
5700 :
5701 : * This method is replace the current SRS with a COMPD_CS coordinate system
5702 : * consisting of the passed in horizontal and vertical coordinate systems.
5703 : *
5704 : * @param pszName the name of the compound coordinate system.
5705 : *
5706 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5707 : *
5708 : * @param poVertSRS the vertical SRS (VERT_CS).
5709 : *
5710 : * @return OGRERR_NONE on success.
5711 : */
5712 :
5713 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5714 : const OGRSpatialReference *poHorizSRS,
5715 : const OGRSpatialReference *poVertSRS)
5716 :
5717 : {
5718 184 : TAKE_OPTIONAL_LOCK();
5719 :
5720 : /* -------------------------------------------------------------------- */
5721 : /* Verify these are legal horizontal and vertical coordinate */
5722 : /* systems. */
5723 : /* -------------------------------------------------------------------- */
5724 92 : if (!poVertSRS->IsVertical())
5725 : {
5726 0 : CPLError(CE_Failure, CPLE_AppDefined,
5727 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5728 0 : return OGRERR_FAILURE;
5729 : }
5730 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5731 : {
5732 0 : CPLError(CE_Failure, CPLE_AppDefined,
5733 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5734 : "GEOGCS.");
5735 0 : return OGRERR_FAILURE;
5736 : }
5737 :
5738 : /* -------------------------------------------------------------------- */
5739 : /* Replace with compound srs. */
5740 : /* -------------------------------------------------------------------- */
5741 92 : Clear();
5742 :
5743 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5744 92 : poHorizSRS->d->m_pj_crs,
5745 92 : poVertSRS->d->m_pj_crs);
5746 92 : d->setPjCRS(compoundCRS);
5747 :
5748 92 : return OGRERR_NONE;
5749 : }
5750 :
5751 : /************************************************************************/
5752 : /* OSRSetCompoundCS() */
5753 : /************************************************************************/
5754 :
5755 : /**
5756 : * \brief Setup a compound coordinate system.
5757 : *
5758 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5759 : */
5760 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5761 : OGRSpatialReferenceH hHorizSRS,
5762 : OGRSpatialReferenceH hVertSRS)
5763 :
5764 : {
5765 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5766 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5767 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5768 :
5769 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5770 16 : ToPointer(hVertSRS));
5771 : }
5772 :
5773 : /************************************************************************/
5774 : /* SetProjCS() */
5775 : /************************************************************************/
5776 :
5777 : /**
5778 : * \brief Set the user visible PROJCS name.
5779 : *
5780 : * This method is the same as the C function OSRSetProjCS().
5781 : *
5782 : * This method will ensure a PROJCS node is created as the root,
5783 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5784 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5785 : *
5786 : * @param pszName the user visible name to assign. Not used as a key.
5787 : *
5788 : * @return OGRERR_NONE on success.
5789 : */
5790 :
5791 4535 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5792 :
5793 : {
5794 4535 : TAKE_OPTIONAL_LOCK();
5795 :
5796 4535 : d->refreshProjObj();
5797 4535 : d->demoteFromBoundCRS();
5798 4535 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5799 : {
5800 487 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5801 : }
5802 : else
5803 : {
5804 4048 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5805 : nullptr, nullptr, nullptr,
5806 : nullptr, nullptr, 0, nullptr);
5807 4048 : auto cs = proj_create_cartesian_2D_cs(
5808 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5809 :
5810 4048 : auto projCRS = proj_create_projected_crs(
5811 4048 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5812 4048 : proj_destroy(dummyConv);
5813 4048 : proj_destroy(cs);
5814 :
5815 4048 : d->setPjCRS(projCRS);
5816 : }
5817 4535 : d->undoDemoteFromBoundCRS();
5818 9070 : return OGRERR_NONE;
5819 : }
5820 :
5821 : /************************************************************************/
5822 : /* OSRSetProjCS() */
5823 : /************************************************************************/
5824 :
5825 : /**
5826 : * \brief Set the user visible PROJCS name.
5827 : *
5828 : * This function is the same as OGRSpatialReference::SetProjCS()
5829 : */
5830 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5831 :
5832 : {
5833 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5834 :
5835 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5836 : }
5837 :
5838 : /************************************************************************/
5839 : /* SetProjection() */
5840 : /************************************************************************/
5841 :
5842 : /**
5843 : * \brief Set a projection name.
5844 : *
5845 : * This method is the same as the C function OSRSetProjection().
5846 : *
5847 : * @param pszProjection the projection name, which should be selected from
5848 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5849 : *
5850 : * @return OGRERR_NONE on success.
5851 : */
5852 :
5853 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5854 :
5855 : {
5856 46 : TAKE_OPTIONAL_LOCK();
5857 :
5858 23 : OGR_SRSNode *poGeogCS = nullptr;
5859 :
5860 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5861 : {
5862 4 : poGeogCS = d->m_poRoot;
5863 4 : d->m_poRoot = nullptr;
5864 : }
5865 :
5866 23 : if (!GetAttrNode("PROJCS"))
5867 : {
5868 11 : SetNode("PROJCS", "unnamed");
5869 : }
5870 :
5871 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5872 23 : if (eErr != OGRERR_NONE)
5873 0 : return eErr;
5874 :
5875 23 : if (poGeogCS != nullptr)
5876 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5877 :
5878 23 : return OGRERR_NONE;
5879 : }
5880 :
5881 : /************************************************************************/
5882 : /* OSRSetProjection() */
5883 : /************************************************************************/
5884 :
5885 : /**
5886 : * \brief Set a projection name.
5887 : *
5888 : * This function is the same as OGRSpatialReference::SetProjection()
5889 : */
5890 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5891 :
5892 : {
5893 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5894 :
5895 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5896 : }
5897 :
5898 : /************************************************************************/
5899 : /* GetWKT2ProjectionMethod() */
5900 : /************************************************************************/
5901 :
5902 : /**
5903 : * \brief Returns info on the projection method, based on WKT2 naming
5904 : * conventions.
5905 : *
5906 : * The returned strings are short lived and should be considered to be
5907 : * invalidated by any further call to the GDAL API.
5908 : *
5909 : * @param[out] ppszMethodName Pointer to a string that will receive the
5910 : * projection method name.
5911 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5912 : * receive the name of the authority that defines the projection method.
5913 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5914 : * an authority.
5915 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5916 : * receive the code that defines the projection method.
5917 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5918 : * an authority.
5919 : *
5920 : * @return OGRERR_NONE on success.
5921 : */
5922 : OGRErr
5923 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5924 : const char **ppszMethodAuthName,
5925 : const char **ppszMethodCode) const
5926 : {
5927 2 : TAKE_OPTIONAL_LOCK();
5928 :
5929 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5930 1 : if (!conv)
5931 0 : return OGRERR_FAILURE;
5932 1 : const char *pszTmpMethodName = "";
5933 1 : const char *pszTmpMethodAuthName = "";
5934 1 : const char *pszTmpMethodCode = "";
5935 1 : int ret = proj_coordoperation_get_method_info(
5936 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5937 : &pszTmpMethodCode);
5938 : // "Internalize" temporary strings returned by PROJ
5939 1 : CPLAssert(pszTmpMethodName);
5940 1 : if (ppszMethodName)
5941 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5942 1 : if (ppszMethodAuthName)
5943 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5944 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5945 0 : : nullptr;
5946 1 : if (ppszMethodCode)
5947 0 : *ppszMethodCode =
5948 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5949 1 : proj_destroy(conv);
5950 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5951 : }
5952 :
5953 : /************************************************************************/
5954 : /* SetProjParm() */
5955 : /************************************************************************/
5956 :
5957 : /**
5958 : * \brief Set a projection parameter value.
5959 : *
5960 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5961 : *
5962 : * This method is the same as the C function OSRSetProjParm().
5963 : *
5964 : * Please check https://gdal.org/proj_list pages for
5965 : * legal parameter names for specific projections.
5966 : *
5967 : *
5968 : * @param pszParamName the parameter name, which should be selected from
5969 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5970 : *
5971 : * @param dfValue value to assign.
5972 : *
5973 : * @return OGRERR_NONE on success.
5974 : */
5975 :
5976 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5977 : double dfValue)
5978 :
5979 : {
5980 258 : TAKE_OPTIONAL_LOCK();
5981 :
5982 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5983 :
5984 129 : if (poPROJCS == nullptr)
5985 3 : return OGRERR_FAILURE;
5986 :
5987 126 : char szValue[64] = {'\0'};
5988 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5989 :
5990 : /* -------------------------------------------------------------------- */
5991 : /* Try to find existing parameter with this name. */
5992 : /* -------------------------------------------------------------------- */
5993 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5994 : {
5995 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5996 :
5997 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5998 1242 : poParam->GetChildCount() == 2 &&
5999 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
6000 : {
6001 39 : poParam->GetChild(1)->SetValue(szValue);
6002 39 : return OGRERR_NONE;
6003 : }
6004 : }
6005 :
6006 : /* -------------------------------------------------------------------- */
6007 : /* Otherwise create a new parameter and append. */
6008 : /* -------------------------------------------------------------------- */
6009 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6010 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
6011 87 : poParam->AddChild(new OGR_SRSNode(szValue));
6012 :
6013 87 : poPROJCS->AddChild(poParam);
6014 :
6015 87 : return OGRERR_NONE;
6016 : }
6017 :
6018 : /************************************************************************/
6019 : /* OSRSetProjParm() */
6020 : /************************************************************************/
6021 :
6022 : /**
6023 : * \brief Set a projection parameter value.
6024 : *
6025 : * This function is the same as OGRSpatialReference::SetProjParm()
6026 : */
6027 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6028 : double dfValue)
6029 :
6030 : {
6031 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6032 :
6033 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6034 : }
6035 :
6036 : /************************************************************************/
6037 : /* FindProjParm() */
6038 : /************************************************************************/
6039 :
6040 : /**
6041 : * \brief Return the child index of the named projection parameter on
6042 : * its parent PROJCS node.
6043 : *
6044 : * @param pszParameter projection parameter to look for
6045 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6046 : * the PROJCS node of the SpatialReference object will be searched.
6047 : *
6048 : * @return the child index of the named projection parameter. -1 on failure
6049 : */
6050 4802 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6051 : const OGR_SRSNode *poPROJCS) const
6052 :
6053 : {
6054 9604 : TAKE_OPTIONAL_LOCK();
6055 :
6056 4802 : if (poPROJCS == nullptr)
6057 0 : poPROJCS = GetAttrNode("PROJCS");
6058 :
6059 4802 : if (poPROJCS == nullptr)
6060 0 : return -1;
6061 :
6062 : /* -------------------------------------------------------------------- */
6063 : /* Search for requested parameter. */
6064 : /* -------------------------------------------------------------------- */
6065 4802 : bool bIsWKT2 = false;
6066 31574 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6067 : {
6068 30957 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6069 :
6070 30957 : if (poParameter->GetChildCount() >= 2)
6071 : {
6072 21394 : const char *pszValue = poParameter->GetValue();
6073 35367 : if (EQUAL(pszValue, "PARAMETER") &&
6074 13973 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6075 : pszParameter))
6076 : {
6077 4185 : return iChild;
6078 : }
6079 17209 : else if (EQUAL(pszValue, "METHOD"))
6080 : {
6081 41 : bIsWKT2 = true;
6082 : }
6083 : }
6084 : }
6085 :
6086 : /* -------------------------------------------------------------------- */
6087 : /* Try similar names, for selected parameters. */
6088 : /* -------------------------------------------------------------------- */
6089 617 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6090 : {
6091 287 : if (bIsWKT2)
6092 : {
6093 8 : int iChild = FindProjParm(
6094 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6095 8 : if (iChild == -1)
6096 3 : iChild = FindProjParm(
6097 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6098 8 : return iChild;
6099 : }
6100 279 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6101 : }
6102 :
6103 330 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6104 : {
6105 34 : if (bIsWKT2)
6106 : {
6107 9 : int iChild = FindProjParm(
6108 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6109 9 : if (iChild == -1)
6110 0 : iChild = FindProjParm(
6111 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6112 9 : return iChild;
6113 : }
6114 25 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6115 25 : if (iChild == -1)
6116 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6117 25 : return iChild;
6118 : }
6119 :
6120 296 : return -1;
6121 : }
6122 :
6123 : /************************************************************************/
6124 : /* GetProjParm() */
6125 : /************************************************************************/
6126 :
6127 : /**
6128 : * \brief Fetch a projection parameter value.
6129 : *
6130 : * NOTE: This code should be modified to translate non degree angles into
6131 : * degrees based on the GEOGCS unit. This has not yet been done.
6132 : *
6133 : * This method is the same as the C function OSRGetProjParm().
6134 : *
6135 : * @param pszName the name of the parameter to fetch, from the set of
6136 : * SRS_PP codes in ogr_srs_api.h.
6137 : *
6138 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6139 : *
6140 : * @param pnErr place to put error code on failure. Ignored if NULL.
6141 : *
6142 : * @return value of parameter.
6143 : */
6144 :
6145 4736 : double OGRSpatialReference::GetProjParm(const char *pszName,
6146 : double dfDefaultValue,
6147 : OGRErr *pnErr) const
6148 :
6149 : {
6150 9472 : TAKE_OPTIONAL_LOCK();
6151 :
6152 4736 : d->refreshProjObj();
6153 4736 : GetRoot(); // force update of d->m_bNodesWKT2
6154 :
6155 4736 : if (pnErr != nullptr)
6156 3757 : *pnErr = OGRERR_NONE;
6157 :
6158 : /* -------------------------------------------------------------------- */
6159 : /* Find the desired parameter. */
6160 : /* -------------------------------------------------------------------- */
6161 : const OGR_SRSNode *poPROJCS =
6162 4736 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6163 4736 : if (poPROJCS == nullptr)
6164 : {
6165 258 : if (pnErr != nullptr)
6166 258 : *pnErr = OGRERR_FAILURE;
6167 258 : return dfDefaultValue;
6168 : }
6169 :
6170 4478 : const int iChild = FindProjParm(pszName, poPROJCS);
6171 4478 : if (iChild == -1)
6172 : {
6173 293 : if (IsProjected() && GetAxesCount() == 3)
6174 : {
6175 3 : OGRSpatialReference *poSRSTmp = Clone();
6176 3 : poSRSTmp->DemoteTo2D(nullptr);
6177 : const double dfRet =
6178 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6179 3 : delete poSRSTmp;
6180 3 : return dfRet;
6181 : }
6182 :
6183 290 : if (pnErr != nullptr)
6184 268 : *pnErr = OGRERR_FAILURE;
6185 290 : return dfDefaultValue;
6186 : }
6187 :
6188 4185 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6189 4185 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6190 : }
6191 :
6192 : /************************************************************************/
6193 : /* OSRGetProjParm() */
6194 : /************************************************************************/
6195 :
6196 : /**
6197 : * \brief Fetch a projection parameter value.
6198 : *
6199 : * This function is the same as OGRSpatialReference::GetProjParm()
6200 : */
6201 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6202 : double dfDefaultValue, OGRErr *pnErr)
6203 :
6204 : {
6205 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6206 :
6207 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6208 : }
6209 :
6210 : /************************************************************************/
6211 : /* GetNormProjParm() */
6212 : /************************************************************************/
6213 :
6214 : /**
6215 : * \brief Fetch a normalized projection parameter value.
6216 : *
6217 : * This method is the same as GetProjParm() except that the value of
6218 : * the parameter is "normalized" into degrees or meters depending on
6219 : * whether it is linear or angular.
6220 : *
6221 : * This method is the same as the C function OSRGetNormProjParm().
6222 : *
6223 : * @param pszName the name of the parameter to fetch, from the set of
6224 : * SRS_PP codes in ogr_srs_api.h.
6225 : *
6226 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6227 : *
6228 : * @param pnErr place to put error code on failure. Ignored if NULL.
6229 : *
6230 : * @return value of parameter.
6231 : */
6232 :
6233 3732 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6234 : double dfDefaultValue,
6235 : OGRErr *pnErr) const
6236 :
6237 : {
6238 7464 : TAKE_OPTIONAL_LOCK();
6239 :
6240 3732 : GetNormInfo();
6241 :
6242 3732 : OGRErr nError = OGRERR_NONE;
6243 3732 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6244 3732 : if (pnErr != nullptr)
6245 0 : *pnErr = nError;
6246 :
6247 : // If we got the default just return it unadjusted.
6248 3732 : if (nError != OGRERR_NONE)
6249 526 : return dfRawResult;
6250 :
6251 3206 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6252 8 : dfRawResult *= d->dfToDegrees;
6253 :
6254 3206 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6255 5 : return dfRawResult * d->dfToMeter;
6256 :
6257 3201 : return dfRawResult;
6258 : }
6259 :
6260 : /************************************************************************/
6261 : /* OSRGetNormProjParm() */
6262 : /************************************************************************/
6263 :
6264 : /**
6265 : * \brief This function is the same as OGRSpatialReference::
6266 : *
6267 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6268 : */
6269 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6270 : double dfDefaultValue, OGRErr *pnErr)
6271 :
6272 : {
6273 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6274 :
6275 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6276 : }
6277 :
6278 : /************************************************************************/
6279 : /* SetNormProjParm() */
6280 : /************************************************************************/
6281 :
6282 : /**
6283 : * \brief Set a projection parameter with a normalized value.
6284 : *
6285 : * This method is the same as SetProjParm() except that the value of
6286 : * the parameter passed in is assumed to be in "normalized" form (decimal
6287 : * degrees for angular values, meters for linear values. The values are
6288 : * converted in a form suitable for the GEOGCS and linear units in effect.
6289 : *
6290 : * This method is the same as the C function OSRSetNormProjParm().
6291 : *
6292 : * @param pszName the parameter name, which should be selected from
6293 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6294 : *
6295 : * @param dfValue value to assign.
6296 : *
6297 : * @return OGRERR_NONE on success.
6298 : */
6299 :
6300 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6301 :
6302 : {
6303 182 : TAKE_OPTIONAL_LOCK();
6304 :
6305 91 : GetNormInfo();
6306 :
6307 91 : if (d->dfToDegrees != 0.0 &&
6308 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6309 0 : IsAngularParameter(pszName))
6310 : {
6311 0 : dfValue /= d->dfToDegrees;
6312 : }
6313 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6314 4 : IsLinearParameter(pszName))
6315 4 : dfValue /= d->dfToMeter;
6316 :
6317 182 : return SetProjParm(pszName, dfValue);
6318 : }
6319 :
6320 : /************************************************************************/
6321 : /* OSRSetNormProjParm() */
6322 : /************************************************************************/
6323 :
6324 : /**
6325 : * \brief Set a projection parameter with a normalized value.
6326 : *
6327 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6328 : */
6329 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6330 : double dfValue)
6331 :
6332 : {
6333 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6334 :
6335 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6336 : }
6337 :
6338 : /************************************************************************/
6339 : /* SetTM() */
6340 : /************************************************************************/
6341 :
6342 439 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6343 : double dfScale, double dfFalseEasting,
6344 : double dfFalseNorthing)
6345 :
6346 : {
6347 878 : TAKE_OPTIONAL_LOCK();
6348 :
6349 439 : return d->replaceConversionAndUnref(
6350 : proj_create_conversion_transverse_mercator(
6351 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6352 878 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6353 : }
6354 :
6355 : /************************************************************************/
6356 : /* OSRSetTM() */
6357 : /************************************************************************/
6358 :
6359 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6360 : double dfCenterLong, double dfScale, double dfFalseEasting,
6361 : double dfFalseNorthing)
6362 :
6363 : {
6364 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6365 :
6366 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6367 1 : dfFalseEasting, dfFalseNorthing);
6368 : }
6369 :
6370 : /************************************************************************/
6371 : /* SetTMVariant() */
6372 : /************************************************************************/
6373 :
6374 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6375 : double dfCenterLat,
6376 : double dfCenterLong, double dfScale,
6377 : double dfFalseEasting,
6378 : double dfFalseNorthing)
6379 :
6380 : {
6381 0 : TAKE_OPTIONAL_LOCK();
6382 :
6383 0 : SetProjection(pszVariantName);
6384 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6385 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6386 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6387 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6388 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6389 :
6390 0 : return OGRERR_NONE;
6391 : }
6392 :
6393 : /************************************************************************/
6394 : /* OSRSetTMVariant() */
6395 : /************************************************************************/
6396 :
6397 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6398 : double dfCenterLat, double dfCenterLong, double dfScale,
6399 : double dfFalseEasting, double dfFalseNorthing)
6400 :
6401 : {
6402 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6403 :
6404 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6405 : dfCenterLong, dfScale, dfFalseEasting,
6406 0 : dfFalseNorthing);
6407 : }
6408 :
6409 : /************************************************************************/
6410 : /* SetTMSO() */
6411 : /************************************************************************/
6412 :
6413 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6414 : double dfScale, double dfFalseEasting,
6415 : double dfFalseNorthing)
6416 :
6417 : {
6418 6 : TAKE_OPTIONAL_LOCK();
6419 :
6420 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6421 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6422 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6423 :
6424 3 : const char *pszName = nullptr;
6425 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6426 3 : CPLString osName = pszName ? pszName : "";
6427 :
6428 3 : d->refreshProjObj();
6429 :
6430 3 : d->demoteFromBoundCRS();
6431 :
6432 3 : auto cs = proj_create_cartesian_2D_cs(
6433 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6434 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6435 : auto projCRS =
6436 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6437 3 : d->getGeodBaseCRS(), conv, cs);
6438 3 : proj_destroy(conv);
6439 3 : proj_destroy(cs);
6440 :
6441 3 : d->setPjCRS(projCRS);
6442 :
6443 3 : d->undoDemoteFromBoundCRS();
6444 :
6445 6 : return OGRERR_NONE;
6446 : }
6447 :
6448 : /************************************************************************/
6449 : /* OSRSetTMSO() */
6450 : /************************************************************************/
6451 :
6452 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6453 : double dfCenterLong, double dfScale, double dfFalseEasting,
6454 : double dfFalseNorthing)
6455 :
6456 : {
6457 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6458 :
6459 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6460 0 : dfFalseEasting, dfFalseNorthing);
6461 : }
6462 :
6463 : /************************************************************************/
6464 : /* SetTPED() */
6465 : /************************************************************************/
6466 :
6467 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6468 : double dfLat2, double dfLong2,
6469 : double dfFalseEasting,
6470 : double dfFalseNorthing)
6471 :
6472 : {
6473 2 : TAKE_OPTIONAL_LOCK();
6474 :
6475 1 : return d->replaceConversionAndUnref(
6476 : proj_create_conversion_two_point_equidistant(
6477 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6478 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6479 : }
6480 :
6481 : /************************************************************************/
6482 : /* OSRSetTPED() */
6483 : /************************************************************************/
6484 :
6485 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6486 : double dfLat2, double dfLong2, double dfFalseEasting,
6487 : double dfFalseNorthing)
6488 :
6489 : {
6490 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6491 :
6492 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6493 0 : dfFalseEasting, dfFalseNorthing);
6494 : }
6495 :
6496 : /************************************************************************/
6497 : /* SetTMG() */
6498 : /************************************************************************/
6499 :
6500 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6501 : double dfFalseEasting,
6502 : double dfFalseNorthing)
6503 :
6504 : {
6505 0 : TAKE_OPTIONAL_LOCK();
6506 :
6507 0 : return d->replaceConversionAndUnref(
6508 : proj_create_conversion_tunisia_mapping_grid(
6509 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6510 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6511 : }
6512 :
6513 : /************************************************************************/
6514 : /* OSRSetTMG() */
6515 : /************************************************************************/
6516 :
6517 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6518 : double dfCenterLong, double dfFalseEasting,
6519 : double dfFalseNorthing)
6520 :
6521 : {
6522 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6523 :
6524 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6525 0 : dfFalseNorthing);
6526 : }
6527 :
6528 : /************************************************************************/
6529 : /* SetACEA() */
6530 : /************************************************************************/
6531 :
6532 39 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6533 : double dfCenterLat, double dfCenterLong,
6534 : double dfFalseEasting,
6535 : double dfFalseNorthing)
6536 :
6537 : {
6538 78 : TAKE_OPTIONAL_LOCK();
6539 :
6540 : // Note different order of parameters. The one in PROJ is conformant with
6541 : // EPSG
6542 39 : return d->replaceConversionAndUnref(
6543 : proj_create_conversion_albers_equal_area(
6544 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6545 78 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6546 : }
6547 :
6548 : /************************************************************************/
6549 : /* OSRSetACEA() */
6550 : /************************************************************************/
6551 :
6552 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6553 : double dfCenterLat, double dfCenterLong,
6554 : double dfFalseEasting, double dfFalseNorthing)
6555 :
6556 : {
6557 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6558 :
6559 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6560 0 : dfFalseEasting, dfFalseNorthing);
6561 : }
6562 :
6563 : /************************************************************************/
6564 : /* SetAE() */
6565 : /************************************************************************/
6566 :
6567 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6568 : double dfFalseEasting, double dfFalseNorthing)
6569 :
6570 : {
6571 42 : TAKE_OPTIONAL_LOCK();
6572 :
6573 21 : return d->replaceConversionAndUnref(
6574 : proj_create_conversion_azimuthal_equidistant(
6575 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6576 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6577 : }
6578 :
6579 : /************************************************************************/
6580 : /* OSRSetAE() */
6581 : /************************************************************************/
6582 :
6583 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6584 : double dfCenterLong, double dfFalseEasting,
6585 : double dfFalseNorthing)
6586 :
6587 : {
6588 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6589 :
6590 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6591 0 : dfFalseNorthing);
6592 : }
6593 :
6594 : /************************************************************************/
6595 : /* SetBonne() */
6596 : /************************************************************************/
6597 :
6598 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6599 : double dfFalseEasting,
6600 : double dfFalseNorthing)
6601 :
6602 : {
6603 2 : TAKE_OPTIONAL_LOCK();
6604 :
6605 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6606 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6607 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6608 : }
6609 :
6610 : /************************************************************************/
6611 : /* OSRSetBonne() */
6612 : /************************************************************************/
6613 :
6614 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6615 : double dfCentralMeridian, double dfFalseEasting,
6616 : double dfFalseNorthing)
6617 :
6618 : {
6619 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6620 :
6621 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6622 0 : dfFalseNorthing);
6623 : }
6624 :
6625 : /************************************************************************/
6626 : /* SetCEA() */
6627 : /************************************************************************/
6628 :
6629 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6630 : double dfFalseEasting,
6631 : double dfFalseNorthing)
6632 :
6633 : {
6634 8 : TAKE_OPTIONAL_LOCK();
6635 :
6636 4 : return d->replaceConversionAndUnref(
6637 : proj_create_conversion_lambert_cylindrical_equal_area(
6638 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6639 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6640 : }
6641 :
6642 : /************************************************************************/
6643 : /* OSRSetCEA() */
6644 : /************************************************************************/
6645 :
6646 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6647 : double dfCentralMeridian, double dfFalseEasting,
6648 : double dfFalseNorthing)
6649 :
6650 : {
6651 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6652 :
6653 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6654 0 : dfFalseNorthing);
6655 : }
6656 :
6657 : /************************************************************************/
6658 : /* SetCS() */
6659 : /************************************************************************/
6660 :
6661 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6662 : double dfFalseEasting, double dfFalseNorthing)
6663 :
6664 : {
6665 10 : TAKE_OPTIONAL_LOCK();
6666 :
6667 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6668 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6669 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6670 : }
6671 :
6672 : /************************************************************************/
6673 : /* OSRSetCS() */
6674 : /************************************************************************/
6675 :
6676 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6677 : double dfCenterLong, double dfFalseEasting,
6678 : double dfFalseNorthing)
6679 :
6680 : {
6681 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6682 :
6683 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6684 0 : dfFalseNorthing);
6685 : }
6686 :
6687 : /************************************************************************/
6688 : /* SetEC() */
6689 : /************************************************************************/
6690 :
6691 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6692 : double dfCenterLat, double dfCenterLong,
6693 : double dfFalseEasting, double dfFalseNorthing)
6694 :
6695 : {
6696 14 : TAKE_OPTIONAL_LOCK();
6697 :
6698 : // Note: different order of arguments
6699 7 : return d->replaceConversionAndUnref(
6700 : proj_create_conversion_equidistant_conic(
6701 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6702 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6703 : }
6704 :
6705 : /************************************************************************/
6706 : /* OSRSetEC() */
6707 : /************************************************************************/
6708 :
6709 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6710 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6711 : double dfFalseNorthing)
6712 :
6713 : {
6714 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6715 :
6716 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6717 0 : dfFalseEasting, dfFalseNorthing);
6718 : }
6719 :
6720 : /************************************************************************/
6721 : /* SetEckert() */
6722 : /************************************************************************/
6723 :
6724 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6725 : double dfCentralMeridian,
6726 : double dfFalseEasting,
6727 : double dfFalseNorthing)
6728 :
6729 : {
6730 20 : TAKE_OPTIONAL_LOCK();
6731 :
6732 : PJ *conv;
6733 10 : if (nVariation == 1)
6734 : {
6735 1 : conv = proj_create_conversion_eckert_i(
6736 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6737 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6738 : }
6739 9 : else if (nVariation == 2)
6740 : {
6741 1 : conv = proj_create_conversion_eckert_ii(
6742 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6743 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6744 : }
6745 8 : else if (nVariation == 3)
6746 : {
6747 1 : conv = proj_create_conversion_eckert_iii(
6748 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6749 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6750 : }
6751 7 : else if (nVariation == 4)
6752 : {
6753 3 : conv = proj_create_conversion_eckert_iv(
6754 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6755 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6756 : }
6757 4 : else if (nVariation == 5)
6758 : {
6759 1 : conv = proj_create_conversion_eckert_v(
6760 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6761 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6762 : }
6763 3 : else if (nVariation == 6)
6764 : {
6765 3 : conv = proj_create_conversion_eckert_vi(
6766 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6767 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6768 : }
6769 : else
6770 : {
6771 0 : CPLError(CE_Failure, CPLE_AppDefined,
6772 : "Unsupported Eckert variation (%d).", nVariation);
6773 0 : return OGRERR_UNSUPPORTED_SRS;
6774 : }
6775 :
6776 10 : return d->replaceConversionAndUnref(conv);
6777 : }
6778 :
6779 : /************************************************************************/
6780 : /* OSRSetEckert() */
6781 : /************************************************************************/
6782 :
6783 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6784 : double dfCentralMeridian, double dfFalseEasting,
6785 : double dfFalseNorthing)
6786 :
6787 : {
6788 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6789 :
6790 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6791 0 : dfFalseEasting, dfFalseNorthing);
6792 : }
6793 :
6794 : /************************************************************************/
6795 : /* SetEckertIV() */
6796 : /* */
6797 : /* Deprecated */
6798 : /************************************************************************/
6799 :
6800 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6801 : double dfFalseEasting,
6802 : double dfFalseNorthing)
6803 :
6804 : {
6805 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6806 : }
6807 :
6808 : /************************************************************************/
6809 : /* OSRSetEckertIV() */
6810 : /************************************************************************/
6811 :
6812 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6813 : double dfFalseEasting, double dfFalseNorthing)
6814 :
6815 : {
6816 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6817 :
6818 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6819 0 : dfFalseNorthing);
6820 : }
6821 :
6822 : /************************************************************************/
6823 : /* SetEckertVI() */
6824 : /* */
6825 : /* Deprecated */
6826 : /************************************************************************/
6827 :
6828 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6829 : double dfFalseEasting,
6830 : double dfFalseNorthing)
6831 :
6832 : {
6833 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6834 : }
6835 :
6836 : /************************************************************************/
6837 : /* OSRSetEckertVI() */
6838 : /************************************************************************/
6839 :
6840 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6841 : double dfFalseEasting, double dfFalseNorthing)
6842 :
6843 : {
6844 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6845 :
6846 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6847 0 : dfFalseNorthing);
6848 : }
6849 :
6850 : /************************************************************************/
6851 : /* SetEquirectangular() */
6852 : /************************************************************************/
6853 :
6854 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6855 : double dfCenterLong,
6856 : double dfFalseEasting,
6857 : double dfFalseNorthing)
6858 :
6859 : {
6860 4 : TAKE_OPTIONAL_LOCK();
6861 :
6862 2 : if (dfCenterLat == 0.0)
6863 : {
6864 0 : return d->replaceConversionAndUnref(
6865 : proj_create_conversion_equidistant_cylindrical(
6866 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6867 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6868 : }
6869 :
6870 : // Non-standard extension with non-zero latitude of origin
6871 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6872 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6873 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6874 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6875 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6876 :
6877 2 : return OGRERR_NONE;
6878 : }
6879 :
6880 : /************************************************************************/
6881 : /* OSRSetEquirectangular() */
6882 : /************************************************************************/
6883 :
6884 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6885 : double dfCenterLong, double dfFalseEasting,
6886 : double dfFalseNorthing)
6887 :
6888 : {
6889 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6890 :
6891 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6892 0 : dfFalseEasting, dfFalseNorthing);
6893 : }
6894 :
6895 : /************************************************************************/
6896 : /* SetEquirectangular2() */
6897 : /* Generalized form */
6898 : /************************************************************************/
6899 :
6900 179 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6901 : double dfCenterLong,
6902 : double dfStdParallel1,
6903 : double dfFalseEasting,
6904 : double dfFalseNorthing)
6905 :
6906 : {
6907 358 : TAKE_OPTIONAL_LOCK();
6908 :
6909 179 : if (dfCenterLat == 0.0)
6910 : {
6911 174 : return d->replaceConversionAndUnref(
6912 : proj_create_conversion_equidistant_cylindrical(
6913 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6914 174 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6915 : }
6916 :
6917 : // Non-standard extension with non-zero latitude of origin
6918 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6919 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6920 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6921 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6922 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6923 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6924 :
6925 5 : return OGRERR_NONE;
6926 : }
6927 :
6928 : /************************************************************************/
6929 : /* OSRSetEquirectangular2() */
6930 : /************************************************************************/
6931 :
6932 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6933 : double dfCenterLong, double dfStdParallel1,
6934 : double dfFalseEasting, double dfFalseNorthing)
6935 :
6936 : {
6937 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6938 :
6939 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6940 : dfStdParallel1, dfFalseEasting,
6941 3 : dfFalseNorthing);
6942 : }
6943 :
6944 : /************************************************************************/
6945 : /* SetGS() */
6946 : /************************************************************************/
6947 :
6948 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6949 : double dfFalseEasting, double dfFalseNorthing)
6950 :
6951 : {
6952 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6953 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6954 5 : nullptr, 0.0, nullptr, 0.0));
6955 : }
6956 :
6957 : /************************************************************************/
6958 : /* OSRSetGS() */
6959 : /************************************************************************/
6960 :
6961 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6962 : double dfFalseEasting, double dfFalseNorthing)
6963 :
6964 : {
6965 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6966 :
6967 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6968 2 : dfFalseNorthing);
6969 : }
6970 :
6971 : /************************************************************************/
6972 : /* SetGH() */
6973 : /************************************************************************/
6974 :
6975 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6976 : double dfFalseEasting, double dfFalseNorthing)
6977 :
6978 : {
6979 0 : TAKE_OPTIONAL_LOCK();
6980 :
6981 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6982 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6983 0 : nullptr, 0.0, nullptr, 0.0));
6984 : }
6985 :
6986 : /************************************************************************/
6987 : /* OSRSetGH() */
6988 : /************************************************************************/
6989 :
6990 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6991 : double dfFalseEasting, double dfFalseNorthing)
6992 :
6993 : {
6994 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6995 :
6996 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6997 0 : dfFalseNorthing);
6998 : }
6999 :
7000 : /************************************************************************/
7001 : /* SetIGH() */
7002 : /************************************************************************/
7003 :
7004 0 : OGRErr OGRSpatialReference::SetIGH()
7005 :
7006 : {
7007 0 : TAKE_OPTIONAL_LOCK();
7008 :
7009 0 : return d->replaceConversionAndUnref(
7010 : proj_create_conversion_interrupted_goode_homolosine(
7011 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7012 : }
7013 :
7014 : /************************************************************************/
7015 : /* OSRSetIGH() */
7016 : /************************************************************************/
7017 :
7018 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7019 :
7020 : {
7021 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7022 :
7023 0 : return ToPointer(hSRS)->SetIGH();
7024 : }
7025 :
7026 : /************************************************************************/
7027 : /* SetGEOS() */
7028 : /************************************************************************/
7029 :
7030 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7031 : double dfSatelliteHeight,
7032 : double dfFalseEasting,
7033 : double dfFalseNorthing)
7034 :
7035 : {
7036 6 : TAKE_OPTIONAL_LOCK();
7037 :
7038 3 : return d->replaceConversionAndUnref(
7039 : proj_create_conversion_geostationary_satellite_sweep_y(
7040 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7041 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7042 : }
7043 :
7044 : /************************************************************************/
7045 : /* OSRSetGEOS() */
7046 : /************************************************************************/
7047 :
7048 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7049 : double dfSatelliteHeight, double dfFalseEasting,
7050 : double dfFalseNorthing)
7051 :
7052 : {
7053 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7054 :
7055 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7056 0 : dfFalseEasting, dfFalseNorthing);
7057 : }
7058 :
7059 : /************************************************************************/
7060 : /* SetGaussSchreiberTMercator() */
7061 : /************************************************************************/
7062 :
7063 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7064 : double dfCenterLong,
7065 : double dfScale,
7066 : double dfFalseEasting,
7067 : double dfFalseNorthing)
7068 :
7069 : {
7070 0 : TAKE_OPTIONAL_LOCK();
7071 :
7072 0 : return d->replaceConversionAndUnref(
7073 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7074 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7075 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7076 : }
7077 :
7078 : /************************************************************************/
7079 : /* OSRSetGaussSchreiberTMercator() */
7080 : /************************************************************************/
7081 :
7082 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7083 : double dfCenterLat, double dfCenterLong,
7084 : double dfScale, double dfFalseEasting,
7085 : double dfFalseNorthing)
7086 :
7087 : {
7088 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7089 :
7090 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7091 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7092 : }
7093 :
7094 : /************************************************************************/
7095 : /* SetGnomonic() */
7096 : /************************************************************************/
7097 :
7098 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7099 : double dfFalseEasting,
7100 : double dfFalseNorthing)
7101 :
7102 : {
7103 4 : TAKE_OPTIONAL_LOCK();
7104 :
7105 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7106 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7107 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7108 : }
7109 :
7110 : /************************************************************************/
7111 : /* OSRSetGnomonic() */
7112 : /************************************************************************/
7113 :
7114 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7115 : double dfCenterLong, double dfFalseEasting,
7116 : double dfFalseNorthing)
7117 :
7118 : {
7119 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7120 :
7121 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7122 0 : dfFalseEasting, dfFalseNorthing);
7123 : }
7124 :
7125 : /************************************************************************/
7126 : /* SetHOMAC() */
7127 : /************************************************************************/
7128 :
7129 : /**
7130 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7131 : * azimuth angle.
7132 : *
7133 : * This projection corresponds to EPSG projection method 9815, also
7134 : * sometimes known as hotine oblique mercator (variant B).
7135 : *
7136 : * This method does the same thing as the C function OSRSetHOMAC().
7137 : *
7138 : * @param dfCenterLat Latitude of the projection origin.
7139 : * @param dfCenterLong Longitude of the projection origin.
7140 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7141 : * centerline.
7142 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7143 : * @param dfScale Scale factor applies to the projection origin.
7144 : * @param dfFalseEasting False easting.
7145 : * @param dfFalseNorthing False northing.
7146 : *
7147 : * @return OGRERR_NONE on success.
7148 : */
7149 :
7150 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7151 : double dfAzimuth, double dfRectToSkew,
7152 : double dfScale, double dfFalseEasting,
7153 : double dfFalseNorthing)
7154 :
7155 : {
7156 8 : TAKE_OPTIONAL_LOCK();
7157 :
7158 4 : return d->replaceConversionAndUnref(
7159 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7160 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7161 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7162 8 : 0.0, nullptr, 0.0));
7163 : }
7164 :
7165 : /************************************************************************/
7166 : /* OSRSetHOMAC() */
7167 : /************************************************************************/
7168 :
7169 : /**
7170 : * \brief Set an Oblique Mercator projection using azimuth angle.
7171 : *
7172 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7173 : */
7174 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7175 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7176 : double dfScale, double dfFalseEasting,
7177 : double dfFalseNorthing)
7178 :
7179 : {
7180 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7181 :
7182 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7183 : dfRectToSkew, dfScale, dfFalseEasting,
7184 0 : dfFalseNorthing);
7185 : }
7186 :
7187 : /************************************************************************/
7188 : /* SetHOM() */
7189 : /************************************************************************/
7190 :
7191 : /**
7192 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7193 : *
7194 : * This projection corresponds to EPSG projection method 9812, also
7195 : * sometimes known as hotine oblique mercator (variant A)..
7196 : *
7197 : * This method does the same thing as the C function OSRSetHOM().
7198 : *
7199 : * @param dfCenterLat Latitude of the projection origin.
7200 : * @param dfCenterLong Longitude of the projection origin.
7201 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7202 : * centerline.
7203 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7204 : * @param dfScale Scale factor applies to the projection origin.
7205 : * @param dfFalseEasting False easting.
7206 : * @param dfFalseNorthing False northing.
7207 : *
7208 : * @return OGRERR_NONE on success.
7209 : */
7210 :
7211 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7212 : double dfAzimuth, double dfRectToSkew,
7213 : double dfScale, double dfFalseEasting,
7214 : double dfFalseNorthing)
7215 :
7216 : {
7217 26 : TAKE_OPTIONAL_LOCK();
7218 :
7219 13 : return d->replaceConversionAndUnref(
7220 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7221 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7222 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7223 26 : 0.0, nullptr, 0.0));
7224 : }
7225 :
7226 : /************************************************************************/
7227 : /* OSRSetHOM() */
7228 : /************************************************************************/
7229 : /**
7230 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7231 : *
7232 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7233 : */
7234 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7235 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7236 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7237 :
7238 : {
7239 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7240 :
7241 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7242 : dfRectToSkew, dfScale, dfFalseEasting,
7243 0 : dfFalseNorthing);
7244 : }
7245 :
7246 : /************************************************************************/
7247 : /* SetHOM2PNO() */
7248 : /************************************************************************/
7249 :
7250 : /**
7251 : * \brief Set a Hotine Oblique Mercator projection using two points on
7252 : * projection centerline.
7253 : *
7254 : * This method does the same thing as the C function OSRSetHOM2PNO().
7255 : *
7256 : * @param dfCenterLat Latitude of the projection origin.
7257 : * @param dfLat1 Latitude of the first point on center line.
7258 : * @param dfLong1 Longitude of the first point on center line.
7259 : * @param dfLat2 Latitude of the second point on center line.
7260 : * @param dfLong2 Longitude of the second point on center line.
7261 : * @param dfScale Scale factor applies to the projection origin.
7262 : * @param dfFalseEasting False easting.
7263 : * @param dfFalseNorthing False northing.
7264 : *
7265 : * @return OGRERR_NONE on success.
7266 : */
7267 :
7268 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7269 : double dfLong1, double dfLat2,
7270 : double dfLong2, double dfScale,
7271 : double dfFalseEasting,
7272 : double dfFalseNorthing)
7273 :
7274 : {
7275 6 : TAKE_OPTIONAL_LOCK();
7276 :
7277 3 : return d->replaceConversionAndUnref(
7278 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7279 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7280 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7281 6 : 0.0));
7282 : }
7283 :
7284 : /************************************************************************/
7285 : /* OSRSetHOM2PNO() */
7286 : /************************************************************************/
7287 : /**
7288 : * \brief Set a Hotine Oblique Mercator projection using two points on
7289 : * projection centerline.
7290 : *
7291 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7292 : */
7293 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7294 : double dfLat1, double dfLong1, double dfLat2,
7295 : double dfLong2, double dfScale, double dfFalseEasting,
7296 : double dfFalseNorthing)
7297 :
7298 : {
7299 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7300 :
7301 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7302 : dfLong2, dfScale, dfFalseEasting,
7303 0 : dfFalseNorthing);
7304 : }
7305 :
7306 : /************************************************************************/
7307 : /* SetLOM() */
7308 : /************************************************************************/
7309 :
7310 : /**
7311 : * \brief Set a Laborde Oblique Mercator projection.
7312 : *
7313 : * @param dfCenterLat Latitude of the projection origin.
7314 : * @param dfCenterLong Longitude of the projection origin.
7315 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7316 : * centerline.
7317 : * @param dfScale Scale factor on the initiali line
7318 : * @param dfFalseEasting False easting.
7319 : * @param dfFalseNorthing False northing.
7320 : *
7321 : * @return OGRERR_NONE on success.
7322 : */
7323 :
7324 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7325 : double dfAzimuth, double dfScale,
7326 : double dfFalseEasting,
7327 : double dfFalseNorthing)
7328 :
7329 : {
7330 0 : TAKE_OPTIONAL_LOCK();
7331 :
7332 0 : return d->replaceConversionAndUnref(
7333 : proj_create_conversion_laborde_oblique_mercator(
7334 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7335 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7336 : }
7337 :
7338 : /************************************************************************/
7339 : /* SetIWMPolyconic() */
7340 : /************************************************************************/
7341 :
7342 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7343 : double dfCenterLong,
7344 : double dfFalseEasting,
7345 : double dfFalseNorthing)
7346 :
7347 : {
7348 0 : TAKE_OPTIONAL_LOCK();
7349 :
7350 0 : return d->replaceConversionAndUnref(
7351 : proj_create_conversion_international_map_world_polyconic(
7352 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7353 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7354 : }
7355 :
7356 : /************************************************************************/
7357 : /* OSRSetIWMPolyconic() */
7358 : /************************************************************************/
7359 :
7360 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7361 : double dfLat2, double dfCenterLong,
7362 : double dfFalseEasting, double dfFalseNorthing)
7363 :
7364 : {
7365 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7366 :
7367 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7368 0 : dfFalseEasting, dfFalseNorthing);
7369 : }
7370 :
7371 : /************************************************************************/
7372 : /* SetKrovak() */
7373 : /************************************************************************/
7374 :
7375 : /** Krovak east-north projection.
7376 : *
7377 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7378 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7379 : */
7380 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7381 : double dfAzimuth,
7382 : double dfPseudoStdParallel1,
7383 : double dfScale, double dfFalseEasting,
7384 : double dfFalseNorthing)
7385 :
7386 : {
7387 6 : TAKE_OPTIONAL_LOCK();
7388 :
7389 3 : return d->replaceConversionAndUnref(
7390 : proj_create_conversion_krovak_north_oriented(
7391 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7392 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7393 6 : nullptr, 0.0, nullptr, 0.0));
7394 : }
7395 :
7396 : /************************************************************************/
7397 : /* OSRSetKrovak() */
7398 : /************************************************************************/
7399 :
7400 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7401 : double dfCenterLong, double dfAzimuth,
7402 : double dfPseudoStdParallel1, double dfScale,
7403 : double dfFalseEasting, double dfFalseNorthing)
7404 :
7405 : {
7406 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7407 :
7408 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7409 : dfPseudoStdParallel1, dfScale,
7410 0 : dfFalseEasting, dfFalseNorthing);
7411 : }
7412 :
7413 : /************************************************************************/
7414 : /* SetLAEA() */
7415 : /************************************************************************/
7416 :
7417 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7418 : double dfFalseEasting,
7419 : double dfFalseNorthing)
7420 :
7421 : {
7422 34 : TAKE_OPTIONAL_LOCK();
7423 :
7424 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7425 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7426 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7427 :
7428 17 : const char *pszName = nullptr;
7429 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7430 17 : CPLString osName = pszName ? pszName : "";
7431 :
7432 17 : d->refreshProjObj();
7433 :
7434 17 : d->demoteFromBoundCRS();
7435 :
7436 17 : auto cs = proj_create_cartesian_2D_cs(
7437 : d->getPROJContext(),
7438 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7439 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7440 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7441 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7442 : : PJ_CART2D_EASTING_NORTHING,
7443 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7444 : auto projCRS =
7445 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7446 17 : d->getGeodBaseCRS(), conv, cs);
7447 17 : proj_destroy(conv);
7448 17 : proj_destroy(cs);
7449 :
7450 17 : d->setPjCRS(projCRS);
7451 :
7452 17 : d->undoDemoteFromBoundCRS();
7453 :
7454 34 : return OGRERR_NONE;
7455 : }
7456 :
7457 : /************************************************************************/
7458 : /* OSRSetLAEA() */
7459 : /************************************************************************/
7460 :
7461 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7462 : double dfCenterLong, double dfFalseEasting,
7463 : double dfFalseNorthing)
7464 :
7465 : {
7466 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7467 :
7468 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7469 0 : dfFalseNorthing);
7470 : }
7471 :
7472 : /************************************************************************/
7473 : /* SetLCC() */
7474 : /************************************************************************/
7475 :
7476 148 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7477 : double dfCenterLat, double dfCenterLong,
7478 : double dfFalseEasting,
7479 : double dfFalseNorthing)
7480 :
7481 : {
7482 296 : TAKE_OPTIONAL_LOCK();
7483 :
7484 148 : return d->replaceConversionAndUnref(
7485 : proj_create_conversion_lambert_conic_conformal_2sp(
7486 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7487 296 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7488 : }
7489 :
7490 : /************************************************************************/
7491 : /* OSRSetLCC() */
7492 : /************************************************************************/
7493 :
7494 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7495 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7496 : double dfFalseNorthing)
7497 :
7498 : {
7499 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7500 :
7501 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7502 1 : dfFalseEasting, dfFalseNorthing);
7503 : }
7504 :
7505 : /************************************************************************/
7506 : /* SetLCC1SP() */
7507 : /************************************************************************/
7508 :
7509 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7510 : double dfScale, double dfFalseEasting,
7511 : double dfFalseNorthing)
7512 :
7513 : {
7514 20 : TAKE_OPTIONAL_LOCK();
7515 :
7516 10 : return d->replaceConversionAndUnref(
7517 : proj_create_conversion_lambert_conic_conformal_1sp(
7518 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7519 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7520 : }
7521 :
7522 : /************************************************************************/
7523 : /* OSRSetLCC1SP() */
7524 : /************************************************************************/
7525 :
7526 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7527 : double dfCenterLong, double dfScale, double dfFalseEasting,
7528 : double dfFalseNorthing)
7529 :
7530 : {
7531 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7532 :
7533 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7534 0 : dfFalseEasting, dfFalseNorthing);
7535 : }
7536 :
7537 : /************************************************************************/
7538 : /* SetLCCB() */
7539 : /************************************************************************/
7540 :
7541 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7542 : double dfCenterLat, double dfCenterLong,
7543 : double dfFalseEasting,
7544 : double dfFalseNorthing)
7545 :
7546 : {
7547 4 : TAKE_OPTIONAL_LOCK();
7548 :
7549 2 : return d->replaceConversionAndUnref(
7550 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7551 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7552 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7553 : }
7554 :
7555 : /************************************************************************/
7556 : /* OSRSetLCCB() */
7557 : /************************************************************************/
7558 :
7559 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7560 : double dfCenterLat, double dfCenterLong,
7561 : double dfFalseEasting, double dfFalseNorthing)
7562 :
7563 : {
7564 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7565 :
7566 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7567 0 : dfFalseEasting, dfFalseNorthing);
7568 : }
7569 :
7570 : /************************************************************************/
7571 : /* SetMC() */
7572 : /************************************************************************/
7573 :
7574 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7575 : double dfFalseEasting, double dfFalseNorthing)
7576 :
7577 : {
7578 8 : TAKE_OPTIONAL_LOCK();
7579 :
7580 : (void)dfCenterLat; // ignored
7581 :
7582 4 : return d->replaceConversionAndUnref(
7583 : proj_create_conversion_miller_cylindrical(
7584 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7585 8 : nullptr, 0, nullptr, 0));
7586 : }
7587 :
7588 : /************************************************************************/
7589 : /* OSRSetMC() */
7590 : /************************************************************************/
7591 :
7592 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7593 : double dfCenterLong, double dfFalseEasting,
7594 : double dfFalseNorthing)
7595 :
7596 : {
7597 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7598 :
7599 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7600 0 : dfFalseNorthing);
7601 : }
7602 :
7603 : /************************************************************************/
7604 : /* SetMercator() */
7605 : /************************************************************************/
7606 :
7607 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7608 : double dfScale, double dfFalseEasting,
7609 : double dfFalseNorthing)
7610 :
7611 : {
7612 118 : TAKE_OPTIONAL_LOCK();
7613 :
7614 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7615 : {
7616 : // Not sure this is correct, but this is how it has been used
7617 : // historically
7618 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7619 0 : dfFalseNorthing);
7620 : }
7621 59 : return d->replaceConversionAndUnref(
7622 : proj_create_conversion_mercator_variant_a(
7623 : d->getPROJContext(),
7624 : dfCenterLat, // should be zero
7625 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7626 59 : nullptr, 0));
7627 : }
7628 :
7629 : /************************************************************************/
7630 : /* OSRSetMercator() */
7631 : /************************************************************************/
7632 :
7633 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7634 : double dfCenterLong, double dfScale,
7635 : double dfFalseEasting, double dfFalseNorthing)
7636 :
7637 : {
7638 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7639 :
7640 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7641 2 : dfFalseEasting, dfFalseNorthing);
7642 : }
7643 :
7644 : /************************************************************************/
7645 : /* SetMercator2SP() */
7646 : /************************************************************************/
7647 :
7648 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7649 : double dfCenterLong,
7650 : double dfFalseEasting,
7651 : double dfFalseNorthing)
7652 :
7653 : {
7654 31 : if (dfCenterLat == 0.0)
7655 : {
7656 30 : return d->replaceConversionAndUnref(
7657 : proj_create_conversion_mercator_variant_b(
7658 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7659 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7660 : }
7661 :
7662 1 : TAKE_OPTIONAL_LOCK();
7663 :
7664 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7665 :
7666 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7667 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7668 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7669 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7670 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7671 :
7672 1 : return OGRERR_NONE;
7673 : }
7674 :
7675 : /************************************************************************/
7676 : /* OSRSetMercator2SP() */
7677 : /************************************************************************/
7678 :
7679 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7680 : double dfCenterLat, double dfCenterLong,
7681 : double dfFalseEasting, double dfFalseNorthing)
7682 :
7683 : {
7684 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7685 :
7686 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7687 1 : dfFalseEasting, dfFalseNorthing);
7688 : }
7689 :
7690 : /************************************************************************/
7691 : /* SetMollweide() */
7692 : /************************************************************************/
7693 :
7694 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7695 : double dfFalseEasting,
7696 : double dfFalseNorthing)
7697 :
7698 : {
7699 6 : TAKE_OPTIONAL_LOCK();
7700 :
7701 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7702 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7703 6 : nullptr, 0, nullptr, 0));
7704 : }
7705 :
7706 : /************************************************************************/
7707 : /* OSRSetMollweide() */
7708 : /************************************************************************/
7709 :
7710 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7711 : double dfFalseEasting, double dfFalseNorthing)
7712 :
7713 : {
7714 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7715 :
7716 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7717 0 : dfFalseNorthing);
7718 : }
7719 :
7720 : /************************************************************************/
7721 : /* SetNZMG() */
7722 : /************************************************************************/
7723 :
7724 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7725 : double dfFalseEasting,
7726 : double dfFalseNorthing)
7727 :
7728 : {
7729 14 : TAKE_OPTIONAL_LOCK();
7730 :
7731 7 : return d->replaceConversionAndUnref(
7732 : proj_create_conversion_new_zealand_mapping_grid(
7733 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7734 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7735 : }
7736 :
7737 : /************************************************************************/
7738 : /* OSRSetNZMG() */
7739 : /************************************************************************/
7740 :
7741 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7742 : double dfCenterLong, double dfFalseEasting,
7743 : double dfFalseNorthing)
7744 :
7745 : {
7746 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7747 :
7748 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7749 0 : dfFalseNorthing);
7750 : }
7751 :
7752 : /************************************************************************/
7753 : /* SetOS() */
7754 : /************************************************************************/
7755 :
7756 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7757 : double dfScale, double dfFalseEasting,
7758 : double dfFalseNorthing)
7759 :
7760 : {
7761 12 : TAKE_OPTIONAL_LOCK();
7762 :
7763 6 : return d->replaceConversionAndUnref(
7764 : proj_create_conversion_oblique_stereographic(
7765 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7766 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7767 : }
7768 :
7769 : /************************************************************************/
7770 : /* OSRSetOS() */
7771 : /************************************************************************/
7772 :
7773 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7774 : double dfCMeridian, double dfScale, double dfFalseEasting,
7775 : double dfFalseNorthing)
7776 :
7777 : {
7778 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7779 :
7780 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7781 0 : dfFalseEasting, dfFalseNorthing);
7782 : }
7783 :
7784 : /************************************************************************/
7785 : /* SetOrthographic() */
7786 : /************************************************************************/
7787 :
7788 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7789 : double dfCenterLong,
7790 : double dfFalseEasting,
7791 : double dfFalseNorthing)
7792 :
7793 : {
7794 14 : TAKE_OPTIONAL_LOCK();
7795 :
7796 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7797 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7798 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7799 : }
7800 :
7801 : /************************************************************************/
7802 : /* OSRSetOrthographic() */
7803 : /************************************************************************/
7804 :
7805 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7806 : double dfCenterLong, double dfFalseEasting,
7807 : double dfFalseNorthing)
7808 :
7809 : {
7810 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7811 :
7812 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7813 1 : dfFalseEasting, dfFalseNorthing);
7814 : }
7815 :
7816 : /************************************************************************/
7817 : /* SetPolyconic() */
7818 : /************************************************************************/
7819 :
7820 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7821 : double dfCenterLong,
7822 : double dfFalseEasting,
7823 : double dfFalseNorthing)
7824 :
7825 : {
7826 14 : TAKE_OPTIONAL_LOCK();
7827 :
7828 : // note: it seems that by some definitions this should include a
7829 : // scale_factor parameter.
7830 7 : return d->replaceConversionAndUnref(
7831 : proj_create_conversion_american_polyconic(
7832 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7833 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7834 : }
7835 :
7836 : /************************************************************************/
7837 : /* OSRSetPolyconic() */
7838 : /************************************************************************/
7839 :
7840 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7841 : double dfCenterLong, double dfFalseEasting,
7842 : double dfFalseNorthing)
7843 :
7844 : {
7845 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7846 :
7847 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7848 0 : dfFalseEasting, dfFalseNorthing);
7849 : }
7850 :
7851 : /************************************************************************/
7852 : /* SetPS() */
7853 : /************************************************************************/
7854 :
7855 : /** Sets a Polar Stereographic projection.
7856 : *
7857 : * Two variants are possible:
7858 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7859 : * interpreted as the latitude of origin, combined with the scale factor
7860 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7861 : * is interpreted as the latitude of true scale. In that situation, dfScale
7862 : * must be set to 1 (it is ignored in the projection parameters)
7863 : */
7864 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7865 : double dfScale, double dfFalseEasting,
7866 : double dfFalseNorthing)
7867 :
7868 : {
7869 60 : TAKE_OPTIONAL_LOCK();
7870 :
7871 : PJ *conv;
7872 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7873 : {
7874 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7875 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7876 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7877 : }
7878 : else
7879 : {
7880 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7881 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7882 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7883 : }
7884 :
7885 30 : const char *pszName = nullptr;
7886 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7887 30 : CPLString osName = pszName ? pszName : "";
7888 :
7889 30 : d->refreshProjObj();
7890 :
7891 30 : d->demoteFromBoundCRS();
7892 :
7893 30 : auto cs = proj_create_cartesian_2D_cs(
7894 : d->getPROJContext(),
7895 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7896 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7897 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7898 : auto projCRS =
7899 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7900 30 : d->getGeodBaseCRS(), conv, cs);
7901 30 : proj_destroy(conv);
7902 30 : proj_destroy(cs);
7903 :
7904 30 : d->setPjCRS(projCRS);
7905 :
7906 30 : d->undoDemoteFromBoundCRS();
7907 :
7908 60 : return OGRERR_NONE;
7909 : }
7910 :
7911 : /************************************************************************/
7912 : /* OSRSetPS() */
7913 : /************************************************************************/
7914 :
7915 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7916 : double dfCenterLong, double dfScale, double dfFalseEasting,
7917 : double dfFalseNorthing)
7918 :
7919 : {
7920 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7921 :
7922 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7923 1 : dfFalseEasting, dfFalseNorthing);
7924 : }
7925 :
7926 : /************************************************************************/
7927 : /* SetRobinson() */
7928 : /************************************************************************/
7929 :
7930 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7931 : double dfFalseEasting,
7932 : double dfFalseNorthing)
7933 :
7934 : {
7935 8 : TAKE_OPTIONAL_LOCK();
7936 :
7937 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7938 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7939 8 : nullptr, 0, nullptr, 0));
7940 : }
7941 :
7942 : /************************************************************************/
7943 : /* OSRSetRobinson() */
7944 : /************************************************************************/
7945 :
7946 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7947 : double dfFalseEasting, double dfFalseNorthing)
7948 :
7949 : {
7950 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7951 :
7952 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7953 0 : dfFalseNorthing);
7954 : }
7955 :
7956 : /************************************************************************/
7957 : /* SetSinusoidal() */
7958 : /************************************************************************/
7959 :
7960 35 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7961 : double dfFalseEasting,
7962 : double dfFalseNorthing)
7963 :
7964 : {
7965 70 : TAKE_OPTIONAL_LOCK();
7966 :
7967 35 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7968 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7969 70 : nullptr, 0, nullptr, 0));
7970 : }
7971 :
7972 : /************************************************************************/
7973 : /* OSRSetSinusoidal() */
7974 : /************************************************************************/
7975 :
7976 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7977 : double dfFalseEasting, double dfFalseNorthing)
7978 :
7979 : {
7980 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7981 :
7982 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7983 1 : dfFalseNorthing);
7984 : }
7985 :
7986 : /************************************************************************/
7987 : /* SetStereographic() */
7988 : /************************************************************************/
7989 :
7990 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7991 : double dfCMeridian, double dfScale,
7992 : double dfFalseEasting,
7993 : double dfFalseNorthing)
7994 :
7995 : {
7996 4 : TAKE_OPTIONAL_LOCK();
7997 :
7998 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7999 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
8000 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8001 : }
8002 :
8003 : /************************************************************************/
8004 : /* OSRSetStereographic() */
8005 : /************************************************************************/
8006 :
8007 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8008 : double dfCMeridian, double dfScale,
8009 : double dfFalseEasting, double dfFalseNorthing)
8010 :
8011 : {
8012 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8013 :
8014 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8015 0 : dfFalseEasting, dfFalseNorthing);
8016 : }
8017 :
8018 : /************************************************************************/
8019 : /* SetSOC() */
8020 : /* */
8021 : /* NOTE: This definition isn't really used in practice any more */
8022 : /* and should be considered deprecated. It seems that swiss */
8023 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
8024 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
8025 : /* EPSG:2056 and Bug 423. */
8026 : /************************************************************************/
8027 :
8028 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8029 : double dfCentralMeridian,
8030 : double dfFalseEasting,
8031 : double dfFalseNorthing)
8032 :
8033 : {
8034 4 : TAKE_OPTIONAL_LOCK();
8035 :
8036 2 : return d->replaceConversionAndUnref(
8037 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8038 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8039 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8040 4 : 0.0));
8041 : #if 0
8042 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8043 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8044 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8045 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8046 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8047 :
8048 : return OGRERR_NONE;
8049 : #endif
8050 : }
8051 :
8052 : /************************************************************************/
8053 : /* OSRSetSOC() */
8054 : /************************************************************************/
8055 :
8056 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8057 : double dfCentralMeridian, double dfFalseEasting,
8058 : double dfFalseNorthing)
8059 :
8060 : {
8061 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8062 :
8063 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8064 0 : dfFalseEasting, dfFalseNorthing);
8065 : }
8066 :
8067 : /************************************************************************/
8068 : /* SetVDG() */
8069 : /************************************************************************/
8070 :
8071 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8072 : double dfFalseNorthing)
8073 :
8074 : {
8075 4 : TAKE_OPTIONAL_LOCK();
8076 :
8077 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8078 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8079 4 : nullptr, 0, nullptr, 0));
8080 : }
8081 :
8082 : /************************************************************************/
8083 : /* OSRSetVDG() */
8084 : /************************************************************************/
8085 :
8086 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8087 : double dfFalseEasting, double dfFalseNorthing)
8088 :
8089 : {
8090 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8091 :
8092 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8093 0 : dfFalseNorthing);
8094 : }
8095 :
8096 : /************************************************************************/
8097 : /* SetUTM() */
8098 : /************************************************************************/
8099 :
8100 : /**
8101 : * \brief Set UTM projection definition.
8102 : *
8103 : * This will generate a projection definition with the full set of
8104 : * transverse mercator projection parameters for the given UTM zone.
8105 : * If no PROJCS[] description is set yet, one will be set to look
8106 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8107 : *
8108 : * This method is the same as the C function OSRSetUTM().
8109 : *
8110 : * @param nZone UTM zone.
8111 : *
8112 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8113 : * hemisphere.
8114 : *
8115 : * @return OGRERR_NONE on success.
8116 : */
8117 :
8118 313 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8119 :
8120 : {
8121 626 : TAKE_OPTIONAL_LOCK();
8122 :
8123 313 : if (nZone < 0 || nZone > 60)
8124 : {
8125 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8126 0 : return OGRERR_FAILURE;
8127 : }
8128 :
8129 313 : return d->replaceConversionAndUnref(
8130 313 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8131 : }
8132 :
8133 : /************************************************************************/
8134 : /* OSRSetUTM() */
8135 : /************************************************************************/
8136 :
8137 : /**
8138 : * \brief Set UTM projection definition.
8139 : *
8140 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8141 : */
8142 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8143 :
8144 : {
8145 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8146 :
8147 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8148 : }
8149 :
8150 : /************************************************************************/
8151 : /* GetUTMZone() */
8152 : /* */
8153 : /* Returns zero if it isn't UTM. */
8154 : /************************************************************************/
8155 :
8156 : /**
8157 : * \brief Get utm zone information.
8158 : *
8159 : * This is the same as the C function OSRGetUTMZone().
8160 : *
8161 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8162 : * zone which is negative in the southern hemisphere instead of having the
8163 : * pbNorth flag used in the C and C++ interface.
8164 : *
8165 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8166 : * FALSE if southern.
8167 : *
8168 : * @return UTM zone number or zero if this isn't a UTM definition.
8169 : */
8170 :
8171 606 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8172 :
8173 : {
8174 1212 : TAKE_OPTIONAL_LOCK();
8175 :
8176 606 : if (IsProjected() && GetAxesCount() == 3)
8177 : {
8178 1 : OGRSpatialReference *poSRSTmp = Clone();
8179 1 : poSRSTmp->DemoteTo2D(nullptr);
8180 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8181 1 : delete poSRSTmp;
8182 1 : return nZone;
8183 : }
8184 :
8185 605 : const char *pszProjection = GetAttrValue("PROJECTION");
8186 :
8187 605 : if (pszProjection == nullptr ||
8188 525 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8189 274 : return 0;
8190 :
8191 331 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8192 5 : return 0;
8193 :
8194 326 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8195 15 : return 0;
8196 :
8197 311 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8198 6 : return 0;
8199 :
8200 305 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8201 :
8202 305 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8203 0 : return 0;
8204 :
8205 305 : if (pbNorth != nullptr)
8206 239 : *pbNorth = (dfFalseNorthing == 0);
8207 :
8208 : const double dfCentralMeridian =
8209 305 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8210 305 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8211 :
8212 610 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8213 915 : std::isnan(dfZone) ||
8214 305 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8215 0 : return 0;
8216 :
8217 305 : return static_cast<int>(dfZone);
8218 : }
8219 :
8220 : /************************************************************************/
8221 : /* OSRGetUTMZone() */
8222 : /************************************************************************/
8223 :
8224 : /**
8225 : * \brief Get utm zone information.
8226 : *
8227 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8228 : */
8229 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8230 :
8231 : {
8232 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8233 :
8234 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8235 : }
8236 :
8237 : /************************************************************************/
8238 : /* SetWagner() */
8239 : /************************************************************************/
8240 :
8241 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8242 : double dfCenterLat, double dfFalseEasting,
8243 : double dfFalseNorthing)
8244 :
8245 : {
8246 0 : TAKE_OPTIONAL_LOCK();
8247 :
8248 : PJ *conv;
8249 0 : if (nVariation == 1)
8250 : {
8251 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8252 : dfFalseEasting, dfFalseNorthing,
8253 : nullptr, 0.0, nullptr, 0.0);
8254 : }
8255 0 : else if (nVariation == 2)
8256 : {
8257 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8258 : dfFalseEasting, dfFalseNorthing,
8259 : nullptr, 0.0, nullptr, 0.0);
8260 : }
8261 0 : else if (nVariation == 3)
8262 : {
8263 0 : conv = proj_create_conversion_wagner_iii(
8264 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8265 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8266 : }
8267 0 : else if (nVariation == 4)
8268 : {
8269 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8270 : dfFalseEasting, dfFalseNorthing,
8271 : nullptr, 0.0, nullptr, 0.0);
8272 : }
8273 0 : else if (nVariation == 5)
8274 : {
8275 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8276 : dfFalseEasting, dfFalseNorthing,
8277 : nullptr, 0.0, nullptr, 0.0);
8278 : }
8279 0 : else if (nVariation == 6)
8280 : {
8281 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8282 : dfFalseEasting, dfFalseNorthing,
8283 : nullptr, 0.0, nullptr, 0.0);
8284 : }
8285 0 : else if (nVariation == 7)
8286 : {
8287 0 : conv = proj_create_conversion_wagner_vii(
8288 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8289 : 0.0, nullptr, 0.0);
8290 : }
8291 : else
8292 : {
8293 0 : CPLError(CE_Failure, CPLE_AppDefined,
8294 : "Unsupported Wagner variation (%d).", nVariation);
8295 0 : return OGRERR_UNSUPPORTED_SRS;
8296 : }
8297 :
8298 0 : return d->replaceConversionAndUnref(conv);
8299 : }
8300 :
8301 : /************************************************************************/
8302 : /* OSRSetWagner() */
8303 : /************************************************************************/
8304 :
8305 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8306 : double dfCenterLat, double dfFalseEasting,
8307 : double dfFalseNorthing)
8308 :
8309 : {
8310 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8311 :
8312 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8313 0 : dfFalseNorthing);
8314 : }
8315 :
8316 : /************************************************************************/
8317 : /* SetQSC() */
8318 : /************************************************************************/
8319 :
8320 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8321 : {
8322 0 : TAKE_OPTIONAL_LOCK();
8323 :
8324 0 : return d->replaceConversionAndUnref(
8325 : proj_create_conversion_quadrilateralized_spherical_cube(
8326 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8327 0 : 0, nullptr, 0));
8328 : }
8329 :
8330 : /************************************************************************/
8331 : /* OSRSetQSC() */
8332 : /************************************************************************/
8333 :
8334 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8335 : double dfCenterLong)
8336 :
8337 : {
8338 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8339 :
8340 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8341 : }
8342 :
8343 : /************************************************************************/
8344 : /* SetSCH() */
8345 : /************************************************************************/
8346 :
8347 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8348 : double dfPegHeading, double dfPegHgt)
8349 :
8350 : {
8351 0 : TAKE_OPTIONAL_LOCK();
8352 :
8353 0 : return d->replaceConversionAndUnref(
8354 : proj_create_conversion_spherical_cross_track_height(
8355 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8356 0 : nullptr, 0, nullptr, 0));
8357 : }
8358 :
8359 : /************************************************************************/
8360 : /* OSRSetSCH() */
8361 : /************************************************************************/
8362 :
8363 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8364 : double dfPegHeading, double dfPegHgt)
8365 :
8366 : {
8367 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8368 :
8369 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8370 : }
8371 :
8372 : /************************************************************************/
8373 : /* SetVerticalPerspective() */
8374 : /************************************************************************/
8375 :
8376 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8377 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8378 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8379 : {
8380 6 : TAKE_OPTIONAL_LOCK();
8381 :
8382 3 : return d->replaceConversionAndUnref(
8383 : proj_create_conversion_vertical_perspective(
8384 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8385 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8386 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8387 : }
8388 :
8389 : /************************************************************************/
8390 : /* OSRSetVerticalPerspective() */
8391 : /************************************************************************/
8392 :
8393 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8394 : double dfTopoOriginLat, double dfTopoOriginLon,
8395 : double dfTopoOriginHeight,
8396 : double dfViewPointHeight,
8397 : double dfFalseEasting, double dfFalseNorthing)
8398 :
8399 : {
8400 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8401 :
8402 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8403 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8404 1 : dfFalseEasting, dfFalseNorthing);
8405 : }
8406 :
8407 : /************************************************************************/
8408 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8409 : /************************************************************************/
8410 :
8411 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8412 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8413 : double dfAxisRotation)
8414 : {
8415 4 : TAKE_OPTIONAL_LOCK();
8416 :
8417 2 : d->refreshProjObj();
8418 2 : if (!d->m_pj_crs)
8419 0 : return OGRERR_FAILURE;
8420 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8421 0 : return OGRERR_FAILURE;
8422 2 : auto ctxt = d->getPROJContext();
8423 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8424 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8425 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8426 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8427 2 : d->m_pj_crs, conv, cs));
8428 2 : proj_destroy(conv);
8429 2 : proj_destroy(cs);
8430 2 : return OGRERR_NONE;
8431 : }
8432 :
8433 : /************************************************************************/
8434 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8435 : /************************************************************************/
8436 :
8437 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8438 : const char *pszCRSName, double dfGridNorthPoleLat,
8439 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8440 : {
8441 3 : TAKE_OPTIONAL_LOCK();
8442 :
8443 : #if PROJ_VERSION_MAJOR > 8 || \
8444 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8445 : d->refreshProjObj();
8446 : if (!d->m_pj_crs)
8447 : return OGRERR_FAILURE;
8448 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8449 : return OGRERR_FAILURE;
8450 : auto ctxt = d->getPROJContext();
8451 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8452 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8453 : nullptr, 0);
8454 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8455 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8456 : d->m_pj_crs, conv, cs));
8457 : proj_destroy(conv);
8458 : proj_destroy(cs);
8459 : return OGRERR_NONE;
8460 : #else
8461 : (void)pszCRSName;
8462 3 : SetProjection("Rotated_pole");
8463 3 : SetExtension(
8464 : "PROJCS", "PROJ4",
8465 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8466 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8467 : "+to_meter=0.0174532925199433 "
8468 : "+wktext",
8469 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8470 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8471 : GetSemiMinor(nullptr)));
8472 6 : return OGRERR_NONE;
8473 : #endif
8474 : }
8475 :
8476 : /************************************************************************/
8477 : /* SetAuthority() */
8478 : /************************************************************************/
8479 :
8480 : /**
8481 : * \brief Set the authority for a node.
8482 : *
8483 : * This method is the same as the C function OSRSetAuthority().
8484 : *
8485 : * @param pszTargetKey the partial or complete path to the node to
8486 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8487 : *
8488 : * @param pszAuthority authority name, such as "EPSG".
8489 : *
8490 : * @param nCode code for value with this authority.
8491 : *
8492 : * @return OGRERR_NONE on success.
8493 : */
8494 :
8495 11835 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8496 : const char *pszAuthority, int nCode)
8497 :
8498 : {
8499 23670 : TAKE_OPTIONAL_LOCK();
8500 :
8501 11835 : d->refreshProjObj();
8502 11835 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8503 :
8504 11835 : if (pszTargetKey == nullptr)
8505 : {
8506 263 : if (!d->m_pj_crs)
8507 0 : return OGRERR_FAILURE;
8508 263 : CPLString osCode;
8509 263 : osCode.Printf("%d", nCode);
8510 263 : d->demoteFromBoundCRS();
8511 263 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8512 : pszAuthority, osCode.c_str()));
8513 263 : d->undoDemoteFromBoundCRS();
8514 263 : return OGRERR_NONE;
8515 : }
8516 :
8517 11572 : d->demoteFromBoundCRS();
8518 11572 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8519 : {
8520 3825 : CPLString osCode;
8521 3825 : osCode.Printf("%d", nCode);
8522 : auto newGeogCRS =
8523 3825 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8524 : pszAuthority, osCode.c_str());
8525 :
8526 : auto conv =
8527 3825 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8528 :
8529 3825 : auto projCRS = proj_create_projected_crs(
8530 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8531 3825 : d->getProjCRSCoordSys());
8532 :
8533 : // Preserve existing id on the PROJCRS
8534 3825 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8535 3825 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8536 3825 : if (pszProjCRSAuthName && pszProjCRSCode)
8537 : {
8538 : auto projCRSWithId =
8539 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8540 : pszProjCRSCode);
8541 0 : proj_destroy(projCRS);
8542 0 : projCRS = projCRSWithId;
8543 : }
8544 :
8545 3825 : proj_destroy(newGeogCRS);
8546 3825 : proj_destroy(conv);
8547 :
8548 3825 : d->setPjCRS(projCRS);
8549 3825 : d->undoDemoteFromBoundCRS();
8550 3825 : return OGRERR_NONE;
8551 : }
8552 7747 : d->undoDemoteFromBoundCRS();
8553 :
8554 : /* -------------------------------------------------------------------- */
8555 : /* Find the node below which the authority should be put. */
8556 : /* -------------------------------------------------------------------- */
8557 7747 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8558 :
8559 7747 : if (poNode == nullptr)
8560 0 : return OGRERR_FAILURE;
8561 :
8562 : /* -------------------------------------------------------------------- */
8563 : /* If there is an existing AUTHORITY child blow it away before */
8564 : /* trying to set a new one. */
8565 : /* -------------------------------------------------------------------- */
8566 7747 : int iOldChild = poNode->FindChild("AUTHORITY");
8567 7747 : if (iOldChild != -1)
8568 5 : poNode->DestroyChild(iOldChild);
8569 :
8570 : /* -------------------------------------------------------------------- */
8571 : /* Create a new authority node. */
8572 : /* -------------------------------------------------------------------- */
8573 7747 : char szCode[32] = {};
8574 :
8575 7747 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8576 :
8577 7747 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8578 7747 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8579 7747 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8580 :
8581 7747 : poNode->AddChild(poAuthNode);
8582 :
8583 7747 : return OGRERR_NONE;
8584 : }
8585 :
8586 : /************************************************************************/
8587 : /* OSRSetAuthority() */
8588 : /************************************************************************/
8589 :
8590 : /**
8591 : * \brief Set the authority for a node.
8592 : *
8593 : * This function is the same as OGRSpatialReference::SetAuthority().
8594 : */
8595 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8596 : const char *pszAuthority, int nCode)
8597 :
8598 : {
8599 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8600 :
8601 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8602 : }
8603 :
8604 : /************************************************************************/
8605 : /* GetAuthorityCode() */
8606 : /************************************************************************/
8607 :
8608 : /**
8609 : * \brief Get the authority code for a node.
8610 : *
8611 : * This method is used to query an AUTHORITY[] node from within the
8612 : * WKT tree, and fetch the code value.
8613 : *
8614 : * While in theory values may be non-numeric, for the EPSG authority all
8615 : * code values should be integral.
8616 : *
8617 : * This method is the same as the C function OSRGetAuthorityCode().
8618 : *
8619 : * @param pszTargetKey the partial or complete path to the node to
8620 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8621 : * search for an authority node on the root element.
8622 : *
8623 : * @return value code from authority node, or NULL on failure. The value
8624 : * returned is internal and should not be freed or modified.
8625 : */
8626 :
8627 : const char *
8628 45202 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8629 :
8630 : {
8631 90404 : TAKE_OPTIONAL_LOCK();
8632 :
8633 45202 : d->refreshProjObj();
8634 45202 : const char *pszInputTargetKey = pszTargetKey;
8635 45202 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8636 45202 : if (pszTargetKey == nullptr)
8637 : {
8638 37342 : if (!d->m_pj_crs)
8639 : {
8640 17 : return nullptr;
8641 : }
8642 37325 : d->demoteFromBoundCRS();
8643 37325 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8644 37325 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8645 : {
8646 1196 : auto ctxt = d->getPROJContext();
8647 1196 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8648 1196 : if (cs)
8649 : {
8650 1196 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8651 1196 : proj_destroy(cs);
8652 1196 : if (axisCount == 3)
8653 : {
8654 : // This might come from a COMPD_CS with a VERT_DATUM type =
8655 : // 2002 in which case, using the WKT1 representation will
8656 : // enable us to recover the EPSG code.
8657 14 : pszTargetKey = pszInputTargetKey;
8658 : }
8659 : }
8660 : }
8661 37325 : d->undoDemoteFromBoundCRS();
8662 37325 : if (ret != nullptr || pszTargetKey == nullptr)
8663 : {
8664 37325 : return ret;
8665 : }
8666 : }
8667 :
8668 : // Special key for that context
8669 7864 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8670 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8671 : {
8672 4 : auto ctxt = d->getPROJContext();
8673 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8674 4 : if (crs)
8675 : {
8676 4 : const char *ret = proj_get_id_code(crs, 0);
8677 4 : if (ret)
8678 4 : ret = CPLSPrintf("%s", ret);
8679 4 : proj_destroy(crs);
8680 4 : return ret;
8681 : }
8682 : }
8683 7860 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8684 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8685 : {
8686 4 : auto ctxt = d->getPROJContext();
8687 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8688 4 : if (crs)
8689 : {
8690 4 : const char *ret = proj_get_id_code(crs, 0);
8691 4 : if (ret)
8692 4 : ret = CPLSPrintf("%s", ret);
8693 4 : proj_destroy(crs);
8694 4 : return ret;
8695 : }
8696 : }
8697 :
8698 : /* -------------------------------------------------------------------- */
8699 : /* Find the node below which the authority should be put. */
8700 : /* -------------------------------------------------------------------- */
8701 7852 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8702 :
8703 7852 : if (poNode == nullptr)
8704 103 : return nullptr;
8705 :
8706 : /* -------------------------------------------------------------------- */
8707 : /* Fetch AUTHORITY child if there is one. */
8708 : /* -------------------------------------------------------------------- */
8709 7749 : if (poNode->FindChild("AUTHORITY") == -1)
8710 183 : return nullptr;
8711 :
8712 7566 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8713 :
8714 : /* -------------------------------------------------------------------- */
8715 : /* Create a new authority node. */
8716 : /* -------------------------------------------------------------------- */
8717 7566 : if (poNode->GetChildCount() < 2)
8718 0 : return nullptr;
8719 :
8720 7566 : return poNode->GetChild(1)->GetValue();
8721 : }
8722 :
8723 : /************************************************************************/
8724 : /* OSRGetAuthorityCode() */
8725 : /************************************************************************/
8726 :
8727 : /**
8728 : * \brief Get the authority code for a node.
8729 : *
8730 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8731 : */
8732 715 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8733 : const char *pszTargetKey)
8734 :
8735 : {
8736 715 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8737 :
8738 715 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8739 : }
8740 :
8741 : /************************************************************************/
8742 : /* GetAuthorityName() */
8743 : /************************************************************************/
8744 :
8745 : /**
8746 : * \brief Get the authority name for a node.
8747 : *
8748 : * This method is used to query an AUTHORITY[] node from within the
8749 : * WKT tree, and fetch the authority name value.
8750 : *
8751 : * The most common authority is "EPSG".
8752 : *
8753 : * This method is the same as the C function OSRGetAuthorityName().
8754 : *
8755 : * @param pszTargetKey the partial or complete path to the node to
8756 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8757 : * search for an authority node on the root element.
8758 : *
8759 : * @return value code from authority node, or NULL on failure. The value
8760 : * returned is internal and should not be freed or modified.
8761 : */
8762 :
8763 : const char *
8764 58381 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8765 :
8766 : {
8767 116762 : TAKE_OPTIONAL_LOCK();
8768 :
8769 58381 : d->refreshProjObj();
8770 58381 : const char *pszInputTargetKey = pszTargetKey;
8771 58381 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8772 58381 : if (pszTargetKey == nullptr)
8773 : {
8774 30955 : if (!d->m_pj_crs)
8775 : {
8776 18 : return nullptr;
8777 : }
8778 30937 : d->demoteFromBoundCRS();
8779 30937 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8780 30937 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8781 : {
8782 819 : auto ctxt = d->getPROJContext();
8783 819 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8784 819 : if (cs)
8785 : {
8786 819 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8787 819 : proj_destroy(cs);
8788 819 : if (axisCount == 3)
8789 : {
8790 : // This might come from a COMPD_CS with a VERT_DATUM type =
8791 : // 2002 in which case, using the WKT1 representation will
8792 : // enable us to recover the EPSG code.
8793 14 : pszTargetKey = pszInputTargetKey;
8794 : }
8795 : }
8796 : }
8797 30937 : d->undoDemoteFromBoundCRS();
8798 30937 : if (ret != nullptr || pszTargetKey == nullptr)
8799 : {
8800 30937 : return ret;
8801 : }
8802 : }
8803 :
8804 : // Special key for that context
8805 27430 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8806 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8807 : {
8808 4 : auto ctxt = d->getPROJContext();
8809 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8810 4 : if (crs)
8811 : {
8812 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8813 4 : if (ret)
8814 4 : ret = CPLSPrintf("%s", ret);
8815 4 : proj_destroy(crs);
8816 4 : return ret;
8817 : }
8818 : }
8819 27426 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8820 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8821 : {
8822 4 : auto ctxt = d->getPROJContext();
8823 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8824 4 : if (crs)
8825 : {
8826 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8827 4 : if (ret)
8828 4 : ret = CPLSPrintf("%s", ret);
8829 4 : proj_destroy(crs);
8830 4 : return ret;
8831 : }
8832 : }
8833 :
8834 : /* -------------------------------------------------------------------- */
8835 : /* Find the node below which the authority should be put. */
8836 : /* -------------------------------------------------------------------- */
8837 27418 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8838 :
8839 27418 : if (poNode == nullptr)
8840 11403 : return nullptr;
8841 :
8842 : /* -------------------------------------------------------------------- */
8843 : /* Fetch AUTHORITY child if there is one. */
8844 : /* -------------------------------------------------------------------- */
8845 16015 : if (poNode->FindChild("AUTHORITY") == -1)
8846 1448 : return nullptr;
8847 :
8848 14567 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8849 :
8850 : /* -------------------------------------------------------------------- */
8851 : /* Create a new authority node. */
8852 : /* -------------------------------------------------------------------- */
8853 14567 : if (poNode->GetChildCount() < 2)
8854 0 : return nullptr;
8855 :
8856 14567 : return poNode->GetChild(0)->GetValue();
8857 : }
8858 :
8859 : /************************************************************************/
8860 : /* OSRGetAuthorityName() */
8861 : /************************************************************************/
8862 :
8863 : /**
8864 : * \brief Get the authority name for a node.
8865 : *
8866 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8867 : */
8868 181 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8869 : const char *pszTargetKey)
8870 :
8871 : {
8872 181 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8873 :
8874 181 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8875 : }
8876 :
8877 : /************************************************************************/
8878 : /* GetOGCURN() */
8879 : /************************************************************************/
8880 :
8881 : /**
8882 : * \brief Get a OGC URN string describing the CRS, when possible
8883 : *
8884 : * This method assumes that the CRS has a top-level identifier, or is
8885 : * a compound CRS whose horizontal and vertical parts have a top-level
8886 : * identifier.
8887 : *
8888 : * @return a string to free with CPLFree(), or nullptr when no result can be
8889 : * generated
8890 : *
8891 : * @since GDAL 3.5
8892 : */
8893 :
8894 61 : char *OGRSpatialReference::GetOGCURN() const
8895 :
8896 : {
8897 122 : TAKE_OPTIONAL_LOCK();
8898 :
8899 61 : const char *pszAuthName = GetAuthorityName(nullptr);
8900 61 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8901 61 : if (pszAuthName && pszAuthCode)
8902 58 : return CPLStrdup(
8903 58 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8904 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8905 2 : return nullptr;
8906 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8907 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8908 1 : char *pszRet = nullptr;
8909 1 : if (horizCRS && vertCRS)
8910 : {
8911 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8912 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8913 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8914 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8915 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8916 : {
8917 1 : pszRet = CPLStrdup(CPLSPrintf(
8918 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8919 : horizAuthCode, vertAuthName, vertAuthCode));
8920 : }
8921 : }
8922 1 : proj_destroy(horizCRS);
8923 1 : proj_destroy(vertCRS);
8924 1 : return pszRet;
8925 : }
8926 :
8927 : /************************************************************************/
8928 : /* StripVertical() */
8929 : /************************************************************************/
8930 :
8931 : /**
8932 : * \brief Convert a compound cs into a horizontal CS.
8933 : *
8934 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8935 : * nodes are stripped resulting and only the horizontal coordinate system
8936 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8937 : *
8938 : * If this is not a compound coordinate system then nothing is changed.
8939 : *
8940 : * This method is the same as the C function OSRStripVertical().
8941 : *
8942 : */
8943 :
8944 44 : OGRErr OGRSpatialReference::StripVertical()
8945 :
8946 : {
8947 88 : TAKE_OPTIONAL_LOCK();
8948 :
8949 44 : d->refreshProjObj();
8950 44 : d->demoteFromBoundCRS();
8951 44 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8952 : {
8953 0 : d->undoDemoteFromBoundCRS();
8954 0 : return OGRERR_NONE;
8955 : }
8956 44 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8957 44 : if (!horizCRS)
8958 : {
8959 0 : d->undoDemoteFromBoundCRS();
8960 0 : return OGRERR_FAILURE;
8961 : }
8962 :
8963 44 : bool reuseExistingBoundCRS = false;
8964 44 : if (d->m_pj_bound_crs_target)
8965 : {
8966 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8967 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8968 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8969 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8970 : }
8971 :
8972 44 : if (reuseExistingBoundCRS)
8973 : {
8974 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8975 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8976 4 : d->m_pj_bound_crs_co);
8977 4 : proj_destroy(horizCRS);
8978 4 : d->undoDemoteFromBoundCRS();
8979 4 : d->setPjCRS(newBoundCRS);
8980 : }
8981 : else
8982 : {
8983 40 : d->undoDemoteFromBoundCRS();
8984 40 : d->setPjCRS(horizCRS);
8985 : }
8986 :
8987 44 : return OGRERR_NONE;
8988 : }
8989 :
8990 : /************************************************************************/
8991 : /* OSRStripVertical() */
8992 : /************************************************************************/
8993 : /**
8994 : * \brief Convert a compound cs into a horizontal CS.
8995 : *
8996 : * This function is the same as the C++ method
8997 : * OGRSpatialReference::StripVertical().
8998 : */
8999 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
9000 :
9001 : {
9002 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9003 :
9004 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9005 : }
9006 :
9007 : /************************************************************************/
9008 : /* StripTOWGS84IfKnownDatumAndAllowed() */
9009 : /************************************************************************/
9010 :
9011 : /**
9012 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9013 : * and this is allowed by the user.
9014 : *
9015 : * The default behavior is to remove TOWGS84 information if the CRS has a
9016 : * known horizontal datum. This can be disabled by setting the
9017 : * OSR_STRIP_TOWGS84 configuration option to NO.
9018 : *
9019 : * @return true if TOWGS84 has been removed.
9020 : * @since OGR 3.1.0
9021 : */
9022 :
9023 9010 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9024 : {
9025 9010 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9026 : {
9027 9007 : if (StripTOWGS84IfKnownDatum())
9028 : {
9029 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9030 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9031 : "configuration option to NO");
9032 72 : return true;
9033 : }
9034 : }
9035 8937 : return false;
9036 : }
9037 :
9038 : /************************************************************************/
9039 : /* StripTOWGS84IfKnownDatum() */
9040 : /************************************************************************/
9041 :
9042 : /**
9043 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9044 : *
9045 : * @return true if TOWGS84 has been removed.
9046 : * @since OGR 3.1.0
9047 : */
9048 :
9049 9013 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9050 :
9051 : {
9052 18025 : TAKE_OPTIONAL_LOCK();
9053 :
9054 9013 : d->refreshProjObj();
9055 9013 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9056 : {
9057 8920 : return false;
9058 : }
9059 92 : auto ctxt = d->getPROJContext();
9060 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9061 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9062 : {
9063 3 : proj_destroy(baseCRS);
9064 3 : return false;
9065 : }
9066 :
9067 : // Known base CRS code ? Return base CRS
9068 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9069 89 : if (pszCode)
9070 : {
9071 2 : d->setPjCRS(baseCRS);
9072 2 : return true;
9073 : }
9074 :
9075 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9076 : #if PROJ_VERSION_MAJOR > 7 || \
9077 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9078 : if (datum == nullptr)
9079 : {
9080 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9081 : }
9082 : #endif
9083 87 : if (!datum)
9084 : {
9085 0 : proj_destroy(baseCRS);
9086 0 : return false;
9087 : }
9088 :
9089 : // Known datum code ? Return base CRS
9090 87 : pszCode = proj_get_id_code(datum, 0);
9091 87 : if (pszCode)
9092 : {
9093 3 : proj_destroy(datum);
9094 3 : d->setPjCRS(baseCRS);
9095 3 : return true;
9096 : }
9097 :
9098 84 : const char *name = proj_get_name(datum);
9099 84 : if (EQUAL(name, "unknown"))
9100 : {
9101 1 : proj_destroy(datum);
9102 1 : proj_destroy(baseCRS);
9103 1 : return false;
9104 : }
9105 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9106 : PJ_OBJ_LIST *list =
9107 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9108 :
9109 83 : bool knownDatumName = false;
9110 83 : if (list)
9111 : {
9112 83 : if (proj_list_get_count(list) == 1)
9113 : {
9114 70 : knownDatumName = true;
9115 : }
9116 83 : proj_list_destroy(list);
9117 : }
9118 :
9119 83 : proj_destroy(datum);
9120 83 : if (knownDatumName)
9121 : {
9122 70 : d->setPjCRS(baseCRS);
9123 70 : return true;
9124 : }
9125 13 : proj_destroy(baseCRS);
9126 13 : return false;
9127 : }
9128 :
9129 : /************************************************************************/
9130 : /* IsCompound() */
9131 : /************************************************************************/
9132 :
9133 : /**
9134 : * \brief Check if coordinate system is compound.
9135 : *
9136 : * This method is the same as the C function OSRIsCompound().
9137 : *
9138 : * @return TRUE if this is rooted with a COMPD_CS node.
9139 : */
9140 :
9141 40030 : int OGRSpatialReference::IsCompound() const
9142 :
9143 : {
9144 40030 : TAKE_OPTIONAL_LOCK();
9145 :
9146 40029 : d->refreshProjObj();
9147 40030 : d->demoteFromBoundCRS();
9148 40029 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9149 40030 : d->undoDemoteFromBoundCRS();
9150 80058 : return isCompound;
9151 : }
9152 :
9153 : /************************************************************************/
9154 : /* OSRIsCompound() */
9155 : /************************************************************************/
9156 :
9157 : /**
9158 : * \brief Check if the coordinate system is compound.
9159 : *
9160 : * This function is the same as OGRSpatialReference::IsCompound().
9161 : */
9162 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9163 :
9164 : {
9165 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9166 :
9167 5 : return ToPointer(hSRS)->IsCompound();
9168 : }
9169 :
9170 : /************************************************************************/
9171 : /* IsProjected() */
9172 : /************************************************************************/
9173 :
9174 : /**
9175 : * \brief Check if projected coordinate system.
9176 : *
9177 : * This method is the same as the C function OSRIsProjected().
9178 : *
9179 : * @return TRUE if this contains a PROJCS node indicating a it is a
9180 : * projected coordinate system. Also if it is a CompoundCRS made of a
9181 : * ProjectedCRS
9182 : */
9183 :
9184 41610 : int OGRSpatialReference::IsProjected() const
9185 :
9186 : {
9187 41610 : TAKE_OPTIONAL_LOCK();
9188 :
9189 41610 : d->refreshProjObj();
9190 41610 : d->demoteFromBoundCRS();
9191 41610 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9192 41610 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9193 : {
9194 : auto horizCRS =
9195 142 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9196 142 : if (horizCRS)
9197 : {
9198 142 : auto horizCRSType = proj_get_type(horizCRS);
9199 142 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9200 142 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9201 : {
9202 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9203 6 : if (base)
9204 : {
9205 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9206 6 : proj_destroy(base);
9207 : }
9208 : }
9209 142 : proj_destroy(horizCRS);
9210 : }
9211 : }
9212 41610 : d->undoDemoteFromBoundCRS();
9213 83220 : return isProjected;
9214 : }
9215 :
9216 : /************************************************************************/
9217 : /* OSRIsProjected() */
9218 : /************************************************************************/
9219 : /**
9220 : * \brief Check if projected coordinate system.
9221 : *
9222 : * This function is the same as OGRSpatialReference::IsProjected().
9223 : */
9224 413 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9225 :
9226 : {
9227 413 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9228 :
9229 413 : return ToPointer(hSRS)->IsProjected();
9230 : }
9231 :
9232 : /************************************************************************/
9233 : /* IsGeocentric() */
9234 : /************************************************************************/
9235 :
9236 : /**
9237 : * \brief Check if geocentric coordinate system.
9238 : *
9239 : * This method is the same as the C function OSRIsGeocentric().
9240 : *
9241 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9242 : * geocentric coordinate system.
9243 : *
9244 : */
9245 :
9246 16934 : int OGRSpatialReference::IsGeocentric() const
9247 :
9248 : {
9249 16934 : TAKE_OPTIONAL_LOCK();
9250 :
9251 16934 : d->refreshProjObj();
9252 16934 : d->demoteFromBoundCRS();
9253 16934 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9254 16934 : d->undoDemoteFromBoundCRS();
9255 33868 : return isGeocentric;
9256 : }
9257 :
9258 : /************************************************************************/
9259 : /* OSRIsGeocentric() */
9260 : /************************************************************************/
9261 : /**
9262 : * \brief Check if geocentric coordinate system.
9263 : *
9264 : * This function is the same as OGRSpatialReference::IsGeocentric().
9265 : *
9266 : */
9267 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9268 :
9269 : {
9270 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9271 :
9272 2 : return ToPointer(hSRS)->IsGeocentric();
9273 : }
9274 :
9275 : /************************************************************************/
9276 : /* IsEmpty() */
9277 : /************************************************************************/
9278 :
9279 : /**
9280 : * \brief Return if the SRS is not set.
9281 : */
9282 :
9283 112933 : bool OGRSpatialReference::IsEmpty() const
9284 : {
9285 112933 : TAKE_OPTIONAL_LOCK();
9286 :
9287 112933 : d->refreshProjObj();
9288 225867 : return d->m_pj_crs == nullptr;
9289 : }
9290 :
9291 : /************************************************************************/
9292 : /* IsGeographic() */
9293 : /************************************************************************/
9294 :
9295 : /**
9296 : * \brief Check if geographic coordinate system.
9297 : *
9298 : * This method is the same as the C function OSRIsGeographic().
9299 : *
9300 : * @return TRUE if this spatial reference is geographic ... that is the
9301 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9302 : * GeographicCRS
9303 : */
9304 :
9305 57600 : int OGRSpatialReference::IsGeographic() const
9306 :
9307 : {
9308 57600 : TAKE_OPTIONAL_LOCK();
9309 :
9310 57600 : d->refreshProjObj();
9311 57600 : d->demoteFromBoundCRS();
9312 80934 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9313 23334 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9314 57600 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9315 : {
9316 : auto horizCRS =
9317 291 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9318 291 : if (horizCRS)
9319 : {
9320 291 : auto horizCRSType = proj_get_type(horizCRS);
9321 291 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9322 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9323 291 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9324 : {
9325 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9326 13 : if (base)
9327 : {
9328 13 : horizCRSType = proj_get_type(base);
9329 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9330 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9331 13 : proj_destroy(base);
9332 : }
9333 : }
9334 291 : proj_destroy(horizCRS);
9335 : }
9336 : }
9337 57600 : d->undoDemoteFromBoundCRS();
9338 115200 : return isGeog;
9339 : }
9340 :
9341 : /************************************************************************/
9342 : /* OSRIsGeographic() */
9343 : /************************************************************************/
9344 : /**
9345 : * \brief Check if geographic coordinate system.
9346 : *
9347 : * This function is the same as OGRSpatialReference::IsGeographic().
9348 : */
9349 304 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9350 :
9351 : {
9352 304 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9353 :
9354 304 : return ToPointer(hSRS)->IsGeographic();
9355 : }
9356 :
9357 : /************************************************************************/
9358 : /* IsDerivedGeographic() */
9359 : /************************************************************************/
9360 :
9361 : /**
9362 : * \brief Check if the CRS is a derived geographic coordinate system.
9363 : * (for example a rotated long/lat grid)
9364 : *
9365 : * This method is the same as the C function OSRIsDerivedGeographic().
9366 : *
9367 : * @since GDAL 3.1.0 and PROJ 6.3.0
9368 : */
9369 :
9370 14804 : int OGRSpatialReference::IsDerivedGeographic() const
9371 :
9372 : {
9373 14804 : TAKE_OPTIONAL_LOCK();
9374 :
9375 14804 : d->refreshProjObj();
9376 14804 : d->demoteFromBoundCRS();
9377 24162 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9378 9358 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9379 : const bool isDerivedGeographic =
9380 14804 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9381 14804 : d->undoDemoteFromBoundCRS();
9382 29608 : return isDerivedGeographic ? TRUE : FALSE;
9383 : }
9384 :
9385 : /************************************************************************/
9386 : /* OSRIsDerivedGeographic() */
9387 : /************************************************************************/
9388 : /**
9389 : * \brief Check if the CRS is a derived geographic coordinate system.
9390 : * (for example a rotated long/lat grid)
9391 : *
9392 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9393 : */
9394 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9395 :
9396 : {
9397 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9398 :
9399 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9400 : }
9401 :
9402 : /************************************************************************/
9403 : /* IsDerivedProjected() */
9404 : /************************************************************************/
9405 :
9406 : /**
9407 : * \brief Check if the CRS is a derived projected coordinate system.
9408 : *
9409 : * This method is the same as the C function OSRIsDerivedGeographic().
9410 : *
9411 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9412 : */
9413 :
9414 0 : int OGRSpatialReference::IsDerivedProjected() const
9415 :
9416 : {
9417 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9418 : TAKE_OPTIONAL_LOCK();
9419 : d->refreshProjObj();
9420 : d->demoteFromBoundCRS();
9421 : const bool isDerivedProjected =
9422 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9423 : d->undoDemoteFromBoundCRS();
9424 : return isDerivedProjected ? TRUE : FALSE;
9425 : #else
9426 0 : return FALSE;
9427 : #endif
9428 : }
9429 :
9430 : /************************************************************************/
9431 : /* OSRIsDerivedProjected() */
9432 : /************************************************************************/
9433 : /**
9434 : * \brief Check if the CRS is a derived projected coordinate system.
9435 : *
9436 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9437 : *
9438 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9439 : */
9440 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9441 :
9442 : {
9443 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9444 :
9445 0 : return ToPointer(hSRS)->IsDerivedProjected();
9446 : }
9447 :
9448 : /************************************************************************/
9449 : /* IsLocal() */
9450 : /************************************************************************/
9451 :
9452 : /**
9453 : * \brief Check if local coordinate system.
9454 : *
9455 : * This method is the same as the C function OSRIsLocal().
9456 : *
9457 : * @return TRUE if this spatial reference is local ... that is the
9458 : * root is a LOCAL_CS node.
9459 : */
9460 :
9461 7643 : int OGRSpatialReference::IsLocal() const
9462 :
9463 : {
9464 7643 : TAKE_OPTIONAL_LOCK();
9465 7643 : d->refreshProjObj();
9466 15286 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9467 : }
9468 :
9469 : /************************************************************************/
9470 : /* OSRIsLocal() */
9471 : /************************************************************************/
9472 : /**
9473 : * \brief Check if local coordinate system.
9474 : *
9475 : * This function is the same as OGRSpatialReference::IsLocal().
9476 : */
9477 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9478 :
9479 : {
9480 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9481 :
9482 8 : return ToPointer(hSRS)->IsLocal();
9483 : }
9484 :
9485 : /************************************************************************/
9486 : /* IsVertical() */
9487 : /************************************************************************/
9488 :
9489 : /**
9490 : * \brief Check if vertical coordinate system.
9491 : *
9492 : * This method is the same as the C function OSRIsVertical().
9493 : *
9494 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9495 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9496 : * VerticalCRS
9497 : *
9498 : */
9499 :
9500 8548 : int OGRSpatialReference::IsVertical() const
9501 :
9502 : {
9503 8548 : TAKE_OPTIONAL_LOCK();
9504 8548 : d->refreshProjObj();
9505 8548 : d->demoteFromBoundCRS();
9506 8548 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9507 8548 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9508 : {
9509 : auto vertCRS =
9510 32 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9511 32 : if (vertCRS)
9512 : {
9513 32 : const auto vertCRSType = proj_get_type(vertCRS);
9514 32 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9515 32 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9516 : {
9517 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9518 0 : if (base)
9519 : {
9520 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9521 0 : proj_destroy(base);
9522 : }
9523 : }
9524 32 : proj_destroy(vertCRS);
9525 : }
9526 : }
9527 8548 : d->undoDemoteFromBoundCRS();
9528 17096 : return isVertical;
9529 : }
9530 :
9531 : /************************************************************************/
9532 : /* OSRIsVertical() */
9533 : /************************************************************************/
9534 : /**
9535 : * \brief Check if vertical coordinate system.
9536 : *
9537 : * This function is the same as OGRSpatialReference::IsVertical().
9538 : *
9539 : */
9540 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9541 :
9542 : {
9543 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9544 :
9545 0 : return ToPointer(hSRS)->IsVertical();
9546 : }
9547 :
9548 : /************************************************************************/
9549 : /* IsDynamic() */
9550 : /************************************************************************/
9551 :
9552 : /**
9553 : * \brief Check if a CRS is a dynamic CRS.
9554 : *
9555 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9556 : * plate-fixed.
9557 : *
9558 : * This method is the same as the C function OSRIsDynamic().
9559 : *
9560 : * @return true if the CRS is dynamic
9561 : *
9562 : * @since OGR 3.4.0
9563 : *
9564 : * @see HasPointMotionOperation()
9565 : */
9566 :
9567 14678 : bool OGRSpatialReference::IsDynamic() const
9568 :
9569 : {
9570 14678 : TAKE_OPTIONAL_LOCK();
9571 14678 : bool isDynamic = false;
9572 14678 : d->refreshProjObj();
9573 14678 : d->demoteFromBoundCRS();
9574 14678 : auto ctxt = d->getPROJContext();
9575 14678 : PJ *horiz = nullptr;
9576 14678 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9577 : {
9578 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9579 : }
9580 14582 : else if (d->m_pj_crs)
9581 : {
9582 14475 : horiz = proj_clone(ctxt, d->m_pj_crs);
9583 : }
9584 14678 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9585 : {
9586 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9587 6 : if (baseCRS)
9588 : {
9589 6 : proj_destroy(horiz);
9590 6 : horiz = baseCRS;
9591 : }
9592 : }
9593 14678 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9594 14678 : if (datum)
9595 : {
9596 14536 : const auto type = proj_get_type(datum);
9597 14536 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9598 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9599 14536 : if (!isDynamic)
9600 : {
9601 14536 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9602 14536 : const char *code = proj_get_id_code(datum, 0);
9603 14536 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9604 14078 : EQUAL(code, "6326"))
9605 : {
9606 9129 : isDynamic = true;
9607 : }
9608 : }
9609 14536 : proj_destroy(datum);
9610 : }
9611 : #if PROJ_VERSION_MAJOR > 7 || \
9612 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9613 : else
9614 : {
9615 : auto ensemble =
9616 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9617 : if (ensemble)
9618 : {
9619 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9620 : if (member)
9621 : {
9622 : const auto type = proj_get_type(member);
9623 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9624 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9625 : proj_destroy(member);
9626 : }
9627 : proj_destroy(ensemble);
9628 : }
9629 : }
9630 : #endif
9631 14678 : proj_destroy(horiz);
9632 14678 : d->undoDemoteFromBoundCRS();
9633 29356 : return isDynamic;
9634 : }
9635 :
9636 : /************************************************************************/
9637 : /* OSRIsDynamic() */
9638 : /************************************************************************/
9639 : /**
9640 : * \brief Check if a CRS is a dynamic CRS.
9641 : *
9642 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9643 : * plate-fixed.
9644 : *
9645 : * This function is the same as OGRSpatialReference::IsDynamic().
9646 : *
9647 : * @since OGR 3.4.0
9648 : */
9649 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9650 :
9651 : {
9652 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9653 :
9654 0 : return ToPointer(hSRS)->IsDynamic();
9655 : }
9656 :
9657 : /************************************************************************/
9658 : /* HasPointMotionOperation() */
9659 : /************************************************************************/
9660 :
9661 : /**
9662 : * \brief Check if a CRS has at least an associated point motion operation.
9663 : *
9664 : * Some CRS are not formally declared as dynamic, but may behave as such
9665 : * in practice due to the presence of point motion operation, to perform
9666 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9667 : *
9668 : * @return true if the CRS has at least an associated point motion operation.
9669 : *
9670 : * @since OGR 3.8.0 and PROJ 9.4.0
9671 : *
9672 : * @see IsDynamic()
9673 : */
9674 :
9675 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9676 :
9677 : {
9678 : #if PROJ_VERSION_MAJOR > 9 || \
9679 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9680 : TAKE_OPTIONAL_LOCK();
9681 : d->refreshProjObj();
9682 : d->demoteFromBoundCRS();
9683 : auto ctxt = d->getPROJContext();
9684 : auto res =
9685 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9686 : d->undoDemoteFromBoundCRS();
9687 : return res;
9688 : #else
9689 5 : return false;
9690 : #endif
9691 : }
9692 :
9693 : /************************************************************************/
9694 : /* OSRHasPointMotionOperation() */
9695 : /************************************************************************/
9696 :
9697 : /**
9698 : * \brief Check if a CRS has at least an associated point motion operation.
9699 : *
9700 : * Some CRS are not formally declared as dynamic, but may behave as such
9701 : * in practice due to the presence of point motion operation, to perform
9702 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9703 : *
9704 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9705 : *
9706 : * @since OGR 3.8.0 and PROJ 9.4.0
9707 : */
9708 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9709 :
9710 : {
9711 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9712 :
9713 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9714 : }
9715 :
9716 : /************************************************************************/
9717 : /* CloneGeogCS() */
9718 : /************************************************************************/
9719 :
9720 : /**
9721 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9722 : * object.
9723 : *
9724 : * @return a new SRS, which becomes the responsibility of the caller.
9725 : */
9726 4121 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9727 :
9728 : {
9729 8242 : TAKE_OPTIONAL_LOCK();
9730 4121 : d->refreshProjObj();
9731 4121 : if (d->m_pj_crs)
9732 : {
9733 4121 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9734 0 : return nullptr;
9735 :
9736 : auto geodCRS =
9737 4121 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9738 4121 : if (geodCRS)
9739 : {
9740 4121 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9741 4121 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9742 : {
9743 : PJ *hub_crs =
9744 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9745 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9746 13 : d->m_pj_crs);
9747 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9748 : geodCRS, hub_crs, co);
9749 13 : proj_destroy(geodCRS);
9750 13 : geodCRS = temp;
9751 13 : proj_destroy(hub_crs);
9752 13 : proj_destroy(co);
9753 : }
9754 :
9755 : /* --------------------------------------------------------------------
9756 : */
9757 : /* We have to reconstruct the GEOGCS node for geocentric */
9758 : /* coordinate systems. */
9759 : /* --------------------------------------------------------------------
9760 : */
9761 4121 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9762 : {
9763 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9764 : #if PROJ_VERSION_MAJOR > 7 || \
9765 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9766 : if (datum == nullptr)
9767 : {
9768 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9769 : geodCRS);
9770 : }
9771 : #endif
9772 0 : if (datum)
9773 : {
9774 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9775 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9776 : nullptr, 0);
9777 0 : auto temp = proj_create_geographic_crs_from_datum(
9778 : d->getPROJContext(), "unnamed", datum, cs);
9779 0 : proj_destroy(datum);
9780 0 : proj_destroy(cs);
9781 0 : proj_destroy(geodCRS);
9782 0 : geodCRS = temp;
9783 : }
9784 : }
9785 :
9786 4121 : poNewSRS->d->setPjCRS(geodCRS);
9787 4121 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9788 2689 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9789 4121 : return poNewSRS;
9790 : }
9791 : }
9792 0 : return nullptr;
9793 : }
9794 :
9795 : /************************************************************************/
9796 : /* OSRCloneGeogCS() */
9797 : /************************************************************************/
9798 : /**
9799 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9800 : * object.
9801 : *
9802 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9803 : */
9804 124 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9805 :
9806 : {
9807 124 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9808 :
9809 124 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9810 : }
9811 :
9812 : /************************************************************************/
9813 : /* IsSameGeogCS() */
9814 : /************************************************************************/
9815 :
9816 : /**
9817 : * \brief Do the GeogCS'es match?
9818 : *
9819 : * This method is the same as the C function OSRIsSameGeogCS().
9820 : *
9821 : * @param poOther the SRS being compared against.
9822 : *
9823 : * @return TRUE if they are the same or FALSE otherwise.
9824 : */
9825 :
9826 8306 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9827 :
9828 : {
9829 8306 : return IsSameGeogCS(poOther, nullptr);
9830 : }
9831 :
9832 : /**
9833 : * \brief Do the GeogCS'es match?
9834 : *
9835 : * This method is the same as the C function OSRIsSameGeogCS().
9836 : *
9837 : * @param poOther the SRS being compared against.
9838 : * @param papszOptions options. ignored
9839 : *
9840 : * @return TRUE if they are the same or FALSE otherwise.
9841 : */
9842 :
9843 8306 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9844 : const char *const *papszOptions) const
9845 :
9846 : {
9847 16612 : TAKE_OPTIONAL_LOCK();
9848 :
9849 8306 : CPL_IGNORE_RET_VAL(papszOptions);
9850 :
9851 8306 : d->refreshProjObj();
9852 8306 : poOther->d->refreshProjObj();
9853 8306 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9854 0 : return FALSE;
9855 8306 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9856 8306 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9857 24918 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9858 8306 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9859 : {
9860 0 : return FALSE;
9861 : }
9862 :
9863 8306 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9864 : auto otherGeodCRS =
9865 8306 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9866 8306 : if (!geodCRS || !otherGeodCRS)
9867 : {
9868 0 : proj_destroy(geodCRS);
9869 0 : proj_destroy(otherGeodCRS);
9870 0 : return FALSE;
9871 : }
9872 :
9873 8306 : int ret = proj_is_equivalent_to(
9874 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9875 :
9876 8306 : proj_destroy(geodCRS);
9877 8306 : proj_destroy(otherGeodCRS);
9878 8306 : return ret;
9879 : }
9880 :
9881 : /************************************************************************/
9882 : /* OSRIsSameGeogCS() */
9883 : /************************************************************************/
9884 :
9885 : /**
9886 : * \brief Do the GeogCS'es match?
9887 : *
9888 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9889 : */
9890 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9891 :
9892 : {
9893 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9894 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9895 :
9896 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9897 : }
9898 :
9899 : /************************************************************************/
9900 : /* IsSameVertCS() */
9901 : /************************************************************************/
9902 :
9903 : /**
9904 : * \brief Do the VertCS'es match?
9905 : *
9906 : * This method is the same as the C function OSRIsSameVertCS().
9907 : *
9908 : * @param poOther the SRS being compared against.
9909 : *
9910 : * @return TRUE if they are the same or FALSE otherwise.
9911 : */
9912 :
9913 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9914 :
9915 : {
9916 0 : TAKE_OPTIONAL_LOCK();
9917 :
9918 : /* -------------------------------------------------------------------- */
9919 : /* Does the datum name match? */
9920 : /* -------------------------------------------------------------------- */
9921 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9922 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9923 :
9924 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9925 0 : !EQUAL(pszThisValue, pszOtherValue))
9926 0 : return FALSE;
9927 :
9928 : /* -------------------------------------------------------------------- */
9929 : /* Do the units match? */
9930 : /* -------------------------------------------------------------------- */
9931 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9932 0 : if (pszThisValue == nullptr)
9933 0 : pszThisValue = "1.0";
9934 :
9935 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9936 0 : if (pszOtherValue == nullptr)
9937 0 : pszOtherValue = "1.0";
9938 :
9939 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9940 0 : return FALSE;
9941 :
9942 0 : return TRUE;
9943 : }
9944 :
9945 : /************************************************************************/
9946 : /* OSRIsSameVertCS() */
9947 : /************************************************************************/
9948 :
9949 : /**
9950 : * \brief Do the VertCS'es match?
9951 : *
9952 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9953 : */
9954 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9955 :
9956 : {
9957 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9958 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9959 :
9960 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9961 : }
9962 :
9963 : /************************************************************************/
9964 : /* IsSame() */
9965 : /************************************************************************/
9966 :
9967 : /**
9968 : * \brief Do these two spatial references describe the same system ?
9969 : *
9970 : * @param poOtherSRS the SRS being compared to.
9971 : *
9972 : * @return TRUE if equivalent or FALSE otherwise.
9973 : */
9974 :
9975 22961 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9976 :
9977 : {
9978 22961 : return IsSame(poOtherSRS, nullptr);
9979 : }
9980 :
9981 : /**
9982 : * \brief Do these two spatial references describe the same system ?
9983 : *
9984 : * This also takes into account the data axis to CRS axis mapping by default
9985 : *
9986 : * @param poOtherSRS the SRS being compared to.
9987 : * @param papszOptions options. NULL or NULL terminated list of options.
9988 : * Currently supported options are:
9989 : * <ul>
9990 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9991 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9992 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9993 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9994 : * </ul>
9995 : *
9996 : * @return TRUE if equivalent or FALSE otherwise.
9997 : */
9998 :
9999 33970 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
10000 : const char *const *papszOptions) const
10001 :
10002 : {
10003 67940 : TAKE_OPTIONAL_LOCK();
10004 :
10005 33970 : d->refreshProjObj();
10006 33970 : poOtherSRS->d->refreshProjObj();
10007 33970 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10008 54 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10009 33916 : if (!CPLTestBool(CSLFetchNameValueDef(
10010 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10011 : {
10012 33293 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10013 2891 : return false;
10014 : }
10015 :
10016 31024 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10017 : "IGNORE_COORDINATE_EPOCH", "NO")))
10018 : {
10019 30655 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10020 27 : return false;
10021 : }
10022 :
10023 30998 : bool reboundSelf = false;
10024 30998 : bool reboundOther = false;
10025 31049 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10026 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10027 : {
10028 14 : d->demoteFromBoundCRS();
10029 14 : reboundSelf = true;
10030 : }
10031 61930 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10032 30948 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10033 : {
10034 28 : poOtherSRS->d->demoteFromBoundCRS();
10035 28 : reboundOther = true;
10036 : }
10037 :
10038 30997 : PJ_COMPARISON_CRITERION criterion =
10039 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10040 30997 : const char *pszCriterion = CSLFetchNameValueDef(
10041 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10042 30997 : if (EQUAL(pszCriterion, "STRICT"))
10043 0 : criterion = PJ_COMP_STRICT;
10044 30997 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10045 8125 : criterion = PJ_COMP_EQUIVALENT;
10046 22872 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10047 : {
10048 0 : CPLError(CE_Warning, CPLE_NotSupported,
10049 : "Unsupported value for CRITERION: %s", pszCriterion);
10050 : }
10051 : int ret =
10052 30997 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10053 30998 : if (reboundSelf)
10054 14 : d->undoDemoteFromBoundCRS();
10055 30998 : if (reboundOther)
10056 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10057 :
10058 30998 : return ret;
10059 : }
10060 :
10061 : /************************************************************************/
10062 : /* OSRIsSame() */
10063 : /************************************************************************/
10064 :
10065 : /**
10066 : * \brief Do these two spatial references describe the same system ?
10067 : *
10068 : * This function is the same as OGRSpatialReference::IsSame().
10069 : */
10070 27 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10071 :
10072 : {
10073 27 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10074 27 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10075 :
10076 27 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10077 : }
10078 :
10079 : /************************************************************************/
10080 : /* OSRIsSameEx() */
10081 : /************************************************************************/
10082 :
10083 : /**
10084 : * \brief Do these two spatial references describe the same system ?
10085 : *
10086 : * This function is the same as OGRSpatialReference::IsSame().
10087 : */
10088 642 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10089 : const char *const *papszOptions)
10090 : {
10091 642 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10092 642 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10093 :
10094 642 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10095 : }
10096 :
10097 : /************************************************************************/
10098 : /* convertToOtherProjection() */
10099 : /************************************************************************/
10100 :
10101 : /**
10102 : * \brief Convert to another equivalent projection
10103 : *
10104 : * Currently implemented:
10105 : * <ul>
10106 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10107 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10108 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10109 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10110 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10111 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10112 : * </ul>
10113 : *
10114 : * @param pszTargetProjection target projection.
10115 : * @param papszOptions lists of options. None supported currently.
10116 : * @return a new SRS, or NULL in case of error.
10117 : *
10118 : */
10119 89 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10120 : const char *pszTargetProjection,
10121 : CPL_UNUSED const char *const *papszOptions) const
10122 : {
10123 178 : TAKE_OPTIONAL_LOCK();
10124 :
10125 89 : if (pszTargetProjection == nullptr)
10126 1 : return nullptr;
10127 : int new_code;
10128 88 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10129 : {
10130 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10131 : }
10132 82 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10133 : {
10134 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10135 : }
10136 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10137 : {
10138 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10139 : }
10140 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10141 : {
10142 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10143 : }
10144 : else
10145 : {
10146 1 : return nullptr;
10147 : }
10148 :
10149 87 : d->refreshProjObj();
10150 87 : d->demoteFromBoundCRS();
10151 87 : OGRSpatialReference *poNewSRS = nullptr;
10152 87 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10153 : {
10154 : auto conv =
10155 86 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10156 86 : auto new_conv = proj_convert_conversion_to_other_method(
10157 : d->getPROJContext(), conv, new_code, nullptr);
10158 86 : proj_destroy(conv);
10159 86 : if (new_conv)
10160 : {
10161 : auto geodCRS =
10162 72 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10163 72 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10164 72 : d->m_pj_crs);
10165 72 : if (geodCRS && cs)
10166 : {
10167 72 : auto new_proj_crs = proj_create_projected_crs(
10168 72 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10169 : new_conv, cs);
10170 72 : proj_destroy(new_conv);
10171 72 : if (new_proj_crs)
10172 : {
10173 72 : poNewSRS = new OGRSpatialReference();
10174 :
10175 72 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10176 : {
10177 9 : auto boundCRS = proj_crs_create_bound_crs(
10178 : d->getPROJContext(), new_proj_crs,
10179 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10180 9 : if (boundCRS)
10181 : {
10182 9 : proj_destroy(new_proj_crs);
10183 9 : new_proj_crs = boundCRS;
10184 : }
10185 : }
10186 :
10187 72 : poNewSRS->d->setPjCRS(new_proj_crs);
10188 : }
10189 : }
10190 72 : proj_destroy(geodCRS);
10191 72 : proj_destroy(cs);
10192 : }
10193 : }
10194 87 : d->undoDemoteFromBoundCRS();
10195 87 : return poNewSRS;
10196 : }
10197 :
10198 : /************************************************************************/
10199 : /* OSRConvertToOtherProjection() */
10200 : /************************************************************************/
10201 :
10202 : /**
10203 : * \brief Convert to another equivalent projection
10204 : *
10205 : * Currently implemented:
10206 : * <ul>
10207 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10208 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10209 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10210 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10211 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10212 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10213 : * </ul>
10214 : *
10215 : * @param hSRS source SRS
10216 : * @param pszTargetProjection target projection.
10217 : * @param papszOptions lists of options. None supported currently.
10218 : * @return a new SRS, or NULL in case of error.
10219 : *
10220 : */
10221 : OGRSpatialReferenceH
10222 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10223 : const char *pszTargetProjection,
10224 : const char *const *papszOptions)
10225 : {
10226 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10227 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10228 28 : pszTargetProjection, papszOptions));
10229 : }
10230 :
10231 : /************************************************************************/
10232 : /* OSRFindMatches() */
10233 : /************************************************************************/
10234 :
10235 : /**
10236 : * \brief Try to identify a match between the passed SRS and a related SRS
10237 : * in a catalog.
10238 : *
10239 : * Matching may be partial, or may fail.
10240 : * Returned entries will be sorted by decreasing match confidence (first
10241 : * entry has the highest match confidence).
10242 : *
10243 : * The exact way matching is done may change in future versions. Starting with
10244 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10245 : *
10246 : * This function is the same as OGRSpatialReference::FindMatches().
10247 : *
10248 : * @param hSRS SRS to match
10249 : * @param papszOptions NULL terminated list of options or NULL
10250 : * @param pnEntries Output parameter. Number of values in the returned array.
10251 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10252 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10253 : * indicate the confidence in the match. 100 is the highest confidence level.
10254 : * The array must be freed with CPLFree().
10255 : *
10256 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10257 : * with OSRFreeSRSArray()
10258 : *
10259 : */
10260 10 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10261 : char **papszOptions, int *pnEntries,
10262 : int **ppanMatchConfidence)
10263 : {
10264 10 : if (pnEntries)
10265 10 : *pnEntries = 0;
10266 10 : if (ppanMatchConfidence)
10267 10 : *ppanMatchConfidence = nullptr;
10268 10 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10269 :
10270 10 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10271 10 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10272 : }
10273 :
10274 : /************************************************************************/
10275 : /* OSRFreeSRSArray() */
10276 : /************************************************************************/
10277 :
10278 : /**
10279 : * \brief Free return of OSRIdentifyMatches()
10280 : *
10281 : * @param pahSRS array of SRS (must be NULL terminated)
10282 : */
10283 197 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10284 : {
10285 197 : if (pahSRS != nullptr)
10286 : {
10287 1744 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10288 : {
10289 1565 : OSRRelease(pahSRS[i]);
10290 : }
10291 179 : CPLFree(pahSRS);
10292 : }
10293 197 : }
10294 :
10295 : /************************************************************************/
10296 : /* FindBestMatch() */
10297 : /************************************************************************/
10298 :
10299 : /**
10300 : * \brief Try to identify the best match between the passed SRS and a related
10301 : * SRS in a catalog.
10302 : *
10303 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10304 : * of filtering its output.
10305 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10306 : * will be considered. If there is a single match, it is returned.
10307 : * If there are several matches, only return the one under the
10308 : * pszPreferredAuthority, if there is a single one under that authority.
10309 : *
10310 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10311 : * 100). If set to 0, 90 is used.
10312 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10313 : * "EPSG" is used.
10314 : * @param papszOptions NULL terminated list of options or NULL. No option is
10315 : * defined at time of writing.
10316 : *
10317 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10318 : *
10319 : * @since GDAL 3.6
10320 : * @see OGRSpatialReference::FindMatches()
10321 : */
10322 : OGRSpatialReference *
10323 1427 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10324 : const char *pszPreferredAuthority,
10325 : CSLConstList papszOptions) const
10326 : {
10327 2854 : TAKE_OPTIONAL_LOCK();
10328 :
10329 1427 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10330 :
10331 1427 : if (nMinimumMatchConfidence == 0)
10332 0 : nMinimumMatchConfidence = 90;
10333 1427 : if (pszPreferredAuthority == nullptr)
10334 200 : pszPreferredAuthority = "EPSG";
10335 :
10336 : // Try to identify the CRS with the database
10337 1427 : int nEntries = 0;
10338 1427 : int *panConfidence = nullptr;
10339 : OGRSpatialReferenceH *pahSRS =
10340 1427 : FindMatches(nullptr, &nEntries, &panConfidence);
10341 1427 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10342 : {
10343 2526 : std::vector<double> adfTOWGS84(7);
10344 1263 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10345 : {
10346 1262 : adfTOWGS84.clear();
10347 : }
10348 :
10349 1263 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10350 :
10351 : auto poBaseGeogCRS =
10352 1263 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10353 :
10354 : // If the base geographic SRS of the SRS is EPSG:4326
10355 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10356 : // SRS code
10357 : // Same with EPSG:4258 (ETRS89), since it's the only known
10358 : // TOWGS84[] style transformation to WGS 84, and given the
10359 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10360 : // chance that a non-NULL TOWGS84[] will emerge.
10361 1263 : const char *pszAuthorityName = nullptr;
10362 1263 : const char *pszAuthorityCode = nullptr;
10363 1263 : const char *pszBaseAuthorityName = nullptr;
10364 1263 : const char *pszBaseAuthorityCode = nullptr;
10365 2526 : if (adfTOWGS84 == std::vector<double>(7) &&
10366 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10367 1 : EQUAL(pszAuthorityName, "EPSG") &&
10368 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10369 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10370 1 : nullptr &&
10371 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10372 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10373 2527 : nullptr &&
10374 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10375 1 : EQUAL(pszBaseAuthorityCode, "4258")))
10376 : {
10377 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10378 : }
10379 :
10380 1263 : CPLFree(pahSRS);
10381 1263 : CPLFree(panConfidence);
10382 :
10383 1263 : return poSRS;
10384 : }
10385 : else
10386 : {
10387 : // If there are several matches >= nMinimumMatchConfidence, take the
10388 : // only one that is under pszPreferredAuthority
10389 164 : int iBestEntry = -1;
10390 1679 : for (int i = 0; i < nEntries; i++)
10391 : {
10392 1515 : if (panConfidence[i] >= nMinimumMatchConfidence)
10393 : {
10394 : const char *pszAuthName =
10395 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10396 3 : ->GetAuthorityName(nullptr);
10397 3 : if (pszAuthName != nullptr &&
10398 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10399 : {
10400 3 : if (iBestEntry < 0)
10401 3 : iBestEntry = i;
10402 : else
10403 : {
10404 0 : iBestEntry = -1;
10405 0 : break;
10406 : }
10407 : }
10408 : }
10409 : }
10410 164 : if (iBestEntry >= 0)
10411 : {
10412 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10413 3 : OSRFreeSRSArray(pahSRS);
10414 3 : CPLFree(panConfidence);
10415 3 : return poRet;
10416 : }
10417 : }
10418 161 : OSRFreeSRSArray(pahSRS);
10419 161 : CPLFree(panConfidence);
10420 161 : return nullptr;
10421 : }
10422 :
10423 : /************************************************************************/
10424 : /* SetTOWGS84() */
10425 : /************************************************************************/
10426 :
10427 : /**
10428 : * \brief Set the Bursa-Wolf conversion to WGS84.
10429 : *
10430 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10431 : * if there is no existing DATUM node. It will replace
10432 : * an existing TOWGS84 node if there is one.
10433 : *
10434 : * The parameters have the same meaning as EPSG transformation 9606
10435 : * (Position Vector 7-param. transformation).
10436 : *
10437 : * This method is the same as the C function OSRSetTOWGS84().
10438 : *
10439 : * @param dfDX X child in meters.
10440 : * @param dfDY Y child in meters.
10441 : * @param dfDZ Z child in meters.
10442 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10443 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10444 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10445 : * @param dfPPM scaling factor (parts per million).
10446 : *
10447 : * @return OGRERR_NONE on success.
10448 : */
10449 :
10450 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10451 : double dfEX, double dfEY, double dfEZ,
10452 : double dfPPM)
10453 :
10454 : {
10455 206 : TAKE_OPTIONAL_LOCK();
10456 :
10457 103 : d->refreshProjObj();
10458 103 : if (d->m_pj_crs == nullptr)
10459 : {
10460 0 : return OGRERR_FAILURE;
10461 : }
10462 :
10463 : // Remove existing BoundCRS
10464 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10465 : {
10466 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10467 0 : if (!baseCRS)
10468 0 : return OGRERR_FAILURE;
10469 0 : d->setPjCRS(baseCRS);
10470 : }
10471 :
10472 : PJ_PARAM_DESCRIPTION params[7];
10473 :
10474 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10475 103 : params[0].auth_name = "EPSG";
10476 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10477 103 : params[0].value = dfDX;
10478 103 : params[0].unit_name = "metre";
10479 103 : params[0].unit_conv_factor = 1.0;
10480 103 : params[0].unit_type = PJ_UT_LINEAR;
10481 :
10482 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10483 103 : params[1].auth_name = "EPSG";
10484 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10485 103 : params[1].value = dfDY;
10486 103 : params[1].unit_name = "metre";
10487 103 : params[1].unit_conv_factor = 1.0;
10488 103 : params[1].unit_type = PJ_UT_LINEAR;
10489 :
10490 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10491 103 : params[2].auth_name = "EPSG";
10492 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10493 103 : params[2].value = dfDZ;
10494 103 : params[2].unit_name = "metre";
10495 103 : params[2].unit_conv_factor = 1.0;
10496 103 : params[2].unit_type = PJ_UT_LINEAR;
10497 :
10498 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10499 103 : params[3].auth_name = "EPSG";
10500 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10501 103 : params[3].value = dfEX;
10502 103 : params[3].unit_name = "arc-second";
10503 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10504 103 : params[3].unit_type = PJ_UT_ANGULAR;
10505 :
10506 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10507 103 : params[4].auth_name = "EPSG";
10508 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10509 103 : params[4].value = dfEY;
10510 103 : params[4].unit_name = "arc-second";
10511 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10512 103 : params[4].unit_type = PJ_UT_ANGULAR;
10513 :
10514 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10515 103 : params[5].auth_name = "EPSG";
10516 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10517 103 : params[5].value = dfEZ;
10518 103 : params[5].unit_name = "arc-second";
10519 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10520 103 : params[5].unit_type = PJ_UT_ANGULAR;
10521 :
10522 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10523 103 : params[6].auth_name = "EPSG";
10524 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10525 103 : params[6].value = dfPPM;
10526 103 : params[6].unit_name = "parts per million";
10527 103 : params[6].unit_conv_factor = 1e-6;
10528 103 : params[6].unit_type = PJ_UT_SCALE;
10529 :
10530 : auto sourceCRS =
10531 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10532 103 : if (!sourceCRS)
10533 : {
10534 0 : return OGRERR_FAILURE;
10535 : }
10536 :
10537 103 : const auto sourceType = proj_get_type(sourceCRS);
10538 :
10539 103 : auto targetCRS = proj_create_from_database(
10540 : d->getPROJContext(), "EPSG",
10541 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10542 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10543 : : "4978",
10544 : PJ_CATEGORY_CRS, false, nullptr);
10545 103 : if (!targetCRS)
10546 : {
10547 0 : proj_destroy(sourceCRS);
10548 0 : return OGRERR_FAILURE;
10549 : }
10550 :
10551 206 : CPLString osMethodCode;
10552 : osMethodCode.Printf("%d",
10553 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10554 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10555 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10556 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10557 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10558 :
10559 103 : auto transf = proj_create_transformation(
10560 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10561 : sourceCRS, targetCRS, nullptr,
10562 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10563 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10564 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10565 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10566 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10567 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10568 103 : proj_destroy(sourceCRS);
10569 103 : if (!transf)
10570 : {
10571 0 : proj_destroy(targetCRS);
10572 0 : return OGRERR_FAILURE;
10573 : }
10574 :
10575 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10576 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10577 103 : proj_destroy(transf);
10578 103 : proj_destroy(targetCRS);
10579 103 : if (!newBoundCRS)
10580 : {
10581 0 : return OGRERR_FAILURE;
10582 : }
10583 :
10584 103 : d->setPjCRS(newBoundCRS);
10585 103 : return OGRERR_NONE;
10586 : }
10587 :
10588 : /************************************************************************/
10589 : /* OSRSetTOWGS84() */
10590 : /************************************************************************/
10591 :
10592 : /**
10593 : * \brief Set the Bursa-Wolf conversion to WGS84.
10594 : *
10595 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10596 : */
10597 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10598 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10599 : double dfPPM)
10600 :
10601 : {
10602 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10603 :
10604 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10605 4 : dfPPM);
10606 : }
10607 :
10608 : /************************************************************************/
10609 : /* GetTOWGS84() */
10610 : /************************************************************************/
10611 :
10612 : /**
10613 : * \brief Fetch TOWGS84 parameters, if available.
10614 : *
10615 : * The parameters have the same meaning as EPSG transformation 9606
10616 : * (Position Vector 7-param. transformation).
10617 : *
10618 : * @param padfCoeff array into which up to 7 coefficients are placed.
10619 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10620 : *
10621 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10622 : * TOWGS84 node available.
10623 : */
10624 :
10625 4657 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10626 :
10627 : {
10628 9314 : TAKE_OPTIONAL_LOCK();
10629 :
10630 4657 : d->refreshProjObj();
10631 4657 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10632 4609 : return OGRERR_FAILURE;
10633 :
10634 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10635 :
10636 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10637 48 : int success = proj_coordoperation_get_towgs84_values(
10638 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10639 48 : proj_destroy(transf);
10640 :
10641 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10642 : }
10643 :
10644 : /************************************************************************/
10645 : /* OSRGetTOWGS84() */
10646 : /************************************************************************/
10647 :
10648 : /**
10649 : * \brief Fetch TOWGS84 parameters, if available.
10650 : *
10651 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10652 : */
10653 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10654 : int nCoeffCount)
10655 :
10656 : {
10657 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10658 :
10659 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10660 : }
10661 :
10662 : /************************************************************************/
10663 : /* IsAngularParameter() */
10664 : /************************************************************************/
10665 :
10666 : /** Is the passed projection parameter an angular one?
10667 : *
10668 : * @return TRUE or FALSE
10669 : */
10670 :
10671 : /* static */
10672 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10673 :
10674 : {
10675 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10676 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10677 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10678 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10679 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10680 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10681 8 : return TRUE;
10682 :
10683 2 : return FALSE;
10684 : }
10685 :
10686 : /************************************************************************/
10687 : /* IsLongitudeParameter() */
10688 : /************************************************************************/
10689 :
10690 : /** Is the passed projection parameter an angular longitude
10691 : * (relative to a prime meridian)?
10692 : *
10693 : * @return TRUE or FALSE
10694 : */
10695 :
10696 : /* static */
10697 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10698 :
10699 : {
10700 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10701 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10702 0 : return TRUE;
10703 :
10704 0 : return FALSE;
10705 : }
10706 :
10707 : /************************************************************************/
10708 : /* IsLinearParameter() */
10709 : /************************************************************************/
10710 :
10711 : /** Is the passed projection parameter an linear one measured in meters or
10712 : * some similar linear measure.
10713 : *
10714 : * @return TRUE or FALSE
10715 : */
10716 :
10717 : /* static */
10718 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10719 :
10720 : {
10721 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10722 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10723 9 : return TRUE;
10724 :
10725 34 : return FALSE;
10726 : }
10727 :
10728 : /************************************************************************/
10729 : /* GetNormInfo() */
10730 : /************************************************************************/
10731 :
10732 : /**
10733 : * \brief Set the internal information for normalizing linear, and angular
10734 : * values.
10735 : */
10736 3823 : void OGRSpatialReference::GetNormInfo() const
10737 :
10738 : {
10739 3823 : TAKE_OPTIONAL_LOCK();
10740 :
10741 3823 : if (d->bNormInfoSet)
10742 2703 : return;
10743 :
10744 : /* -------------------------------------------------------------------- */
10745 : /* Initialize values. */
10746 : /* -------------------------------------------------------------------- */
10747 1120 : d->bNormInfoSet = TRUE;
10748 :
10749 1120 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10750 1120 : d->dfToMeter = GetLinearUnits(nullptr);
10751 1120 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10752 1120 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10753 1117 : d->dfToDegrees = 1.0;
10754 : }
10755 :
10756 : /************************************************************************/
10757 : /* GetExtension() */
10758 : /************************************************************************/
10759 :
10760 : /**
10761 : * \brief Fetch extension value.
10762 : *
10763 : * Fetch the value of the named EXTENSION item for the identified
10764 : * target node.
10765 : *
10766 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10767 : * @param pszName the name of the extension being fetched.
10768 : * @param pszDefault the value to return if the extension is not found.
10769 : *
10770 : * @return node value if successful or pszDefault on failure.
10771 : */
10772 :
10773 11042 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10774 : const char *pszName,
10775 : const char *pszDefault) const
10776 :
10777 : {
10778 22084 : TAKE_OPTIONAL_LOCK();
10779 :
10780 : /* -------------------------------------------------------------------- */
10781 : /* Find the target node. */
10782 : /* -------------------------------------------------------------------- */
10783 : const OGR_SRSNode *poNode =
10784 11042 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10785 :
10786 11042 : if (poNode == nullptr)
10787 2263 : return nullptr;
10788 :
10789 : /* -------------------------------------------------------------------- */
10790 : /* Fetch matching EXTENSION if there is one. */
10791 : /* -------------------------------------------------------------------- */
10792 64717 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10793 : {
10794 55960 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10795 :
10796 55984 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10797 24 : poChild->GetChildCount() >= 2)
10798 : {
10799 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10800 22 : return poChild->GetChild(1)->GetValue();
10801 : }
10802 : }
10803 :
10804 8757 : return pszDefault;
10805 : }
10806 :
10807 : /************************************************************************/
10808 : /* SetExtension() */
10809 : /************************************************************************/
10810 : /**
10811 : * \brief Set extension value.
10812 : *
10813 : * Set the value of the named EXTENSION item for the identified
10814 : * target node.
10815 : *
10816 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10817 : * @param pszName the name of the extension being fetched.
10818 : * @param pszValue the value to set
10819 : *
10820 : * @return OGRERR_NONE on success
10821 : */
10822 :
10823 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10824 : const char *pszName,
10825 : const char *pszValue)
10826 :
10827 : {
10828 40 : TAKE_OPTIONAL_LOCK();
10829 :
10830 : /* -------------------------------------------------------------------- */
10831 : /* Find the target node. */
10832 : /* -------------------------------------------------------------------- */
10833 20 : OGR_SRSNode *poNode = nullptr;
10834 :
10835 20 : if (pszTargetKey == nullptr)
10836 0 : poNode = GetRoot();
10837 : else
10838 20 : poNode = GetAttrNode(pszTargetKey);
10839 :
10840 20 : if (poNode == nullptr)
10841 0 : return OGRERR_FAILURE;
10842 :
10843 : /* -------------------------------------------------------------------- */
10844 : /* Fetch matching EXTENSION if there is one. */
10845 : /* -------------------------------------------------------------------- */
10846 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10847 : {
10848 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10849 :
10850 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10851 6 : poChild->GetChildCount() >= 2)
10852 : {
10853 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10854 : {
10855 6 : poChild->GetChild(1)->SetValue(pszValue);
10856 6 : return OGRERR_NONE;
10857 : }
10858 : }
10859 : }
10860 :
10861 : /* -------------------------------------------------------------------- */
10862 : /* Create a new EXTENSION node. */
10863 : /* -------------------------------------------------------------------- */
10864 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10865 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10866 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10867 :
10868 14 : poNode->AddChild(poAuthNode);
10869 :
10870 14 : return OGRERR_NONE;
10871 : }
10872 :
10873 : /************************************************************************/
10874 : /* OSRCleanup() */
10875 : /************************************************************************/
10876 :
10877 : static void CleanupSRSWGS84Mutex();
10878 :
10879 : /**
10880 : * \brief Cleanup cached SRS related memory.
10881 : *
10882 : * This function will attempt to cleanup any cache spatial reference
10883 : * related information, such as cached tables of coordinate systems.
10884 : *
10885 : * This function should not be called concurrently with any other GDAL/OGR
10886 : * function. It is meant at being called once before process termination
10887 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10888 : * thread-specific resources before thread termination.
10889 : */
10890 1127 : void OSRCleanup(void)
10891 :
10892 : {
10893 1127 : OGRCTDumpStatistics();
10894 1127 : CSVDeaccess(nullptr);
10895 1127 : CleanupSRSWGS84Mutex();
10896 1127 : OSRCTCleanCache();
10897 1127 : OSRCleanupTLSContext();
10898 1127 : }
10899 :
10900 : /************************************************************************/
10901 : /* GetAxesCount() */
10902 : /************************************************************************/
10903 :
10904 : /**
10905 : * \brief Return the number of axis of the coordinate system of the CRS.
10906 : *
10907 : * @since GDAL 3.0
10908 : */
10909 36710 : int OGRSpatialReference::GetAxesCount() const
10910 : {
10911 73420 : TAKE_OPTIONAL_LOCK();
10912 :
10913 36710 : int axisCount = 0;
10914 36710 : d->refreshProjObj();
10915 36710 : if (d->m_pj_crs == nullptr)
10916 : {
10917 6 : return 0;
10918 : }
10919 36704 : d->demoteFromBoundCRS();
10920 36704 : auto ctxt = d->getPROJContext();
10921 36704 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10922 : {
10923 29 : for (int i = 0;; i++)
10924 : {
10925 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10926 87 : if (!subCRS)
10927 29 : break;
10928 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10929 : {
10930 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10931 17 : if (baseCRS)
10932 : {
10933 17 : proj_destroy(subCRS);
10934 17 : subCRS = baseCRS;
10935 : }
10936 : }
10937 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10938 58 : if (cs)
10939 : {
10940 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10941 58 : proj_destroy(cs);
10942 : }
10943 58 : proj_destroy(subCRS);
10944 58 : }
10945 : }
10946 : else
10947 : {
10948 36675 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10949 36675 : if (cs)
10950 : {
10951 36675 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10952 36675 : proj_destroy(cs);
10953 : }
10954 : }
10955 36704 : d->undoDemoteFromBoundCRS();
10956 36704 : return axisCount;
10957 : }
10958 :
10959 : /************************************************************************/
10960 : /* OSRGetAxesCount() */
10961 : /************************************************************************/
10962 :
10963 : /**
10964 : * \brief Return the number of axis of the coordinate system of the CRS.
10965 : *
10966 : * This method is the equivalent of the C++ method
10967 : * OGRSpatialReference::GetAxesCount()
10968 : *
10969 : * @since GDAL 3.1
10970 : */
10971 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10972 :
10973 : {
10974 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10975 :
10976 6 : return ToPointer(hSRS)->GetAxesCount();
10977 : }
10978 :
10979 : /************************************************************************/
10980 : /* GetAxis() */
10981 : /************************************************************************/
10982 :
10983 : /**
10984 : * \brief Fetch the orientation of one axis.
10985 : *
10986 : * Fetches the request axis (iAxis - zero based) from the
10987 : * indicated portion of the coordinate system (pszTargetKey) which
10988 : * should be either "GEOGCS" or "PROJCS".
10989 : *
10990 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10991 : *
10992 : * This method is equivalent to the C function OSRGetAxis().
10993 : *
10994 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10995 : * "GEOGCS").
10996 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10997 : * @param peOrientation location into which to place the fetch orientation, may
10998 : * be NULL.
10999 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11000 : * factor. May be NULL. Only set if pszTargetKey == NULL
11001 : *
11002 : * @return the name of the axis or NULL on failure.
11003 : */
11004 :
11005 7118 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11006 : OGRAxisOrientation *peOrientation,
11007 : double *pdfConvUnit) const
11008 :
11009 : {
11010 14236 : TAKE_OPTIONAL_LOCK();
11011 :
11012 7118 : if (peOrientation != nullptr)
11013 7009 : *peOrientation = OAO_Other;
11014 7118 : if (pdfConvUnit != nullptr)
11015 101 : *pdfConvUnit = 0;
11016 :
11017 7118 : d->refreshProjObj();
11018 7118 : if (d->m_pj_crs == nullptr)
11019 : {
11020 3 : return nullptr;
11021 : }
11022 :
11023 7115 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11024 7115 : if (pszTargetKey == nullptr && iAxis <= 2)
11025 : {
11026 7115 : auto ctxt = d->getPROJContext();
11027 :
11028 7115 : int iAxisModified = iAxis;
11029 :
11030 7115 : d->demoteFromBoundCRS();
11031 :
11032 7115 : PJ *cs = nullptr;
11033 7115 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11034 : {
11035 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11036 134 : if (horizCRS)
11037 : {
11038 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11039 : {
11040 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11041 6 : if (baseCRS)
11042 : {
11043 6 : proj_destroy(horizCRS);
11044 6 : horizCRS = baseCRS;
11045 : }
11046 : }
11047 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11048 134 : proj_destroy(horizCRS);
11049 134 : if (cs)
11050 : {
11051 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11052 : {
11053 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11054 44 : proj_destroy(cs);
11055 44 : cs = nullptr;
11056 : }
11057 : }
11058 : }
11059 :
11060 134 : if (cs == nullptr)
11061 : {
11062 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11063 44 : if (vertCRS)
11064 : {
11065 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11066 : {
11067 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11068 30 : if (baseCRS)
11069 : {
11070 30 : proj_destroy(vertCRS);
11071 30 : vertCRS = baseCRS;
11072 : }
11073 : }
11074 :
11075 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11076 44 : proj_destroy(vertCRS);
11077 : }
11078 : }
11079 : }
11080 : else
11081 : {
11082 6981 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11083 : }
11084 :
11085 7115 : if (cs)
11086 : {
11087 7115 : const char *pszName = nullptr;
11088 7115 : const char *pszOrientation = nullptr;
11089 7115 : double dfConvFactor = 0.0;
11090 7115 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11091 : &pszOrientation, &dfConvFactor, nullptr,
11092 : nullptr, nullptr);
11093 :
11094 7115 : if (pdfConvUnit != nullptr)
11095 : {
11096 101 : *pdfConvUnit = dfConvFactor;
11097 : }
11098 :
11099 7115 : if (pszName && pszOrientation)
11100 : {
11101 7115 : d->m_osAxisName[iAxis] = pszName;
11102 7115 : if (peOrientation)
11103 : {
11104 7006 : if (EQUAL(pszOrientation, "NORTH"))
11105 4457 : *peOrientation = OAO_North;
11106 2549 : else if (EQUAL(pszOrientation, "EAST"))
11107 2506 : *peOrientation = OAO_East;
11108 43 : else if (EQUAL(pszOrientation, "SOUTH"))
11109 32 : *peOrientation = OAO_South;
11110 11 : else if (EQUAL(pszOrientation, "WEST"))
11111 0 : *peOrientation = OAO_West;
11112 11 : else if (EQUAL(pszOrientation, "UP"))
11113 1 : *peOrientation = OAO_Up;
11114 10 : else if (EQUAL(pszOrientation, "DOWN"))
11115 0 : *peOrientation = OAO_Down;
11116 : }
11117 7115 : proj_destroy(cs);
11118 7115 : d->undoDemoteFromBoundCRS();
11119 7115 : return d->m_osAxisName[iAxis].c_str();
11120 : }
11121 0 : proj_destroy(cs);
11122 : }
11123 0 : d->undoDemoteFromBoundCRS();
11124 : }
11125 :
11126 : /* -------------------------------------------------------------------- */
11127 : /* Find the target node. */
11128 : /* -------------------------------------------------------------------- */
11129 0 : const OGR_SRSNode *poNode = nullptr;
11130 :
11131 0 : if (pszTargetKey == nullptr)
11132 0 : poNode = GetRoot();
11133 : else
11134 0 : poNode = GetAttrNode(pszTargetKey);
11135 :
11136 0 : if (poNode == nullptr)
11137 0 : return nullptr;
11138 :
11139 : /* -------------------------------------------------------------------- */
11140 : /* Find desired child AXIS. */
11141 : /* -------------------------------------------------------------------- */
11142 0 : const OGR_SRSNode *poAxis = nullptr;
11143 0 : const int nChildCount = poNode->GetChildCount();
11144 :
11145 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11146 : {
11147 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11148 :
11149 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11150 0 : continue;
11151 :
11152 0 : if (iAxis == 0)
11153 : {
11154 0 : poAxis = poChild;
11155 0 : break;
11156 : }
11157 0 : iAxis--;
11158 : }
11159 :
11160 0 : if (poAxis == nullptr)
11161 0 : return nullptr;
11162 :
11163 0 : if (poAxis->GetChildCount() < 2)
11164 0 : return nullptr;
11165 :
11166 : /* -------------------------------------------------------------------- */
11167 : /* Extract name and orientation if possible. */
11168 : /* -------------------------------------------------------------------- */
11169 0 : if (peOrientation != nullptr)
11170 : {
11171 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11172 :
11173 0 : if (EQUAL(pszOrientation, "NORTH"))
11174 0 : *peOrientation = OAO_North;
11175 0 : else if (EQUAL(pszOrientation, "EAST"))
11176 0 : *peOrientation = OAO_East;
11177 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11178 0 : *peOrientation = OAO_South;
11179 0 : else if (EQUAL(pszOrientation, "WEST"))
11180 0 : *peOrientation = OAO_West;
11181 0 : else if (EQUAL(pszOrientation, "UP"))
11182 0 : *peOrientation = OAO_Up;
11183 0 : else if (EQUAL(pszOrientation, "DOWN"))
11184 0 : *peOrientation = OAO_Down;
11185 0 : else if (EQUAL(pszOrientation, "OTHER"))
11186 0 : *peOrientation = OAO_Other;
11187 : else
11188 : {
11189 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11190 : pszOrientation);
11191 : }
11192 : }
11193 :
11194 0 : return poAxis->GetChild(0)->GetValue();
11195 : }
11196 :
11197 : /************************************************************************/
11198 : /* OSRGetAxis() */
11199 : /************************************************************************/
11200 :
11201 : /**
11202 : * \brief Fetch the orientation of one axis.
11203 : *
11204 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11205 : */
11206 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11207 : int iAxis, OGRAxisOrientation *peOrientation)
11208 :
11209 : {
11210 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11211 :
11212 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11213 : }
11214 :
11215 : /************************************************************************/
11216 : /* OSRAxisEnumToName() */
11217 : /************************************************************************/
11218 :
11219 : /**
11220 : * \brief Return the string representation for the OGRAxisOrientation
11221 : * enumeration.
11222 : *
11223 : * For example "NORTH" for OAO_North.
11224 : *
11225 : * @return an internal string
11226 : */
11227 396 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11228 :
11229 : {
11230 396 : if (eOrientation == OAO_North)
11231 198 : return "NORTH";
11232 198 : if (eOrientation == OAO_East)
11233 198 : return "EAST";
11234 0 : if (eOrientation == OAO_South)
11235 0 : return "SOUTH";
11236 0 : if (eOrientation == OAO_West)
11237 0 : return "WEST";
11238 0 : if (eOrientation == OAO_Up)
11239 0 : return "UP";
11240 0 : if (eOrientation == OAO_Down)
11241 0 : return "DOWN";
11242 0 : if (eOrientation == OAO_Other)
11243 0 : return "OTHER";
11244 :
11245 0 : return "UNKNOWN";
11246 : }
11247 :
11248 : /************************************************************************/
11249 : /* SetAxes() */
11250 : /************************************************************************/
11251 :
11252 : /**
11253 : * \brief Set the axes for a coordinate system.
11254 : *
11255 : * Set the names, and orientations of the axes for either a projected
11256 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11257 : *
11258 : * This method is equivalent to the C function OSRSetAxes().
11259 : *
11260 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11261 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11262 : * @param eXAxisOrientation normally OAO_East.
11263 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11264 : * @param eYAxisOrientation normally OAO_North.
11265 : *
11266 : * @return OGRERR_NONE on success or an error code.
11267 : */
11268 :
11269 198 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11270 : const char *pszXAxisName,
11271 : OGRAxisOrientation eXAxisOrientation,
11272 : const char *pszYAxisName,
11273 : OGRAxisOrientation eYAxisOrientation)
11274 :
11275 : {
11276 396 : TAKE_OPTIONAL_LOCK();
11277 :
11278 : /* -------------------------------------------------------------------- */
11279 : /* Find the target node. */
11280 : /* -------------------------------------------------------------------- */
11281 198 : OGR_SRSNode *poNode = nullptr;
11282 :
11283 198 : if (pszTargetKey == nullptr)
11284 198 : poNode = GetRoot();
11285 : else
11286 0 : poNode = GetAttrNode(pszTargetKey);
11287 :
11288 198 : if (poNode == nullptr)
11289 0 : return OGRERR_FAILURE;
11290 :
11291 : /* -------------------------------------------------------------------- */
11292 : /* Strip any existing AXIS children. */
11293 : /* -------------------------------------------------------------------- */
11294 594 : while (poNode->FindChild("AXIS") >= 0)
11295 396 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11296 :
11297 : /* -------------------------------------------------------------------- */
11298 : /* Insert desired axes */
11299 : /* -------------------------------------------------------------------- */
11300 198 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11301 :
11302 198 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11303 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11304 :
11305 198 : poNode->AddChild(poAxis);
11306 :
11307 198 : poAxis = new OGR_SRSNode("AXIS");
11308 :
11309 198 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11310 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11311 :
11312 198 : poNode->AddChild(poAxis);
11313 :
11314 198 : return OGRERR_NONE;
11315 : }
11316 :
11317 : /************************************************************************/
11318 : /* OSRSetAxes() */
11319 : /************************************************************************/
11320 : /**
11321 : * \brief Set the axes for a coordinate system.
11322 : *
11323 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11324 : */
11325 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11326 : const char *pszXAxisName,
11327 : OGRAxisOrientation eXAxisOrientation,
11328 : const char *pszYAxisName,
11329 : OGRAxisOrientation eYAxisOrientation)
11330 : {
11331 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11332 :
11333 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11334 : eXAxisOrientation, pszYAxisName,
11335 0 : eYAxisOrientation);
11336 : }
11337 :
11338 : /************************************************************************/
11339 : /* OSRExportToMICoordSys() */
11340 : /************************************************************************/
11341 : /**
11342 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11343 : *
11344 : * This method is the equivalent of the C++ method
11345 : * OGRSpatialReference::exportToMICoordSys
11346 : */
11347 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11348 :
11349 : {
11350 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11351 :
11352 5 : *ppszReturn = nullptr;
11353 :
11354 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11355 : }
11356 :
11357 : /************************************************************************/
11358 : /* exportToMICoordSys() */
11359 : /************************************************************************/
11360 :
11361 : /**
11362 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11363 : *
11364 : * Note that the returned WKT string should be freed with
11365 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11366 : *
11367 : * This method is the same as the C function OSRExportToMICoordSys().
11368 : *
11369 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11370 : * definition will be assigned.
11371 : *
11372 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11373 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11374 : */
11375 :
11376 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11377 :
11378 : {
11379 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11380 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11381 7 : return OGRERR_NONE;
11382 :
11383 0 : return OGRERR_FAILURE;
11384 : }
11385 :
11386 : /************************************************************************/
11387 : /* OSRImportFromMICoordSys() */
11388 : /************************************************************************/
11389 : /**
11390 : * \brief Import Mapinfo style CoordSys definition.
11391 : *
11392 : * This method is the equivalent of the C++ method
11393 : * OGRSpatialReference::importFromMICoordSys
11394 : */
11395 :
11396 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11397 : const char *pszCoordSys)
11398 :
11399 : {
11400 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11401 :
11402 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11403 : }
11404 :
11405 : /************************************************************************/
11406 : /* importFromMICoordSys() */
11407 : /************************************************************************/
11408 :
11409 : /**
11410 : * \brief Import Mapinfo style CoordSys definition.
11411 : *
11412 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11413 : * definition string.
11414 : *
11415 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11416 : *
11417 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11418 : *
11419 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11420 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11421 : */
11422 :
11423 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11424 :
11425 : {
11426 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11427 :
11428 17 : if (poResult == nullptr)
11429 0 : return OGRERR_FAILURE;
11430 :
11431 17 : *this = *poResult;
11432 17 : delete poResult;
11433 :
11434 17 : return OGRERR_NONE;
11435 : }
11436 :
11437 : /************************************************************************/
11438 : /* OSRCalcInvFlattening() */
11439 : /************************************************************************/
11440 :
11441 : /**
11442 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11443 : *
11444 : * @param dfSemiMajor Semi-major axis length.
11445 : * @param dfSemiMinor Semi-minor axis length.
11446 : *
11447 : * @return inverse flattening, or 0 if both axis are equal.
11448 : */
11449 :
11450 8404 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11451 : {
11452 8404 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11453 27 : return 0;
11454 8377 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11455 : {
11456 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11457 : "OSRCalcInvFlattening(): Wrong input values");
11458 0 : return 0;
11459 : }
11460 :
11461 8377 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11462 : }
11463 :
11464 : /************************************************************************/
11465 : /* OSRCalcInvFlattening() */
11466 : /************************************************************************/
11467 :
11468 : /**
11469 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11470 : *
11471 : * @param dfSemiMajor Semi-major axis length.
11472 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11473 : *
11474 : * @return semi-minor axis
11475 : */
11476 :
11477 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11478 : double dfInvFlattening)
11479 : {
11480 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11481 103 : return dfSemiMajor;
11482 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11483 : {
11484 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11485 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11486 0 : return dfSemiMajor;
11487 : }
11488 :
11489 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11490 : }
11491 :
11492 : /************************************************************************/
11493 : /* GetWGS84SRS() */
11494 : /************************************************************************/
11495 :
11496 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11497 : static CPLMutex *hMutex = nullptr;
11498 :
11499 : /**
11500 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11501 : *
11502 : * Note: the instance will have
11503 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11504 : *
11505 : * The reference counter of the returned object is not increased by this
11506 : * operation.
11507 : *
11508 : * @return instance.
11509 : */
11510 :
11511 1001 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11512 : {
11513 1001 : CPLMutexHolderD(&hMutex);
11514 1001 : if (poSRSWGS84 == nullptr)
11515 : {
11516 5 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11517 5 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11518 : }
11519 2002 : return poSRSWGS84;
11520 : }
11521 :
11522 : /************************************************************************/
11523 : /* CleanupSRSWGS84Mutex() */
11524 : /************************************************************************/
11525 :
11526 1127 : static void CleanupSRSWGS84Mutex()
11527 : {
11528 1127 : if (hMutex != nullptr)
11529 : {
11530 3 : poSRSWGS84->Release();
11531 3 : poSRSWGS84 = nullptr;
11532 3 : CPLDestroyMutex(hMutex);
11533 3 : hMutex = nullptr;
11534 : }
11535 1127 : }
11536 :
11537 : /************************************************************************/
11538 : /* OSRImportFromProj4() */
11539 : /************************************************************************/
11540 : /**
11541 : * \brief Import PROJ coordinate string.
11542 : *
11543 : * This function is the same as OGRSpatialReference::importFromProj4().
11544 : */
11545 186 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11546 :
11547 : {
11548 186 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11549 :
11550 186 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11551 : }
11552 :
11553 : /************************************************************************/
11554 : /* importFromProj4() */
11555 : /************************************************************************/
11556 :
11557 : /**
11558 : * \brief Import PROJ coordinate string.
11559 : *
11560 : * The OGRSpatialReference is initialized from the passed PROJs style
11561 : * coordinate system string.
11562 : *
11563 : * Example:
11564 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11565 : *
11566 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11567 : * a legacy syntax that should be avoided in the future. In particular they will
11568 : * result in CRS objects whose axis order might not correspond to the official
11569 : * EPSG axis order.
11570 : *
11571 : * This method is the equivalent of the C function OSRImportFromProj4().
11572 : *
11573 : * @param pszProj4 the PROJ style string.
11574 : *
11575 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11576 : */
11577 :
11578 725 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11579 :
11580 : {
11581 1450 : TAKE_OPTIONAL_LOCK();
11582 :
11583 725 : if (strlen(pszProj4) >= 10000)
11584 : {
11585 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11586 1 : return OGRERR_CORRUPT_DATA;
11587 : }
11588 :
11589 : /* -------------------------------------------------------------------- */
11590 : /* Clear any existing definition. */
11591 : /* -------------------------------------------------------------------- */
11592 724 : Clear();
11593 :
11594 724 : CPLString osProj4(pszProj4);
11595 724 : if (osProj4.find("type=crs") == std::string::npos)
11596 : {
11597 715 : osProj4 += " +type=crs";
11598 : }
11599 :
11600 726 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11601 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11602 : {
11603 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11604 : "+init=epsg:XXXX syntax is deprecated. It might return "
11605 : "a CRS with a non-EPSG compliant axis order.");
11606 : }
11607 724 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11608 724 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11609 724 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11610 724 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11611 : }
11612 :
11613 : /************************************************************************/
11614 : /* OSRExportToProj4() */
11615 : /************************************************************************/
11616 : /**
11617 : * \brief Export coordinate system in PROJ.4 legacy format.
11618 : *
11619 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11620 : * PROJ >= 6 is significantly different from earlier versions. In particular
11621 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11622 : * will be missing most of the time. PROJ strings to encode CRS should be
11623 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11624 : * is the recommended way.
11625 : *
11626 : * This function is the same as OGRSpatialReference::exportToProj4().
11627 : */
11628 427 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11629 : char **ppszReturn)
11630 :
11631 : {
11632 427 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11633 :
11634 427 : *ppszReturn = nullptr;
11635 :
11636 427 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11637 : }
11638 :
11639 : /************************************************************************/
11640 : /* exportToProj4() */
11641 : /************************************************************************/
11642 :
11643 : /**
11644 : * \brief Export coordinate system in PROJ.4 legacy format.
11645 : *
11646 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11647 : * PROJ >= 6 is significantly different from earlier versions. In particular
11648 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11649 : * will be missing most of the time. PROJ strings to encode CRS should be
11650 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11651 : * representation is the recommended way.
11652 : *
11653 : * Converts the loaded coordinate reference system into PROJ format
11654 : * to the extent possible. The string returned in ppszProj4 should be
11655 : * deallocated by the caller with CPLFree() when no longer needed.
11656 : *
11657 : * LOCAL_CS coordinate systems are not translatable. An empty string
11658 : * will be returned along with OGRERR_NONE.
11659 : *
11660 : * Special processing for Transverse Mercator:
11661 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11662 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11663 : * for the tmerc and utm projection methods, rather than the more accurate
11664 : * method.
11665 : *
11666 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11667 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11668 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11669 : * added. This automatic addition may be disabled by setting the
11670 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11671 : *
11672 : * This method is the equivalent of the C function OSRExportToProj4().
11673 : *
11674 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11675 : * will be assigned.
11676 : *
11677 : * @return OGRERR_NONE on success or an error code on failure.
11678 : */
11679 :
11680 1398 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11681 :
11682 : {
11683 : // In the past calling this method was thread-safe, even if we never
11684 : // guaranteed it. Now proj_as_proj_string() will cache the result
11685 : // internally, so this is no longer thread-safe.
11686 2796 : std::lock_guard oLock(d->m_mutex);
11687 :
11688 1398 : d->refreshProjObj();
11689 1398 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11690 : {
11691 4 : *ppszProj4 = CPLStrdup("");
11692 4 : return OGRERR_FAILURE;
11693 : }
11694 :
11695 : // OSR_USE_ETMERC is here just for legacy
11696 1394 : bool bForceApproxTMerc = false;
11697 1394 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11698 1394 : if (pszUseETMERC && pszUseETMERC[0])
11699 : {
11700 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11701 : "OSR_USE_ETMERC is a legacy configuration option, which "
11702 : "now has only effect when set to NO (YES is the default). "
11703 : "Use OSR_USE_APPROX_TMERC=YES instead");
11704 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11705 : }
11706 : else
11707 : {
11708 : const char *pszUseApproxTMERC =
11709 1394 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11710 1394 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11711 : {
11712 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11713 : }
11714 : }
11715 1394 : const char *options[] = {
11716 1394 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11717 :
11718 1394 : const char *projString = proj_as_proj_string(
11719 1394 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11720 :
11721 1394 : PJ *boundCRS = nullptr;
11722 2784 : if (projString &&
11723 1390 : (strstr(projString, "+datum=") == nullptr ||
11724 2794 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11725 555 : CPLTestBool(
11726 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11727 : {
11728 555 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11729 555 : d->getPROJContext(), d->m_pj_crs, true,
11730 555 : strstr(projString, "+datum=") == nullptr);
11731 555 : if (boundCRS)
11732 : {
11733 227 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11734 : PJ_PROJ_4, options);
11735 : }
11736 : }
11737 :
11738 1394 : if (projString == nullptr)
11739 : {
11740 4 : *ppszProj4 = CPLStrdup("");
11741 4 : proj_destroy(boundCRS);
11742 4 : return OGRERR_FAILURE;
11743 : }
11744 1390 : *ppszProj4 = CPLStrdup(projString);
11745 1390 : proj_destroy(boundCRS);
11746 1390 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11747 1390 : if (pszTypeCrs)
11748 1390 : *pszTypeCrs = '\0';
11749 1390 : return OGRERR_NONE;
11750 : }
11751 :
11752 : /************************************************************************/
11753 : /* morphToESRI() */
11754 : /************************************************************************/
11755 : /**
11756 : * \brief Convert in place to ESRI WKT format.
11757 : *
11758 : * The value nodes of this coordinate system are modified in various manners
11759 : * more closely map onto the ESRI concept of WKT format. This includes
11760 : * renaming a variety of projections and arguments, and stripping out
11761 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11762 : *
11763 : * \note Since GDAL 3.0, this function has only user-visible effects at
11764 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11765 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11766 : *
11767 : * This does the same as the C function OSRMorphToESRI().
11768 : *
11769 : * @return OGRERR_NONE unless something goes badly wrong.
11770 : * @deprecated
11771 : */
11772 :
11773 235 : OGRErr OGRSpatialReference::morphToESRI()
11774 :
11775 : {
11776 235 : TAKE_OPTIONAL_LOCK();
11777 :
11778 235 : d->refreshProjObj();
11779 235 : d->setMorphToESRI(true);
11780 :
11781 470 : return OGRERR_NONE;
11782 : }
11783 :
11784 : /************************************************************************/
11785 : /* OSRMorphToESRI() */
11786 : /************************************************************************/
11787 :
11788 : /**
11789 : * \brief Convert in place to ESRI WKT format.
11790 : *
11791 : * This function is the same as the C++ method
11792 : * OGRSpatialReference::morphToESRI().
11793 : */
11794 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11795 :
11796 : {
11797 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11798 :
11799 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11800 : }
11801 :
11802 : /************************************************************************/
11803 : /* morphFromESRI() */
11804 : /************************************************************************/
11805 :
11806 : /**
11807 : * \brief Convert in place from ESRI WKT format.
11808 : *
11809 : * The value notes of this coordinate system are modified in various manners
11810 : * to adhere more closely to the WKT standard. This mostly involves
11811 : * translating a variety of ESRI names for projections, arguments and
11812 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11813 : * translation of EPSG to WKT for the CT specification.
11814 : *
11815 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11816 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11817 : * effect is to undo the effect of a potential prior call to morphToESRI().
11818 : *
11819 : * This does the same as the C function OSRMorphFromESRI().
11820 : *
11821 : * @return OGRERR_NONE unless something goes badly wrong.
11822 : * @deprecated
11823 : */
11824 :
11825 21 : OGRErr OGRSpatialReference::morphFromESRI()
11826 :
11827 : {
11828 21 : TAKE_OPTIONAL_LOCK();
11829 :
11830 21 : d->refreshProjObj();
11831 21 : d->setMorphToESRI(false);
11832 :
11833 42 : return OGRERR_NONE;
11834 : }
11835 :
11836 : /************************************************************************/
11837 : /* OSRMorphFromESRI() */
11838 : /************************************************************************/
11839 :
11840 : /**
11841 : * \brief Convert in place from ESRI WKT format.
11842 : *
11843 : * This function is the same as the C++ method
11844 : * OGRSpatialReference::morphFromESRI().
11845 : */
11846 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11847 :
11848 : {
11849 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11850 :
11851 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11852 : }
11853 :
11854 : /************************************************************************/
11855 : /* FindMatches() */
11856 : /************************************************************************/
11857 :
11858 : /**
11859 : * \brief Try to identify a match between the passed SRS and a related SRS
11860 : * in a catalog.
11861 : *
11862 : * Matching may be partial, or may fail.
11863 : * Returned entries will be sorted by decreasing match confidence (first
11864 : * entry has the highest match confidence).
11865 : *
11866 : * The exact way matching is done may change in future versions. Starting with
11867 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11868 : *
11869 : * This method is the same as OSRFindMatches().
11870 : *
11871 : * @param papszOptions NULL terminated list of options or NULL
11872 : * @param pnEntries Output parameter. Number of values in the returned array.
11873 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11874 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11875 : * indicate the confidence in the match. 100 is the highest confidence level.
11876 : * The array must be freed with CPLFree().
11877 : *
11878 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11879 : * with OSRFreeSRSArray()
11880 : *
11881 : *
11882 : * @see OGRSpatialReference::FindBestMatch()
11883 : */
11884 : OGRSpatialReferenceH *
11885 1442 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11886 : int **ppanMatchConfidence) const
11887 : {
11888 2884 : TAKE_OPTIONAL_LOCK();
11889 :
11890 1442 : CPL_IGNORE_RET_VAL(papszOptions);
11891 :
11892 1442 : if (pnEntries)
11893 1442 : *pnEntries = 0;
11894 1442 : if (ppanMatchConfidence)
11895 1442 : *ppanMatchConfidence = nullptr;
11896 :
11897 1442 : d->refreshProjObj();
11898 1442 : if (!d->m_pj_crs)
11899 0 : return nullptr;
11900 :
11901 1442 : int *panConfidence = nullptr;
11902 1442 : auto ctxt = d->getPROJContext();
11903 : auto list =
11904 1442 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11905 1442 : if (!list)
11906 0 : return nullptr;
11907 :
11908 1442 : const int nMatches = proj_list_get_count(list);
11909 :
11910 1442 : if (pnEntries)
11911 1442 : *pnEntries = static_cast<int>(nMatches);
11912 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11913 1442 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11914 1442 : if (ppanMatchConfidence)
11915 : {
11916 1442 : *ppanMatchConfidence =
11917 1442 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11918 : }
11919 :
11920 1442 : bool bSortAgain = false;
11921 :
11922 4270 : for (int i = 0; i < nMatches; i++)
11923 : {
11924 2828 : PJ *obj = proj_list_get(ctxt, list, i);
11925 2828 : CPLAssert(obj);
11926 2828 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11927 2828 : poSRS->d->setPjCRS(obj);
11928 2828 : pahRet[i] = ToHandle(poSRS);
11929 :
11930 : // Identify matches that only differ by axis order
11931 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11932 2846 : poSRS->GetAxesCount() == 2 &&
11933 2837 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11934 : {
11935 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11936 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11937 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11938 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11939 9 : GetAxis(nullptr, 0, &eThisAxis0);
11940 9 : GetAxis(nullptr, 1, &eThisAxis1);
11941 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11942 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11943 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11944 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11945 : {
11946 : auto pj_crs_normalized =
11947 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11948 9 : if (pj_crs_normalized)
11949 : {
11950 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11951 9 : PJ_COMP_EQUIVALENT))
11952 : {
11953 3 : bSortAgain = true;
11954 3 : panConfidence[i] = 90;
11955 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11956 : }
11957 9 : proj_destroy(pj_crs_normalized);
11958 : }
11959 : }
11960 : }
11961 :
11962 2828 : if (ppanMatchConfidence)
11963 2828 : (*ppanMatchConfidence)[i] = panConfidence[i];
11964 : }
11965 :
11966 1442 : if (bSortAgain)
11967 : {
11968 3 : std::vector<int> anIndices;
11969 12 : for (int i = 0; i < nMatches; ++i)
11970 9 : anIndices.push_back(i);
11971 :
11972 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11973 9 : [&panConfidence](int i, int j)
11974 9 : { return panConfidence[i] > panConfidence[j]; });
11975 :
11976 : OGRSpatialReferenceH *pahRetSorted =
11977 : static_cast<OGRSpatialReferenceH *>(
11978 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11979 12 : for (int i = 0; i < nMatches; ++i)
11980 : {
11981 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11982 9 : if (ppanMatchConfidence)
11983 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11984 : }
11985 3 : CPLFree(pahRet);
11986 3 : pahRet = pahRetSorted;
11987 : }
11988 :
11989 1442 : pahRet[nMatches] = nullptr;
11990 1442 : proj_list_destroy(list);
11991 1442 : proj_int_list_destroy(panConfidence);
11992 :
11993 1442 : return pahRet;
11994 : }
11995 :
11996 : /************************************************************************/
11997 : /* importFromEPSGA() */
11998 : /************************************************************************/
11999 :
12000 : /**
12001 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12002 : * code.
12003 : *
12004 : * This method will initialize the spatial reference based on the
12005 : * passed in EPSG CRS code found in the PROJ database.
12006 : *
12007 : * Since GDAL 3.0, this method is identical to importFromEPSG().
12008 : *
12009 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12010 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12011 : * such method available for the CRS. This behavior might not always be
12012 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12013 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12014 : * The AddGuessedTOWGS84() method can also be used for that purpose.
12015 : *
12016 : * The method will also by default substitute a deprecated EPSG code by its
12017 : * non-deprecated replacement. If this behavior is not desired, the
12018 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12019 : *
12020 : * This method is the same as the C function OSRImportFromEPSGA().
12021 : *
12022 : * @param nCode a CRS code.
12023 : *
12024 : * @return OGRERR_NONE on success, or an error code on failure.
12025 : */
12026 :
12027 43403 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12028 :
12029 : {
12030 86806 : TAKE_OPTIONAL_LOCK();
12031 :
12032 43403 : Clear();
12033 :
12034 : const char *pszUseNonDeprecated =
12035 43403 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12036 : const bool bUseNonDeprecated =
12037 43403 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12038 43403 : const bool bAddTOWGS84 = CPLTestBool(
12039 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12040 43403 : auto tlsCache = OSRGetProjTLSCache();
12041 43403 : if (tlsCache)
12042 : {
12043 : auto cachedObj =
12044 43403 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12045 43403 : if (cachedObj)
12046 : {
12047 33311 : d->setPjCRS(cachedObj);
12048 33311 : return OGRERR_NONE;
12049 : }
12050 : }
12051 :
12052 20184 : CPLString osCode;
12053 10092 : osCode.Printf("%d", nCode);
12054 : PJ *obj;
12055 10092 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12056 10092 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12057 : {
12058 10087 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12059 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12060 : nullptr);
12061 10087 : if (!obj)
12062 : {
12063 23 : return OGRERR_FAILURE;
12064 : }
12065 : }
12066 : else
12067 : {
12068 : // Likely to be an ESRI CRS...
12069 5 : CPLErr eLastErrorType = CE_None;
12070 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12071 5 : std::string osLastErrorMsg;
12072 5 : bool bIsESRI = false;
12073 : {
12074 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12075 5 : CPLErrorReset();
12076 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12077 : osCode.c_str(), PJ_CATEGORY_CRS,
12078 : true, nullptr);
12079 5 : if (!obj)
12080 : {
12081 2 : eLastErrorType = CPLGetLastErrorType();
12082 2 : eLastErrorNum = CPLGetLastErrorNo();
12083 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12084 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12085 : osCode.c_str(), PJ_CATEGORY_CRS,
12086 : true, nullptr);
12087 2 : if (obj)
12088 1 : bIsESRI = true;
12089 : }
12090 : }
12091 5 : if (!obj)
12092 : {
12093 1 : if (eLastErrorType != CE_None)
12094 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12095 : osLastErrorMsg.c_str());
12096 1 : return OGRERR_FAILURE;
12097 : }
12098 4 : if (bIsESRI)
12099 : {
12100 1 : CPLError(CE_Warning, CPLE_AppDefined,
12101 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12102 : "Assuming ESRI:%d was meant",
12103 : nCode, nCode, nCode);
12104 : }
12105 : }
12106 :
12107 10068 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12108 : {
12109 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12110 410 : if (list)
12111 : {
12112 410 : const auto count = proj_list_get_count(list);
12113 410 : if (count == 1)
12114 : {
12115 : auto nonDeprecated =
12116 359 : proj_list_get(d->getPROJContext(), list, 0);
12117 359 : if (nonDeprecated)
12118 : {
12119 359 : if (pszUseNonDeprecated == nullptr)
12120 : {
12121 : const char *pszNewAuth =
12122 359 : proj_get_id_auth_name(nonDeprecated, 0);
12123 : const char *pszNewCode =
12124 359 : proj_get_id_code(nonDeprecated, 0);
12125 359 : CPLError(CE_Warning, CPLE_AppDefined,
12126 : "CRS EPSG:%d is deprecated. "
12127 : "Its non-deprecated replacement %s:%s "
12128 : "will be used instead. "
12129 : "To use the original CRS, set the "
12130 : "OSR_USE_NON_DEPRECATED "
12131 : "configuration option to NO.",
12132 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12133 : pszNewCode ? pszNewCode : "(null)");
12134 : }
12135 359 : proj_destroy(obj);
12136 359 : obj = nonDeprecated;
12137 : }
12138 : }
12139 : }
12140 410 : proj_list_destroy(list);
12141 : }
12142 :
12143 10068 : if (bAddTOWGS84)
12144 : {
12145 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12146 : obj, nullptr);
12147 1 : if (boundCRS)
12148 : {
12149 1 : proj_destroy(obj);
12150 1 : obj = boundCRS;
12151 : }
12152 : }
12153 :
12154 10068 : d->setPjCRS(obj);
12155 :
12156 10068 : if (tlsCache)
12157 : {
12158 10068 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12159 : obj);
12160 : }
12161 :
12162 10068 : return OGRERR_NONE;
12163 : }
12164 :
12165 : /************************************************************************/
12166 : /* AddGuessedTOWGS84() */
12167 : /************************************************************************/
12168 :
12169 : /**
12170 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12171 : * to WGS84.
12172 : *
12173 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12174 : * to WGS84 when there is one and only one such method available for the CRS.
12175 : * Note: this is more restrictive to how GDAL < 3 worked.
12176 : *
12177 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12178 : *
12179 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12180 : * already a transformation to WGS84 or none matching could be found).
12181 : *
12182 : * @since GDAL 3.0.3
12183 : */
12184 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12185 : {
12186 36 : TAKE_OPTIONAL_LOCK();
12187 :
12188 18 : d->refreshProjObj();
12189 18 : if (!d->m_pj_crs)
12190 0 : return OGRERR_FAILURE;
12191 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12192 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12193 18 : if (!boundCRS)
12194 : {
12195 0 : return OGRERR_FAILURE;
12196 : }
12197 18 : d->setPjCRS(boundCRS);
12198 18 : return OGRERR_NONE;
12199 : }
12200 :
12201 : /************************************************************************/
12202 : /* OSRImportFromEPSGA() */
12203 : /************************************************************************/
12204 :
12205 : /**
12206 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12207 : * to WGS84.
12208 : *
12209 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12210 : *
12211 : * @since GDAL 3.0.3
12212 : */
12213 :
12214 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12215 :
12216 : {
12217 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12218 :
12219 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12220 : }
12221 :
12222 : /************************************************************************/
12223 : /* OSRImportFromEPSGA() */
12224 : /************************************************************************/
12225 :
12226 : /**
12227 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12228 : * code.
12229 : *
12230 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12231 : */
12232 :
12233 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12234 :
12235 : {
12236 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12237 :
12238 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12239 : }
12240 :
12241 : /************************************************************************/
12242 : /* importFromEPSG() */
12243 : /************************************************************************/
12244 :
12245 : /**
12246 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12247 : * code.
12248 : *
12249 : * This method will initialize the spatial reference based on the
12250 : * passed in EPSG CRS code found in the PROJ database.
12251 : *
12252 : * This method is the same as the C function OSRImportFromEPSG().
12253 : *
12254 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12255 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12256 : * such method available for the CRS. This behavior might not always be
12257 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12258 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12259 : *
12260 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12261 : *
12262 : * @return OGRERR_NONE on success, or an error code on failure.
12263 : */
12264 :
12265 38606 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12266 :
12267 : {
12268 38606 : return importFromEPSGA(nCode);
12269 : }
12270 :
12271 : /************************************************************************/
12272 : /* OSRImportFromEPSG() */
12273 : /************************************************************************/
12274 :
12275 : /**
12276 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12277 : * code.
12278 : *
12279 : * This function is the same as OGRSpatialReference::importFromEPSG().
12280 : */
12281 :
12282 1458 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12283 :
12284 : {
12285 1458 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12286 :
12287 1458 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12288 : }
12289 :
12290 : /************************************************************************/
12291 : /* EPSGTreatsAsLatLong() */
12292 : /************************************************************************/
12293 :
12294 : /**
12295 : * \brief This method returns TRUE if this geographic coordinate
12296 : * system should be treated as having lat/long coordinate ordering.
12297 : *
12298 : * Currently this returns TRUE for all geographic coordinate systems
12299 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12300 : * also checked that the CRS had belonged to EPSG authority, but this check
12301 : * has now been removed).
12302 : *
12303 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12304 : * geographic CRS imported with importFromEPSG() would cause this method to
12305 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12306 : * is now equivalent to importFromEPSGA().
12307 : *
12308 : * FALSE will be returned for all coordinate systems that are not geographic,
12309 : * or whose axes ordering is not latitude, longitude.
12310 : *
12311 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12312 : *
12313 : * @return TRUE or FALSE.
12314 : */
12315 :
12316 866 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12317 :
12318 : {
12319 1732 : TAKE_OPTIONAL_LOCK();
12320 :
12321 866 : if (!IsGeographic())
12322 695 : return FALSE;
12323 :
12324 171 : d->demoteFromBoundCRS();
12325 :
12326 171 : bool ret = false;
12327 171 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12328 : {
12329 : auto horizCRS =
12330 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12331 3 : if (horizCRS)
12332 : {
12333 : auto cs =
12334 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12335 3 : if (cs)
12336 : {
12337 3 : const char *pszDirection = nullptr;
12338 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12339 : nullptr, &pszDirection, nullptr,
12340 3 : nullptr, nullptr, nullptr))
12341 : {
12342 3 : if (EQUAL(pszDirection, "north"))
12343 : {
12344 3 : ret = true;
12345 : }
12346 : }
12347 :
12348 3 : proj_destroy(cs);
12349 : }
12350 :
12351 3 : proj_destroy(horizCRS);
12352 : }
12353 : }
12354 : else
12355 : {
12356 : auto cs =
12357 168 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12358 168 : if (cs)
12359 : {
12360 168 : const char *pszDirection = nullptr;
12361 168 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12362 : nullptr, &pszDirection, nullptr, nullptr,
12363 168 : nullptr, nullptr))
12364 : {
12365 168 : if (EQUAL(pszDirection, "north"))
12366 : {
12367 119 : ret = true;
12368 : }
12369 : }
12370 :
12371 168 : proj_destroy(cs);
12372 : }
12373 : }
12374 171 : d->undoDemoteFromBoundCRS();
12375 :
12376 171 : return ret;
12377 : }
12378 :
12379 : /************************************************************************/
12380 : /* OSREPSGTreatsAsLatLong() */
12381 : /************************************************************************/
12382 :
12383 : /**
12384 : * \brief This function returns TRUE if this geographic coordinate
12385 : * system should be treated as having lat/long coordinate ordering.
12386 : *
12387 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12388 : */
12389 :
12390 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12391 :
12392 : {
12393 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12394 :
12395 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12396 : }
12397 :
12398 : /************************************************************************/
12399 : /* EPSGTreatsAsNorthingEasting() */
12400 : /************************************************************************/
12401 :
12402 : /**
12403 : * \brief This method returns TRUE if this projected coordinate
12404 : * system should be treated as having northing/easting coordinate ordering.
12405 : *
12406 : * Currently this returns TRUE for all projected coordinate systems
12407 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12408 : * also checked that the CRS had belonged to EPSG authority, but this check
12409 : * has now been removed).
12410 : *
12411 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12412 : * projected CRS with northing, easting axis order imported with
12413 : * importFromEPSG() would cause this method to
12414 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12415 : * is now equivalent to importFromEPSGA().
12416 : *
12417 : * FALSE will be returned for all coordinate systems that are not projected,
12418 : * or whose axes ordering is not northing, easting.
12419 : *
12420 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12421 : *
12422 : * @return TRUE or FALSE.
12423 : *
12424 : */
12425 :
12426 754 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12427 :
12428 : {
12429 1508 : TAKE_OPTIONAL_LOCK();
12430 :
12431 754 : if (!IsProjected())
12432 46 : return FALSE;
12433 :
12434 708 : d->demoteFromBoundCRS();
12435 : PJ *projCRS;
12436 708 : const auto ctxt = d->getPROJContext();
12437 708 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12438 : {
12439 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12440 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12441 : {
12442 0 : d->undoDemoteFromBoundCRS();
12443 0 : proj_destroy(projCRS);
12444 0 : return FALSE;
12445 : }
12446 : }
12447 : else
12448 : {
12449 704 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12450 : }
12451 :
12452 708 : bool ret = false;
12453 708 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12454 708 : proj_destroy(projCRS);
12455 708 : d->undoDemoteFromBoundCRS();
12456 :
12457 708 : if (cs)
12458 : {
12459 708 : ret = isNorthEastAxisOrder(ctxt, cs);
12460 708 : proj_destroy(cs);
12461 : }
12462 :
12463 708 : return ret;
12464 : }
12465 :
12466 : /************************************************************************/
12467 : /* OSREPSGTreatsAsNorthingEasting() */
12468 : /************************************************************************/
12469 :
12470 : /**
12471 : * \brief This function returns TRUE if this projected coordinate
12472 : * system should be treated as having northing/easting coordinate ordering.
12473 : *
12474 : * This function is the same as
12475 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12476 : *
12477 : */
12478 :
12479 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12480 :
12481 : {
12482 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12483 :
12484 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12485 : }
12486 :
12487 : /************************************************************************/
12488 : /* ImportFromESRIWisconsinWKT() */
12489 : /* */
12490 : /* Search a ESRI State Plane WKT and import it. */
12491 : /************************************************************************/
12492 :
12493 : // This is only used by the HFA driver and somewhat dubious we really need that
12494 : // Coming from an old ESRI merge
12495 :
12496 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12497 : double centralMeridian,
12498 : double latOfOrigin,
12499 : const char *unitsName,
12500 : const char *crsName)
12501 : {
12502 2 : TAKE_OPTIONAL_LOCK();
12503 :
12504 1 : if (centralMeridian < -93 || centralMeridian > -87)
12505 0 : return OGRERR_FAILURE;
12506 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12507 0 : return OGRERR_FAILURE;
12508 :
12509 : // If the CS name is known.
12510 1 : if (!prjName && !unitsName && crsName)
12511 : {
12512 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12513 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12514 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12515 0 : if (list)
12516 : {
12517 0 : if (proj_list_get_count(list) == 1)
12518 : {
12519 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12520 0 : if (crs)
12521 : {
12522 0 : Clear();
12523 0 : d->setPjCRS(crs);
12524 0 : proj_list_destroy(list);
12525 0 : return OGRERR_NONE;
12526 : }
12527 : }
12528 0 : proj_list_destroy(list);
12529 : }
12530 0 : return OGRERR_FAILURE;
12531 : }
12532 :
12533 1 : if (prjName == nullptr || unitsName == nullptr)
12534 : {
12535 0 : return OGRERR_FAILURE;
12536 : }
12537 :
12538 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12539 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12540 : "NAD_1983_HARN_WISCRS_", &type, 1,
12541 : true, 0, nullptr);
12542 1 : if (list)
12543 : {
12544 1 : const auto listSize = proj_list_get_count(list);
12545 8 : for (int i = 0; i < listSize; i++)
12546 : {
12547 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12548 8 : if (!crs)
12549 : {
12550 7 : continue;
12551 : }
12552 :
12553 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12554 8 : if (!conv)
12555 : {
12556 0 : proj_destroy(crs);
12557 0 : continue;
12558 : }
12559 8 : const char *pszMethodCode = nullptr;
12560 8 : proj_coordoperation_get_method_info(
12561 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12562 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12563 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12564 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12565 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12566 : nMethodCode ==
12567 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12568 : {
12569 3 : proj_destroy(crs);
12570 3 : proj_destroy(conv);
12571 3 : continue;
12572 : }
12573 :
12574 : auto coordSys =
12575 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12576 5 : if (!coordSys)
12577 : {
12578 0 : proj_destroy(crs);
12579 0 : proj_destroy(conv);
12580 0 : continue;
12581 : }
12582 :
12583 5 : double dfConvFactor = 0.0;
12584 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12585 : nullptr, nullptr, &dfConvFactor, nullptr,
12586 : nullptr, nullptr);
12587 5 : proj_destroy(coordSys);
12588 :
12589 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12590 1 : (!EQUAL(unitsName, "meters") &&
12591 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12592 : 1e-10))
12593 : {
12594 4 : proj_destroy(crs);
12595 4 : proj_destroy(conv);
12596 4 : continue;
12597 : }
12598 :
12599 1 : int idx_lat = proj_coordoperation_get_param_index(
12600 : d->getPROJContext(), conv,
12601 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12602 1 : double valueLat = -1000;
12603 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12604 : nullptr, nullptr, nullptr, &valueLat,
12605 : nullptr, nullptr, nullptr, nullptr,
12606 : nullptr, nullptr);
12607 1 : int idx_lon = proj_coordoperation_get_param_index(
12608 : d->getPROJContext(), conv,
12609 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12610 1 : double valueLong = -1000;
12611 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12612 : nullptr, nullptr, nullptr, &valueLong,
12613 : nullptr, nullptr, nullptr, nullptr,
12614 : nullptr, nullptr);
12615 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12616 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12617 : {
12618 1 : Clear();
12619 1 : d->setPjCRS(crs);
12620 1 : proj_list_destroy(list);
12621 1 : proj_destroy(conv);
12622 1 : return OGRERR_NONE;
12623 : }
12624 :
12625 0 : proj_destroy(crs);
12626 0 : proj_destroy(conv);
12627 : }
12628 0 : proj_list_destroy(list);
12629 : }
12630 :
12631 0 : return OGRERR_FAILURE;
12632 : }
12633 :
12634 : /************************************************************************/
12635 : /* GetAxisMappingStrategy() */
12636 : /************************************************************************/
12637 :
12638 : /** \brief Return the data axis to CRS axis mapping strategy.
12639 : *
12640 : * <ul>
12641 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12642 : * lat/long order, the data will still be long/lat ordered. Similarly for
12643 : * a projected CRS with northing/easting order, the data will still be
12644 : * easting/northing ordered.
12645 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12646 : * the CRS axis.
12647 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12648 : * SetDataAxisToSRSAxisMapping()
12649 : * </ul>
12650 : * @return the data axis to CRS axis mapping strategy.
12651 : * @since GDAL 3.0
12652 : */
12653 72 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12654 : {
12655 72 : TAKE_OPTIONAL_LOCK();
12656 :
12657 144 : return d->m_axisMappingStrategy;
12658 : }
12659 :
12660 : /************************************************************************/
12661 : /* OSRGetAxisMappingStrategy() */
12662 : /************************************************************************/
12663 :
12664 : /** \brief Return the data axis to CRS axis mapping strategy.
12665 : *
12666 : * See OGRSpatialReference::GetAxisMappingStrategy()
12667 : * @since GDAL 3.0
12668 : */
12669 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12670 : {
12671 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12672 :
12673 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12674 : }
12675 :
12676 : /************************************************************************/
12677 : /* SetAxisMappingStrategy() */
12678 : /************************************************************************/
12679 :
12680 : /** \brief Set the data axis to CRS axis mapping strategy.
12681 : *
12682 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12683 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12684 : * later being the default value when the option is not set) to control the
12685 : * value of the data axis to CRS axis mapping strategy when a
12686 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12687 : * override this default value.
12688 : *
12689 : * See OGRSpatialReference::GetAxisMappingStrategy()
12690 : * @since GDAL 3.0
12691 : */
12692 86044 : void OGRSpatialReference::SetAxisMappingStrategy(
12693 : OSRAxisMappingStrategy strategy)
12694 : {
12695 171916 : TAKE_OPTIONAL_LOCK();
12696 :
12697 85922 : d->m_axisMappingStrategy = strategy;
12698 85903 : d->refreshAxisMapping();
12699 85904 : }
12700 :
12701 : /************************************************************************/
12702 : /* OSRSetAxisMappingStrategy() */
12703 : /************************************************************************/
12704 :
12705 : /** \brief Set the data axis to CRS axis mapping strategy.
12706 : *
12707 : * See OGRSpatialReference::SetAxisMappingStrategy()
12708 : * @since GDAL 3.0
12709 : */
12710 830 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12711 : OSRAxisMappingStrategy strategy)
12712 : {
12713 830 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12714 :
12715 830 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12716 : }
12717 :
12718 : /************************************************************************/
12719 : /* GetDataAxisToSRSAxisMapping() */
12720 : /************************************************************************/
12721 :
12722 : /** \brief Return the data axis to SRS axis mapping.
12723 : *
12724 : * The number of elements of the vector will be the number of axis of the CRS.
12725 : * Values start at 1.
12726 : *
12727 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12728 : * for the first axis of the CRS.
12729 : *
12730 : * @since GDAL 3.0
12731 : */
12732 4220540 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12733 : {
12734 4220540 : TAKE_OPTIONAL_LOCK();
12735 :
12736 8440300 : return d->m_axisMapping;
12737 : }
12738 :
12739 : /************************************************************************/
12740 : /* OSRGetDataAxisToSRSAxisMapping() */
12741 : /************************************************************************/
12742 :
12743 : /** \brief Return the data axis to SRS axis mapping.
12744 : *
12745 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12746 : *
12747 : * @since GDAL 3.0
12748 : */
12749 220 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12750 : int *pnCount)
12751 : {
12752 220 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12753 220 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12754 :
12755 : const auto &v =
12756 220 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12757 220 : *pnCount = static_cast<int>(v.size());
12758 220 : return v.data();
12759 : }
12760 :
12761 : /************************************************************************/
12762 : /* SetDataAxisToSRSAxisMapping() */
12763 : /************************************************************************/
12764 :
12765 : /** \brief Set a custom data axis to CRS axis mapping.
12766 : *
12767 : * The number of elements of the mapping vector should be the number of axis
12768 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12769 : * check that, beyond checking there are at least 2 elements, so that this
12770 : * method and setting the CRS can be done in any order).
12771 : * This is taken into account by OGRCoordinateTransformation to transform the
12772 : * order of coordinates to the order expected by the CRS before
12773 : * transformation, and back to the data order after transformation.
12774 : *
12775 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12776 : * axis of the CRS. A negative value can also be used to ask for a sign
12777 : * reversal during coordinate transformation (to deal with northing vs southing,
12778 : * easting vs westing, heights vs depths).
12779 : *
12780 : * When used with OGRCoordinateTransformation,
12781 : * - the only valid values for mapping[0] (data axis number for the first axis
12782 : * of the CRS) are 1, 2, -1, -2.
12783 : * - the only valid values for mapping[1] (data axis number for the second axis
12784 : * of the CRS) are 1, 2, -1, -2.
12785 : * - the only valid values mapping[2] are 3 or -3.
12786 : * Note: this method does not validate the values of mapping[].
12787 : *
12788 : * mapping=[2,1] typically expresses the inversion of axis between the data
12789 : * axis and the CRS axis for a 2D CRS.
12790 : *
12791 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12792 : *
12793 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12794 : *
12795 : * @param mapping The new data axis to CRS axis mapping.
12796 : *
12797 : * @since GDAL 3.0
12798 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12799 : */
12800 8493 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12801 : const std::vector<int> &mapping)
12802 : {
12803 16986 : TAKE_OPTIONAL_LOCK();
12804 :
12805 8493 : if (mapping.size() < 2)
12806 0 : return OGRERR_FAILURE;
12807 8493 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12808 8493 : d->m_axisMapping = mapping;
12809 8493 : return OGRERR_NONE;
12810 : }
12811 :
12812 : /************************************************************************/
12813 : /* OSRSetDataAxisToSRSAxisMapping() */
12814 : /************************************************************************/
12815 :
12816 : /** \brief Set a custom data axis to CRS axis mapping.
12817 : *
12818 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12819 : *
12820 : * This is the same as the C++ method
12821 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12822 : *
12823 : * @since GDAL 3.1
12824 : */
12825 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12826 : int nMappingSize, const int *panMapping)
12827 : {
12828 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12829 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12830 : OGRERR_FAILURE);
12831 :
12832 15 : if (nMappingSize < 0)
12833 0 : return OGRERR_FAILURE;
12834 :
12835 30 : std::vector<int> mapping(nMappingSize);
12836 15 : if (nMappingSize)
12837 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12838 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12839 15 : mapping);
12840 : }
12841 :
12842 : /************************************************************************/
12843 : /* GetAreaOfUse() */
12844 : /************************************************************************/
12845 :
12846 : /** \brief Return the area of use of the CRS.
12847 : *
12848 : * This method is the same as the OSRGetAreaOfUse() function.
12849 : *
12850 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12851 : * longitude, expressed in degree. Might be NULL. If the returned value is
12852 : * -1000, the bounding box is unknown.
12853 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12854 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12855 : * the bounding box is unknown.
12856 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12857 : * longitude, expressed in degree. Might be NULL. If the returned value is
12858 : * -1000, the bounding box is unknown.
12859 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12860 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12861 : * the bounding box is unknown.
12862 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12863 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12864 : * invalidated by further calls.
12865 : * @return true in case of success
12866 : * @since GDAL 3.0
12867 : */
12868 32 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12869 : double *pdfSouthLatitudeDeg,
12870 : double *pdfEastLongitudeDeg,
12871 : double *pdfNorthLatitudeDeg,
12872 : const char **ppszAreaName) const
12873 : {
12874 64 : TAKE_OPTIONAL_LOCK();
12875 :
12876 32 : d->refreshProjObj();
12877 32 : if (!d->m_pj_crs)
12878 : {
12879 0 : return false;
12880 : }
12881 32 : d->demoteFromBoundCRS();
12882 32 : const char *pszAreaName = nullptr;
12883 32 : int bSuccess = proj_get_area_of_use(
12884 32 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12885 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12886 : &pszAreaName);
12887 32 : d->undoDemoteFromBoundCRS();
12888 32 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12889 32 : if (ppszAreaName)
12890 1 : *ppszAreaName = d->m_osAreaName.c_str();
12891 32 : return CPL_TO_BOOL(bSuccess);
12892 : }
12893 :
12894 : /************************************************************************/
12895 : /* GetAreaOfUse() */
12896 : /************************************************************************/
12897 :
12898 : /** \brief Return the area of use of the CRS.
12899 : *
12900 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12901 : *
12902 : * @since GDAL 3.0
12903 : */
12904 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12905 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12906 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12907 : {
12908 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12909 :
12910 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12911 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12912 1 : pdfNorthLatitudeDeg, ppszAreaName);
12913 : }
12914 :
12915 : /************************************************************************/
12916 : /* OSRGetCRSInfoListFromDatabase() */
12917 : /************************************************************************/
12918 :
12919 : /** \brief Enumerate CRS objects from the database.
12920 : *
12921 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12922 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12923 : *
12924 : * @param pszAuthName Authority name, used to restrict the search.
12925 : * Or NULL for all authorities.
12926 : * @param params Additional criteria. Must be set to NULL for now.
12927 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12928 : * the size of the result list. Might be NULL
12929 : * @return an array of OSRCRSInfo* pointers to be freed with
12930 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12931 : *
12932 : * @since GDAL 3.0
12933 : */
12934 : OSRCRSInfo **
12935 24 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12936 : CPL_UNUSED const OSRCRSListParameters *params,
12937 : int *pnOutResultCount)
12938 : {
12939 24 : int nResultCount = 0;
12940 24 : auto projList = proj_get_crs_info_list_from_database(
12941 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12942 24 : if (pnOutResultCount)
12943 24 : *pnOutResultCount = nResultCount;
12944 24 : if (!projList)
12945 : {
12946 0 : return nullptr;
12947 : }
12948 24 : auto res = new OSRCRSInfo *[nResultCount + 1];
12949 89181 : for (int i = 0; i < nResultCount; i++)
12950 : {
12951 89157 : res[i] = new OSRCRSInfo;
12952 178314 : res[i]->pszAuthName = projList[i]->auth_name
12953 89157 : ? CPLStrdup(projList[i]->auth_name)
12954 : : nullptr;
12955 89157 : res[i]->pszCode =
12956 89157 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12957 89157 : res[i]->pszName =
12958 89157 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12959 89157 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12960 89157 : switch (projList[i]->type)
12961 : {
12962 8727 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12963 8727 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12964 8727 : break;
12965 2811 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12966 2811 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12967 2811 : break;
12968 3066 : case PJ_TYPE_GEOCENTRIC_CRS:
12969 3066 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12970 3066 : break;
12971 67740 : case PJ_TYPE_PROJECTED_CRS:
12972 67740 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12973 67740 : break;
12974 2808 : case PJ_TYPE_VERTICAL_CRS:
12975 2808 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12976 2808 : break;
12977 4005 : case PJ_TYPE_COMPOUND_CRS:
12978 4005 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12979 4005 : break;
12980 0 : default:
12981 0 : break;
12982 : }
12983 89157 : res[i]->bDeprecated = projList[i]->deprecated;
12984 89157 : res[i]->bBboxValid = projList[i]->bbox_valid;
12985 89157 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12986 89157 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12987 89157 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12988 89157 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12989 178314 : res[i]->pszAreaName = projList[i]->area_name
12990 89157 : ? CPLStrdup(projList[i]->area_name)
12991 : : nullptr;
12992 89157 : res[i]->pszProjectionMethod =
12993 89157 : projList[i]->projection_method_name
12994 89157 : ? CPLStrdup(projList[i]->projection_method_name)
12995 : : nullptr;
12996 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
12997 : res[i]->pszCelestialBodyName =
12998 : projList[i]->celestial_body_name
12999 : ? CPLStrdup(projList[i]->celestial_body_name)
13000 : : nullptr;
13001 : #else
13002 89157 : res[i]->pszCelestialBodyName =
13003 89157 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13004 178314 : ? CPLStrdup("Earth")
13005 : : nullptr;
13006 : #endif
13007 : }
13008 24 : res[nResultCount] = nullptr;
13009 24 : proj_crs_info_list_destroy(projList);
13010 24 : return res;
13011 : }
13012 :
13013 : /************************************************************************/
13014 : /* OSRDestroyCRSInfoList() */
13015 : /************************************************************************/
13016 :
13017 : /** \brief Destroy the result returned by
13018 : * OSRGetCRSInfoListFromDatabase().
13019 : *
13020 : * @since GDAL 3.0
13021 : */
13022 24 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13023 : {
13024 24 : if (list)
13025 : {
13026 89181 : for (int i = 0; list[i] != nullptr; i++)
13027 : {
13028 89157 : CPLFree(list[i]->pszAuthName);
13029 89157 : CPLFree(list[i]->pszCode);
13030 89157 : CPLFree(list[i]->pszName);
13031 89157 : CPLFree(list[i]->pszAreaName);
13032 89157 : CPLFree(list[i]->pszProjectionMethod);
13033 89157 : CPLFree(list[i]->pszCelestialBodyName);
13034 89157 : delete list[i];
13035 : }
13036 24 : delete[] list;
13037 : }
13038 24 : }
13039 :
13040 : /************************************************************************/
13041 : /* OSRGetAuthorityListFromDatabase() */
13042 : /************************************************************************/
13043 :
13044 : /** \brief Return the list of CRS authorities used in the PROJ database.
13045 : *
13046 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13047 : *
13048 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13049 : *
13050 : * @return nullptr in case of error, or a NULL terminated list of strings to
13051 : * free with CSLDestroy()
13052 : * @since GDAL 3.10
13053 : */
13054 4 : char **OSRGetAuthorityListFromDatabase()
13055 : {
13056 : PROJ_STRING_LIST list =
13057 4 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13058 4 : if (!list)
13059 : {
13060 0 : return nullptr;
13061 : }
13062 4 : int count = 0;
13063 24 : while (list[count])
13064 20 : ++count;
13065 4 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13066 24 : for (int i = 0; i < count; ++i)
13067 20 : res[i] = CPLStrdup(list[i]);
13068 4 : proj_string_list_destroy(list);
13069 4 : return res;
13070 : }
13071 :
13072 : /************************************************************************/
13073 : /* UpdateCoordinateSystemFromGeogCRS() */
13074 : /************************************************************************/
13075 :
13076 : /*! @cond Doxygen_Suppress */
13077 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13078 : *
13079 : * @since GDAL 3.1
13080 : */
13081 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13082 : {
13083 1 : TAKE_OPTIONAL_LOCK();
13084 :
13085 1 : d->refreshProjObj();
13086 1 : if (!d->m_pj_crs)
13087 0 : return;
13088 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13089 0 : return;
13090 1 : if (GetAxesCount() == 3)
13091 0 : return;
13092 1 : auto ctxt = d->getPROJContext();
13093 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13094 1 : if (!baseCRS)
13095 0 : return;
13096 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13097 1 : if (!baseCRSCS)
13098 : {
13099 0 : proj_destroy(baseCRS);
13100 0 : return;
13101 : }
13102 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13103 : {
13104 0 : proj_destroy(baseCRSCS);
13105 0 : proj_destroy(baseCRS);
13106 0 : return;
13107 : }
13108 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13109 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13110 : {
13111 0 : proj_destroy(baseCRSCS);
13112 0 : proj_destroy(baseCRS);
13113 0 : proj_destroy(projCS);
13114 0 : return;
13115 : }
13116 :
13117 : PJ_AXIS_DESCRIPTION axis[3];
13118 4 : for (int i = 0; i < 3; i++)
13119 : {
13120 3 : const char *name = nullptr;
13121 3 : const char *abbreviation = nullptr;
13122 3 : const char *direction = nullptr;
13123 3 : double unit_conv_factor = 0;
13124 3 : const char *unit_name = nullptr;
13125 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13126 : &abbreviation, &direction, &unit_conv_factor,
13127 : &unit_name, nullptr, nullptr);
13128 3 : axis[i].name = CPLStrdup(name);
13129 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13130 3 : axis[i].direction = CPLStrdup(direction);
13131 3 : axis[i].unit_name = CPLStrdup(unit_name);
13132 3 : axis[i].unit_conv_factor = unit_conv_factor;
13133 3 : axis[i].unit_type = PJ_UT_LINEAR;
13134 : }
13135 1 : proj_destroy(baseCRSCS);
13136 1 : proj_destroy(projCS);
13137 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13138 4 : for (int i = 0; i < 3; i++)
13139 : {
13140 3 : CPLFree(axis[i].name);
13141 3 : CPLFree(axis[i].abbreviation);
13142 3 : CPLFree(axis[i].direction);
13143 3 : CPLFree(axis[i].unit_name);
13144 : }
13145 1 : if (!cs)
13146 : {
13147 0 : proj_destroy(baseCRS);
13148 0 : return;
13149 : }
13150 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13151 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13152 : conversion, cs);
13153 1 : proj_destroy(baseCRS);
13154 1 : proj_destroy(conversion);
13155 1 : proj_destroy(cs);
13156 1 : d->setPjCRS(crs);
13157 : }
13158 :
13159 : /*! @endcond */
13160 :
13161 : /************************************************************************/
13162 : /* PromoteTo3D() */
13163 : /************************************************************************/
13164 :
13165 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13166 : *
13167 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13168 : * units.
13169 : *
13170 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13171 : * be used.
13172 : * @return OGRERR_NONE if no error occurred.
13173 : * @since GDAL 3.1 and PROJ 6.3
13174 : */
13175 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13176 : {
13177 84 : TAKE_OPTIONAL_LOCK();
13178 :
13179 42 : d->refreshProjObj();
13180 42 : if (!d->m_pj_crs)
13181 0 : return OGRERR_FAILURE;
13182 : auto newPj =
13183 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13184 42 : if (!newPj)
13185 0 : return OGRERR_FAILURE;
13186 42 : d->setPjCRS(newPj);
13187 42 : return OGRERR_NONE;
13188 : }
13189 :
13190 : /************************************************************************/
13191 : /* OSRPromoteTo3D() */
13192 : /************************************************************************/
13193 :
13194 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13195 : *
13196 : * See OGRSpatialReference::PromoteTo3D()
13197 : *
13198 : * @since GDAL 3.1 and PROJ 6.3
13199 : */
13200 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13201 : {
13202 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13203 :
13204 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13205 : }
13206 :
13207 : /************************************************************************/
13208 : /* DemoteTo2D() */
13209 : /************************************************************************/
13210 :
13211 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13212 : *
13213 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13214 : * be used.
13215 : * @return OGRERR_NONE if no error occurred.
13216 : * @since GDAL 3.2 and PROJ 6.3
13217 : */
13218 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13219 : {
13220 90 : TAKE_OPTIONAL_LOCK();
13221 :
13222 45 : d->refreshProjObj();
13223 45 : if (!d->m_pj_crs)
13224 0 : return OGRERR_FAILURE;
13225 : auto newPj =
13226 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13227 45 : if (!newPj)
13228 0 : return OGRERR_FAILURE;
13229 45 : d->setPjCRS(newPj);
13230 45 : return OGRERR_NONE;
13231 : }
13232 :
13233 : /************************************************************************/
13234 : /* OSRDemoteTo2D() */
13235 : /************************************************************************/
13236 :
13237 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13238 : *
13239 : * See OGRSpatialReference::DemoteTo2D()
13240 : *
13241 : * @since GDAL 3.2 and PROJ 6.3
13242 : */
13243 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13244 : {
13245 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13246 :
13247 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13248 : }
13249 :
13250 : /************************************************************************/
13251 : /* GetEPSGGeogCS() */
13252 : /************************************************************************/
13253 :
13254 : /** Try to establish what the EPSG code for this coordinate systems
13255 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13256 : *
13257 : * @return EPSG code
13258 : */
13259 :
13260 342 : int OGRSpatialReference::GetEPSGGeogCS() const
13261 :
13262 : {
13263 684 : TAKE_OPTIONAL_LOCK();
13264 :
13265 : /* -------------------------------------------------------------------- */
13266 : /* Check axis order. */
13267 : /* -------------------------------------------------------------------- */
13268 684 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13269 342 : if (!poGeogCRS)
13270 0 : return -1;
13271 :
13272 342 : bool ret = false;
13273 342 : poGeogCRS->d->demoteFromBoundCRS();
13274 342 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13275 342 : poGeogCRS->d->m_pj_crs);
13276 342 : poGeogCRS->d->undoDemoteFromBoundCRS();
13277 342 : if (cs)
13278 : {
13279 342 : const char *pszDirection = nullptr;
13280 342 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13281 : &pszDirection, nullptr, nullptr, nullptr,
13282 342 : nullptr))
13283 : {
13284 342 : if (EQUAL(pszDirection, "north"))
13285 : {
13286 143 : ret = true;
13287 : }
13288 : }
13289 :
13290 342 : proj_destroy(cs);
13291 : }
13292 342 : if (!ret)
13293 199 : return -1;
13294 :
13295 : /* -------------------------------------------------------------------- */
13296 : /* Do we already have it? */
13297 : /* -------------------------------------------------------------------- */
13298 143 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13299 143 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13300 66 : return atoi(GetAuthorityCode("GEOGCS"));
13301 :
13302 : /* -------------------------------------------------------------------- */
13303 : /* Get the datum and geogcs names. */
13304 : /* -------------------------------------------------------------------- */
13305 :
13306 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13307 77 : const char *pszDatum = GetAttrValue("DATUM");
13308 :
13309 : // We can only operate on coordinate systems with a geogcs.
13310 154 : OGRSpatialReference oSRSTmp;
13311 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13312 : {
13313 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13314 : // export to WKT1, so try to extract the geographic CRS through PROJ
13315 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13316 1 : oSRSTmp.CopyGeogCSFrom(this);
13317 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13318 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13319 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13320 : {
13321 0 : return -1;
13322 : }
13323 : }
13324 :
13325 : // Lookup geographic CRS name
13326 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13327 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13328 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13329 77 : if (list)
13330 : {
13331 77 : const auto listSize = proj_list_get_count(list);
13332 77 : if (listSize == 1)
13333 : {
13334 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13335 49 : if (crs)
13336 : {
13337 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13338 49 : const char *pszCode = proj_get_id_code(crs, 0);
13339 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13340 : {
13341 47 : const int nCode = atoi(pszCode);
13342 47 : proj_destroy(crs);
13343 47 : proj_list_destroy(list);
13344 47 : return nCode;
13345 : }
13346 2 : proj_destroy(crs);
13347 : }
13348 : }
13349 30 : proj_list_destroy(list);
13350 : }
13351 :
13352 : /* -------------------------------------------------------------------- */
13353 : /* Is this a "well known" geographic coordinate system? */
13354 : /* -------------------------------------------------------------------- */
13355 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13356 30 : strstr(pszDatum, "WGS") ||
13357 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13358 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13359 90 : strstr(pszDatum, "World Geodetic System") ||
13360 30 : strstr(pszDatum, "World_Geodetic_System");
13361 :
13362 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13363 30 : strstr(pszDatum, "NAD") ||
13364 30 : strstr(pszGEOGCS, "North American") ||
13365 30 : strstr(pszGEOGCS, "North_American") ||
13366 90 : strstr(pszDatum, "North American") ||
13367 30 : strstr(pszDatum, "North_American");
13368 :
13369 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13370 0 : return 4326;
13371 :
13372 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13373 0 : return 4322;
13374 :
13375 : // This is questionable as there are several 'flavors' of NAD83 that
13376 : // are not the same as 4269
13377 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13378 0 : return 4269;
13379 :
13380 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13381 0 : return 4267;
13382 :
13383 : /* -------------------------------------------------------------------- */
13384 : /* If we know the datum, associate the most likely GCS with */
13385 : /* it. */
13386 : /* -------------------------------------------------------------------- */
13387 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13388 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13389 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13390 0 : GetPrimeMeridian() == 0.0)
13391 : {
13392 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13393 :
13394 0 : if (nDatum >= 6000 && nDatum <= 6999)
13395 0 : return nDatum - 2000;
13396 : }
13397 :
13398 30 : return -1;
13399 : }
13400 :
13401 : /************************************************************************/
13402 : /* SetCoordinateEpoch() */
13403 : /************************************************************************/
13404 :
13405 : /** Set the coordinate epoch, as decimal year.
13406 : *
13407 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13408 : * change with time. To be unambiguous the coordinates must always be qualified
13409 : * with the epoch at which they are valid. The coordinate epoch is not
13410 : * necessarily the epoch at which the observation was collected.
13411 : *
13412 : * Pedantically the coordinate epoch of an observation belongs to the
13413 : * observation, and not to the CRS, however it is often more practical to
13414 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13415 : * CRS (see IsDynamic())
13416 : *
13417 : * This method is the same as the OSRSetCoordinateEpoch() function.
13418 : *
13419 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13420 : * @since OGR 3.4
13421 : */
13422 :
13423 837 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13424 : {
13425 837 : d->m_coordinateEpoch = dfCoordinateEpoch;
13426 837 : }
13427 :
13428 : /************************************************************************/
13429 : /* OSRSetCoordinateEpoch() */
13430 : /************************************************************************/
13431 :
13432 : /** \brief Set the coordinate epoch, as decimal year.
13433 : *
13434 : * See OGRSpatialReference::SetCoordinateEpoch()
13435 : *
13436 : * @since OGR 3.4
13437 : */
13438 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13439 : {
13440 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13441 :
13442 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13443 31 : dfCoordinateEpoch);
13444 : }
13445 :
13446 : /************************************************************************/
13447 : /* GetCoordinateEpoch() */
13448 : /************************************************************************/
13449 :
13450 : /** Return the coordinate epoch, as decimal year.
13451 : *
13452 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13453 : * change with time. To be unambiguous the coordinates must always be qualified
13454 : * with the epoch at which they are valid. The coordinate epoch is not
13455 : * necessarily the epoch at which the observation was collected.
13456 : *
13457 : * Pedantically the coordinate epoch of an observation belongs to the
13458 : * observation, and not to the CRS, however it is often more practical to
13459 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13460 : * CRS (see IsDynamic())
13461 : *
13462 : * This method is the same as the OSRGetCoordinateEpoch() function.
13463 : *
13464 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13465 : * if not set, or relevant.
13466 : * @since OGR 3.4
13467 : */
13468 :
13469 12579 : double OGRSpatialReference::GetCoordinateEpoch() const
13470 : {
13471 12579 : return d->m_coordinateEpoch;
13472 : }
13473 :
13474 : /************************************************************************/
13475 : /* OSRGetCoordinateEpoch() */
13476 : /************************************************************************/
13477 :
13478 : /** \brief Get the coordinate epoch, as decimal year.
13479 : *
13480 : * See OGRSpatialReference::GetCoordinateEpoch()
13481 : *
13482 : * @since OGR 3.4
13483 : */
13484 679 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13485 : {
13486 679 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13487 :
13488 679 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13489 : }
|