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 236579 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 236579 : }
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 1187140 : PJ_CONTEXT *getPROJContext()
159 : {
160 1187140 : 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 5262280 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 5262280 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 5262280 : }
184 :
185 5262280 : ~OptionalLockGuard()
186 5262280 : {
187 5262280 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 5262280 : }
190 : };
191 :
192 5262280 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 5262280 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2222910 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2222910 : m_poObj->nodesChanged();
201 2222910 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 236579 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 236579 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 236579 : 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 236578 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 236579 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 236579 : : m_poSelf(poSelf), m_poListener(std::make_shared<Listener>(this))
229 : {
230 : // Get the default value for m_axisMappingStrategy from the
231 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
232 236579 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
233 236579 : }
234 :
235 940924 : OGRSpatialReference::Private::~Private()
236 : {
237 : // In case we destroy the object not in the thread that created it,
238 : // we need to reassign the PROJ context. Having the context bundled inside
239 : // PJ* deeply sucks...
240 235231 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
241 : PJ_CONTEXT *ctxt;
242 235231 : if (GDALThreadLocalDatasetCacheIsInDestruction())
243 : {
244 180 : pj_context_to_destroy = proj_context_create();
245 180 : ctxt = pj_context_to_destroy;
246 : }
247 : else
248 : {
249 235051 : ctxt = getPROJContext();
250 : }
251 :
252 235231 : proj_assign_context(m_pj_crs, ctxt);
253 235231 : proj_destroy(m_pj_crs);
254 :
255 235231 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
256 235231 : proj_destroy(m_pj_geod_base_crs_temp);
257 :
258 235231 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
259 235231 : proj_destroy(m_pj_proj_crs_cs_temp);
260 :
261 235231 : proj_assign_context(m_pj_bound_crs_target, ctxt);
262 235231 : proj_destroy(m_pj_bound_crs_target);
263 :
264 235231 : proj_assign_context(m_pj_bound_crs_co, ctxt);
265 235231 : proj_destroy(m_pj_bound_crs_co);
266 :
267 235231 : proj_assign_context(m_pj_crs_backup, ctxt);
268 235231 : proj_destroy(m_pj_crs_backup);
269 :
270 235231 : delete m_poRootBackup;
271 235231 : delete m_poRoot;
272 235231 : proj_context_destroy(pj_context_to_destroy);
273 235231 : }
274 :
275 123463 : void OGRSpatialReference::Private::clear()
276 : {
277 123463 : proj_assign_context(m_pj_crs, getPROJContext());
278 123463 : proj_destroy(m_pj_crs);
279 123463 : m_pj_crs = nullptr;
280 :
281 123463 : delete m_poRoot;
282 123463 : m_poRoot = nullptr;
283 123463 : m_bNodesChanged = false;
284 :
285 123463 : m_wktImportWarnings.clear();
286 123463 : m_wktImportErrors.clear();
287 :
288 123463 : m_pj_crs_modified_during_demote = false;
289 123463 : m_pjType = PJ_TYPE_UNKNOWN;
290 123463 : m_osPrimeMeridianName.clear();
291 123463 : m_osAngularUnits.clear();
292 123463 : m_osLinearUnits.clear();
293 :
294 123463 : bNormInfoSet = FALSE;
295 123463 : dfFromGreenwich = 1.0;
296 123463 : dfToMeter = 1.0;
297 123463 : dfToDegrees = 1.0;
298 123463 : m_dfAngularUnitToRadian = 0.0;
299 :
300 123463 : m_bMorphToESRI = false;
301 123463 : m_bHasCenterLong = false;
302 :
303 123463 : m_coordinateEpoch = 0.0;
304 123463 : }
305 :
306 27802 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
307 : {
308 27802 : m_poRoot = poRoot;
309 27802 : if (m_poRoot)
310 : {
311 27802 : m_poRoot->RegisterListener(m_poListener);
312 : }
313 27802 : nodesChanged();
314 27802 : }
315 :
316 187144 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
317 : bool doRefreshAxisMapping)
318 : {
319 187144 : auto ctxt = getPROJContext();
320 :
321 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
322 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
323 : {
324 : const double dfEpoch =
325 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
326 : if (!std::isnan(dfEpoch))
327 : {
328 : m_poSelf->SetCoordinateEpoch(dfEpoch);
329 : }
330 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
331 : proj_destroy(pj_crsIn);
332 : pj_crsIn = crs;
333 : }
334 : #endif
335 :
336 187144 : proj_assign_context(m_pj_crs, ctxt);
337 187144 : proj_destroy(m_pj_crs);
338 187144 : m_pj_crs = pj_crsIn;
339 187144 : if (m_pj_crs)
340 : {
341 187093 : m_pjType = proj_get_type(m_pj_crs);
342 : }
343 187144 : if (m_pj_crs_backup)
344 : {
345 21 : m_pj_crs_modified_during_demote = true;
346 : }
347 187144 : invalidateNodes();
348 187144 : if (doRefreshAxisMapping)
349 : {
350 187124 : refreshAxisMapping();
351 : }
352 187144 : }
353 :
354 759691 : void OGRSpatialReference::Private::refreshProjObj()
355 : {
356 759691 : if (m_bNodesChanged && m_poRoot)
357 : {
358 8571 : char *pszWKT = nullptr;
359 8571 : m_poRoot->exportToWkt(&pszWKT);
360 8571 : auto poRootBackup = m_poRoot;
361 8571 : m_poRoot = nullptr;
362 8571 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
363 8571 : clear();
364 8571 : m_coordinateEpoch = dfCoordinateEpochBackup;
365 8571 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
366 :
367 8571 : const char *const options[] = {
368 : "STRICT=NO",
369 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
370 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
371 : #endif
372 : nullptr
373 : };
374 8571 : PROJ_STRING_LIST warnings = nullptr;
375 8571 : PROJ_STRING_LIST errors = nullptr;
376 8571 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
377 : &warnings, &errors));
378 16745 : for (auto iter = warnings; iter && *iter; ++iter)
379 : {
380 8174 : m_wktImportWarnings.push_back(*iter);
381 : }
382 8786 : for (auto iter = errors; iter && *iter; ++iter)
383 : {
384 215 : m_wktImportErrors.push_back(*iter);
385 : }
386 8571 : proj_string_list_destroy(warnings);
387 8571 : proj_string_list_destroy(errors);
388 :
389 8571 : CPLFree(pszWKT);
390 :
391 8571 : m_poRoot = poRootBackup;
392 8571 : m_bNodesChanged = false;
393 : }
394 759691 : }
395 :
396 29927 : void OGRSpatialReference::Private::refreshRootFromProjObj()
397 : {
398 29927 : CPLAssert(m_poRoot == nullptr);
399 :
400 29927 : if (m_pj_crs)
401 : {
402 55516 : CPLStringList aosOptions;
403 27758 : if (!m_bMorphToESRI)
404 : {
405 27754 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
406 27754 : aosOptions.SetNameValue("MULTILINE", "NO");
407 : }
408 27758 : aosOptions.SetNameValue("STRICT", "NO");
409 :
410 : const char *pszWKT;
411 : {
412 27758 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
413 27758 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
414 27758 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
415 27758 : aosOptions.List());
416 27758 : m_bNodesWKT2 = false;
417 : }
418 27758 : if (!m_bMorphToESRI && pszWKT == nullptr)
419 : {
420 96 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
421 96 : aosOptions.List());
422 96 : m_bNodesWKT2 = true;
423 : }
424 27758 : if (pszWKT)
425 : {
426 27758 : auto root = new OGR_SRSNode();
427 27758 : setRoot(root);
428 27758 : root->importFromWkt(&pszWKT);
429 27758 : m_bNodesChanged = false;
430 : }
431 : }
432 29927 : }
433 :
434 216335 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
435 : {
436 216335 : const char *pszName1 = nullptr;
437 216335 : const char *pszDirection1 = nullptr;
438 216335 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
439 : nullptr, nullptr, nullptr, nullptr);
440 216335 : const char *pszName2 = nullptr;
441 216335 : const char *pszDirection2 = nullptr;
442 216335 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
443 : nullptr, nullptr, nullptr, nullptr);
444 216335 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
445 95557 : EQUAL(pszDirection2, "east"))
446 : {
447 94134 : return true;
448 : }
449 122201 : if (pszDirection1 && pszDirection2 &&
450 122201 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
451 120795 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
452 2872 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
453 1099 : STARTS_WITH_CI(pszName2, "easting"))
454 : {
455 1099 : return true;
456 : }
457 121102 : return false;
458 : }
459 :
460 274808 : void OGRSpatialReference::Private::refreshAxisMapping()
461 : {
462 274808 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
463 59039 : return;
464 :
465 215769 : bool doUndoDemote = false;
466 215769 : if (m_pj_crs_backup == nullptr)
467 : {
468 215748 : doUndoDemote = true;
469 215748 : demoteFromBoundCRS();
470 : }
471 215769 : const auto ctxt = getPROJContext();
472 215769 : PJ *horizCRS = nullptr;
473 215769 : int axisCount = 0;
474 215769 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
475 : {
476 218 : axisCount = 1;
477 : }
478 215551 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
479 : {
480 1107 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
481 1107 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
482 : {
483 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
484 222 : if (baseCRS)
485 : {
486 222 : proj_destroy(horizCRS);
487 222 : horizCRS = baseCRS;
488 : }
489 : }
490 :
491 1107 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
492 1107 : if (vertCRS)
493 : {
494 1104 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
495 : {
496 398 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
497 398 : if (baseCRS)
498 : {
499 398 : proj_destroy(vertCRS);
500 398 : vertCRS = baseCRS;
501 : }
502 : }
503 :
504 1104 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
505 1104 : if (cs)
506 : {
507 1104 : axisCount += proj_cs_get_axis_count(ctxt, cs);
508 1104 : proj_destroy(cs);
509 : }
510 1104 : proj_destroy(vertCRS);
511 : }
512 : }
513 : else
514 : {
515 214444 : horizCRS = m_pj_crs;
516 : }
517 :
518 215769 : bool bSwitchForGisFriendlyOrder = false;
519 215769 : if (horizCRS)
520 : {
521 215548 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
522 215548 : if (cs)
523 : {
524 215548 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
525 215548 : axisCount += nHorizCSAxisCount;
526 215548 : if (nHorizCSAxisCount >= 2)
527 : {
528 215538 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
529 : }
530 215548 : proj_destroy(cs);
531 : }
532 : }
533 215769 : if (horizCRS != m_pj_crs)
534 : {
535 1325 : proj_destroy(horizCRS);
536 : }
537 215769 : if (doUndoDemote)
538 : {
539 215748 : undoDemoteFromBoundCRS();
540 : }
541 :
542 215769 : m_axisMapping.resize(axisCount);
543 215769 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
544 63274 : !bSwitchForGisFriendlyOrder)
545 : {
546 562770 : for (int i = 0; i < axisCount; i++)
547 : {
548 375694 : m_axisMapping[i] = i + 1;
549 187076 : }
550 : }
551 : else
552 : {
553 28693 : m_axisMapping[0] = 2;
554 28693 : m_axisMapping[1] = 1;
555 28693 : if (axisCount == 3)
556 : {
557 343 : m_axisMapping[2] = 3;
558 : }
559 : }
560 : }
561 :
562 2250710 : void OGRSpatialReference::Private::nodesChanged()
563 : {
564 2250710 : m_bNodesChanged = true;
565 2250710 : }
566 :
567 187400 : void OGRSpatialReference::Private::invalidateNodes()
568 : {
569 187400 : delete m_poRoot;
570 187400 : m_poRoot = nullptr;
571 187400 : m_bNodesChanged = false;
572 187400 : }
573 :
574 256 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
575 : {
576 256 : invalidateNodes();
577 256 : m_bMorphToESRI = b;
578 256 : }
579 :
580 638926 : void OGRSpatialReference::Private::demoteFromBoundCRS()
581 : {
582 638926 : CPLAssert(m_pj_bound_crs_target == nullptr);
583 638926 : CPLAssert(m_pj_bound_crs_co == nullptr);
584 638926 : CPLAssert(m_poRootBackup == nullptr);
585 638926 : CPLAssert(m_pj_crs_backup == nullptr);
586 :
587 638926 : m_pj_crs_modified_during_demote = false;
588 :
589 638926 : if (m_pjType == PJ_TYPE_BOUND_CRS)
590 : {
591 2759 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
592 2759 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
593 2759 : m_pj_bound_crs_co =
594 2759 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
595 :
596 2759 : m_poRootBackup = m_poRoot;
597 2759 : m_poRoot = nullptr;
598 2759 : m_pj_crs_backup = m_pj_crs;
599 2759 : m_pj_crs = baseCRS;
600 2759 : m_pjType = proj_get_type(m_pj_crs);
601 : }
602 638926 : }
603 :
604 638926 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
605 : {
606 638926 : if (m_pj_bound_crs_target)
607 : {
608 2759 : CPLAssert(m_poRoot == nullptr);
609 2759 : CPLAssert(m_pj_crs);
610 2759 : if (!m_pj_crs_modified_during_demote)
611 : {
612 2739 : proj_destroy(m_pj_crs);
613 2739 : m_pj_crs = m_pj_crs_backup;
614 2739 : m_pjType = proj_get_type(m_pj_crs);
615 2739 : m_poRoot = m_poRootBackup;
616 : }
617 : else
618 : {
619 20 : delete m_poRootBackup;
620 20 : m_poRootBackup = nullptr;
621 20 : proj_destroy(m_pj_crs_backup);
622 20 : m_pj_crs_backup = nullptr;
623 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
624 20 : m_pj_bound_crs_target,
625 20 : m_pj_bound_crs_co),
626 : false);
627 : }
628 : }
629 :
630 638926 : m_poRootBackup = nullptr;
631 638926 : m_pj_crs_backup = nullptr;
632 638926 : proj_destroy(m_pj_bound_crs_target);
633 638926 : m_pj_bound_crs_target = nullptr;
634 638926 : proj_destroy(m_pj_bound_crs_co);
635 638926 : m_pj_bound_crs_co = nullptr;
636 638926 : m_pj_crs_modified_during_demote = false;
637 638926 : }
638 :
639 160256 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
640 : const char *pszTargetKey)
641 : {
642 160256 : if (pszTargetKey)
643 : {
644 59981 : demoteFromBoundCRS();
645 59981 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
646 30999 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
647 29060 : EQUAL(pszTargetKey, "GEOGCS"))
648 : {
649 7123 : pszTargetKey = nullptr;
650 : }
651 52858 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
652 20 : EQUAL(pszTargetKey, "GEOCCS"))
653 : {
654 0 : pszTargetKey = nullptr;
655 : }
656 52858 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
657 29614 : EQUAL(pszTargetKey, "PROJCS"))
658 : {
659 4121 : pszTargetKey = nullptr;
660 : }
661 48737 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
662 4 : EQUAL(pszTargetKey, "VERT_CS"))
663 : {
664 2 : pszTargetKey = nullptr;
665 : }
666 59981 : undoDemoteFromBoundCRS();
667 : }
668 160256 : return pszTargetKey;
669 : }
670 :
671 9587 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
672 : {
673 9587 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
674 9534 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
675 : {
676 53 : return m_pj_crs;
677 : }
678 :
679 9534 : auto ctxt = getPROJContext();
680 9534 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
681 : {
682 4389 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
683 4389 : proj_destroy(m_pj_geod_base_crs_temp);
684 4389 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
685 4389 : return m_pj_geod_base_crs_temp;
686 : }
687 :
688 5145 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
689 5145 : proj_destroy(m_pj_geod_base_crs_temp);
690 5145 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
691 : nullptr, 0);
692 5145 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
693 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
694 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
695 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
696 5145 : proj_destroy(cs);
697 :
698 5145 : return m_pj_geod_base_crs_temp;
699 : }
700 :
701 5346 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
702 : {
703 5346 : auto ctxt = getPROJContext();
704 5346 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
705 : {
706 4375 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
707 4375 : proj_destroy(m_pj_proj_crs_cs_temp);
708 4375 : m_pj_proj_crs_cs_temp =
709 4375 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
710 4375 : return m_pj_proj_crs_cs_temp;
711 : }
712 :
713 971 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
714 971 : proj_destroy(m_pj_proj_crs_cs_temp);
715 971 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
716 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
717 971 : return m_pj_proj_crs_cs_temp;
718 : }
719 :
720 5397 : const char *OGRSpatialReference::Private::getProjCRSName()
721 : {
722 5397 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
723 : {
724 4390 : return proj_get_name(m_pj_crs);
725 : }
726 :
727 1007 : return "unnamed";
728 : }
729 :
730 1378 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
731 : {
732 1378 : refreshProjObj();
733 :
734 1378 : demoteFromBoundCRS();
735 :
736 : auto projCRS =
737 1378 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
738 1378 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
739 1378 : proj_destroy(conv);
740 :
741 1378 : setPjCRS(projCRS);
742 :
743 1378 : undoDemoteFromBoundCRS();
744 1378 : return OGRERR_NONE;
745 : }
746 :
747 : /************************************************************************/
748 : /* ToPointer() */
749 : /************************************************************************/
750 :
751 26252 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
752 : {
753 26252 : return OGRSpatialReference::FromHandle(hSRS);
754 : }
755 :
756 : /************************************************************************/
757 : /* ToHandle() */
758 : /************************************************************************/
759 :
760 4511 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
761 : {
762 4511 : return OGRSpatialReference::ToHandle(poSRS);
763 : }
764 :
765 : /************************************************************************/
766 : /* OGRsnPrintDouble() */
767 : /************************************************************************/
768 :
769 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
770 :
771 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
772 :
773 : {
774 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
775 :
776 126 : const size_t nLen = strlen(pszStrBuf);
777 :
778 : // The following hack is intended to truncate some "precision" in cases
779 : // that appear to be roundoff error.
780 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
781 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
782 : {
783 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
784 : }
785 :
786 : // Force to user periods regardless of locale.
787 126 : if (strchr(pszStrBuf, ',') != nullptr)
788 : {
789 0 : char *const pszDelim = strchr(pszStrBuf, ',');
790 0 : *pszDelim = '.';
791 : }
792 126 : }
793 :
794 : /************************************************************************/
795 : /* OGRSpatialReference() */
796 : /************************************************************************/
797 :
798 : /**
799 : * \brief Constructor.
800 : *
801 : * This constructor takes an optional string argument which if passed
802 : * should be a WKT representation of an SRS. Passing this is equivalent
803 : * to not passing it, and then calling importFromWkt() with the WKT string.
804 : *
805 : * Note that newly created objects are given a reference count of one.
806 : *
807 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
808 : * object are assumed to be in the order of the axis of the CRS definition
809 : (which
810 : * for example means latitude first, longitude second for geographic CRS
811 : belonging
812 : * to the EPSG authority). It is possible to define a data axis to CRS axis
813 : * mapping strategy with the SetAxisMappingStrategy() method.
814 : *
815 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
816 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
817 : later
818 : * being the default value when the option is not set) to control the value of
819 : the
820 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
821 : * created. Calling SetAxisMappingStrategy() will override this default value.
822 :
823 : * The C function OSRNewSpatialReference() does the same thing as this
824 : * constructor.
825 : *
826 : * @param pszWKT well known text definition to which the object should
827 : * be initialized, or NULL (the default).
828 : */
829 :
830 234035 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
831 234035 : : d(new Private(this))
832 : {
833 234035 : if (pszWKT != nullptr)
834 281 : importFromWkt(pszWKT);
835 234035 : }
836 :
837 : /************************************************************************/
838 : /* OSRNewSpatialReference() */
839 : /************************************************************************/
840 :
841 : /**
842 : * \brief Constructor.
843 : *
844 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
845 : * object are assumed to be in the order of the axis of the CRS definition
846 : * (which for example means latitude first, longitude second for geographic CRS
847 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
848 : * axis mapping strategy with the SetAxisMappingStrategy() method.
849 : *
850 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
851 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
852 : * later being the default value when the option is not set) to control the
853 : * value of the data axis to CRS axis mapping strategy when a
854 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
855 : * override this default value.
856 : *
857 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
858 : */
859 3190 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
860 :
861 : {
862 3190 : OGRSpatialReference *poSRS = new OGRSpatialReference();
863 :
864 3190 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
865 : {
866 65 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
867 : {
868 0 : delete poSRS;
869 0 : poSRS = nullptr;
870 : }
871 : }
872 :
873 3190 : return ToHandle(poSRS);
874 : }
875 :
876 : /************************************************************************/
877 : /* OGRSpatialReference() */
878 : /************************************************************************/
879 :
880 : /** Copy constructor. See also Clone().
881 : * @param oOther other spatial reference
882 : */
883 2544 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
884 2544 : : d(new Private(this))
885 : {
886 2544 : *this = oOther;
887 2544 : }
888 :
889 : /************************************************************************/
890 : /* OGRSpatialReference() */
891 : /************************************************************************/
892 :
893 : /** Move constructor.
894 : * @param oOther other spatial reference
895 : */
896 29 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
897 29 : : d(std::move(oOther.d))
898 : {
899 29 : }
900 :
901 : /************************************************************************/
902 : /* ~OGRSpatialReference() */
903 : /************************************************************************/
904 :
905 : /**
906 : * \brief OGRSpatialReference destructor.
907 : *
908 : * The C function OSRDestroySpatialReference() does the same thing as this
909 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
910 : *
911 : * @deprecated
912 : */
913 :
914 293474 : OGRSpatialReference::~OGRSpatialReference()
915 :
916 : {
917 293474 : }
918 :
919 : /************************************************************************/
920 : /* DestroySpatialReference() */
921 : /************************************************************************/
922 :
923 : /**
924 : * \brief OGRSpatialReference destructor.
925 : *
926 : * This static method will destroy a OGRSpatialReference. It is
927 : * equivalent to calling delete on the object, but it ensures that the
928 : * deallocation is properly executed within the OGR libraries heap on
929 : * platforms where this can matter (win32).
930 : *
931 : * This function is the same as OSRDestroySpatialReference()
932 : *
933 : * @param poSRS the object to delete
934 : *
935 : */
936 :
937 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
938 : {
939 0 : delete poSRS;
940 0 : }
941 :
942 : /************************************************************************/
943 : /* OSRDestroySpatialReference() */
944 : /************************************************************************/
945 :
946 : /**
947 : * \brief OGRSpatialReference destructor.
948 : *
949 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
950 : * and OGRSpatialReference::DestroySpatialReference()
951 : *
952 : * @param hSRS the object to delete
953 : */
954 9428 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
955 :
956 : {
957 9428 : delete ToPointer(hSRS);
958 9428 : }
959 :
960 : /************************************************************************/
961 : /* Clear() */
962 : /************************************************************************/
963 :
964 : /**
965 : * \brief Wipe current definition.
966 : *
967 : * Returns OGRSpatialReference to a state with no definition, as it
968 : * exists when first created. It does not affect reference counts.
969 : */
970 :
971 114892 : void OGRSpatialReference::Clear()
972 :
973 : {
974 114892 : d->clear();
975 114892 : }
976 :
977 : /************************************************************************/
978 : /* operator=() */
979 : /************************************************************************/
980 :
981 : /** Assignment operator.
982 : * @param oSource SRS to assign to *this
983 : * @return *this
984 : */
985 : OGRSpatialReference &
986 26899 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
987 :
988 : {
989 26899 : if (&oSource != this)
990 : {
991 26898 : Clear();
992 : #ifdef CPPCHECK
993 : // Otherwise cppcheck would protest that nRefCount isn't modified
994 : d->nRefCount = (d->nRefCount + 1) - 1;
995 : #endif
996 :
997 26898 : oSource.d->refreshProjObj();
998 26898 : if (oSource.d->m_pj_crs)
999 26543 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1000 26898 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1001 12251 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1002 14647 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1003 124 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1004 :
1005 26898 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1006 : }
1007 :
1008 26899 : return *this;
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* operator=() */
1013 : /************************************************************************/
1014 :
1015 : /** Move assignment operator.
1016 : * @param oSource SRS to assign to *this
1017 : * @return *this
1018 : */
1019 : OGRSpatialReference &
1020 4694 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1021 :
1022 : {
1023 4694 : if (&oSource != this)
1024 : {
1025 4693 : d = std::move(oSource.d);
1026 : }
1027 :
1028 4694 : return *this;
1029 : }
1030 :
1031 : /************************************************************************/
1032 : /* AssignAndSetThreadSafe() */
1033 : /************************************************************************/
1034 :
1035 : /** Assignment method, with thread-safety.
1036 : *
1037 : * Same as an assignment operator, but asking also that the *this instance
1038 : * becomes thread-safe.
1039 : *
1040 : * @param oSource SRS to assign to *this
1041 : * @return *this
1042 : * @since 3.10
1043 : */
1044 :
1045 : OGRSpatialReference &
1046 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1047 : {
1048 2 : *this = oSource;
1049 2 : d->SetThreadSafe();
1050 2 : return *this;
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* Reference() */
1055 : /************************************************************************/
1056 :
1057 : /**
1058 : * \brief Increments the reference count by one.
1059 : *
1060 : * The reference count is used keep track of the number of OGRGeometry objects
1061 : * referencing this SRS.
1062 : *
1063 : * The method does the same thing as the C function OSRReference().
1064 : *
1065 : * @return the updated reference count.
1066 : */
1067 :
1068 3627890 : int OGRSpatialReference::Reference()
1069 :
1070 : {
1071 3627890 : return CPLAtomicInc(&d->nRefCount);
1072 : }
1073 :
1074 : /************************************************************************/
1075 : /* OSRReference() */
1076 : /************************************************************************/
1077 :
1078 : /**
1079 : * \brief Increments the reference count by one.
1080 : *
1081 : * This function is the same as OGRSpatialReference::Reference()
1082 : */
1083 1001 : int OSRReference(OGRSpatialReferenceH hSRS)
1084 :
1085 : {
1086 1001 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1087 :
1088 1001 : return ToPointer(hSRS)->Reference();
1089 : }
1090 :
1091 : /************************************************************************/
1092 : /* Dereference() */
1093 : /************************************************************************/
1094 :
1095 : /**
1096 : * \brief Decrements the reference count by one.
1097 : *
1098 : * The method does the same thing as the C function OSRDereference().
1099 : *
1100 : * @return the updated reference count.
1101 : */
1102 :
1103 3669850 : int OGRSpatialReference::Dereference()
1104 :
1105 : {
1106 3669850 : if (d->nRefCount <= 0)
1107 0 : CPLDebug("OSR",
1108 : "Dereference() called on an object with refcount %d,"
1109 : "likely already destroyed!",
1110 0 : d->nRefCount);
1111 3669850 : return CPLAtomicDec(&d->nRefCount);
1112 : }
1113 :
1114 : /************************************************************************/
1115 : /* OSRDereference() */
1116 : /************************************************************************/
1117 :
1118 : /**
1119 : * \brief Decrements the reference count by one.
1120 : *
1121 : * This function is the same as OGRSpatialReference::Dereference()
1122 : */
1123 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1124 :
1125 : {
1126 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1127 :
1128 0 : return ToPointer(hSRS)->Dereference();
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* GetReferenceCount() */
1133 : /************************************************************************/
1134 :
1135 : /**
1136 : * \brief Fetch current reference count.
1137 : *
1138 : * @return the current reference count.
1139 : */
1140 180 : int OGRSpatialReference::GetReferenceCount() const
1141 : {
1142 180 : return d->nRefCount;
1143 : }
1144 :
1145 : /************************************************************************/
1146 : /* Release() */
1147 : /************************************************************************/
1148 :
1149 : /**
1150 : * \brief Decrements the reference count by one, and destroy if zero.
1151 : *
1152 : * The method does the same thing as the C function OSRRelease().
1153 : */
1154 :
1155 3667080 : void OGRSpatialReference::Release()
1156 :
1157 : {
1158 3667080 : if (Dereference() <= 0)
1159 41910 : delete this;
1160 3667080 : }
1161 :
1162 : /************************************************************************/
1163 : /* OSRRelease() */
1164 : /************************************************************************/
1165 :
1166 : /**
1167 : * \brief Decrements the reference count by one, and destroy if zero.
1168 : *
1169 : * This function is the same as OGRSpatialReference::Release()
1170 : */
1171 6500 : void OSRRelease(OGRSpatialReferenceH hSRS)
1172 :
1173 : {
1174 6500 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1175 :
1176 6500 : ToPointer(hSRS)->Release();
1177 : }
1178 :
1179 91121 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1180 : {
1181 91121 : TAKE_OPTIONAL_LOCK();
1182 :
1183 91121 : if (!d->m_poRoot)
1184 : {
1185 26895 : d->refreshRootFromProjObj();
1186 : }
1187 182242 : return d->m_poRoot;
1188 : }
1189 :
1190 8169 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1191 : {
1192 8169 : TAKE_OPTIONAL_LOCK();
1193 :
1194 8169 : if (!d->m_poRoot)
1195 : {
1196 3032 : d->refreshRootFromProjObj();
1197 : }
1198 16338 : return d->m_poRoot;
1199 : }
1200 :
1201 : /************************************************************************/
1202 : /* SetRoot() */
1203 : /************************************************************************/
1204 :
1205 : /**
1206 : * \brief Set the root SRS node.
1207 : *
1208 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1209 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1210 : * is assumed by the OGRSpatialReference.
1211 : *
1212 : * @param poNewRoot object to assign as root.
1213 : */
1214 :
1215 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1216 :
1217 : {
1218 44 : if (d->m_poRoot != poNewRoot)
1219 : {
1220 44 : delete d->m_poRoot;
1221 44 : d->setRoot(poNewRoot);
1222 : }
1223 44 : }
1224 :
1225 : /************************************************************************/
1226 : /* GetAttrNode() */
1227 : /************************************************************************/
1228 :
1229 : /**
1230 : * \brief Find named node in tree.
1231 : *
1232 : * This method does a pre-order traversal of the node tree searching for
1233 : * a node with this exact value (case insensitive), and returns it. Leaf
1234 : * nodes are not considered, under the assumption that they are just
1235 : * attribute value nodes.
1236 : *
1237 : * If a node appears more than once in the tree (such as UNIT for instance),
1238 : * the first encountered will be returned. Use GetNode() on a subtree to be
1239 : * more specific.
1240 : *
1241 : * @param pszNodePath the name of the node to search for. May contain multiple
1242 : * components such as "GEOGCS|UNIT".
1243 : *
1244 : * @return a pointer to the node found, or NULL if none.
1245 : */
1246 :
1247 87989 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1248 :
1249 : {
1250 87989 : if (strchr(pszNodePath, '|') == nullptr)
1251 : {
1252 : // Fast path
1253 48330 : OGR_SRSNode *poNode = GetRoot();
1254 48330 : if (poNode)
1255 47125 : poNode = poNode->GetNode(pszNodePath);
1256 48330 : return poNode;
1257 : }
1258 :
1259 : char **papszPathTokens =
1260 39659 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1261 :
1262 39659 : if (CSLCount(papszPathTokens) < 1)
1263 : {
1264 0 : CSLDestroy(papszPathTokens);
1265 0 : return nullptr;
1266 : }
1267 :
1268 39659 : OGR_SRSNode *poNode = GetRoot();
1269 120359 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1270 : {
1271 80700 : poNode = poNode->GetNode(papszPathTokens[i]);
1272 : }
1273 :
1274 39659 : CSLDestroy(papszPathTokens);
1275 :
1276 39659 : return poNode;
1277 : }
1278 :
1279 : /**
1280 : * \brief Find named node in tree.
1281 : *
1282 : * This method does a pre-order traversal of the node tree searching for
1283 : * a node with this exact value (case insensitive), and returns it. Leaf
1284 : * nodes are not considered, under the assumption that they are just
1285 : * attribute value nodes.
1286 : *
1287 : * If a node appears more than once in the tree (such as UNIT for instance),
1288 : * the first encountered will be returned. Use GetNode() on a subtree to be
1289 : * more specific.
1290 : *
1291 : * @param pszNodePath the name of the node to search for. May contain multiple
1292 : * components such as "GEOGCS|UNIT".
1293 : *
1294 : * @return a pointer to the node found, or NULL if none.
1295 : */
1296 :
1297 : const OGR_SRSNode *
1298 79677 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1299 :
1300 : {
1301 : OGR_SRSNode *poNode =
1302 79677 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1303 :
1304 79677 : return poNode;
1305 : }
1306 :
1307 : /************************************************************************/
1308 : /* GetAttrValue() */
1309 : /************************************************************************/
1310 :
1311 : /**
1312 : * \brief Fetch indicated attribute of named node.
1313 : *
1314 : * This method uses GetAttrNode() to find the named node, and then extracts
1315 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1316 : * would return the second child of the UNIT node, which is normally the
1317 : * length of the linear unit in meters.
1318 : *
1319 : * This method does the same thing as the C function OSRGetAttrValue().
1320 : *
1321 : * @param pszNodeName the tree node to look for (case insensitive).
1322 : * @param iAttr the child of the node to fetch (zero based).
1323 : *
1324 : * @return the requested value, or NULL if it fails for any reason.
1325 : */
1326 :
1327 24140 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1328 : int iAttr) const
1329 :
1330 : {
1331 24140 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1332 24140 : if (poNode == nullptr)
1333 : {
1334 10421 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1335 : {
1336 14 : return GetAttrValue("METHOD", iAttr);
1337 : }
1338 10407 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1339 : {
1340 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1341 : }
1342 10407 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1343 : {
1344 1 : return GetAttrValue("PROJCRS", iAttr);
1345 : }
1346 10406 : return nullptr;
1347 : }
1348 :
1349 13719 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1350 0 : return nullptr;
1351 :
1352 13719 : return poNode->GetChild(iAttr)->GetValue();
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* OSRGetAttrValue() */
1357 : /************************************************************************/
1358 :
1359 : /**
1360 : * \brief Fetch indicated attribute of named node.
1361 : *
1362 : * This function is the same as OGRSpatialReference::GetAttrValue()
1363 : */
1364 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1365 : const char *pszKey, int iChild)
1366 :
1367 : {
1368 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1369 :
1370 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1371 : }
1372 :
1373 : /************************************************************************/
1374 : /* GetName() */
1375 : /************************************************************************/
1376 :
1377 : /**
1378 : * \brief Return the CRS name.
1379 : *
1380 : * The returned value is only short lived and should not be used after other
1381 : * calls to methods on this object.
1382 : *
1383 : * @since GDAL 3.0
1384 : */
1385 :
1386 8128 : const char *OGRSpatialReference::GetName() const
1387 : {
1388 16256 : TAKE_OPTIONAL_LOCK();
1389 :
1390 8128 : d->refreshProjObj();
1391 8128 : if (!d->m_pj_crs)
1392 113 : return nullptr;
1393 8015 : const char *pszName = proj_get_name(d->m_pj_crs);
1394 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1395 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1396 : {
1397 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1398 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1399 : if (baseCRS)
1400 : {
1401 : pszName = proj_get_name(baseCRS);
1402 : // pszName still remains valid after proj_destroy(), since
1403 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1404 : proj_destroy(baseCRS);
1405 : }
1406 : }
1407 : #endif
1408 8015 : return pszName;
1409 : }
1410 :
1411 : /************************************************************************/
1412 : /* OSRGetName() */
1413 : /************************************************************************/
1414 :
1415 : /**
1416 : * \brief Return the CRS name.
1417 : *
1418 : * The returned value is only short lived and should not be used after other
1419 : * calls to methods on this object.
1420 : *
1421 : * @since GDAL 3.0
1422 : */
1423 44 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1424 :
1425 : {
1426 44 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1427 :
1428 44 : return ToPointer(hSRS)->GetName();
1429 : }
1430 :
1431 : /************************************************************************/
1432 : /* GetCelestialBodyName() */
1433 : /************************************************************************/
1434 :
1435 : /**
1436 : * \brief Return the name of the celestial body of this CRS.
1437 : *
1438 : * e.g. "Earth" for an Earth CRS
1439 : *
1440 : * The returned value is only short lived and should not be used after other
1441 : * calls to methods on this object.
1442 : *
1443 : * @since GDAL 3.12 and PROJ 8.1
1444 : */
1445 :
1446 4 : const char *OGRSpatialReference::GetCelestialBodyName() const
1447 : {
1448 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1449 :
1450 : TAKE_OPTIONAL_LOCK();
1451 :
1452 : d->refreshProjObj();
1453 : if (!d->m_pj_crs)
1454 : return nullptr;
1455 : d->demoteFromBoundCRS();
1456 : const char *name =
1457 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1458 : if (name)
1459 : {
1460 : d->m_celestialBodyName = name;
1461 : }
1462 : d->undoDemoteFromBoundCRS();
1463 : return d->m_celestialBodyName.c_str();
1464 : #else
1465 4 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1466 : 0.05 * SRS_WGS84_SEMIMAJOR)
1467 4 : return "Earth";
1468 0 : const char *pszAuthName = GetAuthorityName(nullptr);
1469 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1470 0 : return "Earth";
1471 0 : return nullptr;
1472 : #endif
1473 : }
1474 :
1475 : /************************************************************************/
1476 : /* OSRGetCelestialBodyName() */
1477 : /************************************************************************/
1478 :
1479 : /**
1480 : * \brief Return the name of the celestial body of this CRS.
1481 : *
1482 : * e.g. "Earth" for an Earth CRS
1483 : *
1484 : * The returned value is only short lived and should not be used after other
1485 : * calls to methods on this object.
1486 : *
1487 : * @since GDAL 3.12 and PROJ 8.1
1488 : */
1489 :
1490 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1491 :
1492 : {
1493 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1494 :
1495 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1496 : }
1497 :
1498 : /************************************************************************/
1499 : /* Clone() */
1500 : /************************************************************************/
1501 :
1502 : /**
1503 : * \brief Make a duplicate of this OGRSpatialReference.
1504 : *
1505 : * This method is the same as the C function OSRClone().
1506 : *
1507 : * @return a new SRS, which becomes the responsibility of the caller.
1508 : */
1509 :
1510 33546 : OGRSpatialReference *OGRSpatialReference::Clone() const
1511 :
1512 : {
1513 33546 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1514 :
1515 33546 : TAKE_OPTIONAL_LOCK();
1516 :
1517 33546 : d->refreshProjObj();
1518 33546 : if (d->m_pj_crs != nullptr)
1519 33489 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1520 33546 : if (d->m_bHasCenterLong && d->m_poRoot)
1521 : {
1522 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1523 : }
1524 33546 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1525 33546 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1526 33546 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1527 67092 : return poNewRef;
1528 : }
1529 :
1530 : /************************************************************************/
1531 : /* OSRClone() */
1532 : /************************************************************************/
1533 :
1534 : /**
1535 : * \brief Make a duplicate of this OGRSpatialReference.
1536 : *
1537 : * This function is the same as OGRSpatialReference::Clone()
1538 : */
1539 1167 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1540 :
1541 : {
1542 1167 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1543 :
1544 1167 : return ToHandle(ToPointer(hSRS)->Clone());
1545 : }
1546 :
1547 : /************************************************************************/
1548 : /* dumpReadable() */
1549 : /************************************************************************/
1550 :
1551 : /** Dump pretty wkt to stdout, mostly for debugging.
1552 : */
1553 0 : void OGRSpatialReference::dumpReadable()
1554 :
1555 : {
1556 0 : char *pszPrettyWkt = nullptr;
1557 :
1558 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1559 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1560 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1561 0 : CPLFree(pszPrettyWkt);
1562 0 : }
1563 :
1564 : /************************************************************************/
1565 : /* exportToPrettyWkt() */
1566 : /************************************************************************/
1567 :
1568 : /**
1569 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1570 : * person.
1571 : *
1572 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1573 : * Issues</a> page for implementation details of WKT 1 in OGR.
1574 : *
1575 : * Note that the returned WKT string should be freed with
1576 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1577 : *
1578 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1579 : * option. Valid values are the one of the FORMAT option of
1580 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1581 : *
1582 : * This method is the same as the C function OSRExportToPrettyWkt().
1583 : *
1584 : * @param ppszResult the resulting string is returned in this pointer.
1585 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1586 : * stripped off.
1587 : *
1588 : * @return OGRERR_NONE if successful.
1589 : */
1590 :
1591 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1592 : int bSimplify) const
1593 :
1594 : {
1595 116 : CPLStringList aosOptions;
1596 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1597 58 : if (bSimplify)
1598 : {
1599 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1600 : }
1601 116 : return exportToWkt(ppszResult, aosOptions.List());
1602 : }
1603 :
1604 : /************************************************************************/
1605 : /* OSRExportToPrettyWkt() */
1606 : /************************************************************************/
1607 :
1608 : /**
1609 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1610 : * person.
1611 : *
1612 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1613 : * option. Valid values are the one of the FORMAT option of
1614 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1615 : *
1616 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1617 : */
1618 :
1619 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1620 : char **ppszReturn, int bSimplify)
1621 :
1622 : {
1623 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1624 :
1625 56 : *ppszReturn = nullptr;
1626 :
1627 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1628 : }
1629 :
1630 : /************************************************************************/
1631 : /* exportToWkt() */
1632 : /************************************************************************/
1633 :
1634 : /**
1635 : * \brief Convert this SRS into WKT 1 format.
1636 : *
1637 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1638 : * Issues</a> page for implementation details of WKT 1 in OGR.
1639 : *
1640 : * Note that the returned WKT string should be freed with
1641 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1642 : *
1643 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1644 : * option. Valid values are the one of the FORMAT option of
1645 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1646 : *
1647 : * This method is the same as the C function OSRExportToWkt().
1648 : *
1649 : * @param ppszResult the resulting string is returned in this pointer.
1650 : *
1651 : * @return OGRERR_NONE if successful.
1652 : */
1653 :
1654 12448 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1655 :
1656 : {
1657 12448 : return exportToWkt(ppszResult, nullptr);
1658 : }
1659 :
1660 : /************************************************************************/
1661 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1662 : /************************************************************************/
1663 :
1664 579 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1665 : bool onlyIfEPSGCode,
1666 : bool canModifyHorizPart)
1667 : {
1668 579 : PJ *ret = nullptr;
1669 579 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1670 : {
1671 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1672 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1673 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1674 26 : vertCRS &&
1675 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1676 : {
1677 : auto boundHoriz =
1678 : canModifyHorizPart
1679 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1680 3 : : proj_clone(ctx, horizCRS);
1681 : auto boundVert =
1682 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1683 3 : if (boundHoriz && boundVert)
1684 : {
1685 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1686 : boundHoriz, boundVert);
1687 : }
1688 3 : proj_destroy(boundHoriz);
1689 3 : proj_destroy(boundVert);
1690 : }
1691 13 : proj_destroy(horizCRS);
1692 13 : proj_destroy(vertCRS);
1693 : }
1694 1092 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1695 526 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1696 : {
1697 242 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1698 : }
1699 579 : return ret;
1700 : }
1701 :
1702 : /************************************************************************/
1703 : /* exportToWkt() */
1704 : /************************************************************************/
1705 :
1706 : /**
1707 : * Convert this SRS into a WKT string.
1708 : *
1709 : * Note that the returned WKT string should be freed with
1710 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1711 : *
1712 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1713 : * Issues</a> page for implementation details of WKT 1 in OGR.
1714 : *
1715 : * @param ppszResult the resulting string is returned in this pointer.
1716 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1717 : * supported options are
1718 : * <ul>
1719 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1720 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1721 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1722 : * node is returned.
1723 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1724 : * node is returned.
1725 : * WKT1 is an alias of WKT1_GDAL.
1726 : * WKT2 will default to the latest revision implemented (currently
1727 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1728 : * </li>
1729 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1730 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1731 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1732 : * height (for example for use with LAS 1.4 WKT1).
1733 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1734 : * </ul>
1735 : *
1736 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1737 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1738 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1739 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1740 : * TOWGS84[] node may be added.
1741 : *
1742 : * @return OGRERR_NONE if successful.
1743 : * @since GDAL 3.0
1744 : */
1745 :
1746 17123 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1747 : const char *const *papszOptions) const
1748 : {
1749 : // In the past calling this method was thread-safe, even if we never
1750 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1751 : // so this is no longer thread-safe.
1752 34246 : std::lock_guard oLock(d->m_mutex);
1753 :
1754 17123 : d->refreshProjObj();
1755 17123 : if (!d->m_pj_crs)
1756 : {
1757 21 : *ppszResult = CPLStrdup("");
1758 21 : return OGRERR_FAILURE;
1759 : }
1760 :
1761 17102 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1762 : {
1763 0 : return d->m_poRoot->exportToWkt(ppszResult);
1764 : }
1765 :
1766 17102 : auto ctxt = d->getPROJContext();
1767 17102 : auto wktFormat = PJ_WKT1_GDAL;
1768 : const char *pszFormat =
1769 17102 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1770 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1771 17102 : if (EQUAL(pszFormat, "DEFAULT"))
1772 14453 : pszFormat = "";
1773 :
1774 17102 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1775 : {
1776 644 : wktFormat = PJ_WKT1_ESRI;
1777 : }
1778 16458 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1779 15736 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1780 : {
1781 727 : wktFormat = PJ_WKT1_GDAL;
1782 : }
1783 15731 : else if (EQUAL(pszFormat, "WKT2_2015"))
1784 : {
1785 301 : wktFormat = PJ_WKT2_2015;
1786 : }
1787 15430 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1788 15064 : EQUAL(pszFormat, "WKT2_2019"))
1789 : {
1790 1206 : wktFormat = PJ_WKT2_2018;
1791 : }
1792 14224 : else if (pszFormat[0] == '\0')
1793 : {
1794 : // cppcheck-suppress knownConditionTrueFalse
1795 14224 : if (IsDerivedGeographic())
1796 : {
1797 2 : wktFormat = PJ_WKT2_2018;
1798 : }
1799 27814 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1800 13592 : GetAxesCount() == 3)
1801 : {
1802 56 : wktFormat = PJ_WKT2_2018;
1803 : }
1804 : }
1805 : else
1806 : {
1807 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1808 0 : *ppszResult = CPLStrdup("");
1809 0 : return OGRERR_FAILURE;
1810 : }
1811 :
1812 34204 : CPLStringList aosOptions;
1813 17102 : if (wktFormat != PJ_WKT1_ESRI)
1814 : {
1815 16458 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1816 : }
1817 : aosOptions.SetNameValue(
1818 17102 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1819 :
1820 17102 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1821 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1822 17102 : if (pszAllowEllpsHeightAsVertCS)
1823 : {
1824 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1825 0 : pszAllowEllpsHeightAsVertCS);
1826 : }
1827 :
1828 17102 : PJ *boundCRS = nullptr;
1829 31995 : if (wktFormat == PJ_WKT1_GDAL &&
1830 14893 : CPLTestBool(CSLFetchNameValueDef(
1831 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1832 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1833 : {
1834 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1835 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1836 : }
1837 :
1838 34204 : CPLErrorAccumulator oErrorAccumulator;
1839 : const char *pszWKT;
1840 : {
1841 17102 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1842 17102 : CPL_IGNORE_RET_VAL(oAccumulator);
1843 17102 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1844 17102 : aosOptions.List());
1845 : }
1846 17104 : for (const auto &oError : oErrorAccumulator.GetErrors())
1847 : {
1848 32 : if (pszFormat[0] == '\0' &&
1849 14 : (oError.msg.find("Unsupported conversion method") !=
1850 2 : std::string::npos ||
1851 2 : oError.msg.find("can only be exported to WKT2") !=
1852 0 : std::string::npos ||
1853 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1854 : std::string::npos))
1855 : {
1856 14 : CPLErrorReset();
1857 : // If we cannot export in the default mode (WKT1), retry with WKT2
1858 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1859 14 : PJ_WKT2_2018, aosOptions.List());
1860 14 : break;
1861 : }
1862 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1863 : }
1864 :
1865 17102 : if (!pszWKT)
1866 : {
1867 2 : *ppszResult = CPLStrdup("");
1868 2 : proj_destroy(boundCRS);
1869 2 : return OGRERR_FAILURE;
1870 : }
1871 :
1872 17100 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1873 : {
1874 5 : OGR_SRSNode oRoot;
1875 5 : oRoot.importFromWkt(&pszWKT);
1876 5 : oRoot.StripNodes("AXIS");
1877 5 : if (EQUAL(pszFormat, "SFSQL"))
1878 : {
1879 3 : oRoot.StripNodes("TOWGS84");
1880 : }
1881 5 : oRoot.StripNodes("AUTHORITY");
1882 5 : oRoot.StripNodes("EXTENSION");
1883 : OGRErr eErr;
1884 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1885 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1886 : else
1887 3 : eErr = oRoot.exportToWkt(ppszResult);
1888 5 : proj_destroy(boundCRS);
1889 5 : return eErr;
1890 : }
1891 :
1892 17095 : *ppszResult = CPLStrdup(pszWKT);
1893 :
1894 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1895 17095 : if (wktFormat == PJ_WKT2_2018)
1896 : {
1897 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1898 : // related to a wrong EPSG code assigned to UTM South conversions
1899 1264 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1900 1264 : if (pszPtr)
1901 : {
1902 313 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1903 313 : const int nZone = atoi(pszPtr);
1904 938 : while (*pszPtr >= '0' && *pszPtr <= '9')
1905 625 : ++pszPtr;
1906 313 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1907 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1908 : {
1909 1 : pszPtr += 3;
1910 1 : int nLevel = 0;
1911 1 : bool bInString = false;
1912 : // Find the ID node corresponding to this CONVERSION node
1913 480 : while (*pszPtr)
1914 : {
1915 480 : if (bInString)
1916 : {
1917 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1918 : {
1919 0 : ++pszPtr;
1920 : }
1921 197 : else if (*pszPtr == '"')
1922 : {
1923 17 : bInString = false;
1924 : }
1925 : }
1926 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1927 : {
1928 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1929 : 17000 + nZone)))
1930 : {
1931 1 : CPLAssert(pszPtr[11] == '7');
1932 1 : CPLAssert(pszPtr[12] == '0');
1933 1 : pszPtr[11] = '6';
1934 1 : pszPtr[12] = '1';
1935 : }
1936 1 : break;
1937 : }
1938 282 : else if (*pszPtr == '"')
1939 : {
1940 17 : bInString = true;
1941 : }
1942 265 : else if (*pszPtr == '[')
1943 : {
1944 17 : ++nLevel;
1945 : }
1946 248 : else if (*pszPtr == ']')
1947 : {
1948 17 : --nLevel;
1949 : }
1950 :
1951 479 : ++pszPtr;
1952 : }
1953 : }
1954 : }
1955 : }
1956 : #endif
1957 :
1958 17095 : proj_destroy(boundCRS);
1959 17095 : return OGRERR_NONE;
1960 : }
1961 :
1962 : /************************************************************************/
1963 : /* exportToWkt() */
1964 : /************************************************************************/
1965 :
1966 : /**
1967 : * Convert this SRS into a WKT string.
1968 : *
1969 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1970 : * Issues</a> page for implementation details of WKT 1 in OGR.
1971 : *
1972 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1973 : * supported options are
1974 : * <ul>
1975 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1976 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1977 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1978 : * node is returned.
1979 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1980 : * node is returned.
1981 : * WKT1 is an alias of WKT1_GDAL.
1982 : * WKT2 will default to the latest revision implemented (currently
1983 : * WKT2_2019)
1984 : * </li>
1985 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1986 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1987 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1988 : * height (for example for use with LAS 1.4 WKT1).
1989 : * Requires PROJ 7.2.1.</li>
1990 : * </ul>
1991 : *
1992 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1993 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1994 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1995 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1996 : * TOWGS84[] node may be added.
1997 : *
1998 : * @return a non-empty string if successful.
1999 : * @since GDAL 3.9
2000 : */
2001 :
2002 : std::string
2003 301 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2004 : {
2005 301 : std::string osWKT;
2006 301 : char *pszWKT = nullptr;
2007 301 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2008 301 : osWKT = pszWKT;
2009 301 : CPLFree(pszWKT);
2010 602 : return osWKT;
2011 : }
2012 :
2013 : /************************************************************************/
2014 : /* OSRExportToWkt() */
2015 : /************************************************************************/
2016 :
2017 : /**
2018 : * \brief Convert this SRS into WKT 1 format.
2019 : *
2020 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2021 : * Issues</a> page for implementation details of WKT in OGR.
2022 : *
2023 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2024 : * option. Valid values are the one of the FORMAT option of
2025 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2026 : *
2027 : * This function is the same as OGRSpatialReference::exportToWkt().
2028 : */
2029 :
2030 916 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2031 :
2032 : {
2033 916 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2034 :
2035 916 : *ppszReturn = nullptr;
2036 :
2037 916 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2038 : }
2039 :
2040 : /************************************************************************/
2041 : /* OSRExportToWktEx() */
2042 : /************************************************************************/
2043 :
2044 : /**
2045 : * \brief Convert this SRS into WKT format.
2046 : *
2047 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2048 : * ppszResult,const char* const* papszOptions ) const
2049 : *
2050 : * @since GDAL 3.0
2051 : */
2052 :
2053 1356 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2054 : const char *const *papszOptions)
2055 : {
2056 1356 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2057 :
2058 1356 : *ppszReturn = nullptr;
2059 :
2060 1356 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2061 : }
2062 :
2063 : /************************************************************************/
2064 : /* exportToPROJJSON() */
2065 : /************************************************************************/
2066 :
2067 : /**
2068 : * Convert this SRS into a PROJJSON string.
2069 : *
2070 : * Note that the returned JSON string should be freed with
2071 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2072 : *
2073 : * @param ppszResult the resulting string is returned in this pointer.
2074 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2075 : * supported options are
2076 : * <ul>
2077 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2078 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2079 : * on).</li>
2080 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2081 : * disable it.</li>
2082 : * </ul>
2083 : *
2084 : * @return OGRERR_NONE if successful.
2085 : * @since GDAL 3.1 and PROJ 6.2
2086 : */
2087 :
2088 2598 : OGRErr OGRSpatialReference::exportToPROJJSON(
2089 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2090 : {
2091 5196 : TAKE_OPTIONAL_LOCK();
2092 :
2093 2598 : d->refreshProjObj();
2094 2598 : if (!d->m_pj_crs)
2095 : {
2096 1 : *ppszResult = nullptr;
2097 1 : return OGRERR_FAILURE;
2098 : }
2099 :
2100 : const char *pszPROJJSON =
2101 2597 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2102 :
2103 2597 : if (!pszPROJJSON)
2104 : {
2105 0 : *ppszResult = CPLStrdup("");
2106 0 : return OGRERR_FAILURE;
2107 : }
2108 :
2109 2597 : *ppszResult = CPLStrdup(pszPROJJSON);
2110 :
2111 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2112 : {
2113 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2114 : // related to a wrong EPSG code assigned to UTM South conversions
2115 2597 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2116 2597 : if (pszPtr)
2117 : {
2118 254 : pszPtr += strlen("\"name\": \"UTM zone ");
2119 254 : const int nZone = atoi(pszPtr);
2120 761 : while (*pszPtr >= '0' && *pszPtr <= '9')
2121 507 : ++pszPtr;
2122 254 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2123 : {
2124 4 : pszPtr += 2;
2125 4 : int nLevel = 0;
2126 4 : bool bInString = false;
2127 : // Find the id node corresponding to this conversion node
2128 5299 : while (*pszPtr)
2129 : {
2130 5299 : if (bInString)
2131 : {
2132 1950 : if (*pszPtr == '\\')
2133 : {
2134 0 : ++pszPtr;
2135 : }
2136 1950 : else if (*pszPtr == '"')
2137 : {
2138 244 : bInString = false;
2139 : }
2140 : }
2141 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2142 : {
2143 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2144 : const char *pszAuthEPSG =
2145 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2146 4 : char *pszCode = strstr(
2147 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2148 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2149 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2150 4 : pszNextEndCurl - pszCode > 0)
2151 : {
2152 4 : CPLAssert(pszCode[9] == '7');
2153 4 : CPLAssert(pszCode[10] == '0');
2154 4 : pszCode[9] = '6';
2155 4 : pszCode[10] = '1';
2156 : }
2157 4 : break;
2158 : }
2159 3345 : else if (*pszPtr == '"')
2160 : {
2161 244 : bInString = true;
2162 : }
2163 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2164 : {
2165 60 : ++nLevel;
2166 : }
2167 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2168 : {
2169 60 : --nLevel;
2170 : }
2171 :
2172 5295 : ++pszPtr;
2173 : }
2174 : }
2175 : }
2176 : }
2177 : #endif
2178 :
2179 2597 : return OGRERR_NONE;
2180 : }
2181 :
2182 : /************************************************************************/
2183 : /* OSRExportToPROJJSON() */
2184 : /************************************************************************/
2185 :
2186 : /**
2187 : * \brief Convert this SRS into PROJJSON format.
2188 : *
2189 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2190 : *
2191 : * @since GDAL 3.1 and PROJ 6.2
2192 : */
2193 :
2194 79 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2195 : const char *const *papszOptions)
2196 : {
2197 79 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2198 :
2199 79 : *ppszReturn = nullptr;
2200 :
2201 79 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2202 : }
2203 :
2204 : /************************************************************************/
2205 : /* importFromWkt() */
2206 : /************************************************************************/
2207 :
2208 : /**
2209 : * \brief Import from WKT string.
2210 : *
2211 : * This method will wipe the existing SRS definition, and
2212 : * reassign it based on the contents of the passed WKT string. Only as
2213 : * much of the input string as needed to construct this SRS is consumed from
2214 : * the input string, and the input string pointer
2215 : * is then updated to point to the remaining (unused) input.
2216 : *
2217 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2218 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2219 : * and the coordinate epoch potentially present used as the coordinate epoch
2220 : * property of the OGRSpatialReference object.
2221 : *
2222 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2223 : * Issues</a> page for implementation details of WKT in OGR.
2224 : *
2225 : * This method is the same as the C function OSRImportFromWkt().
2226 : *
2227 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2228 : * point to remaining unused input text.
2229 : *
2230 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2231 : * fails for any reason.
2232 : */
2233 :
2234 17543 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2235 :
2236 : {
2237 17543 : return importFromWkt(ppszInput, nullptr);
2238 : }
2239 :
2240 : /************************************************************************/
2241 : /* importFromWkt() */
2242 : /************************************************************************/
2243 :
2244 : /*! @cond Doxygen_Suppress */
2245 :
2246 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2247 : CSLConstList papszOptions)
2248 :
2249 : {
2250 21 : return importFromWkt(&pszInput, papszOptions);
2251 : }
2252 :
2253 17564 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2254 : CSLConstList papszOptions)
2255 :
2256 : {
2257 35128 : TAKE_OPTIONAL_LOCK();
2258 :
2259 17564 : if (!ppszInput || !*ppszInput)
2260 0 : return OGRERR_FAILURE;
2261 :
2262 17564 : if (strlen(*ppszInput) > 100 * 1000 &&
2263 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2264 : {
2265 0 : CPLError(CE_Failure, CPLE_NotSupported,
2266 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2267 : "You can remove this limitation by definition the "
2268 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2269 0 : return OGRERR_FAILURE;
2270 : }
2271 :
2272 17564 : Clear();
2273 :
2274 17564 : bool canCache = false;
2275 17564 : auto tlsCache = OSRGetProjTLSCache();
2276 35128 : std::string osWkt;
2277 17564 : if (**ppszInput)
2278 : {
2279 17008 : osWkt = *ppszInput;
2280 17008 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2281 17008 : if (cachedObj)
2282 : {
2283 15158 : d->setPjCRS(cachedObj);
2284 : }
2285 : else
2286 : {
2287 3700 : CPLStringList aosOptions(papszOptions);
2288 1850 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2289 1850 : aosOptions.SetNameValue("STRICT", "NO");
2290 1850 : PROJ_STRING_LIST warnings = nullptr;
2291 1850 : PROJ_STRING_LIST errors = nullptr;
2292 1850 : auto ctxt = d->getPROJContext();
2293 1850 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2294 : &warnings, &errors);
2295 1850 : d->setPjCRS(pj);
2296 :
2297 1903 : for (auto iter = warnings; iter && *iter; ++iter)
2298 : {
2299 53 : d->m_wktImportWarnings.push_back(*iter);
2300 : }
2301 2086 : for (auto iter = errors; iter && *iter; ++iter)
2302 : {
2303 236 : d->m_wktImportErrors.push_back(*iter);
2304 236 : if (!d->m_pj_crs)
2305 : {
2306 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2307 : }
2308 : }
2309 1850 : if (warnings == nullptr && errors == nullptr)
2310 : {
2311 1570 : canCache = true;
2312 : }
2313 1850 : proj_string_list_destroy(warnings);
2314 1850 : proj_string_list_destroy(errors);
2315 : }
2316 : }
2317 17564 : if (!d->m_pj_crs)
2318 590 : return OGRERR_CORRUPT_DATA;
2319 :
2320 : // Only accept CRS objects
2321 16974 : if (!proj_is_crs(d->m_pj_crs))
2322 : {
2323 0 : Clear();
2324 0 : return OGRERR_CORRUPT_DATA;
2325 : }
2326 :
2327 16974 : if (canCache)
2328 : {
2329 1570 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2330 : }
2331 :
2332 16974 : if (strstr(*ppszInput, "CENTER_LONG"))
2333 : {
2334 0 : auto poRoot = new OGR_SRSNode();
2335 0 : d->setRoot(poRoot);
2336 0 : const char *pszTmp = *ppszInput;
2337 0 : poRoot->importFromWkt(&pszTmp);
2338 0 : d->m_bHasCenterLong = true;
2339 : }
2340 :
2341 : // TODO? we don't really update correctly since we assume that the
2342 : // passed string is only WKT.
2343 16974 : *ppszInput += strlen(*ppszInput);
2344 16974 : return OGRERR_NONE;
2345 :
2346 : #if no_longer_implemented_for_now
2347 : /* -------------------------------------------------------------------- */
2348 : /* The following seems to try and detect and unconsumed */
2349 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2350 : /* import and attach it to the existing root. Likely we will */
2351 : /* need to extend this somewhat to bring it into an acceptable */
2352 : /* OGRSpatialReference organization at some point. */
2353 : /* -------------------------------------------------------------------- */
2354 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2355 : {
2356 : if (((*ppszInput)[0]) == ',')
2357 : (*ppszInput)++;
2358 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2359 : poRoot->AddChild(poNewChild);
2360 : return poNewChild->importFromWkt(ppszInput);
2361 : }
2362 : #endif
2363 : }
2364 :
2365 : /*! @endcond */
2366 :
2367 : /**
2368 : * \brief Import from WKT string.
2369 : *
2370 : * This method will wipe the existing SRS definition, and
2371 : * reassign it based on the contents of the passed WKT string. Only as
2372 : * much of the input string as needed to construct this SRS is consumed from
2373 : * the input string, and the input string pointer
2374 : * is then updated to point to the remaining (unused) input.
2375 : *
2376 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2377 : * Issues</a> page for implementation details of WKT in OGR.
2378 : *
2379 : * This method is the same as the C function OSRImportFromWkt().
2380 : *
2381 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2382 : * point to remaining unused input text.
2383 : *
2384 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2385 : * fails for any reason.
2386 : * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2387 : * char*)
2388 : */
2389 :
2390 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2391 :
2392 : {
2393 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2394 : }
2395 :
2396 : /**
2397 : * \brief Import from WKT string.
2398 : *
2399 : * This method will wipe the existing SRS definition, and
2400 : * reassign it based on the contents of the passed WKT string. Only as
2401 : * much of the input string as needed to construct this SRS is consumed from
2402 : * the input string, and the input string pointer
2403 : * is then updated to point to the remaining (unused) input.
2404 : *
2405 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2406 : * Issues</a> page for implementation details of WKT in OGR.
2407 : *
2408 : * @param pszInput Input WKT
2409 : *
2410 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2411 : * fails for any reason.
2412 : */
2413 :
2414 17253 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2415 : {
2416 17253 : return importFromWkt(&pszInput);
2417 : }
2418 :
2419 : /************************************************************************/
2420 : /* Validate() */
2421 : /************************************************************************/
2422 :
2423 : /**
2424 : * \brief Validate CRS imported with importFromWkt() or with modified with
2425 : * direct node manipulations. Otherwise the CRS should be always valid.
2426 : *
2427 : * This method attempts to verify that the spatial reference system is
2428 : * well formed, and consists of known tokens. The validation is not
2429 : * comprehensive.
2430 : *
2431 : * This method is the same as the C function OSRValidate().
2432 : *
2433 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2434 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2435 : * but contains non-standard PROJECTION[] values.
2436 : */
2437 :
2438 116 : OGRErr OGRSpatialReference::Validate() const
2439 :
2440 : {
2441 232 : TAKE_OPTIONAL_LOCK();
2442 :
2443 154 : for (const auto &str : d->m_wktImportErrors)
2444 : {
2445 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2446 : }
2447 116 : for (const auto &str : d->m_wktImportWarnings)
2448 : {
2449 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2450 : }
2451 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2452 : {
2453 37 : return OGRERR_CORRUPT_DATA;
2454 : }
2455 79 : if (!d->m_wktImportWarnings.empty())
2456 : {
2457 0 : return OGRERR_UNSUPPORTED_SRS;
2458 : }
2459 79 : return OGRERR_NONE;
2460 : }
2461 :
2462 : /************************************************************************/
2463 : /* OSRValidate() */
2464 : /************************************************************************/
2465 : /**
2466 : * \brief Validate SRS tokens.
2467 : *
2468 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2469 : */
2470 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2471 :
2472 : {
2473 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2474 :
2475 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2476 : }
2477 :
2478 : /************************************************************************/
2479 : /* OSRImportFromWkt() */
2480 : /************************************************************************/
2481 :
2482 : /**
2483 : * \brief Import from WKT string.
2484 : *
2485 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2486 : * Issues</a> page for implementation details of WKT in OGR.
2487 : *
2488 : * This function is the same as OGRSpatialReference::importFromWkt().
2489 : */
2490 :
2491 290 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2492 :
2493 : {
2494 290 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2495 :
2496 290 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2497 : }
2498 :
2499 : /************************************************************************/
2500 : /* SetNode() */
2501 : /************************************************************************/
2502 :
2503 : /**
2504 : * \brief Set attribute value in spatial reference.
2505 : *
2506 : * Missing intermediate nodes in the path will be created if not already
2507 : * in existence. If the attribute has no children one will be created and
2508 : * assigned the value otherwise the zeroth child will be assigned the value.
2509 : *
2510 : * This method does the same as the C function OSRSetAttrValue().
2511 : *
2512 : * @param pszNodePath full path to attribute to be set. For instance
2513 : * "PROJCS|GEOGCS|UNIT".
2514 : *
2515 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2516 : * This may be NULL if you just want to force creation of the intermediate
2517 : * path.
2518 : *
2519 : * @return OGRERR_NONE on success.
2520 : */
2521 :
2522 583 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2523 : const char *pszNewNodeValue)
2524 :
2525 : {
2526 1166 : TAKE_OPTIONAL_LOCK();
2527 :
2528 : char **papszPathTokens =
2529 583 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2530 :
2531 583 : if (CSLCount(papszPathTokens) < 1)
2532 : {
2533 0 : CSLDestroy(papszPathTokens);
2534 0 : return OGRERR_FAILURE;
2535 : }
2536 :
2537 1018 : if (GetRoot() == nullptr ||
2538 435 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2539 : {
2540 268 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2541 116 : CSLCount(papszPathTokens) == 1)
2542 : {
2543 116 : CSLDestroy(papszPathTokens);
2544 116 : return SetProjCS(pszNewNodeValue);
2545 : }
2546 : else
2547 : {
2548 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2549 : }
2550 : }
2551 :
2552 467 : OGR_SRSNode *poNode = GetRoot();
2553 725 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2554 : {
2555 258 : int j = 0; // Used after for.
2556 :
2557 645 : for (; j < poNode->GetChildCount(); j++)
2558 : {
2559 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2560 : {
2561 198 : poNode = poNode->GetChild(j);
2562 198 : j = -1;
2563 198 : break;
2564 : }
2565 : }
2566 :
2567 258 : if (j != -1)
2568 : {
2569 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2570 60 : poNode->AddChild(poNewNode);
2571 60 : poNode = poNewNode;
2572 : }
2573 : }
2574 :
2575 467 : CSLDestroy(papszPathTokens);
2576 :
2577 467 : if (pszNewNodeValue != nullptr)
2578 : {
2579 467 : if (poNode->GetChildCount() > 0)
2580 371 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2581 : else
2582 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2583 : };
2584 467 : return OGRERR_NONE;
2585 : }
2586 :
2587 : /************************************************************************/
2588 : /* OSRSetAttrValue() */
2589 : /************************************************************************/
2590 :
2591 : /**
2592 : * \brief Set attribute value in spatial reference.
2593 : *
2594 : * This function is the same as OGRSpatialReference::SetNode()
2595 : */
2596 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2597 : const char *pszPath, const char *pszValue)
2598 :
2599 : {
2600 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2601 :
2602 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2603 : }
2604 :
2605 : /************************************************************************/
2606 : /* SetNode() */
2607 : /************************************************************************/
2608 :
2609 : /**
2610 : * \brief Set attribute value in spatial reference.
2611 : *
2612 : * Missing intermediate nodes in the path will be created if not already
2613 : * in existence. If the attribute has no children one will be created and
2614 : * assigned the value otherwise the zeroth child will be assigned the value.
2615 : *
2616 : * This method does the same as the C function OSRSetAttrValue().
2617 : *
2618 : * @param pszNodePath full path to attribute to be set. For instance
2619 : * "PROJCS|GEOGCS|UNIT".
2620 : *
2621 : * @param dfValue value to be assigned to node.
2622 : *
2623 : * @return OGRERR_NONE on success.
2624 : */
2625 :
2626 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2627 :
2628 : {
2629 0 : char szValue[64] = {'\0'};
2630 :
2631 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2632 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2633 : else
2634 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2635 :
2636 0 : return SetNode(pszNodePath, szValue);
2637 : }
2638 :
2639 : /************************************************************************/
2640 : /* SetAngularUnits() */
2641 : /************************************************************************/
2642 :
2643 : /**
2644 : * \brief Set the angular units for the geographic coordinate system.
2645 : *
2646 : * This method creates a UNIT subnode with the specified values as a
2647 : * child of the GEOGCS node.
2648 : *
2649 : * This method does the same as the C function OSRSetAngularUnits().
2650 : *
2651 : * @param pszUnitsName the units name to be used. Some preferred units
2652 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2653 : *
2654 : * @param dfInRadians the value to multiple by an angle in the indicated
2655 : * units to transform to radians. Some standard conversion factors can
2656 : * be found in ogr_srs_api.h.
2657 : *
2658 : * @return OGRERR_NONE on success.
2659 : */
2660 :
2661 1388 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2662 : double dfInRadians)
2663 :
2664 : {
2665 2776 : TAKE_OPTIONAL_LOCK();
2666 :
2667 1388 : d->bNormInfoSet = FALSE;
2668 :
2669 1388 : d->refreshProjObj();
2670 1388 : if (!d->m_pj_crs)
2671 0 : return OGRERR_FAILURE;
2672 1388 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2673 1388 : if (!geodCRS)
2674 0 : return OGRERR_FAILURE;
2675 1388 : proj_destroy(geodCRS);
2676 1388 : d->demoteFromBoundCRS();
2677 1388 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2678 : pszUnitsName, dfInRadians,
2679 : nullptr, nullptr));
2680 1388 : d->undoDemoteFromBoundCRS();
2681 :
2682 1388 : d->m_osAngularUnits = pszUnitsName;
2683 1388 : d->m_dfAngularUnitToRadian = dfInRadians;
2684 :
2685 1388 : return OGRERR_NONE;
2686 : }
2687 :
2688 : /************************************************************************/
2689 : /* OSRSetAngularUnits() */
2690 : /************************************************************************/
2691 :
2692 : /**
2693 : * \brief Set the angular units for the geographic coordinate system.
2694 : *
2695 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2696 : */
2697 48 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2698 : double dfInRadians)
2699 :
2700 : {
2701 48 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2702 :
2703 48 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2704 : }
2705 :
2706 : /************************************************************************/
2707 : /* GetAngularUnits() */
2708 : /************************************************************************/
2709 :
2710 : /**
2711 : * \brief Fetch angular geographic coordinate system units.
2712 : *
2713 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2714 : * will be assumed. This method only checks directly under the GEOGCS node
2715 : * for units.
2716 : *
2717 : * This method does the same thing as the C function OSRGetAngularUnits().
2718 : *
2719 : * @param ppszName a pointer to be updated with the pointer to the units name.
2720 : * The returned value remains internal to the OGRSpatialReference and should
2721 : * not be freed, or modified. It may be invalidated on the next
2722 : * OGRSpatialReference call.
2723 : *
2724 : * @return the value to multiply by angular distances to transform them to
2725 : * radians.
2726 : */
2727 :
2728 8275 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2729 :
2730 : {
2731 16550 : TAKE_OPTIONAL_LOCK();
2732 :
2733 8275 : d->refreshProjObj();
2734 :
2735 8275 : if (!d->m_osAngularUnits.empty())
2736 : {
2737 2269 : if (ppszName != nullptr)
2738 181 : *ppszName = d->m_osAngularUnits.c_str();
2739 2269 : return d->m_dfAngularUnitToRadian;
2740 : }
2741 :
2742 : do
2743 : {
2744 6006 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2745 : {
2746 113 : break;
2747 : }
2748 :
2749 : auto geodCRS =
2750 5895 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2751 5895 : if (!geodCRS)
2752 : {
2753 0 : break;
2754 : }
2755 : auto coordSys =
2756 5895 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2757 5895 : proj_destroy(geodCRS);
2758 5895 : if (!coordSys)
2759 : {
2760 0 : break;
2761 : }
2762 5895 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2763 : PJ_CS_TYPE_ELLIPSOIDAL)
2764 : {
2765 2 : proj_destroy(coordSys);
2766 2 : break;
2767 : }
2768 :
2769 5893 : double dfConvFactor = 0.0;
2770 5893 : const char *pszUnitName = nullptr;
2771 5893 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2772 : nullptr, nullptr, &dfConvFactor,
2773 : &pszUnitName, nullptr, nullptr))
2774 : {
2775 0 : proj_destroy(coordSys);
2776 0 : break;
2777 : }
2778 :
2779 5893 : d->m_osAngularUnits = pszUnitName;
2780 :
2781 5893 : proj_destroy(coordSys);
2782 5893 : d->m_dfAngularUnitToRadian = dfConvFactor;
2783 : } while (false);
2784 :
2785 6006 : if (d->m_osAngularUnits.empty())
2786 : {
2787 113 : d->m_osAngularUnits = "degree";
2788 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2789 : }
2790 :
2791 6006 : if (ppszName != nullptr)
2792 3171 : *ppszName = d->m_osAngularUnits.c_str();
2793 6006 : return d->m_dfAngularUnitToRadian;
2794 : }
2795 :
2796 : /**
2797 : * \brief Fetch angular geographic coordinate system units.
2798 : *
2799 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2800 : * will be assumed. This method only checks directly under the GEOGCS node
2801 : * for units.
2802 : *
2803 : * This method does the same thing as the C function OSRGetAngularUnits().
2804 : *
2805 : * @param ppszName a pointer to be updated with the pointer to the units name.
2806 : * The returned value remains internal to the OGRSpatialReference and should
2807 : * not be freed, or modified. It may be invalidated on the next
2808 : * OGRSpatialReference call.
2809 : *
2810 : * @return the value to multiply by angular distances to transform them to
2811 : * radians.
2812 : * @deprecated Use GetAngularUnits(const char**) const.
2813 : */
2814 :
2815 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2816 :
2817 : {
2818 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2819 : }
2820 :
2821 : /************************************************************************/
2822 : /* OSRGetAngularUnits() */
2823 : /************************************************************************/
2824 :
2825 : /**
2826 : * \brief Fetch angular geographic coordinate system units.
2827 : *
2828 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2829 : */
2830 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2831 :
2832 : {
2833 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2834 :
2835 1 : return ToPointer(hSRS)->GetAngularUnits(
2836 1 : const_cast<const char **>(ppszName));
2837 : }
2838 :
2839 : /************************************************************************/
2840 : /* SetLinearUnitsAndUpdateParameters() */
2841 : /************************************************************************/
2842 :
2843 : /**
2844 : * \brief Set the linear units for the projection.
2845 : *
2846 : * This method creates a UNIT subnode with the specified values as a
2847 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2848 : * SetLinearUnits() method, but it also updates all existing linear
2849 : * projection parameter values from the old units to the new units.
2850 : *
2851 : * @param pszName the units name to be used. Some preferred units
2852 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2853 : * and SRS_UL_US_FOOT.
2854 : *
2855 : * @param dfInMeters the value to multiple by a length in the indicated
2856 : * units to transform to meters. Some standard conversion factors can
2857 : * be found in ogr_srs_api.h.
2858 : *
2859 : * @param pszUnitAuthority Unit authority name. Or nullptr
2860 : *
2861 : * @param pszUnitCode Unit code. Or nullptr
2862 : *
2863 : * @return OGRERR_NONE on success.
2864 : */
2865 :
2866 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2867 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2868 : const char *pszUnitCode)
2869 :
2870 : {
2871 78 : TAKE_OPTIONAL_LOCK();
2872 :
2873 39 : if (dfInMeters <= 0.0)
2874 0 : return OGRERR_FAILURE;
2875 :
2876 39 : d->refreshProjObj();
2877 39 : if (!d->m_pj_crs)
2878 0 : return OGRERR_FAILURE;
2879 :
2880 39 : d->demoteFromBoundCRS();
2881 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2882 : {
2883 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2884 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2885 : pszUnitAuthority, pszUnitCode, true));
2886 : }
2887 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2888 : pszName, dfInMeters,
2889 : pszUnitAuthority, pszUnitCode));
2890 39 : d->undoDemoteFromBoundCRS();
2891 :
2892 39 : d->m_osLinearUnits = pszName;
2893 39 : d->dfToMeter = dfInMeters;
2894 :
2895 39 : return OGRERR_NONE;
2896 : }
2897 :
2898 : /************************************************************************/
2899 : /* OSRSetLinearUnitsAndUpdateParameters() */
2900 : /************************************************************************/
2901 :
2902 : /**
2903 : * \brief Set the linear units for the projection.
2904 : *
2905 : * This function is the same as
2906 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2907 : */
2908 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2909 : const char *pszUnits,
2910 : double dfInMeters)
2911 :
2912 : {
2913 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2914 : OGRERR_FAILURE);
2915 :
2916 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2917 1 : dfInMeters);
2918 : }
2919 :
2920 : /************************************************************************/
2921 : /* SetLinearUnits() */
2922 : /************************************************************************/
2923 :
2924 : /**
2925 : * \brief Set the linear units for the projection.
2926 : *
2927 : * This method creates a UNIT subnode with the specified values as a
2928 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2929 : * Geographic 3D CRS the vertical axis units will be set.
2930 : *
2931 : * This method does the same as the C function OSRSetLinearUnits().
2932 : *
2933 : * @param pszUnitsName the units name to be used. Some preferred units
2934 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2935 : * and SRS_UL_US_FOOT.
2936 : *
2937 : * @param dfInMeters the value to multiple by a length in the indicated
2938 : * units to transform to meters. Some standard conversion factors can
2939 : * be found in ogr_srs_api.h.
2940 : *
2941 : * @return OGRERR_NONE on success.
2942 : */
2943 :
2944 7255 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2945 : double dfInMeters)
2946 :
2947 : {
2948 7255 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2949 : }
2950 :
2951 : /************************************************************************/
2952 : /* OSRSetLinearUnits() */
2953 : /************************************************************************/
2954 :
2955 : /**
2956 : * \brief Set the linear units for the projection.
2957 : *
2958 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2959 : */
2960 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2961 : double dfInMeters)
2962 :
2963 : {
2964 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2965 :
2966 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2967 : }
2968 :
2969 : /************************************************************************/
2970 : /* SetTargetLinearUnits() */
2971 : /************************************************************************/
2972 :
2973 : /**
2974 : * \brief Set the linear units for the projection.
2975 : *
2976 : * This method creates a UNIT subnode with the specified values as a
2977 : * child of the target node.
2978 : *
2979 : * This method does the same as the C function OSRSetTargetLinearUnits().
2980 : *
2981 : * @param pszTargetKey the keyword to set the linear units for.
2982 : * i.e. "PROJCS" or "VERT_CS"
2983 : *
2984 : * @param pszUnitsName the units name to be used. Some preferred units
2985 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2986 : * and SRS_UL_US_FOOT.
2987 : *
2988 : * @param dfInMeters the value to multiple by a length in the indicated
2989 : * units to transform to meters. Some standard conversion factors can
2990 : * be found in ogr_srs_api.h.
2991 : *
2992 : * @param pszUnitAuthority Unit authority name. Or nullptr
2993 : *
2994 : * @param pszUnitCode Unit code. Or nullptr
2995 : *
2996 : * @return OGRERR_NONE on success.
2997 : *
2998 : */
2999 :
3000 11566 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3001 : const char *pszUnitsName,
3002 : double dfInMeters,
3003 : const char *pszUnitAuthority,
3004 : const char *pszUnitCode)
3005 :
3006 : {
3007 23132 : TAKE_OPTIONAL_LOCK();
3008 :
3009 11566 : if (dfInMeters <= 0.0)
3010 0 : return OGRERR_FAILURE;
3011 :
3012 11566 : d->refreshProjObj();
3013 11566 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3014 11566 : if (pszTargetKey == nullptr)
3015 : {
3016 11566 : if (!d->m_pj_crs)
3017 0 : return OGRERR_FAILURE;
3018 :
3019 11566 : d->demoteFromBoundCRS();
3020 11566 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3021 : {
3022 17346 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3023 8673 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3024 : pszUnitAuthority, pszUnitCode, false));
3025 : }
3026 23132 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3027 11566 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3028 : pszUnitAuthority, pszUnitCode));
3029 11566 : d->undoDemoteFromBoundCRS();
3030 :
3031 11566 : d->m_osLinearUnits = pszUnitsName;
3032 11566 : d->dfToMeter = dfInMeters;
3033 :
3034 11566 : return OGRERR_NONE;
3035 : }
3036 :
3037 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3038 :
3039 0 : if (poCS == nullptr)
3040 0 : return OGRERR_FAILURE;
3041 :
3042 0 : char szValue[128] = {'\0'};
3043 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3044 0 : dfInMeters > std::numeric_limits<int>::min() &&
3045 0 : dfInMeters == static_cast<int>(dfInMeters))
3046 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3047 : else
3048 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3049 :
3050 0 : OGR_SRSNode *poUnits = nullptr;
3051 0 : if (poCS->FindChild("UNIT") >= 0)
3052 : {
3053 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3054 0 : if (poUnits->GetChildCount() < 2)
3055 0 : return OGRERR_FAILURE;
3056 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3057 0 : poUnits->GetChild(1)->SetValue(szValue);
3058 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3059 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3060 : }
3061 : else
3062 : {
3063 0 : poUnits = new OGR_SRSNode("UNIT");
3064 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3065 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3066 :
3067 0 : poCS->AddChild(poUnits);
3068 : }
3069 :
3070 0 : return OGRERR_NONE;
3071 : }
3072 :
3073 : /************************************************************************/
3074 : /* OSRSetLinearUnits() */
3075 : /************************************************************************/
3076 :
3077 : /**
3078 : * \brief Set the linear units for the target node.
3079 : *
3080 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3081 : *
3082 : */
3083 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3084 : const char *pszTargetKey, const char *pszUnits,
3085 : double dfInMeters)
3086 :
3087 : {
3088 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3089 :
3090 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3091 1 : dfInMeters);
3092 : }
3093 :
3094 : /************************************************************************/
3095 : /* GetLinearUnits() */
3096 : /************************************************************************/
3097 :
3098 : /**
3099 : * \brief Fetch linear projection units.
3100 : *
3101 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3102 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3103 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3104 : * axis units will be returned.
3105 : *
3106 : * This method does the same thing as the C function OSRGetLinearUnits()
3107 : *
3108 : * @param ppszName a pointer to be updated with the pointer to the units name.
3109 : * The returned value remains internal to the OGRSpatialReference and should
3110 : * not be freed, or modified. It may be invalidated on the next
3111 : * OGRSpatialReference call.
3112 : *
3113 : * @return the value to multiply by linear distances to transform them to
3114 : * meters.
3115 : * @deprecated Use GetLinearUnits(const char**) const.
3116 : */
3117 :
3118 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3119 :
3120 : {
3121 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3122 : }
3123 :
3124 : /**
3125 : * \brief Fetch linear projection units.
3126 : *
3127 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3128 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3129 : * for units.
3130 : *
3131 : * This method does the same thing as the C function OSRGetLinearUnits()
3132 : *
3133 : * @param ppszName a pointer to be updated with the pointer to the units name.
3134 : * The returned value remains internal to the OGRSpatialReference and should
3135 : * not be freed, or modified. It may be invalidated on the next
3136 : * OGRSpatialReference call.
3137 : *
3138 : * @return the value to multiply by linear distances to transform them to
3139 : * meters.
3140 : */
3141 :
3142 20473 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3143 :
3144 : {
3145 20473 : return GetTargetLinearUnits(nullptr, ppszName);
3146 : }
3147 :
3148 : /************************************************************************/
3149 : /* OSRGetLinearUnits() */
3150 : /************************************************************************/
3151 :
3152 : /**
3153 : * \brief Fetch linear projection units.
3154 : *
3155 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3156 : */
3157 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3158 :
3159 : {
3160 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3161 :
3162 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3163 : }
3164 :
3165 : /************************************************************************/
3166 : /* GetTargetLinearUnits() */
3167 : /************************************************************************/
3168 :
3169 : /**
3170 : * \brief Fetch linear units for target.
3171 : *
3172 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3173 : *
3174 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3175 : *
3176 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3177 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3178 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3179 : * @param ppszName a pointer to be updated with the pointer to the units name.
3180 : * The returned value remains internal to the OGRSpatialReference and should not
3181 : * be freed, or modified. It may be invalidated on the next
3182 : * OGRSpatialReference call. ppszName can be set to NULL.
3183 : *
3184 : * @return the value to multiply by linear distances to transform them to
3185 : * meters.
3186 : *
3187 : * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3188 : * const.
3189 : */
3190 :
3191 20624 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3192 : const char **ppszName) const
3193 :
3194 : {
3195 41248 : TAKE_OPTIONAL_LOCK();
3196 :
3197 20624 : d->refreshProjObj();
3198 :
3199 20624 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3200 20624 : if (pszTargetKey == nullptr)
3201 : {
3202 : // Use cached result if available
3203 20533 : if (!d->m_osLinearUnits.empty())
3204 : {
3205 8750 : if (ppszName)
3206 7969 : *ppszName = d->m_osLinearUnits.c_str();
3207 8750 : return d->dfToMeter;
3208 : }
3209 :
3210 : while (true)
3211 : {
3212 11783 : if (d->m_pj_crs == nullptr)
3213 : {
3214 242 : break;
3215 : }
3216 :
3217 11541 : d->demoteFromBoundCRS();
3218 11541 : PJ *coordSys = nullptr;
3219 11541 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3220 : {
3221 37 : for (int iComponent = 0; iComponent < 2; iComponent++)
3222 : {
3223 37 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3224 37 : d->m_pj_crs, iComponent);
3225 37 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3226 : {
3227 : auto temp =
3228 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3229 0 : proj_destroy(subCRS);
3230 0 : subCRS = temp;
3231 : }
3232 74 : if (subCRS &&
3233 37 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3234 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3235 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3236 : {
3237 31 : coordSys = proj_crs_get_coordinate_system(
3238 : d->getPROJContext(), subCRS);
3239 31 : proj_destroy(subCRS);
3240 31 : break;
3241 : }
3242 6 : else if (subCRS)
3243 : {
3244 6 : proj_destroy(subCRS);
3245 : }
3246 : }
3247 31 : if (coordSys == nullptr)
3248 : {
3249 0 : d->undoDemoteFromBoundCRS();
3250 0 : break;
3251 : }
3252 : }
3253 : else
3254 : {
3255 11510 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3256 11510 : d->m_pj_crs);
3257 : }
3258 :
3259 11541 : d->undoDemoteFromBoundCRS();
3260 11541 : if (!coordSys)
3261 : {
3262 0 : break;
3263 : }
3264 11541 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3265 :
3266 11541 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3267 2242 : csType != PJ_CS_TYPE_VERTICAL &&
3268 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3269 : csType != PJ_CS_TYPE_SPHERICAL)
3270 : {
3271 0 : proj_destroy(coordSys);
3272 0 : break;
3273 : }
3274 :
3275 11541 : int axis = 0;
3276 :
3277 11541 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3278 : csType == PJ_CS_TYPE_SPHERICAL)
3279 : {
3280 : const int axisCount =
3281 2242 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3282 :
3283 2242 : if (axisCount == 3)
3284 : {
3285 4 : axis = 2;
3286 : }
3287 : else
3288 : {
3289 2238 : proj_destroy(coordSys);
3290 2238 : break;
3291 : }
3292 : }
3293 :
3294 9303 : double dfConvFactor = 0.0;
3295 9303 : const char *pszUnitName = nullptr;
3296 9303 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3297 : nullptr, nullptr, nullptr, &dfConvFactor,
3298 : &pszUnitName, nullptr, nullptr))
3299 : {
3300 0 : proj_destroy(coordSys);
3301 0 : break;
3302 : }
3303 :
3304 9303 : d->m_osLinearUnits = pszUnitName;
3305 9303 : d->dfToMeter = dfConvFactor;
3306 9303 : if (ppszName)
3307 1209 : *ppszName = d->m_osLinearUnits.c_str();
3308 :
3309 9303 : proj_destroy(coordSys);
3310 9303 : return dfConvFactor;
3311 : }
3312 :
3313 2480 : d->m_osLinearUnits = "unknown";
3314 2480 : d->dfToMeter = 1.0;
3315 :
3316 2480 : if (ppszName != nullptr)
3317 2294 : *ppszName = d->m_osLinearUnits.c_str();
3318 2480 : return 1.0;
3319 : }
3320 :
3321 91 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3322 :
3323 91 : if (ppszName != nullptr)
3324 37 : *ppszName = "unknown";
3325 :
3326 91 : if (poCS == nullptr)
3327 53 : return 1.0;
3328 :
3329 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3330 : {
3331 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3332 :
3333 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3334 : {
3335 38 : if (ppszName != nullptr)
3336 37 : *ppszName = poChild->GetChild(0)->GetValue();
3337 :
3338 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3339 : }
3340 : }
3341 :
3342 0 : return 1.0;
3343 : }
3344 :
3345 : /**
3346 : * \brief Fetch linear units for target.
3347 : *
3348 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3349 : *
3350 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3351 : *
3352 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3353 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3354 : * GEOCCS and VERT_CS are looked up)
3355 : * @param ppszName a pointer to be updated with the pointer to the units name.
3356 : * The returned value remains internal to the OGRSpatialReference and should not
3357 : * be freed, or modified. It may be invalidated on the next
3358 : * OGRSpatialReference call. ppszName can be set to NULL.
3359 : *
3360 : * @return the value to multiply by linear distances to transform them to
3361 : * meters.
3362 : *
3363 : */
3364 :
3365 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3366 : char **ppszName) const
3367 :
3368 : {
3369 0 : return GetTargetLinearUnits(pszTargetKey,
3370 0 : const_cast<const char **>(ppszName));
3371 : }
3372 :
3373 : /************************************************************************/
3374 : /* OSRGetTargetLinearUnits() */
3375 : /************************************************************************/
3376 :
3377 : /**
3378 : * \brief Fetch linear projection units.
3379 : *
3380 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3381 : *
3382 : */
3383 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3384 : const char *pszTargetKey, char **ppszName)
3385 :
3386 : {
3387 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3388 :
3389 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3390 4 : pszTargetKey, const_cast<const char **>(ppszName));
3391 : }
3392 :
3393 : /************************************************************************/
3394 : /* GetPrimeMeridian() */
3395 : /************************************************************************/
3396 :
3397 : /**
3398 : * \brief Fetch prime meridian info.
3399 : *
3400 : * Returns the offset of the prime meridian from greenwich in degrees,
3401 : * and the prime meridian name (if requested). If no PRIMEM value exists
3402 : * in the coordinate system definition a value of "Greenwich" and an
3403 : * offset of 0.0 is assumed.
3404 : *
3405 : * If the prime meridian name is returned, the pointer is to an internal
3406 : * copy of the name. It should not be freed, altered or depended on after
3407 : * the next OGR call.
3408 : *
3409 : * This method is the same as the C function OSRGetPrimeMeridian().
3410 : *
3411 : * @param ppszName return location for prime meridian name. If NULL, name
3412 : * is not returned.
3413 : *
3414 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3415 : * degrees.
3416 : * @deprecated Use GetPrimeMeridian(const char**) const.
3417 : */
3418 :
3419 1480 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3420 :
3421 : {
3422 2960 : TAKE_OPTIONAL_LOCK();
3423 :
3424 1480 : d->refreshProjObj();
3425 :
3426 1480 : if (!d->m_osPrimeMeridianName.empty())
3427 : {
3428 60 : if (ppszName != nullptr)
3429 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3430 60 : return d->dfFromGreenwich;
3431 : }
3432 :
3433 : while (true)
3434 : {
3435 1420 : if (!d->m_pj_crs)
3436 0 : break;
3437 :
3438 1420 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3439 1420 : if (!pm)
3440 0 : break;
3441 :
3442 1420 : d->m_osPrimeMeridianName = proj_get_name(pm);
3443 1420 : if (ppszName)
3444 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3445 1420 : double dfLongitude = 0.0;
3446 1420 : double dfConvFactor = 0.0;
3447 1420 : proj_prime_meridian_get_parameters(
3448 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3449 1420 : proj_destroy(pm);
3450 2840 : d->dfFromGreenwich =
3451 1420 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3452 1420 : return d->dfFromGreenwich;
3453 : }
3454 :
3455 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3456 0 : d->dfFromGreenwich = 0.0;
3457 0 : if (ppszName != nullptr)
3458 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3459 0 : return d->dfFromGreenwich;
3460 : }
3461 :
3462 : /**
3463 : * \brief Fetch prime meridian info.
3464 : *
3465 : * Returns the offset of the prime meridian from greenwich in degrees,
3466 : * and the prime meridian name (if requested). If no PRIMEM value exists
3467 : * in the coordinate system definition a value of "Greenwich" and an
3468 : * offset of 0.0 is assumed.
3469 : *
3470 : * If the prime meridian name is returned, the pointer is to an internal
3471 : * copy of the name. It should not be freed, altered or depended on after
3472 : * the next OGR call.
3473 : *
3474 : * This method is the same as the C function OSRGetPrimeMeridian().
3475 : *
3476 : * @param ppszName return location for prime meridian name. If NULL, name
3477 : * is not returned.
3478 : *
3479 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3480 : * degrees.
3481 : */
3482 :
3483 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3484 :
3485 : {
3486 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3487 : }
3488 :
3489 : /************************************************************************/
3490 : /* OSRGetPrimeMeridian() */
3491 : /************************************************************************/
3492 :
3493 : /**
3494 : * \brief Fetch prime meridian info.
3495 : *
3496 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3497 : */
3498 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3499 :
3500 : {
3501 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3502 :
3503 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3504 0 : const_cast<const char **>(ppszName));
3505 : }
3506 :
3507 : /************************************************************************/
3508 : /* SetGeogCS() */
3509 : /************************************************************************/
3510 :
3511 : /**
3512 : * \brief Set geographic coordinate system.
3513 : *
3514 : * This method is used to set the datum, ellipsoid, prime meridian and
3515 : * angular units for a geographic coordinate system. It can be used on its
3516 : * own to establish a geographic spatial reference, or applied to a
3517 : * projected coordinate system to establish the underlying geographic
3518 : * coordinate system.
3519 : *
3520 : * This method does the same as the C function OSRSetGeogCS().
3521 : *
3522 : * @param pszGeogName user visible name for the geographic coordinate system
3523 : * (not to serve as a key).
3524 : *
3525 : * @param pszDatumName key name for this datum. The OpenGIS specification
3526 : * lists some known values, and otherwise EPSG datum names with a standard
3527 : * transformation are considered legal keys.
3528 : *
3529 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3530 : *
3531 : * @param dfSemiMajor the semi major axis of the spheroid.
3532 : *
3533 : * @param dfInvFlattening the inverse flattening for the spheroid.
3534 : * This can be computed from the semi minor axis as
3535 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3536 : *
3537 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3538 : * If this is NULL a default value of "Greenwich" will be used.
3539 : *
3540 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3541 : * meridian. Always in Degrees
3542 : *
3543 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3544 : * standard names). If NULL a value of "degrees" will be assumed.
3545 : *
3546 : * @param dfConvertToRadians value to multiply angular units by to transform
3547 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3548 : * pszAngularUnits is NULL.
3549 : *
3550 : * @return OGRERR_NONE on success.
3551 : */
3552 :
3553 9523 : OGRErr OGRSpatialReference::SetGeogCS(
3554 : const char *pszGeogName, const char *pszDatumName,
3555 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3556 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3557 : double dfConvertToRadians)
3558 :
3559 : {
3560 19046 : TAKE_OPTIONAL_LOCK();
3561 :
3562 9523 : d->bNormInfoSet = FALSE;
3563 9523 : d->m_osAngularUnits.clear();
3564 9523 : d->m_dfAngularUnitToRadian = 0.0;
3565 9523 : d->m_osPrimeMeridianName.clear();
3566 9523 : d->dfFromGreenwich = 0.0;
3567 :
3568 : /* -------------------------------------------------------------------- */
3569 : /* For a geocentric coordinate system we want to set the datum */
3570 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3571 : /* temporary srs and use the copy method which has special */
3572 : /* handling for GEOCCS. */
3573 : /* -------------------------------------------------------------------- */
3574 9523 : if (IsGeocentric())
3575 : {
3576 4 : OGRSpatialReference oGCS;
3577 :
3578 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3579 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3580 : dfConvertToRadians);
3581 2 : return CopyGeogCSFrom(&oGCS);
3582 : }
3583 :
3584 9521 : auto cs = proj_create_ellipsoidal_2D_cs(
3585 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3586 : dfConvertToRadians);
3587 : // Prime meridian expressed in Degree
3588 9521 : auto obj = proj_create_geographic_crs(
3589 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3590 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3591 9521 : proj_destroy(cs);
3592 :
3593 14371 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3594 4850 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3595 : {
3596 4671 : d->setPjCRS(obj);
3597 : }
3598 4850 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3599 : {
3600 9700 : d->setPjCRS(
3601 4850 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3602 4850 : proj_destroy(obj);
3603 : }
3604 : else
3605 : {
3606 0 : proj_destroy(obj);
3607 : }
3608 :
3609 9521 : return OGRERR_NONE;
3610 : }
3611 :
3612 : /************************************************************************/
3613 : /* OSRSetGeogCS() */
3614 : /************************************************************************/
3615 :
3616 : /**
3617 : * \brief Set geographic coordinate system.
3618 : *
3619 : * This function is the same as OGRSpatialReference::SetGeogCS()
3620 : */
3621 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3622 : const char *pszDatumName, const char *pszSpheroidName,
3623 : double dfSemiMajor, double dfInvFlattening,
3624 : const char *pszPMName, double dfPMOffset,
3625 : const char *pszAngularUnits, double dfConvertToRadians)
3626 :
3627 : {
3628 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3629 :
3630 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3631 : pszSpheroidName, dfSemiMajor,
3632 : dfInvFlattening, pszPMName, dfPMOffset,
3633 18 : pszAngularUnits, dfConvertToRadians);
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* SetWellKnownGeogCS() */
3638 : /************************************************************************/
3639 :
3640 : /**
3641 : * \brief Set a GeogCS based on well known name.
3642 : *
3643 : * This may be called on an empty OGRSpatialReference to make a geographic
3644 : * coordinate system, or on something with an existing PROJCS node to
3645 : * set the underlying geographic coordinate system of a projected coordinate
3646 : * system.
3647 : *
3648 : * The following well known text values are currently supported,
3649 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3650 : * <ul>
3651 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3652 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3653 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3654 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3655 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3656 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3657 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3658 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3659 : * </ul>
3660 : *
3661 : * @param pszName name of well known geographic coordinate system.
3662 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3663 : * recognised, the target object is already initialized, or an EPSG value
3664 : * can't be successfully looked up.
3665 : */
3666 :
3667 3129 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3668 :
3669 : {
3670 6258 : TAKE_OPTIONAL_LOCK();
3671 :
3672 : /* -------------------------------------------------------------------- */
3673 : /* Check for EPSG authority numbers. */
3674 : /* -------------------------------------------------------------------- */
3675 3129 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3676 : {
3677 84 : OGRSpatialReference oSRS2;
3678 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3679 42 : if (eErr != OGRERR_NONE)
3680 0 : return eErr;
3681 :
3682 42 : if (!oSRS2.IsGeographic())
3683 0 : return OGRERR_FAILURE;
3684 :
3685 42 : return CopyGeogCSFrom(&oSRS2);
3686 : }
3687 :
3688 : /* -------------------------------------------------------------------- */
3689 : /* Check for simple names. */
3690 : /* -------------------------------------------------------------------- */
3691 3087 : const char *pszWKT = nullptr;
3692 :
3693 3087 : if (EQUAL(pszName, "WGS84"))
3694 : {
3695 2273 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3696 : }
3697 814 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3698 : {
3699 632 : pszWKT =
3700 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3701 : "ELLIPSOID[\"WGS "
3702 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3703 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3704 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3705 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3706 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3707 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3708 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3709 : "ID[\"OGC\",\"CRS84\"]]";
3710 : }
3711 182 : else if (EQUAL(pszName, "WGS72"))
3712 19 : pszWKT =
3713 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3714 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3715 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3716 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3717 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3718 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3719 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3720 :
3721 163 : else if (EQUAL(pszName, "NAD27"))
3722 135 : pszWKT =
3723 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3724 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3725 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3726 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3727 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3728 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3729 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3730 :
3731 28 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3732 0 : pszWKT =
3733 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3734 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3735 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3736 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3737 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3738 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3739 :
3740 28 : else if (EQUAL(pszName, "NAD83"))
3741 24 : pszWKT =
3742 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3743 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3744 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3745 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3746 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3747 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3748 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3749 : "\"EPSG\",\"4269\"]]";
3750 :
3751 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3752 0 : pszWKT =
3753 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3754 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3755 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3756 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3757 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3758 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3759 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3760 :
3761 : else
3762 4 : return OGRERR_FAILURE;
3763 :
3764 : /* -------------------------------------------------------------------- */
3765 : /* Import the WKT */
3766 : /* -------------------------------------------------------------------- */
3767 6166 : OGRSpatialReference oSRS2;
3768 3083 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3769 3083 : if (eErr != OGRERR_NONE)
3770 0 : return eErr;
3771 :
3772 : /* -------------------------------------------------------------------- */
3773 : /* Copy over. */
3774 : /* -------------------------------------------------------------------- */
3775 3083 : return CopyGeogCSFrom(&oSRS2);
3776 : }
3777 :
3778 : /************************************************************************/
3779 : /* OSRSetWellKnownGeogCS() */
3780 : /************************************************************************/
3781 :
3782 : /**
3783 : * \brief Set a GeogCS based on well known name.
3784 : *
3785 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3786 : */
3787 152 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3788 :
3789 : {
3790 152 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3791 :
3792 152 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3793 : }
3794 :
3795 : /************************************************************************/
3796 : /* CopyGeogCSFrom() */
3797 : /************************************************************************/
3798 :
3799 : /**
3800 : * \brief Copy GEOGCS from another OGRSpatialReference.
3801 : *
3802 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3803 : * If this object has a PROJCS root already, the GEOGCS is installed within
3804 : * it, otherwise it is installed as the root.
3805 : *
3806 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3807 : *
3808 : * @return OGRERR_NONE on success or an error code.
3809 : */
3810 :
3811 3722 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3812 :
3813 : {
3814 7444 : TAKE_OPTIONAL_LOCK();
3815 :
3816 3722 : d->bNormInfoSet = FALSE;
3817 3722 : d->m_osAngularUnits.clear();
3818 3722 : d->m_dfAngularUnitToRadian = 0.0;
3819 3722 : d->m_osPrimeMeridianName.clear();
3820 3722 : d->dfFromGreenwich = 0.0;
3821 :
3822 3722 : d->refreshProjObj();
3823 3722 : poSrcSRS->d->refreshProjObj();
3824 3722 : if (!poSrcSRS->d->m_pj_crs)
3825 : {
3826 1 : return OGRERR_FAILURE;
3827 : }
3828 : auto geodCRS =
3829 3721 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3830 3721 : if (!geodCRS)
3831 : {
3832 0 : return OGRERR_FAILURE;
3833 : }
3834 :
3835 : /* -------------------------------------------------------------------- */
3836 : /* Handle geocentric coordinate systems specially. We just */
3837 : /* want to copy the DATUM. */
3838 : /* -------------------------------------------------------------------- */
3839 3721 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3840 : {
3841 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3842 : #if PROJ_VERSION_MAJOR > 7 || \
3843 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3844 : if (datum == nullptr)
3845 : {
3846 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3847 : }
3848 : #endif
3849 3 : if (datum == nullptr)
3850 : {
3851 0 : proj_destroy(geodCRS);
3852 0 : return OGRERR_FAILURE;
3853 : }
3854 :
3855 3 : const char *pszUnitName = nullptr;
3856 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3857 :
3858 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3859 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3860 : unitConvFactor);
3861 3 : proj_destroy(datum);
3862 :
3863 3 : d->setPjCRS(pj_crs);
3864 : }
3865 :
3866 3718 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3867 : {
3868 319 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3869 319 : d->m_pj_crs, geodCRS);
3870 319 : d->setPjCRS(pj_crs);
3871 : }
3872 :
3873 : else
3874 : {
3875 3399 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3876 : }
3877 :
3878 : // Apply TOWGS84 of source CRS
3879 3721 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3880 : {
3881 : auto target =
3882 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3883 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3884 1 : poSrcSRS->d->m_pj_crs);
3885 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3886 : target, co));
3887 1 : proj_destroy(target);
3888 1 : proj_destroy(co);
3889 : }
3890 :
3891 3721 : proj_destroy(geodCRS);
3892 :
3893 3721 : return OGRERR_NONE;
3894 : }
3895 :
3896 : /************************************************************************/
3897 : /* OSRCopyGeogCSFrom() */
3898 : /************************************************************************/
3899 :
3900 : /**
3901 : * \brief Copy GEOGCS from another OGRSpatialReference.
3902 : *
3903 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3904 : */
3905 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3906 : const OGRSpatialReferenceH hSrcSRS)
3907 :
3908 : {
3909 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3910 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3911 :
3912 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3913 : }
3914 :
3915 : /************************************************************************/
3916 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3917 : /************************************************************************/
3918 :
3919 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3920 : *
3921 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3922 : */
3923 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3924 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3925 :
3926 : /**
3927 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3928 : */
3929 2813 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3930 : {
3931 2813 : return SET_FROM_USER_INPUT_LIMITATIONS;
3932 : }
3933 :
3934 : /************************************************************************/
3935 : /* RemoveIDFromMemberOfEnsembles() */
3936 : /************************************************************************/
3937 :
3938 : // cppcheck-suppress constParameterReference
3939 198 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3940 : {
3941 : // Remove "id" from members of datum ensembles for compatibility with
3942 : // older PROJ versions
3943 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3944 : // and https://github.com/OSGeo/PROJ/pull/3221
3945 198 : if (obj.GetType() == CPLJSONObject::Type::Object)
3946 : {
3947 243 : for (auto &subObj : obj.GetChildren())
3948 : {
3949 191 : RemoveIDFromMemberOfEnsembles(subObj);
3950 : }
3951 : }
3952 162 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3953 162 : obj.GetName() == "members")
3954 : {
3955 51 : for (auto &subObj : obj.ToArray())
3956 : {
3957 44 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3958 : {
3959 43 : subObj.Delete("id");
3960 : }
3961 : }
3962 : }
3963 198 : }
3964 :
3965 : /************************************************************************/
3966 : /* SetFromUserInput() */
3967 : /************************************************************************/
3968 :
3969 : /**
3970 : * \brief Set spatial reference from various text formats.
3971 : *
3972 : * This method will examine the provided input, and try to deduce the
3973 : * format, and then use it to initialize the spatial reference system. It
3974 : * may take the following forms:
3975 : *
3976 : * <ol>
3977 : * <li> Well Known Text definition - passed on to importFromWkt().
3978 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3979 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3980 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3981 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3982 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3983 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3984 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3985 : * WGS84 or WGS72.
3986 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3987 : * <li> PROJJSON (PROJ >= 6.2)
3988 : * </ol>
3989 : *
3990 : * It is expected that this method will be extended in the future to support
3991 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3992 : * State Plane definitions.
3993 : *
3994 : * This method is intended to be flexible, but by its nature it is
3995 : * imprecise as it must guess information about the format intended. When
3996 : * possible applications should call the specific method appropriate if the
3997 : * input is known to be in a particular format.
3998 : *
3999 : * This method does the same thing as the OSRSetFromUserInput() function.
4000 : *
4001 : * @param pszDefinition text definition to try to deduce SRS from.
4002 : *
4003 : * @return OGRERR_NONE on success, or an error code if the name isn't
4004 : * recognised, the definition is corrupt, or an EPSG value can't be
4005 : * successfully looked up.
4006 : */
4007 :
4008 19447 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4009 : {
4010 19447 : return SetFromUserInput(pszDefinition, nullptr);
4011 : }
4012 :
4013 : /**
4014 : * \brief Set spatial reference from various text formats.
4015 : *
4016 : * This method will examine the provided input, and try to deduce the
4017 : * format, and then use it to initialize the spatial reference system. It
4018 : * may take the following forms:
4019 : *
4020 : * <ol>
4021 : * <li> Well Known Text definition - passed on to importFromWkt().
4022 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4023 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4024 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4025 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4026 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4027 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4028 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4029 : * WGS84 or WGS72.
4030 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4031 : * <li> PROJJSON (PROJ >= 6.2)
4032 : * </ol>
4033 : *
4034 : * It is expected that this method will be extended in the future to support
4035 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4036 : * State Plane definitions.
4037 : *
4038 : * This method is intended to be flexible, but by its nature it is
4039 : * imprecise as it must guess information about the format intended. When
4040 : * possible applications should call the specific method appropriate if the
4041 : * input is known to be in a particular format.
4042 : *
4043 : * This method does the same thing as the OSRSetFromUserInput() and
4044 : * OSRSetFromUserInputEx() functions.
4045 : *
4046 : * @param pszDefinition text definition to try to deduce SRS from.
4047 : *
4048 : * @param papszOptions NULL terminated list of options, or NULL.
4049 : * <ol>
4050 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4051 : * Whether http:// or https:// access is allowed. Defaults to YES.
4052 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4053 : * Whether reading a file using the Virtual File System layer is allowed
4054 : * (can also involve network access). Defaults to YES.
4055 : * </ol>
4056 : *
4057 : * @return OGRERR_NONE on success, or an error code if the name isn't
4058 : * recognised, the definition is corrupt, or an EPSG value can't be
4059 : * successfully looked up.
4060 : */
4061 :
4062 26627 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4063 : CSLConstList papszOptions)
4064 : {
4065 53254 : TAKE_OPTIONAL_LOCK();
4066 :
4067 : // Skip leading white space
4068 26629 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4069 2 : pszDefinition++;
4070 :
4071 26627 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4072 : {
4073 1 : pszDefinition += 6;
4074 : }
4075 :
4076 : /* -------------------------------------------------------------------- */
4077 : /* Is it a recognised syntax? */
4078 : /* -------------------------------------------------------------------- */
4079 26627 : const char *const wktKeywords[] = {
4080 : // WKT1
4081 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4082 : // WKT2"
4083 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4084 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4085 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4086 395813 : for (const char *keyword : wktKeywords)
4087 : {
4088 377926 : if (STARTS_WITH_CI(pszDefinition, keyword))
4089 : {
4090 8740 : return importFromWkt(pszDefinition);
4091 : }
4092 : }
4093 :
4094 17887 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4095 17887 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4096 : {
4097 10355 : OGRErr eStatus = OGRERR_NONE;
4098 :
4099 10355 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4100 : {
4101 : // Use proj_create() as it allows things like EPSG:3157+4617
4102 : // that are not normally supported by the below code that
4103 : // builds manually a compound CRS
4104 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4105 62 : if (!pj)
4106 : {
4107 1 : return OGRERR_FAILURE;
4108 : }
4109 61 : Clear();
4110 61 : d->setPjCRS(pj);
4111 61 : return OGRERR_NONE;
4112 : }
4113 : else
4114 : {
4115 : eStatus =
4116 10293 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4117 : }
4118 :
4119 10293 : return eStatus;
4120 : }
4121 :
4122 7532 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4123 6814 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4124 6813 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4125 6755 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4126 6755 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4127 6755 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4128 777 : return importFromURN(pszDefinition);
4129 :
4130 6755 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4131 6753 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4132 6752 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4133 1441 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4134 1440 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4135 5315 : return importFromCRSURL(pszDefinition);
4136 :
4137 1440 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4138 1 : return importFromWMSAUTO(pszDefinition);
4139 :
4140 : // WMS/WCS OGC codes like OGC:CRS84.
4141 1439 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4142 76 : return SetWellKnownGeogCS(pszDefinition + 4);
4143 :
4144 1363 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4145 1 : return SetWellKnownGeogCS(pszDefinition);
4146 :
4147 1362 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4148 : {
4149 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4150 0 : char *pszCode = strstr(pszFile, ",") + 1;
4151 :
4152 0 : pszCode[-1] = '\0';
4153 :
4154 0 : OGRErr err = importFromDict(pszFile, pszCode);
4155 0 : CPLFree(pszFile);
4156 :
4157 0 : return err;
4158 : }
4159 :
4160 1362 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4161 1357 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4162 : {
4163 409 : Clear();
4164 409 : return SetWellKnownGeogCS(pszDefinition);
4165 : }
4166 :
4167 : // PROJJSON
4168 953 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4169 178 : (strstr(pszDefinition, "GeodeticCRS") ||
4170 178 : strstr(pszDefinition, "GeographicCRS") ||
4171 129 : strstr(pszDefinition, "ProjectedCRS") ||
4172 0 : strstr(pszDefinition, "VerticalCRS") ||
4173 0 : strstr(pszDefinition, "BoundCRS") ||
4174 0 : strstr(pszDefinition, "CompoundCRS") ||
4175 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4176 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4177 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4178 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4179 0 : strstr(pszDefinition, "EngineeringCRS") ||
4180 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4181 0 : strstr(pszDefinition, "ParametricCRS") ||
4182 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4183 0 : strstr(pszDefinition, "TemporalCRS") ||
4184 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4185 : {
4186 : PJ *pj;
4187 178 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4188 : {
4189 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4190 : // a unknown id.
4191 7 : CPLJSONDocument oCRSDoc;
4192 7 : if (!oCRSDoc.LoadMemory(pszDefinition))
4193 0 : return OGRERR_CORRUPT_DATA;
4194 7 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4195 7 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4196 7 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4197 : }
4198 : else
4199 : {
4200 171 : pj = proj_create(d->getPROJContext(), pszDefinition);
4201 : }
4202 178 : if (!pj)
4203 : {
4204 2 : return OGRERR_FAILURE;
4205 : }
4206 176 : Clear();
4207 176 : d->setPjCRS(pj);
4208 176 : return OGRERR_NONE;
4209 : }
4210 :
4211 775 : if (strstr(pszDefinition, "+proj") != nullptr ||
4212 325 : strstr(pszDefinition, "+init") != nullptr)
4213 450 : return importFromProj4(pszDefinition);
4214 :
4215 325 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4216 300 : STARTS_WITH_CI(pszDefinition, "https://"))
4217 : {
4218 26 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4219 : "ALLOW_NETWORK_ACCESS", "YES")))
4220 0 : return importFromUrl(pszDefinition);
4221 :
4222 26 : CPLError(CE_Failure, CPLE_AppDefined,
4223 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4224 : pszDefinition);
4225 26 : return OGRERR_FAILURE;
4226 : }
4227 :
4228 299 : if (EQUAL(pszDefinition, "osgb:BNG"))
4229 : {
4230 8 : return importFromEPSG(27700);
4231 : }
4232 :
4233 : // Used by German CityGML files
4234 291 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4235 : {
4236 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4237 0 : return SetFromUserInput("EPSG:25832+5783");
4238 : }
4239 291 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4240 : {
4241 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4242 3 : return SetFromUserInput("EPSG:25832+7837");
4243 : }
4244 :
4245 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4246 288 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4247 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4248 288 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4249 8 : return importFromEPSG(6668); // JGD2011
4250 280 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4251 : {
4252 : // FIXME when EPSG attributes a CRS code
4253 3 : return importFromWkt(
4254 : "GEOGCRS[\"JGD2024\",\n"
4255 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4256 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4257 : " LENGTHUNIT[\"metre\",1]]],\n"
4258 : " PRIMEM[\"Greenwich\",0,\n"
4259 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4260 : " CS[ellipsoidal,2],\n"
4261 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4262 : " ORDER[1],\n"
4263 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4264 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4265 : " ORDER[2],\n"
4266 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4267 : " USAGE[\n"
4268 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4269 : " AREA[\"Japan - onshore and offshore.\"],\n"
4270 3 : " BBOX[17.09,122.38,46.05,157.65]]]");
4271 : }
4272 :
4273 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4274 277 : const char *pszDot = strrchr(pszDefinition, ':');
4275 277 : if (pszDot)
4276 : {
4277 125 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4278 : auto authorities =
4279 125 : proj_get_authorities_from_database(d->getPROJContext());
4280 125 : if (authorities)
4281 : {
4282 125 : std::set<std::string> aosCandidateAuthorities;
4283 290 : for (auto iter = authorities; *iter; ++iter)
4284 : {
4285 288 : if (*iter == osPrefix)
4286 : {
4287 123 : aosCandidateAuthorities.clear();
4288 123 : aosCandidateAuthorities.insert(*iter);
4289 123 : break;
4290 : }
4291 : // Deal with "IAU_2015" as authority in the list and input
4292 : // "IAU:code"
4293 165 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4294 165 : 0 &&
4295 0 : (*iter)[osPrefix.size()] == '_')
4296 : {
4297 0 : aosCandidateAuthorities.insert(*iter);
4298 : }
4299 : // Deal with "IAU_2015" as authority in the list and input
4300 : // "IAU:2015:code"
4301 330 : else if (osPrefix.find(':') != std::string::npos &&
4302 165 : osPrefix.size() == strlen(*iter) &&
4303 165 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4304 : {
4305 0 : aosCandidateAuthorities.clear();
4306 0 : aosCandidateAuthorities.insert(*iter);
4307 0 : break;
4308 : }
4309 : }
4310 :
4311 125 : proj_string_list_destroy(authorities);
4312 :
4313 125 : if (!aosCandidateAuthorities.empty())
4314 : {
4315 123 : auto obj = proj_create_from_database(
4316 : d->getPROJContext(),
4317 123 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4318 : PJ_CATEGORY_CRS, false, nullptr);
4319 123 : if (!obj)
4320 : {
4321 16 : return OGRERR_FAILURE;
4322 : }
4323 107 : Clear();
4324 107 : d->setPjCRS(obj);
4325 107 : return OGRERR_NONE;
4326 : }
4327 : }
4328 : }
4329 :
4330 : /* -------------------------------------------------------------------- */
4331 : /* Try to open it as a file. */
4332 : /* -------------------------------------------------------------------- */
4333 154 : if (!CPLTestBool(
4334 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4335 : {
4336 : VSIStatBufL sStat;
4337 40 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4338 20 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4339 : {
4340 0 : CPLError(CE_Failure, CPLE_AppDefined,
4341 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4342 : pszDefinition);
4343 0 : return OGRERR_FAILURE;
4344 : }
4345 : // We used to silently return an error without a CE_Failure message
4346 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4347 20 : return OGRERR_CORRUPT_DATA;
4348 : }
4349 :
4350 268 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4351 134 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4352 134 : if (fp == nullptr)
4353 131 : return OGRERR_CORRUPT_DATA;
4354 :
4355 3 : const size_t nBufMax = 100000;
4356 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4357 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4358 3 : VSIFCloseL(fp);
4359 :
4360 3 : if (nBytes == nBufMax - 1)
4361 : {
4362 0 : CPLDebug("OGR",
4363 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4364 : "but it is to large for our generous buffer. Is it really "
4365 : "just a WKT definition?",
4366 : pszDefinition);
4367 0 : CPLFree(pszBuffer);
4368 0 : return OGRERR_FAILURE;
4369 : }
4370 :
4371 3 : pszBuffer[nBytes] = '\0';
4372 :
4373 3 : char *pszBufPtr = pszBuffer;
4374 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4375 0 : pszBufPtr++;
4376 :
4377 3 : OGRErr err = OGRERR_NONE;
4378 3 : if (pszBufPtr[0] == '<')
4379 0 : err = importFromXML(pszBufPtr);
4380 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4381 3 : strstr(pszBuffer, "+init") != nullptr) &&
4382 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4383 0 : strstr(pszBuffer, "extension") == nullptr))
4384 0 : err = importFromProj4(pszBufPtr);
4385 : else
4386 : {
4387 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4388 : {
4389 0 : pszBufPtr += 6;
4390 : }
4391 :
4392 : // coverity[tainted_data]
4393 3 : err = importFromWkt(pszBufPtr);
4394 : }
4395 :
4396 3 : CPLFree(pszBuffer);
4397 :
4398 3 : return err;
4399 : }
4400 :
4401 : /************************************************************************/
4402 : /* OSRSetFromUserInput() */
4403 : /************************************************************************/
4404 :
4405 : /**
4406 : * \brief Set spatial reference from various text formats.
4407 : *
4408 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4409 : *
4410 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4411 : */
4412 308 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4413 : const char *pszDef)
4414 :
4415 : {
4416 308 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4417 :
4418 308 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4419 : }
4420 :
4421 : /************************************************************************/
4422 : /* OSRSetFromUserInputEx() */
4423 : /************************************************************************/
4424 :
4425 : /**
4426 : * \brief Set spatial reference from various text formats.
4427 : *
4428 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4429 : *
4430 : * @since GDAL 3.9
4431 : */
4432 1139 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4433 : CSLConstList papszOptions)
4434 :
4435 : {
4436 1139 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4437 :
4438 1139 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4439 : }
4440 :
4441 : /************************************************************************/
4442 : /* ImportFromUrl() */
4443 : /************************************************************************/
4444 :
4445 : /**
4446 : * \brief Set spatial reference from a URL.
4447 : *
4448 : * This method will download the spatial reference at a given URL and
4449 : * feed it into SetFromUserInput for you.
4450 : *
4451 : * This method does the same thing as the OSRImportFromUrl() function.
4452 : *
4453 : * @param pszUrl text definition to try to deduce SRS from.
4454 : *
4455 : * @return OGRERR_NONE on success, or an error code with the curl
4456 : * error message if it is unable to download data.
4457 : */
4458 :
4459 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4460 :
4461 : {
4462 10 : TAKE_OPTIONAL_LOCK();
4463 :
4464 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4465 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4466 : {
4467 2 : CPLError(CE_Failure, CPLE_AppDefined,
4468 : "The given string is not recognized as a URL"
4469 : "starting with 'http://' -- %s",
4470 : pszUrl);
4471 2 : return OGRERR_FAILURE;
4472 : }
4473 :
4474 : /* -------------------------------------------------------------------- */
4475 : /* Fetch the result. */
4476 : /* -------------------------------------------------------------------- */
4477 3 : CPLErrorReset();
4478 :
4479 6 : std::string osUrl(pszUrl);
4480 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4481 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4482 : // to query WKT. To allow a static server to be used, rather append a
4483 : // "ogcwkt/" suffix.
4484 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4485 5 : "http://spatialreference.org/ref/"})
4486 : {
4487 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4488 : {
4489 : const CPLStringList aosTokens(
4490 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4491 3 : if (aosTokens.size() == 2)
4492 : {
4493 2 : osUrl = "https://spatialreference.org/ref/";
4494 2 : osUrl += aosTokens[0]; // authority
4495 2 : osUrl += '/';
4496 2 : osUrl += aosTokens[1]; // code
4497 2 : osUrl += "/ogcwkt/";
4498 : }
4499 3 : break;
4500 : }
4501 : }
4502 :
4503 3 : const char *pszTimeout = "TIMEOUT=10";
4504 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4505 :
4506 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4507 :
4508 : /* -------------------------------------------------------------------- */
4509 : /* Try to handle errors. */
4510 : /* -------------------------------------------------------------------- */
4511 :
4512 3 : if (psResult == nullptr)
4513 0 : return OGRERR_FAILURE;
4514 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4515 3 : psResult->pabyData == nullptr)
4516 : {
4517 0 : if (CPLGetLastErrorNo() == 0)
4518 : {
4519 0 : CPLError(CE_Failure, CPLE_AppDefined,
4520 : "No data was returned from the given URL");
4521 : }
4522 0 : CPLHTTPDestroyResult(psResult);
4523 0 : return OGRERR_FAILURE;
4524 : }
4525 :
4526 3 : if (psResult->nStatus != 0)
4527 : {
4528 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4529 : psResult->nStatus, psResult->pszErrBuf);
4530 0 : CPLHTTPDestroyResult(psResult);
4531 0 : return OGRERR_FAILURE;
4532 : }
4533 :
4534 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4535 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4536 3 : STARTS_WITH_CI(pszData, "https://"))
4537 : {
4538 0 : CPLError(CE_Failure, CPLE_AppDefined,
4539 : "The data that was downloaded also starts with 'http://' "
4540 : "and cannot be passed into SetFromUserInput. Is this "
4541 : "really a spatial reference definition? ");
4542 0 : CPLHTTPDestroyResult(psResult);
4543 0 : return OGRERR_FAILURE;
4544 : }
4545 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4546 : {
4547 0 : CPLHTTPDestroyResult(psResult);
4548 0 : return OGRERR_FAILURE;
4549 : }
4550 :
4551 3 : CPLHTTPDestroyResult(psResult);
4552 3 : return OGRERR_NONE;
4553 : }
4554 :
4555 : /************************************************************************/
4556 : /* OSRimportFromUrl() */
4557 : /************************************************************************/
4558 :
4559 : /**
4560 : * \brief Set spatial reference from a URL.
4561 : *
4562 : * This function is the same as OGRSpatialReference::importFromUrl()
4563 : */
4564 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4565 :
4566 : {
4567 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4568 :
4569 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4570 : }
4571 :
4572 : /************************************************************************/
4573 : /* importFromURNPart() */
4574 : /************************************************************************/
4575 5526 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4576 : const char *pszCode,
4577 : const char *pszURN)
4578 : {
4579 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4580 : (void)this;
4581 : (void)pszAuthority;
4582 : (void)pszCode;
4583 : (void)pszURN;
4584 : return OGRERR_FAILURE;
4585 : #else
4586 : /* -------------------------------------------------------------------- */
4587 : /* Is this an EPSG code? Note that we import it with EPSG */
4588 : /* preferred axis ordering for geographic coordinate systems. */
4589 : /* -------------------------------------------------------------------- */
4590 5526 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4591 4968 : return importFromEPSGA(atoi(pszCode));
4592 :
4593 : /* -------------------------------------------------------------------- */
4594 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4595 : /* -------------------------------------------------------------------- */
4596 558 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4597 0 : return importFromDict("IAU2000.wkt", pszCode);
4598 :
4599 : /* -------------------------------------------------------------------- */
4600 : /* Is this an OGC code? */
4601 : /* -------------------------------------------------------------------- */
4602 558 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4603 : {
4604 1 : CPLError(CE_Failure, CPLE_AppDefined,
4605 : "URN %s has unrecognized authority.", pszURN);
4606 1 : return OGRERR_FAILURE;
4607 : }
4608 :
4609 557 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4610 545 : return SetWellKnownGeogCS(pszCode);
4611 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4612 0 : return SetWellKnownGeogCS(pszCode);
4613 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4614 0 : return SetWellKnownGeogCS(pszCode);
4615 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4616 10 : return SetWellKnownGeogCS("CRS84");
4617 :
4618 : /* -------------------------------------------------------------------- */
4619 : /* Handle auto codes. We need to convert from format */
4620 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4621 : /* -------------------------------------------------------------------- */
4622 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4623 : {
4624 2 : char szWMSAuto[100] = {'\0'};
4625 :
4626 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4627 0 : return OGRERR_FAILURE;
4628 :
4629 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4630 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4631 : {
4632 26 : if (szWMSAuto[i] == ':')
4633 4 : szWMSAuto[i] = ',';
4634 : }
4635 :
4636 2 : return importFromWMSAUTO(szWMSAuto);
4637 : }
4638 :
4639 : /* -------------------------------------------------------------------- */
4640 : /* Not a recognise OGC item. */
4641 : /* -------------------------------------------------------------------- */
4642 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4643 : pszURN);
4644 :
4645 0 : return OGRERR_FAILURE;
4646 : #endif
4647 : }
4648 :
4649 : /************************************************************************/
4650 : /* importFromURN() */
4651 : /* */
4652 : /* See OGC recommendation paper 06-023r1 or later for details. */
4653 : /************************************************************************/
4654 :
4655 : /**
4656 : * \brief Initialize from OGC URN.
4657 : *
4658 : * Initializes this spatial reference from a coordinate system defined
4659 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4660 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4661 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4662 : *
4663 : * This method is also support through SetFromUserInput() which can
4664 : * normally be used for URNs.
4665 : *
4666 : * @param pszURN the urn string.
4667 : *
4668 : * @return OGRERR_NONE on success or an error code.
4669 : */
4670 :
4671 838 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4672 :
4673 : {
4674 838 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4675 1591 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4676 753 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4677 : CPL_VALUE_INTEGER)
4678 : {
4679 750 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4680 : }
4681 :
4682 176 : TAKE_OPTIONAL_LOCK();
4683 :
4684 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4685 :
4686 : // PROJ 8.2.0 has support for IAU codes now.
4687 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4688 : /* -------------------------------------------------------------------- */
4689 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4690 : /* -------------------------------------------------------------------- */
4691 : const char *pszIAU = strstr(pszURN, "IAU");
4692 : if (pszIAU)
4693 : {
4694 : const char *pszCode = strchr(pszIAU, ':');
4695 : if (pszCode)
4696 : {
4697 : ++pszCode;
4698 : if (*pszCode == ':')
4699 : ++pszCode;
4700 : return importFromDict("IAU2000.wkt", pszCode);
4701 : }
4702 : }
4703 : #endif
4704 :
4705 : if (strlen(pszURN) >= 1000)
4706 : {
4707 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4708 : return OGRERR_CORRUPT_DATA;
4709 : }
4710 : auto obj = proj_create(d->getPROJContext(), pszURN);
4711 : if (!obj)
4712 : {
4713 : return OGRERR_FAILURE;
4714 : }
4715 : Clear();
4716 : d->setPjCRS(obj);
4717 : return OGRERR_NONE;
4718 : #else
4719 88 : const char *pszCur = nullptr;
4720 :
4721 88 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4722 23 : pszCur = pszURN + 16;
4723 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4724 1 : pszCur = pszURN + 20;
4725 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4726 62 : pszCur = pszURN + 18;
4727 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4728 0 : pszCur = pszURN + 16;
4729 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4730 0 : pszCur = pszURN + 20;
4731 : else
4732 : {
4733 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4734 : pszURN);
4735 2 : return OGRERR_FAILURE;
4736 : }
4737 :
4738 : /* -------------------------------------------------------------------- */
4739 : /* Clear any existing definition. */
4740 : /* -------------------------------------------------------------------- */
4741 86 : Clear();
4742 :
4743 : /* -------------------------------------------------------------------- */
4744 : /* Find code (ignoring version) out of string like: */
4745 : /* */
4746 : /* authority:[version]:code */
4747 : /* -------------------------------------------------------------------- */
4748 86 : const char *pszAuthority = pszCur;
4749 :
4750 : // skip authority
4751 414 : while (*pszCur != ':' && *pszCur)
4752 328 : pszCur++;
4753 86 : if (*pszCur == ':')
4754 86 : pszCur++;
4755 :
4756 : // skip version
4757 86 : const char *pszBeforeVersion = pszCur;
4758 387 : while (*pszCur != ':' && *pszCur)
4759 301 : pszCur++;
4760 86 : if (*pszCur == ':')
4761 58 : pszCur++;
4762 : else
4763 : // We come here in the case, the content to parse is authority:code
4764 : // (instead of authority::code) which is probably illegal according to
4765 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4766 : // for example in what is returned by GeoServer.
4767 28 : pszCur = pszBeforeVersion;
4768 :
4769 86 : const char *pszCode = pszCur;
4770 :
4771 86 : const char *pszComma = strchr(pszCur, ',');
4772 86 : if (pszComma == nullptr)
4773 85 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4774 :
4775 : // There's a second part with the vertical SRS.
4776 1 : pszCur = pszComma + 1;
4777 1 : if (!STARTS_WITH(pszCur, "crs:"))
4778 : {
4779 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4780 : pszURN);
4781 0 : return OGRERR_FAILURE;
4782 : }
4783 :
4784 1 : pszCur += 4;
4785 :
4786 1 : char *pszFirstCode = CPLStrdup(pszCode);
4787 1 : pszFirstCode[pszComma - pszCode] = '\0';
4788 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4789 1 : CPLFree(pszFirstCode);
4790 :
4791 : // Do we want to turn this into a compound definition
4792 : // with a vertical datum?
4793 1 : if (eStatus != OGRERR_NONE)
4794 0 : return eStatus;
4795 :
4796 : /* -------------------------------------------------------------------- */
4797 : /* Find code (ignoring version) out of string like: */
4798 : /* */
4799 : /* authority:[version]:code */
4800 : /* -------------------------------------------------------------------- */
4801 1 : pszAuthority = pszCur;
4802 :
4803 : // skip authority
4804 5 : while (*pszCur != ':' && *pszCur)
4805 4 : pszCur++;
4806 1 : if (*pszCur == ':')
4807 1 : pszCur++;
4808 :
4809 : // skip version
4810 1 : pszBeforeVersion = pszCur;
4811 1 : while (*pszCur != ':' && *pszCur)
4812 0 : pszCur++;
4813 1 : if (*pszCur == ':')
4814 1 : pszCur++;
4815 : else
4816 0 : pszCur = pszBeforeVersion;
4817 :
4818 1 : pszCode = pszCur;
4819 :
4820 2 : OGRSpatialReference oVertSRS;
4821 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4822 1 : if (eStatus == OGRERR_NONE)
4823 : {
4824 1 : OGRSpatialReference oHorizSRS(*this);
4825 :
4826 1 : Clear();
4827 :
4828 1 : oHorizSRS.d->refreshProjObj();
4829 1 : oVertSRS.d->refreshProjObj();
4830 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4831 0 : return OGRERR_FAILURE;
4832 :
4833 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4834 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4835 :
4836 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4837 1 : osName += " + ";
4838 1 : osName += pszVertName ? pszVertName : "";
4839 :
4840 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4841 : }
4842 :
4843 1 : return eStatus;
4844 : #endif
4845 : }
4846 :
4847 : /************************************************************************/
4848 : /* importFromCRSURL() */
4849 : /* */
4850 : /* See OGC Best Practice document 11-135 for details. */
4851 : /************************************************************************/
4852 :
4853 : /**
4854 : * \brief Initialize from OGC URL.
4855 : *
4856 : * Initializes this spatial reference from a coordinate system defined
4857 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4858 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4859 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4860 : *
4861 : * This method is also supported through SetFromUserInput() which can
4862 : * normally be used for URLs.
4863 : *
4864 : * @param pszURL the URL string.
4865 : *
4866 : * @return OGRERR_NONE on success or an error code.
4867 : */
4868 :
4869 5452 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4870 :
4871 : {
4872 10904 : TAKE_OPTIONAL_LOCK();
4873 :
4874 : #if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4875 5452 : if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4876 : {
4877 12 : PJ *obj = proj_create(
4878 : d->getPROJContext(),
4879 : "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4880 : " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4881 : " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4882 : " MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4883 : " MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4884 : " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4885 : " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4886 : " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4887 : " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4888 : " MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4889 : " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4890 : " LENGTHUNIT[\"metre\",1]],\n"
4891 : " ENSEMBLEACCURACY[2.0]],\n"
4892 : " PRIMEM[\"Greenwich\",0,\n"
4893 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4894 : " CS[ellipsoidal,3],\n"
4895 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4896 : " ORDER[1],\n"
4897 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4898 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4899 : " ORDER[2],\n"
4900 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4901 : " AXIS[\"ellipsoidal height (h)\",up,\n"
4902 : " ORDER[3],\n"
4903 : " LENGTHUNIT[\"metre\",1]],\n"
4904 : " USAGE[\n"
4905 : " SCOPE[\"3D system frequently used in GIS, Web APIs and "
4906 : "Web applications\"],\n"
4907 : " AREA[\"World.\"],\n"
4908 : " BBOX[-90,-180,90,180]],\n"
4909 : " ID[\"OGC\",\"CRS84h\"]]");
4910 12 : if (!obj)
4911 : {
4912 0 : return OGRERR_FAILURE;
4913 : }
4914 12 : Clear();
4915 12 : d->setPjCRS(obj);
4916 12 : return OGRERR_NONE;
4917 : }
4918 : #endif
4919 :
4920 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4921 : if (strlen(pszURL) >= 10000)
4922 : {
4923 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4924 : return OGRERR_CORRUPT_DATA;
4925 : }
4926 :
4927 : PJ *obj;
4928 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4929 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4930 : {
4931 : obj = proj_create(
4932 : d->getPROJContext(),
4933 : CPLSPrintf("IAU:%s",
4934 : pszURL +
4935 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4936 : }
4937 : else
4938 : #endif
4939 : {
4940 : obj = proj_create(d->getPROJContext(), pszURL);
4941 : }
4942 : if (!obj)
4943 : {
4944 : return OGRERR_FAILURE;
4945 : }
4946 : Clear();
4947 : d->setPjCRS(obj);
4948 : return OGRERR_NONE;
4949 : #else
4950 5440 : const char *pszCur = nullptr;
4951 :
4952 5440 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4953 2 : pszCur = pszURL + 26;
4954 5438 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4955 1 : pszCur = pszURL + 27;
4956 5437 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4957 5436 : pszCur = pszURL + 30;
4958 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4959 1 : pszCur = pszURL + 31;
4960 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4961 0 : pszCur = pszURL + 23;
4962 : else
4963 : {
4964 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4965 : pszURL);
4966 0 : return OGRERR_FAILURE;
4967 : }
4968 :
4969 5440 : if (*pszCur == '\0')
4970 : {
4971 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4972 0 : return OGRERR_FAILURE;
4973 : }
4974 :
4975 : /* -------------------------------------------------------------------- */
4976 : /* Clear any existing definition. */
4977 : /* -------------------------------------------------------------------- */
4978 5440 : Clear();
4979 :
4980 5440 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4981 : {
4982 : /* --------------------------------------------------------------------
4983 : */
4984 : /* It's a compound CRS, of the form: */
4985 : /* */
4986 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4987 : /* --------------------------------------------------------------------
4988 : */
4989 1 : pszCur += 12;
4990 :
4991 : // Extract each component CRS URL.
4992 1 : int iComponentUrl = 2;
4993 :
4994 2 : CPLString osName = "";
4995 1 : Clear();
4996 :
4997 3 : while (iComponentUrl != -1)
4998 : {
4999 2 : char searchStr[15] = {};
5000 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5001 :
5002 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
5003 :
5004 : // Figure out the next component URL.
5005 2 : char *pszComponentUrl = nullptr;
5006 :
5007 2 : if (pszUrlEnd)
5008 : {
5009 1 : size_t nLen = pszUrlEnd - pszCur;
5010 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5011 1 : strncpy(pszComponentUrl, pszCur, nLen);
5012 1 : pszComponentUrl[nLen] = '\0';
5013 :
5014 1 : ++iComponentUrl;
5015 1 : pszCur += nLen + strlen(searchStr);
5016 : }
5017 : else
5018 : {
5019 1 : if (iComponentUrl == 2)
5020 : {
5021 0 : CPLError(CE_Failure, CPLE_AppDefined,
5022 : "Compound CRS URLs must have at least two "
5023 : "component CRSs.");
5024 0 : return OGRERR_FAILURE;
5025 : }
5026 : else
5027 : {
5028 1 : pszComponentUrl = CPLStrdup(pszCur);
5029 : // no more components
5030 1 : iComponentUrl = -1;
5031 : }
5032 : }
5033 :
5034 2 : OGRSpatialReference oComponentSRS;
5035 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5036 :
5037 2 : CPLFree(pszComponentUrl);
5038 2 : pszComponentUrl = nullptr;
5039 :
5040 2 : if (eStatus == OGRERR_NONE)
5041 : {
5042 2 : if (osName.length() != 0)
5043 : {
5044 1 : osName += " + ";
5045 : }
5046 2 : osName += oComponentSRS.GetRoot()->GetValue();
5047 2 : SetNode("COMPD_CS", osName);
5048 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5049 : }
5050 : else
5051 0 : return eStatus;
5052 : }
5053 :
5054 1 : return OGRERR_NONE;
5055 : }
5056 :
5057 : /* -------------------------------------------------------------------- */
5058 : /* It's a normal CRS URL, of the form: */
5059 : /* */
5060 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5061 : /* -------------------------------------------------------------------- */
5062 5439 : ++pszCur;
5063 5439 : const char *pszAuthority = pszCur;
5064 :
5065 : // skip authority
5066 126650 : while (*pszCur != '/' && *pszCur)
5067 121211 : pszCur++;
5068 5439 : if (*pszCur == '/')
5069 5438 : pszCur++;
5070 :
5071 : // skip version
5072 11931 : while (*pszCur != '/' && *pszCur)
5073 6492 : pszCur++;
5074 5439 : if (*pszCur == '/')
5075 5438 : pszCur++;
5076 :
5077 5439 : const char *pszCode = pszCur;
5078 :
5079 5439 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5080 : #endif
5081 : }
5082 :
5083 : /************************************************************************/
5084 : /* importFromWMSAUTO() */
5085 : /************************************************************************/
5086 :
5087 : /**
5088 : * \brief Initialize from WMSAUTO string.
5089 : *
5090 : * Note that the WMS 1.3 specification does not include the
5091 : * units code, while apparently earlier specs do. We try to
5092 : * guess around this.
5093 : *
5094 : * @param pszDefinition the WMSAUTO string
5095 : *
5096 : * @return OGRERR_NONE on success or an error code.
5097 : */
5098 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5099 :
5100 : {
5101 6 : TAKE_OPTIONAL_LOCK();
5102 :
5103 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5104 : if (strlen(pszDefinition) >= 10000)
5105 : {
5106 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5107 : return OGRERR_CORRUPT_DATA;
5108 : }
5109 :
5110 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5111 : if (!obj)
5112 : {
5113 : return OGRERR_FAILURE;
5114 : }
5115 : Clear();
5116 : d->setPjCRS(obj);
5117 : return OGRERR_NONE;
5118 : #else
5119 : int nProjId, nUnitsId;
5120 3 : double dfRefLong, dfRefLat = 0.0;
5121 :
5122 : /* -------------------------------------------------------------------- */
5123 : /* Tokenize */
5124 : /* -------------------------------------------------------------------- */
5125 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5126 3 : pszDefinition += 5;
5127 :
5128 : char **papszTokens =
5129 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5130 :
5131 3 : if (CSLCount(papszTokens) == 4)
5132 : {
5133 0 : nProjId = atoi(papszTokens[0]);
5134 0 : nUnitsId = atoi(papszTokens[1]);
5135 0 : dfRefLong = CPLAtof(papszTokens[2]);
5136 0 : dfRefLat = CPLAtof(papszTokens[3]);
5137 : }
5138 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5139 : {
5140 0 : nProjId = atoi(papszTokens[0]);
5141 0 : nUnitsId = atoi(papszTokens[1]);
5142 0 : dfRefLong = CPLAtof(papszTokens[2]);
5143 0 : dfRefLat = 0.0;
5144 : }
5145 3 : else if (CSLCount(papszTokens) == 3)
5146 : {
5147 2 : nProjId = atoi(papszTokens[0]);
5148 2 : nUnitsId = 9001;
5149 2 : dfRefLong = CPLAtof(papszTokens[1]);
5150 2 : dfRefLat = CPLAtof(papszTokens[2]);
5151 : }
5152 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5153 : {
5154 0 : nProjId = atoi(papszTokens[0]);
5155 0 : nUnitsId = 9001;
5156 0 : dfRefLong = CPLAtof(papszTokens[1]);
5157 : }
5158 : else
5159 : {
5160 1 : CSLDestroy(papszTokens);
5161 1 : CPLError(CE_Failure, CPLE_AppDefined,
5162 : "AUTO projection has wrong number of arguments, expected\n"
5163 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5164 : "AUTO:proj_id,ref_long,ref_lat");
5165 1 : return OGRERR_FAILURE;
5166 : }
5167 :
5168 2 : CSLDestroy(papszTokens);
5169 2 : papszTokens = nullptr;
5170 :
5171 : /* -------------------------------------------------------------------- */
5172 : /* Build coordsys. */
5173 : /* -------------------------------------------------------------------- */
5174 2 : Clear();
5175 :
5176 : /* -------------------------------------------------------------------- */
5177 : /* Set WGS84. */
5178 : /* -------------------------------------------------------------------- */
5179 2 : SetWellKnownGeogCS("WGS84");
5180 :
5181 2 : switch (nProjId)
5182 : {
5183 2 : case 42001: // Auto UTM
5184 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5185 : dfRefLat >= 0.0);
5186 2 : break;
5187 :
5188 0 : case 42002: // Auto TM (strangely very UTM-like).
5189 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5190 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5191 0 : break;
5192 :
5193 0 : case 42003: // Auto Orthographic.
5194 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5195 0 : break;
5196 :
5197 0 : case 42004: // Auto Equirectangular
5198 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5199 0 : break;
5200 :
5201 0 : case 42005:
5202 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5203 0 : break;
5204 :
5205 0 : default:
5206 0 : CPLError(CE_Failure, CPLE_AppDefined,
5207 : "Unsupported projection id in importFromWMSAUTO(): %d",
5208 : nProjId);
5209 0 : return OGRERR_FAILURE;
5210 : }
5211 :
5212 : /* -------------------------------------------------------------------- */
5213 : /* Set units. */
5214 : /* -------------------------------------------------------------------- */
5215 :
5216 2 : switch (nUnitsId)
5217 : {
5218 2 : case 9001:
5219 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5220 2 : break;
5221 :
5222 0 : case 9002:
5223 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5224 0 : break;
5225 :
5226 0 : case 9003:
5227 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5228 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5229 0 : break;
5230 :
5231 0 : default:
5232 0 : CPLError(CE_Failure, CPLE_AppDefined,
5233 : "Unsupported units code (%d).", nUnitsId);
5234 0 : return OGRERR_FAILURE;
5235 : break;
5236 : }
5237 :
5238 2 : return OGRERR_NONE;
5239 : #endif
5240 : }
5241 :
5242 : /************************************************************************/
5243 : /* GetSemiMajor() */
5244 : /************************************************************************/
5245 :
5246 : /**
5247 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5248 : *
5249 : * This method does the same thing as the C function OSRGetSemiMajor().
5250 : *
5251 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5252 : * can be found.
5253 : *
5254 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5255 : */
5256 :
5257 6769 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5258 :
5259 : {
5260 13538 : TAKE_OPTIONAL_LOCK();
5261 :
5262 6769 : if (pnErr != nullptr)
5263 3497 : *pnErr = OGRERR_FAILURE;
5264 :
5265 6769 : d->refreshProjObj();
5266 6769 : if (!d->m_pj_crs)
5267 111 : return SRS_WGS84_SEMIMAJOR;
5268 :
5269 6658 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5270 6658 : if (!ellps)
5271 5 : return SRS_WGS84_SEMIMAJOR;
5272 :
5273 6653 : double dfSemiMajor = 0.0;
5274 6653 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5275 : nullptr, nullptr, nullptr);
5276 6653 : proj_destroy(ellps);
5277 :
5278 6653 : if (dfSemiMajor > 0)
5279 : {
5280 6653 : if (pnErr != nullptr)
5281 3383 : *pnErr = OGRERR_NONE;
5282 6653 : return dfSemiMajor;
5283 : }
5284 :
5285 0 : return SRS_WGS84_SEMIMAJOR;
5286 : }
5287 :
5288 : /************************************************************************/
5289 : /* OSRGetSemiMajor() */
5290 : /************************************************************************/
5291 :
5292 : /**
5293 : * \brief Get spheroid semi major axis.
5294 : *
5295 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5296 : */
5297 84 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5298 :
5299 : {
5300 84 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5301 :
5302 84 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5303 : }
5304 :
5305 : /************************************************************************/
5306 : /* GetInvFlattening() */
5307 : /************************************************************************/
5308 :
5309 : /**
5310 : * \brief Get spheroid inverse flattening.
5311 : *
5312 : * This method does the same thing as the C function OSRGetInvFlattening().
5313 : *
5314 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5315 : * can be found.
5316 : *
5317 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5318 : */
5319 :
5320 4466 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5321 :
5322 : {
5323 8932 : TAKE_OPTIONAL_LOCK();
5324 :
5325 4466 : if (pnErr != nullptr)
5326 3394 : *pnErr = OGRERR_FAILURE;
5327 :
5328 4466 : d->refreshProjObj();
5329 4466 : if (!d->m_pj_crs)
5330 111 : return SRS_WGS84_INVFLATTENING;
5331 :
5332 4355 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5333 4355 : if (!ellps)
5334 2 : return SRS_WGS84_INVFLATTENING;
5335 :
5336 4353 : double dfInvFlattening = -1.0;
5337 4353 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5338 : nullptr, &dfInvFlattening);
5339 4353 : proj_destroy(ellps);
5340 :
5341 4353 : if (dfInvFlattening >= 0.0)
5342 : {
5343 4353 : if (pnErr != nullptr)
5344 3283 : *pnErr = OGRERR_NONE;
5345 4353 : return dfInvFlattening;
5346 : }
5347 :
5348 0 : return SRS_WGS84_INVFLATTENING;
5349 : }
5350 :
5351 : /************************************************************************/
5352 : /* OSRGetInvFlattening() */
5353 : /************************************************************************/
5354 :
5355 : /**
5356 : * \brief Get spheroid inverse flattening.
5357 : *
5358 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5359 : */
5360 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5361 :
5362 : {
5363 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5364 :
5365 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5366 : }
5367 :
5368 : /************************************************************************/
5369 : /* GetEccentricity() */
5370 : /************************************************************************/
5371 :
5372 : /**
5373 : * \brief Get spheroid eccentricity
5374 : *
5375 : * @return eccentricity (or -1 in case of error)
5376 : */
5377 :
5378 0 : double OGRSpatialReference::GetEccentricity() const
5379 :
5380 : {
5381 0 : OGRErr eErr = OGRERR_NONE;
5382 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5383 0 : if (eErr != OGRERR_NONE)
5384 : {
5385 0 : return -1.0;
5386 : }
5387 0 : if (dfInvFlattening == 0.0)
5388 0 : return 0.0;
5389 0 : if (dfInvFlattening < 0.5)
5390 0 : return -1.0;
5391 0 : return sqrt(2.0 / dfInvFlattening -
5392 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5393 : }
5394 :
5395 : /************************************************************************/
5396 : /* GetSquaredEccentricity() */
5397 : /************************************************************************/
5398 :
5399 : /**
5400 : * \brief Get spheroid squared eccentricity
5401 : *
5402 : * @return squared eccentricity (or -1 in case of error)
5403 : */
5404 :
5405 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5406 :
5407 : {
5408 0 : OGRErr eErr = OGRERR_NONE;
5409 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5410 0 : if (eErr != OGRERR_NONE)
5411 : {
5412 0 : return -1.0;
5413 : }
5414 0 : if (dfInvFlattening == 0.0)
5415 0 : return 0.0;
5416 0 : if (dfInvFlattening < 0.5)
5417 0 : return -1.0;
5418 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5419 : }
5420 :
5421 : /************************************************************************/
5422 : /* GetSemiMinor() */
5423 : /************************************************************************/
5424 :
5425 : /**
5426 : * \brief Get spheroid semi minor axis.
5427 : *
5428 : * This method does the same thing as the C function OSRGetSemiMinor().
5429 : *
5430 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5431 : * can be found.
5432 : *
5433 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5434 : */
5435 :
5436 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5437 :
5438 : {
5439 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5440 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5441 :
5442 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5443 : }
5444 :
5445 : /************************************************************************/
5446 : /* OSRGetSemiMinor() */
5447 : /************************************************************************/
5448 :
5449 : /**
5450 : * \brief Get spheroid semi minor axis.
5451 : *
5452 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5453 : */
5454 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5455 :
5456 : {
5457 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5458 :
5459 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5460 : }
5461 :
5462 : /************************************************************************/
5463 : /* SetLocalCS() */
5464 : /************************************************************************/
5465 :
5466 : /**
5467 : * \brief Set the user visible LOCAL_CS name.
5468 : *
5469 : * This method is the same as the C function OSRSetLocalCS().
5470 : *
5471 : * This method will ensure a LOCAL_CS node is created as the root,
5472 : * and set the provided name on it. It must be used before SetLinearUnits().
5473 : *
5474 : * @param pszName the user visible name to assign. Not used as a key.
5475 : *
5476 : * @return OGRERR_NONE on success.
5477 : */
5478 :
5479 2892 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5480 :
5481 : {
5482 5784 : TAKE_OPTIONAL_LOCK();
5483 :
5484 2892 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5485 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5486 : {
5487 2892 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5488 : }
5489 : else
5490 : {
5491 0 : CPLDebug("OGR",
5492 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5493 : "It appears an incompatible object already exists.",
5494 : pszName);
5495 0 : return OGRERR_FAILURE;
5496 : }
5497 :
5498 2892 : return OGRERR_NONE;
5499 : }
5500 :
5501 : /************************************************************************/
5502 : /* OSRSetLocalCS() */
5503 : /************************************************************************/
5504 :
5505 : /**
5506 : * \brief Set the user visible LOCAL_CS name.
5507 : *
5508 : * This function is the same as OGRSpatialReference::SetLocalCS()
5509 : */
5510 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5511 :
5512 : {
5513 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5514 :
5515 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5516 : }
5517 :
5518 : /************************************************************************/
5519 : /* SetGeocCS() */
5520 : /************************************************************************/
5521 :
5522 : /**
5523 : * \brief Set the user visible GEOCCS name.
5524 : *
5525 : * This method is the same as the C function OSRSetGeocCS().
5526 :
5527 : * This method will ensure a GEOCCS node is created as the root,
5528 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5529 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5530 : * the GEOGCS.
5531 : *
5532 : * @param pszName the user visible name to assign. Not used as a key.
5533 : *
5534 : * @return OGRERR_NONE on success.
5535 : *
5536 : */
5537 :
5538 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5539 :
5540 : {
5541 12 : TAKE_OPTIONAL_LOCK();
5542 :
5543 6 : OGRErr eErr = OGRERR_NONE;
5544 6 : d->refreshProjObj();
5545 6 : d->demoteFromBoundCRS();
5546 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5547 : {
5548 3 : d->setPjCRS(proj_create_geocentric_crs(
5549 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5550 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5551 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5552 : "Metre", 1.0));
5553 : }
5554 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5555 : {
5556 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5557 : }
5558 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5559 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5560 : {
5561 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5562 : #if PROJ_VERSION_MAJOR > 7 || \
5563 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5564 : if (datum == nullptr)
5565 : {
5566 : datum =
5567 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5568 : }
5569 : #endif
5570 1 : if (datum == nullptr)
5571 : {
5572 0 : d->undoDemoteFromBoundCRS();
5573 0 : return OGRERR_FAILURE;
5574 : }
5575 :
5576 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5577 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5578 : 0.0);
5579 1 : d->setPjCRS(pj_crs);
5580 :
5581 1 : proj_destroy(datum);
5582 : }
5583 : else
5584 : {
5585 1 : CPLDebug("OGR",
5586 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5587 : "It appears an incompatible object already exists.",
5588 : pszName);
5589 1 : eErr = OGRERR_FAILURE;
5590 : }
5591 6 : d->undoDemoteFromBoundCRS();
5592 :
5593 6 : return eErr;
5594 : }
5595 :
5596 : /************************************************************************/
5597 : /* OSRSetGeocCS() */
5598 : /************************************************************************/
5599 :
5600 : /**
5601 : * \brief Set the user visible PROJCS name.
5602 : *
5603 : * This function is the same as OGRSpatialReference::SetGeocCS()
5604 : *
5605 : */
5606 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5607 :
5608 : {
5609 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5610 :
5611 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5612 : }
5613 :
5614 : /************************************************************************/
5615 : /* SetVertCS() */
5616 : /************************************************************************/
5617 :
5618 : /**
5619 : * \brief Set the user visible VERT_CS name.
5620 : *
5621 : * This method is the same as the C function OSRSetVertCS().
5622 :
5623 : * This method will ensure a VERT_CS node is created if needed. If the
5624 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5625 : * turned into a COMPD_CS.
5626 : *
5627 : * @param pszVertCSName the user visible name of the vertical coordinate
5628 : * system. Not used as a key.
5629 : *
5630 : * @param pszVertDatumName the user visible name of the vertical datum. It
5631 : * is helpful if this matches the EPSG name.
5632 : *
5633 : * @param nVertDatumType the OGC vertical datum type. Ignored
5634 : *
5635 : * @return OGRERR_NONE on success.
5636 : *
5637 : */
5638 :
5639 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5640 : const char *pszVertDatumName,
5641 : int nVertDatumType)
5642 :
5643 : {
5644 1 : TAKE_OPTIONAL_LOCK();
5645 :
5646 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5647 :
5648 1 : d->refreshProjObj();
5649 :
5650 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5651 : pszVertDatumName, nullptr, 0.0);
5652 :
5653 : /* -------------------------------------------------------------------- */
5654 : /* Handle the case where we want to make a compound coordinate */
5655 : /* system. */
5656 : /* -------------------------------------------------------------------- */
5657 1 : if (IsProjected() || IsGeographic())
5658 : {
5659 1 : auto compoundCRS = proj_create_compound_crs(
5660 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5661 1 : proj_destroy(vertCRS);
5662 1 : d->setPjCRS(compoundCRS);
5663 : }
5664 : else
5665 : {
5666 0 : d->setPjCRS(vertCRS);
5667 : }
5668 2 : return OGRERR_NONE;
5669 : }
5670 :
5671 : /************************************************************************/
5672 : /* OSRSetVertCS() */
5673 : /************************************************************************/
5674 :
5675 : /**
5676 : * \brief Setup the vertical coordinate system.
5677 : *
5678 : * This function is the same as OGRSpatialReference::SetVertCS()
5679 : *
5680 : */
5681 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5682 : const char *pszVertDatumName, int nVertDatumType)
5683 :
5684 : {
5685 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5686 :
5687 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5688 0 : nVertDatumType);
5689 : }
5690 :
5691 : /************************************************************************/
5692 : /* SetCompoundCS() */
5693 : /************************************************************************/
5694 :
5695 : /**
5696 : * \brief Setup a compound coordinate system.
5697 : *
5698 : * This method is the same as the C function OSRSetCompoundCS().
5699 :
5700 : * This method is replace the current SRS with a COMPD_CS coordinate system
5701 : * consisting of the passed in horizontal and vertical coordinate systems.
5702 : *
5703 : * @param pszName the name of the compound coordinate system.
5704 : *
5705 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5706 : *
5707 : * @param poVertSRS the vertical SRS (VERT_CS).
5708 : *
5709 : * @return OGRERR_NONE on success.
5710 : */
5711 :
5712 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5713 : const OGRSpatialReference *poHorizSRS,
5714 : const OGRSpatialReference *poVertSRS)
5715 :
5716 : {
5717 184 : TAKE_OPTIONAL_LOCK();
5718 :
5719 : /* -------------------------------------------------------------------- */
5720 : /* Verify these are legal horizontal and vertical coordinate */
5721 : /* systems. */
5722 : /* -------------------------------------------------------------------- */
5723 92 : if (!poVertSRS->IsVertical())
5724 : {
5725 0 : CPLError(CE_Failure, CPLE_AppDefined,
5726 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5727 0 : return OGRERR_FAILURE;
5728 : }
5729 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5730 : {
5731 0 : CPLError(CE_Failure, CPLE_AppDefined,
5732 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5733 : "GEOGCS.");
5734 0 : return OGRERR_FAILURE;
5735 : }
5736 :
5737 : /* -------------------------------------------------------------------- */
5738 : /* Replace with compound srs. */
5739 : /* -------------------------------------------------------------------- */
5740 92 : Clear();
5741 :
5742 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5743 92 : poHorizSRS->d->m_pj_crs,
5744 92 : poVertSRS->d->m_pj_crs);
5745 92 : d->setPjCRS(compoundCRS);
5746 :
5747 92 : return OGRERR_NONE;
5748 : }
5749 :
5750 : /************************************************************************/
5751 : /* OSRSetCompoundCS() */
5752 : /************************************************************************/
5753 :
5754 : /**
5755 : * \brief Setup a compound coordinate system.
5756 : *
5757 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5758 : */
5759 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5760 : OGRSpatialReferenceH hHorizSRS,
5761 : OGRSpatialReferenceH hVertSRS)
5762 :
5763 : {
5764 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5765 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5766 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5767 :
5768 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5769 16 : ToPointer(hVertSRS));
5770 : }
5771 :
5772 : /************************************************************************/
5773 : /* SetProjCS() */
5774 : /************************************************************************/
5775 :
5776 : /**
5777 : * \brief Set the user visible PROJCS name.
5778 : *
5779 : * This method is the same as the C function OSRSetProjCS().
5780 : *
5781 : * This method will ensure a PROJCS node is created as the root,
5782 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5783 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5784 : *
5785 : * @param pszName the user visible name to assign. Not used as a key.
5786 : *
5787 : * @return OGRERR_NONE on success.
5788 : */
5789 :
5790 4678 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5791 :
5792 : {
5793 4678 : TAKE_OPTIONAL_LOCK();
5794 :
5795 4678 : d->refreshProjObj();
5796 4678 : d->demoteFromBoundCRS();
5797 4678 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5798 : {
5799 487 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5800 : }
5801 : else
5802 : {
5803 4191 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5804 : nullptr, nullptr, nullptr,
5805 : nullptr, nullptr, 0, nullptr);
5806 4191 : auto cs = proj_create_cartesian_2D_cs(
5807 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5808 :
5809 4191 : auto projCRS = proj_create_projected_crs(
5810 4191 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5811 4191 : proj_destroy(dummyConv);
5812 4191 : proj_destroy(cs);
5813 :
5814 4191 : d->setPjCRS(projCRS);
5815 : }
5816 4678 : d->undoDemoteFromBoundCRS();
5817 9356 : return OGRERR_NONE;
5818 : }
5819 :
5820 : /************************************************************************/
5821 : /* OSRSetProjCS() */
5822 : /************************************************************************/
5823 :
5824 : /**
5825 : * \brief Set the user visible PROJCS name.
5826 : *
5827 : * This function is the same as OGRSpatialReference::SetProjCS()
5828 : */
5829 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5830 :
5831 : {
5832 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5833 :
5834 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5835 : }
5836 :
5837 : /************************************************************************/
5838 : /* SetProjection() */
5839 : /************************************************************************/
5840 :
5841 : /**
5842 : * \brief Set a projection name.
5843 : *
5844 : * This method is the same as the C function OSRSetProjection().
5845 : *
5846 : * @param pszProjection the projection name, which should be selected from
5847 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5848 : *
5849 : * @return OGRERR_NONE on success.
5850 : */
5851 :
5852 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5853 :
5854 : {
5855 46 : TAKE_OPTIONAL_LOCK();
5856 :
5857 23 : OGR_SRSNode *poGeogCS = nullptr;
5858 :
5859 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5860 : {
5861 4 : poGeogCS = d->m_poRoot;
5862 4 : d->m_poRoot = nullptr;
5863 : }
5864 :
5865 23 : if (!GetAttrNode("PROJCS"))
5866 : {
5867 11 : SetNode("PROJCS", "unnamed");
5868 : }
5869 :
5870 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5871 23 : if (eErr != OGRERR_NONE)
5872 0 : return eErr;
5873 :
5874 23 : if (poGeogCS != nullptr)
5875 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5876 :
5877 23 : return OGRERR_NONE;
5878 : }
5879 :
5880 : /************************************************************************/
5881 : /* OSRSetProjection() */
5882 : /************************************************************************/
5883 :
5884 : /**
5885 : * \brief Set a projection name.
5886 : *
5887 : * This function is the same as OGRSpatialReference::SetProjection()
5888 : */
5889 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5890 :
5891 : {
5892 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5893 :
5894 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5895 : }
5896 :
5897 : /************************************************************************/
5898 : /* GetWKT2ProjectionMethod() */
5899 : /************************************************************************/
5900 :
5901 : /**
5902 : * \brief Returns info on the projection method, based on WKT2 naming
5903 : * conventions.
5904 : *
5905 : * The returned strings are short lived and should be considered to be
5906 : * invalidated by any further call to the GDAL API.
5907 : *
5908 : * @param[out] ppszMethodName Pointer to a string that will receive the
5909 : * projection method name.
5910 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5911 : * receive the name of the authority that defines the projection method.
5912 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5913 : * an authority.
5914 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5915 : * receive the code that defines the projection method.
5916 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5917 : * an authority.
5918 : *
5919 : * @return OGRERR_NONE on success.
5920 : */
5921 : OGRErr
5922 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5923 : const char **ppszMethodAuthName,
5924 : const char **ppszMethodCode) const
5925 : {
5926 2 : TAKE_OPTIONAL_LOCK();
5927 :
5928 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5929 1 : if (!conv)
5930 0 : return OGRERR_FAILURE;
5931 1 : const char *pszTmpMethodName = "";
5932 1 : const char *pszTmpMethodAuthName = "";
5933 1 : const char *pszTmpMethodCode = "";
5934 1 : int ret = proj_coordoperation_get_method_info(
5935 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5936 : &pszTmpMethodCode);
5937 : // "Internalize" temporary strings returned by PROJ
5938 1 : CPLAssert(pszTmpMethodName);
5939 1 : if (ppszMethodName)
5940 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5941 1 : if (ppszMethodAuthName)
5942 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5943 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5944 0 : : nullptr;
5945 1 : if (ppszMethodCode)
5946 0 : *ppszMethodCode =
5947 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5948 1 : proj_destroy(conv);
5949 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5950 : }
5951 :
5952 : /************************************************************************/
5953 : /* SetProjParm() */
5954 : /************************************************************************/
5955 :
5956 : /**
5957 : * \brief Set a projection parameter value.
5958 : *
5959 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5960 : *
5961 : * This method is the same as the C function OSRSetProjParm().
5962 : *
5963 : * Please check https://gdal.org/proj_list pages for
5964 : * legal parameter names for specific projections.
5965 : *
5966 : *
5967 : * @param pszParamName the parameter name, which should be selected from
5968 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5969 : *
5970 : * @param dfValue value to assign.
5971 : *
5972 : * @return OGRERR_NONE on success.
5973 : */
5974 :
5975 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5976 : double dfValue)
5977 :
5978 : {
5979 258 : TAKE_OPTIONAL_LOCK();
5980 :
5981 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5982 :
5983 129 : if (poPROJCS == nullptr)
5984 3 : return OGRERR_FAILURE;
5985 :
5986 126 : char szValue[64] = {'\0'};
5987 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5988 :
5989 : /* -------------------------------------------------------------------- */
5990 : /* Try to find existing parameter with this name. */
5991 : /* -------------------------------------------------------------------- */
5992 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5993 : {
5994 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5995 :
5996 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5997 1242 : poParam->GetChildCount() == 2 &&
5998 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5999 : {
6000 39 : poParam->GetChild(1)->SetValue(szValue);
6001 39 : return OGRERR_NONE;
6002 : }
6003 : }
6004 :
6005 : /* -------------------------------------------------------------------- */
6006 : /* Otherwise create a new parameter and append. */
6007 : /* -------------------------------------------------------------------- */
6008 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6009 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
6010 87 : poParam->AddChild(new OGR_SRSNode(szValue));
6011 :
6012 87 : poPROJCS->AddChild(poParam);
6013 :
6014 87 : return OGRERR_NONE;
6015 : }
6016 :
6017 : /************************************************************************/
6018 : /* OSRSetProjParm() */
6019 : /************************************************************************/
6020 :
6021 : /**
6022 : * \brief Set a projection parameter value.
6023 : *
6024 : * This function is the same as OGRSpatialReference::SetProjParm()
6025 : */
6026 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6027 : double dfValue)
6028 :
6029 : {
6030 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6031 :
6032 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6033 : }
6034 :
6035 : /************************************************************************/
6036 : /* FindProjParm() */
6037 : /************************************************************************/
6038 :
6039 : /**
6040 : * \brief Return the child index of the named projection parameter on
6041 : * its parent PROJCS node.
6042 : *
6043 : * @param pszParameter projection parameter to look for
6044 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6045 : * the PROJCS node of the SpatialReference object will be searched.
6046 : *
6047 : * @return the child index of the named projection parameter. -1 on failure
6048 : */
6049 4983 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6050 : const OGR_SRSNode *poPROJCS) const
6051 :
6052 : {
6053 9966 : TAKE_OPTIONAL_LOCK();
6054 :
6055 4983 : if (poPROJCS == nullptr)
6056 0 : poPROJCS = GetAttrNode("PROJCS");
6057 :
6058 4983 : if (poPROJCS == nullptr)
6059 0 : return -1;
6060 :
6061 : /* -------------------------------------------------------------------- */
6062 : /* Search for requested parameter. */
6063 : /* -------------------------------------------------------------------- */
6064 4983 : bool bIsWKT2 = false;
6065 32886 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6066 : {
6067 32239 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6068 :
6069 32239 : if (poParameter->GetChildCount() >= 2)
6070 : {
6071 22314 : const char *pszValue = poParameter->GetValue();
6072 36884 : if (EQUAL(pszValue, "PARAMETER") &&
6073 14570 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6074 : pszParameter))
6075 : {
6076 4336 : return iChild;
6077 : }
6078 17978 : else if (EQUAL(pszValue, "METHOD"))
6079 : {
6080 41 : bIsWKT2 = true;
6081 : }
6082 : }
6083 : }
6084 :
6085 : /* -------------------------------------------------------------------- */
6086 : /* Try similar names, for selected parameters. */
6087 : /* -------------------------------------------------------------------- */
6088 647 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6089 : {
6090 302 : if (bIsWKT2)
6091 : {
6092 8 : int iChild = FindProjParm(
6093 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6094 8 : if (iChild == -1)
6095 3 : iChild = FindProjParm(
6096 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6097 8 : return iChild;
6098 : }
6099 294 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6100 : }
6101 :
6102 345 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6103 : {
6104 36 : if (bIsWKT2)
6105 : {
6106 9 : int iChild = FindProjParm(
6107 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6108 9 : if (iChild == -1)
6109 0 : iChild = FindProjParm(
6110 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6111 9 : return iChild;
6112 : }
6113 27 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6114 27 : if (iChild == -1)
6115 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6116 27 : return iChild;
6117 : }
6118 :
6119 309 : return -1;
6120 : }
6121 :
6122 : /************************************************************************/
6123 : /* GetProjParm() */
6124 : /************************************************************************/
6125 :
6126 : /**
6127 : * \brief Fetch a projection parameter value.
6128 : *
6129 : * NOTE: This code should be modified to translate non degree angles into
6130 : * degrees based on the GEOGCS unit. This has not yet been done.
6131 : *
6132 : * This method is the same as the C function OSRGetProjParm().
6133 : *
6134 : * @param pszName the name of the parameter to fetch, from the set of
6135 : * SRS_PP codes in ogr_srs_api.h.
6136 : *
6137 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6138 : *
6139 : * @param pnErr place to put error code on failure. Ignored if NULL.
6140 : *
6141 : * @return value of parameter.
6142 : */
6143 :
6144 4900 : double OGRSpatialReference::GetProjParm(const char *pszName,
6145 : double dfDefaultValue,
6146 : OGRErr *pnErr) const
6147 :
6148 : {
6149 9800 : TAKE_OPTIONAL_LOCK();
6150 :
6151 4900 : d->refreshProjObj();
6152 4900 : GetRoot(); // force update of d->m_bNodesWKT2
6153 :
6154 4900 : if (pnErr != nullptr)
6155 3921 : *pnErr = OGRERR_NONE;
6156 :
6157 : /* -------------------------------------------------------------------- */
6158 : /* Find the desired parameter. */
6159 : /* -------------------------------------------------------------------- */
6160 : const OGR_SRSNode *poPROJCS =
6161 4900 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6162 4900 : if (poPROJCS == nullptr)
6163 : {
6164 258 : if (pnErr != nullptr)
6165 258 : *pnErr = OGRERR_FAILURE;
6166 258 : return dfDefaultValue;
6167 : }
6168 :
6169 4642 : const int iChild = FindProjParm(pszName, poPROJCS);
6170 4642 : if (iChild == -1)
6171 : {
6172 306 : if (IsProjected() && GetAxesCount() == 3)
6173 : {
6174 3 : OGRSpatialReference *poSRSTmp = Clone();
6175 3 : poSRSTmp->DemoteTo2D(nullptr);
6176 : const double dfRet =
6177 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6178 3 : delete poSRSTmp;
6179 3 : return dfRet;
6180 : }
6181 :
6182 303 : if (pnErr != nullptr)
6183 281 : *pnErr = OGRERR_FAILURE;
6184 303 : return dfDefaultValue;
6185 : }
6186 :
6187 4336 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6188 4336 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6189 : }
6190 :
6191 : /************************************************************************/
6192 : /* OSRGetProjParm() */
6193 : /************************************************************************/
6194 :
6195 : /**
6196 : * \brief Fetch a projection parameter value.
6197 : *
6198 : * This function is the same as OGRSpatialReference::GetProjParm()
6199 : */
6200 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6201 : double dfDefaultValue, OGRErr *pnErr)
6202 :
6203 : {
6204 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6205 :
6206 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6207 : }
6208 :
6209 : /************************************************************************/
6210 : /* GetNormProjParm() */
6211 : /************************************************************************/
6212 :
6213 : /**
6214 : * \brief Fetch a normalized projection parameter value.
6215 : *
6216 : * This method is the same as GetProjParm() except that the value of
6217 : * the parameter is "normalized" into degrees or meters depending on
6218 : * whether it is linear or angular.
6219 : *
6220 : * This method is the same as the C function OSRGetNormProjParm().
6221 : *
6222 : * @param pszName the name of the parameter to fetch, from the set of
6223 : * SRS_PP codes in ogr_srs_api.h.
6224 : *
6225 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6226 : *
6227 : * @param pnErr place to put error code on failure. Ignored if NULL.
6228 : *
6229 : * @return value of parameter.
6230 : */
6231 :
6232 3896 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6233 : double dfDefaultValue,
6234 : OGRErr *pnErr) const
6235 :
6236 : {
6237 7792 : TAKE_OPTIONAL_LOCK();
6238 :
6239 3896 : GetNormInfo();
6240 :
6241 3896 : OGRErr nError = OGRERR_NONE;
6242 3896 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6243 3896 : if (pnErr != nullptr)
6244 0 : *pnErr = nError;
6245 :
6246 : // If we got the default just return it unadjusted.
6247 3896 : if (nError != OGRERR_NONE)
6248 539 : return dfRawResult;
6249 :
6250 3357 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6251 8 : dfRawResult *= d->dfToDegrees;
6252 :
6253 3357 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6254 5 : return dfRawResult * d->dfToMeter;
6255 :
6256 3352 : return dfRawResult;
6257 : }
6258 :
6259 : /************************************************************************/
6260 : /* OSRGetNormProjParm() */
6261 : /************************************************************************/
6262 :
6263 : /**
6264 : * \brief This function is the same as OGRSpatialReference::
6265 : *
6266 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6267 : */
6268 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6269 : double dfDefaultValue, OGRErr *pnErr)
6270 :
6271 : {
6272 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6273 :
6274 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6275 : }
6276 :
6277 : /************************************************************************/
6278 : /* SetNormProjParm() */
6279 : /************************************************************************/
6280 :
6281 : /**
6282 : * \brief Set a projection parameter with a normalized value.
6283 : *
6284 : * This method is the same as SetProjParm() except that the value of
6285 : * the parameter passed in is assumed to be in "normalized" form (decimal
6286 : * degrees for angular values, meters for linear values. The values are
6287 : * converted in a form suitable for the GEOGCS and linear units in effect.
6288 : *
6289 : * This method is the same as the C function OSRSetNormProjParm().
6290 : *
6291 : * @param pszName the parameter name, which should be selected from
6292 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6293 : *
6294 : * @param dfValue value to assign.
6295 : *
6296 : * @return OGRERR_NONE on success.
6297 : */
6298 :
6299 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6300 :
6301 : {
6302 182 : TAKE_OPTIONAL_LOCK();
6303 :
6304 91 : GetNormInfo();
6305 :
6306 91 : if (d->dfToDegrees != 0.0 &&
6307 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6308 0 : IsAngularParameter(pszName))
6309 : {
6310 0 : dfValue /= d->dfToDegrees;
6311 : }
6312 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6313 4 : IsLinearParameter(pszName))
6314 4 : dfValue /= d->dfToMeter;
6315 :
6316 182 : return SetProjParm(pszName, dfValue);
6317 : }
6318 :
6319 : /************************************************************************/
6320 : /* OSRSetNormProjParm() */
6321 : /************************************************************************/
6322 :
6323 : /**
6324 : * \brief Set a projection parameter with a normalized value.
6325 : *
6326 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6327 : */
6328 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6329 : double dfValue)
6330 :
6331 : {
6332 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6333 :
6334 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6335 : }
6336 :
6337 : /************************************************************************/
6338 : /* SetTM() */
6339 : /************************************************************************/
6340 :
6341 439 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6342 : double dfScale, double dfFalseEasting,
6343 : double dfFalseNorthing)
6344 :
6345 : {
6346 878 : TAKE_OPTIONAL_LOCK();
6347 :
6348 439 : return d->replaceConversionAndUnref(
6349 : proj_create_conversion_transverse_mercator(
6350 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6351 878 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6352 : }
6353 :
6354 : /************************************************************************/
6355 : /* OSRSetTM() */
6356 : /************************************************************************/
6357 :
6358 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6359 : double dfCenterLong, double dfScale, double dfFalseEasting,
6360 : double dfFalseNorthing)
6361 :
6362 : {
6363 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6364 :
6365 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6366 1 : dfFalseEasting, dfFalseNorthing);
6367 : }
6368 :
6369 : /************************************************************************/
6370 : /* SetTMVariant() */
6371 : /************************************************************************/
6372 :
6373 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6374 : double dfCenterLat,
6375 : double dfCenterLong, double dfScale,
6376 : double dfFalseEasting,
6377 : double dfFalseNorthing)
6378 :
6379 : {
6380 0 : TAKE_OPTIONAL_LOCK();
6381 :
6382 0 : SetProjection(pszVariantName);
6383 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6384 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6385 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6386 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6387 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6388 :
6389 0 : return OGRERR_NONE;
6390 : }
6391 :
6392 : /************************************************************************/
6393 : /* OSRSetTMVariant() */
6394 : /************************************************************************/
6395 :
6396 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6397 : double dfCenterLat, double dfCenterLong, double dfScale,
6398 : double dfFalseEasting, double dfFalseNorthing)
6399 :
6400 : {
6401 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6402 :
6403 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6404 : dfCenterLong, dfScale, dfFalseEasting,
6405 0 : dfFalseNorthing);
6406 : }
6407 :
6408 : /************************************************************************/
6409 : /* SetTMSO() */
6410 : /************************************************************************/
6411 :
6412 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6413 : double dfScale, double dfFalseEasting,
6414 : double dfFalseNorthing)
6415 :
6416 : {
6417 6 : TAKE_OPTIONAL_LOCK();
6418 :
6419 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6420 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6421 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6422 :
6423 3 : const char *pszName = nullptr;
6424 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6425 3 : CPLString osName = pszName ? pszName : "";
6426 :
6427 3 : d->refreshProjObj();
6428 :
6429 3 : d->demoteFromBoundCRS();
6430 :
6431 3 : auto cs = proj_create_cartesian_2D_cs(
6432 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6433 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6434 : auto projCRS =
6435 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6436 3 : d->getGeodBaseCRS(), conv, cs);
6437 3 : proj_destroy(conv);
6438 3 : proj_destroy(cs);
6439 :
6440 3 : d->setPjCRS(projCRS);
6441 :
6442 3 : d->undoDemoteFromBoundCRS();
6443 :
6444 6 : return OGRERR_NONE;
6445 : }
6446 :
6447 : /************************************************************************/
6448 : /* OSRSetTMSO() */
6449 : /************************************************************************/
6450 :
6451 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6452 : double dfCenterLong, double dfScale, double dfFalseEasting,
6453 : double dfFalseNorthing)
6454 :
6455 : {
6456 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6457 :
6458 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6459 0 : dfFalseEasting, dfFalseNorthing);
6460 : }
6461 :
6462 : /************************************************************************/
6463 : /* SetTPED() */
6464 : /************************************************************************/
6465 :
6466 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6467 : double dfLat2, double dfLong2,
6468 : double dfFalseEasting,
6469 : double dfFalseNorthing)
6470 :
6471 : {
6472 2 : TAKE_OPTIONAL_LOCK();
6473 :
6474 1 : return d->replaceConversionAndUnref(
6475 : proj_create_conversion_two_point_equidistant(
6476 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6477 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6478 : }
6479 :
6480 : /************************************************************************/
6481 : /* OSRSetTPED() */
6482 : /************************************************************************/
6483 :
6484 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6485 : double dfLat2, double dfLong2, double dfFalseEasting,
6486 : double dfFalseNorthing)
6487 :
6488 : {
6489 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6490 :
6491 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6492 0 : dfFalseEasting, dfFalseNorthing);
6493 : }
6494 :
6495 : /************************************************************************/
6496 : /* SetTMG() */
6497 : /************************************************************************/
6498 :
6499 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6500 : double dfFalseEasting,
6501 : double dfFalseNorthing)
6502 :
6503 : {
6504 0 : TAKE_OPTIONAL_LOCK();
6505 :
6506 0 : return d->replaceConversionAndUnref(
6507 : proj_create_conversion_tunisia_mapping_grid(
6508 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6509 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6510 : }
6511 :
6512 : /************************************************************************/
6513 : /* OSRSetTMG() */
6514 : /************************************************************************/
6515 :
6516 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6517 : double dfCenterLong, double dfFalseEasting,
6518 : double dfFalseNorthing)
6519 :
6520 : {
6521 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6522 :
6523 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6524 0 : dfFalseNorthing);
6525 : }
6526 :
6527 : /************************************************************************/
6528 : /* SetACEA() */
6529 : /************************************************************************/
6530 :
6531 39 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6532 : double dfCenterLat, double dfCenterLong,
6533 : double dfFalseEasting,
6534 : double dfFalseNorthing)
6535 :
6536 : {
6537 78 : TAKE_OPTIONAL_LOCK();
6538 :
6539 : // Note different order of parameters. The one in PROJ is conformant with
6540 : // EPSG
6541 39 : return d->replaceConversionAndUnref(
6542 : proj_create_conversion_albers_equal_area(
6543 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6544 78 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6545 : }
6546 :
6547 : /************************************************************************/
6548 : /* OSRSetACEA() */
6549 : /************************************************************************/
6550 :
6551 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6552 : double dfCenterLat, double dfCenterLong,
6553 : double dfFalseEasting, double dfFalseNorthing)
6554 :
6555 : {
6556 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6557 :
6558 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6559 0 : dfFalseEasting, dfFalseNorthing);
6560 : }
6561 :
6562 : /************************************************************************/
6563 : /* SetAE() */
6564 : /************************************************************************/
6565 :
6566 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6567 : double dfFalseEasting, double dfFalseNorthing)
6568 :
6569 : {
6570 42 : TAKE_OPTIONAL_LOCK();
6571 :
6572 21 : return d->replaceConversionAndUnref(
6573 : proj_create_conversion_azimuthal_equidistant(
6574 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6575 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6576 : }
6577 :
6578 : /************************************************************************/
6579 : /* OSRSetAE() */
6580 : /************************************************************************/
6581 :
6582 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6583 : double dfCenterLong, double dfFalseEasting,
6584 : double dfFalseNorthing)
6585 :
6586 : {
6587 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6588 :
6589 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6590 0 : dfFalseNorthing);
6591 : }
6592 :
6593 : /************************************************************************/
6594 : /* SetBonne() */
6595 : /************************************************************************/
6596 :
6597 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6598 : double dfFalseEasting,
6599 : double dfFalseNorthing)
6600 :
6601 : {
6602 2 : TAKE_OPTIONAL_LOCK();
6603 :
6604 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6605 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6606 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6607 : }
6608 :
6609 : /************************************************************************/
6610 : /* OSRSetBonne() */
6611 : /************************************************************************/
6612 :
6613 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6614 : double dfCentralMeridian, double dfFalseEasting,
6615 : double dfFalseNorthing)
6616 :
6617 : {
6618 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6619 :
6620 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6621 0 : dfFalseNorthing);
6622 : }
6623 :
6624 : /************************************************************************/
6625 : /* SetCEA() */
6626 : /************************************************************************/
6627 :
6628 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6629 : double dfFalseEasting,
6630 : double dfFalseNorthing)
6631 :
6632 : {
6633 8 : TAKE_OPTIONAL_LOCK();
6634 :
6635 4 : return d->replaceConversionAndUnref(
6636 : proj_create_conversion_lambert_cylindrical_equal_area(
6637 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6638 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6639 : }
6640 :
6641 : /************************************************************************/
6642 : /* OSRSetCEA() */
6643 : /************************************************************************/
6644 :
6645 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6646 : double dfCentralMeridian, double dfFalseEasting,
6647 : double dfFalseNorthing)
6648 :
6649 : {
6650 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6651 :
6652 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6653 0 : dfFalseNorthing);
6654 : }
6655 :
6656 : /************************************************************************/
6657 : /* SetCS() */
6658 : /************************************************************************/
6659 :
6660 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6661 : double dfFalseEasting, double dfFalseNorthing)
6662 :
6663 : {
6664 10 : TAKE_OPTIONAL_LOCK();
6665 :
6666 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6667 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6668 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6669 : }
6670 :
6671 : /************************************************************************/
6672 : /* OSRSetCS() */
6673 : /************************************************************************/
6674 :
6675 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6676 : double dfCenterLong, double dfFalseEasting,
6677 : double dfFalseNorthing)
6678 :
6679 : {
6680 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6681 :
6682 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6683 0 : dfFalseNorthing);
6684 : }
6685 :
6686 : /************************************************************************/
6687 : /* SetEC() */
6688 : /************************************************************************/
6689 :
6690 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6691 : double dfCenterLat, double dfCenterLong,
6692 : double dfFalseEasting, double dfFalseNorthing)
6693 :
6694 : {
6695 14 : TAKE_OPTIONAL_LOCK();
6696 :
6697 : // Note: different order of arguments
6698 7 : return d->replaceConversionAndUnref(
6699 : proj_create_conversion_equidistant_conic(
6700 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6701 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6702 : }
6703 :
6704 : /************************************************************************/
6705 : /* OSRSetEC() */
6706 : /************************************************************************/
6707 :
6708 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6709 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6710 : double dfFalseNorthing)
6711 :
6712 : {
6713 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6714 :
6715 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6716 0 : dfFalseEasting, dfFalseNorthing);
6717 : }
6718 :
6719 : /************************************************************************/
6720 : /* SetEckert() */
6721 : /************************************************************************/
6722 :
6723 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6724 : double dfCentralMeridian,
6725 : double dfFalseEasting,
6726 : double dfFalseNorthing)
6727 :
6728 : {
6729 20 : TAKE_OPTIONAL_LOCK();
6730 :
6731 : PJ *conv;
6732 10 : if (nVariation == 1)
6733 : {
6734 1 : conv = proj_create_conversion_eckert_i(
6735 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6736 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6737 : }
6738 9 : else if (nVariation == 2)
6739 : {
6740 1 : conv = proj_create_conversion_eckert_ii(
6741 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6742 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6743 : }
6744 8 : else if (nVariation == 3)
6745 : {
6746 1 : conv = proj_create_conversion_eckert_iii(
6747 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6748 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6749 : }
6750 7 : else if (nVariation == 4)
6751 : {
6752 3 : conv = proj_create_conversion_eckert_iv(
6753 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6754 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6755 : }
6756 4 : else if (nVariation == 5)
6757 : {
6758 1 : conv = proj_create_conversion_eckert_v(
6759 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6760 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6761 : }
6762 3 : else if (nVariation == 6)
6763 : {
6764 3 : conv = proj_create_conversion_eckert_vi(
6765 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6766 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6767 : }
6768 : else
6769 : {
6770 0 : CPLError(CE_Failure, CPLE_AppDefined,
6771 : "Unsupported Eckert variation (%d).", nVariation);
6772 0 : return OGRERR_UNSUPPORTED_SRS;
6773 : }
6774 :
6775 10 : return d->replaceConversionAndUnref(conv);
6776 : }
6777 :
6778 : /************************************************************************/
6779 : /* OSRSetEckert() */
6780 : /************************************************************************/
6781 :
6782 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6783 : double dfCentralMeridian, double dfFalseEasting,
6784 : double dfFalseNorthing)
6785 :
6786 : {
6787 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6788 :
6789 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6790 0 : dfFalseEasting, dfFalseNorthing);
6791 : }
6792 :
6793 : /************************************************************************/
6794 : /* SetEckertIV() */
6795 : /* */
6796 : /* Deprecated */
6797 : /************************************************************************/
6798 :
6799 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6800 : double dfFalseEasting,
6801 : double dfFalseNorthing)
6802 :
6803 : {
6804 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6805 : }
6806 :
6807 : /************************************************************************/
6808 : /* OSRSetEckertIV() */
6809 : /************************************************************************/
6810 :
6811 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6812 : double dfFalseEasting, double dfFalseNorthing)
6813 :
6814 : {
6815 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6816 :
6817 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6818 0 : dfFalseNorthing);
6819 : }
6820 :
6821 : /************************************************************************/
6822 : /* SetEckertVI() */
6823 : /* */
6824 : /* Deprecated */
6825 : /************************************************************************/
6826 :
6827 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6828 : double dfFalseEasting,
6829 : double dfFalseNorthing)
6830 :
6831 : {
6832 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6833 : }
6834 :
6835 : /************************************************************************/
6836 : /* OSRSetEckertVI() */
6837 : /************************************************************************/
6838 :
6839 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6840 : double dfFalseEasting, double dfFalseNorthing)
6841 :
6842 : {
6843 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6844 :
6845 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6846 0 : dfFalseNorthing);
6847 : }
6848 :
6849 : /************************************************************************/
6850 : /* SetEquirectangular() */
6851 : /************************************************************************/
6852 :
6853 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6854 : double dfCenterLong,
6855 : double dfFalseEasting,
6856 : double dfFalseNorthing)
6857 :
6858 : {
6859 4 : TAKE_OPTIONAL_LOCK();
6860 :
6861 2 : if (dfCenterLat == 0.0)
6862 : {
6863 0 : return d->replaceConversionAndUnref(
6864 : proj_create_conversion_equidistant_cylindrical(
6865 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6866 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6867 : }
6868 :
6869 : // Non-standard extension with non-zero latitude of origin
6870 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6871 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6872 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6873 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6874 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6875 :
6876 2 : return OGRERR_NONE;
6877 : }
6878 :
6879 : /************************************************************************/
6880 : /* OSRSetEquirectangular() */
6881 : /************************************************************************/
6882 :
6883 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6884 : double dfCenterLong, double dfFalseEasting,
6885 : double dfFalseNorthing)
6886 :
6887 : {
6888 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6889 :
6890 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6891 0 : dfFalseEasting, dfFalseNorthing);
6892 : }
6893 :
6894 : /************************************************************************/
6895 : /* SetEquirectangular2() */
6896 : /* Generalized form */
6897 : /************************************************************************/
6898 :
6899 179 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6900 : double dfCenterLong,
6901 : double dfStdParallel1,
6902 : double dfFalseEasting,
6903 : double dfFalseNorthing)
6904 :
6905 : {
6906 358 : TAKE_OPTIONAL_LOCK();
6907 :
6908 179 : if (dfCenterLat == 0.0)
6909 : {
6910 174 : return d->replaceConversionAndUnref(
6911 : proj_create_conversion_equidistant_cylindrical(
6912 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6913 174 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6914 : }
6915 :
6916 : // Non-standard extension with non-zero latitude of origin
6917 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6918 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6919 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6920 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6921 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6922 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6923 :
6924 5 : return OGRERR_NONE;
6925 : }
6926 :
6927 : /************************************************************************/
6928 : /* OSRSetEquirectangular2() */
6929 : /************************************************************************/
6930 :
6931 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6932 : double dfCenterLong, double dfStdParallel1,
6933 : double dfFalseEasting, double dfFalseNorthing)
6934 :
6935 : {
6936 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6937 :
6938 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6939 : dfStdParallel1, dfFalseEasting,
6940 3 : dfFalseNorthing);
6941 : }
6942 :
6943 : /************************************************************************/
6944 : /* SetGS() */
6945 : /************************************************************************/
6946 :
6947 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6948 : double dfFalseEasting, double dfFalseNorthing)
6949 :
6950 : {
6951 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6952 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6953 5 : nullptr, 0.0, nullptr, 0.0));
6954 : }
6955 :
6956 : /************************************************************************/
6957 : /* OSRSetGS() */
6958 : /************************************************************************/
6959 :
6960 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6961 : double dfFalseEasting, double dfFalseNorthing)
6962 :
6963 : {
6964 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6965 :
6966 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6967 2 : dfFalseNorthing);
6968 : }
6969 :
6970 : /************************************************************************/
6971 : /* SetGH() */
6972 : /************************************************************************/
6973 :
6974 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6975 : double dfFalseEasting, double dfFalseNorthing)
6976 :
6977 : {
6978 0 : TAKE_OPTIONAL_LOCK();
6979 :
6980 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6981 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6982 0 : nullptr, 0.0, nullptr, 0.0));
6983 : }
6984 :
6985 : /************************************************************************/
6986 : /* OSRSetGH() */
6987 : /************************************************************************/
6988 :
6989 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6990 : double dfFalseEasting, double dfFalseNorthing)
6991 :
6992 : {
6993 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6994 :
6995 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6996 0 : dfFalseNorthing);
6997 : }
6998 :
6999 : /************************************************************************/
7000 : /* SetIGH() */
7001 : /************************************************************************/
7002 :
7003 0 : OGRErr OGRSpatialReference::SetIGH()
7004 :
7005 : {
7006 0 : TAKE_OPTIONAL_LOCK();
7007 :
7008 0 : return d->replaceConversionAndUnref(
7009 : proj_create_conversion_interrupted_goode_homolosine(
7010 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7011 : }
7012 :
7013 : /************************************************************************/
7014 : /* OSRSetIGH() */
7015 : /************************************************************************/
7016 :
7017 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7018 :
7019 : {
7020 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7021 :
7022 0 : return ToPointer(hSRS)->SetIGH();
7023 : }
7024 :
7025 : /************************************************************************/
7026 : /* SetGEOS() */
7027 : /************************************************************************/
7028 :
7029 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7030 : double dfSatelliteHeight,
7031 : double dfFalseEasting,
7032 : double dfFalseNorthing)
7033 :
7034 : {
7035 6 : TAKE_OPTIONAL_LOCK();
7036 :
7037 3 : return d->replaceConversionAndUnref(
7038 : proj_create_conversion_geostationary_satellite_sweep_y(
7039 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7040 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7041 : }
7042 :
7043 : /************************************************************************/
7044 : /* OSRSetGEOS() */
7045 : /************************************************************************/
7046 :
7047 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7048 : double dfSatelliteHeight, double dfFalseEasting,
7049 : double dfFalseNorthing)
7050 :
7051 : {
7052 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7053 :
7054 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7055 0 : dfFalseEasting, dfFalseNorthing);
7056 : }
7057 :
7058 : /************************************************************************/
7059 : /* SetGaussSchreiberTMercator() */
7060 : /************************************************************************/
7061 :
7062 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7063 : double dfCenterLong,
7064 : double dfScale,
7065 : double dfFalseEasting,
7066 : double dfFalseNorthing)
7067 :
7068 : {
7069 0 : TAKE_OPTIONAL_LOCK();
7070 :
7071 0 : return d->replaceConversionAndUnref(
7072 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7073 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7074 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7075 : }
7076 :
7077 : /************************************************************************/
7078 : /* OSRSetGaussSchreiberTMercator() */
7079 : /************************************************************************/
7080 :
7081 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7082 : double dfCenterLat, double dfCenterLong,
7083 : double dfScale, double dfFalseEasting,
7084 : double dfFalseNorthing)
7085 :
7086 : {
7087 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7088 :
7089 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7090 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7091 : }
7092 :
7093 : /************************************************************************/
7094 : /* SetGnomonic() */
7095 : /************************************************************************/
7096 :
7097 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7098 : double dfFalseEasting,
7099 : double dfFalseNorthing)
7100 :
7101 : {
7102 4 : TAKE_OPTIONAL_LOCK();
7103 :
7104 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7105 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7106 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7107 : }
7108 :
7109 : /************************************************************************/
7110 : /* OSRSetGnomonic() */
7111 : /************************************************************************/
7112 :
7113 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7114 : double dfCenterLong, double dfFalseEasting,
7115 : double dfFalseNorthing)
7116 :
7117 : {
7118 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7119 :
7120 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7121 0 : dfFalseEasting, dfFalseNorthing);
7122 : }
7123 :
7124 : /************************************************************************/
7125 : /* SetHOMAC() */
7126 : /************************************************************************/
7127 :
7128 : /**
7129 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7130 : * azimuth angle.
7131 : *
7132 : * This projection corresponds to EPSG projection method 9815, also
7133 : * sometimes known as hotine oblique mercator (variant B).
7134 : *
7135 : * This method does the same thing as the C function OSRSetHOMAC().
7136 : *
7137 : * @param dfCenterLat Latitude of the projection origin.
7138 : * @param dfCenterLong Longitude of the projection origin.
7139 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7140 : * centerline.
7141 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7142 : * @param dfScale Scale factor applies to the projection origin.
7143 : * @param dfFalseEasting False easting.
7144 : * @param dfFalseNorthing False northing.
7145 : *
7146 : * @return OGRERR_NONE on success.
7147 : */
7148 :
7149 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7150 : double dfAzimuth, double dfRectToSkew,
7151 : double dfScale, double dfFalseEasting,
7152 : double dfFalseNorthing)
7153 :
7154 : {
7155 8 : TAKE_OPTIONAL_LOCK();
7156 :
7157 4 : return d->replaceConversionAndUnref(
7158 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7159 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7160 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7161 8 : 0.0, nullptr, 0.0));
7162 : }
7163 :
7164 : /************************************************************************/
7165 : /* OSRSetHOMAC() */
7166 : /************************************************************************/
7167 :
7168 : /**
7169 : * \brief Set an Oblique Mercator projection using azimuth angle.
7170 : *
7171 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7172 : */
7173 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7174 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7175 : double dfScale, double dfFalseEasting,
7176 : double dfFalseNorthing)
7177 :
7178 : {
7179 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7180 :
7181 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7182 : dfRectToSkew, dfScale, dfFalseEasting,
7183 0 : dfFalseNorthing);
7184 : }
7185 :
7186 : /************************************************************************/
7187 : /* SetHOM() */
7188 : /************************************************************************/
7189 :
7190 : /**
7191 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7192 : *
7193 : * This projection corresponds to EPSG projection method 9812, also
7194 : * sometimes known as hotine oblique mercator (variant A)..
7195 : *
7196 : * This method does the same thing as the C function OSRSetHOM().
7197 : *
7198 : * @param dfCenterLat Latitude of the projection origin.
7199 : * @param dfCenterLong Longitude of the projection origin.
7200 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7201 : * centerline.
7202 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7203 : * @param dfScale Scale factor applies to the projection origin.
7204 : * @param dfFalseEasting False easting.
7205 : * @param dfFalseNorthing False northing.
7206 : *
7207 : * @return OGRERR_NONE on success.
7208 : */
7209 :
7210 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7211 : double dfAzimuth, double dfRectToSkew,
7212 : double dfScale, double dfFalseEasting,
7213 : double dfFalseNorthing)
7214 :
7215 : {
7216 26 : TAKE_OPTIONAL_LOCK();
7217 :
7218 13 : return d->replaceConversionAndUnref(
7219 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7220 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7221 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7222 26 : 0.0, nullptr, 0.0));
7223 : }
7224 :
7225 : /************************************************************************/
7226 : /* OSRSetHOM() */
7227 : /************************************************************************/
7228 : /**
7229 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7230 : *
7231 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7232 : */
7233 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7234 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7235 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7236 :
7237 : {
7238 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7239 :
7240 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7241 : dfRectToSkew, dfScale, dfFalseEasting,
7242 0 : dfFalseNorthing);
7243 : }
7244 :
7245 : /************************************************************************/
7246 : /* SetHOM2PNO() */
7247 : /************************************************************************/
7248 :
7249 : /**
7250 : * \brief Set a Hotine Oblique Mercator projection using two points on
7251 : * projection centerline.
7252 : *
7253 : * This method does the same thing as the C function OSRSetHOM2PNO().
7254 : *
7255 : * @param dfCenterLat Latitude of the projection origin.
7256 : * @param dfLat1 Latitude of the first point on center line.
7257 : * @param dfLong1 Longitude of the first point on center line.
7258 : * @param dfLat2 Latitude of the second point on center line.
7259 : * @param dfLong2 Longitude of the second point on center line.
7260 : * @param dfScale Scale factor applies to the projection origin.
7261 : * @param dfFalseEasting False easting.
7262 : * @param dfFalseNorthing False northing.
7263 : *
7264 : * @return OGRERR_NONE on success.
7265 : */
7266 :
7267 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7268 : double dfLong1, double dfLat2,
7269 : double dfLong2, double dfScale,
7270 : double dfFalseEasting,
7271 : double dfFalseNorthing)
7272 :
7273 : {
7274 6 : TAKE_OPTIONAL_LOCK();
7275 :
7276 3 : return d->replaceConversionAndUnref(
7277 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7278 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7279 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7280 6 : 0.0));
7281 : }
7282 :
7283 : /************************************************************************/
7284 : /* OSRSetHOM2PNO() */
7285 : /************************************************************************/
7286 : /**
7287 : * \brief Set a Hotine Oblique Mercator projection using two points on
7288 : * projection centerline.
7289 : *
7290 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7291 : */
7292 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7293 : double dfLat1, double dfLong1, double dfLat2,
7294 : double dfLong2, double dfScale, double dfFalseEasting,
7295 : double dfFalseNorthing)
7296 :
7297 : {
7298 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7299 :
7300 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7301 : dfLong2, dfScale, dfFalseEasting,
7302 0 : dfFalseNorthing);
7303 : }
7304 :
7305 : /************************************************************************/
7306 : /* SetLOM() */
7307 : /************************************************************************/
7308 :
7309 : /**
7310 : * \brief Set a Laborde Oblique Mercator projection.
7311 : *
7312 : * @param dfCenterLat Latitude of the projection origin.
7313 : * @param dfCenterLong Longitude of the projection origin.
7314 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7315 : * centerline.
7316 : * @param dfScale Scale factor on the initial line
7317 : * @param dfFalseEasting False easting.
7318 : * @param dfFalseNorthing False northing.
7319 : *
7320 : * @return OGRERR_NONE on success.
7321 : */
7322 :
7323 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7324 : double dfAzimuth, double dfScale,
7325 : double dfFalseEasting,
7326 : double dfFalseNorthing)
7327 :
7328 : {
7329 0 : TAKE_OPTIONAL_LOCK();
7330 :
7331 0 : return d->replaceConversionAndUnref(
7332 : proj_create_conversion_laborde_oblique_mercator(
7333 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7334 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7335 : }
7336 :
7337 : /************************************************************************/
7338 : /* SetIWMPolyconic() */
7339 : /************************************************************************/
7340 :
7341 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7342 : double dfCenterLong,
7343 : double dfFalseEasting,
7344 : double dfFalseNorthing)
7345 :
7346 : {
7347 0 : TAKE_OPTIONAL_LOCK();
7348 :
7349 0 : return d->replaceConversionAndUnref(
7350 : proj_create_conversion_international_map_world_polyconic(
7351 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7352 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7353 : }
7354 :
7355 : /************************************************************************/
7356 : /* OSRSetIWMPolyconic() */
7357 : /************************************************************************/
7358 :
7359 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7360 : double dfLat2, double dfCenterLong,
7361 : double dfFalseEasting, double dfFalseNorthing)
7362 :
7363 : {
7364 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7365 :
7366 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7367 0 : dfFalseEasting, dfFalseNorthing);
7368 : }
7369 :
7370 : /************************************************************************/
7371 : /* SetKrovak() */
7372 : /************************************************************************/
7373 :
7374 : /** Krovak east-north projection.
7375 : *
7376 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7377 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7378 : */
7379 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7380 : double dfAzimuth,
7381 : double dfPseudoStdParallel1,
7382 : double dfScale, double dfFalseEasting,
7383 : double dfFalseNorthing)
7384 :
7385 : {
7386 6 : TAKE_OPTIONAL_LOCK();
7387 :
7388 3 : return d->replaceConversionAndUnref(
7389 : proj_create_conversion_krovak_north_oriented(
7390 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7391 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7392 6 : nullptr, 0.0, nullptr, 0.0));
7393 : }
7394 :
7395 : /************************************************************************/
7396 : /* OSRSetKrovak() */
7397 : /************************************************************************/
7398 :
7399 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7400 : double dfCenterLong, double dfAzimuth,
7401 : double dfPseudoStdParallel1, double dfScale,
7402 : double dfFalseEasting, double dfFalseNorthing)
7403 :
7404 : {
7405 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7406 :
7407 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7408 : dfPseudoStdParallel1, dfScale,
7409 0 : dfFalseEasting, dfFalseNorthing);
7410 : }
7411 :
7412 : /************************************************************************/
7413 : /* SetLAEA() */
7414 : /************************************************************************/
7415 :
7416 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7417 : double dfFalseEasting,
7418 : double dfFalseNorthing)
7419 :
7420 : {
7421 34 : TAKE_OPTIONAL_LOCK();
7422 :
7423 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7424 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7425 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7426 :
7427 17 : const char *pszName = nullptr;
7428 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7429 17 : CPLString osName = pszName ? pszName : "";
7430 :
7431 17 : d->refreshProjObj();
7432 :
7433 17 : d->demoteFromBoundCRS();
7434 :
7435 17 : auto cs = proj_create_cartesian_2D_cs(
7436 : d->getPROJContext(),
7437 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7438 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7439 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7440 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7441 : : PJ_CART2D_EASTING_NORTHING,
7442 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7443 : auto projCRS =
7444 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7445 17 : d->getGeodBaseCRS(), conv, cs);
7446 17 : proj_destroy(conv);
7447 17 : proj_destroy(cs);
7448 :
7449 17 : d->setPjCRS(projCRS);
7450 :
7451 17 : d->undoDemoteFromBoundCRS();
7452 :
7453 34 : return OGRERR_NONE;
7454 : }
7455 :
7456 : /************************************************************************/
7457 : /* OSRSetLAEA() */
7458 : /************************************************************************/
7459 :
7460 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7461 : double dfCenterLong, double dfFalseEasting,
7462 : double dfFalseNorthing)
7463 :
7464 : {
7465 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7466 :
7467 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7468 0 : dfFalseNorthing);
7469 : }
7470 :
7471 : /************************************************************************/
7472 : /* SetLCC() */
7473 : /************************************************************************/
7474 :
7475 148 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7476 : double dfCenterLat, double dfCenterLong,
7477 : double dfFalseEasting,
7478 : double dfFalseNorthing)
7479 :
7480 : {
7481 296 : TAKE_OPTIONAL_LOCK();
7482 :
7483 148 : return d->replaceConversionAndUnref(
7484 : proj_create_conversion_lambert_conic_conformal_2sp(
7485 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7486 296 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7487 : }
7488 :
7489 : /************************************************************************/
7490 : /* OSRSetLCC() */
7491 : /************************************************************************/
7492 :
7493 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7494 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7495 : double dfFalseNorthing)
7496 :
7497 : {
7498 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7499 :
7500 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7501 1 : dfFalseEasting, dfFalseNorthing);
7502 : }
7503 :
7504 : /************************************************************************/
7505 : /* SetLCC1SP() */
7506 : /************************************************************************/
7507 :
7508 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7509 : double dfScale, double dfFalseEasting,
7510 : double dfFalseNorthing)
7511 :
7512 : {
7513 20 : TAKE_OPTIONAL_LOCK();
7514 :
7515 10 : return d->replaceConversionAndUnref(
7516 : proj_create_conversion_lambert_conic_conformal_1sp(
7517 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7518 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7519 : }
7520 :
7521 : /************************************************************************/
7522 : /* OSRSetLCC1SP() */
7523 : /************************************************************************/
7524 :
7525 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7526 : double dfCenterLong, double dfScale, double dfFalseEasting,
7527 : double dfFalseNorthing)
7528 :
7529 : {
7530 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7531 :
7532 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7533 0 : dfFalseEasting, dfFalseNorthing);
7534 : }
7535 :
7536 : /************************************************************************/
7537 : /* SetLCCB() */
7538 : /************************************************************************/
7539 :
7540 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7541 : double dfCenterLat, double dfCenterLong,
7542 : double dfFalseEasting,
7543 : double dfFalseNorthing)
7544 :
7545 : {
7546 4 : TAKE_OPTIONAL_LOCK();
7547 :
7548 2 : return d->replaceConversionAndUnref(
7549 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7550 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7551 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7552 : }
7553 :
7554 : /************************************************************************/
7555 : /* OSRSetLCCB() */
7556 : /************************************************************************/
7557 :
7558 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7559 : double dfCenterLat, double dfCenterLong,
7560 : double dfFalseEasting, double dfFalseNorthing)
7561 :
7562 : {
7563 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7564 :
7565 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7566 0 : dfFalseEasting, dfFalseNorthing);
7567 : }
7568 :
7569 : /************************************************************************/
7570 : /* SetMC() */
7571 : /************************************************************************/
7572 :
7573 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7574 : double dfFalseEasting, double dfFalseNorthing)
7575 :
7576 : {
7577 8 : TAKE_OPTIONAL_LOCK();
7578 :
7579 : (void)dfCenterLat; // ignored
7580 :
7581 4 : return d->replaceConversionAndUnref(
7582 : proj_create_conversion_miller_cylindrical(
7583 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7584 8 : nullptr, 0, nullptr, 0));
7585 : }
7586 :
7587 : /************************************************************************/
7588 : /* OSRSetMC() */
7589 : /************************************************************************/
7590 :
7591 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7592 : double dfCenterLong, double dfFalseEasting,
7593 : double dfFalseNorthing)
7594 :
7595 : {
7596 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7597 :
7598 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7599 0 : dfFalseNorthing);
7600 : }
7601 :
7602 : /************************************************************************/
7603 : /* SetMercator() */
7604 : /************************************************************************/
7605 :
7606 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7607 : double dfScale, double dfFalseEasting,
7608 : double dfFalseNorthing)
7609 :
7610 : {
7611 118 : TAKE_OPTIONAL_LOCK();
7612 :
7613 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7614 : {
7615 : // Not sure this is correct, but this is how it has been used
7616 : // historically
7617 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7618 0 : dfFalseNorthing);
7619 : }
7620 59 : return d->replaceConversionAndUnref(
7621 : proj_create_conversion_mercator_variant_a(
7622 : d->getPROJContext(),
7623 : dfCenterLat, // should be zero
7624 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7625 59 : nullptr, 0));
7626 : }
7627 :
7628 : /************************************************************************/
7629 : /* OSRSetMercator() */
7630 : /************************************************************************/
7631 :
7632 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7633 : double dfCenterLong, double dfScale,
7634 : double dfFalseEasting, double dfFalseNorthing)
7635 :
7636 : {
7637 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7638 :
7639 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7640 2 : dfFalseEasting, dfFalseNorthing);
7641 : }
7642 :
7643 : /************************************************************************/
7644 : /* SetMercator2SP() */
7645 : /************************************************************************/
7646 :
7647 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7648 : double dfCenterLong,
7649 : double dfFalseEasting,
7650 : double dfFalseNorthing)
7651 :
7652 : {
7653 31 : if (dfCenterLat == 0.0)
7654 : {
7655 30 : return d->replaceConversionAndUnref(
7656 : proj_create_conversion_mercator_variant_b(
7657 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7658 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7659 : }
7660 :
7661 1 : TAKE_OPTIONAL_LOCK();
7662 :
7663 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7664 :
7665 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7666 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7667 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7668 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7669 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7670 :
7671 1 : return OGRERR_NONE;
7672 : }
7673 :
7674 : /************************************************************************/
7675 : /* OSRSetMercator2SP() */
7676 : /************************************************************************/
7677 :
7678 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7679 : double dfCenterLat, double dfCenterLong,
7680 : double dfFalseEasting, double dfFalseNorthing)
7681 :
7682 : {
7683 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7684 :
7685 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7686 1 : dfFalseEasting, dfFalseNorthing);
7687 : }
7688 :
7689 : /************************************************************************/
7690 : /* SetMollweide() */
7691 : /************************************************************************/
7692 :
7693 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7694 : double dfFalseEasting,
7695 : double dfFalseNorthing)
7696 :
7697 : {
7698 6 : TAKE_OPTIONAL_LOCK();
7699 :
7700 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7701 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7702 6 : nullptr, 0, nullptr, 0));
7703 : }
7704 :
7705 : /************************************************************************/
7706 : /* OSRSetMollweide() */
7707 : /************************************************************************/
7708 :
7709 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7710 : double dfFalseEasting, double dfFalseNorthing)
7711 :
7712 : {
7713 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7714 :
7715 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7716 0 : dfFalseNorthing);
7717 : }
7718 :
7719 : /************************************************************************/
7720 : /* SetNZMG() */
7721 : /************************************************************************/
7722 :
7723 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7724 : double dfFalseEasting,
7725 : double dfFalseNorthing)
7726 :
7727 : {
7728 14 : TAKE_OPTIONAL_LOCK();
7729 :
7730 7 : return d->replaceConversionAndUnref(
7731 : proj_create_conversion_new_zealand_mapping_grid(
7732 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7733 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7734 : }
7735 :
7736 : /************************************************************************/
7737 : /* OSRSetNZMG() */
7738 : /************************************************************************/
7739 :
7740 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7741 : double dfCenterLong, double dfFalseEasting,
7742 : double dfFalseNorthing)
7743 :
7744 : {
7745 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7746 :
7747 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7748 0 : dfFalseNorthing);
7749 : }
7750 :
7751 : /************************************************************************/
7752 : /* SetOS() */
7753 : /************************************************************************/
7754 :
7755 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7756 : double dfScale, double dfFalseEasting,
7757 : double dfFalseNorthing)
7758 :
7759 : {
7760 12 : TAKE_OPTIONAL_LOCK();
7761 :
7762 6 : return d->replaceConversionAndUnref(
7763 : proj_create_conversion_oblique_stereographic(
7764 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7765 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7766 : }
7767 :
7768 : /************************************************************************/
7769 : /* OSRSetOS() */
7770 : /************************************************************************/
7771 :
7772 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7773 : double dfCMeridian, double dfScale, double dfFalseEasting,
7774 : double dfFalseNorthing)
7775 :
7776 : {
7777 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7778 :
7779 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7780 0 : dfFalseEasting, dfFalseNorthing);
7781 : }
7782 :
7783 : /************************************************************************/
7784 : /* SetOrthographic() */
7785 : /************************************************************************/
7786 :
7787 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7788 : double dfCenterLong,
7789 : double dfFalseEasting,
7790 : double dfFalseNorthing)
7791 :
7792 : {
7793 14 : TAKE_OPTIONAL_LOCK();
7794 :
7795 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7796 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7797 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7798 : }
7799 :
7800 : /************************************************************************/
7801 : /* OSRSetOrthographic() */
7802 : /************************************************************************/
7803 :
7804 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7805 : double dfCenterLong, double dfFalseEasting,
7806 : double dfFalseNorthing)
7807 :
7808 : {
7809 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7810 :
7811 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7812 1 : dfFalseEasting, dfFalseNorthing);
7813 : }
7814 :
7815 : /************************************************************************/
7816 : /* SetPolyconic() */
7817 : /************************************************************************/
7818 :
7819 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7820 : double dfCenterLong,
7821 : double dfFalseEasting,
7822 : double dfFalseNorthing)
7823 :
7824 : {
7825 14 : TAKE_OPTIONAL_LOCK();
7826 :
7827 : // note: it seems that by some definitions this should include a
7828 : // scale_factor parameter.
7829 7 : return d->replaceConversionAndUnref(
7830 : proj_create_conversion_american_polyconic(
7831 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7832 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7833 : }
7834 :
7835 : /************************************************************************/
7836 : /* OSRSetPolyconic() */
7837 : /************************************************************************/
7838 :
7839 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7840 : double dfCenterLong, double dfFalseEasting,
7841 : double dfFalseNorthing)
7842 :
7843 : {
7844 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7845 :
7846 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7847 0 : dfFalseEasting, dfFalseNorthing);
7848 : }
7849 :
7850 : /************************************************************************/
7851 : /* SetPS() */
7852 : /************************************************************************/
7853 :
7854 : /** Sets a Polar Stereographic projection.
7855 : *
7856 : * Two variants are possible:
7857 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7858 : * interpreted as the latitude of origin, combined with the scale factor
7859 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7860 : * is interpreted as the latitude of true scale. In that situation, dfScale
7861 : * must be set to 1 (it is ignored in the projection parameters)
7862 : */
7863 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7864 : double dfScale, double dfFalseEasting,
7865 : double dfFalseNorthing)
7866 :
7867 : {
7868 60 : TAKE_OPTIONAL_LOCK();
7869 :
7870 : PJ *conv;
7871 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7872 : {
7873 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7874 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7875 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7876 : }
7877 : else
7878 : {
7879 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7880 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7881 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7882 : }
7883 :
7884 30 : const char *pszName = nullptr;
7885 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7886 30 : CPLString osName = pszName ? pszName : "";
7887 :
7888 30 : d->refreshProjObj();
7889 :
7890 30 : d->demoteFromBoundCRS();
7891 :
7892 30 : auto cs = proj_create_cartesian_2D_cs(
7893 : d->getPROJContext(),
7894 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7895 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7896 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7897 : auto projCRS =
7898 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7899 30 : d->getGeodBaseCRS(), conv, cs);
7900 30 : proj_destroy(conv);
7901 30 : proj_destroy(cs);
7902 :
7903 30 : d->setPjCRS(projCRS);
7904 :
7905 30 : d->undoDemoteFromBoundCRS();
7906 :
7907 60 : return OGRERR_NONE;
7908 : }
7909 :
7910 : /************************************************************************/
7911 : /* OSRSetPS() */
7912 : /************************************************************************/
7913 :
7914 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7915 : double dfCenterLong, double dfScale, double dfFalseEasting,
7916 : double dfFalseNorthing)
7917 :
7918 : {
7919 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7920 :
7921 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7922 1 : dfFalseEasting, dfFalseNorthing);
7923 : }
7924 :
7925 : /************************************************************************/
7926 : /* SetRobinson() */
7927 : /************************************************************************/
7928 :
7929 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7930 : double dfFalseEasting,
7931 : double dfFalseNorthing)
7932 :
7933 : {
7934 8 : TAKE_OPTIONAL_LOCK();
7935 :
7936 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7937 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7938 8 : nullptr, 0, nullptr, 0));
7939 : }
7940 :
7941 : /************************************************************************/
7942 : /* OSRSetRobinson() */
7943 : /************************************************************************/
7944 :
7945 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7946 : double dfFalseEasting, double dfFalseNorthing)
7947 :
7948 : {
7949 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7950 :
7951 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7952 0 : dfFalseNorthing);
7953 : }
7954 :
7955 : /************************************************************************/
7956 : /* SetSinusoidal() */
7957 : /************************************************************************/
7958 :
7959 35 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7960 : double dfFalseEasting,
7961 : double dfFalseNorthing)
7962 :
7963 : {
7964 70 : TAKE_OPTIONAL_LOCK();
7965 :
7966 35 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7967 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7968 70 : nullptr, 0, nullptr, 0));
7969 : }
7970 :
7971 : /************************************************************************/
7972 : /* OSRSetSinusoidal() */
7973 : /************************************************************************/
7974 :
7975 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7976 : double dfFalseEasting, double dfFalseNorthing)
7977 :
7978 : {
7979 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7980 :
7981 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7982 1 : dfFalseNorthing);
7983 : }
7984 :
7985 : /************************************************************************/
7986 : /* SetStereographic() */
7987 : /************************************************************************/
7988 :
7989 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7990 : double dfCMeridian, double dfScale,
7991 : double dfFalseEasting,
7992 : double dfFalseNorthing)
7993 :
7994 : {
7995 4 : TAKE_OPTIONAL_LOCK();
7996 :
7997 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7998 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7999 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8000 : }
8001 :
8002 : /************************************************************************/
8003 : /* OSRSetStereographic() */
8004 : /************************************************************************/
8005 :
8006 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8007 : double dfCMeridian, double dfScale,
8008 : double dfFalseEasting, double dfFalseNorthing)
8009 :
8010 : {
8011 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8012 :
8013 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8014 0 : dfFalseEasting, dfFalseNorthing);
8015 : }
8016 :
8017 : /************************************************************************/
8018 : /* SetSOC() */
8019 : /* */
8020 : /* NOTE: This definition isn't really used in practice any more */
8021 : /* and should be considered deprecated. It seems that swiss */
8022 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
8023 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
8024 : /* EPSG:2056 and Bug 423. */
8025 : /************************************************************************/
8026 :
8027 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8028 : double dfCentralMeridian,
8029 : double dfFalseEasting,
8030 : double dfFalseNorthing)
8031 :
8032 : {
8033 4 : TAKE_OPTIONAL_LOCK();
8034 :
8035 2 : return d->replaceConversionAndUnref(
8036 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8037 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8038 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8039 4 : 0.0));
8040 : #if 0
8041 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8042 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8043 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8044 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8045 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8046 :
8047 : return OGRERR_NONE;
8048 : #endif
8049 : }
8050 :
8051 : /************************************************************************/
8052 : /* OSRSetSOC() */
8053 : /************************************************************************/
8054 :
8055 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8056 : double dfCentralMeridian, double dfFalseEasting,
8057 : double dfFalseNorthing)
8058 :
8059 : {
8060 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8061 :
8062 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8063 0 : dfFalseEasting, dfFalseNorthing);
8064 : }
8065 :
8066 : /************************************************************************/
8067 : /* SetVDG() */
8068 : /************************************************************************/
8069 :
8070 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8071 : double dfFalseNorthing)
8072 :
8073 : {
8074 4 : TAKE_OPTIONAL_LOCK();
8075 :
8076 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8077 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8078 4 : nullptr, 0, nullptr, 0));
8079 : }
8080 :
8081 : /************************************************************************/
8082 : /* OSRSetVDG() */
8083 : /************************************************************************/
8084 :
8085 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8086 : double dfFalseEasting, double dfFalseNorthing)
8087 :
8088 : {
8089 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8090 :
8091 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8092 0 : dfFalseNorthing);
8093 : }
8094 :
8095 : /************************************************************************/
8096 : /* SetUTM() */
8097 : /************************************************************************/
8098 :
8099 : /**
8100 : * \brief Set UTM projection definition.
8101 : *
8102 : * This will generate a projection definition with the full set of
8103 : * transverse mercator projection parameters for the given UTM zone.
8104 : * If no PROJCS[] description is set yet, one will be set to look
8105 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8106 : *
8107 : * This method is the same as the C function OSRSetUTM().
8108 : *
8109 : * @param nZone UTM zone.
8110 : *
8111 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8112 : * hemisphere.
8113 : *
8114 : * @return OGRERR_NONE on success.
8115 : */
8116 :
8117 313 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8118 :
8119 : {
8120 626 : TAKE_OPTIONAL_LOCK();
8121 :
8122 313 : if (nZone < 0 || nZone > 60)
8123 : {
8124 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8125 0 : return OGRERR_FAILURE;
8126 : }
8127 :
8128 313 : return d->replaceConversionAndUnref(
8129 313 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8130 : }
8131 :
8132 : /************************************************************************/
8133 : /* OSRSetUTM() */
8134 : /************************************************************************/
8135 :
8136 : /**
8137 : * \brief Set UTM projection definition.
8138 : *
8139 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8140 : */
8141 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8142 :
8143 : {
8144 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8145 :
8146 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8147 : }
8148 :
8149 : /************************************************************************/
8150 : /* GetUTMZone() */
8151 : /* */
8152 : /* Returns zero if it isn't UTM. */
8153 : /************************************************************************/
8154 :
8155 : /**
8156 : * \brief Get utm zone information.
8157 : *
8158 : * This is the same as the C function OSRGetUTMZone().
8159 : *
8160 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8161 : * zone which is negative in the southern hemisphere instead of having the
8162 : * pbNorth flag used in the C and C++ interface.
8163 : *
8164 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8165 : * FALSE if southern.
8166 : *
8167 : * @return UTM zone number or zero if this isn't a UTM definition.
8168 : */
8169 :
8170 606 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8171 :
8172 : {
8173 1212 : TAKE_OPTIONAL_LOCK();
8174 :
8175 606 : if (IsProjected() && GetAxesCount() == 3)
8176 : {
8177 1 : OGRSpatialReference *poSRSTmp = Clone();
8178 1 : poSRSTmp->DemoteTo2D(nullptr);
8179 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8180 1 : delete poSRSTmp;
8181 1 : return nZone;
8182 : }
8183 :
8184 605 : const char *pszProjection = GetAttrValue("PROJECTION");
8185 :
8186 605 : if (pszProjection == nullptr ||
8187 525 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8188 274 : return 0;
8189 :
8190 331 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8191 5 : return 0;
8192 :
8193 326 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8194 15 : return 0;
8195 :
8196 311 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8197 6 : return 0;
8198 :
8199 305 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8200 :
8201 305 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8202 0 : return 0;
8203 :
8204 305 : if (pbNorth != nullptr)
8205 239 : *pbNorth = (dfFalseNorthing == 0);
8206 :
8207 : const double dfCentralMeridian =
8208 305 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8209 305 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8210 :
8211 610 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8212 915 : std::isnan(dfZone) ||
8213 305 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8214 0 : return 0;
8215 :
8216 305 : return static_cast<int>(dfZone);
8217 : }
8218 :
8219 : /************************************************************************/
8220 : /* OSRGetUTMZone() */
8221 : /************************************************************************/
8222 :
8223 : /**
8224 : * \brief Get utm zone information.
8225 : *
8226 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8227 : */
8228 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8229 :
8230 : {
8231 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8232 :
8233 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8234 : }
8235 :
8236 : /************************************************************************/
8237 : /* SetWagner() */
8238 : /************************************************************************/
8239 :
8240 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8241 : double dfCenterLat, double dfFalseEasting,
8242 : double dfFalseNorthing)
8243 :
8244 : {
8245 0 : TAKE_OPTIONAL_LOCK();
8246 :
8247 : PJ *conv;
8248 0 : if (nVariation == 1)
8249 : {
8250 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8251 : dfFalseEasting, dfFalseNorthing,
8252 : nullptr, 0.0, nullptr, 0.0);
8253 : }
8254 0 : else if (nVariation == 2)
8255 : {
8256 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8257 : dfFalseEasting, dfFalseNorthing,
8258 : nullptr, 0.0, nullptr, 0.0);
8259 : }
8260 0 : else if (nVariation == 3)
8261 : {
8262 0 : conv = proj_create_conversion_wagner_iii(
8263 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8264 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8265 : }
8266 0 : else if (nVariation == 4)
8267 : {
8268 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8269 : dfFalseEasting, dfFalseNorthing,
8270 : nullptr, 0.0, nullptr, 0.0);
8271 : }
8272 0 : else if (nVariation == 5)
8273 : {
8274 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8275 : dfFalseEasting, dfFalseNorthing,
8276 : nullptr, 0.0, nullptr, 0.0);
8277 : }
8278 0 : else if (nVariation == 6)
8279 : {
8280 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8281 : dfFalseEasting, dfFalseNorthing,
8282 : nullptr, 0.0, nullptr, 0.0);
8283 : }
8284 0 : else if (nVariation == 7)
8285 : {
8286 0 : conv = proj_create_conversion_wagner_vii(
8287 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8288 : 0.0, nullptr, 0.0);
8289 : }
8290 : else
8291 : {
8292 0 : CPLError(CE_Failure, CPLE_AppDefined,
8293 : "Unsupported Wagner variation (%d).", nVariation);
8294 0 : return OGRERR_UNSUPPORTED_SRS;
8295 : }
8296 :
8297 0 : return d->replaceConversionAndUnref(conv);
8298 : }
8299 :
8300 : /************************************************************************/
8301 : /* OSRSetWagner() */
8302 : /************************************************************************/
8303 :
8304 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8305 : double dfCenterLat, double dfFalseEasting,
8306 : double dfFalseNorthing)
8307 :
8308 : {
8309 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8310 :
8311 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8312 0 : dfFalseNorthing);
8313 : }
8314 :
8315 : /************************************************************************/
8316 : /* SetQSC() */
8317 : /************************************************************************/
8318 :
8319 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8320 : {
8321 0 : TAKE_OPTIONAL_LOCK();
8322 :
8323 0 : return d->replaceConversionAndUnref(
8324 : proj_create_conversion_quadrilateralized_spherical_cube(
8325 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8326 0 : 0, nullptr, 0));
8327 : }
8328 :
8329 : /************************************************************************/
8330 : /* OSRSetQSC() */
8331 : /************************************************************************/
8332 :
8333 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8334 : double dfCenterLong)
8335 :
8336 : {
8337 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8338 :
8339 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8340 : }
8341 :
8342 : /************************************************************************/
8343 : /* SetSCH() */
8344 : /************************************************************************/
8345 :
8346 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8347 : double dfPegHeading, double dfPegHgt)
8348 :
8349 : {
8350 0 : TAKE_OPTIONAL_LOCK();
8351 :
8352 0 : return d->replaceConversionAndUnref(
8353 : proj_create_conversion_spherical_cross_track_height(
8354 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8355 0 : nullptr, 0, nullptr, 0));
8356 : }
8357 :
8358 : /************************************************************************/
8359 : /* OSRSetSCH() */
8360 : /************************************************************************/
8361 :
8362 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8363 : double dfPegHeading, double dfPegHgt)
8364 :
8365 : {
8366 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8367 :
8368 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8369 : }
8370 :
8371 : /************************************************************************/
8372 : /* SetVerticalPerspective() */
8373 : /************************************************************************/
8374 :
8375 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8376 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8377 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8378 : {
8379 6 : TAKE_OPTIONAL_LOCK();
8380 :
8381 3 : return d->replaceConversionAndUnref(
8382 : proj_create_conversion_vertical_perspective(
8383 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8384 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8385 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8386 : }
8387 :
8388 : /************************************************************************/
8389 : /* OSRSetVerticalPerspective() */
8390 : /************************************************************************/
8391 :
8392 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8393 : double dfTopoOriginLat, double dfTopoOriginLon,
8394 : double dfTopoOriginHeight,
8395 : double dfViewPointHeight,
8396 : double dfFalseEasting, double dfFalseNorthing)
8397 :
8398 : {
8399 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8400 :
8401 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8402 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8403 1 : dfFalseEasting, dfFalseNorthing);
8404 : }
8405 :
8406 : /************************************************************************/
8407 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8408 : /************************************************************************/
8409 :
8410 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8411 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8412 : double dfAxisRotation)
8413 : {
8414 4 : TAKE_OPTIONAL_LOCK();
8415 :
8416 2 : d->refreshProjObj();
8417 2 : if (!d->m_pj_crs)
8418 0 : return OGRERR_FAILURE;
8419 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8420 0 : return OGRERR_FAILURE;
8421 2 : auto ctxt = d->getPROJContext();
8422 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8423 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8424 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8425 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8426 2 : d->m_pj_crs, conv, cs));
8427 2 : proj_destroy(conv);
8428 2 : proj_destroy(cs);
8429 2 : return OGRERR_NONE;
8430 : }
8431 :
8432 : /************************************************************************/
8433 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8434 : /************************************************************************/
8435 :
8436 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8437 : const char *pszCRSName, double dfGridNorthPoleLat,
8438 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8439 : {
8440 3 : TAKE_OPTIONAL_LOCK();
8441 :
8442 : #if PROJ_VERSION_MAJOR > 8 || \
8443 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8444 : d->refreshProjObj();
8445 : if (!d->m_pj_crs)
8446 : return OGRERR_FAILURE;
8447 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8448 : return OGRERR_FAILURE;
8449 : auto ctxt = d->getPROJContext();
8450 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8451 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8452 : nullptr, 0);
8453 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8454 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8455 : d->m_pj_crs, conv, cs));
8456 : proj_destroy(conv);
8457 : proj_destroy(cs);
8458 : return OGRERR_NONE;
8459 : #else
8460 : (void)pszCRSName;
8461 3 : SetProjection("Rotated_pole");
8462 3 : SetExtension(
8463 : "PROJCS", "PROJ4",
8464 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8465 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8466 : "+to_meter=0.0174532925199433 "
8467 : "+wktext",
8468 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8469 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8470 : GetSemiMinor(nullptr)));
8471 6 : return OGRERR_NONE;
8472 : #endif
8473 : }
8474 :
8475 : /************************************************************************/
8476 : /* SetAuthority() */
8477 : /************************************************************************/
8478 :
8479 : /**
8480 : * \brief Set the authority for a node.
8481 : *
8482 : * This method is the same as the C function OSRSetAuthority().
8483 : *
8484 : * @param pszTargetKey the partial or complete path to the node to
8485 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8486 : *
8487 : * @param pszAuthority authority name, such as "EPSG".
8488 : *
8489 : * @param nCode code for value with this authority.
8490 : *
8491 : * @return OGRERR_NONE on success.
8492 : */
8493 :
8494 12265 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8495 : const char *pszAuthority, int nCode)
8496 :
8497 : {
8498 24530 : TAKE_OPTIONAL_LOCK();
8499 :
8500 12265 : d->refreshProjObj();
8501 12265 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8502 :
8503 12265 : if (pszTargetKey == nullptr)
8504 : {
8505 263 : if (!d->m_pj_crs)
8506 0 : return OGRERR_FAILURE;
8507 263 : CPLString osCode;
8508 263 : osCode.Printf("%d", nCode);
8509 263 : d->demoteFromBoundCRS();
8510 263 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8511 : pszAuthority, osCode.c_str()));
8512 263 : d->undoDemoteFromBoundCRS();
8513 263 : return OGRERR_NONE;
8514 : }
8515 :
8516 12002 : d->demoteFromBoundCRS();
8517 12002 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8518 : {
8519 3968 : CPLString osCode;
8520 3968 : osCode.Printf("%d", nCode);
8521 : auto newGeogCRS =
8522 3968 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8523 : pszAuthority, osCode.c_str());
8524 :
8525 : auto conv =
8526 3968 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8527 :
8528 3968 : auto projCRS = proj_create_projected_crs(
8529 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8530 3968 : d->getProjCRSCoordSys());
8531 :
8532 : // Preserve existing id on the PROJCRS
8533 3968 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8534 3968 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8535 3968 : if (pszProjCRSAuthName && pszProjCRSCode)
8536 : {
8537 : auto projCRSWithId =
8538 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8539 : pszProjCRSCode);
8540 0 : proj_destroy(projCRS);
8541 0 : projCRS = projCRSWithId;
8542 : }
8543 :
8544 3968 : proj_destroy(newGeogCRS);
8545 3968 : proj_destroy(conv);
8546 :
8547 3968 : d->setPjCRS(projCRS);
8548 3968 : d->undoDemoteFromBoundCRS();
8549 3968 : return OGRERR_NONE;
8550 : }
8551 8034 : d->undoDemoteFromBoundCRS();
8552 :
8553 : /* -------------------------------------------------------------------- */
8554 : /* Find the node below which the authority should be put. */
8555 : /* -------------------------------------------------------------------- */
8556 8034 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8557 :
8558 8034 : if (poNode == nullptr)
8559 0 : return OGRERR_FAILURE;
8560 :
8561 : /* -------------------------------------------------------------------- */
8562 : /* If there is an existing AUTHORITY child blow it away before */
8563 : /* trying to set a new one. */
8564 : /* -------------------------------------------------------------------- */
8565 8034 : int iOldChild = poNode->FindChild("AUTHORITY");
8566 8034 : if (iOldChild != -1)
8567 5 : poNode->DestroyChild(iOldChild);
8568 :
8569 : /* -------------------------------------------------------------------- */
8570 : /* Create a new authority node. */
8571 : /* -------------------------------------------------------------------- */
8572 8034 : char szCode[32] = {};
8573 :
8574 8034 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8575 :
8576 8034 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8577 8034 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8578 8034 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8579 :
8580 8034 : poNode->AddChild(poAuthNode);
8581 :
8582 8034 : return OGRERR_NONE;
8583 : }
8584 :
8585 : /************************************************************************/
8586 : /* OSRSetAuthority() */
8587 : /************************************************************************/
8588 :
8589 : /**
8590 : * \brief Set the authority for a node.
8591 : *
8592 : * This function is the same as OGRSpatialReference::SetAuthority().
8593 : */
8594 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8595 : const char *pszAuthority, int nCode)
8596 :
8597 : {
8598 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8599 :
8600 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8601 : }
8602 :
8603 : /************************************************************************/
8604 : /* GetAuthorityCode() */
8605 : /************************************************************************/
8606 :
8607 : /**
8608 : * \brief Get the authority code for a node.
8609 : *
8610 : * This method is used to query an AUTHORITY[] node from within the
8611 : * WKT tree, and fetch the code value.
8612 : *
8613 : * While in theory values may be non-numeric, for the EPSG authority all
8614 : * code values should be integral.
8615 : *
8616 : * This method is the same as the C function OSRGetAuthorityCode().
8617 : *
8618 : * @param pszTargetKey the partial or complete path to the node to
8619 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8620 : * search for an authority node on the root element.
8621 : *
8622 : * @return value code from authority node, or NULL on failure. The value
8623 : * returned is internal and should not be freed or modified.
8624 : */
8625 :
8626 : const char *
8627 47242 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8628 :
8629 : {
8630 94484 : TAKE_OPTIONAL_LOCK();
8631 :
8632 47242 : d->refreshProjObj();
8633 47242 : const char *pszInputTargetKey = pszTargetKey;
8634 47242 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8635 47242 : if (pszTargetKey == nullptr)
8636 : {
8637 39110 : if (!d->m_pj_crs)
8638 : {
8639 17 : return nullptr;
8640 : }
8641 39093 : d->demoteFromBoundCRS();
8642 39093 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8643 39093 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8644 : {
8645 1282 : auto ctxt = d->getPROJContext();
8646 1282 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8647 1282 : if (cs)
8648 : {
8649 1282 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8650 1282 : proj_destroy(cs);
8651 1282 : if (axisCount == 3)
8652 : {
8653 : // This might come from a COMPD_CS with a VERT_DATUM type =
8654 : // 2002 in which case, using the WKT1 representation will
8655 : // enable us to recover the EPSG code.
8656 14 : pszTargetKey = pszInputTargetKey;
8657 : }
8658 : }
8659 : }
8660 39093 : d->undoDemoteFromBoundCRS();
8661 39093 : if (ret != nullptr || pszTargetKey == nullptr)
8662 : {
8663 39093 : return ret;
8664 : }
8665 : }
8666 :
8667 : // Special key for that context
8668 8136 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8669 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8670 : {
8671 4 : auto ctxt = d->getPROJContext();
8672 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8673 4 : if (crs)
8674 : {
8675 4 : const char *ret = proj_get_id_code(crs, 0);
8676 4 : if (ret)
8677 4 : ret = CPLSPrintf("%s", ret);
8678 4 : proj_destroy(crs);
8679 4 : return ret;
8680 : }
8681 : }
8682 8132 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8683 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8684 : {
8685 4 : auto ctxt = d->getPROJContext();
8686 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8687 4 : if (crs)
8688 : {
8689 4 : const char *ret = proj_get_id_code(crs, 0);
8690 4 : if (ret)
8691 4 : ret = CPLSPrintf("%s", ret);
8692 4 : proj_destroy(crs);
8693 4 : return ret;
8694 : }
8695 : }
8696 :
8697 : /* -------------------------------------------------------------------- */
8698 : /* Find the node below which the authority should be put. */
8699 : /* -------------------------------------------------------------------- */
8700 8124 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8701 :
8702 8124 : if (poNode == nullptr)
8703 103 : return nullptr;
8704 :
8705 : /* -------------------------------------------------------------------- */
8706 : /* Fetch AUTHORITY child if there is one. */
8707 : /* -------------------------------------------------------------------- */
8708 8021 : if (poNode->FindChild("AUTHORITY") == -1)
8709 195 : return nullptr;
8710 :
8711 7826 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8712 :
8713 : /* -------------------------------------------------------------------- */
8714 : /* Create a new authority node. */
8715 : /* -------------------------------------------------------------------- */
8716 7826 : if (poNode->GetChildCount() < 2)
8717 0 : return nullptr;
8718 :
8719 7826 : return poNode->GetChild(1)->GetValue();
8720 : }
8721 :
8722 : /************************************************************************/
8723 : /* OSRGetAuthorityCode() */
8724 : /************************************************************************/
8725 :
8726 : /**
8727 : * \brief Get the authority code for a node.
8728 : *
8729 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8730 : */
8731 745 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8732 : const char *pszTargetKey)
8733 :
8734 : {
8735 745 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8736 :
8737 745 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8738 : }
8739 :
8740 : /************************************************************************/
8741 : /* GetAuthorityName() */
8742 : /************************************************************************/
8743 :
8744 : /**
8745 : * \brief Get the authority name for a node.
8746 : *
8747 : * This method is used to query an AUTHORITY[] node from within the
8748 : * WKT tree, and fetch the authority name value.
8749 : *
8750 : * The most common authority is "EPSG".
8751 : *
8752 : * This method is the same as the C function OSRGetAuthorityName().
8753 : *
8754 : * @param pszTargetKey the partial or complete path to the node to
8755 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8756 : * search for an authority node on the root element.
8757 : *
8758 : * @return value code from authority node, or NULL on failure. The value
8759 : * returned is internal and should not be freed or modified.
8760 : */
8761 :
8762 : const char *
8763 61040 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8764 :
8765 : {
8766 122080 : TAKE_OPTIONAL_LOCK();
8767 :
8768 61040 : d->refreshProjObj();
8769 61040 : const char *pszInputTargetKey = pszTargetKey;
8770 61040 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8771 61040 : if (pszTargetKey == nullptr)
8772 : {
8773 32530 : if (!d->m_pj_crs)
8774 : {
8775 18 : return nullptr;
8776 : }
8777 32512 : d->demoteFromBoundCRS();
8778 32512 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8779 32512 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8780 : {
8781 904 : auto ctxt = d->getPROJContext();
8782 904 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8783 904 : if (cs)
8784 : {
8785 904 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8786 904 : proj_destroy(cs);
8787 904 : if (axisCount == 3)
8788 : {
8789 : // This might come from a COMPD_CS with a VERT_DATUM type =
8790 : // 2002 in which case, using the WKT1 representation will
8791 : // enable us to recover the EPSG code.
8792 14 : pszTargetKey = pszInputTargetKey;
8793 : }
8794 : }
8795 : }
8796 32512 : d->undoDemoteFromBoundCRS();
8797 32512 : if (ret != nullptr || pszTargetKey == nullptr)
8798 : {
8799 32512 : return ret;
8800 : }
8801 : }
8802 :
8803 : // Special key for that context
8804 28514 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8805 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8806 : {
8807 4 : auto ctxt = d->getPROJContext();
8808 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8809 4 : if (crs)
8810 : {
8811 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8812 4 : if (ret)
8813 4 : ret = CPLSPrintf("%s", ret);
8814 4 : proj_destroy(crs);
8815 4 : return ret;
8816 : }
8817 : }
8818 28510 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8819 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8820 : {
8821 4 : auto ctxt = d->getPROJContext();
8822 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8823 4 : if (crs)
8824 : {
8825 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8826 4 : if (ret)
8827 4 : ret = CPLSPrintf("%s", ret);
8828 4 : proj_destroy(crs);
8829 4 : return ret;
8830 : }
8831 : }
8832 :
8833 : /* -------------------------------------------------------------------- */
8834 : /* Find the node below which the authority should be put. */
8835 : /* -------------------------------------------------------------------- */
8836 28502 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8837 :
8838 28502 : if (poNode == nullptr)
8839 11863 : return nullptr;
8840 :
8841 : /* -------------------------------------------------------------------- */
8842 : /* Fetch AUTHORITY child if there is one. */
8843 : /* -------------------------------------------------------------------- */
8844 16639 : if (poNode->FindChild("AUTHORITY") == -1)
8845 1591 : return nullptr;
8846 :
8847 15048 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8848 :
8849 : /* -------------------------------------------------------------------- */
8850 : /* Create a new authority node. */
8851 : /* -------------------------------------------------------------------- */
8852 15048 : if (poNode->GetChildCount() < 2)
8853 0 : return nullptr;
8854 :
8855 15048 : return poNode->GetChild(0)->GetValue();
8856 : }
8857 :
8858 : /************************************************************************/
8859 : /* OSRGetAuthorityName() */
8860 : /************************************************************************/
8861 :
8862 : /**
8863 : * \brief Get the authority name for a node.
8864 : *
8865 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8866 : */
8867 187 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8868 : const char *pszTargetKey)
8869 :
8870 : {
8871 187 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8872 :
8873 187 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8874 : }
8875 :
8876 : /************************************************************************/
8877 : /* GetOGCURN() */
8878 : /************************************************************************/
8879 :
8880 : /**
8881 : * \brief Get a OGC URN string describing the CRS, when possible
8882 : *
8883 : * This method assumes that the CRS has a top-level identifier, or is
8884 : * a compound CRS whose horizontal and vertical parts have a top-level
8885 : * identifier.
8886 : *
8887 : * @return a string to free with CPLFree(), or nullptr when no result can be
8888 : * generated
8889 : *
8890 : * @since GDAL 3.5
8891 : */
8892 :
8893 61 : char *OGRSpatialReference::GetOGCURN() const
8894 :
8895 : {
8896 122 : TAKE_OPTIONAL_LOCK();
8897 :
8898 61 : const char *pszAuthName = GetAuthorityName(nullptr);
8899 61 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8900 61 : if (pszAuthName && pszAuthCode)
8901 58 : return CPLStrdup(
8902 58 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8903 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8904 2 : return nullptr;
8905 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8906 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8907 1 : char *pszRet = nullptr;
8908 1 : if (horizCRS && vertCRS)
8909 : {
8910 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8911 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8912 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8913 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8914 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8915 : {
8916 1 : pszRet = CPLStrdup(CPLSPrintf(
8917 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8918 : horizAuthCode, vertAuthName, vertAuthCode));
8919 : }
8920 : }
8921 1 : proj_destroy(horizCRS);
8922 1 : proj_destroy(vertCRS);
8923 1 : return pszRet;
8924 : }
8925 :
8926 : /************************************************************************/
8927 : /* StripVertical() */
8928 : /************************************************************************/
8929 :
8930 : /**
8931 : * \brief Convert a compound cs into a horizontal CS.
8932 : *
8933 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8934 : * nodes are stripped resulting and only the horizontal coordinate system
8935 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8936 : *
8937 : * If this is not a compound coordinate system then nothing is changed.
8938 : *
8939 : * This method is the same as the C function OSRStripVertical().
8940 : *
8941 : */
8942 :
8943 46 : OGRErr OGRSpatialReference::StripVertical()
8944 :
8945 : {
8946 92 : TAKE_OPTIONAL_LOCK();
8947 :
8948 46 : d->refreshProjObj();
8949 46 : d->demoteFromBoundCRS();
8950 46 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8951 : {
8952 0 : d->undoDemoteFromBoundCRS();
8953 0 : return OGRERR_NONE;
8954 : }
8955 46 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8956 46 : if (!horizCRS)
8957 : {
8958 0 : d->undoDemoteFromBoundCRS();
8959 0 : return OGRERR_FAILURE;
8960 : }
8961 :
8962 46 : bool reuseExistingBoundCRS = false;
8963 46 : if (d->m_pj_bound_crs_target)
8964 : {
8965 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8966 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8967 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8968 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8969 : }
8970 :
8971 46 : if (reuseExistingBoundCRS)
8972 : {
8973 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8974 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8975 4 : d->m_pj_bound_crs_co);
8976 4 : proj_destroy(horizCRS);
8977 4 : d->undoDemoteFromBoundCRS();
8978 4 : d->setPjCRS(newBoundCRS);
8979 : }
8980 : else
8981 : {
8982 42 : d->undoDemoteFromBoundCRS();
8983 42 : d->setPjCRS(horizCRS);
8984 : }
8985 :
8986 46 : return OGRERR_NONE;
8987 : }
8988 :
8989 : /************************************************************************/
8990 : /* OSRStripVertical() */
8991 : /************************************************************************/
8992 : /**
8993 : * \brief Convert a compound cs into a horizontal CS.
8994 : *
8995 : * This function is the same as the C++ method
8996 : * OGRSpatialReference::StripVertical().
8997 : */
8998 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8999 :
9000 : {
9001 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9002 :
9003 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9004 : }
9005 :
9006 : /************************************************************************/
9007 : /* StripTOWGS84IfKnownDatumAndAllowed() */
9008 : /************************************************************************/
9009 :
9010 : /**
9011 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9012 : * and this is allowed by the user.
9013 : *
9014 : * The default behavior is to remove TOWGS84 information if the CRS has a
9015 : * known horizontal datum. This can be disabled by setting the
9016 : * OSR_STRIP_TOWGS84 configuration option to NO.
9017 : *
9018 : * @return true if TOWGS84 has been removed.
9019 : * @since OGR 3.1.0
9020 : */
9021 :
9022 9236 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9023 : {
9024 9236 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9025 : {
9026 9233 : if (StripTOWGS84IfKnownDatum())
9027 : {
9028 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9029 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9030 : "configuration option to NO");
9031 72 : return true;
9032 : }
9033 : }
9034 9164 : return false;
9035 : }
9036 :
9037 : /************************************************************************/
9038 : /* StripTOWGS84IfKnownDatum() */
9039 : /************************************************************************/
9040 :
9041 : /**
9042 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9043 : *
9044 : * @return true if TOWGS84 has been removed.
9045 : * @since OGR 3.1.0
9046 : */
9047 :
9048 9239 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9049 :
9050 : {
9051 18478 : TAKE_OPTIONAL_LOCK();
9052 :
9053 9239 : d->refreshProjObj();
9054 9239 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9055 : {
9056 9147 : return false;
9057 : }
9058 92 : auto ctxt = d->getPROJContext();
9059 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9060 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9061 : {
9062 3 : proj_destroy(baseCRS);
9063 3 : return false;
9064 : }
9065 :
9066 : // Known base CRS code ? Return base CRS
9067 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9068 89 : if (pszCode)
9069 : {
9070 2 : d->setPjCRS(baseCRS);
9071 2 : return true;
9072 : }
9073 :
9074 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9075 : #if PROJ_VERSION_MAJOR > 7 || \
9076 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9077 : if (datum == nullptr)
9078 : {
9079 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9080 : }
9081 : #endif
9082 87 : if (!datum)
9083 : {
9084 0 : proj_destroy(baseCRS);
9085 0 : return false;
9086 : }
9087 :
9088 : // Known datum code ? Return base CRS
9089 87 : pszCode = proj_get_id_code(datum, 0);
9090 87 : if (pszCode)
9091 : {
9092 3 : proj_destroy(datum);
9093 3 : d->setPjCRS(baseCRS);
9094 3 : return true;
9095 : }
9096 :
9097 84 : const char *name = proj_get_name(datum);
9098 84 : if (EQUAL(name, "unknown"))
9099 : {
9100 1 : proj_destroy(datum);
9101 1 : proj_destroy(baseCRS);
9102 1 : return false;
9103 : }
9104 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9105 : PJ_OBJ_LIST *list =
9106 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9107 :
9108 83 : bool knownDatumName = false;
9109 83 : if (list)
9110 : {
9111 83 : if (proj_list_get_count(list) == 1)
9112 : {
9113 70 : knownDatumName = true;
9114 : }
9115 83 : proj_list_destroy(list);
9116 : }
9117 :
9118 83 : proj_destroy(datum);
9119 83 : if (knownDatumName)
9120 : {
9121 70 : d->setPjCRS(baseCRS);
9122 70 : return true;
9123 : }
9124 13 : proj_destroy(baseCRS);
9125 13 : return false;
9126 : }
9127 :
9128 : /************************************************************************/
9129 : /* IsCompound() */
9130 : /************************************************************************/
9131 :
9132 : /**
9133 : * \brief Check if coordinate system is compound.
9134 : *
9135 : * This method is the same as the C function OSRIsCompound().
9136 : *
9137 : * @return TRUE if this is rooted with a COMPD_CS node.
9138 : */
9139 :
9140 41170 : int OGRSpatialReference::IsCompound() const
9141 :
9142 : {
9143 41170 : TAKE_OPTIONAL_LOCK();
9144 :
9145 41170 : d->refreshProjObj();
9146 41170 : d->demoteFromBoundCRS();
9147 41170 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9148 41170 : d->undoDemoteFromBoundCRS();
9149 82340 : return isCompound;
9150 : }
9151 :
9152 : /************************************************************************/
9153 : /* OSRIsCompound() */
9154 : /************************************************************************/
9155 :
9156 : /**
9157 : * \brief Check if the coordinate system is compound.
9158 : *
9159 : * This function is the same as OGRSpatialReference::IsCompound().
9160 : */
9161 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9162 :
9163 : {
9164 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9165 :
9166 5 : return ToPointer(hSRS)->IsCompound();
9167 : }
9168 :
9169 : /************************************************************************/
9170 : /* IsProjected() */
9171 : /************************************************************************/
9172 :
9173 : /**
9174 : * \brief Check if projected coordinate system.
9175 : *
9176 : * This method is the same as the C function OSRIsProjected().
9177 : *
9178 : * @return TRUE if this contains a PROJCS node indicating a it is a
9179 : * projected coordinate system. Also if it is a CompoundCRS made of a
9180 : * ProjectedCRS
9181 : */
9182 :
9183 43374 : int OGRSpatialReference::IsProjected() const
9184 :
9185 : {
9186 43374 : TAKE_OPTIONAL_LOCK();
9187 :
9188 43374 : d->refreshProjObj();
9189 43374 : d->demoteFromBoundCRS();
9190 43374 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9191 43374 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9192 : {
9193 : auto horizCRS =
9194 150 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9195 150 : if (horizCRS)
9196 : {
9197 150 : auto horizCRSType = proj_get_type(horizCRS);
9198 150 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9199 150 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9200 : {
9201 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9202 6 : if (base)
9203 : {
9204 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9205 6 : proj_destroy(base);
9206 : }
9207 : }
9208 150 : proj_destroy(horizCRS);
9209 : }
9210 : }
9211 43374 : d->undoDemoteFromBoundCRS();
9212 86748 : return isProjected;
9213 : }
9214 :
9215 : /************************************************************************/
9216 : /* OSRIsProjected() */
9217 : /************************************************************************/
9218 : /**
9219 : * \brief Check if projected coordinate system.
9220 : *
9221 : * This function is the same as OGRSpatialReference::IsProjected().
9222 : */
9223 413 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9224 :
9225 : {
9226 413 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9227 :
9228 413 : return ToPointer(hSRS)->IsProjected();
9229 : }
9230 :
9231 : /************************************************************************/
9232 : /* IsGeocentric() */
9233 : /************************************************************************/
9234 :
9235 : /**
9236 : * \brief Check if geocentric coordinate system.
9237 : *
9238 : * This method is the same as the C function OSRIsGeocentric().
9239 : *
9240 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9241 : * geocentric coordinate system.
9242 : *
9243 : */
9244 :
9245 17476 : int OGRSpatialReference::IsGeocentric() const
9246 :
9247 : {
9248 17476 : TAKE_OPTIONAL_LOCK();
9249 :
9250 17476 : d->refreshProjObj();
9251 17476 : d->demoteFromBoundCRS();
9252 17476 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9253 17476 : d->undoDemoteFromBoundCRS();
9254 34952 : return isGeocentric;
9255 : }
9256 :
9257 : /************************************************************************/
9258 : /* OSRIsGeocentric() */
9259 : /************************************************************************/
9260 : /**
9261 : * \brief Check if geocentric coordinate system.
9262 : *
9263 : * This function is the same as OGRSpatialReference::IsGeocentric().
9264 : *
9265 : */
9266 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9267 :
9268 : {
9269 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9270 :
9271 2 : return ToPointer(hSRS)->IsGeocentric();
9272 : }
9273 :
9274 : /************************************************************************/
9275 : /* IsEmpty() */
9276 : /************************************************************************/
9277 :
9278 : /**
9279 : * \brief Return if the SRS is not set.
9280 : */
9281 :
9282 114978 : bool OGRSpatialReference::IsEmpty() const
9283 : {
9284 114978 : TAKE_OPTIONAL_LOCK();
9285 :
9286 114978 : d->refreshProjObj();
9287 229956 : return d->m_pj_crs == nullptr;
9288 : }
9289 :
9290 : /************************************************************************/
9291 : /* IsGeographic() */
9292 : /************************************************************************/
9293 :
9294 : /**
9295 : * \brief Check if geographic coordinate system.
9296 : *
9297 : * This method is the same as the C function OSRIsGeographic().
9298 : *
9299 : * @return TRUE if this spatial reference is geographic ... that is the
9300 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9301 : * GeographicCRS
9302 : */
9303 :
9304 59976 : int OGRSpatialReference::IsGeographic() const
9305 :
9306 : {
9307 59976 : TAKE_OPTIONAL_LOCK();
9308 :
9309 59976 : d->refreshProjObj();
9310 59976 : d->demoteFromBoundCRS();
9311 84005 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9312 24029 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9313 59976 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9314 : {
9315 : auto horizCRS =
9316 300 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9317 300 : if (horizCRS)
9318 : {
9319 300 : auto horizCRSType = proj_get_type(horizCRS);
9320 300 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9321 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9322 300 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9323 : {
9324 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9325 13 : if (base)
9326 : {
9327 13 : horizCRSType = proj_get_type(base);
9328 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9329 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9330 13 : proj_destroy(base);
9331 : }
9332 : }
9333 300 : proj_destroy(horizCRS);
9334 : }
9335 : }
9336 59976 : d->undoDemoteFromBoundCRS();
9337 119952 : return isGeog;
9338 : }
9339 :
9340 : /************************************************************************/
9341 : /* OSRIsGeographic() */
9342 : /************************************************************************/
9343 : /**
9344 : * \brief Check if geographic coordinate system.
9345 : *
9346 : * This function is the same as OGRSpatialReference::IsGeographic().
9347 : */
9348 304 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9349 :
9350 : {
9351 304 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9352 :
9353 304 : return ToPointer(hSRS)->IsGeographic();
9354 : }
9355 :
9356 : /************************************************************************/
9357 : /* IsDerivedGeographic() */
9358 : /************************************************************************/
9359 :
9360 : /**
9361 : * \brief Check if the CRS is a derived geographic coordinate system.
9362 : * (for example a rotated long/lat grid)
9363 : *
9364 : * This method is the same as the C function OSRIsDerivedGeographic().
9365 : *
9366 : * @since GDAL 3.1.0 and PROJ 6.3.0
9367 : */
9368 :
9369 15296 : int OGRSpatialReference::IsDerivedGeographic() const
9370 :
9371 : {
9372 15296 : TAKE_OPTIONAL_LOCK();
9373 :
9374 15296 : d->refreshProjObj();
9375 15296 : d->demoteFromBoundCRS();
9376 24918 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9377 9622 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9378 : const bool isDerivedGeographic =
9379 15296 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9380 15296 : d->undoDemoteFromBoundCRS();
9381 30592 : return isDerivedGeographic ? TRUE : FALSE;
9382 : }
9383 :
9384 : /************************************************************************/
9385 : /* OSRIsDerivedGeographic() */
9386 : /************************************************************************/
9387 : /**
9388 : * \brief Check if the CRS is a derived geographic coordinate system.
9389 : * (for example a rotated long/lat grid)
9390 : *
9391 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9392 : */
9393 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9394 :
9395 : {
9396 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9397 :
9398 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9399 : }
9400 :
9401 : /************************************************************************/
9402 : /* IsDerivedProjected() */
9403 : /************************************************************************/
9404 :
9405 : /**
9406 : * \brief Check if the CRS is a derived projected coordinate system.
9407 : *
9408 : * This method is the same as the C function OSRIsDerivedGeographic().
9409 : *
9410 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9411 : */
9412 :
9413 0 : int OGRSpatialReference::IsDerivedProjected() const
9414 :
9415 : {
9416 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9417 : TAKE_OPTIONAL_LOCK();
9418 : d->refreshProjObj();
9419 : d->demoteFromBoundCRS();
9420 : const bool isDerivedProjected =
9421 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9422 : d->undoDemoteFromBoundCRS();
9423 : return isDerivedProjected ? TRUE : FALSE;
9424 : #else
9425 0 : return FALSE;
9426 : #endif
9427 : }
9428 :
9429 : /************************************************************************/
9430 : /* OSRIsDerivedProjected() */
9431 : /************************************************************************/
9432 : /**
9433 : * \brief Check if the CRS is a derived projected coordinate system.
9434 : *
9435 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9436 : *
9437 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9438 : */
9439 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9440 :
9441 : {
9442 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9443 :
9444 0 : return ToPointer(hSRS)->IsDerivedProjected();
9445 : }
9446 :
9447 : /************************************************************************/
9448 : /* IsLocal() */
9449 : /************************************************************************/
9450 :
9451 : /**
9452 : * \brief Check if local coordinate system.
9453 : *
9454 : * This method is the same as the C function OSRIsLocal().
9455 : *
9456 : * @return TRUE if this spatial reference is local ... that is the
9457 : * root is a LOCAL_CS node.
9458 : */
9459 :
9460 7952 : int OGRSpatialReference::IsLocal() const
9461 :
9462 : {
9463 7952 : TAKE_OPTIONAL_LOCK();
9464 7952 : d->refreshProjObj();
9465 15904 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9466 : }
9467 :
9468 : /************************************************************************/
9469 : /* OSRIsLocal() */
9470 : /************************************************************************/
9471 : /**
9472 : * \brief Check if local coordinate system.
9473 : *
9474 : * This function is the same as OGRSpatialReference::IsLocal().
9475 : */
9476 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9477 :
9478 : {
9479 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9480 :
9481 8 : return ToPointer(hSRS)->IsLocal();
9482 : }
9483 :
9484 : /************************************************************************/
9485 : /* IsVertical() */
9486 : /************************************************************************/
9487 :
9488 : /**
9489 : * \brief Check if vertical coordinate system.
9490 : *
9491 : * This method is the same as the C function OSRIsVertical().
9492 : *
9493 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9494 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9495 : * VerticalCRS
9496 : *
9497 : */
9498 :
9499 8935 : int OGRSpatialReference::IsVertical() const
9500 :
9501 : {
9502 8935 : TAKE_OPTIONAL_LOCK();
9503 8935 : d->refreshProjObj();
9504 8935 : d->demoteFromBoundCRS();
9505 8935 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9506 8935 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9507 : {
9508 : auto vertCRS =
9509 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9510 33 : if (vertCRS)
9511 : {
9512 33 : const auto vertCRSType = proj_get_type(vertCRS);
9513 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9514 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9515 : {
9516 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9517 0 : if (base)
9518 : {
9519 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9520 0 : proj_destroy(base);
9521 : }
9522 : }
9523 33 : proj_destroy(vertCRS);
9524 : }
9525 : }
9526 8935 : d->undoDemoteFromBoundCRS();
9527 17870 : return isVertical;
9528 : }
9529 :
9530 : /************************************************************************/
9531 : /* OSRIsVertical() */
9532 : /************************************************************************/
9533 : /**
9534 : * \brief Check if vertical coordinate system.
9535 : *
9536 : * This function is the same as OGRSpatialReference::IsVertical().
9537 : *
9538 : */
9539 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9540 :
9541 : {
9542 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9543 :
9544 0 : return ToPointer(hSRS)->IsVertical();
9545 : }
9546 :
9547 : /************************************************************************/
9548 : /* IsDynamic() */
9549 : /************************************************************************/
9550 :
9551 : /**
9552 : * \brief Check if a CRS is a dynamic CRS.
9553 : *
9554 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9555 : * plate-fixed.
9556 : *
9557 : * This method is the same as the C function OSRIsDynamic().
9558 : *
9559 : * @return true if the CRS is dynamic
9560 : *
9561 : * @since OGR 3.4.0
9562 : *
9563 : * @see HasPointMotionOperation()
9564 : */
9565 :
9566 15285 : bool OGRSpatialReference::IsDynamic() const
9567 :
9568 : {
9569 15285 : TAKE_OPTIONAL_LOCK();
9570 15285 : bool isDynamic = false;
9571 15285 : d->refreshProjObj();
9572 15285 : d->demoteFromBoundCRS();
9573 15285 : auto ctxt = d->getPROJContext();
9574 15285 : PJ *horiz = nullptr;
9575 15285 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9576 : {
9577 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9578 : }
9579 15189 : else if (d->m_pj_crs)
9580 : {
9581 15076 : horiz = proj_clone(ctxt, d->m_pj_crs);
9582 : }
9583 15285 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9584 : {
9585 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9586 6 : if (baseCRS)
9587 : {
9588 6 : proj_destroy(horiz);
9589 6 : horiz = baseCRS;
9590 : }
9591 : }
9592 15285 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9593 15285 : if (datum)
9594 : {
9595 15137 : const auto type = proj_get_type(datum);
9596 15137 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9597 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9598 15137 : if (!isDynamic)
9599 : {
9600 15137 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9601 15137 : const char *code = proj_get_id_code(datum, 0);
9602 15137 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9603 14688 : EQUAL(code, "6326"))
9604 : {
9605 9568 : isDynamic = true;
9606 : }
9607 : }
9608 15137 : proj_destroy(datum);
9609 : }
9610 : #if PROJ_VERSION_MAJOR > 7 || \
9611 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9612 : else
9613 : {
9614 : auto ensemble =
9615 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9616 : if (ensemble)
9617 : {
9618 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9619 : if (member)
9620 : {
9621 : const auto type = proj_get_type(member);
9622 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9623 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9624 : proj_destroy(member);
9625 : }
9626 : proj_destroy(ensemble);
9627 : }
9628 : }
9629 : #endif
9630 15285 : proj_destroy(horiz);
9631 15285 : d->undoDemoteFromBoundCRS();
9632 30570 : return isDynamic;
9633 : }
9634 :
9635 : /************************************************************************/
9636 : /* OSRIsDynamic() */
9637 : /************************************************************************/
9638 : /**
9639 : * \brief Check if a CRS is a dynamic CRS.
9640 : *
9641 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9642 : * plate-fixed.
9643 : *
9644 : * This function is the same as OGRSpatialReference::IsDynamic().
9645 : *
9646 : * @since OGR 3.4.0
9647 : */
9648 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9649 :
9650 : {
9651 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9652 :
9653 0 : return ToPointer(hSRS)->IsDynamic();
9654 : }
9655 :
9656 : /************************************************************************/
9657 : /* HasPointMotionOperation() */
9658 : /************************************************************************/
9659 :
9660 : /**
9661 : * \brief Check if a CRS has at least an associated point motion operation.
9662 : *
9663 : * Some CRS are not formally declared as dynamic, but may behave as such
9664 : * in practice due to the presence of point motion operation, to perform
9665 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9666 : *
9667 : * @return true if the CRS has at least an associated point motion operation.
9668 : *
9669 : * @since OGR 3.8.0 and PROJ 9.4.0
9670 : *
9671 : * @see IsDynamic()
9672 : */
9673 :
9674 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9675 :
9676 : {
9677 : #if PROJ_VERSION_MAJOR > 9 || \
9678 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9679 : TAKE_OPTIONAL_LOCK();
9680 : d->refreshProjObj();
9681 : d->demoteFromBoundCRS();
9682 : auto ctxt = d->getPROJContext();
9683 : auto res =
9684 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9685 : d->undoDemoteFromBoundCRS();
9686 : return res;
9687 : #else
9688 5 : return false;
9689 : #endif
9690 : }
9691 :
9692 : /************************************************************************/
9693 : /* OSRHasPointMotionOperation() */
9694 : /************************************************************************/
9695 :
9696 : /**
9697 : * \brief Check if a CRS has at least an associated point motion operation.
9698 : *
9699 : * Some CRS are not formally declared as dynamic, but may behave as such
9700 : * in practice due to the presence of point motion operation, to perform
9701 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9702 : *
9703 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9704 : *
9705 : * @since OGR 3.8.0 and PROJ 9.4.0
9706 : */
9707 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9708 :
9709 : {
9710 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9711 :
9712 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9713 : }
9714 :
9715 : /************************************************************************/
9716 : /* CloneGeogCS() */
9717 : /************************************************************************/
9718 :
9719 : /**
9720 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9721 : * object.
9722 : *
9723 : * @return a new SRS, which becomes the responsibility of the caller.
9724 : */
9725 4176 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9726 :
9727 : {
9728 8352 : TAKE_OPTIONAL_LOCK();
9729 4176 : d->refreshProjObj();
9730 4176 : if (d->m_pj_crs)
9731 : {
9732 4176 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9733 0 : return nullptr;
9734 :
9735 : auto geodCRS =
9736 4176 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9737 4176 : if (geodCRS)
9738 : {
9739 4176 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9740 4176 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9741 : {
9742 : PJ *hub_crs =
9743 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9744 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9745 13 : d->m_pj_crs);
9746 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9747 : geodCRS, hub_crs, co);
9748 13 : proj_destroy(geodCRS);
9749 13 : geodCRS = temp;
9750 13 : proj_destroy(hub_crs);
9751 13 : proj_destroy(co);
9752 : }
9753 :
9754 : /* --------------------------------------------------------------------
9755 : */
9756 : /* We have to reconstruct the GEOGCS node for geocentric */
9757 : /* coordinate systems. */
9758 : /* --------------------------------------------------------------------
9759 : */
9760 4176 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9761 : {
9762 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9763 : #if PROJ_VERSION_MAJOR > 7 || \
9764 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9765 : if (datum == nullptr)
9766 : {
9767 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9768 : geodCRS);
9769 : }
9770 : #endif
9771 0 : if (datum)
9772 : {
9773 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9774 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9775 : nullptr, 0);
9776 0 : auto temp = proj_create_geographic_crs_from_datum(
9777 : d->getPROJContext(), "unnamed", datum, cs);
9778 0 : proj_destroy(datum);
9779 0 : proj_destroy(cs);
9780 0 : proj_destroy(geodCRS);
9781 0 : geodCRS = temp;
9782 : }
9783 : }
9784 :
9785 4176 : poNewSRS->d->setPjCRS(geodCRS);
9786 4176 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9787 2733 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9788 4176 : return poNewSRS;
9789 : }
9790 : }
9791 0 : return nullptr;
9792 : }
9793 :
9794 : /************************************************************************/
9795 : /* OSRCloneGeogCS() */
9796 : /************************************************************************/
9797 : /**
9798 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9799 : * object.
9800 : *
9801 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9802 : */
9803 126 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9804 :
9805 : {
9806 126 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9807 :
9808 126 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9809 : }
9810 :
9811 : /************************************************************************/
9812 : /* IsSameGeogCS() */
9813 : /************************************************************************/
9814 :
9815 : /**
9816 : * \brief Do the GeogCS'es match?
9817 : *
9818 : * This method is the same as the C function OSRIsSameGeogCS().
9819 : *
9820 : * @param poOther the SRS being compared against.
9821 : *
9822 : * @return TRUE if they are the same or FALSE otherwise.
9823 : */
9824 :
9825 8528 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9826 :
9827 : {
9828 8528 : return IsSameGeogCS(poOther, nullptr);
9829 : }
9830 :
9831 : /**
9832 : * \brief Do the GeogCS'es match?
9833 : *
9834 : * This method is the same as the C function OSRIsSameGeogCS().
9835 : *
9836 : * @param poOther the SRS being compared against.
9837 : * @param papszOptions options. ignored
9838 : *
9839 : * @return TRUE if they are the same or FALSE otherwise.
9840 : */
9841 :
9842 8528 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9843 : const char *const *papszOptions) const
9844 :
9845 : {
9846 17056 : TAKE_OPTIONAL_LOCK();
9847 :
9848 8528 : CPL_IGNORE_RET_VAL(papszOptions);
9849 :
9850 8528 : d->refreshProjObj();
9851 8528 : poOther->d->refreshProjObj();
9852 8528 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9853 0 : return FALSE;
9854 8528 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9855 8528 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9856 25584 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9857 8528 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9858 : {
9859 0 : return FALSE;
9860 : }
9861 :
9862 8528 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9863 : auto otherGeodCRS =
9864 8528 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9865 8528 : if (!geodCRS || !otherGeodCRS)
9866 : {
9867 0 : proj_destroy(geodCRS);
9868 0 : proj_destroy(otherGeodCRS);
9869 0 : return FALSE;
9870 : }
9871 :
9872 8528 : int ret = proj_is_equivalent_to(
9873 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9874 :
9875 8528 : proj_destroy(geodCRS);
9876 8528 : proj_destroy(otherGeodCRS);
9877 8528 : return ret;
9878 : }
9879 :
9880 : /************************************************************************/
9881 : /* OSRIsSameGeogCS() */
9882 : /************************************************************************/
9883 :
9884 : /**
9885 : * \brief Do the GeogCS'es match?
9886 : *
9887 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9888 : */
9889 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9890 :
9891 : {
9892 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9893 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9894 :
9895 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9896 : }
9897 :
9898 : /************************************************************************/
9899 : /* IsSameVertCS() */
9900 : /************************************************************************/
9901 :
9902 : /**
9903 : * \brief Do the VertCS'es match?
9904 : *
9905 : * This method is the same as the C function OSRIsSameVertCS().
9906 : *
9907 : * @param poOther the SRS being compared against.
9908 : *
9909 : * @return TRUE if they are the same or FALSE otherwise.
9910 : */
9911 :
9912 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9913 :
9914 : {
9915 0 : TAKE_OPTIONAL_LOCK();
9916 :
9917 : /* -------------------------------------------------------------------- */
9918 : /* Does the datum name match? */
9919 : /* -------------------------------------------------------------------- */
9920 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9921 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9922 :
9923 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9924 0 : !EQUAL(pszThisValue, pszOtherValue))
9925 0 : return FALSE;
9926 :
9927 : /* -------------------------------------------------------------------- */
9928 : /* Do the units match? */
9929 : /* -------------------------------------------------------------------- */
9930 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9931 0 : if (pszThisValue == nullptr)
9932 0 : pszThisValue = "1.0";
9933 :
9934 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9935 0 : if (pszOtherValue == nullptr)
9936 0 : pszOtherValue = "1.0";
9937 :
9938 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9939 0 : return FALSE;
9940 :
9941 0 : return TRUE;
9942 : }
9943 :
9944 : /************************************************************************/
9945 : /* OSRIsSameVertCS() */
9946 : /************************************************************************/
9947 :
9948 : /**
9949 : * \brief Do the VertCS'es match?
9950 : *
9951 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9952 : */
9953 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9954 :
9955 : {
9956 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9957 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9958 :
9959 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9960 : }
9961 :
9962 : /************************************************************************/
9963 : /* IsSame() */
9964 : /************************************************************************/
9965 :
9966 : /**
9967 : * \brief Do these two spatial references describe the same system ?
9968 : *
9969 : * @param poOtherSRS the SRS being compared to.
9970 : *
9971 : * @return TRUE if equivalent or FALSE otherwise.
9972 : */
9973 :
9974 22974 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9975 :
9976 : {
9977 22974 : return IsSame(poOtherSRS, nullptr);
9978 : }
9979 :
9980 : /**
9981 : * \brief Do these two spatial references describe the same system ?
9982 : *
9983 : * This also takes into account the data axis to CRS axis mapping by default
9984 : *
9985 : * @param poOtherSRS the SRS being compared to.
9986 : * @param papszOptions options. NULL or NULL terminated list of options.
9987 : * Currently supported options are:
9988 : * <ul>
9989 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9990 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9991 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9992 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9993 : * </ul>
9994 : *
9995 : * @return TRUE if equivalent or FALSE otherwise.
9996 : */
9997 :
9998 34507 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9999 : const char *const *papszOptions) const
10000 :
10001 : {
10002 69014 : TAKE_OPTIONAL_LOCK();
10003 :
10004 34507 : d->refreshProjObj();
10005 34507 : poOtherSRS->d->refreshProjObj();
10006 34507 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10007 51 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10008 34456 : if (!CPLTestBool(CSLFetchNameValueDef(
10009 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10010 : {
10011 33825 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10012 3056 : return false;
10013 : }
10014 :
10015 31400 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10016 : "IGNORE_COORDINATE_EPOCH", "NO")))
10017 : {
10018 31026 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10019 27 : return false;
10020 : }
10021 :
10022 31373 : bool reboundSelf = false;
10023 31373 : bool reboundOther = false;
10024 31424 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10025 51 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10026 : {
10027 14 : d->demoteFromBoundCRS();
10028 14 : reboundSelf = true;
10029 : }
10030 62681 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10031 31322 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10032 : {
10033 28 : poOtherSRS->d->demoteFromBoundCRS();
10034 28 : reboundOther = true;
10035 : }
10036 :
10037 31373 : PJ_COMPARISON_CRITERION criterion =
10038 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10039 31373 : const char *pszCriterion = CSLFetchNameValueDef(
10040 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10041 31373 : if (EQUAL(pszCriterion, "STRICT"))
10042 0 : criterion = PJ_COMP_STRICT;
10043 31373 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10044 8527 : criterion = PJ_COMP_EQUIVALENT;
10045 22846 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10046 : {
10047 0 : CPLError(CE_Warning, CPLE_NotSupported,
10048 : "Unsupported value for CRITERION: %s", pszCriterion);
10049 : }
10050 : int ret =
10051 31373 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10052 31373 : if (reboundSelf)
10053 14 : d->undoDemoteFromBoundCRS();
10054 31373 : if (reboundOther)
10055 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10056 :
10057 31373 : return ret;
10058 : }
10059 :
10060 : /************************************************************************/
10061 : /* OSRIsSame() */
10062 : /************************************************************************/
10063 :
10064 : /**
10065 : * \brief Do these two spatial references describe the same system ?
10066 : *
10067 : * This function is the same as OGRSpatialReference::IsSame().
10068 : */
10069 28 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10070 :
10071 : {
10072 28 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10073 28 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10074 :
10075 28 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10076 : }
10077 :
10078 : /************************************************************************/
10079 : /* OSRIsSameEx() */
10080 : /************************************************************************/
10081 :
10082 : /**
10083 : * \brief Do these two spatial references describe the same system ?
10084 : *
10085 : * This function is the same as OGRSpatialReference::IsSame().
10086 : */
10087 642 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10088 : const char *const *papszOptions)
10089 : {
10090 642 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10091 642 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10092 :
10093 642 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10094 : }
10095 :
10096 : /************************************************************************/
10097 : /* convertToOtherProjection() */
10098 : /************************************************************************/
10099 :
10100 : /**
10101 : * \brief Convert to another equivalent projection
10102 : *
10103 : * Currently implemented:
10104 : * <ul>
10105 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10106 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10107 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10108 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10109 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10110 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10111 : * </ul>
10112 : *
10113 : * @param pszTargetProjection target projection.
10114 : * @param papszOptions lists of options. None supported currently.
10115 : * @return a new SRS, or NULL in case of error.
10116 : *
10117 : */
10118 91 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10119 : const char *pszTargetProjection,
10120 : CPL_UNUSED const char *const *papszOptions) const
10121 : {
10122 182 : TAKE_OPTIONAL_LOCK();
10123 :
10124 91 : if (pszTargetProjection == nullptr)
10125 1 : return nullptr;
10126 : int new_code;
10127 90 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10128 : {
10129 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10130 : }
10131 84 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10132 : {
10133 7 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10134 : }
10135 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10136 : {
10137 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10138 : }
10139 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10140 : {
10141 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10142 : }
10143 : else
10144 : {
10145 1 : return nullptr;
10146 : }
10147 :
10148 89 : d->refreshProjObj();
10149 89 : d->demoteFromBoundCRS();
10150 89 : OGRSpatialReference *poNewSRS = nullptr;
10151 89 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10152 : {
10153 : auto conv =
10154 88 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10155 88 : auto new_conv = proj_convert_conversion_to_other_method(
10156 : d->getPROJContext(), conv, new_code, nullptr);
10157 88 : proj_destroy(conv);
10158 88 : if (new_conv)
10159 : {
10160 : auto geodCRS =
10161 74 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10162 74 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10163 74 : d->m_pj_crs);
10164 74 : if (geodCRS && cs)
10165 : {
10166 74 : auto new_proj_crs = proj_create_projected_crs(
10167 74 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10168 : new_conv, cs);
10169 74 : proj_destroy(new_conv);
10170 74 : if (new_proj_crs)
10171 : {
10172 74 : poNewSRS = new OGRSpatialReference();
10173 :
10174 74 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10175 : {
10176 9 : auto boundCRS = proj_crs_create_bound_crs(
10177 : d->getPROJContext(), new_proj_crs,
10178 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10179 9 : if (boundCRS)
10180 : {
10181 9 : proj_destroy(new_proj_crs);
10182 9 : new_proj_crs = boundCRS;
10183 : }
10184 : }
10185 :
10186 74 : poNewSRS->d->setPjCRS(new_proj_crs);
10187 : }
10188 : }
10189 74 : proj_destroy(geodCRS);
10190 74 : proj_destroy(cs);
10191 : }
10192 : }
10193 89 : d->undoDemoteFromBoundCRS();
10194 89 : return poNewSRS;
10195 : }
10196 :
10197 : /************************************************************************/
10198 : /* OSRConvertToOtherProjection() */
10199 : /************************************************************************/
10200 :
10201 : /**
10202 : * \brief Convert to another equivalent projection
10203 : *
10204 : * Currently implemented:
10205 : * <ul>
10206 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10207 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10208 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10209 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10210 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10211 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10212 : * </ul>
10213 : *
10214 : * @param hSRS source SRS
10215 : * @param pszTargetProjection target projection.
10216 : * @param papszOptions lists of options. None supported currently.
10217 : * @return a new SRS, or NULL in case of error.
10218 : *
10219 : */
10220 : OGRSpatialReferenceH
10221 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10222 : const char *pszTargetProjection,
10223 : const char *const *papszOptions)
10224 : {
10225 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10226 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10227 28 : pszTargetProjection, papszOptions));
10228 : }
10229 :
10230 : /************************************************************************/
10231 : /* OSRFindMatches() */
10232 : /************************************************************************/
10233 :
10234 : /**
10235 : * \brief Try to identify a match between the passed SRS and a related SRS
10236 : * in a catalog.
10237 : *
10238 : * Matching may be partial, or may fail.
10239 : * Returned entries will be sorted by decreasing match confidence (first
10240 : * entry has the highest match confidence).
10241 : *
10242 : * The exact way matching is done may change in future versions. Starting with
10243 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10244 : *
10245 : * This function is the same as OGRSpatialReference::FindMatches().
10246 : *
10247 : * @param hSRS SRS to match
10248 : * @param papszOptions NULL terminated list of options or NULL
10249 : * @param pnEntries Output parameter. Number of values in the returned array.
10250 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10251 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10252 : * indicate the confidence in the match. 100 is the highest confidence level.
10253 : * The array must be freed with CPLFree().
10254 : *
10255 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10256 : * with OSRFreeSRSArray()
10257 : *
10258 : */
10259 10 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10260 : char **papszOptions, int *pnEntries,
10261 : int **ppanMatchConfidence)
10262 : {
10263 10 : if (pnEntries)
10264 10 : *pnEntries = 0;
10265 10 : if (ppanMatchConfidence)
10266 10 : *ppanMatchConfidence = nullptr;
10267 10 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10268 :
10269 10 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10270 10 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10271 : }
10272 :
10273 : /************************************************************************/
10274 : /* OSRFreeSRSArray() */
10275 : /************************************************************************/
10276 :
10277 : /**
10278 : * \brief Free return of OSRIdentifyMatches()
10279 : *
10280 : * @param pahSRS array of SRS (must be NULL terminated)
10281 : */
10282 197 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10283 : {
10284 197 : if (pahSRS != nullptr)
10285 : {
10286 1744 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10287 : {
10288 1565 : OSRRelease(pahSRS[i]);
10289 : }
10290 179 : CPLFree(pahSRS);
10291 : }
10292 197 : }
10293 :
10294 : /************************************************************************/
10295 : /* FindBestMatch() */
10296 : /************************************************************************/
10297 :
10298 : /**
10299 : * \brief Try to identify the best match between the passed SRS and a related
10300 : * SRS in a catalog.
10301 : *
10302 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10303 : * of filtering its output.
10304 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10305 : * will be considered. If there is a single match, it is returned.
10306 : * If there are several matches, only return the one under the
10307 : * pszPreferredAuthority, if there is a single one under that authority.
10308 : *
10309 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10310 : * 100). If set to 0, 90 is used.
10311 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10312 : * "EPSG" is used.
10313 : * @param papszOptions NULL terminated list of options or NULL. No option is
10314 : * defined at time of writing.
10315 : *
10316 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10317 : *
10318 : * @since GDAL 3.6
10319 : * @see OGRSpatialReference::FindMatches()
10320 : */
10321 : OGRSpatialReference *
10322 1438 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10323 : const char *pszPreferredAuthority,
10324 : CSLConstList papszOptions) const
10325 : {
10326 2876 : TAKE_OPTIONAL_LOCK();
10327 :
10328 1438 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10329 :
10330 1438 : if (nMinimumMatchConfidence == 0)
10331 0 : nMinimumMatchConfidence = 90;
10332 1438 : if (pszPreferredAuthority == nullptr)
10333 200 : pszPreferredAuthority = "EPSG";
10334 :
10335 : // Try to identify the CRS with the database
10336 1438 : int nEntries = 0;
10337 1438 : int *panConfidence = nullptr;
10338 : OGRSpatialReferenceH *pahSRS =
10339 1438 : FindMatches(nullptr, &nEntries, &panConfidence);
10340 1438 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10341 : {
10342 2548 : std::vector<double> adfTOWGS84(7);
10343 1274 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10344 : {
10345 1273 : adfTOWGS84.clear();
10346 : }
10347 :
10348 1274 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10349 :
10350 : auto poBaseGeogCRS =
10351 1274 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10352 1274 : if (poBaseGeogCRS)
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 1274 : const char *pszAuthorityName = nullptr;
10362 1274 : const char *pszAuthorityCode = nullptr;
10363 1274 : const char *pszBaseAuthorityName = nullptr;
10364 1274 : const char *pszBaseAuthorityCode = nullptr;
10365 1274 : const char *pszBaseName = poBaseGeogCRS->GetName();
10366 2548 : if (adfTOWGS84 == std::vector<double>(7) &&
10367 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) !=
10368 1 : nullptr &&
10369 1 : EQUAL(pszAuthorityName, "EPSG") &&
10370 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) !=
10371 1 : nullptr &&
10372 : (pszBaseAuthorityName =
10373 1 : poBaseGeogCRS->GetAuthorityName(nullptr)) != nullptr &&
10374 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10375 : (pszBaseAuthorityCode =
10376 2550 : poBaseGeogCRS->GetAuthorityCode(nullptr)) != nullptr &&
10377 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10378 1 : EQUAL(pszBaseAuthorityCode, "4258") ||
10379 : // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10380 0 : (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10381 : {
10382 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10383 : }
10384 : }
10385 :
10386 1274 : CPLFree(pahSRS);
10387 1274 : CPLFree(panConfidence);
10388 :
10389 1274 : return poSRS;
10390 : }
10391 : else
10392 : {
10393 : // If there are several matches >= nMinimumMatchConfidence, take the
10394 : // only one that is under pszPreferredAuthority
10395 164 : int iBestEntry = -1;
10396 1679 : for (int i = 0; i < nEntries; i++)
10397 : {
10398 1515 : if (panConfidence[i] >= nMinimumMatchConfidence)
10399 : {
10400 : const char *pszAuthName =
10401 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10402 3 : ->GetAuthorityName(nullptr);
10403 3 : if (pszAuthName != nullptr &&
10404 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10405 : {
10406 3 : if (iBestEntry < 0)
10407 3 : iBestEntry = i;
10408 : else
10409 : {
10410 0 : iBestEntry = -1;
10411 0 : break;
10412 : }
10413 : }
10414 : }
10415 : }
10416 164 : if (iBestEntry >= 0)
10417 : {
10418 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10419 3 : OSRFreeSRSArray(pahSRS);
10420 3 : CPLFree(panConfidence);
10421 3 : return poRet;
10422 : }
10423 : }
10424 161 : OSRFreeSRSArray(pahSRS);
10425 161 : CPLFree(panConfidence);
10426 161 : return nullptr;
10427 : }
10428 :
10429 : /************************************************************************/
10430 : /* SetTOWGS84() */
10431 : /************************************************************************/
10432 :
10433 : /**
10434 : * \brief Set the Bursa-Wolf conversion to WGS84.
10435 : *
10436 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10437 : * if there is no existing DATUM node. It will replace
10438 : * an existing TOWGS84 node if there is one.
10439 : *
10440 : * The parameters have the same meaning as EPSG transformation 9606
10441 : * (Position Vector 7-param. transformation).
10442 : *
10443 : * This method is the same as the C function OSRSetTOWGS84().
10444 : *
10445 : * @param dfDX X child in meters.
10446 : * @param dfDY Y child in meters.
10447 : * @param dfDZ Z child in meters.
10448 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10449 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10450 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10451 : * @param dfPPM scaling factor (parts per million).
10452 : *
10453 : * @return OGRERR_NONE on success.
10454 : */
10455 :
10456 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10457 : double dfEX, double dfEY, double dfEZ,
10458 : double dfPPM)
10459 :
10460 : {
10461 206 : TAKE_OPTIONAL_LOCK();
10462 :
10463 103 : d->refreshProjObj();
10464 103 : if (d->m_pj_crs == nullptr)
10465 : {
10466 0 : return OGRERR_FAILURE;
10467 : }
10468 :
10469 : // Remove existing BoundCRS
10470 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10471 : {
10472 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10473 0 : if (!baseCRS)
10474 0 : return OGRERR_FAILURE;
10475 0 : d->setPjCRS(baseCRS);
10476 : }
10477 :
10478 : PJ_PARAM_DESCRIPTION params[7];
10479 :
10480 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10481 103 : params[0].auth_name = "EPSG";
10482 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10483 103 : params[0].value = dfDX;
10484 103 : params[0].unit_name = "metre";
10485 103 : params[0].unit_conv_factor = 1.0;
10486 103 : params[0].unit_type = PJ_UT_LINEAR;
10487 :
10488 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10489 103 : params[1].auth_name = "EPSG";
10490 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10491 103 : params[1].value = dfDY;
10492 103 : params[1].unit_name = "metre";
10493 103 : params[1].unit_conv_factor = 1.0;
10494 103 : params[1].unit_type = PJ_UT_LINEAR;
10495 :
10496 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10497 103 : params[2].auth_name = "EPSG";
10498 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10499 103 : params[2].value = dfDZ;
10500 103 : params[2].unit_name = "metre";
10501 103 : params[2].unit_conv_factor = 1.0;
10502 103 : params[2].unit_type = PJ_UT_LINEAR;
10503 :
10504 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10505 103 : params[3].auth_name = "EPSG";
10506 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10507 103 : params[3].value = dfEX;
10508 103 : params[3].unit_name = "arc-second";
10509 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10510 103 : params[3].unit_type = PJ_UT_ANGULAR;
10511 :
10512 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10513 103 : params[4].auth_name = "EPSG";
10514 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10515 103 : params[4].value = dfEY;
10516 103 : params[4].unit_name = "arc-second";
10517 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10518 103 : params[4].unit_type = PJ_UT_ANGULAR;
10519 :
10520 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10521 103 : params[5].auth_name = "EPSG";
10522 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10523 103 : params[5].value = dfEZ;
10524 103 : params[5].unit_name = "arc-second";
10525 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10526 103 : params[5].unit_type = PJ_UT_ANGULAR;
10527 :
10528 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10529 103 : params[6].auth_name = "EPSG";
10530 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10531 103 : params[6].value = dfPPM;
10532 103 : params[6].unit_name = "parts per million";
10533 103 : params[6].unit_conv_factor = 1e-6;
10534 103 : params[6].unit_type = PJ_UT_SCALE;
10535 :
10536 : auto sourceCRS =
10537 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10538 103 : if (!sourceCRS)
10539 : {
10540 0 : return OGRERR_FAILURE;
10541 : }
10542 :
10543 103 : const auto sourceType = proj_get_type(sourceCRS);
10544 :
10545 103 : auto targetCRS = proj_create_from_database(
10546 : d->getPROJContext(), "EPSG",
10547 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10548 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10549 : : "4978",
10550 : PJ_CATEGORY_CRS, false, nullptr);
10551 103 : if (!targetCRS)
10552 : {
10553 0 : proj_destroy(sourceCRS);
10554 0 : return OGRERR_FAILURE;
10555 : }
10556 :
10557 206 : CPLString osMethodCode;
10558 : osMethodCode.Printf("%d",
10559 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10560 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10561 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10562 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10563 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10564 :
10565 103 : auto transf = proj_create_transformation(
10566 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10567 : sourceCRS, targetCRS, nullptr,
10568 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10569 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10570 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10571 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10572 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10573 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10574 103 : proj_destroy(sourceCRS);
10575 103 : if (!transf)
10576 : {
10577 0 : proj_destroy(targetCRS);
10578 0 : return OGRERR_FAILURE;
10579 : }
10580 :
10581 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10582 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10583 103 : proj_destroy(transf);
10584 103 : proj_destroy(targetCRS);
10585 103 : if (!newBoundCRS)
10586 : {
10587 0 : return OGRERR_FAILURE;
10588 : }
10589 :
10590 103 : d->setPjCRS(newBoundCRS);
10591 103 : return OGRERR_NONE;
10592 : }
10593 :
10594 : /************************************************************************/
10595 : /* OSRSetTOWGS84() */
10596 : /************************************************************************/
10597 :
10598 : /**
10599 : * \brief Set the Bursa-Wolf conversion to WGS84.
10600 : *
10601 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10602 : */
10603 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10604 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10605 : double dfPPM)
10606 :
10607 : {
10608 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10609 :
10610 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10611 4 : dfPPM);
10612 : }
10613 :
10614 : /************************************************************************/
10615 : /* GetTOWGS84() */
10616 : /************************************************************************/
10617 :
10618 : /**
10619 : * \brief Fetch TOWGS84 parameters, if available.
10620 : *
10621 : * The parameters have the same meaning as EPSG transformation 9606
10622 : * (Position Vector 7-param. transformation).
10623 : *
10624 : * @param padfCoeff array into which up to 7 coefficients are placed.
10625 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10626 : *
10627 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10628 : * TOWGS84 node available.
10629 : */
10630 :
10631 4802 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10632 :
10633 : {
10634 9604 : TAKE_OPTIONAL_LOCK();
10635 :
10636 4802 : d->refreshProjObj();
10637 4802 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10638 4754 : return OGRERR_FAILURE;
10639 :
10640 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10641 :
10642 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10643 48 : int success = proj_coordoperation_get_towgs84_values(
10644 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10645 48 : proj_destroy(transf);
10646 :
10647 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10648 : }
10649 :
10650 : /************************************************************************/
10651 : /* OSRGetTOWGS84() */
10652 : /************************************************************************/
10653 :
10654 : /**
10655 : * \brief Fetch TOWGS84 parameters, if available.
10656 : *
10657 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10658 : */
10659 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10660 : int nCoeffCount)
10661 :
10662 : {
10663 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10664 :
10665 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10666 : }
10667 :
10668 : /************************************************************************/
10669 : /* IsAngularParameter() */
10670 : /************************************************************************/
10671 :
10672 : /** Is the passed projection parameter an angular one?
10673 : *
10674 : * @return TRUE or FALSE
10675 : */
10676 :
10677 : /* static */
10678 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10679 :
10680 : {
10681 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10682 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10683 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10684 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10685 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10686 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10687 8 : return TRUE;
10688 :
10689 2 : return FALSE;
10690 : }
10691 :
10692 : /************************************************************************/
10693 : /* IsLongitudeParameter() */
10694 : /************************************************************************/
10695 :
10696 : /** Is the passed projection parameter an angular longitude
10697 : * (relative to a prime meridian)?
10698 : *
10699 : * @return TRUE or FALSE
10700 : */
10701 :
10702 : /* static */
10703 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10704 :
10705 : {
10706 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10707 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10708 0 : return TRUE;
10709 :
10710 0 : return FALSE;
10711 : }
10712 :
10713 : /************************************************************************/
10714 : /* IsLinearParameter() */
10715 : /************************************************************************/
10716 :
10717 : /** Is the passed projection parameter an linear one measured in meters or
10718 : * some similar linear measure.
10719 : *
10720 : * @return TRUE or FALSE
10721 : */
10722 :
10723 : /* static */
10724 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10725 :
10726 : {
10727 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10728 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10729 9 : return TRUE;
10730 :
10731 34 : return FALSE;
10732 : }
10733 :
10734 : /************************************************************************/
10735 : /* GetNormInfo() */
10736 : /************************************************************************/
10737 :
10738 : /**
10739 : * \brief Set the internal information for normalizing linear, and angular
10740 : * values.
10741 : */
10742 3987 : void OGRSpatialReference::GetNormInfo() const
10743 :
10744 : {
10745 3987 : TAKE_OPTIONAL_LOCK();
10746 :
10747 3987 : if (d->bNormInfoSet)
10748 2827 : return;
10749 :
10750 : /* -------------------------------------------------------------------- */
10751 : /* Initialize values. */
10752 : /* -------------------------------------------------------------------- */
10753 1160 : d->bNormInfoSet = TRUE;
10754 :
10755 1160 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10756 1160 : d->dfToMeter = GetLinearUnits(nullptr);
10757 1160 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10758 1160 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10759 1157 : d->dfToDegrees = 1.0;
10760 : }
10761 :
10762 : /************************************************************************/
10763 : /* GetExtension() */
10764 : /************************************************************************/
10765 :
10766 : /**
10767 : * \brief Fetch extension value.
10768 : *
10769 : * Fetch the value of the named EXTENSION item for the identified
10770 : * target node.
10771 : *
10772 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10773 : * @param pszName the name of the extension being fetched.
10774 : * @param pszDefault the value to return if the extension is not found.
10775 : *
10776 : * @return node value if successful or pszDefault on failure.
10777 : */
10778 :
10779 11611 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10780 : const char *pszName,
10781 : const char *pszDefault) const
10782 :
10783 : {
10784 23222 : TAKE_OPTIONAL_LOCK();
10785 :
10786 : /* -------------------------------------------------------------------- */
10787 : /* Find the target node. */
10788 : /* -------------------------------------------------------------------- */
10789 : const OGR_SRSNode *poNode =
10790 11611 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10791 :
10792 11611 : if (poNode == nullptr)
10793 2355 : return nullptr;
10794 :
10795 : /* -------------------------------------------------------------------- */
10796 : /* Fetch matching EXTENSION if there is one. */
10797 : /* -------------------------------------------------------------------- */
10798 68172 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10799 : {
10800 58940 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10801 :
10802 58966 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10803 26 : poChild->GetChildCount() >= 2)
10804 : {
10805 26 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10806 24 : return poChild->GetChild(1)->GetValue();
10807 : }
10808 : }
10809 :
10810 9232 : return pszDefault;
10811 : }
10812 :
10813 : /************************************************************************/
10814 : /* SetExtension() */
10815 : /************************************************************************/
10816 : /**
10817 : * \brief Set extension value.
10818 : *
10819 : * Set the value of the named EXTENSION item for the identified
10820 : * target node.
10821 : *
10822 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10823 : * @param pszName the name of the extension being fetched.
10824 : * @param pszValue the value to set
10825 : *
10826 : * @return OGRERR_NONE on success
10827 : */
10828 :
10829 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10830 : const char *pszName,
10831 : const char *pszValue)
10832 :
10833 : {
10834 40 : TAKE_OPTIONAL_LOCK();
10835 :
10836 : /* -------------------------------------------------------------------- */
10837 : /* Find the target node. */
10838 : /* -------------------------------------------------------------------- */
10839 20 : OGR_SRSNode *poNode = nullptr;
10840 :
10841 20 : if (pszTargetKey == nullptr)
10842 0 : poNode = GetRoot();
10843 : else
10844 20 : poNode = GetAttrNode(pszTargetKey);
10845 :
10846 20 : if (poNode == nullptr)
10847 0 : return OGRERR_FAILURE;
10848 :
10849 : /* -------------------------------------------------------------------- */
10850 : /* Fetch matching EXTENSION if there is one. */
10851 : /* -------------------------------------------------------------------- */
10852 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10853 : {
10854 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10855 :
10856 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10857 6 : poChild->GetChildCount() >= 2)
10858 : {
10859 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10860 : {
10861 6 : poChild->GetChild(1)->SetValue(pszValue);
10862 6 : return OGRERR_NONE;
10863 : }
10864 : }
10865 : }
10866 :
10867 : /* -------------------------------------------------------------------- */
10868 : /* Create a new EXTENSION node. */
10869 : /* -------------------------------------------------------------------- */
10870 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10871 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10872 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10873 :
10874 14 : poNode->AddChild(poAuthNode);
10875 :
10876 14 : return OGRERR_NONE;
10877 : }
10878 :
10879 : /************************************************************************/
10880 : /* OSRCleanup() */
10881 : /************************************************************************/
10882 :
10883 : static void CleanupSRSWGS84Mutex();
10884 :
10885 : /**
10886 : * \brief Cleanup cached SRS related memory.
10887 : *
10888 : * This function will attempt to cleanup any cache spatial reference
10889 : * related information, such as cached tables of coordinate systems.
10890 : *
10891 : * This function should not be called concurrently with any other GDAL/OGR
10892 : * function. It is meant at being called once before process termination
10893 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10894 : * thread-specific resources before thread termination.
10895 : */
10896 1131 : void OSRCleanup(void)
10897 :
10898 : {
10899 1131 : OGRCTDumpStatistics();
10900 1131 : CSVDeaccess(nullptr);
10901 1131 : CleanupSRSWGS84Mutex();
10902 1131 : OSRCTCleanCache();
10903 1131 : OSRCleanupTLSContext();
10904 1131 : }
10905 :
10906 : /************************************************************************/
10907 : /* GetAxesCount() */
10908 : /************************************************************************/
10909 :
10910 : /**
10911 : * \brief Return the number of axis of the coordinate system of the CRS.
10912 : *
10913 : * @since GDAL 3.0
10914 : */
10915 37941 : int OGRSpatialReference::GetAxesCount() const
10916 : {
10917 75882 : TAKE_OPTIONAL_LOCK();
10918 :
10919 37941 : int axisCount = 0;
10920 37941 : d->refreshProjObj();
10921 37941 : if (d->m_pj_crs == nullptr)
10922 : {
10923 0 : return 0;
10924 : }
10925 37941 : d->demoteFromBoundCRS();
10926 37941 : auto ctxt = d->getPROJContext();
10927 37941 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10928 : {
10929 38 : for (int i = 0;; i++)
10930 : {
10931 114 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10932 114 : if (!subCRS)
10933 38 : break;
10934 76 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10935 : {
10936 18 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10937 18 : if (baseCRS)
10938 : {
10939 18 : proj_destroy(subCRS);
10940 18 : subCRS = baseCRS;
10941 : }
10942 : }
10943 76 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10944 76 : if (cs)
10945 : {
10946 76 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10947 76 : proj_destroy(cs);
10948 : }
10949 76 : proj_destroy(subCRS);
10950 76 : }
10951 : }
10952 : else
10953 : {
10954 37903 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10955 37903 : if (cs)
10956 : {
10957 37903 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10958 37903 : proj_destroy(cs);
10959 : }
10960 : }
10961 37941 : d->undoDemoteFromBoundCRS();
10962 37941 : return axisCount;
10963 : }
10964 :
10965 : /************************************************************************/
10966 : /* OSRGetAxesCount() */
10967 : /************************************************************************/
10968 :
10969 : /**
10970 : * \brief Return the number of axis of the coordinate system of the CRS.
10971 : *
10972 : * This method is the equivalent of the C++ method
10973 : * OGRSpatialReference::GetAxesCount()
10974 : *
10975 : * @since GDAL 3.1
10976 : */
10977 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10978 :
10979 : {
10980 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10981 :
10982 6 : return ToPointer(hSRS)->GetAxesCount();
10983 : }
10984 :
10985 : /************************************************************************/
10986 : /* GetAxis() */
10987 : /************************************************************************/
10988 :
10989 : /**
10990 : * \brief Fetch the orientation of one axis.
10991 : *
10992 : * Fetches the request axis (iAxis - zero based) from the
10993 : * indicated portion of the coordinate system (pszTargetKey) which
10994 : * should be either "GEOGCS" or "PROJCS".
10995 : *
10996 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10997 : *
10998 : * This method is equivalent to the C function OSRGetAxis().
10999 : *
11000 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11001 : * "GEOGCS").
11002 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11003 : * @param peOrientation location into which to place the fetch orientation, may
11004 : * be NULL.
11005 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11006 : * factor. May be NULL. Only set if pszTargetKey == NULL
11007 : *
11008 : * @return the name of the axis or NULL on failure.
11009 : */
11010 :
11011 7522 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11012 : OGRAxisOrientation *peOrientation,
11013 : double *pdfConvUnit) const
11014 :
11015 : {
11016 15044 : TAKE_OPTIONAL_LOCK();
11017 :
11018 7522 : if (peOrientation != nullptr)
11019 7413 : *peOrientation = OAO_Other;
11020 7522 : if (pdfConvUnit != nullptr)
11021 101 : *pdfConvUnit = 0;
11022 :
11023 7522 : d->refreshProjObj();
11024 7522 : if (d->m_pj_crs == nullptr)
11025 : {
11026 3 : return nullptr;
11027 : }
11028 :
11029 7519 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11030 7519 : if (pszTargetKey == nullptr && iAxis <= 2)
11031 : {
11032 7519 : auto ctxt = d->getPROJContext();
11033 :
11034 7519 : int iAxisModified = iAxis;
11035 :
11036 7519 : d->demoteFromBoundCRS();
11037 :
11038 7519 : PJ *cs = nullptr;
11039 7519 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11040 : {
11041 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11042 134 : if (horizCRS)
11043 : {
11044 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11045 : {
11046 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11047 6 : if (baseCRS)
11048 : {
11049 6 : proj_destroy(horizCRS);
11050 6 : horizCRS = baseCRS;
11051 : }
11052 : }
11053 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11054 134 : proj_destroy(horizCRS);
11055 134 : if (cs)
11056 : {
11057 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11058 : {
11059 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11060 44 : proj_destroy(cs);
11061 44 : cs = nullptr;
11062 : }
11063 : }
11064 : }
11065 :
11066 134 : if (cs == nullptr)
11067 : {
11068 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11069 44 : if (vertCRS)
11070 : {
11071 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11072 : {
11073 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11074 30 : if (baseCRS)
11075 : {
11076 30 : proj_destroy(vertCRS);
11077 30 : vertCRS = baseCRS;
11078 : }
11079 : }
11080 :
11081 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11082 44 : proj_destroy(vertCRS);
11083 : }
11084 : }
11085 : }
11086 : else
11087 : {
11088 7385 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11089 : }
11090 :
11091 7519 : if (cs)
11092 : {
11093 7519 : const char *pszName = nullptr;
11094 7519 : const char *pszOrientation = nullptr;
11095 7519 : double dfConvFactor = 0.0;
11096 7519 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11097 : &pszOrientation, &dfConvFactor, nullptr,
11098 : nullptr, nullptr);
11099 :
11100 7519 : if (pdfConvUnit != nullptr)
11101 : {
11102 101 : *pdfConvUnit = dfConvFactor;
11103 : }
11104 :
11105 7519 : if (pszName && pszOrientation)
11106 : {
11107 7519 : d->m_osAxisName[iAxis] = pszName;
11108 7519 : if (peOrientation)
11109 : {
11110 7410 : if (EQUAL(pszOrientation, "NORTH"))
11111 4677 : *peOrientation = OAO_North;
11112 2733 : else if (EQUAL(pszOrientation, "EAST"))
11113 2675 : *peOrientation = OAO_East;
11114 58 : else if (EQUAL(pszOrientation, "SOUTH"))
11115 47 : *peOrientation = OAO_South;
11116 11 : else if (EQUAL(pszOrientation, "WEST"))
11117 0 : *peOrientation = OAO_West;
11118 11 : else if (EQUAL(pszOrientation, "UP"))
11119 1 : *peOrientation = OAO_Up;
11120 10 : else if (EQUAL(pszOrientation, "DOWN"))
11121 0 : *peOrientation = OAO_Down;
11122 : }
11123 7519 : proj_destroy(cs);
11124 7519 : d->undoDemoteFromBoundCRS();
11125 7519 : return d->m_osAxisName[iAxis].c_str();
11126 : }
11127 0 : proj_destroy(cs);
11128 : }
11129 0 : d->undoDemoteFromBoundCRS();
11130 : }
11131 :
11132 : /* -------------------------------------------------------------------- */
11133 : /* Find the target node. */
11134 : /* -------------------------------------------------------------------- */
11135 0 : const OGR_SRSNode *poNode = nullptr;
11136 :
11137 0 : if (pszTargetKey == nullptr)
11138 0 : poNode = GetRoot();
11139 : else
11140 0 : poNode = GetAttrNode(pszTargetKey);
11141 :
11142 0 : if (poNode == nullptr)
11143 0 : return nullptr;
11144 :
11145 : /* -------------------------------------------------------------------- */
11146 : /* Find desired child AXIS. */
11147 : /* -------------------------------------------------------------------- */
11148 0 : const OGR_SRSNode *poAxis = nullptr;
11149 0 : const int nChildCount = poNode->GetChildCount();
11150 :
11151 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11152 : {
11153 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11154 :
11155 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11156 0 : continue;
11157 :
11158 0 : if (iAxis == 0)
11159 : {
11160 0 : poAxis = poChild;
11161 0 : break;
11162 : }
11163 0 : iAxis--;
11164 : }
11165 :
11166 0 : if (poAxis == nullptr)
11167 0 : return nullptr;
11168 :
11169 0 : if (poAxis->GetChildCount() < 2)
11170 0 : return nullptr;
11171 :
11172 : /* -------------------------------------------------------------------- */
11173 : /* Extract name and orientation if possible. */
11174 : /* -------------------------------------------------------------------- */
11175 0 : if (peOrientation != nullptr)
11176 : {
11177 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11178 :
11179 0 : if (EQUAL(pszOrientation, "NORTH"))
11180 0 : *peOrientation = OAO_North;
11181 0 : else if (EQUAL(pszOrientation, "EAST"))
11182 0 : *peOrientation = OAO_East;
11183 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11184 0 : *peOrientation = OAO_South;
11185 0 : else if (EQUAL(pszOrientation, "WEST"))
11186 0 : *peOrientation = OAO_West;
11187 0 : else if (EQUAL(pszOrientation, "UP"))
11188 0 : *peOrientation = OAO_Up;
11189 0 : else if (EQUAL(pszOrientation, "DOWN"))
11190 0 : *peOrientation = OAO_Down;
11191 0 : else if (EQUAL(pszOrientation, "OTHER"))
11192 0 : *peOrientation = OAO_Other;
11193 : else
11194 : {
11195 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11196 : pszOrientation);
11197 : }
11198 : }
11199 :
11200 0 : return poAxis->GetChild(0)->GetValue();
11201 : }
11202 :
11203 : /************************************************************************/
11204 : /* OSRGetAxis() */
11205 : /************************************************************************/
11206 :
11207 : /**
11208 : * \brief Fetch the orientation of one axis.
11209 : *
11210 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11211 : */
11212 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11213 : int iAxis, OGRAxisOrientation *peOrientation)
11214 :
11215 : {
11216 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11217 :
11218 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11219 : }
11220 :
11221 : /************************************************************************/
11222 : /* OSRAxisEnumToName() */
11223 : /************************************************************************/
11224 :
11225 : /**
11226 : * \brief Return the string representation for the OGRAxisOrientation
11227 : * enumeration.
11228 : *
11229 : * For example "NORTH" for OAO_North.
11230 : *
11231 : * @return an internal string
11232 : */
11233 396 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11234 :
11235 : {
11236 396 : if (eOrientation == OAO_North)
11237 198 : return "NORTH";
11238 198 : if (eOrientation == OAO_East)
11239 198 : return "EAST";
11240 0 : if (eOrientation == OAO_South)
11241 0 : return "SOUTH";
11242 0 : if (eOrientation == OAO_West)
11243 0 : return "WEST";
11244 0 : if (eOrientation == OAO_Up)
11245 0 : return "UP";
11246 0 : if (eOrientation == OAO_Down)
11247 0 : return "DOWN";
11248 0 : if (eOrientation == OAO_Other)
11249 0 : return "OTHER";
11250 :
11251 0 : return "UNKNOWN";
11252 : }
11253 :
11254 : /************************************************************************/
11255 : /* SetAxes() */
11256 : /************************************************************************/
11257 :
11258 : /**
11259 : * \brief Set the axes for a coordinate system.
11260 : *
11261 : * Set the names, and orientations of the axes for either a projected
11262 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11263 : *
11264 : * This method is equivalent to the C function OSRSetAxes().
11265 : *
11266 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11267 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11268 : * @param eXAxisOrientation normally OAO_East.
11269 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11270 : * @param eYAxisOrientation normally OAO_North.
11271 : *
11272 : * @return OGRERR_NONE on success or an error code.
11273 : */
11274 :
11275 198 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11276 : const char *pszXAxisName,
11277 : OGRAxisOrientation eXAxisOrientation,
11278 : const char *pszYAxisName,
11279 : OGRAxisOrientation eYAxisOrientation)
11280 :
11281 : {
11282 396 : TAKE_OPTIONAL_LOCK();
11283 :
11284 : /* -------------------------------------------------------------------- */
11285 : /* Find the target node. */
11286 : /* -------------------------------------------------------------------- */
11287 198 : OGR_SRSNode *poNode = nullptr;
11288 :
11289 198 : if (pszTargetKey == nullptr)
11290 198 : poNode = GetRoot();
11291 : else
11292 0 : poNode = GetAttrNode(pszTargetKey);
11293 :
11294 198 : if (poNode == nullptr)
11295 0 : return OGRERR_FAILURE;
11296 :
11297 : /* -------------------------------------------------------------------- */
11298 : /* Strip any existing AXIS children. */
11299 : /* -------------------------------------------------------------------- */
11300 594 : while (poNode->FindChild("AXIS") >= 0)
11301 396 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11302 :
11303 : /* -------------------------------------------------------------------- */
11304 : /* Insert desired axes */
11305 : /* -------------------------------------------------------------------- */
11306 198 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11307 :
11308 198 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11309 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11310 :
11311 198 : poNode->AddChild(poAxis);
11312 :
11313 198 : poAxis = new OGR_SRSNode("AXIS");
11314 :
11315 198 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11316 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11317 :
11318 198 : poNode->AddChild(poAxis);
11319 :
11320 198 : return OGRERR_NONE;
11321 : }
11322 :
11323 : /************************************************************************/
11324 : /* OSRSetAxes() */
11325 : /************************************************************************/
11326 : /**
11327 : * \brief Set the axes for a coordinate system.
11328 : *
11329 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11330 : */
11331 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11332 : const char *pszXAxisName,
11333 : OGRAxisOrientation eXAxisOrientation,
11334 : const char *pszYAxisName,
11335 : OGRAxisOrientation eYAxisOrientation)
11336 : {
11337 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11338 :
11339 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11340 : eXAxisOrientation, pszYAxisName,
11341 0 : eYAxisOrientation);
11342 : }
11343 :
11344 : /************************************************************************/
11345 : /* OSRExportToMICoordSys() */
11346 : /************************************************************************/
11347 : /**
11348 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11349 : *
11350 : * This method is the equivalent of the C++ method
11351 : * OGRSpatialReference::exportToMICoordSys
11352 : */
11353 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11354 :
11355 : {
11356 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11357 :
11358 5 : *ppszReturn = nullptr;
11359 :
11360 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11361 : }
11362 :
11363 : /************************************************************************/
11364 : /* exportToMICoordSys() */
11365 : /************************************************************************/
11366 :
11367 : /**
11368 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11369 : *
11370 : * Note that the returned WKT string should be freed with
11371 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11372 : *
11373 : * This method is the same as the C function OSRExportToMICoordSys().
11374 : *
11375 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11376 : * definition will be assigned.
11377 : *
11378 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11379 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11380 : */
11381 :
11382 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11383 :
11384 : {
11385 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11386 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11387 7 : return OGRERR_NONE;
11388 :
11389 0 : return OGRERR_FAILURE;
11390 : }
11391 :
11392 : /************************************************************************/
11393 : /* OSRImportFromMICoordSys() */
11394 : /************************************************************************/
11395 : /**
11396 : * \brief Import Mapinfo style CoordSys definition.
11397 : *
11398 : * This method is the equivalent of the C++ method
11399 : * OGRSpatialReference::importFromMICoordSys
11400 : */
11401 :
11402 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11403 : const char *pszCoordSys)
11404 :
11405 : {
11406 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11407 :
11408 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11409 : }
11410 :
11411 : /************************************************************************/
11412 : /* importFromMICoordSys() */
11413 : /************************************************************************/
11414 :
11415 : /**
11416 : * \brief Import Mapinfo style CoordSys definition.
11417 : *
11418 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11419 : * definition string.
11420 : *
11421 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11422 : *
11423 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11424 : *
11425 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11426 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11427 : */
11428 :
11429 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11430 :
11431 : {
11432 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11433 :
11434 17 : if (poResult == nullptr)
11435 0 : return OGRERR_FAILURE;
11436 :
11437 17 : *this = *poResult;
11438 17 : delete poResult;
11439 :
11440 17 : return OGRERR_NONE;
11441 : }
11442 :
11443 : /************************************************************************/
11444 : /* OSRCalcInvFlattening() */
11445 : /************************************************************************/
11446 :
11447 : /**
11448 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11449 : *
11450 : * @param dfSemiMajor Semi-major axis length.
11451 : * @param dfSemiMinor Semi-minor axis length.
11452 : *
11453 : * @return inverse flattening, or 0 if both axis are equal.
11454 : */
11455 :
11456 8627 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11457 : {
11458 8627 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11459 27 : return 0;
11460 8600 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11461 : {
11462 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11463 : "OSRCalcInvFlattening(): Wrong input values");
11464 0 : return 0;
11465 : }
11466 :
11467 8600 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11468 : }
11469 :
11470 : /************************************************************************/
11471 : /* OSRCalcInvFlattening() */
11472 : /************************************************************************/
11473 :
11474 : /**
11475 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11476 : *
11477 : * @param dfSemiMajor Semi-major axis length.
11478 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11479 : *
11480 : * @return semi-minor axis
11481 : */
11482 :
11483 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11484 : double dfInvFlattening)
11485 : {
11486 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11487 103 : return dfSemiMajor;
11488 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11489 : {
11490 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11491 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11492 0 : return dfSemiMajor;
11493 : }
11494 :
11495 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11496 : }
11497 :
11498 : /************************************************************************/
11499 : /* GetWGS84SRS() */
11500 : /************************************************************************/
11501 :
11502 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11503 : static CPLMutex *hMutex = nullptr;
11504 :
11505 : /**
11506 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11507 : *
11508 : * Note: the instance will have
11509 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11510 : *
11511 : * The reference counter of the returned object is not increased by this
11512 : * operation.
11513 : *
11514 : * @return instance.
11515 : */
11516 :
11517 1003 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11518 : {
11519 1003 : CPLMutexHolderD(&hMutex);
11520 1003 : if (poSRSWGS84 == nullptr)
11521 : {
11522 5 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11523 5 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11524 : }
11525 2006 : return poSRSWGS84;
11526 : }
11527 :
11528 : /************************************************************************/
11529 : /* CleanupSRSWGS84Mutex() */
11530 : /************************************************************************/
11531 :
11532 1131 : static void CleanupSRSWGS84Mutex()
11533 : {
11534 1131 : if (hMutex != nullptr)
11535 : {
11536 3 : poSRSWGS84->Release();
11537 3 : poSRSWGS84 = nullptr;
11538 3 : CPLDestroyMutex(hMutex);
11539 3 : hMutex = nullptr;
11540 : }
11541 1131 : }
11542 :
11543 : /************************************************************************/
11544 : /* OSRImportFromProj4() */
11545 : /************************************************************************/
11546 : /**
11547 : * \brief Import PROJ coordinate string.
11548 : *
11549 : * This function is the same as OGRSpatialReference::importFromProj4().
11550 : */
11551 220 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11552 :
11553 : {
11554 220 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11555 :
11556 220 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11557 : }
11558 :
11559 : /************************************************************************/
11560 : /* importFromProj4() */
11561 : /************************************************************************/
11562 :
11563 : /**
11564 : * \brief Import PROJ coordinate string.
11565 : *
11566 : * The OGRSpatialReference is initialized from the passed PROJs style
11567 : * coordinate system string.
11568 : *
11569 : * Example:
11570 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11571 : *
11572 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11573 : * a legacy syntax that should be avoided in the future. In particular they will
11574 : * result in CRS objects whose axis order might not correspond to the official
11575 : * EPSG axis order.
11576 : *
11577 : * This method is the equivalent of the C function OSRImportFromProj4().
11578 : *
11579 : * @param pszProj4 the PROJ style string.
11580 : *
11581 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11582 : */
11583 :
11584 764 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11585 :
11586 : {
11587 1528 : TAKE_OPTIONAL_LOCK();
11588 :
11589 764 : if (strlen(pszProj4) >= 10000)
11590 : {
11591 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11592 1 : return OGRERR_CORRUPT_DATA;
11593 : }
11594 :
11595 : /* -------------------------------------------------------------------- */
11596 : /* Clear any existing definition. */
11597 : /* -------------------------------------------------------------------- */
11598 763 : Clear();
11599 :
11600 763 : CPLString osProj4(pszProj4);
11601 763 : if (osProj4.find("type=crs") == std::string::npos)
11602 : {
11603 754 : osProj4 += " +type=crs";
11604 : }
11605 :
11606 765 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11607 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11608 : {
11609 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11610 : "+init=epsg:XXXX syntax is deprecated. It might return "
11611 : "a CRS with a non-EPSG compliant axis order.");
11612 : }
11613 763 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11614 763 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11615 763 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11616 763 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11617 : }
11618 :
11619 : /************************************************************************/
11620 : /* OSRExportToProj4() */
11621 : /************************************************************************/
11622 : /**
11623 : * \brief Export coordinate system in PROJ.4 legacy format.
11624 : *
11625 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11626 : * PROJ >= 6 is significantly different from earlier versions. In particular
11627 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11628 : * will be missing most of the time. PROJ strings to encode CRS should be
11629 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11630 : * is the recommended way.
11631 : *
11632 : * This function is the same as OGRSpatialReference::exportToProj4().
11633 : */
11634 455 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11635 : char **ppszReturn)
11636 :
11637 : {
11638 455 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11639 :
11640 455 : *ppszReturn = nullptr;
11641 :
11642 455 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11643 : }
11644 :
11645 : /************************************************************************/
11646 : /* exportToProj4() */
11647 : /************************************************************************/
11648 :
11649 : /**
11650 : * \brief Export coordinate system in PROJ.4 legacy format.
11651 : *
11652 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11653 : * PROJ >= 6 is significantly different from earlier versions. In particular
11654 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11655 : * will be missing most of the time. PROJ strings to encode CRS should be
11656 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11657 : * representation is the recommended way.
11658 : *
11659 : * Converts the loaded coordinate reference system into PROJ format
11660 : * to the extent possible. The string returned in ppszProj4 should be
11661 : * deallocated by the caller with CPLFree() when no longer needed.
11662 : *
11663 : * LOCAL_CS coordinate systems are not translatable. An empty string
11664 : * will be returned along with OGRERR_NONE.
11665 : *
11666 : * Special processing for Transverse Mercator:
11667 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11668 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11669 : * for the tmerc and utm projection methods, rather than the more accurate
11670 : * method.
11671 : *
11672 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11673 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11674 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11675 : * added. This automatic addition may be disabled by setting the
11676 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11677 : *
11678 : * This method is the equivalent of the C function OSRExportToProj4().
11679 : *
11680 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11681 : * will be assigned.
11682 : *
11683 : * @return OGRERR_NONE on success or an error code on failure.
11684 : */
11685 :
11686 1484 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11687 :
11688 : {
11689 : // In the past calling this method was thread-safe, even if we never
11690 : // guaranteed it. Now proj_as_proj_string() will cache the result
11691 : // internally, so this is no longer thread-safe.
11692 2968 : std::lock_guard oLock(d->m_mutex);
11693 :
11694 1484 : d->refreshProjObj();
11695 1484 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11696 : {
11697 4 : *ppszProj4 = CPLStrdup("");
11698 4 : return OGRERR_FAILURE;
11699 : }
11700 :
11701 : // OSR_USE_ETMERC is here just for legacy
11702 1480 : bool bForceApproxTMerc = false;
11703 1480 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11704 1480 : if (pszUseETMERC && pszUseETMERC[0])
11705 : {
11706 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11707 : "OSR_USE_ETMERC is a legacy configuration option, which "
11708 : "now has only effect when set to NO (YES is the default). "
11709 : "Use OSR_USE_APPROX_TMERC=YES instead");
11710 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11711 : }
11712 : else
11713 : {
11714 : const char *pszUseApproxTMERC =
11715 1480 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11716 1480 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11717 : {
11718 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11719 : }
11720 : }
11721 1480 : const char *options[] = {
11722 1480 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11723 :
11724 1480 : const char *projString = proj_as_proj_string(
11725 1480 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11726 :
11727 1480 : PJ *boundCRS = nullptr;
11728 2956 : if (projString &&
11729 1476 : (strstr(projString, "+datum=") == nullptr ||
11730 2966 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11731 561 : CPLTestBool(
11732 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11733 : {
11734 561 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11735 561 : d->getPROJContext(), d->m_pj_crs, true,
11736 561 : strstr(projString, "+datum=") == nullptr);
11737 561 : if (boundCRS)
11738 : {
11739 227 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11740 : PJ_PROJ_4, options);
11741 : }
11742 : }
11743 :
11744 1480 : if (projString == nullptr)
11745 : {
11746 4 : *ppszProj4 = CPLStrdup("");
11747 4 : proj_destroy(boundCRS);
11748 4 : return OGRERR_FAILURE;
11749 : }
11750 1476 : *ppszProj4 = CPLStrdup(projString);
11751 1476 : proj_destroy(boundCRS);
11752 1476 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11753 1476 : if (pszTypeCrs)
11754 1476 : *pszTypeCrs = '\0';
11755 1476 : return OGRERR_NONE;
11756 : }
11757 :
11758 : /************************************************************************/
11759 : /* morphToESRI() */
11760 : /************************************************************************/
11761 : /**
11762 : * \brief Convert in place to ESRI WKT format.
11763 : *
11764 : * The value nodes of this coordinate system are modified in various manners
11765 : * more closely map onto the ESRI concept of WKT format. This includes
11766 : * renaming a variety of projections and arguments, and stripping out
11767 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11768 : *
11769 : * \note Since GDAL 3.0, this function has only user-visible effects at
11770 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11771 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11772 : *
11773 : * This does the same as the C function OSRMorphToESRI().
11774 : *
11775 : * @return OGRERR_NONE unless something goes badly wrong.
11776 : * @deprecated
11777 : */
11778 :
11779 235 : OGRErr OGRSpatialReference::morphToESRI()
11780 :
11781 : {
11782 235 : TAKE_OPTIONAL_LOCK();
11783 :
11784 235 : d->refreshProjObj();
11785 235 : d->setMorphToESRI(true);
11786 :
11787 470 : return OGRERR_NONE;
11788 : }
11789 :
11790 : /************************************************************************/
11791 : /* OSRMorphToESRI() */
11792 : /************************************************************************/
11793 :
11794 : /**
11795 : * \brief Convert in place to ESRI WKT format.
11796 : *
11797 : * This function is the same as the C++ method
11798 : * OGRSpatialReference::morphToESRI().
11799 : */
11800 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11801 :
11802 : {
11803 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11804 :
11805 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11806 : }
11807 :
11808 : /************************************************************************/
11809 : /* morphFromESRI() */
11810 : /************************************************************************/
11811 :
11812 : /**
11813 : * \brief Convert in place from ESRI WKT format.
11814 : *
11815 : * The value notes of this coordinate system are modified in various manners
11816 : * to adhere more closely to the WKT standard. This mostly involves
11817 : * translating a variety of ESRI names for projections, arguments and
11818 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11819 : * translation of EPSG to WKT for the CT specification.
11820 : *
11821 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11822 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11823 : * effect is to undo the effect of a potential prior call to morphToESRI().
11824 : *
11825 : * This does the same as the C function OSRMorphFromESRI().
11826 : *
11827 : * @return OGRERR_NONE unless something goes badly wrong.
11828 : * @deprecated
11829 : */
11830 :
11831 21 : OGRErr OGRSpatialReference::morphFromESRI()
11832 :
11833 : {
11834 21 : TAKE_OPTIONAL_LOCK();
11835 :
11836 21 : d->refreshProjObj();
11837 21 : d->setMorphToESRI(false);
11838 :
11839 42 : return OGRERR_NONE;
11840 : }
11841 :
11842 : /************************************************************************/
11843 : /* OSRMorphFromESRI() */
11844 : /************************************************************************/
11845 :
11846 : /**
11847 : * \brief Convert in place from ESRI WKT format.
11848 : *
11849 : * This function is the same as the C++ method
11850 : * OGRSpatialReference::morphFromESRI().
11851 : */
11852 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11853 :
11854 : {
11855 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11856 :
11857 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11858 : }
11859 :
11860 : /************************************************************************/
11861 : /* FindMatches() */
11862 : /************************************************************************/
11863 :
11864 : /**
11865 : * \brief Try to identify a match between the passed SRS and a related SRS
11866 : * in a catalog.
11867 : *
11868 : * Matching may be partial, or may fail.
11869 : * Returned entries will be sorted by decreasing match confidence (first
11870 : * entry has the highest match confidence).
11871 : *
11872 : * The exact way matching is done may change in future versions. Starting with
11873 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11874 : *
11875 : * This method is the same as OSRFindMatches().
11876 : *
11877 : * @param papszOptions NULL terminated list of options or NULL
11878 : * @param pnEntries Output parameter. Number of values in the returned array.
11879 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11880 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11881 : * indicate the confidence in the match. 100 is the highest confidence level.
11882 : * The array must be freed with CPLFree().
11883 : *
11884 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11885 : * with OSRFreeSRSArray()
11886 : *
11887 : *
11888 : * @see OGRSpatialReference::FindBestMatch()
11889 : */
11890 : OGRSpatialReferenceH *
11891 1453 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11892 : int **ppanMatchConfidence) const
11893 : {
11894 2906 : TAKE_OPTIONAL_LOCK();
11895 :
11896 1453 : CPL_IGNORE_RET_VAL(papszOptions);
11897 :
11898 1453 : if (pnEntries)
11899 1453 : *pnEntries = 0;
11900 1453 : if (ppanMatchConfidence)
11901 1453 : *ppanMatchConfidence = nullptr;
11902 :
11903 1453 : d->refreshProjObj();
11904 1453 : if (!d->m_pj_crs)
11905 0 : return nullptr;
11906 :
11907 1453 : int *panConfidence = nullptr;
11908 1453 : auto ctxt = d->getPROJContext();
11909 : auto list =
11910 1453 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11911 1453 : if (!list)
11912 0 : return nullptr;
11913 :
11914 1453 : const int nMatches = proj_list_get_count(list);
11915 :
11916 1453 : if (pnEntries)
11917 1453 : *pnEntries = static_cast<int>(nMatches);
11918 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11919 1453 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11920 1453 : if (ppanMatchConfidence)
11921 : {
11922 1453 : *ppanMatchConfidence =
11923 1453 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11924 : }
11925 :
11926 1453 : bool bSortAgain = false;
11927 :
11928 4292 : for (int i = 0; i < nMatches; i++)
11929 : {
11930 2839 : PJ *obj = proj_list_get(ctxt, list, i);
11931 2839 : CPLAssert(obj);
11932 2839 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11933 2839 : poSRS->d->setPjCRS(obj);
11934 2839 : pahRet[i] = ToHandle(poSRS);
11935 :
11936 : // Identify matches that only differ by axis order
11937 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11938 2857 : poSRS->GetAxesCount() == 2 &&
11939 2848 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11940 : {
11941 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11942 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11943 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11944 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11945 9 : GetAxis(nullptr, 0, &eThisAxis0);
11946 9 : GetAxis(nullptr, 1, &eThisAxis1);
11947 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11948 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11949 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11950 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11951 : {
11952 : auto pj_crs_normalized =
11953 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11954 9 : if (pj_crs_normalized)
11955 : {
11956 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11957 9 : PJ_COMP_EQUIVALENT))
11958 : {
11959 3 : bSortAgain = true;
11960 3 : panConfidence[i] = 90;
11961 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11962 : }
11963 9 : proj_destroy(pj_crs_normalized);
11964 : }
11965 : }
11966 : }
11967 :
11968 2839 : if (ppanMatchConfidence)
11969 2839 : (*ppanMatchConfidence)[i] = panConfidence[i];
11970 : }
11971 :
11972 1453 : if (bSortAgain)
11973 : {
11974 3 : std::vector<int> anIndices;
11975 12 : for (int i = 0; i < nMatches; ++i)
11976 9 : anIndices.push_back(i);
11977 :
11978 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11979 9 : [&panConfidence](int i, int j)
11980 9 : { return panConfidence[i] > panConfidence[j]; });
11981 :
11982 : OGRSpatialReferenceH *pahRetSorted =
11983 : static_cast<OGRSpatialReferenceH *>(
11984 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11985 12 : for (int i = 0; i < nMatches; ++i)
11986 : {
11987 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11988 9 : if (ppanMatchConfidence)
11989 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11990 : }
11991 3 : CPLFree(pahRet);
11992 3 : pahRet = pahRetSorted;
11993 : }
11994 :
11995 1453 : pahRet[nMatches] = nullptr;
11996 1453 : proj_list_destroy(list);
11997 1453 : proj_int_list_destroy(panConfidence);
11998 :
11999 1453 : return pahRet;
12000 : }
12001 :
12002 : /************************************************************************/
12003 : /* importFromEPSGA() */
12004 : /************************************************************************/
12005 :
12006 : /**
12007 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12008 : * code.
12009 : *
12010 : * This method will initialize the spatial reference based on the
12011 : * passed in EPSG CRS code found in the PROJ database.
12012 : *
12013 : * Since GDAL 3.0, this method is identical to importFromEPSG().
12014 : *
12015 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12016 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12017 : * such method available for the CRS. This behavior might not always be
12018 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12019 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12020 : * The AddGuessedTOWGS84() method can also be used for that purpose.
12021 : *
12022 : * The method will also by default substitute a deprecated EPSG code by its
12023 : * non-deprecated replacement. If this behavior is not desired, the
12024 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12025 : *
12026 : * This method is the same as the C function OSRImportFromEPSGA().
12027 : *
12028 : * @param nCode a CRS code.
12029 : *
12030 : * @return OGRERR_NONE on success, or an error code on failure.
12031 : */
12032 :
12033 44720 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12034 :
12035 : {
12036 89440 : TAKE_OPTIONAL_LOCK();
12037 :
12038 44720 : Clear();
12039 :
12040 : const char *pszUseNonDeprecated =
12041 44720 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12042 : const bool bUseNonDeprecated =
12043 44720 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12044 44720 : const bool bAddTOWGS84 = CPLTestBool(
12045 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12046 44720 : auto tlsCache = OSRGetProjTLSCache();
12047 44720 : if (tlsCache)
12048 : {
12049 : auto cachedObj =
12050 44720 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12051 44720 : if (cachedObj)
12052 : {
12053 34528 : d->setPjCRS(cachedObj);
12054 34528 : return OGRERR_NONE;
12055 : }
12056 : }
12057 :
12058 20384 : CPLString osCode;
12059 10192 : osCode.Printf("%d", nCode);
12060 : PJ *obj;
12061 10192 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12062 10192 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12063 : {
12064 10187 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12065 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12066 : nullptr);
12067 10187 : if (!obj)
12068 : {
12069 24 : return OGRERR_FAILURE;
12070 : }
12071 : }
12072 : else
12073 : {
12074 : // Likely to be an ESRI CRS...
12075 5 : CPLErr eLastErrorType = CE_None;
12076 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12077 5 : std::string osLastErrorMsg;
12078 5 : bool bIsESRI = false;
12079 : {
12080 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12081 5 : CPLErrorReset();
12082 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12083 : osCode.c_str(), PJ_CATEGORY_CRS,
12084 : true, nullptr);
12085 5 : if (!obj)
12086 : {
12087 2 : eLastErrorType = CPLGetLastErrorType();
12088 2 : eLastErrorNum = CPLGetLastErrorNo();
12089 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12090 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12091 : osCode.c_str(), PJ_CATEGORY_CRS,
12092 : true, nullptr);
12093 2 : if (obj)
12094 1 : bIsESRI = true;
12095 : }
12096 : }
12097 5 : if (!obj)
12098 : {
12099 1 : if (eLastErrorType != CE_None)
12100 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12101 : osLastErrorMsg.c_str());
12102 1 : return OGRERR_FAILURE;
12103 : }
12104 4 : if (bIsESRI)
12105 : {
12106 1 : CPLError(CE_Warning, CPLE_AppDefined,
12107 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12108 : "Assuming ESRI:%d was meant",
12109 : nCode, nCode, nCode);
12110 : }
12111 : }
12112 :
12113 10167 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12114 : {
12115 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12116 410 : if (list)
12117 : {
12118 410 : const auto count = proj_list_get_count(list);
12119 410 : if (count == 1)
12120 : {
12121 : auto nonDeprecated =
12122 359 : proj_list_get(d->getPROJContext(), list, 0);
12123 359 : if (nonDeprecated)
12124 : {
12125 359 : if (pszUseNonDeprecated == nullptr)
12126 : {
12127 : const char *pszNewAuth =
12128 359 : proj_get_id_auth_name(nonDeprecated, 0);
12129 : const char *pszNewCode =
12130 359 : proj_get_id_code(nonDeprecated, 0);
12131 359 : CPLError(CE_Warning, CPLE_AppDefined,
12132 : "CRS EPSG:%d is deprecated. "
12133 : "Its non-deprecated replacement %s:%s "
12134 : "will be used instead. "
12135 : "To use the original CRS, set the "
12136 : "OSR_USE_NON_DEPRECATED "
12137 : "configuration option to NO.",
12138 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12139 : pszNewCode ? pszNewCode : "(null)");
12140 : }
12141 359 : proj_destroy(obj);
12142 359 : obj = nonDeprecated;
12143 : }
12144 : }
12145 : }
12146 410 : proj_list_destroy(list);
12147 : }
12148 :
12149 10167 : if (bAddTOWGS84)
12150 : {
12151 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12152 : obj, nullptr);
12153 1 : if (boundCRS)
12154 : {
12155 1 : proj_destroy(obj);
12156 1 : obj = boundCRS;
12157 : }
12158 : }
12159 :
12160 10167 : d->setPjCRS(obj);
12161 :
12162 10167 : if (tlsCache)
12163 : {
12164 10167 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12165 : obj);
12166 : }
12167 :
12168 10167 : return OGRERR_NONE;
12169 : }
12170 :
12171 : /************************************************************************/
12172 : /* AddGuessedTOWGS84() */
12173 : /************************************************************************/
12174 :
12175 : /**
12176 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12177 : * to WGS84.
12178 : *
12179 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12180 : * to WGS84 when there is one and only one such method available for the CRS.
12181 : * Note: this is more restrictive to how GDAL < 3 worked.
12182 : *
12183 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12184 : *
12185 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12186 : * already a transformation to WGS84 or none matching could be found).
12187 : *
12188 : * @since GDAL 3.0.3
12189 : */
12190 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12191 : {
12192 36 : TAKE_OPTIONAL_LOCK();
12193 :
12194 18 : d->refreshProjObj();
12195 18 : if (!d->m_pj_crs)
12196 0 : return OGRERR_FAILURE;
12197 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12198 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12199 18 : if (!boundCRS)
12200 : {
12201 0 : return OGRERR_FAILURE;
12202 : }
12203 18 : d->setPjCRS(boundCRS);
12204 18 : return OGRERR_NONE;
12205 : }
12206 :
12207 : /************************************************************************/
12208 : /* OSRImportFromEPSGA() */
12209 : /************************************************************************/
12210 :
12211 : /**
12212 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12213 : * to WGS84.
12214 : *
12215 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12216 : *
12217 : * @since GDAL 3.0.3
12218 : */
12219 :
12220 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12221 :
12222 : {
12223 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12224 :
12225 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12226 : }
12227 :
12228 : /************************************************************************/
12229 : /* OSRImportFromEPSGA() */
12230 : /************************************************************************/
12231 :
12232 : /**
12233 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12234 : * code.
12235 : *
12236 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12237 : */
12238 :
12239 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12240 :
12241 : {
12242 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12243 :
12244 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12245 : }
12246 :
12247 : /************************************************************************/
12248 : /* importFromEPSG() */
12249 : /************************************************************************/
12250 :
12251 : /**
12252 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12253 : * code.
12254 : *
12255 : * This method will initialize the spatial reference based on the
12256 : * passed in EPSG CRS code found in the PROJ database.
12257 : *
12258 : * This method is the same as the C function OSRImportFromEPSG().
12259 : *
12260 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12261 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12262 : * such method available for the CRS. This behavior might not always be
12263 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12264 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12265 : *
12266 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12267 : *
12268 : * @return OGRERR_NONE on success, or an error code on failure.
12269 : */
12270 :
12271 39717 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12272 :
12273 : {
12274 39717 : return importFromEPSGA(nCode);
12275 : }
12276 :
12277 : /************************************************************************/
12278 : /* OSRImportFromEPSG() */
12279 : /************************************************************************/
12280 :
12281 : /**
12282 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12283 : * code.
12284 : *
12285 : * This function is the same as OGRSpatialReference::importFromEPSG().
12286 : */
12287 :
12288 1464 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12289 :
12290 : {
12291 1464 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12292 :
12293 1464 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12294 : }
12295 :
12296 : /************************************************************************/
12297 : /* EPSGTreatsAsLatLong() */
12298 : /************************************************************************/
12299 :
12300 : /**
12301 : * \brief This method returns TRUE if this geographic coordinate
12302 : * system should be treated as having lat/long coordinate ordering.
12303 : *
12304 : * Currently this returns TRUE for all geographic coordinate systems
12305 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12306 : * also checked that the CRS had belonged to EPSG authority, but this check
12307 : * has now been removed).
12308 : *
12309 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12310 : * geographic CRS imported with importFromEPSG() would cause this method to
12311 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12312 : * is now equivalent to importFromEPSGA().
12313 : *
12314 : * FALSE will be returned for all coordinate systems that are not geographic,
12315 : * or whose axes ordering is not latitude, longitude.
12316 : *
12317 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12318 : *
12319 : * @return TRUE or FALSE.
12320 : */
12321 :
12322 1100 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12323 :
12324 : {
12325 2200 : TAKE_OPTIONAL_LOCK();
12326 :
12327 1100 : if (!IsGeographic())
12328 759 : return FALSE;
12329 :
12330 341 : d->demoteFromBoundCRS();
12331 :
12332 341 : bool ret = false;
12333 341 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12334 : {
12335 : auto horizCRS =
12336 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12337 3 : if (horizCRS)
12338 : {
12339 : auto cs =
12340 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12341 3 : if (cs)
12342 : {
12343 3 : const char *pszDirection = nullptr;
12344 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12345 : nullptr, &pszDirection, nullptr,
12346 3 : nullptr, nullptr, nullptr))
12347 : {
12348 3 : if (EQUAL(pszDirection, "north"))
12349 : {
12350 3 : ret = true;
12351 : }
12352 : }
12353 :
12354 3 : proj_destroy(cs);
12355 : }
12356 :
12357 3 : proj_destroy(horizCRS);
12358 : }
12359 : }
12360 : else
12361 : {
12362 : auto cs =
12363 338 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12364 338 : if (cs)
12365 : {
12366 338 : const char *pszDirection = nullptr;
12367 338 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12368 : nullptr, &pszDirection, nullptr, nullptr,
12369 338 : nullptr, nullptr))
12370 : {
12371 338 : if (EQUAL(pszDirection, "north"))
12372 : {
12373 292 : ret = true;
12374 : }
12375 : }
12376 :
12377 338 : proj_destroy(cs);
12378 : }
12379 : }
12380 341 : d->undoDemoteFromBoundCRS();
12381 :
12382 341 : return ret;
12383 : }
12384 :
12385 : /************************************************************************/
12386 : /* OSREPSGTreatsAsLatLong() */
12387 : /************************************************************************/
12388 :
12389 : /**
12390 : * \brief This function returns TRUE if this geographic coordinate
12391 : * system should be treated as having lat/long coordinate ordering.
12392 : *
12393 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12394 : */
12395 :
12396 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12397 :
12398 : {
12399 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12400 :
12401 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12402 : }
12403 :
12404 : /************************************************************************/
12405 : /* EPSGTreatsAsNorthingEasting() */
12406 : /************************************************************************/
12407 :
12408 : /**
12409 : * \brief This method returns TRUE if this projected coordinate
12410 : * system should be treated as having northing/easting coordinate ordering.
12411 : *
12412 : * Currently this returns TRUE for all projected coordinate systems
12413 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12414 : * also checked that the CRS had belonged to EPSG authority, but this check
12415 : * has now been removed).
12416 : *
12417 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12418 : * projected CRS with northing, easting axis order imported with
12419 : * importFromEPSG() would cause this method to
12420 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12421 : * is now equivalent to importFromEPSGA().
12422 : *
12423 : * FALSE will be returned for all coordinate systems that are not projected,
12424 : * or whose axes ordering is not northing, easting.
12425 : *
12426 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12427 : *
12428 : * @return TRUE or FALSE.
12429 : *
12430 : */
12431 :
12432 845 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12433 :
12434 : {
12435 1690 : TAKE_OPTIONAL_LOCK();
12436 :
12437 845 : if (!IsProjected())
12438 48 : return FALSE;
12439 :
12440 797 : d->demoteFromBoundCRS();
12441 : PJ *projCRS;
12442 797 : const auto ctxt = d->getPROJContext();
12443 797 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12444 : {
12445 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12446 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12447 : {
12448 0 : d->undoDemoteFromBoundCRS();
12449 0 : proj_destroy(projCRS);
12450 0 : return FALSE;
12451 : }
12452 : }
12453 : else
12454 : {
12455 793 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12456 : }
12457 :
12458 797 : bool ret = false;
12459 797 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12460 797 : proj_destroy(projCRS);
12461 797 : d->undoDemoteFromBoundCRS();
12462 :
12463 797 : if (cs)
12464 : {
12465 797 : ret = isNorthEastAxisOrder(ctxt, cs);
12466 797 : proj_destroy(cs);
12467 : }
12468 :
12469 797 : return ret;
12470 : }
12471 :
12472 : /************************************************************************/
12473 : /* OSREPSGTreatsAsNorthingEasting() */
12474 : /************************************************************************/
12475 :
12476 : /**
12477 : * \brief This function returns TRUE if this projected coordinate
12478 : * system should be treated as having northing/easting coordinate ordering.
12479 : *
12480 : * This function is the same as
12481 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12482 : *
12483 : */
12484 :
12485 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12486 :
12487 : {
12488 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12489 :
12490 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12491 : }
12492 :
12493 : /************************************************************************/
12494 : /* ImportFromESRIWisconsinWKT() */
12495 : /* */
12496 : /* Search a ESRI State Plane WKT and import it. */
12497 : /************************************************************************/
12498 :
12499 : // This is only used by the HFA driver and somewhat dubious we really need that
12500 : // Coming from an old ESRI merge
12501 :
12502 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12503 : double centralMeridian,
12504 : double latOfOrigin,
12505 : const char *unitsName,
12506 : const char *crsName)
12507 : {
12508 2 : TAKE_OPTIONAL_LOCK();
12509 :
12510 1 : if (centralMeridian < -93 || centralMeridian > -87)
12511 0 : return OGRERR_FAILURE;
12512 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12513 0 : return OGRERR_FAILURE;
12514 :
12515 : // If the CS name is known.
12516 1 : if (!prjName && !unitsName && crsName)
12517 : {
12518 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12519 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12520 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12521 0 : if (list)
12522 : {
12523 0 : if (proj_list_get_count(list) == 1)
12524 : {
12525 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12526 0 : if (crs)
12527 : {
12528 0 : Clear();
12529 0 : d->setPjCRS(crs);
12530 0 : proj_list_destroy(list);
12531 0 : return OGRERR_NONE;
12532 : }
12533 : }
12534 0 : proj_list_destroy(list);
12535 : }
12536 0 : return OGRERR_FAILURE;
12537 : }
12538 :
12539 1 : if (prjName == nullptr || unitsName == nullptr)
12540 : {
12541 0 : return OGRERR_FAILURE;
12542 : }
12543 :
12544 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12545 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12546 : "NAD_1983_HARN_WISCRS_", &type, 1,
12547 : true, 0, nullptr);
12548 1 : if (list)
12549 : {
12550 1 : const auto listSize = proj_list_get_count(list);
12551 8 : for (int i = 0; i < listSize; i++)
12552 : {
12553 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12554 8 : if (!crs)
12555 : {
12556 7 : continue;
12557 : }
12558 :
12559 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12560 8 : if (!conv)
12561 : {
12562 0 : proj_destroy(crs);
12563 0 : continue;
12564 : }
12565 8 : const char *pszMethodCode = nullptr;
12566 8 : proj_coordoperation_get_method_info(
12567 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12568 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12569 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12570 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12571 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12572 : nMethodCode ==
12573 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12574 : {
12575 3 : proj_destroy(crs);
12576 3 : proj_destroy(conv);
12577 3 : continue;
12578 : }
12579 :
12580 : auto coordSys =
12581 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12582 5 : if (!coordSys)
12583 : {
12584 0 : proj_destroy(crs);
12585 0 : proj_destroy(conv);
12586 0 : continue;
12587 : }
12588 :
12589 5 : double dfConvFactor = 0.0;
12590 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12591 : nullptr, nullptr, &dfConvFactor, nullptr,
12592 : nullptr, nullptr);
12593 5 : proj_destroy(coordSys);
12594 :
12595 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12596 1 : (!EQUAL(unitsName, "meters") &&
12597 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12598 : 1e-10))
12599 : {
12600 4 : proj_destroy(crs);
12601 4 : proj_destroy(conv);
12602 4 : continue;
12603 : }
12604 :
12605 1 : int idx_lat = proj_coordoperation_get_param_index(
12606 : d->getPROJContext(), conv,
12607 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12608 1 : double valueLat = -1000;
12609 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12610 : nullptr, nullptr, nullptr, &valueLat,
12611 : nullptr, nullptr, nullptr, nullptr,
12612 : nullptr, nullptr);
12613 1 : int idx_lon = proj_coordoperation_get_param_index(
12614 : d->getPROJContext(), conv,
12615 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12616 1 : double valueLong = -1000;
12617 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12618 : nullptr, nullptr, nullptr, &valueLong,
12619 : nullptr, nullptr, nullptr, nullptr,
12620 : nullptr, nullptr);
12621 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12622 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12623 : {
12624 1 : Clear();
12625 1 : d->setPjCRS(crs);
12626 1 : proj_list_destroy(list);
12627 1 : proj_destroy(conv);
12628 1 : return OGRERR_NONE;
12629 : }
12630 :
12631 0 : proj_destroy(crs);
12632 0 : proj_destroy(conv);
12633 : }
12634 0 : proj_list_destroy(list);
12635 : }
12636 :
12637 0 : return OGRERR_FAILURE;
12638 : }
12639 :
12640 : /************************************************************************/
12641 : /* GetAxisMappingStrategy() */
12642 : /************************************************************************/
12643 :
12644 : /** \brief Return the data axis to CRS axis mapping strategy.
12645 : *
12646 : * <ul>
12647 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12648 : * lat/long order, the data will still be long/lat ordered. Similarly for
12649 : * a projected CRS with northing/easting order, the data will still be
12650 : * easting/northing ordered.
12651 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12652 : * the CRS axis.
12653 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12654 : * SetDataAxisToSRSAxisMapping()
12655 : * </ul>
12656 : * @return the data axis to CRS axis mapping strategy.
12657 : * @since GDAL 3.0
12658 : */
12659 92 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12660 : {
12661 92 : TAKE_OPTIONAL_LOCK();
12662 :
12663 184 : return d->m_axisMappingStrategy;
12664 : }
12665 :
12666 : /************************************************************************/
12667 : /* OSRGetAxisMappingStrategy() */
12668 : /************************************************************************/
12669 :
12670 : /** \brief Return the data axis to CRS axis mapping strategy.
12671 : *
12672 : * See OGRSpatialReference::GetAxisMappingStrategy()
12673 : * @since GDAL 3.0
12674 : */
12675 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12676 : {
12677 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12678 :
12679 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12680 : }
12681 :
12682 : /************************************************************************/
12683 : /* SetAxisMappingStrategy() */
12684 : /************************************************************************/
12685 :
12686 : /** \brief Set the data axis to CRS axis mapping strategy.
12687 : *
12688 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12689 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12690 : * later being the default value when the option is not set) to control the
12691 : * value of the data axis to CRS axis mapping strategy when a
12692 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12693 : * override this default value.
12694 : *
12695 : * See OGRSpatialReference::GetAxisMappingStrategy()
12696 : * @since GDAL 3.0
12697 : */
12698 87684 : void OGRSpatialReference::SetAxisMappingStrategy(
12699 : OSRAxisMappingStrategy strategy)
12700 : {
12701 175368 : TAKE_OPTIONAL_LOCK();
12702 :
12703 87684 : d->m_axisMappingStrategy = strategy;
12704 87684 : d->refreshAxisMapping();
12705 87684 : }
12706 :
12707 : /************************************************************************/
12708 : /* OSRSetAxisMappingStrategy() */
12709 : /************************************************************************/
12710 :
12711 : /** \brief Set the data axis to CRS axis mapping strategy.
12712 : *
12713 : * See OGRSpatialReference::SetAxisMappingStrategy()
12714 : * @since GDAL 3.0
12715 : */
12716 868 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12717 : OSRAxisMappingStrategy strategy)
12718 : {
12719 868 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12720 :
12721 868 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12722 : }
12723 :
12724 : /************************************************************************/
12725 : /* GetDataAxisToSRSAxisMapping() */
12726 : /************************************************************************/
12727 :
12728 : /** \brief Return the data axis to SRS axis mapping.
12729 : *
12730 : * The number of elements of the vector will be the number of axis of the CRS.
12731 : * Values start at 1.
12732 : *
12733 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12734 : * for the first axis of the CRS.
12735 : *
12736 : * @since GDAL 3.0
12737 : */
12738 4257990 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12739 : {
12740 4257990 : TAKE_OPTIONAL_LOCK();
12741 :
12742 8515980 : return d->m_axisMapping;
12743 : }
12744 :
12745 : /************************************************************************/
12746 : /* OSRGetDataAxisToSRSAxisMapping() */
12747 : /************************************************************************/
12748 :
12749 : /** \brief Return the data axis to SRS axis mapping.
12750 : *
12751 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12752 : *
12753 : * @since GDAL 3.0
12754 : */
12755 229 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12756 : int *pnCount)
12757 : {
12758 229 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12759 229 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12760 :
12761 : const auto &v =
12762 229 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12763 229 : *pnCount = static_cast<int>(v.size());
12764 229 : return v.data();
12765 : }
12766 :
12767 : /************************************************************************/
12768 : /* SetDataAxisToSRSAxisMapping() */
12769 : /************************************************************************/
12770 :
12771 : /** \brief Set a custom data axis to CRS axis mapping.
12772 : *
12773 : * The number of elements of the mapping vector should be the number of axis
12774 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12775 : * check that, beyond checking there are at least 2 elements, so that this
12776 : * method and setting the CRS can be done in any order).
12777 : * This is taken into account by OGRCoordinateTransformation to transform the
12778 : * order of coordinates to the order expected by the CRS before
12779 : * transformation, and back to the data order after transformation.
12780 : *
12781 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12782 : * axis of the CRS. A negative value can also be used to ask for a sign
12783 : * reversal during coordinate transformation (to deal with northing vs southing,
12784 : * easting vs westing, heights vs depths).
12785 : *
12786 : * When used with OGRCoordinateTransformation,
12787 : * - the only valid values for mapping[0] (data axis number for the first axis
12788 : * of the CRS) are 1, 2, -1, -2.
12789 : * - the only valid values for mapping[1] (data axis number for the second axis
12790 : * of the CRS) are 1, 2, -1, -2.
12791 : * - the only valid values mapping[2] are 3 or -3.
12792 : * Note: this method does not validate the values of mapping[].
12793 : *
12794 : * mapping=[2,1] typically expresses the inversion of axis between the data
12795 : * axis and the CRS axis for a 2D CRS.
12796 : *
12797 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12798 : *
12799 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12800 : *
12801 : * @param mapping The new data axis to CRS axis mapping.
12802 : *
12803 : * @since GDAL 3.0
12804 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12805 : */
12806 8933 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12807 : const std::vector<int> &mapping)
12808 : {
12809 17866 : TAKE_OPTIONAL_LOCK();
12810 :
12811 8933 : if (mapping.size() < 2)
12812 0 : return OGRERR_FAILURE;
12813 8933 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12814 8933 : d->m_axisMapping = mapping;
12815 8933 : return OGRERR_NONE;
12816 : }
12817 :
12818 : /************************************************************************/
12819 : /* OSRSetDataAxisToSRSAxisMapping() */
12820 : /************************************************************************/
12821 :
12822 : /** \brief Set a custom data axis to CRS axis mapping.
12823 : *
12824 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12825 : *
12826 : * This is the same as the C++ method
12827 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12828 : *
12829 : * @since GDAL 3.1
12830 : */
12831 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12832 : int nMappingSize, const int *panMapping)
12833 : {
12834 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12835 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12836 : OGRERR_FAILURE);
12837 :
12838 15 : if (nMappingSize < 0)
12839 0 : return OGRERR_FAILURE;
12840 :
12841 30 : std::vector<int> mapping(nMappingSize);
12842 15 : if (nMappingSize)
12843 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12844 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12845 15 : mapping);
12846 : }
12847 :
12848 : /************************************************************************/
12849 : /* GetAreaOfUse() */
12850 : /************************************************************************/
12851 :
12852 : /** \brief Return the area of use of the CRS.
12853 : *
12854 : * This method is the same as the OSRGetAreaOfUse() function.
12855 : *
12856 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12857 : * longitude, expressed in degree. Might be NULL. If the returned value is
12858 : * -1000, the bounding box is unknown.
12859 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12860 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12861 : * the bounding box is unknown.
12862 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12863 : * longitude, expressed in degree. Might be NULL. If the returned value is
12864 : * -1000, the bounding box is unknown.
12865 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12866 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12867 : * the bounding box is unknown.
12868 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12869 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12870 : * invalidated by further calls.
12871 : * @return true in case of success
12872 : * @since GDAL 3.0
12873 : */
12874 52 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12875 : double *pdfSouthLatitudeDeg,
12876 : double *pdfEastLongitudeDeg,
12877 : double *pdfNorthLatitudeDeg,
12878 : const char **ppszAreaName) const
12879 : {
12880 104 : TAKE_OPTIONAL_LOCK();
12881 :
12882 52 : d->refreshProjObj();
12883 52 : if (!d->m_pj_crs)
12884 : {
12885 0 : return false;
12886 : }
12887 52 : d->demoteFromBoundCRS();
12888 52 : const char *pszAreaName = nullptr;
12889 52 : int bSuccess = proj_get_area_of_use(
12890 52 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12891 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12892 : &pszAreaName);
12893 52 : d->undoDemoteFromBoundCRS();
12894 52 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12895 52 : if (ppszAreaName)
12896 1 : *ppszAreaName = d->m_osAreaName.c_str();
12897 52 : return CPL_TO_BOOL(bSuccess);
12898 : }
12899 :
12900 : /************************************************************************/
12901 : /* GetAreaOfUse() */
12902 : /************************************************************************/
12903 :
12904 : /** \brief Return the area of use of the CRS.
12905 : *
12906 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12907 : *
12908 : * @since GDAL 3.0
12909 : */
12910 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12911 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12912 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12913 : {
12914 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12915 :
12916 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12917 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12918 1 : pdfNorthLatitudeDeg, ppszAreaName);
12919 : }
12920 :
12921 : /************************************************************************/
12922 : /* OSRGetCRSInfoListFromDatabase() */
12923 : /************************************************************************/
12924 :
12925 : /** \brief Enumerate CRS objects from the database.
12926 : *
12927 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12928 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12929 : *
12930 : * @param pszAuthName Authority name, used to restrict the search.
12931 : * Or NULL for all authorities.
12932 : * @param params Additional criteria. Must be set to NULL for now.
12933 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12934 : * the size of the result list. Might be NULL
12935 : * @return an array of OSRCRSInfo* pointers to be freed with
12936 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12937 : *
12938 : * @since GDAL 3.0
12939 : */
12940 : OSRCRSInfo **
12941 24 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12942 : CPL_UNUSED const OSRCRSListParameters *params,
12943 : int *pnOutResultCount)
12944 : {
12945 24 : int nResultCount = 0;
12946 24 : auto projList = proj_get_crs_info_list_from_database(
12947 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12948 24 : if (pnOutResultCount)
12949 24 : *pnOutResultCount = nResultCount;
12950 24 : if (!projList)
12951 : {
12952 0 : return nullptr;
12953 : }
12954 24 : auto res = new OSRCRSInfo *[nResultCount + 1];
12955 89181 : for (int i = 0; i < nResultCount; i++)
12956 : {
12957 89157 : res[i] = new OSRCRSInfo;
12958 178314 : res[i]->pszAuthName = projList[i]->auth_name
12959 89157 : ? CPLStrdup(projList[i]->auth_name)
12960 : : nullptr;
12961 89157 : res[i]->pszCode =
12962 89157 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12963 89157 : res[i]->pszName =
12964 89157 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12965 89157 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12966 89157 : switch (projList[i]->type)
12967 : {
12968 8727 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12969 8727 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12970 8727 : break;
12971 2811 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12972 2811 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12973 2811 : break;
12974 3066 : case PJ_TYPE_GEOCENTRIC_CRS:
12975 3066 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12976 3066 : break;
12977 67740 : case PJ_TYPE_PROJECTED_CRS:
12978 67740 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12979 67740 : break;
12980 2808 : case PJ_TYPE_VERTICAL_CRS:
12981 2808 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12982 2808 : break;
12983 4005 : case PJ_TYPE_COMPOUND_CRS:
12984 4005 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12985 4005 : break;
12986 0 : default:
12987 0 : break;
12988 : }
12989 89157 : res[i]->bDeprecated = projList[i]->deprecated;
12990 89157 : res[i]->bBboxValid = projList[i]->bbox_valid;
12991 89157 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12992 89157 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12993 89157 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12994 89157 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12995 178314 : res[i]->pszAreaName = projList[i]->area_name
12996 89157 : ? CPLStrdup(projList[i]->area_name)
12997 : : nullptr;
12998 89157 : res[i]->pszProjectionMethod =
12999 89157 : projList[i]->projection_method_name
13000 89157 : ? CPLStrdup(projList[i]->projection_method_name)
13001 : : nullptr;
13002 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
13003 : res[i]->pszCelestialBodyName =
13004 : projList[i]->celestial_body_name
13005 : ? CPLStrdup(projList[i]->celestial_body_name)
13006 : : nullptr;
13007 : #else
13008 89157 : res[i]->pszCelestialBodyName =
13009 89157 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13010 178314 : ? CPLStrdup("Earth")
13011 : : nullptr;
13012 : #endif
13013 : }
13014 24 : res[nResultCount] = nullptr;
13015 24 : proj_crs_info_list_destroy(projList);
13016 24 : return res;
13017 : }
13018 :
13019 : /************************************************************************/
13020 : /* OSRDestroyCRSInfoList() */
13021 : /************************************************************************/
13022 :
13023 : /** \brief Destroy the result returned by
13024 : * OSRGetCRSInfoListFromDatabase().
13025 : *
13026 : * @since GDAL 3.0
13027 : */
13028 24 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13029 : {
13030 24 : if (list)
13031 : {
13032 89181 : for (int i = 0; list[i] != nullptr; i++)
13033 : {
13034 89157 : CPLFree(list[i]->pszAuthName);
13035 89157 : CPLFree(list[i]->pszCode);
13036 89157 : CPLFree(list[i]->pszName);
13037 89157 : CPLFree(list[i]->pszAreaName);
13038 89157 : CPLFree(list[i]->pszProjectionMethod);
13039 89157 : CPLFree(list[i]->pszCelestialBodyName);
13040 89157 : delete list[i];
13041 : }
13042 24 : delete[] list;
13043 : }
13044 24 : }
13045 :
13046 : /************************************************************************/
13047 : /* OSRGetAuthorityListFromDatabase() */
13048 : /************************************************************************/
13049 :
13050 : /** \brief Return the list of CRS authorities used in the PROJ database.
13051 : *
13052 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13053 : *
13054 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13055 : *
13056 : * @return nullptr in case of error, or a NULL terminated list of strings to
13057 : * free with CSLDestroy()
13058 : * @since GDAL 3.10
13059 : */
13060 4 : char **OSRGetAuthorityListFromDatabase()
13061 : {
13062 : PROJ_STRING_LIST list =
13063 4 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13064 4 : if (!list)
13065 : {
13066 0 : return nullptr;
13067 : }
13068 4 : int count = 0;
13069 24 : while (list[count])
13070 20 : ++count;
13071 4 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13072 24 : for (int i = 0; i < count; ++i)
13073 20 : res[i] = CPLStrdup(list[i]);
13074 4 : proj_string_list_destroy(list);
13075 4 : return res;
13076 : }
13077 :
13078 : /************************************************************************/
13079 : /* UpdateCoordinateSystemFromGeogCRS() */
13080 : /************************************************************************/
13081 :
13082 : /*! @cond Doxygen_Suppress */
13083 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13084 : *
13085 : * @since GDAL 3.1
13086 : */
13087 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13088 : {
13089 1 : TAKE_OPTIONAL_LOCK();
13090 :
13091 1 : d->refreshProjObj();
13092 1 : if (!d->m_pj_crs)
13093 0 : return;
13094 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13095 0 : return;
13096 1 : if (GetAxesCount() == 3)
13097 0 : return;
13098 1 : auto ctxt = d->getPROJContext();
13099 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13100 1 : if (!baseCRS)
13101 0 : return;
13102 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13103 1 : if (!baseCRSCS)
13104 : {
13105 0 : proj_destroy(baseCRS);
13106 0 : return;
13107 : }
13108 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13109 : {
13110 0 : proj_destroy(baseCRSCS);
13111 0 : proj_destroy(baseCRS);
13112 0 : return;
13113 : }
13114 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13115 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13116 : {
13117 0 : proj_destroy(baseCRSCS);
13118 0 : proj_destroy(baseCRS);
13119 0 : proj_destroy(projCS);
13120 0 : return;
13121 : }
13122 :
13123 : PJ_AXIS_DESCRIPTION axis[3];
13124 4 : for (int i = 0; i < 3; i++)
13125 : {
13126 3 : const char *name = nullptr;
13127 3 : const char *abbreviation = nullptr;
13128 3 : const char *direction = nullptr;
13129 3 : double unit_conv_factor = 0;
13130 3 : const char *unit_name = nullptr;
13131 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13132 : &abbreviation, &direction, &unit_conv_factor,
13133 : &unit_name, nullptr, nullptr);
13134 3 : axis[i].name = CPLStrdup(name);
13135 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13136 3 : axis[i].direction = CPLStrdup(direction);
13137 3 : axis[i].unit_name = CPLStrdup(unit_name);
13138 3 : axis[i].unit_conv_factor = unit_conv_factor;
13139 3 : axis[i].unit_type = PJ_UT_LINEAR;
13140 : }
13141 1 : proj_destroy(baseCRSCS);
13142 1 : proj_destroy(projCS);
13143 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13144 4 : for (int i = 0; i < 3; i++)
13145 : {
13146 3 : CPLFree(axis[i].name);
13147 3 : CPLFree(axis[i].abbreviation);
13148 3 : CPLFree(axis[i].direction);
13149 3 : CPLFree(axis[i].unit_name);
13150 : }
13151 1 : if (!cs)
13152 : {
13153 0 : proj_destroy(baseCRS);
13154 0 : return;
13155 : }
13156 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13157 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13158 : conversion, cs);
13159 1 : proj_destroy(baseCRS);
13160 1 : proj_destroy(conversion);
13161 1 : proj_destroy(cs);
13162 1 : d->setPjCRS(crs);
13163 : }
13164 :
13165 : /*! @endcond */
13166 :
13167 : /************************************************************************/
13168 : /* PromoteTo3D() */
13169 : /************************************************************************/
13170 :
13171 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13172 : *
13173 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13174 : * units.
13175 : *
13176 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13177 : * be used.
13178 : * @return OGRERR_NONE if no error occurred.
13179 : * @since GDAL 3.1 and PROJ 6.3
13180 : */
13181 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13182 : {
13183 84 : TAKE_OPTIONAL_LOCK();
13184 :
13185 42 : d->refreshProjObj();
13186 42 : if (!d->m_pj_crs)
13187 0 : return OGRERR_FAILURE;
13188 : auto newPj =
13189 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13190 42 : if (!newPj)
13191 0 : return OGRERR_FAILURE;
13192 42 : d->setPjCRS(newPj);
13193 42 : return OGRERR_NONE;
13194 : }
13195 :
13196 : /************************************************************************/
13197 : /* OSRPromoteTo3D() */
13198 : /************************************************************************/
13199 :
13200 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13201 : *
13202 : * See OGRSpatialReference::PromoteTo3D()
13203 : *
13204 : * @since GDAL 3.1 and PROJ 6.3
13205 : */
13206 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13207 : {
13208 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13209 :
13210 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13211 : }
13212 :
13213 : /************************************************************************/
13214 : /* DemoteTo2D() */
13215 : /************************************************************************/
13216 :
13217 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13218 : *
13219 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13220 : * be used.
13221 : * @return OGRERR_NONE if no error occurred.
13222 : * @since GDAL 3.2 and PROJ 6.3
13223 : */
13224 47 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13225 : {
13226 94 : TAKE_OPTIONAL_LOCK();
13227 :
13228 47 : d->refreshProjObj();
13229 47 : if (!d->m_pj_crs)
13230 0 : return OGRERR_FAILURE;
13231 : auto newPj =
13232 47 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13233 47 : if (!newPj)
13234 0 : return OGRERR_FAILURE;
13235 47 : d->setPjCRS(newPj);
13236 47 : return OGRERR_NONE;
13237 : }
13238 :
13239 : /************************************************************************/
13240 : /* OSRDemoteTo2D() */
13241 : /************************************************************************/
13242 :
13243 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13244 : *
13245 : * See OGRSpatialReference::DemoteTo2D()
13246 : *
13247 : * @since GDAL 3.2 and PROJ 6.3
13248 : */
13249 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13250 : {
13251 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13252 :
13253 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13254 : }
13255 :
13256 : /************************************************************************/
13257 : /* GetEPSGGeogCS() */
13258 : /************************************************************************/
13259 :
13260 : /** Try to establish what the EPSG code for this coordinate systems
13261 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13262 : *
13263 : * @return EPSG code
13264 : */
13265 :
13266 342 : int OGRSpatialReference::GetEPSGGeogCS() const
13267 :
13268 : {
13269 684 : TAKE_OPTIONAL_LOCK();
13270 :
13271 : /* -------------------------------------------------------------------- */
13272 : /* Check axis order. */
13273 : /* -------------------------------------------------------------------- */
13274 684 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13275 342 : if (!poGeogCRS)
13276 0 : return -1;
13277 :
13278 342 : bool ret = false;
13279 342 : poGeogCRS->d->demoteFromBoundCRS();
13280 342 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13281 342 : poGeogCRS->d->m_pj_crs);
13282 342 : poGeogCRS->d->undoDemoteFromBoundCRS();
13283 342 : if (cs)
13284 : {
13285 342 : const char *pszDirection = nullptr;
13286 342 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13287 : &pszDirection, nullptr, nullptr, nullptr,
13288 342 : nullptr))
13289 : {
13290 342 : if (EQUAL(pszDirection, "north"))
13291 : {
13292 143 : ret = true;
13293 : }
13294 : }
13295 :
13296 342 : proj_destroy(cs);
13297 : }
13298 342 : if (!ret)
13299 199 : return -1;
13300 :
13301 : /* -------------------------------------------------------------------- */
13302 : /* Do we already have it? */
13303 : /* -------------------------------------------------------------------- */
13304 143 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13305 143 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13306 66 : return atoi(GetAuthorityCode("GEOGCS"));
13307 :
13308 : /* -------------------------------------------------------------------- */
13309 : /* Get the datum and geogcs names. */
13310 : /* -------------------------------------------------------------------- */
13311 :
13312 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13313 77 : const char *pszDatum = GetAttrValue("DATUM");
13314 :
13315 : // We can only operate on coordinate systems with a geogcs.
13316 154 : OGRSpatialReference oSRSTmp;
13317 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13318 : {
13319 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13320 : // export to WKT1, so try to extract the geographic CRS through PROJ
13321 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13322 1 : oSRSTmp.CopyGeogCSFrom(this);
13323 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13324 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13325 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13326 : {
13327 0 : return -1;
13328 : }
13329 : }
13330 :
13331 : // Lookup geographic CRS name
13332 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13333 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13334 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13335 77 : if (list)
13336 : {
13337 77 : const auto listSize = proj_list_get_count(list);
13338 77 : if (listSize == 1)
13339 : {
13340 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13341 49 : if (crs)
13342 : {
13343 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13344 49 : const char *pszCode = proj_get_id_code(crs, 0);
13345 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13346 : {
13347 47 : const int nCode = atoi(pszCode);
13348 47 : proj_destroy(crs);
13349 47 : proj_list_destroy(list);
13350 47 : return nCode;
13351 : }
13352 2 : proj_destroy(crs);
13353 : }
13354 : }
13355 30 : proj_list_destroy(list);
13356 : }
13357 :
13358 : /* -------------------------------------------------------------------- */
13359 : /* Is this a "well known" geographic coordinate system? */
13360 : /* -------------------------------------------------------------------- */
13361 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13362 30 : strstr(pszDatum, "WGS") ||
13363 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13364 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13365 90 : strstr(pszDatum, "World Geodetic System") ||
13366 30 : strstr(pszDatum, "World_Geodetic_System");
13367 :
13368 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13369 30 : strstr(pszDatum, "NAD") ||
13370 30 : strstr(pszGEOGCS, "North American") ||
13371 30 : strstr(pszGEOGCS, "North_American") ||
13372 90 : strstr(pszDatum, "North American") ||
13373 30 : strstr(pszDatum, "North_American");
13374 :
13375 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13376 0 : return 4326;
13377 :
13378 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13379 0 : return 4322;
13380 :
13381 : // This is questionable as there are several 'flavors' of NAD83 that
13382 : // are not the same as 4269
13383 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13384 0 : return 4269;
13385 :
13386 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13387 0 : return 4267;
13388 :
13389 : /* -------------------------------------------------------------------- */
13390 : /* If we know the datum, associate the most likely GCS with */
13391 : /* it. */
13392 : /* -------------------------------------------------------------------- */
13393 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13394 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13395 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13396 0 : GetPrimeMeridian() == 0.0)
13397 : {
13398 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13399 :
13400 0 : if (nDatum >= 6000 && nDatum <= 6999)
13401 0 : return nDatum - 2000;
13402 : }
13403 :
13404 30 : return -1;
13405 : }
13406 :
13407 : /************************************************************************/
13408 : /* SetCoordinateEpoch() */
13409 : /************************************************************************/
13410 :
13411 : /** Set the coordinate epoch, as decimal year.
13412 : *
13413 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13414 : * change with time. To be unambiguous the coordinates must always be qualified
13415 : * with the epoch at which they are valid. The coordinate epoch is not
13416 : * necessarily the epoch at which the observation was collected.
13417 : *
13418 : * Pedantically the coordinate epoch of an observation belongs to the
13419 : * observation, and not to the CRS, however it is often more practical to
13420 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13421 : * CRS (see IsDynamic())
13422 : *
13423 : * This method is the same as the OSRSetCoordinateEpoch() function.
13424 : *
13425 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13426 : * @since OGR 3.4
13427 : */
13428 :
13429 841 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13430 : {
13431 841 : d->m_coordinateEpoch = dfCoordinateEpoch;
13432 841 : }
13433 :
13434 : /************************************************************************/
13435 : /* OSRSetCoordinateEpoch() */
13436 : /************************************************************************/
13437 :
13438 : /** \brief Set the coordinate epoch, as decimal year.
13439 : *
13440 : * See OGRSpatialReference::SetCoordinateEpoch()
13441 : *
13442 : * @since OGR 3.4
13443 : */
13444 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13445 : {
13446 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13447 :
13448 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13449 31 : dfCoordinateEpoch);
13450 : }
13451 :
13452 : /************************************************************************/
13453 : /* GetCoordinateEpoch() */
13454 : /************************************************************************/
13455 :
13456 : /** Return the coordinate epoch, as decimal year.
13457 : *
13458 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13459 : * change with time. To be unambiguous the coordinates must always be qualified
13460 : * with the epoch at which they are valid. The coordinate epoch is not
13461 : * necessarily the epoch at which the observation was collected.
13462 : *
13463 : * Pedantically the coordinate epoch of an observation belongs to the
13464 : * observation, and not to the CRS, however it is often more practical to
13465 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13466 : * CRS (see IsDynamic())
13467 : *
13468 : * This method is the same as the OSRGetCoordinateEpoch() function.
13469 : *
13470 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13471 : * if not set, or relevant.
13472 : * @since OGR 3.4
13473 : */
13474 :
13475 13158 : double OGRSpatialReference::GetCoordinateEpoch() const
13476 : {
13477 13158 : return d->m_coordinateEpoch;
13478 : }
13479 :
13480 : /************************************************************************/
13481 : /* OSRGetCoordinateEpoch() */
13482 : /************************************************************************/
13483 :
13484 : /** \brief Get the coordinate epoch, as decimal year.
13485 : *
13486 : * See OGRSpatialReference::GetCoordinateEpoch()
13487 : *
13488 : * @since OGR 3.4
13489 : */
13490 687 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13491 : {
13492 687 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13493 :
13494 687 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13495 : }
|