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 282385 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 282385 : }
73 :
74 : Listener(const Listener &) = delete;
75 : Listener &operator=(const Listener &) = delete;
76 :
77 : void notifyChange(OGR_SRSNode *) override;
78 : };
79 :
80 : OGRSpatialReference *m_poSelf = nullptr;
81 : PJ *m_pj_crs = nullptr;
82 :
83 : // Temporary state used for object construction
84 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85 : CPLString m_osPrimeMeridianName{};
86 : CPLString m_osAngularUnits{};
87 : CPLString m_osLinearUnits{};
88 : CPLString m_osAxisName[3]{};
89 :
90 : std::vector<std::string> m_wktImportWarnings{};
91 : std::vector<std::string> m_wktImportErrors{};
92 : CPLString m_osAreaName{};
93 : CPLString m_celestialBodyName{};
94 :
95 : bool m_bIsThreadSafe = false;
96 : bool m_bNodesChanged = false;
97 : bool m_bNodesWKT2 = false;
98 : OGR_SRSNode *m_poRoot = nullptr;
99 :
100 : double dfFromGreenwich = 0.0;
101 : double dfToMeter = 0.0;
102 : double dfToDegrees = 0.0;
103 : double m_dfAngularUnitToRadian = 0.0;
104 :
105 : int nRefCount = 1;
106 : int bNormInfoSet = FALSE;
107 :
108 : PJ *m_pj_geod_base_crs_temp = nullptr;
109 : PJ *m_pj_proj_crs_cs_temp = nullptr;
110 :
111 : bool m_pj_crs_modified_during_demote = false;
112 : PJ *m_pj_bound_crs_target = nullptr;
113 : PJ *m_pj_bound_crs_co = nullptr;
114 : PJ *m_pj_crs_backup = nullptr;
115 : OGR_SRSNode *m_poRootBackup = nullptr;
116 :
117 : bool m_bMorphToESRI = false;
118 : bool m_bHasCenterLong = false;
119 :
120 : std::shared_ptr<Listener> m_poListener{};
121 :
122 : std::recursive_mutex m_mutex{};
123 :
124 : OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125 : std::vector<int> m_axisMapping{1, 2, 3};
126 :
127 : double m_coordinateEpoch = 0; // as decimal year
128 :
129 : explicit Private(OGRSpatialReference *poSelf);
130 : ~Private();
131 : Private(const Private &) = delete;
132 : Private &operator=(const Private &) = delete;
133 :
134 2 : void SetThreadSafe()
135 : {
136 2 : m_bIsThreadSafe = true;
137 2 : }
138 :
139 : void clear();
140 : void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141 : void setRoot(OGR_SRSNode *poRoot);
142 : void refreshProjObj();
143 : void nodesChanged();
144 : void refreshRootFromProjObj(bool bForceWKT2);
145 : void invalidateNodes();
146 :
147 : void setMorphToESRI(bool b);
148 :
149 : PJ *getGeodBaseCRS();
150 : PJ *getProjCRSCoordSys();
151 :
152 : const char *getProjCRSName();
153 : OGRErr replaceConversionAndUnref(PJ *conv);
154 :
155 : void demoteFromBoundCRS();
156 : void undoDemoteFromBoundCRS();
157 :
158 1306230 : PJ_CONTEXT *getPROJContext()
159 : {
160 1306230 : 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 18467700 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 18467700 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 18467700 : }
184 :
185 18467700 : ~OptionalLockGuard()
186 18467700 : {
187 18467700 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 18467700 : }
190 : };
191 :
192 18467700 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 18467700 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2448570 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2448570 : m_poObj->nodesChanged();
201 2448570 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 282385 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 282385 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 282385 : 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 282384 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 282385 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 282385 : : 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 282385 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
233 282385 : }
234 :
235 1125580 : 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 281395 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
241 : PJ_CONTEXT *ctxt;
242 281395 : if (GDALThreadLocalDatasetCacheIsInDestruction())
243 : {
244 185 : pj_context_to_destroy = proj_context_create();
245 185 : ctxt = pj_context_to_destroy;
246 : }
247 : else
248 : {
249 281210 : ctxt = getPROJContext();
250 : }
251 :
252 281395 : proj_assign_context(m_pj_crs, ctxt);
253 281395 : proj_destroy(m_pj_crs);
254 :
255 281395 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
256 281395 : proj_destroy(m_pj_geod_base_crs_temp);
257 :
258 281395 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
259 281395 : proj_destroy(m_pj_proj_crs_cs_temp);
260 :
261 281395 : proj_assign_context(m_pj_bound_crs_target, ctxt);
262 281395 : proj_destroy(m_pj_bound_crs_target);
263 :
264 281395 : proj_assign_context(m_pj_bound_crs_co, ctxt);
265 281395 : proj_destroy(m_pj_bound_crs_co);
266 :
267 281395 : proj_assign_context(m_pj_crs_backup, ctxt);
268 281395 : proj_destroy(m_pj_crs_backup);
269 :
270 281395 : delete m_poRootBackup;
271 281395 : delete m_poRoot;
272 281395 : proj_context_destroy(pj_context_to_destroy);
273 281395 : }
274 :
275 137403 : void OGRSpatialReference::Private::clear()
276 : {
277 137403 : proj_assign_context(m_pj_crs, getPROJContext());
278 137403 : proj_destroy(m_pj_crs);
279 137403 : m_pj_crs = nullptr;
280 :
281 137403 : delete m_poRoot;
282 137403 : m_poRoot = nullptr;
283 137403 : m_bNodesChanged = false;
284 :
285 137403 : m_wktImportWarnings.clear();
286 137403 : m_wktImportErrors.clear();
287 :
288 137403 : m_pj_crs_modified_during_demote = false;
289 137403 : m_pjType = PJ_TYPE_UNKNOWN;
290 137403 : m_osPrimeMeridianName.clear();
291 137403 : m_osAngularUnits.clear();
292 137403 : m_osLinearUnits.clear();
293 :
294 137403 : bNormInfoSet = FALSE;
295 137403 : dfFromGreenwich = 1.0;
296 137403 : dfToMeter = 1.0;
297 137403 : dfToDegrees = 1.0;
298 137403 : m_dfAngularUnitToRadian = 0.0;
299 :
300 137403 : m_bMorphToESRI = false;
301 137403 : m_bHasCenterLong = false;
302 :
303 137403 : m_coordinateEpoch = 0.0;
304 137403 : }
305 :
306 30501 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
307 : {
308 30501 : m_poRoot = poRoot;
309 30501 : if (m_poRoot)
310 : {
311 30501 : m_poRoot->RegisterListener(m_poListener);
312 : }
313 30501 : nodesChanged();
314 30501 : }
315 :
316 201275 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
317 : bool doRefreshAxisMapping)
318 : {
319 201275 : 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 201275 : proj_assign_context(m_pj_crs, ctxt);
337 201275 : proj_destroy(m_pj_crs);
338 201275 : m_pj_crs = pj_crsIn;
339 201275 : if (m_pj_crs)
340 : {
341 201222 : m_pjType = proj_get_type(m_pj_crs);
342 : }
343 201275 : if (m_pj_crs_backup)
344 : {
345 21 : m_pj_crs_modified_during_demote = true;
346 : }
347 201275 : invalidateNodes();
348 201275 : if (doRefreshAxisMapping)
349 : {
350 201255 : refreshAxisMapping();
351 : }
352 201275 : }
353 :
354 781589 : void OGRSpatialReference::Private::refreshProjObj()
355 : {
356 781589 : if (m_bNodesChanged && m_poRoot)
357 : {
358 9012 : char *pszWKT = nullptr;
359 9012 : m_poRoot->exportToWkt(&pszWKT);
360 9012 : auto poRootBackup = m_poRoot;
361 9012 : m_poRoot = nullptr;
362 9012 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
363 9012 : clear();
364 9012 : m_coordinateEpoch = dfCoordinateEpochBackup;
365 9012 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
366 :
367 9012 : 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 9012 : PROJ_STRING_LIST warnings = nullptr;
374 9012 : PROJ_STRING_LIST errors = nullptr;
375 9012 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
376 : &warnings, &errors));
377 17641 : for (auto iter = warnings; iter && *iter; ++iter)
378 : {
379 8629 : m_wktImportWarnings.push_back(*iter);
380 : }
381 9228 : for (auto iter = errors; iter && *iter; ++iter)
382 : {
383 216 : m_wktImportErrors.push_back(*iter);
384 : }
385 9012 : proj_string_list_destroy(warnings);
386 9012 : proj_string_list_destroy(errors);
387 :
388 9012 : CPLFree(pszWKT);
389 :
390 9012 : m_poRoot = poRootBackup;
391 9012 : m_bNodesChanged = false;
392 : }
393 781589 : }
394 :
395 32642 : void OGRSpatialReference::Private::refreshRootFromProjObj(bool bForceWKT2)
396 : {
397 32642 : CPLAssert(m_poRoot == nullptr);
398 :
399 32642 : if (m_pj_crs)
400 : {
401 60912 : CPLStringList aosOptions;
402 30456 : if (!m_bMorphToESRI)
403 : {
404 30452 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
405 30452 : aosOptions.SetNameValue("MULTILINE", "NO");
406 : }
407 30456 : aosOptions.SetNameValue("STRICT", "NO");
408 :
409 30456 : const char *pszWKT = nullptr;
410 30456 : if (!bForceWKT2)
411 : {
412 30438 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
413 30438 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
414 30438 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
415 30438 : aosOptions.List());
416 30438 : m_bNodesWKT2 = false;
417 : }
418 30456 : if (!m_bMorphToESRI && pszWKT == nullptr)
419 : {
420 114 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
421 114 : aosOptions.List());
422 114 : m_bNodesWKT2 = true;
423 : }
424 30456 : if (pszWKT)
425 : {
426 30456 : auto root = new OGR_SRSNode();
427 30456 : setRoot(root);
428 30456 : root->importFromWkt(&pszWKT);
429 30456 : m_bNodesChanged = false;
430 : }
431 : }
432 32642 : }
433 :
434 235373 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
435 : {
436 235373 : const char *pszName1 = nullptr;
437 235373 : const char *pszDirection1 = nullptr;
438 235373 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
439 : nullptr, nullptr, nullptr, nullptr);
440 235373 : const char *pszName2 = nullptr;
441 235373 : const char *pszDirection2 = nullptr;
442 235373 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
443 : nullptr, nullptr, nullptr, nullptr);
444 235373 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
445 104580 : EQUAL(pszDirection2, "east"))
446 : {
447 102920 : return true;
448 : }
449 132453 : if (pszDirection1 && pszDirection2 &&
450 132453 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
451 130810 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
452 3424 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
453 1395 : STARTS_WITH_CI(pszName2, "easting"))
454 : {
455 1395 : return true;
456 : }
457 131058 : return false;
458 : }
459 :
460 296945 : void OGRSpatialReference::Private::refreshAxisMapping()
461 : {
462 296945 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
463 62149 : return;
464 :
465 234796 : bool doUndoDemote = false;
466 234796 : if (m_pj_crs_backup == nullptr)
467 : {
468 234775 : doUndoDemote = true;
469 234775 : demoteFromBoundCRS();
470 : }
471 234796 : const auto ctxt = getPROJContext();
472 234796 : PJ *horizCRS = nullptr;
473 234796 : int axisCount = 0;
474 234796 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
475 : {
476 311 : axisCount = 1;
477 : }
478 234485 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
479 : {
480 1272 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
481 1272 : 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 1272 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
492 1272 : if (vertCRS)
493 : {
494 1269 : 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 1269 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
505 1269 : if (cs)
506 : {
507 1269 : axisCount += proj_cs_get_axis_count(ctxt, cs);
508 1269 : proj_destroy(cs);
509 : }
510 1269 : proj_destroy(vertCRS);
511 : }
512 : }
513 : else
514 : {
515 233213 : horizCRS = m_pj_crs;
516 : }
517 :
518 234796 : bool bSwitchForGisFriendlyOrder = false;
519 234796 : if (horizCRS)
520 : {
521 234482 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
522 234482 : if (cs)
523 : {
524 234482 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
525 234482 : axisCount += nHorizCSAxisCount;
526 234482 : if (nHorizCSAxisCount >= 2)
527 : {
528 234472 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
529 : }
530 234482 : proj_destroy(cs);
531 : }
532 : }
533 234796 : if (horizCRS != m_pj_crs)
534 : {
535 1583 : proj_destroy(horizCRS);
536 : }
537 234796 : if (doUndoDemote)
538 : {
539 234775 : undoDemoteFromBoundCRS();
540 : }
541 :
542 234796 : m_axisMapping.resize(axisCount);
543 234796 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
544 70400 : !bSwitchForGisFriendlyOrder)
545 : {
546 608392 : for (int i = 0; i < axisCount; i++)
547 : {
548 406107 : m_axisMapping[i] = i + 1;
549 202285 : }
550 : }
551 : else
552 : {
553 32511 : m_axisMapping[0] = 2;
554 32511 : m_axisMapping[1] = 1;
555 32511 : if (axisCount == 3)
556 : {
557 511 : m_axisMapping[2] = 3;
558 : }
559 : }
560 : }
561 :
562 2479070 : void OGRSpatialReference::Private::nodesChanged()
563 : {
564 2479070 : m_bNodesChanged = true;
565 2479070 : }
566 :
567 201550 : void OGRSpatialReference::Private::invalidateNodes()
568 : {
569 201550 : delete m_poRoot;
570 201550 : m_poRoot = nullptr;
571 201550 : m_bNodesChanged = false;
572 201550 : }
573 :
574 257 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
575 : {
576 257 : invalidateNodes();
577 257 : m_bMorphToESRI = b;
578 257 : }
579 :
580 688801 : void OGRSpatialReference::Private::demoteFromBoundCRS()
581 : {
582 688801 : CPLAssert(m_pj_bound_crs_target == nullptr);
583 688801 : CPLAssert(m_pj_bound_crs_co == nullptr);
584 688801 : CPLAssert(m_poRootBackup == nullptr);
585 688801 : CPLAssert(m_pj_crs_backup == nullptr);
586 :
587 688801 : m_pj_crs_modified_during_demote = false;
588 :
589 688801 : if (m_pjType == PJ_TYPE_BOUND_CRS)
590 : {
591 2753 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
592 2753 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
593 2753 : m_pj_bound_crs_co =
594 2753 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
595 :
596 2753 : m_poRootBackup = m_poRoot;
597 2753 : m_poRoot = nullptr;
598 2753 : m_pj_crs_backup = m_pj_crs;
599 2753 : m_pj_crs = baseCRS;
600 2753 : m_pjType = proj_get_type(m_pj_crs);
601 : }
602 688801 : }
603 :
604 688801 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
605 : {
606 688801 : if (m_pj_bound_crs_target)
607 : {
608 2753 : CPLAssert(m_poRoot == nullptr);
609 2753 : CPLAssert(m_pj_crs);
610 2753 : if (!m_pj_crs_modified_during_demote)
611 : {
612 2733 : proj_destroy(m_pj_crs);
613 2733 : m_pj_crs = m_pj_crs_backup;
614 2733 : m_pjType = proj_get_type(m_pj_crs);
615 2733 : 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 688801 : m_poRootBackup = nullptr;
631 688801 : m_pj_crs_backup = nullptr;
632 688801 : proj_destroy(m_pj_bound_crs_target);
633 688801 : m_pj_bound_crs_target = nullptr;
634 688801 : proj_destroy(m_pj_bound_crs_co);
635 688801 : m_pj_bound_crs_co = nullptr;
636 688801 : m_pj_crs_modified_during_demote = false;
637 688801 : }
638 :
639 174916 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
640 : const char *pszTargetKey)
641 : {
642 174916 : if (pszTargetKey)
643 : {
644 62460 : demoteFromBoundCRS();
645 62460 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
646 32806 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
647 29732 : EQUAL(pszTargetKey, "GEOGCS"))
648 : {
649 7194 : pszTargetKey = nullptr;
650 : }
651 55266 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
652 20 : EQUAL(pszTargetKey, "GEOCCS"))
653 : {
654 0 : pszTargetKey = nullptr;
655 : }
656 55266 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
657 31416 : EQUAL(pszTargetKey, "PROJCS"))
658 : {
659 4416 : pszTargetKey = nullptr;
660 : }
661 50850 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
662 4 : EQUAL(pszTargetKey, "VERT_CS"))
663 : {
664 2 : pszTargetKey = nullptr;
665 : }
666 62460 : undoDemoteFromBoundCRS();
667 : }
668 174916 : return pszTargetKey;
669 : }
670 :
671 10038 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
672 : {
673 10038 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
674 9985 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
675 : {
676 53 : return m_pj_crs;
677 : }
678 :
679 9985 : auto ctxt = getPROJContext();
680 9985 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
681 : {
682 4610 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
683 4610 : proj_destroy(m_pj_geod_base_crs_temp);
684 4610 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
685 4610 : return m_pj_geod_base_crs_temp;
686 : }
687 :
688 5375 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
689 5375 : proj_destroy(m_pj_geod_base_crs_temp);
690 5375 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
691 : nullptr, 0);
692 5375 : 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 5375 : proj_destroy(cs);
697 :
698 5375 : return m_pj_geod_base_crs_temp;
699 : }
700 :
701 5575 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
702 : {
703 5575 : auto ctxt = getPROJContext();
704 5575 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
705 : {
706 4595 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
707 4595 : proj_destroy(m_pj_proj_crs_cs_temp);
708 4595 : m_pj_proj_crs_cs_temp =
709 4595 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
710 4595 : return m_pj_proj_crs_cs_temp;
711 : }
712 :
713 980 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
714 980 : proj_destroy(m_pj_proj_crs_cs_temp);
715 980 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
716 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
717 980 : return m_pj_proj_crs_cs_temp;
718 : }
719 :
720 5628 : const char *OGRSpatialReference::Private::getProjCRSName()
721 : {
722 5628 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
723 : {
724 4611 : return proj_get_name(m_pj_crs);
725 : }
726 :
727 1017 : return "unnamed";
728 : }
729 :
730 1389 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
731 : {
732 1389 : refreshProjObj();
733 :
734 1389 : demoteFromBoundCRS();
735 :
736 : auto projCRS =
737 1389 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
738 1389 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
739 1389 : proj_destroy(conv);
740 :
741 1389 : setPjCRS(projCRS);
742 :
743 1389 : undoDemoteFromBoundCRS();
744 1389 : return OGRERR_NONE;
745 : }
746 :
747 : /************************************************************************/
748 : /* ToPointer() */
749 : /************************************************************************/
750 :
751 27684 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
752 : {
753 27684 : return OGRSpatialReference::FromHandle(hSRS);
754 : }
755 :
756 : /************************************************************************/
757 : /* ToHandle() */
758 : /************************************************************************/
759 :
760 4938 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
761 : {
762 4938 : 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 279636 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
831 279636 : : d(new Private(this))
832 : {
833 279636 : if (pszWKT != nullptr)
834 1253 : importFromWkt(pszWKT);
835 279636 : }
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 3426 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
860 :
861 : {
862 3426 : OGRSpatialReference *poSRS = new OGRSpatialReference();
863 :
864 3426 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
865 : {
866 66 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
867 : {
868 0 : delete poSRS;
869 0 : poSRS = nullptr;
870 : }
871 : }
872 :
873 3426 : return ToHandle(poSRS);
874 : }
875 :
876 : /************************************************************************/
877 : /* OGRSpatialReference() */
878 : /************************************************************************/
879 :
880 : /** Copy constructor. See also Clone().
881 : * @param oOther other spatial reference
882 : */
883 2749 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
884 2749 : : d(new Private(this))
885 : {
886 2749 : *this = oOther;
887 2749 : }
888 :
889 : /************************************************************************/
890 : /* OGRSpatialReference() */
891 : /************************************************************************/
892 :
893 : /** Move constructor.
894 : * @param oOther other spatial reference
895 : */
896 1 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
897 1 : : d(std::move(oOther.d))
898 : {
899 1 : }
900 :
901 : /************************************************************************/
902 : /* ~OGRSpatialReference() */
903 : /************************************************************************/
904 :
905 : /**
906 : * \brief OGRSpatialReference destructor.
907 : *
908 : * The C function OSRDestroySpatialReference() does the same thing as this
909 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
910 : *
911 : * @deprecated
912 : */
913 :
914 341897 : OGRSpatialReference::~OGRSpatialReference()
915 :
916 : {
917 341897 : }
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 9812 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
955 :
956 : {
957 9812 : delete ToPointer(hSRS);
958 9812 : }
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 128391 : void OGRSpatialReference::Clear()
972 :
973 : {
974 128391 : d->clear();
975 128391 : }
976 :
977 : /************************************************************************/
978 : /* operator=() */
979 : /************************************************************************/
980 :
981 : /** Assignment operator.
982 : * @param oSource SRS to assign to *this
983 : * @return *this
984 : */
985 : OGRSpatialReference &
986 28642 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
987 :
988 : {
989 28642 : if (&oSource != this)
990 : {
991 28641 : 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 28641 : oSource.d->refreshProjObj();
998 28641 : if (oSource.d->m_pj_crs)
999 28187 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1000 28641 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1001 13565 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1002 15076 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1003 144 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1004 :
1005 28641 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1006 : }
1007 :
1008 28642 : 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 5341 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1021 :
1022 : {
1023 5341 : if (&oSource != this)
1024 : {
1025 5340 : d = std::move(oSource.d);
1026 : }
1027 :
1028 5341 : 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 3209750 : int OGRSpatialReference::Reference()
1069 :
1070 : {
1071 3209750 : 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 1095 : int OSRReference(OGRSpatialReferenceH hSRS)
1084 :
1085 : {
1086 1095 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1087 :
1088 1095 : 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 3252900 : int OGRSpatialReference::Dereference()
1104 :
1105 : {
1106 3252900 : 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 3252900 : 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 3252640 : void OGRSpatialReference::Release()
1156 :
1157 : {
1158 3252640 : if (Dereference() <= 0)
1159 43085 : delete this;
1160 3252640 : }
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 6924 : void OSRRelease(OGRSpatialReferenceH hSRS)
1172 :
1173 : {
1174 6924 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1175 :
1176 6924 : ToPointer(hSRS)->Release();
1177 : }
1178 :
1179 96048 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1180 : {
1181 96048 : TAKE_OPTIONAL_LOCK();
1182 :
1183 96048 : if (!d->m_poRoot)
1184 : {
1185 29218 : d->refreshRootFromProjObj(false);
1186 : }
1187 192096 : return d->m_poRoot;
1188 : }
1189 :
1190 8815 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1191 : {
1192 8815 : TAKE_OPTIONAL_LOCK();
1193 :
1194 8815 : if (!d->m_poRoot)
1195 : {
1196 3406 : d->refreshRootFromProjObj(false);
1197 : }
1198 17630 : 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 45 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1216 :
1217 : {
1218 45 : if (d->m_poRoot != poNewRoot)
1219 : {
1220 45 : delete d->m_poRoot;
1221 45 : d->setRoot(poNewRoot);
1222 : }
1223 45 : }
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 92900 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1248 :
1249 : {
1250 92900 : if (strstr(pszNodePath, "CONVERSION") && !d->m_bNodesWKT2)
1251 : {
1252 18 : d->invalidateNodes();
1253 18 : d->refreshRootFromProjObj(/* bForceWKT2 = */ true);
1254 : }
1255 :
1256 92900 : if (strchr(pszNodePath, '|') == nullptr)
1257 : {
1258 : // Fast path
1259 51581 : OGR_SRSNode *poNode = GetRoot();
1260 51581 : if (poNode)
1261 50361 : poNode = poNode->GetNode(pszNodePath);
1262 51581 : return poNode;
1263 : }
1264 :
1265 : char **papszPathTokens =
1266 41319 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1267 :
1268 41319 : if (CSLCount(papszPathTokens) < 1)
1269 : {
1270 0 : CSLDestroy(papszPathTokens);
1271 0 : return nullptr;
1272 : }
1273 :
1274 41319 : OGR_SRSNode *poNode = GetRoot();
1275 125774 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1276 : {
1277 84455 : poNode = poNode->GetNode(papszPathTokens[i]);
1278 : }
1279 :
1280 41319 : CSLDestroy(papszPathTokens);
1281 :
1282 41319 : return poNode;
1283 : }
1284 :
1285 : /**
1286 : * \brief Find named node in tree.
1287 : *
1288 : * This method does a pre-order traversal of the node tree searching for
1289 : * a node with this exact value (case insensitive), and returns it. Leaf
1290 : * nodes are not considered, under the assumption that they are just
1291 : * attribute value nodes.
1292 : *
1293 : * If a node appears more than once in the tree (such as UNIT for instance),
1294 : * the first encountered will be returned. Use GetNode() on a subtree to be
1295 : * more specific.
1296 : *
1297 : * @param pszNodePath the name of the node to search for. May contain multiple
1298 : * components such as "GEOGCS|UNIT".
1299 : *
1300 : * @return a pointer to the node found, or NULL if none.
1301 : */
1302 :
1303 : const OGR_SRSNode *
1304 84137 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1305 :
1306 : {
1307 : OGR_SRSNode *poNode =
1308 84137 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1309 :
1310 84137 : return poNode;
1311 : }
1312 :
1313 : /************************************************************************/
1314 : /* GetAttrValue() */
1315 : /************************************************************************/
1316 :
1317 : /**
1318 : * \brief Fetch indicated attribute of named node.
1319 : *
1320 : * This method uses GetAttrNode() to find the named node, and then extracts
1321 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1322 : * would return the second child of the UNIT node, which is normally the
1323 : * length of the linear unit in meters.
1324 : *
1325 : * This method does the same thing as the C function OSRGetAttrValue().
1326 : *
1327 : * @param pszNodeName the tree node to look for (case insensitive).
1328 : * @param iAttr the child of the node to fetch (zero based).
1329 : *
1330 : * @return the requested value, or NULL if it fails for any reason.
1331 : */
1332 :
1333 25174 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1334 : int iAttr) const
1335 :
1336 : {
1337 25174 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1338 25174 : if (poNode == nullptr)
1339 : {
1340 10787 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1341 : {
1342 14 : return GetAttrValue("METHOD", iAttr);
1343 : }
1344 10773 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1345 : {
1346 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1347 : }
1348 10773 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1349 : {
1350 1 : return GetAttrValue("PROJCRS", iAttr);
1351 : }
1352 10772 : return nullptr;
1353 : }
1354 :
1355 14387 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1356 0 : return nullptr;
1357 :
1358 14387 : return poNode->GetChild(iAttr)->GetValue();
1359 : }
1360 :
1361 : /************************************************************************/
1362 : /* OSRGetAttrValue() */
1363 : /************************************************************************/
1364 :
1365 : /**
1366 : * \brief Fetch indicated attribute of named node.
1367 : *
1368 : * This function is the same as OGRSpatialReference::GetAttrValue()
1369 : */
1370 36 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1371 : const char *pszKey, int iChild)
1372 :
1373 : {
1374 36 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1375 :
1376 36 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* GetName() */
1381 : /************************************************************************/
1382 :
1383 : /**
1384 : * \brief Return the CRS name.
1385 : *
1386 : * The returned value is only short lived and should not be used after other
1387 : * calls to methods on this object.
1388 : *
1389 : * @since GDAL 3.0
1390 : */
1391 :
1392 8997 : const char *OGRSpatialReference::GetName() const
1393 : {
1394 17994 : TAKE_OPTIONAL_LOCK();
1395 :
1396 8997 : d->refreshProjObj();
1397 8997 : if (!d->m_pj_crs)
1398 113 : return nullptr;
1399 8884 : const char *pszName = proj_get_name(d->m_pj_crs);
1400 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1401 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1402 : {
1403 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1404 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1405 : if (baseCRS)
1406 : {
1407 : pszName = proj_get_name(baseCRS);
1408 : // pszName still remains valid after proj_destroy(), since
1409 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1410 : proj_destroy(baseCRS);
1411 : }
1412 : }
1413 : #endif
1414 8884 : return pszName;
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* OSRGetName() */
1419 : /************************************************************************/
1420 :
1421 : /**
1422 : * \brief Return the CRS name.
1423 : *
1424 : * The returned value is only short lived and should not be used after other
1425 : * calls to methods on this object.
1426 : *
1427 : * @since GDAL 3.0
1428 : */
1429 47 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1430 :
1431 : {
1432 47 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1433 :
1434 47 : return ToPointer(hSRS)->GetName();
1435 : }
1436 :
1437 : /************************************************************************/
1438 : /* GetCelestialBodyName() */
1439 : /************************************************************************/
1440 :
1441 : /**
1442 : * \brief Return the name of the celestial body of this CRS.
1443 : *
1444 : * e.g. "Earth" for an Earth CRS
1445 : *
1446 : * The returned value is only short lived and should not be used after other
1447 : * calls to methods on this object.
1448 : *
1449 : * @since GDAL 3.12 and PROJ 8.1
1450 : */
1451 :
1452 4 : const char *OGRSpatialReference::GetCelestialBodyName() const
1453 : {
1454 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1455 :
1456 : TAKE_OPTIONAL_LOCK();
1457 :
1458 : d->refreshProjObj();
1459 : if (!d->m_pj_crs)
1460 : return nullptr;
1461 : d->demoteFromBoundCRS();
1462 : const char *name =
1463 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1464 : if (name)
1465 : {
1466 : d->m_celestialBodyName = name;
1467 : }
1468 : d->undoDemoteFromBoundCRS();
1469 : return d->m_celestialBodyName.c_str();
1470 : #else
1471 4 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1472 : 0.05 * SRS_WGS84_SEMIMAJOR)
1473 4 : return "Earth";
1474 0 : const char *pszAuthName = GetAuthorityName();
1475 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1476 0 : return "Earth";
1477 0 : return nullptr;
1478 : #endif
1479 : }
1480 :
1481 : /************************************************************************/
1482 : /* OSRGetCelestialBodyName() */
1483 : /************************************************************************/
1484 :
1485 : /**
1486 : * \brief Return the name of the celestial body of this CRS.
1487 : *
1488 : * e.g. "Earth" for an Earth CRS
1489 : *
1490 : * The returned value is only short lived and should not be used after other
1491 : * calls to methods on this object.
1492 : *
1493 : * @since GDAL 3.12 and PROJ 8.1
1494 : */
1495 :
1496 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1497 :
1498 : {
1499 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1500 :
1501 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1502 : }
1503 :
1504 : /************************************************************************/
1505 : /* Clone() */
1506 : /************************************************************************/
1507 :
1508 : /**
1509 : * \brief Make a duplicate of this OGRSpatialReference.
1510 : *
1511 : * This method is the same as the C function OSRClone().
1512 : *
1513 : * @return a new SRS, which becomes the responsibility of the caller.
1514 : */
1515 :
1516 32463 : OGRSpatialReference *OGRSpatialReference::Clone() const
1517 :
1518 : {
1519 32463 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1520 :
1521 32463 : TAKE_OPTIONAL_LOCK();
1522 :
1523 32463 : d->refreshProjObj();
1524 32463 : if (d->m_pj_crs != nullptr)
1525 32419 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1526 32463 : if (d->m_bHasCenterLong && d->m_poRoot)
1527 : {
1528 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1529 : }
1530 32463 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1531 32463 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1532 32463 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1533 64926 : return poNewRef;
1534 : }
1535 :
1536 : /************************************************************************/
1537 : /* OSRClone() */
1538 : /************************************************************************/
1539 :
1540 : /**
1541 : * \brief Make a duplicate of this OGRSpatialReference.
1542 : *
1543 : * This function is the same as OGRSpatialReference::Clone()
1544 : */
1545 1357 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1546 :
1547 : {
1548 1357 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1549 :
1550 1357 : return ToHandle(ToPointer(hSRS)->Clone());
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* dumpReadable() */
1555 : /************************************************************************/
1556 :
1557 : /** Dump pretty wkt to stdout, mostly for debugging.
1558 : */
1559 0 : void OGRSpatialReference::dumpReadable()
1560 :
1561 : {
1562 0 : char *pszPrettyWkt = nullptr;
1563 :
1564 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1565 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1566 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1567 0 : CPLFree(pszPrettyWkt);
1568 0 : }
1569 :
1570 : /************************************************************************/
1571 : /* exportToPrettyWkt() */
1572 : /************************************************************************/
1573 :
1574 : /**
1575 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1576 : * person.
1577 : *
1578 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1579 : * Issues</a> page for implementation details of WKT 1 in OGR.
1580 : *
1581 : * Note that the returned WKT string should be freed with
1582 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1583 : *
1584 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1585 : * option. Valid values are the one of the FORMAT option of
1586 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1587 : *
1588 : * This method is the same as the C function OSRExportToPrettyWkt().
1589 : *
1590 : * @param ppszResult the resulting string is returned in this pointer.
1591 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1592 : * stripped off.
1593 : *
1594 : * @return OGRERR_NONE if successful.
1595 : */
1596 :
1597 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1598 : int bSimplify) const
1599 :
1600 : {
1601 116 : CPLStringList aosOptions;
1602 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1603 58 : if (bSimplify)
1604 : {
1605 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1606 : }
1607 116 : return exportToWkt(ppszResult, aosOptions.List());
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* OSRExportToPrettyWkt() */
1612 : /************************************************************************/
1613 :
1614 : /**
1615 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1616 : * person.
1617 : *
1618 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1619 : * option. Valid values are the one of the FORMAT option of
1620 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1621 : *
1622 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1623 : */
1624 :
1625 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1626 : char **ppszReturn, int bSimplify)
1627 :
1628 : {
1629 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1630 :
1631 56 : *ppszReturn = nullptr;
1632 :
1633 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1634 : }
1635 :
1636 : /************************************************************************/
1637 : /* exportToWkt() */
1638 : /************************************************************************/
1639 :
1640 : /**
1641 : * \brief Convert this SRS into WKT 1 format.
1642 : *
1643 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1644 : * Issues</a> page for implementation details of WKT 1 in OGR.
1645 : *
1646 : * Note that the returned WKT string should be freed with
1647 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1648 : *
1649 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1650 : * option. Valid values are the one of the FORMAT option of
1651 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1652 : *
1653 : * This method is the same as the C function OSRExportToWkt().
1654 : *
1655 : * @param ppszResult the resulting string is returned in this pointer.
1656 : *
1657 : * @return OGRERR_NONE if successful.
1658 : */
1659 :
1660 12395 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1661 :
1662 : {
1663 12395 : return exportToWkt(ppszResult, nullptr);
1664 : }
1665 :
1666 : /************************************************************************/
1667 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1668 : /************************************************************************/
1669 :
1670 562 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1671 : bool onlyIfEPSGCode,
1672 : bool canModifyHorizPart)
1673 : {
1674 562 : PJ *ret = nullptr;
1675 562 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1676 : {
1677 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1678 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1679 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1680 26 : vertCRS &&
1681 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1682 : {
1683 : auto boundHoriz =
1684 : canModifyHorizPart
1685 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1686 3 : : proj_clone(ctx, horizCRS);
1687 : auto boundVert =
1688 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1689 3 : if (boundHoriz && boundVert)
1690 : {
1691 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1692 : boundHoriz, boundVert);
1693 : }
1694 3 : proj_destroy(boundHoriz);
1695 3 : proj_destroy(boundVert);
1696 : }
1697 13 : proj_destroy(horizCRS);
1698 13 : proj_destroy(vertCRS);
1699 : }
1700 1058 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1701 509 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1702 : {
1703 189 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1704 : }
1705 562 : return ret;
1706 : }
1707 :
1708 : /************************************************************************/
1709 : /* exportToWkt() */
1710 : /************************************************************************/
1711 :
1712 : /**
1713 : * Convert this SRS into a WKT string.
1714 : *
1715 : * Note that the returned WKT string should be freed with
1716 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1717 : *
1718 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1719 : * Issues</a> page for implementation details of WKT 1 in OGR.
1720 : *
1721 : * @param ppszResult the resulting string is returned in this pointer.
1722 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1723 : * supported options are
1724 : * <ul>
1725 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1726 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1727 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1728 : * node is returned.
1729 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1730 : * node is returned.
1731 : * WKT1 is an alias of WKT1_GDAL.
1732 : * WKT2 will default to the latest revision implemented (currently
1733 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1734 : * </li>
1735 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1736 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1737 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1738 : * height (for example for use with LAS 1.4 WKT1).
1739 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1740 : * </ul>
1741 : *
1742 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1743 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1744 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1745 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1746 : * TOWGS84[] node may be added.
1747 : *
1748 : * @return OGRERR_NONE if successful.
1749 : * @since GDAL 3.0
1750 : */
1751 :
1752 17526 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1753 : const char *const *papszOptions) const
1754 : {
1755 : // In the past calling this method was thread-safe, even if we never
1756 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1757 : // so this is no longer thread-safe.
1758 35052 : std::lock_guard oLock(d->m_mutex);
1759 :
1760 17526 : d->refreshProjObj();
1761 17526 : if (!d->m_pj_crs)
1762 : {
1763 21 : *ppszResult = CPLStrdup("");
1764 21 : return OGRERR_FAILURE;
1765 : }
1766 :
1767 17505 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1768 : {
1769 0 : return d->m_poRoot->exportToWkt(ppszResult);
1770 : }
1771 :
1772 17505 : auto ctxt = d->getPROJContext();
1773 17505 : auto wktFormat = PJ_WKT1_GDAL;
1774 : const char *pszFormat =
1775 17505 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1776 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1777 17505 : if (EQUAL(pszFormat, "DEFAULT"))
1778 14640 : pszFormat = "";
1779 :
1780 17505 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1781 : {
1782 672 : wktFormat = PJ_WKT1_ESRI;
1783 : }
1784 16833 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1785 16114 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1786 : {
1787 724 : wktFormat = PJ_WKT1_GDAL;
1788 : }
1789 16109 : else if (EQUAL(pszFormat, "WKT2_2015"))
1790 : {
1791 363 : wktFormat = PJ_WKT2_2015;
1792 : }
1793 15746 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1794 15346 : EQUAL(pszFormat, "WKT2_2019"))
1795 : {
1796 1336 : wktFormat = PJ_WKT2_2018;
1797 : }
1798 14410 : else if (pszFormat[0] == '\0')
1799 : {
1800 : // cppcheck-suppress knownConditionTrueFalse
1801 14410 : if (IsDerivedGeographic())
1802 : {
1803 2 : wktFormat = PJ_WKT2_2018;
1804 : }
1805 28182 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1806 13774 : GetAxesCount() == 3)
1807 : {
1808 80 : wktFormat = PJ_WKT2_2018;
1809 : }
1810 : }
1811 : else
1812 : {
1813 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1814 0 : *ppszResult = CPLStrdup("");
1815 0 : return OGRERR_FAILURE;
1816 : }
1817 :
1818 35010 : CPLStringList aosOptions;
1819 17505 : if (wktFormat != PJ_WKT1_ESRI)
1820 : {
1821 16833 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1822 : }
1823 : aosOptions.SetNameValue(
1824 17505 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1825 :
1826 17505 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1827 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1828 17505 : if (pszAllowEllpsHeightAsVertCS)
1829 : {
1830 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1831 0 : pszAllowEllpsHeightAsVertCS);
1832 : }
1833 :
1834 17505 : PJ *boundCRS = nullptr;
1835 32557 : if (wktFormat == PJ_WKT1_GDAL &&
1836 15052 : CPLTestBool(CSLFetchNameValueDef(
1837 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1838 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1839 : {
1840 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1841 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1842 : }
1843 :
1844 35010 : CPLErrorAccumulator oErrorAccumulator;
1845 : const char *pszWKT;
1846 : {
1847 17505 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1848 17505 : CPL_IGNORE_RET_VAL(oAccumulator);
1849 17505 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1850 17505 : aosOptions.List());
1851 : }
1852 17507 : for (const auto &oError : oErrorAccumulator.GetErrors())
1853 : {
1854 32 : if (pszFormat[0] == '\0' &&
1855 14 : (oError.msg.find("Unsupported conversion method") !=
1856 2 : std::string::npos ||
1857 2 : oError.msg.find("can only be exported to WKT2") !=
1858 0 : std::string::npos ||
1859 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1860 : std::string::npos))
1861 : {
1862 14 : CPLErrorReset();
1863 : // If we cannot export in the default mode (WKT1), retry with WKT2
1864 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1865 14 : PJ_WKT2_2018, aosOptions.List());
1866 14 : break;
1867 : }
1868 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1869 : }
1870 :
1871 17505 : if (!pszWKT)
1872 : {
1873 2 : *ppszResult = CPLStrdup("");
1874 2 : proj_destroy(boundCRS);
1875 2 : return OGRERR_FAILURE;
1876 : }
1877 :
1878 17503 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1879 : {
1880 5 : OGR_SRSNode oRoot;
1881 5 : oRoot.importFromWkt(&pszWKT);
1882 5 : oRoot.StripNodes("AXIS");
1883 5 : if (EQUAL(pszFormat, "SFSQL"))
1884 : {
1885 3 : oRoot.StripNodes("TOWGS84");
1886 : }
1887 5 : oRoot.StripNodes("AUTHORITY");
1888 5 : oRoot.StripNodes("EXTENSION");
1889 : OGRErr eErr;
1890 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1891 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1892 : else
1893 3 : eErr = oRoot.exportToWkt(ppszResult);
1894 5 : proj_destroy(boundCRS);
1895 5 : return eErr;
1896 : }
1897 :
1898 17498 : *ppszResult = CPLStrdup(pszWKT);
1899 :
1900 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1901 17498 : if (wktFormat == PJ_WKT2_2018)
1902 : {
1903 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1904 : // related to a wrong EPSG code assigned to UTM South conversions
1905 1418 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1906 1418 : if (pszPtr)
1907 : {
1908 349 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1909 349 : const int nZone = atoi(pszPtr);
1910 1046 : while (*pszPtr >= '0' && *pszPtr <= '9')
1911 697 : ++pszPtr;
1912 349 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1913 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1914 : {
1915 1 : pszPtr += 3;
1916 1 : int nLevel = 0;
1917 1 : bool bInString = false;
1918 : // Find the ID node corresponding to this CONVERSION node
1919 480 : while (*pszPtr)
1920 : {
1921 480 : if (bInString)
1922 : {
1923 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1924 : {
1925 0 : ++pszPtr;
1926 : }
1927 197 : else if (*pszPtr == '"')
1928 : {
1929 17 : bInString = false;
1930 : }
1931 : }
1932 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1933 : {
1934 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1935 : 17000 + nZone)))
1936 : {
1937 1 : CPLAssert(pszPtr[11] == '7');
1938 1 : CPLAssert(pszPtr[12] == '0');
1939 1 : pszPtr[11] = '6';
1940 1 : pszPtr[12] = '1';
1941 : }
1942 1 : break;
1943 : }
1944 282 : else if (*pszPtr == '"')
1945 : {
1946 17 : bInString = true;
1947 : }
1948 265 : else if (*pszPtr == '[')
1949 : {
1950 17 : ++nLevel;
1951 : }
1952 248 : else if (*pszPtr == ']')
1953 : {
1954 17 : --nLevel;
1955 : }
1956 :
1957 479 : ++pszPtr;
1958 : }
1959 : }
1960 : }
1961 : }
1962 : #endif
1963 :
1964 17498 : proj_destroy(boundCRS);
1965 17498 : return OGRERR_NONE;
1966 : }
1967 :
1968 : /************************************************************************/
1969 : /* exportToWkt() */
1970 : /************************************************************************/
1971 :
1972 : /**
1973 : * Convert this SRS into a WKT string.
1974 : *
1975 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1976 : * Issues</a> page for implementation details of WKT 1 in OGR.
1977 : *
1978 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1979 : * supported options are
1980 : * <ul>
1981 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1982 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1983 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1984 : * node is returned.
1985 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1986 : * node is returned.
1987 : * WKT1 is an alias of WKT1_GDAL.
1988 : * WKT2 will default to the latest revision implemented (currently
1989 : * WKT2_2019)
1990 : * </li>
1991 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1992 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1993 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1994 : * height (for example for use with LAS 1.4 WKT1).
1995 : * Requires PROJ 7.2.1.</li>
1996 : * </ul>
1997 : *
1998 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1999 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
2000 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
2001 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
2002 : * TOWGS84[] node may be added.
2003 : *
2004 : * @return a non-empty string if successful.
2005 : * @since GDAL 3.9
2006 : */
2007 :
2008 : std::string
2009 806 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2010 : {
2011 806 : std::string osWKT;
2012 806 : char *pszWKT = nullptr;
2013 806 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2014 793 : osWKT = pszWKT;
2015 806 : CPLFree(pszWKT);
2016 1612 : return osWKT;
2017 : }
2018 :
2019 : /************************************************************************/
2020 : /* OSRExportToWkt() */
2021 : /************************************************************************/
2022 :
2023 : /**
2024 : * \brief Convert this SRS into WKT 1 format.
2025 : *
2026 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2027 : * Issues</a> page for implementation details of WKT in OGR.
2028 : *
2029 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2030 : * option. Valid values are the one of the FORMAT option of
2031 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2032 : *
2033 : * This function is the same as OGRSpatialReference::exportToWkt().
2034 : */
2035 :
2036 1077 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2037 :
2038 : {
2039 1077 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2040 :
2041 1077 : *ppszReturn = nullptr;
2042 :
2043 1077 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2044 : }
2045 :
2046 : /************************************************************************/
2047 : /* OSRExportToWktEx() */
2048 : /************************************************************************/
2049 :
2050 : /**
2051 : * \brief Convert this SRS into WKT format.
2052 : *
2053 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2054 : * ppszResult,const char* const* papszOptions ) const
2055 : *
2056 : * @since GDAL 3.0
2057 : */
2058 :
2059 1301 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2060 : const char *const *papszOptions)
2061 : {
2062 1301 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2063 :
2064 1301 : *ppszReturn = nullptr;
2065 :
2066 1301 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2067 : }
2068 :
2069 : /************************************************************************/
2070 : /* exportToPROJJSON() */
2071 : /************************************************************************/
2072 :
2073 : /**
2074 : * Convert this SRS into a PROJJSON string.
2075 : *
2076 : * Note that the returned JSON string should be freed with
2077 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2078 : *
2079 : * @param ppszResult the resulting string is returned in this pointer.
2080 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2081 : * supported options are
2082 : * <ul>
2083 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2084 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2085 : * on).</li>
2086 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2087 : * disable it.</li>
2088 : * </ul>
2089 : *
2090 : * @return OGRERR_NONE if successful.
2091 : * @since GDAL 3.1 and PROJ 6.2
2092 : */
2093 :
2094 2968 : OGRErr OGRSpatialReference::exportToPROJJSON(
2095 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2096 : {
2097 5936 : TAKE_OPTIONAL_LOCK();
2098 :
2099 2968 : d->refreshProjObj();
2100 2968 : if (!d->m_pj_crs)
2101 : {
2102 1 : *ppszResult = nullptr;
2103 1 : return OGRERR_FAILURE;
2104 : }
2105 :
2106 : const char *pszPROJJSON =
2107 2967 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2108 :
2109 2967 : if (!pszPROJJSON)
2110 : {
2111 0 : *ppszResult = CPLStrdup("");
2112 0 : return OGRERR_FAILURE;
2113 : }
2114 :
2115 2967 : *ppszResult = CPLStrdup(pszPROJJSON);
2116 :
2117 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2118 : {
2119 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2120 : // related to a wrong EPSG code assigned to UTM South conversions
2121 2967 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2122 2967 : if (pszPtr)
2123 : {
2124 257 : pszPtr += strlen("\"name\": \"UTM zone ");
2125 257 : const int nZone = atoi(pszPtr);
2126 770 : while (*pszPtr >= '0' && *pszPtr <= '9')
2127 513 : ++pszPtr;
2128 257 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2129 : {
2130 3 : pszPtr += 2;
2131 3 : int nLevel = 0;
2132 3 : bool bInString = false;
2133 : // Find the id node corresponding to this conversion node
2134 3773 : while (*pszPtr)
2135 : {
2136 3773 : if (bInString)
2137 : {
2138 1384 : if (*pszPtr == '\\')
2139 : {
2140 0 : ++pszPtr;
2141 : }
2142 1384 : else if (*pszPtr == '"')
2143 : {
2144 175 : bInString = false;
2145 : }
2146 : }
2147 2389 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2148 : {
2149 3 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2150 : const char *pszAuthEPSG =
2151 3 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2152 3 : char *pszCode = strstr(
2153 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2154 3 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2155 3 : pszNextEndCurl - pszAuthEPSG > 0 &&
2156 3 : pszNextEndCurl - pszCode > 0)
2157 : {
2158 3 : CPLAssert(pszCode[9] == '7');
2159 3 : CPLAssert(pszCode[10] == '0');
2160 3 : pszCode[9] = '6';
2161 3 : pszCode[10] = '1';
2162 : }
2163 3 : break;
2164 : }
2165 2386 : else if (*pszPtr == '"')
2166 : {
2167 175 : bInString = true;
2168 : }
2169 2211 : else if (*pszPtr == '{' || *pszPtr == '[')
2170 : {
2171 43 : ++nLevel;
2172 : }
2173 2168 : else if (*pszPtr == '}' || *pszPtr == ']')
2174 : {
2175 43 : --nLevel;
2176 : }
2177 :
2178 3770 : ++pszPtr;
2179 : }
2180 : }
2181 : }
2182 : }
2183 : #endif
2184 :
2185 2967 : return OGRERR_NONE;
2186 : }
2187 :
2188 : /************************************************************************/
2189 : /* OSRExportToPROJJSON() */
2190 : /************************************************************************/
2191 :
2192 : /**
2193 : * \brief Convert this SRS into PROJJSON format.
2194 : *
2195 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2196 : *
2197 : * @since GDAL 3.1 and PROJ 6.2
2198 : */
2199 :
2200 2 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2201 : const char *const *papszOptions)
2202 : {
2203 2 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2204 :
2205 2 : *ppszReturn = nullptr;
2206 :
2207 2 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2208 : }
2209 :
2210 : /************************************************************************/
2211 : /* importFromWkt() */
2212 : /************************************************************************/
2213 :
2214 : /**
2215 : * \brief Import from WKT string.
2216 : *
2217 : * This method will wipe the existing SRS definition, and
2218 : * reassign it based on the contents of the passed WKT string. Only as
2219 : * much of the input string as needed to construct this SRS is consumed from
2220 : * the input string, and the input string pointer
2221 : * is then updated to point to the remaining (unused) input.
2222 : *
2223 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2224 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2225 : * and the coordinate epoch potentially present used as the coordinate epoch
2226 : * property of the OGRSpatialReference object.
2227 : *
2228 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2229 : * Issues</a> page for implementation details of WKT in OGR.
2230 : *
2231 : * This method is the same as the C function OSRImportFromWkt().
2232 : *
2233 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2234 : * point to remaining unused input text.
2235 : *
2236 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2237 : * fails for any reason.
2238 : */
2239 :
2240 21090 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2241 :
2242 : {
2243 21090 : return importFromWkt(ppszInput, nullptr);
2244 : }
2245 :
2246 : /************************************************************************/
2247 : /* importFromWkt() */
2248 : /************************************************************************/
2249 :
2250 : /*! @cond Doxygen_Suppress */
2251 :
2252 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2253 : CSLConstList papszOptions)
2254 :
2255 : {
2256 21 : return importFromWkt(&pszInput, papszOptions);
2257 : }
2258 :
2259 21111 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2260 : CSLConstList papszOptions)
2261 :
2262 : {
2263 42222 : TAKE_OPTIONAL_LOCK();
2264 :
2265 21111 : if (!ppszInput || !*ppszInput)
2266 0 : return OGRERR_FAILURE;
2267 :
2268 21111 : if (strlen(*ppszInput) > 100 * 1000 &&
2269 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2270 : {
2271 0 : CPLError(CE_Failure, CPLE_NotSupported,
2272 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2273 : "You can remove this limitation by definition the "
2274 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2275 0 : return OGRERR_FAILURE;
2276 : }
2277 :
2278 21111 : Clear();
2279 :
2280 21111 : bool canCache = false;
2281 21111 : auto tlsCache = OSRGetProjTLSCache();
2282 42222 : std::string osWkt;
2283 21111 : if (**ppszInput)
2284 : {
2285 20556 : osWkt = *ppszInput;
2286 20556 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2287 20556 : if (cachedObj)
2288 : {
2289 18543 : d->setPjCRS(cachedObj);
2290 : }
2291 : else
2292 : {
2293 4026 : CPLStringList aosOptions(papszOptions);
2294 2013 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2295 2013 : aosOptions.SetNameValue("STRICT", "NO");
2296 2013 : PROJ_STRING_LIST warnings = nullptr;
2297 2013 : PROJ_STRING_LIST errors = nullptr;
2298 2013 : auto ctxt = d->getPROJContext();
2299 2013 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2300 : &warnings, &errors);
2301 2013 : d->setPjCRS(pj);
2302 :
2303 2066 : for (auto iter = warnings; iter && *iter; ++iter)
2304 : {
2305 53 : d->m_wktImportWarnings.push_back(*iter);
2306 : }
2307 2250 : for (auto iter = errors; iter && *iter; ++iter)
2308 : {
2309 237 : d->m_wktImportErrors.push_back(*iter);
2310 237 : if (!d->m_pj_crs)
2311 : {
2312 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2313 : }
2314 : }
2315 2013 : if (warnings == nullptr && errors == nullptr)
2316 : {
2317 1732 : canCache = true;
2318 : }
2319 2013 : proj_string_list_destroy(warnings);
2320 2013 : proj_string_list_destroy(errors);
2321 : }
2322 : }
2323 21111 : if (!d->m_pj_crs)
2324 589 : return OGRERR_CORRUPT_DATA;
2325 :
2326 : // Only accept CRS objects
2327 20522 : if (!proj_is_crs(d->m_pj_crs))
2328 : {
2329 0 : Clear();
2330 0 : return OGRERR_CORRUPT_DATA;
2331 : }
2332 :
2333 20522 : if (canCache)
2334 : {
2335 1732 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2336 : }
2337 :
2338 20522 : if (strstr(*ppszInput, "CENTER_LONG"))
2339 : {
2340 0 : auto poRoot = new OGR_SRSNode();
2341 0 : d->setRoot(poRoot);
2342 0 : const char *pszTmp = *ppszInput;
2343 0 : poRoot->importFromWkt(&pszTmp);
2344 0 : d->m_bHasCenterLong = true;
2345 : }
2346 :
2347 : // TODO? we don't really update correctly since we assume that the
2348 : // passed string is only WKT.
2349 20522 : *ppszInput += strlen(*ppszInput);
2350 20522 : return OGRERR_NONE;
2351 :
2352 : #if no_longer_implemented_for_now
2353 : /* -------------------------------------------------------------------- */
2354 : /* The following seems to try and detect and unconsumed */
2355 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2356 : /* import and attach it to the existing root. Likely we will */
2357 : /* need to extend this somewhat to bring it into an acceptable */
2358 : /* OGRSpatialReference organization at some point. */
2359 : /* -------------------------------------------------------------------- */
2360 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2361 : {
2362 : if (((*ppszInput)[0]) == ',')
2363 : (*ppszInput)++;
2364 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2365 : poRoot->AddChild(poNewChild);
2366 : return poNewChild->importFromWkt(ppszInput);
2367 : }
2368 : #endif
2369 : }
2370 :
2371 : /*! @endcond */
2372 :
2373 : /**
2374 : * \brief Import from WKT string.
2375 : *
2376 : * This method will wipe the existing SRS definition, and
2377 : * reassign it based on the contents of the passed WKT string. Only as
2378 : * much of the input string as needed to construct this SRS is consumed from
2379 : * the input string, and the input string pointer
2380 : * is then updated to point to the remaining (unused) input.
2381 : *
2382 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2383 : * Issues</a> page for implementation details of WKT in OGR.
2384 : *
2385 : * This method is the same as the C function OSRImportFromWkt().
2386 : *
2387 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2388 : * point to remaining unused input text.
2389 : *
2390 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2391 : * fails for any reason.
2392 : * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2393 : * char*)
2394 : */
2395 :
2396 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2397 :
2398 : {
2399 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2400 : }
2401 :
2402 : /**
2403 : * \brief Import from WKT string.
2404 : *
2405 : * This method will wipe the existing SRS definition, and
2406 : * reassign it based on the contents of the passed WKT string. Only as
2407 : * much of the input string as needed to construct this SRS is consumed from
2408 : * the input string, and the input string pointer
2409 : * is then updated to point to the remaining (unused) input.
2410 : *
2411 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2412 : * Issues</a> page for implementation details of WKT in OGR.
2413 : *
2414 : * @param pszInput Input WKT
2415 : *
2416 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2417 : * fails for any reason.
2418 : */
2419 :
2420 20827 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2421 : {
2422 20827 : return importFromWkt(&pszInput);
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* Validate() */
2427 : /************************************************************************/
2428 :
2429 : /**
2430 : * \brief Validate CRS imported with importFromWkt() or with modified with
2431 : * direct node manipulations. Otherwise the CRS should be always valid.
2432 : *
2433 : * This method attempts to verify that the spatial reference system is
2434 : * well formed, and consists of known tokens. The validation is not
2435 : * comprehensive.
2436 : *
2437 : * This method is the same as the C function OSRValidate().
2438 : *
2439 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2440 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2441 : * but contains non-standard PROJECTION[] values.
2442 : */
2443 :
2444 116 : OGRErr OGRSpatialReference::Validate() const
2445 :
2446 : {
2447 232 : TAKE_OPTIONAL_LOCK();
2448 :
2449 154 : for (const auto &str : d->m_wktImportErrors)
2450 : {
2451 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2452 : }
2453 116 : for (const auto &str : d->m_wktImportWarnings)
2454 : {
2455 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2456 : }
2457 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2458 : {
2459 37 : return OGRERR_CORRUPT_DATA;
2460 : }
2461 79 : if (!d->m_wktImportWarnings.empty())
2462 : {
2463 0 : return OGRERR_UNSUPPORTED_SRS;
2464 : }
2465 79 : return OGRERR_NONE;
2466 : }
2467 :
2468 : /************************************************************************/
2469 : /* OSRValidate() */
2470 : /************************************************************************/
2471 : /**
2472 : * \brief Validate SRS tokens.
2473 : *
2474 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2475 : */
2476 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2477 :
2478 : {
2479 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2480 :
2481 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2482 : }
2483 :
2484 : /************************************************************************/
2485 : /* OSRImportFromWkt() */
2486 : /************************************************************************/
2487 :
2488 : /**
2489 : * \brief Import from WKT string.
2490 : *
2491 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2492 : * Issues</a> page for implementation details of WKT in OGR.
2493 : *
2494 : * This function is the same as OGRSpatialReference::importFromWkt().
2495 : */
2496 :
2497 263 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2498 :
2499 : {
2500 263 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2501 :
2502 263 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2503 : }
2504 :
2505 : /************************************************************************/
2506 : /* SetNode() */
2507 : /************************************************************************/
2508 :
2509 : /**
2510 : * \brief Set attribute value in spatial reference.
2511 : *
2512 : * Missing intermediate nodes in the path will be created if not already
2513 : * in existence. If the attribute has no children one will be created and
2514 : * assigned the value otherwise the zeroth child will be assigned the value.
2515 : *
2516 : * This method does the same as the C function OSRSetAttrValue().
2517 : *
2518 : * @param pszNodePath full path to attribute to be set. For instance
2519 : * "PROJCS|GEOGCS|UNIT".
2520 : *
2521 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2522 : * This may be NULL if you just want to force creation of the intermediate
2523 : * path.
2524 : *
2525 : * @return OGRERR_NONE on success.
2526 : */
2527 :
2528 587 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2529 : const char *pszNewNodeValue)
2530 :
2531 : {
2532 1174 : TAKE_OPTIONAL_LOCK();
2533 :
2534 : char **papszPathTokens =
2535 587 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2536 :
2537 587 : if (CSLCount(papszPathTokens) < 1)
2538 : {
2539 0 : CSLDestroy(papszPathTokens);
2540 0 : return OGRERR_FAILURE;
2541 : }
2542 :
2543 1024 : if (GetRoot() == nullptr ||
2544 437 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2545 : {
2546 271 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2547 117 : CSLCount(papszPathTokens) == 1)
2548 : {
2549 117 : CSLDestroy(papszPathTokens);
2550 117 : return SetProjCS(pszNewNodeValue);
2551 : }
2552 : else
2553 : {
2554 37 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2555 : }
2556 : }
2557 :
2558 470 : OGR_SRSNode *poNode = GetRoot();
2559 728 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2560 : {
2561 258 : int j = 0; // Used after for.
2562 :
2563 645 : for (; j < poNode->GetChildCount(); j++)
2564 : {
2565 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2566 : {
2567 198 : poNode = poNode->GetChild(j);
2568 198 : j = -1;
2569 198 : break;
2570 : }
2571 : }
2572 :
2573 258 : if (j != -1)
2574 : {
2575 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2576 60 : poNode->AddChild(poNewNode);
2577 60 : poNode = poNewNode;
2578 : }
2579 : }
2580 :
2581 470 : CSLDestroy(papszPathTokens);
2582 :
2583 470 : if (pszNewNodeValue != nullptr)
2584 : {
2585 470 : if (poNode->GetChildCount() > 0)
2586 373 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2587 : else
2588 97 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2589 : };
2590 470 : return OGRERR_NONE;
2591 : }
2592 :
2593 : /************************************************************************/
2594 : /* OSRSetAttrValue() */
2595 : /************************************************************************/
2596 :
2597 : /**
2598 : * \brief Set attribute value in spatial reference.
2599 : *
2600 : * This function is the same as OGRSpatialReference::SetNode()
2601 : */
2602 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2603 : const char *pszPath, const char *pszValue)
2604 :
2605 : {
2606 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2607 :
2608 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2609 : }
2610 :
2611 : /************************************************************************/
2612 : /* SetNode() */
2613 : /************************************************************************/
2614 :
2615 : /**
2616 : * \brief Set attribute value in spatial reference.
2617 : *
2618 : * Missing intermediate nodes in the path will be created if not already
2619 : * in existence. If the attribute has no children one will be created and
2620 : * assigned the value otherwise the zeroth child will be assigned the value.
2621 : *
2622 : * This method does the same as the C function OSRSetAttrValue().
2623 : *
2624 : * @param pszNodePath full path to attribute to be set. For instance
2625 : * "PROJCS|GEOGCS|UNIT".
2626 : *
2627 : * @param dfValue value to be assigned to node.
2628 : *
2629 : * @return OGRERR_NONE on success.
2630 : */
2631 :
2632 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2633 :
2634 : {
2635 0 : char szValue[64] = {'\0'};
2636 :
2637 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2638 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2639 : else
2640 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2641 :
2642 0 : return SetNode(pszNodePath, szValue);
2643 : }
2644 :
2645 : /************************************************************************/
2646 : /* SetAngularUnits() */
2647 : /************************************************************************/
2648 :
2649 : /**
2650 : * \brief Set the angular units for the geographic coordinate system.
2651 : *
2652 : * This method creates a UNIT subnode with the specified values as a
2653 : * child of the GEOGCS node.
2654 : *
2655 : * This method does the same as the C function OSRSetAngularUnits().
2656 : *
2657 : * @param pszUnitsName the units name to be used. Some preferred units
2658 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2659 : *
2660 : * @param dfInRadians the value to multiple by an angle in the indicated
2661 : * units to transform to radians. Some standard conversion factors can
2662 : * be found in ogr_srs_api.h.
2663 : *
2664 : * @return OGRERR_NONE on success.
2665 : */
2666 :
2667 1601 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2668 : double dfInRadians)
2669 :
2670 : {
2671 3202 : TAKE_OPTIONAL_LOCK();
2672 :
2673 1601 : d->bNormInfoSet = FALSE;
2674 :
2675 1601 : d->refreshProjObj();
2676 1601 : if (!d->m_pj_crs)
2677 0 : return OGRERR_FAILURE;
2678 1601 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2679 1601 : if (!geodCRS)
2680 0 : return OGRERR_FAILURE;
2681 1601 : proj_destroy(geodCRS);
2682 1601 : d->demoteFromBoundCRS();
2683 1601 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2684 : pszUnitsName, dfInRadians,
2685 : nullptr, nullptr));
2686 1601 : d->undoDemoteFromBoundCRS();
2687 :
2688 1601 : d->m_osAngularUnits = pszUnitsName;
2689 1601 : d->m_dfAngularUnitToRadian = dfInRadians;
2690 :
2691 1601 : return OGRERR_NONE;
2692 : }
2693 :
2694 : /************************************************************************/
2695 : /* OSRSetAngularUnits() */
2696 : /************************************************************************/
2697 :
2698 : /**
2699 : * \brief Set the angular units for the geographic coordinate system.
2700 : *
2701 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2702 : */
2703 49 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2704 : double dfInRadians)
2705 :
2706 : {
2707 49 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2708 :
2709 49 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2710 : }
2711 :
2712 : /************************************************************************/
2713 : /* GetAngularUnits() */
2714 : /************************************************************************/
2715 :
2716 : /**
2717 : * \brief Fetch angular geographic coordinate system units.
2718 : *
2719 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2720 : * will be assumed. This method only checks directly under the GEOGCS node
2721 : * for units.
2722 : *
2723 : * This method does the same thing as the C function OSRGetAngularUnits().
2724 : *
2725 : * @param ppszName a pointer to be updated with the pointer to the units name.
2726 : * The returned value remains internal to the OGRSpatialReference and should
2727 : * not be freed, or modified. It may be invalidated on the next
2728 : * OGRSpatialReference call.
2729 : *
2730 : * @return the value to multiply by angular distances to transform them to
2731 : * radians.
2732 : */
2733 :
2734 8853 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2735 :
2736 : {
2737 17706 : TAKE_OPTIONAL_LOCK();
2738 :
2739 8853 : d->refreshProjObj();
2740 :
2741 8853 : if (!d->m_osAngularUnits.empty())
2742 : {
2743 2429 : if (ppszName != nullptr)
2744 185 : *ppszName = d->m_osAngularUnits.c_str();
2745 2429 : return d->m_dfAngularUnitToRadian;
2746 : }
2747 :
2748 : do
2749 : {
2750 6424 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2751 : {
2752 113 : break;
2753 : }
2754 :
2755 : auto geodCRS =
2756 6313 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2757 6313 : if (!geodCRS)
2758 : {
2759 0 : break;
2760 : }
2761 : auto coordSys =
2762 6313 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2763 6313 : proj_destroy(geodCRS);
2764 6313 : if (!coordSys)
2765 : {
2766 0 : break;
2767 : }
2768 6313 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2769 : PJ_CS_TYPE_ELLIPSOIDAL)
2770 : {
2771 2 : proj_destroy(coordSys);
2772 2 : break;
2773 : }
2774 :
2775 6311 : double dfConvFactor = 0.0;
2776 6311 : const char *pszUnitName = nullptr;
2777 6311 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2778 : nullptr, nullptr, &dfConvFactor,
2779 : &pszUnitName, nullptr, nullptr))
2780 : {
2781 0 : proj_destroy(coordSys);
2782 0 : break;
2783 : }
2784 :
2785 6311 : d->m_osAngularUnits = pszUnitName;
2786 :
2787 6311 : proj_destroy(coordSys);
2788 6311 : d->m_dfAngularUnitToRadian = dfConvFactor;
2789 : } while (false);
2790 :
2791 6424 : if (d->m_osAngularUnits.empty())
2792 : {
2793 113 : d->m_osAngularUnits = "degree";
2794 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2795 : }
2796 :
2797 6424 : if (ppszName != nullptr)
2798 3291 : *ppszName = d->m_osAngularUnits.c_str();
2799 6424 : return d->m_dfAngularUnitToRadian;
2800 : }
2801 :
2802 : /**
2803 : * \brief Fetch angular geographic coordinate system units.
2804 : *
2805 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2806 : * will be assumed. This method only checks directly under the GEOGCS node
2807 : * for units.
2808 : *
2809 : * This method does the same thing as the C function OSRGetAngularUnits().
2810 : *
2811 : * @param ppszName a pointer to be updated with the pointer to the units name.
2812 : * The returned value remains internal to the OGRSpatialReference and should
2813 : * not be freed, or modified. It may be invalidated on the next
2814 : * OGRSpatialReference call.
2815 : *
2816 : * @return the value to multiply by angular distances to transform them to
2817 : * radians.
2818 : * @deprecated Use GetAngularUnits(const char**) const.
2819 : */
2820 :
2821 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2822 :
2823 : {
2824 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2825 : }
2826 :
2827 : /************************************************************************/
2828 : /* OSRGetAngularUnits() */
2829 : /************************************************************************/
2830 :
2831 : /**
2832 : * \brief Fetch angular geographic coordinate system units.
2833 : *
2834 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2835 : */
2836 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2837 :
2838 : {
2839 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2840 :
2841 1 : return ToPointer(hSRS)->GetAngularUnits(
2842 1 : const_cast<const char **>(ppszName));
2843 : }
2844 :
2845 : /************************************************************************/
2846 : /* SetLinearUnitsAndUpdateParameters() */
2847 : /************************************************************************/
2848 :
2849 : /**
2850 : * \brief Set the linear units for the projection.
2851 : *
2852 : * This method creates a UNIT subnode with the specified values as a
2853 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2854 : * SetLinearUnits() method, but it also updates all existing linear
2855 : * projection parameter values from the old units to the new units.
2856 : *
2857 : * @param pszName the units name to be used. Some preferred units
2858 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2859 : * and SRS_UL_US_FOOT.
2860 : *
2861 : * @param dfInMeters the value to multiple by a length in the indicated
2862 : * units to transform to meters. Some standard conversion factors can
2863 : * be found in ogr_srs_api.h.
2864 : *
2865 : * @param pszUnitAuthority Unit authority name. Or nullptr
2866 : *
2867 : * @param pszUnitCode Unit code. Or nullptr
2868 : *
2869 : * @return OGRERR_NONE on success.
2870 : */
2871 :
2872 38 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2873 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2874 : const char *pszUnitCode)
2875 :
2876 : {
2877 76 : TAKE_OPTIONAL_LOCK();
2878 :
2879 38 : if (dfInMeters <= 0.0)
2880 0 : return OGRERR_FAILURE;
2881 :
2882 38 : d->refreshProjObj();
2883 38 : if (!d->m_pj_crs)
2884 0 : return OGRERR_FAILURE;
2885 :
2886 38 : d->demoteFromBoundCRS();
2887 38 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2888 : {
2889 76 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2890 38 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2891 : pszUnitAuthority, pszUnitCode, true));
2892 : }
2893 38 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2894 : pszName, dfInMeters,
2895 : pszUnitAuthority, pszUnitCode));
2896 38 : d->undoDemoteFromBoundCRS();
2897 :
2898 38 : d->m_osLinearUnits = pszName;
2899 38 : d->dfToMeter = dfInMeters;
2900 :
2901 38 : return OGRERR_NONE;
2902 : }
2903 :
2904 : /************************************************************************/
2905 : /* OSRSetLinearUnitsAndUpdateParameters() */
2906 : /************************************************************************/
2907 :
2908 : /**
2909 : * \brief Set the linear units for the projection.
2910 : *
2911 : * This function is the same as
2912 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2913 : */
2914 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2915 : const char *pszUnits,
2916 : double dfInMeters)
2917 :
2918 : {
2919 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2920 : OGRERR_FAILURE);
2921 :
2922 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2923 1 : dfInMeters);
2924 : }
2925 :
2926 : /************************************************************************/
2927 : /* SetLinearUnits() */
2928 : /************************************************************************/
2929 :
2930 : /**
2931 : * \brief Set the linear units for the projection.
2932 : *
2933 : * This method creates a UNIT subnode with the specified values as a
2934 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2935 : * Geographic 3D CRS the vertical axis units will be set.
2936 : *
2937 : * This method does the same as the C function OSRSetLinearUnits().
2938 : *
2939 : * @param pszUnitsName the units name to be used. Some preferred units
2940 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2941 : * and SRS_UL_US_FOOT.
2942 : *
2943 : * @param dfInMeters the value to multiple by a length in the indicated
2944 : * units to transform to meters. Some standard conversion factors can
2945 : * be found in ogr_srs_api.h.
2946 : *
2947 : * @return OGRERR_NONE on success.
2948 : */
2949 :
2950 7483 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2951 : double dfInMeters)
2952 :
2953 : {
2954 7483 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2955 : }
2956 :
2957 : /************************************************************************/
2958 : /* OSRSetLinearUnits() */
2959 : /************************************************************************/
2960 :
2961 : /**
2962 : * \brief Set the linear units for the projection.
2963 : *
2964 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2965 : */
2966 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2967 : double dfInMeters)
2968 :
2969 : {
2970 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2971 :
2972 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2973 : }
2974 :
2975 : /************************************************************************/
2976 : /* SetTargetLinearUnits() */
2977 : /************************************************************************/
2978 :
2979 : /**
2980 : * \brief Set the linear units for the projection.
2981 : *
2982 : * This method creates a UNIT subnode with the specified values as a
2983 : * child of the target node.
2984 : *
2985 : * This method does the same as the C function OSRSetTargetLinearUnits().
2986 : *
2987 : * @param pszTargetKey the keyword to set the linear units for.
2988 : * i.e. "PROJCS" or "VERT_CS"
2989 : *
2990 : * @param pszUnitsName the units name to be used. Some preferred units
2991 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2992 : * and SRS_UL_US_FOOT.
2993 : *
2994 : * @param dfInMeters the value to multiple by a length in the indicated
2995 : * units to transform to meters. Some standard conversion factors can
2996 : * be found in ogr_srs_api.h.
2997 : *
2998 : * @param pszUnitAuthority Unit authority name. Or nullptr
2999 : *
3000 : * @param pszUnitCode Unit code. Or nullptr
3001 : *
3002 : * @return OGRERR_NONE on success.
3003 : *
3004 : */
3005 :
3006 12013 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3007 : const char *pszUnitsName,
3008 : double dfInMeters,
3009 : const char *pszUnitAuthority,
3010 : const char *pszUnitCode)
3011 :
3012 : {
3013 24026 : TAKE_OPTIONAL_LOCK();
3014 :
3015 12013 : if (dfInMeters <= 0.0)
3016 0 : return OGRERR_FAILURE;
3017 :
3018 12013 : d->refreshProjObj();
3019 12013 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3020 12013 : if (pszTargetKey == nullptr)
3021 : {
3022 12013 : if (!d->m_pj_crs)
3023 0 : return OGRERR_FAILURE;
3024 :
3025 12013 : d->demoteFromBoundCRS();
3026 12013 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3027 : {
3028 18224 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3029 9112 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3030 : pszUnitAuthority, pszUnitCode, false));
3031 : }
3032 24026 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3033 12013 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3034 : pszUnitAuthority, pszUnitCode));
3035 12013 : d->undoDemoteFromBoundCRS();
3036 :
3037 12013 : d->m_osLinearUnits = pszUnitsName;
3038 12013 : d->dfToMeter = dfInMeters;
3039 :
3040 12013 : return OGRERR_NONE;
3041 : }
3042 :
3043 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3044 :
3045 0 : if (poCS == nullptr)
3046 0 : return OGRERR_FAILURE;
3047 :
3048 0 : char szValue[128] = {'\0'};
3049 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3050 0 : dfInMeters > std::numeric_limits<int>::min() &&
3051 0 : dfInMeters == static_cast<int>(dfInMeters))
3052 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3053 : else
3054 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3055 :
3056 0 : OGR_SRSNode *poUnits = nullptr;
3057 0 : if (poCS->FindChild("UNIT") >= 0)
3058 : {
3059 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3060 0 : if (poUnits->GetChildCount() < 2)
3061 0 : return OGRERR_FAILURE;
3062 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3063 0 : poUnits->GetChild(1)->SetValue(szValue);
3064 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3065 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3066 : }
3067 : else
3068 : {
3069 0 : poUnits = new OGR_SRSNode("UNIT");
3070 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3071 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3072 :
3073 0 : poCS->AddChild(poUnits);
3074 : }
3075 :
3076 0 : return OGRERR_NONE;
3077 : }
3078 :
3079 : /************************************************************************/
3080 : /* OSRSetLinearUnits() */
3081 : /************************************************************************/
3082 :
3083 : /**
3084 : * \brief Set the linear units for the target node.
3085 : *
3086 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3087 : *
3088 : */
3089 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3090 : const char *pszTargetKey, const char *pszUnits,
3091 : double dfInMeters)
3092 :
3093 : {
3094 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3095 :
3096 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3097 1 : dfInMeters);
3098 : }
3099 :
3100 : /************************************************************************/
3101 : /* GetLinearUnits() */
3102 : /************************************************************************/
3103 :
3104 : /**
3105 : * \brief Fetch linear projection units.
3106 : *
3107 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3108 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3109 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3110 : * axis units will be returned.
3111 : *
3112 : * This method does the same thing as the C function OSRGetLinearUnits()
3113 : *
3114 : * @param ppszName a pointer to be updated with the pointer to the units name.
3115 : * The returned value remains internal to the OGRSpatialReference and should
3116 : * not be freed, or modified. It may be invalidated on the next
3117 : * OGRSpatialReference call.
3118 : *
3119 : * @return the value to multiply by linear distances to transform them to
3120 : * meters.
3121 : * @deprecated Use GetLinearUnits(const char**) const.
3122 : */
3123 :
3124 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3125 :
3126 : {
3127 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3128 : }
3129 :
3130 : /**
3131 : * \brief Fetch linear projection units.
3132 : *
3133 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3134 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3135 : * for units.
3136 : *
3137 : * This method does the same thing as the C function OSRGetLinearUnits()
3138 : *
3139 : * @param ppszName a pointer to be updated with the pointer to the units name.
3140 : * The returned value remains internal to the OGRSpatialReference and should
3141 : * not be freed, or modified. It may be invalidated on the next
3142 : * OGRSpatialReference call.
3143 : *
3144 : * @return the value to multiply by linear distances to transform them to
3145 : * meters.
3146 : */
3147 :
3148 21876 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3149 :
3150 : {
3151 21876 : return GetTargetLinearUnits(nullptr, ppszName);
3152 : }
3153 :
3154 : /************************************************************************/
3155 : /* OSRGetLinearUnits() */
3156 : /************************************************************************/
3157 :
3158 : /**
3159 : * \brief Fetch linear projection units.
3160 : *
3161 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3162 : */
3163 255 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3164 :
3165 : {
3166 255 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3167 :
3168 255 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3169 : }
3170 :
3171 : /************************************************************************/
3172 : /* GetTargetLinearUnits() */
3173 : /************************************************************************/
3174 :
3175 : /**
3176 : * \brief Fetch linear units for target.
3177 : *
3178 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3179 : *
3180 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3181 : *
3182 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3183 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3184 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3185 : * @param ppszName a pointer to be updated with the pointer to the units name.
3186 : * The returned value remains internal to the OGRSpatialReference and should not
3187 : * be freed, or modified. It may be invalidated on the next
3188 : * OGRSpatialReference call. ppszName can be set to NULL.
3189 : *
3190 : * @return the value to multiply by linear distances to transform them to
3191 : * meters.
3192 : *
3193 : * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3194 : * const.
3195 : */
3196 :
3197 22030 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3198 : const char **ppszName) const
3199 :
3200 : {
3201 44060 : TAKE_OPTIONAL_LOCK();
3202 :
3203 22030 : d->refreshProjObj();
3204 :
3205 22030 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3206 22030 : if (pszTargetKey == nullptr)
3207 : {
3208 : // Use cached result if available
3209 21938 : if (!d->m_osLinearUnits.empty())
3210 : {
3211 9285 : if (ppszName)
3212 8407 : *ppszName = d->m_osLinearUnits.c_str();
3213 9285 : return d->dfToMeter;
3214 : }
3215 :
3216 : while (true)
3217 : {
3218 12653 : if (d->m_pj_crs == nullptr)
3219 : {
3220 244 : break;
3221 : }
3222 :
3223 12409 : d->demoteFromBoundCRS();
3224 12409 : PJ *coordSys = nullptr;
3225 12409 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3226 : {
3227 37 : for (int iComponent = 0; iComponent < 2; iComponent++)
3228 : {
3229 37 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3230 37 : d->m_pj_crs, iComponent);
3231 37 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3232 : {
3233 : auto temp =
3234 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3235 0 : proj_destroy(subCRS);
3236 0 : subCRS = temp;
3237 : }
3238 74 : if (subCRS &&
3239 37 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3240 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3241 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3242 : {
3243 31 : coordSys = proj_crs_get_coordinate_system(
3244 : d->getPROJContext(), subCRS);
3245 31 : proj_destroy(subCRS);
3246 31 : break;
3247 : }
3248 6 : else if (subCRS)
3249 : {
3250 6 : proj_destroy(subCRS);
3251 : }
3252 : }
3253 31 : if (coordSys == nullptr)
3254 : {
3255 0 : d->undoDemoteFromBoundCRS();
3256 0 : break;
3257 : }
3258 : }
3259 : else
3260 : {
3261 12378 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3262 12378 : d->m_pj_crs);
3263 : }
3264 :
3265 12409 : d->undoDemoteFromBoundCRS();
3266 12409 : if (!coordSys)
3267 : {
3268 0 : break;
3269 : }
3270 12409 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3271 :
3272 12409 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3273 2296 : csType != PJ_CS_TYPE_VERTICAL &&
3274 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3275 : csType != PJ_CS_TYPE_SPHERICAL)
3276 : {
3277 0 : proj_destroy(coordSys);
3278 0 : break;
3279 : }
3280 :
3281 12409 : int axis = 0;
3282 :
3283 12409 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3284 : csType == PJ_CS_TYPE_SPHERICAL)
3285 : {
3286 : const int axisCount =
3287 2296 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3288 :
3289 2296 : if (axisCount == 3)
3290 : {
3291 4 : axis = 2;
3292 : }
3293 : else
3294 : {
3295 2292 : proj_destroy(coordSys);
3296 2292 : break;
3297 : }
3298 : }
3299 :
3300 10117 : double dfConvFactor = 0.0;
3301 10117 : const char *pszUnitName = nullptr;
3302 10117 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3303 : nullptr, nullptr, nullptr, &dfConvFactor,
3304 : &pszUnitName, nullptr, nullptr))
3305 : {
3306 0 : proj_destroy(coordSys);
3307 0 : break;
3308 : }
3309 :
3310 10117 : d->m_osLinearUnits = pszUnitName;
3311 10117 : d->dfToMeter = dfConvFactor;
3312 10117 : if (ppszName)
3313 1305 : *ppszName = d->m_osLinearUnits.c_str();
3314 :
3315 10117 : proj_destroy(coordSys);
3316 10117 : return dfConvFactor;
3317 : }
3318 :
3319 2536 : d->m_osLinearUnits = "unknown";
3320 2536 : d->dfToMeter = 1.0;
3321 :
3322 2536 : if (ppszName != nullptr)
3323 2350 : *ppszName = d->m_osLinearUnits.c_str();
3324 2536 : return 1.0;
3325 : }
3326 :
3327 92 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3328 :
3329 92 : if (ppszName != nullptr)
3330 38 : *ppszName = "unknown";
3331 :
3332 92 : if (poCS == nullptr)
3333 53 : return 1.0;
3334 :
3335 117 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3336 : {
3337 117 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3338 :
3339 117 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3340 : {
3341 39 : if (ppszName != nullptr)
3342 38 : *ppszName = poChild->GetChild(0)->GetValue();
3343 :
3344 39 : return CPLAtof(poChild->GetChild(1)->GetValue());
3345 : }
3346 : }
3347 :
3348 0 : return 1.0;
3349 : }
3350 :
3351 : /**
3352 : * \brief Fetch linear units for target.
3353 : *
3354 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3355 : *
3356 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3357 : *
3358 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3359 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3360 : * GEOCCS and VERT_CS are looked up)
3361 : * @param ppszName a pointer to be updated with the pointer to the units name.
3362 : * The returned value remains internal to the OGRSpatialReference and should not
3363 : * be freed, or modified. It may be invalidated on the next
3364 : * OGRSpatialReference call. ppszName can be set to NULL.
3365 : *
3366 : * @return the value to multiply by linear distances to transform them to
3367 : * meters.
3368 : *
3369 : */
3370 :
3371 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3372 : char **ppszName) const
3373 :
3374 : {
3375 0 : return GetTargetLinearUnits(pszTargetKey,
3376 0 : const_cast<const char **>(ppszName));
3377 : }
3378 :
3379 : /************************************************************************/
3380 : /* OSRGetTargetLinearUnits() */
3381 : /************************************************************************/
3382 :
3383 : /**
3384 : * \brief Fetch linear projection units.
3385 : *
3386 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3387 : *
3388 : */
3389 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3390 : const char *pszTargetKey, char **ppszName)
3391 :
3392 : {
3393 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3394 :
3395 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3396 4 : pszTargetKey, const_cast<const char **>(ppszName));
3397 : }
3398 :
3399 : /************************************************************************/
3400 : /* GetPrimeMeridian() */
3401 : /************************************************************************/
3402 :
3403 : /**
3404 : * \brief Fetch prime meridian info.
3405 : *
3406 : * Returns the offset of the prime meridian from greenwich in degrees,
3407 : * and the prime meridian name (if requested). If no PRIMEM value exists
3408 : * in the coordinate system definition a value of "Greenwich" and an
3409 : * offset of 0.0 is assumed.
3410 : *
3411 : * If the prime meridian name is returned, the pointer is to an internal
3412 : * copy of the name. It should not be freed, altered or depended on after
3413 : * the next OGR call.
3414 : *
3415 : * This method is the same as the C function OSRGetPrimeMeridian().
3416 : *
3417 : * @param ppszName return location for prime meridian name. If NULL, name
3418 : * is not returned.
3419 : *
3420 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3421 : * degrees.
3422 : * @deprecated Use GetPrimeMeridian(const char**) const.
3423 : */
3424 :
3425 1550 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3426 :
3427 : {
3428 3100 : TAKE_OPTIONAL_LOCK();
3429 :
3430 1550 : d->refreshProjObj();
3431 :
3432 1550 : if (!d->m_osPrimeMeridianName.empty())
3433 : {
3434 87 : if (ppszName != nullptr)
3435 11 : *ppszName = d->m_osPrimeMeridianName.c_str();
3436 87 : return d->dfFromGreenwich;
3437 : }
3438 :
3439 : while (true)
3440 : {
3441 1463 : if (!d->m_pj_crs)
3442 0 : break;
3443 :
3444 1463 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3445 1463 : if (!pm)
3446 0 : break;
3447 :
3448 1463 : d->m_osPrimeMeridianName = proj_get_name(pm);
3449 1463 : if (ppszName)
3450 20 : *ppszName = d->m_osPrimeMeridianName.c_str();
3451 1463 : double dfLongitude = 0.0;
3452 1463 : double dfConvFactor = 0.0;
3453 1463 : proj_prime_meridian_get_parameters(
3454 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3455 1463 : proj_destroy(pm);
3456 2926 : d->dfFromGreenwich =
3457 1463 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3458 1463 : return d->dfFromGreenwich;
3459 : }
3460 :
3461 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3462 0 : d->dfFromGreenwich = 0.0;
3463 0 : if (ppszName != nullptr)
3464 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3465 0 : return d->dfFromGreenwich;
3466 : }
3467 :
3468 : /**
3469 : * \brief Fetch prime meridian info.
3470 : *
3471 : * Returns the offset of the prime meridian from greenwich in degrees,
3472 : * and the prime meridian name (if requested). If no PRIMEM value exists
3473 : * in the coordinate system definition a value of "Greenwich" and an
3474 : * offset of 0.0 is assumed.
3475 : *
3476 : * If the prime meridian name is returned, the pointer is to an internal
3477 : * copy of the name. It should not be freed, altered or depended on after
3478 : * the next OGR call.
3479 : *
3480 : * This method is the same as the C function OSRGetPrimeMeridian().
3481 : *
3482 : * @param ppszName return location for prime meridian name. If NULL, name
3483 : * is not returned.
3484 : *
3485 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3486 : * degrees.
3487 : */
3488 :
3489 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3490 :
3491 : {
3492 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3493 : }
3494 :
3495 : /************************************************************************/
3496 : /* OSRGetPrimeMeridian() */
3497 : /************************************************************************/
3498 :
3499 : /**
3500 : * \brief Fetch prime meridian info.
3501 : *
3502 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3503 : */
3504 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3505 :
3506 : {
3507 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3508 :
3509 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3510 0 : const_cast<const char **>(ppszName));
3511 : }
3512 :
3513 : /************************************************************************/
3514 : /* SetGeogCS() */
3515 : /************************************************************************/
3516 :
3517 : /**
3518 : * \brief Set geographic coordinate system.
3519 : *
3520 : * This method is used to set the datum, ellipsoid, prime meridian and
3521 : * angular units for a geographic coordinate system. It can be used on its
3522 : * own to establish a geographic spatial reference, or applied to a
3523 : * projected coordinate system to establish the underlying geographic
3524 : * coordinate system.
3525 : *
3526 : * This method does the same as the C function OSRSetGeogCS().
3527 : *
3528 : * @param pszGeogName user visible name for the geographic coordinate system
3529 : * (not to serve as a key).
3530 : *
3531 : * @param pszDatumName key name for this datum. The OpenGIS specification
3532 : * lists some known values, and otherwise EPSG datum names with a standard
3533 : * transformation are considered legal keys.
3534 : *
3535 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3536 : *
3537 : * @param dfSemiMajor the semi major axis of the spheroid.
3538 : *
3539 : * @param dfInvFlattening the inverse flattening for the spheroid.
3540 : * This can be computed from the semi minor axis as
3541 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3542 : *
3543 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3544 : * If this is NULL a default value of "Greenwich" will be used.
3545 : *
3546 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3547 : * meridian. Always in Degrees
3548 : *
3549 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3550 : * standard names). If NULL a value of "degrees" will be assumed.
3551 : *
3552 : * @param dfConvertToRadians value to multiply angular units by to transform
3553 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3554 : * pszAngularUnits is NULL.
3555 : *
3556 : * @return OGRERR_NONE on success.
3557 : */
3558 :
3559 9831 : OGRErr OGRSpatialReference::SetGeogCS(
3560 : const char *pszGeogName, const char *pszDatumName,
3561 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3562 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3563 : double dfConvertToRadians)
3564 :
3565 : {
3566 19662 : TAKE_OPTIONAL_LOCK();
3567 :
3568 9831 : d->bNormInfoSet = FALSE;
3569 9831 : d->m_osAngularUnits.clear();
3570 9831 : d->m_dfAngularUnitToRadian = 0.0;
3571 9831 : d->m_osPrimeMeridianName.clear();
3572 9831 : d->dfFromGreenwich = 0.0;
3573 :
3574 : /* -------------------------------------------------------------------- */
3575 : /* For a geocentric coordinate system we want to set the datum */
3576 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3577 : /* temporary srs and use the copy method which has special */
3578 : /* handling for GEOCCS. */
3579 : /* -------------------------------------------------------------------- */
3580 9831 : if (IsGeocentric())
3581 : {
3582 4 : OGRSpatialReference oGCS;
3583 :
3584 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3585 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3586 : dfConvertToRadians);
3587 2 : return CopyGeogCSFrom(&oGCS);
3588 : }
3589 :
3590 9829 : auto cs = proj_create_ellipsoidal_2D_cs(
3591 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3592 : dfConvertToRadians);
3593 : // Prime meridian expressed in Degree
3594 9829 : auto obj = proj_create_geographic_crs(
3595 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3596 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3597 9829 : proj_destroy(cs);
3598 :
3599 14910 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3600 5081 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3601 : {
3602 4748 : d->setPjCRS(obj);
3603 : }
3604 5081 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3605 : {
3606 10162 : d->setPjCRS(
3607 5081 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3608 5081 : proj_destroy(obj);
3609 : }
3610 : else
3611 : {
3612 0 : proj_destroy(obj);
3613 : }
3614 :
3615 9829 : return OGRERR_NONE;
3616 : }
3617 :
3618 : /************************************************************************/
3619 : /* OSRSetGeogCS() */
3620 : /************************************************************************/
3621 :
3622 : /**
3623 : * \brief Set geographic coordinate system.
3624 : *
3625 : * This function is the same as OGRSpatialReference::SetGeogCS()
3626 : */
3627 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3628 : const char *pszDatumName, const char *pszSpheroidName,
3629 : double dfSemiMajor, double dfInvFlattening,
3630 : const char *pszPMName, double dfPMOffset,
3631 : const char *pszAngularUnits, double dfConvertToRadians)
3632 :
3633 : {
3634 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3635 :
3636 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3637 : pszSpheroidName, dfSemiMajor,
3638 : dfInvFlattening, pszPMName, dfPMOffset,
3639 18 : pszAngularUnits, dfConvertToRadians);
3640 : }
3641 :
3642 : /************************************************************************/
3643 : /* SetWellKnownGeogCS() */
3644 : /************************************************************************/
3645 :
3646 : /**
3647 : * \brief Set a GeogCS based on well known name.
3648 : *
3649 : * This may be called on an empty OGRSpatialReference to make a geographic
3650 : * coordinate system, or on something with an existing PROJCS node to
3651 : * set the underlying geographic coordinate system of a projected coordinate
3652 : * system.
3653 : *
3654 : * The following well known text values are currently supported,
3655 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3656 : * <ul>
3657 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3658 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3659 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3660 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3661 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3662 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3663 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3664 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3665 : * </ul>
3666 : *
3667 : * @param pszName name of well known geographic coordinate system.
3668 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3669 : * recognised, the target object is already initialized, or an EPSG value
3670 : * can't be successfully looked up.
3671 : */
3672 :
3673 4953 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3674 :
3675 : {
3676 9906 : TAKE_OPTIONAL_LOCK();
3677 :
3678 : /* -------------------------------------------------------------------- */
3679 : /* Check for EPSG authority numbers. */
3680 : /* -------------------------------------------------------------------- */
3681 4953 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3682 : {
3683 84 : OGRSpatialReference oSRS2;
3684 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3685 42 : if (eErr != OGRERR_NONE)
3686 0 : return eErr;
3687 :
3688 42 : if (!oSRS2.IsGeographic())
3689 0 : return OGRERR_FAILURE;
3690 :
3691 42 : return CopyGeogCSFrom(&oSRS2);
3692 : }
3693 :
3694 : /* -------------------------------------------------------------------- */
3695 : /* Check for simple names. */
3696 : /* -------------------------------------------------------------------- */
3697 4911 : const char *pszWKT = nullptr;
3698 :
3699 4911 : if (EQUAL(pszName, "WGS84"))
3700 : {
3701 2793 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3702 : }
3703 2118 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3704 : {
3705 1177 : pszWKT =
3706 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3707 : "ELLIPSOID[\"WGS "
3708 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3709 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3710 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3711 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3712 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3713 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3714 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3715 : "ID[\"OGC\",\"CRS84\"]]";
3716 : }
3717 941 : else if (EQUAL(pszName, "WGS72"))
3718 19 : pszWKT =
3719 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3720 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3721 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3722 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3723 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3724 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3725 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3726 :
3727 922 : else if (EQUAL(pszName, "NAD27"))
3728 136 : pszWKT =
3729 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3730 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3731 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3732 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3733 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3734 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3735 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3736 :
3737 786 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3738 0 : pszWKT =
3739 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3740 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3741 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3742 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3743 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3744 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3745 :
3746 786 : else if (EQUAL(pszName, "NAD83"))
3747 782 : pszWKT =
3748 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3749 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3750 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3751 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3752 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3753 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3754 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3755 : "\"EPSG\",\"4269\"]]";
3756 :
3757 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3758 0 : pszWKT =
3759 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3760 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3761 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3762 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3763 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3764 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3765 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3766 :
3767 : else
3768 4 : return OGRERR_FAILURE;
3769 :
3770 : /* -------------------------------------------------------------------- */
3771 : /* Import the WKT */
3772 : /* -------------------------------------------------------------------- */
3773 9814 : OGRSpatialReference oSRS2;
3774 4907 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3775 4907 : if (eErr != OGRERR_NONE)
3776 0 : return eErr;
3777 :
3778 : /* -------------------------------------------------------------------- */
3779 : /* Copy over. */
3780 : /* -------------------------------------------------------------------- */
3781 4907 : return CopyGeogCSFrom(&oSRS2);
3782 : }
3783 :
3784 : /************************************************************************/
3785 : /* OSRSetWellKnownGeogCS() */
3786 : /************************************************************************/
3787 :
3788 : /**
3789 : * \brief Set a GeogCS based on well known name.
3790 : *
3791 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3792 : */
3793 155 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3794 :
3795 : {
3796 155 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3797 :
3798 155 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3799 : }
3800 :
3801 : /************************************************************************/
3802 : /* CopyGeogCSFrom() */
3803 : /************************************************************************/
3804 :
3805 : /**
3806 : * \brief Copy GEOGCS from another OGRSpatialReference.
3807 : *
3808 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3809 : * If this object has a PROJCS root already, the GEOGCS is installed within
3810 : * it, otherwise it is installed as the root.
3811 : *
3812 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3813 : *
3814 : * @return OGRERR_NONE on success or an error code.
3815 : */
3816 5415 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3817 :
3818 : {
3819 5415 : return CopyGeogCSFrom(poSrcSRS, false);
3820 : }
3821 :
3822 : /**
3823 : * \brief Copy GEOGCS from another OGRSpatialReference.
3824 : *
3825 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3826 : * If this object has a PROJCS root already, the GEOGCS is installed within
3827 : * it, otherwise it is installed as the root.
3828 : *
3829 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3830 : * @param bInnerMostGeogCRS Whether the inner-most geographic CRS must be used.
3831 : * This setting makes a difference if this CRS is a
3832 : * derived geographic CRS. Setting bInnerMostGeogCRS
3833 : * to true will then extract its base CRS.
3834 : *
3835 : * @return OGRERR_NONE on success or an error code.
3836 : * @since 3.13.2
3837 : */
3838 5571 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS,
3839 : bool bInnerMostGeogCRS)
3840 :
3841 : {
3842 11142 : TAKE_OPTIONAL_LOCK();
3843 :
3844 5571 : d->bNormInfoSet = FALSE;
3845 5571 : d->m_osAngularUnits.clear();
3846 5571 : d->m_dfAngularUnitToRadian = 0.0;
3847 5571 : d->m_osPrimeMeridianName.clear();
3848 5571 : d->dfFromGreenwich = 0.0;
3849 :
3850 5571 : d->refreshProjObj();
3851 5571 : poSrcSRS->d->refreshProjObj();
3852 5571 : if (!poSrcSRS->d->m_pj_crs)
3853 : {
3854 1 : return OGRERR_FAILURE;
3855 : }
3856 : auto geodCRS =
3857 5570 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3858 5570 : if (!geodCRS)
3859 : {
3860 0 : return OGRERR_FAILURE;
3861 : }
3862 :
3863 5570 : if (bInnerMostGeogCRS && poSrcSRS->IsDerivedGeographic())
3864 : {
3865 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), geodCRS);
3866 0 : if (!baseCRS)
3867 0 : return OGRERR_FAILURE;
3868 0 : proj_destroy(geodCRS);
3869 0 : geodCRS = baseCRS;
3870 : }
3871 :
3872 : /* -------------------------------------------------------------------- */
3873 : /* Handle geocentric coordinate systems specially. We just */
3874 : /* want to copy the DATUM. */
3875 : /* -------------------------------------------------------------------- */
3876 5570 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3877 : {
3878 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3879 : #if PROJ_VERSION_MAJOR > 7 || \
3880 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3881 : if (datum == nullptr)
3882 : {
3883 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3884 : }
3885 : #endif
3886 3 : if (datum == nullptr)
3887 : {
3888 0 : proj_destroy(geodCRS);
3889 0 : return OGRERR_FAILURE;
3890 : }
3891 :
3892 3 : const char *pszUnitName = nullptr;
3893 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3894 :
3895 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3896 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3897 : unitConvFactor);
3898 3 : proj_destroy(datum);
3899 :
3900 3 : d->setPjCRS(pj_crs);
3901 : }
3902 :
3903 5567 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3904 : {
3905 319 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3906 319 : d->m_pj_crs, geodCRS);
3907 319 : d->setPjCRS(pj_crs);
3908 : }
3909 :
3910 : else
3911 : {
3912 5248 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3913 : }
3914 :
3915 : // Apply TOWGS84 of source CRS
3916 5570 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3917 : {
3918 : auto target =
3919 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3920 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3921 1 : poSrcSRS->d->m_pj_crs);
3922 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3923 : target, co));
3924 1 : proj_destroy(target);
3925 1 : proj_destroy(co);
3926 : }
3927 :
3928 5570 : proj_destroy(geodCRS);
3929 :
3930 5570 : return OGRERR_NONE;
3931 : }
3932 :
3933 : /************************************************************************/
3934 : /* OSRCopyGeogCSFrom() */
3935 : /************************************************************************/
3936 :
3937 : /**
3938 : * \brief Copy GEOGCS from another OGRSpatialReference.
3939 : *
3940 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3941 : */
3942 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3943 : const OGRSpatialReferenceH hSrcSRS)
3944 :
3945 : {
3946 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3947 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3948 :
3949 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3950 : }
3951 :
3952 : /************************************************************************/
3953 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3954 : /************************************************************************/
3955 :
3956 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3957 : *
3958 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3959 : */
3960 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3961 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3962 :
3963 : /**
3964 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3965 : */
3966 2777 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3967 : {
3968 2777 : return SET_FROM_USER_INPUT_LIMITATIONS;
3969 : }
3970 :
3971 : /************************************************************************/
3972 : /* RemoveIDFromMemberOfEnsembles() */
3973 : /************************************************************************/
3974 :
3975 : // cppcheck-suppress constParameterReference
3976 243 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3977 : {
3978 : // Remove "id" from members of datum ensembles for compatibility with
3979 : // older PROJ versions
3980 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3981 : // and https://github.com/OSGeo/PROJ/pull/3221
3982 243 : if (obj.GetType() == CPLJSONObject::Type::Object)
3983 : {
3984 300 : for (auto &subObj : obj.GetChildren())
3985 : {
3986 235 : RemoveIDFromMemberOfEnsembles(subObj);
3987 : }
3988 : }
3989 198 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3990 198 : obj.GetName() == "members")
3991 : {
3992 60 : for (auto &subObj : obj.ToArray())
3993 : {
3994 52 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3995 : {
3996 51 : subObj.Delete("id");
3997 : }
3998 : }
3999 : }
4000 243 : }
4001 :
4002 : /************************************************************************/
4003 : /* SetFromUserInput() */
4004 : /************************************************************************/
4005 :
4006 : /**
4007 : * \brief Set spatial reference from various text formats.
4008 : *
4009 : * This method will examine the provided input, and try to deduce the
4010 : * format, and then use it to initialize the spatial reference system. It
4011 : * may take the following forms:
4012 : *
4013 : * <ol>
4014 : * <li> Well Known Text definition - passed on to importFromWkt().
4015 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4016 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4017 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4018 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4019 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4020 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4021 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4022 : * WGS84 or WGS72.
4023 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4024 : * <li> PROJJSON (PROJ >= 6.2)
4025 : * </ol>
4026 : *
4027 : * It is expected that this method will be extended in the future to support
4028 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4029 : * State Plane definitions.
4030 : *
4031 : * This method is intended to be flexible, but by its nature it is
4032 : * imprecise as it must guess information about the format intended. When
4033 : * possible applications should call the specific method appropriate if the
4034 : * input is known to be in a particular format.
4035 : *
4036 : * This method does the same thing as the OSRSetFromUserInput() function.
4037 : *
4038 : * @param pszDefinition text definition to try to deduce SRS from.
4039 : *
4040 : * @return OGRERR_NONE on success, or an error code if the name isn't
4041 : * recognised, the definition is corrupt, or an EPSG value can't be
4042 : * successfully looked up.
4043 : */
4044 :
4045 23602 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4046 : {
4047 23602 : return SetFromUserInput(pszDefinition, nullptr);
4048 : }
4049 :
4050 : /**
4051 : * \brief Set spatial reference from various text formats.
4052 : *
4053 : * This method will examine the provided input, and try to deduce the
4054 : * format, and then use it to initialize the spatial reference system. It
4055 : * may take the following forms:
4056 : *
4057 : * <ol>
4058 : * <li> Well Known Text definition - passed on to importFromWkt().
4059 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4060 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4061 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4062 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4063 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4064 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4065 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4066 : * WGS84 or WGS72.
4067 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4068 : * <li> PROJJSON (PROJ >= 6.2)
4069 : * </ol>
4070 : *
4071 : * It is expected that this method will be extended in the future to support
4072 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4073 : * State Plane definitions.
4074 : *
4075 : * This method is intended to be flexible, but by its nature it is
4076 : * imprecise as it must guess information about the format intended. When
4077 : * possible applications should call the specific method appropriate if the
4078 : * input is known to be in a particular format.
4079 : *
4080 : * This method does the same thing as the OSRSetFromUserInput() and
4081 : * OSRSetFromUserInputEx() functions.
4082 : *
4083 : * @param pszDefinition text definition to try to deduce SRS from.
4084 : *
4085 : * @param papszOptions NULL terminated list of options, or NULL.
4086 : * <ol>
4087 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4088 : * Whether http:// or https:// access is allowed. Defaults to YES.
4089 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4090 : * Whether reading a file using the Virtual File System layer is allowed
4091 : * (can also involve network access). Defaults to YES.
4092 : * </ol>
4093 : *
4094 : * @return OGRERR_NONE on success, or an error code if the name isn't
4095 : * recognised, the definition is corrupt, or an EPSG value can't be
4096 : * successfully looked up.
4097 : */
4098 :
4099 31320 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4100 : CSLConstList papszOptions)
4101 : {
4102 62640 : TAKE_OPTIONAL_LOCK();
4103 :
4104 : // Skip leading white space
4105 31322 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4106 2 : pszDefinition++;
4107 :
4108 31320 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4109 : {
4110 1 : pszDefinition += 6;
4111 : }
4112 :
4113 : /* -------------------------------------------------------------------- */
4114 : /* Is it a recognised syntax? */
4115 : /* -------------------------------------------------------------------- */
4116 31320 : const char *const wktKeywords[] = {
4117 : // WKT1
4118 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4119 : // WKT2"
4120 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4121 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4122 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4123 485880 : for (const char *keyword : wktKeywords)
4124 : {
4125 463850 : if (STARTS_WITH_CI(pszDefinition, keyword))
4126 : {
4127 9290 : return importFromWkt(pszDefinition);
4128 : }
4129 : }
4130 :
4131 22030 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4132 22030 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4133 : {
4134 12364 : OGRErr eStatus = OGRERR_NONE;
4135 :
4136 12364 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4137 : {
4138 : // Use proj_create() as it allows things like EPSG:3157+4617
4139 : // that are not normally supported by the below code that
4140 : // builds manually a compound CRS
4141 69 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4142 69 : if (!pj)
4143 : {
4144 1 : return OGRERR_FAILURE;
4145 : }
4146 68 : Clear();
4147 68 : d->setPjCRS(pj);
4148 68 : return OGRERR_NONE;
4149 : }
4150 : else
4151 : {
4152 : eStatus =
4153 12295 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4154 : }
4155 :
4156 12295 : return eStatus;
4157 : }
4158 :
4159 9666 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4160 8941 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4161 8940 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4162 8882 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4163 8882 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4164 8882 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4165 784 : return importFromURN(pszDefinition);
4166 :
4167 8882 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4168 8880 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4169 8879 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4170 1721 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4171 1720 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4172 7162 : return importFromCRSURL(pszDefinition);
4173 :
4174 1720 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4175 1 : return importFromWMSAUTO(pszDefinition);
4176 :
4177 : // WMS/WCS OGC codes like OGC:CRS84.
4178 1719 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4179 79 : return SetWellKnownGeogCS(pszDefinition + 4);
4180 :
4181 1640 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4182 1 : return SetWellKnownGeogCS(pszDefinition);
4183 :
4184 1639 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4185 : {
4186 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4187 0 : char *pszCode = strstr(pszFile, ",") + 1;
4188 :
4189 0 : pszCode[-1] = '\0';
4190 :
4191 0 : OGRErr err = importFromDict(pszFile, pszCode);
4192 0 : CPLFree(pszFile);
4193 :
4194 0 : return err;
4195 : }
4196 :
4197 1639 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4198 1634 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4199 : {
4200 593 : Clear();
4201 593 : return SetWellKnownGeogCS(pszDefinition);
4202 : }
4203 :
4204 : // PROJJSON
4205 1046 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4206 179 : (strstr(pszDefinition, "GeodeticCRS") ||
4207 179 : strstr(pszDefinition, "GeographicCRS") ||
4208 129 : strstr(pszDefinition, "ProjectedCRS") ||
4209 0 : strstr(pszDefinition, "VerticalCRS") ||
4210 0 : strstr(pszDefinition, "BoundCRS") ||
4211 0 : strstr(pszDefinition, "CompoundCRS") ||
4212 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4213 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4214 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4215 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4216 0 : strstr(pszDefinition, "EngineeringCRS") ||
4217 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4218 0 : strstr(pszDefinition, "ParametricCRS") ||
4219 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4220 0 : strstr(pszDefinition, "TemporalCRS") ||
4221 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4222 : {
4223 : PJ *pj;
4224 179 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4225 : {
4226 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4227 : // a unknown id.
4228 8 : CPLJSONDocument oCRSDoc;
4229 8 : if (!oCRSDoc.LoadMemory(pszDefinition))
4230 0 : return OGRERR_CORRUPT_DATA;
4231 8 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4232 8 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4233 8 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4234 : }
4235 : else
4236 : {
4237 171 : pj = proj_create(d->getPROJContext(), pszDefinition);
4238 : }
4239 179 : if (!pj)
4240 : {
4241 2 : return OGRERR_FAILURE;
4242 : }
4243 177 : Clear();
4244 177 : d->setPjCRS(pj);
4245 177 : return OGRERR_NONE;
4246 : }
4247 :
4248 867 : if (strstr(pszDefinition, "+proj") != nullptr ||
4249 407 : strstr(pszDefinition, "+init") != nullptr)
4250 460 : return importFromProj4(pszDefinition);
4251 :
4252 407 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4253 382 : STARTS_WITH_CI(pszDefinition, "https://"))
4254 : {
4255 26 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4256 : "ALLOW_NETWORK_ACCESS", "YES")))
4257 0 : return importFromUrl(pszDefinition);
4258 :
4259 26 : CPLError(CE_Failure, CPLE_AppDefined,
4260 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4261 : pszDefinition);
4262 26 : return OGRERR_FAILURE;
4263 : }
4264 :
4265 381 : if (EQUAL(pszDefinition, "osgb:BNG"))
4266 : {
4267 8 : return importFromEPSG(27700);
4268 : }
4269 :
4270 : // Used by German CityGML files
4271 373 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4272 : {
4273 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4274 0 : return SetFromUserInput("EPSG:25832+5783");
4275 : }
4276 373 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4277 : {
4278 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4279 3 : return SetFromUserInput("EPSG:25832+7837");
4280 : }
4281 :
4282 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4283 370 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4284 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4285 370 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4286 8 : return importFromEPSG(6668); // JGD2011
4287 362 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4288 : {
4289 : // FIXME when EPSG attributes a CRS code
4290 3 : return importFromWkt(
4291 : "GEOGCRS[\"JGD2024\",\n"
4292 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4293 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4294 : " LENGTHUNIT[\"metre\",1]]],\n"
4295 : " PRIMEM[\"Greenwich\",0,\n"
4296 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4297 : " CS[ellipsoidal,2],\n"
4298 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4299 : " ORDER[1],\n"
4300 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4301 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4302 : " ORDER[2],\n"
4303 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4304 : " USAGE[\n"
4305 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4306 : " AREA[\"Japan - onshore and offshore.\"],\n"
4307 3 : " BBOX[17.09,122.38,46.05,157.65]]]");
4308 : }
4309 :
4310 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4311 359 : const char *pszDot = strrchr(pszDefinition, ':');
4312 359 : if (pszDot)
4313 : {
4314 125 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4315 : auto authorities =
4316 125 : proj_get_authorities_from_database(d->getPROJContext());
4317 125 : if (authorities)
4318 : {
4319 125 : std::set<std::string> aosCandidateAuthorities;
4320 290 : for (auto iter = authorities; *iter; ++iter)
4321 : {
4322 288 : if (*iter == osPrefix)
4323 : {
4324 123 : aosCandidateAuthorities.clear();
4325 123 : aosCandidateAuthorities.insert(*iter);
4326 123 : break;
4327 : }
4328 : // Deal with "IAU_2015" as authority in the list and input
4329 : // "IAU:code"
4330 165 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4331 165 : 0 &&
4332 0 : (*iter)[osPrefix.size()] == '_')
4333 : {
4334 0 : aosCandidateAuthorities.insert(*iter);
4335 : }
4336 : // Deal with "IAU_2015" as authority in the list and input
4337 : // "IAU:2015:code"
4338 330 : else if (osPrefix.find(':') != std::string::npos &&
4339 165 : osPrefix.size() == strlen(*iter) &&
4340 165 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4341 : {
4342 0 : aosCandidateAuthorities.clear();
4343 0 : aosCandidateAuthorities.insert(*iter);
4344 0 : break;
4345 : }
4346 : }
4347 :
4348 125 : proj_string_list_destroy(authorities);
4349 :
4350 125 : if (!aosCandidateAuthorities.empty())
4351 : {
4352 123 : auto obj = proj_create_from_database(
4353 : d->getPROJContext(),
4354 123 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4355 : PJ_CATEGORY_CRS, false, nullptr);
4356 123 : if (!obj)
4357 : {
4358 16 : return OGRERR_FAILURE;
4359 : }
4360 107 : Clear();
4361 107 : d->setPjCRS(obj);
4362 107 : return OGRERR_NONE;
4363 : }
4364 : }
4365 : }
4366 :
4367 : /* -------------------------------------------------------------------- */
4368 : /* Try to open it as a file. */
4369 : /* -------------------------------------------------------------------- */
4370 236 : if (!CPLTestBool(
4371 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4372 : {
4373 : VSIStatBufL sStat;
4374 40 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4375 20 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4376 : {
4377 0 : CPLError(CE_Failure, CPLE_AppDefined,
4378 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4379 : pszDefinition);
4380 0 : return OGRERR_FAILURE;
4381 : }
4382 : // We used to silently return an error without a CE_Failure message
4383 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4384 20 : return OGRERR_CORRUPT_DATA;
4385 : }
4386 :
4387 432 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4388 216 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4389 216 : if (fp == nullptr)
4390 213 : return OGRERR_CORRUPT_DATA;
4391 :
4392 3 : const size_t nBufMax = 100000;
4393 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4394 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4395 3 : VSIFCloseL(fp);
4396 :
4397 3 : if (nBytes == nBufMax - 1)
4398 : {
4399 0 : CPLDebug("OGR",
4400 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4401 : "but it is to large for our generous buffer. Is it really "
4402 : "just a WKT definition?",
4403 : pszDefinition);
4404 0 : CPLFree(pszBuffer);
4405 0 : return OGRERR_FAILURE;
4406 : }
4407 :
4408 3 : pszBuffer[nBytes] = '\0';
4409 :
4410 3 : char *pszBufPtr = pszBuffer;
4411 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4412 0 : pszBufPtr++;
4413 :
4414 3 : OGRErr err = OGRERR_NONE;
4415 3 : if (pszBufPtr[0] == '<')
4416 0 : err = importFromXML(pszBufPtr);
4417 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4418 3 : strstr(pszBuffer, "+init") != nullptr) &&
4419 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4420 0 : strstr(pszBuffer, "extension") == nullptr))
4421 0 : err = importFromProj4(pszBufPtr);
4422 : else
4423 : {
4424 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4425 : {
4426 0 : pszBufPtr += 6;
4427 : }
4428 :
4429 : // coverity[tainted_data]
4430 3 : err = importFromWkt(pszBufPtr);
4431 : }
4432 :
4433 3 : CPLFree(pszBuffer);
4434 :
4435 3 : return err;
4436 : }
4437 :
4438 : /************************************************************************/
4439 : /* OSRSetFromUserInput() */
4440 : /************************************************************************/
4441 :
4442 : /**
4443 : * \brief Set spatial reference from various text formats.
4444 : *
4445 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4446 : *
4447 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4448 : */
4449 383 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4450 : const char *pszDef)
4451 :
4452 : {
4453 383 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4454 :
4455 383 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4456 : }
4457 :
4458 : /************************************************************************/
4459 : /* OSRSetFromUserInputEx() */
4460 : /************************************************************************/
4461 :
4462 : /**
4463 : * \brief Set spatial reference from various text formats.
4464 : *
4465 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4466 : *
4467 : * @since GDAL 3.9
4468 : */
4469 1265 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4470 : CSLConstList papszOptions)
4471 :
4472 : {
4473 1265 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4474 :
4475 1265 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4476 : }
4477 :
4478 : /************************************************************************/
4479 : /* ImportFromUrl() */
4480 : /************************************************************************/
4481 :
4482 : /**
4483 : * \brief Set spatial reference from a URL.
4484 : *
4485 : * This method will download the spatial reference at a given URL and
4486 : * feed it into SetFromUserInput for you.
4487 : *
4488 : * This method does the same thing as the OSRImportFromUrl() function.
4489 : *
4490 : * @param pszUrl text definition to try to deduce SRS from.
4491 : *
4492 : * @return OGRERR_NONE on success, or an error code with the curl
4493 : * error message if it is unable to download data.
4494 : */
4495 :
4496 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4497 :
4498 : {
4499 10 : TAKE_OPTIONAL_LOCK();
4500 :
4501 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4502 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4503 : {
4504 2 : CPLError(CE_Failure, CPLE_AppDefined,
4505 : "The given string is not recognized as a URL"
4506 : "starting with 'http://' -- %s",
4507 : pszUrl);
4508 2 : return OGRERR_FAILURE;
4509 : }
4510 :
4511 : /* -------------------------------------------------------------------- */
4512 : /* Fetch the result. */
4513 : /* -------------------------------------------------------------------- */
4514 3 : CPLErrorReset();
4515 :
4516 6 : std::string osUrl(pszUrl);
4517 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4518 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4519 : // to query WKT. To allow a static server to be used, rather append a
4520 : // "ogcwkt/" suffix.
4521 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4522 5 : "http://spatialreference.org/ref/"})
4523 : {
4524 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4525 : {
4526 : const CPLStringList aosTokens(
4527 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4528 3 : if (aosTokens.size() == 2)
4529 : {
4530 2 : osUrl = "https://spatialreference.org/ref/";
4531 2 : osUrl += aosTokens[0]; // authority
4532 2 : osUrl += '/';
4533 2 : osUrl += aosTokens[1]; // code
4534 2 : osUrl += "/ogcwkt/";
4535 : }
4536 3 : break;
4537 : }
4538 : }
4539 :
4540 3 : const char *pszTimeout = "TIMEOUT=10";
4541 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4542 :
4543 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4544 :
4545 : /* -------------------------------------------------------------------- */
4546 : /* Try to handle errors. */
4547 : /* -------------------------------------------------------------------- */
4548 :
4549 3 : if (psResult == nullptr)
4550 0 : return OGRERR_FAILURE;
4551 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4552 3 : psResult->pabyData == nullptr)
4553 : {
4554 0 : if (CPLGetLastErrorNo() == 0)
4555 : {
4556 0 : CPLError(CE_Failure, CPLE_AppDefined,
4557 : "No data was returned from the given URL");
4558 : }
4559 0 : CPLHTTPDestroyResult(psResult);
4560 0 : return OGRERR_FAILURE;
4561 : }
4562 :
4563 3 : if (psResult->nStatus != 0)
4564 : {
4565 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4566 : psResult->nStatus, psResult->pszErrBuf);
4567 0 : CPLHTTPDestroyResult(psResult);
4568 0 : return OGRERR_FAILURE;
4569 : }
4570 :
4571 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4572 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4573 3 : STARTS_WITH_CI(pszData, "https://"))
4574 : {
4575 0 : CPLError(CE_Failure, CPLE_AppDefined,
4576 : "The data that was downloaded also starts with 'http://' "
4577 : "and cannot be passed into SetFromUserInput. Is this "
4578 : "really a spatial reference definition? ");
4579 0 : CPLHTTPDestroyResult(psResult);
4580 0 : return OGRERR_FAILURE;
4581 : }
4582 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4583 : {
4584 0 : CPLHTTPDestroyResult(psResult);
4585 0 : return OGRERR_FAILURE;
4586 : }
4587 :
4588 3 : CPLHTTPDestroyResult(psResult);
4589 3 : return OGRERR_NONE;
4590 : }
4591 :
4592 : /************************************************************************/
4593 : /* OSRimportFromUrl() */
4594 : /************************************************************************/
4595 :
4596 : /**
4597 : * \brief Set spatial reference from a URL.
4598 : *
4599 : * This function is the same as OGRSpatialReference::importFromUrl()
4600 : */
4601 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4602 :
4603 : {
4604 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4605 :
4606 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4607 : }
4608 :
4609 : /************************************************************************/
4610 : /* importFromURNPart() */
4611 : /************************************************************************/
4612 7373 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4613 : const char *pszCode,
4614 : const char *pszURN)
4615 : {
4616 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4617 : (void)this;
4618 : (void)pszAuthority;
4619 : (void)pszCode;
4620 : (void)pszURN;
4621 : return OGRERR_FAILURE;
4622 : #else
4623 : /* -------------------------------------------------------------------- */
4624 : /* Is this an EPSG code? Note that we import it with EPSG */
4625 : /* preferred axis ordering for geographic coordinate systems. */
4626 : /* -------------------------------------------------------------------- */
4627 7373 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4628 6273 : return importFromEPSGA(atoi(pszCode));
4629 :
4630 : /* -------------------------------------------------------------------- */
4631 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4632 : /* -------------------------------------------------------------------- */
4633 1100 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4634 0 : return importFromDict("IAU2000.wkt", pszCode);
4635 :
4636 : /* -------------------------------------------------------------------- */
4637 : /* Is this an OGC code? */
4638 : /* -------------------------------------------------------------------- */
4639 1100 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4640 : {
4641 1 : CPLError(CE_Failure, CPLE_AppDefined,
4642 : "URN %s has unrecognized authority.", pszURN);
4643 1 : return OGRERR_FAILURE;
4644 : }
4645 :
4646 1099 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4647 1087 : return SetWellKnownGeogCS(pszCode);
4648 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4649 0 : return SetWellKnownGeogCS(pszCode);
4650 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4651 0 : return SetWellKnownGeogCS(pszCode);
4652 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4653 10 : return SetWellKnownGeogCS("CRS84");
4654 :
4655 : /* -------------------------------------------------------------------- */
4656 : /* Handle auto codes. We need to convert from format */
4657 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4658 : /* -------------------------------------------------------------------- */
4659 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4660 : {
4661 2 : char szWMSAuto[100] = {'\0'};
4662 :
4663 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4664 0 : return OGRERR_FAILURE;
4665 :
4666 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4667 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4668 : {
4669 26 : if (szWMSAuto[i] == ':')
4670 4 : szWMSAuto[i] = ',';
4671 : }
4672 :
4673 2 : return importFromWMSAUTO(szWMSAuto);
4674 : }
4675 :
4676 : /* -------------------------------------------------------------------- */
4677 : /* Not a recognise OGC item. */
4678 : /* -------------------------------------------------------------------- */
4679 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4680 : pszURN);
4681 :
4682 0 : return OGRERR_FAILURE;
4683 : #endif
4684 : }
4685 :
4686 : /************************************************************************/
4687 : /* importFromURN() */
4688 : /* */
4689 : /* See OGC recommendation paper 06-023r1 or later for details. */
4690 : /************************************************************************/
4691 :
4692 : /**
4693 : * \brief Initialize from OGC URN.
4694 : *
4695 : * Initializes this spatial reference from a coordinate system defined
4696 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4697 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4698 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4699 : *
4700 : * This method is also support through SetFromUserInput() which can
4701 : * normally be used for URNs.
4702 : *
4703 : * @param pszURN the urn string.
4704 : *
4705 : * @return OGRERR_NONE on success or an error code.
4706 : */
4707 :
4708 845 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4709 :
4710 : {
4711 845 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4712 1605 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4713 760 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4714 : CPL_VALUE_INTEGER)
4715 : {
4716 757 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4717 : }
4718 :
4719 176 : TAKE_OPTIONAL_LOCK();
4720 :
4721 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4722 :
4723 : // PROJ 8.2.0 has support for IAU codes now.
4724 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4725 : /* -------------------------------------------------------------------- */
4726 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4727 : /* -------------------------------------------------------------------- */
4728 : const char *pszIAU = strstr(pszURN, "IAU");
4729 : if (pszIAU)
4730 : {
4731 : const char *pszCode = strchr(pszIAU, ':');
4732 : if (pszCode)
4733 : {
4734 : ++pszCode;
4735 : if (*pszCode == ':')
4736 : ++pszCode;
4737 : return importFromDict("IAU2000.wkt", pszCode);
4738 : }
4739 : }
4740 : #endif
4741 :
4742 : if (strlen(pszURN) >= 1000)
4743 : {
4744 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4745 : return OGRERR_CORRUPT_DATA;
4746 : }
4747 : auto obj = proj_create(d->getPROJContext(), pszURN);
4748 : if (!obj)
4749 : {
4750 : return OGRERR_FAILURE;
4751 : }
4752 : Clear();
4753 : d->setPjCRS(obj);
4754 : return OGRERR_NONE;
4755 : #else
4756 88 : const char *pszCur = nullptr;
4757 :
4758 88 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4759 23 : pszCur = pszURN + 16;
4760 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4761 1 : pszCur = pszURN + 20;
4762 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4763 62 : pszCur = pszURN + 18;
4764 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4765 0 : pszCur = pszURN + 16;
4766 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4767 0 : pszCur = pszURN + 20;
4768 : else
4769 : {
4770 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4771 : pszURN);
4772 2 : return OGRERR_FAILURE;
4773 : }
4774 :
4775 : /* -------------------------------------------------------------------- */
4776 : /* Clear any existing definition. */
4777 : /* -------------------------------------------------------------------- */
4778 86 : Clear();
4779 :
4780 : /* -------------------------------------------------------------------- */
4781 : /* Find code (ignoring version) out of string like: */
4782 : /* */
4783 : /* authority:[version]:code */
4784 : /* -------------------------------------------------------------------- */
4785 86 : const char *pszAuthority = pszCur;
4786 :
4787 : // skip authority
4788 414 : while (*pszCur != ':' && *pszCur)
4789 328 : pszCur++;
4790 86 : if (*pszCur == ':')
4791 86 : pszCur++;
4792 :
4793 : // skip version
4794 86 : const char *pszBeforeVersion = pszCur;
4795 387 : while (*pszCur != ':' && *pszCur)
4796 301 : pszCur++;
4797 86 : if (*pszCur == ':')
4798 58 : pszCur++;
4799 : else
4800 : // We come here in the case, the content to parse is authority:code
4801 : // (instead of authority::code) which is probably illegal according to
4802 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4803 : // for example in what is returned by GeoServer.
4804 28 : pszCur = pszBeforeVersion;
4805 :
4806 86 : const char *pszCode = pszCur;
4807 :
4808 86 : const char *pszComma = strchr(pszCur, ',');
4809 86 : if (pszComma == nullptr)
4810 85 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4811 :
4812 : // There's a second part with the vertical SRS.
4813 1 : pszCur = pszComma + 1;
4814 1 : if (!STARTS_WITH(pszCur, "crs:"))
4815 : {
4816 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4817 : pszURN);
4818 0 : return OGRERR_FAILURE;
4819 : }
4820 :
4821 1 : pszCur += 4;
4822 :
4823 1 : char *pszFirstCode = CPLStrdup(pszCode);
4824 1 : pszFirstCode[pszComma - pszCode] = '\0';
4825 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4826 1 : CPLFree(pszFirstCode);
4827 :
4828 : // Do we want to turn this into a compound definition
4829 : // with a vertical datum?
4830 1 : if (eStatus != OGRERR_NONE)
4831 0 : return eStatus;
4832 :
4833 : /* -------------------------------------------------------------------- */
4834 : /* Find code (ignoring version) out of string like: */
4835 : /* */
4836 : /* authority:[version]:code */
4837 : /* -------------------------------------------------------------------- */
4838 1 : pszAuthority = pszCur;
4839 :
4840 : // skip authority
4841 5 : while (*pszCur != ':' && *pszCur)
4842 4 : pszCur++;
4843 1 : if (*pszCur == ':')
4844 1 : pszCur++;
4845 :
4846 : // skip version
4847 1 : pszBeforeVersion = pszCur;
4848 1 : while (*pszCur != ':' && *pszCur)
4849 0 : pszCur++;
4850 1 : if (*pszCur == ':')
4851 1 : pszCur++;
4852 : else
4853 0 : pszCur = pszBeforeVersion;
4854 :
4855 1 : pszCode = pszCur;
4856 :
4857 2 : OGRSpatialReference oVertSRS;
4858 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4859 1 : if (eStatus == OGRERR_NONE)
4860 : {
4861 1 : OGRSpatialReference oHorizSRS(*this);
4862 :
4863 1 : Clear();
4864 :
4865 1 : oHorizSRS.d->refreshProjObj();
4866 1 : oVertSRS.d->refreshProjObj();
4867 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4868 0 : return OGRERR_FAILURE;
4869 :
4870 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4871 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4872 :
4873 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4874 1 : osName += " + ";
4875 1 : osName += pszVertName ? pszVertName : "";
4876 :
4877 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4878 : }
4879 :
4880 1 : return eStatus;
4881 : #endif
4882 : }
4883 :
4884 : /************************************************************************/
4885 : /* importFromCRSURL() */
4886 : /* */
4887 : /* See OGC Best Practice document 11-135 for details. */
4888 : /************************************************************************/
4889 :
4890 : /**
4891 : * \brief Initialize from OGC URL.
4892 : *
4893 : * Initializes this spatial reference from a coordinate system defined
4894 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4895 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4896 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4897 : *
4898 : * This method is also supported through SetFromUserInput() which can
4899 : * normally be used for URLs.
4900 : *
4901 : * @param pszURL the URL string.
4902 : *
4903 : * @return OGRERR_NONE on success or an error code.
4904 : */
4905 :
4906 7299 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4907 :
4908 : {
4909 14598 : TAKE_OPTIONAL_LOCK();
4910 :
4911 : #if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4912 7299 : if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4913 : {
4914 12 : PJ *obj = proj_create(
4915 : d->getPROJContext(),
4916 : "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4917 : " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4918 : " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4919 : " MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4920 : " MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4921 : " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4922 : " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4923 : " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4924 : " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4925 : " MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4926 : " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4927 : " LENGTHUNIT[\"metre\",1]],\n"
4928 : " ENSEMBLEACCURACY[2.0]],\n"
4929 : " PRIMEM[\"Greenwich\",0,\n"
4930 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4931 : " CS[ellipsoidal,3],\n"
4932 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4933 : " ORDER[1],\n"
4934 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4935 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4936 : " ORDER[2],\n"
4937 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4938 : " AXIS[\"ellipsoidal height (h)\",up,\n"
4939 : " ORDER[3],\n"
4940 : " LENGTHUNIT[\"metre\",1]],\n"
4941 : " USAGE[\n"
4942 : " SCOPE[\"3D system frequently used in GIS, Web APIs and "
4943 : "Web applications\"],\n"
4944 : " AREA[\"World.\"],\n"
4945 : " BBOX[-90,-180,90,180]],\n"
4946 : " ID[\"OGC\",\"CRS84h\"]]");
4947 12 : if (!obj)
4948 : {
4949 0 : return OGRERR_FAILURE;
4950 : }
4951 12 : Clear();
4952 12 : d->setPjCRS(obj);
4953 12 : return OGRERR_NONE;
4954 : }
4955 : #endif
4956 :
4957 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4958 : if (strlen(pszURL) >= 10000)
4959 : {
4960 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4961 : return OGRERR_CORRUPT_DATA;
4962 : }
4963 :
4964 : PJ *obj;
4965 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4966 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4967 : {
4968 : obj = proj_create(
4969 : d->getPROJContext(),
4970 : CPLSPrintf("IAU:%s",
4971 : pszURL +
4972 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4973 : }
4974 : else
4975 : #endif
4976 : {
4977 : obj = proj_create(d->getPROJContext(), pszURL);
4978 : }
4979 : if (!obj)
4980 : {
4981 : return OGRERR_FAILURE;
4982 : }
4983 : Clear();
4984 : d->setPjCRS(obj);
4985 : return OGRERR_NONE;
4986 : #else
4987 7287 : const char *pszCur = nullptr;
4988 :
4989 7287 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4990 2 : pszCur = pszURL + 26;
4991 7285 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4992 1 : pszCur = pszURL + 27;
4993 7284 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4994 7283 : pszCur = pszURL + 30;
4995 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4996 1 : pszCur = pszURL + 31;
4997 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4998 0 : pszCur = pszURL + 23;
4999 : else
5000 : {
5001 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
5002 : pszURL);
5003 0 : return OGRERR_FAILURE;
5004 : }
5005 :
5006 7287 : if (*pszCur == '\0')
5007 : {
5008 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
5009 0 : return OGRERR_FAILURE;
5010 : }
5011 :
5012 : /* -------------------------------------------------------------------- */
5013 : /* Clear any existing definition. */
5014 : /* -------------------------------------------------------------------- */
5015 7287 : Clear();
5016 :
5017 7287 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
5018 : {
5019 : /* --------------------------------------------------------------------
5020 : */
5021 : /* It's a compound CRS, of the form: */
5022 : /* */
5023 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
5024 : /* --------------------------------------------------------------------
5025 : */
5026 1 : pszCur += 12;
5027 :
5028 : // Extract each component CRS URL.
5029 1 : int iComponentUrl = 2;
5030 :
5031 2 : CPLString osName = "";
5032 1 : Clear();
5033 :
5034 3 : while (iComponentUrl != -1)
5035 : {
5036 2 : char searchStr[15] = {};
5037 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5038 :
5039 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
5040 :
5041 : // Figure out the next component URL.
5042 2 : char *pszComponentUrl = nullptr;
5043 :
5044 2 : if (pszUrlEnd)
5045 : {
5046 1 : size_t nLen = pszUrlEnd - pszCur;
5047 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5048 1 : strncpy(pszComponentUrl, pszCur, nLen);
5049 1 : pszComponentUrl[nLen] = '\0';
5050 :
5051 1 : ++iComponentUrl;
5052 1 : pszCur += nLen + strlen(searchStr);
5053 : }
5054 : else
5055 : {
5056 1 : if (iComponentUrl == 2)
5057 : {
5058 0 : CPLError(CE_Failure, CPLE_AppDefined,
5059 : "Compound CRS URLs must have at least two "
5060 : "component CRSs.");
5061 0 : return OGRERR_FAILURE;
5062 : }
5063 : else
5064 : {
5065 1 : pszComponentUrl = CPLStrdup(pszCur);
5066 : // no more components
5067 1 : iComponentUrl = -1;
5068 : }
5069 : }
5070 :
5071 2 : OGRSpatialReference oComponentSRS;
5072 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5073 :
5074 2 : CPLFree(pszComponentUrl);
5075 2 : pszComponentUrl = nullptr;
5076 :
5077 2 : if (eStatus == OGRERR_NONE)
5078 : {
5079 2 : if (osName.length() != 0)
5080 : {
5081 1 : osName += " + ";
5082 : }
5083 2 : osName += oComponentSRS.GetRoot()->GetValue();
5084 2 : SetNode("COMPD_CS", osName);
5085 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5086 : }
5087 : else
5088 0 : return eStatus;
5089 : }
5090 :
5091 1 : return OGRERR_NONE;
5092 : }
5093 :
5094 : /* -------------------------------------------------------------------- */
5095 : /* It's a normal CRS URL, of the form: */
5096 : /* */
5097 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5098 : /* -------------------------------------------------------------------- */
5099 7286 : ++pszCur;
5100 7286 : const char *pszAuthority = pszCur;
5101 :
5102 : // skip authority
5103 135343 : while (*pszCur != '/' && *pszCur)
5104 128057 : pszCur++;
5105 7286 : if (*pszCur == '/')
5106 7285 : pszCur++;
5107 :
5108 : // skip version
5109 16709 : while (*pszCur != '/' && *pszCur)
5110 9423 : pszCur++;
5111 7286 : if (*pszCur == '/')
5112 7285 : pszCur++;
5113 :
5114 7286 : const char *pszCode = pszCur;
5115 :
5116 7286 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5117 : #endif
5118 : }
5119 :
5120 : /************************************************************************/
5121 : /* importFromWMSAUTO() */
5122 : /************************************************************************/
5123 :
5124 : /**
5125 : * \brief Initialize from WMSAUTO string.
5126 : *
5127 : * Note that the WMS 1.3 specification does not include the
5128 : * units code, while apparently earlier specs do. We try to
5129 : * guess around this.
5130 : *
5131 : * @param pszDefinition the WMSAUTO string
5132 : *
5133 : * @return OGRERR_NONE on success or an error code.
5134 : */
5135 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5136 :
5137 : {
5138 6 : TAKE_OPTIONAL_LOCK();
5139 :
5140 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5141 : if (strlen(pszDefinition) >= 10000)
5142 : {
5143 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5144 : return OGRERR_CORRUPT_DATA;
5145 : }
5146 :
5147 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5148 : if (!obj)
5149 : {
5150 : return OGRERR_FAILURE;
5151 : }
5152 : Clear();
5153 : d->setPjCRS(obj);
5154 : return OGRERR_NONE;
5155 : #else
5156 : int nProjId, nUnitsId;
5157 3 : double dfRefLong, dfRefLat = 0.0;
5158 :
5159 : /* -------------------------------------------------------------------- */
5160 : /* Tokenize */
5161 : /* -------------------------------------------------------------------- */
5162 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5163 3 : pszDefinition += 5;
5164 :
5165 : char **papszTokens =
5166 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5167 :
5168 3 : if (CSLCount(papszTokens) == 4)
5169 : {
5170 0 : nProjId = atoi(papszTokens[0]);
5171 0 : nUnitsId = atoi(papszTokens[1]);
5172 0 : dfRefLong = CPLAtof(papszTokens[2]);
5173 0 : dfRefLat = CPLAtof(papszTokens[3]);
5174 : }
5175 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5176 : {
5177 0 : nProjId = atoi(papszTokens[0]);
5178 0 : nUnitsId = atoi(papszTokens[1]);
5179 0 : dfRefLong = CPLAtof(papszTokens[2]);
5180 0 : dfRefLat = 0.0;
5181 : }
5182 3 : else if (CSLCount(papszTokens) == 3)
5183 : {
5184 2 : nProjId = atoi(papszTokens[0]);
5185 2 : nUnitsId = 9001;
5186 2 : dfRefLong = CPLAtof(papszTokens[1]);
5187 2 : dfRefLat = CPLAtof(papszTokens[2]);
5188 : }
5189 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5190 : {
5191 0 : nProjId = atoi(papszTokens[0]);
5192 0 : nUnitsId = 9001;
5193 0 : dfRefLong = CPLAtof(papszTokens[1]);
5194 : }
5195 : else
5196 : {
5197 1 : CSLDestroy(papszTokens);
5198 1 : CPLError(CE_Failure, CPLE_AppDefined,
5199 : "AUTO projection has wrong number of arguments, expected\n"
5200 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5201 : "AUTO:proj_id,ref_long,ref_lat");
5202 1 : return OGRERR_FAILURE;
5203 : }
5204 :
5205 2 : CSLDestroy(papszTokens);
5206 2 : papszTokens = nullptr;
5207 :
5208 : /* -------------------------------------------------------------------- */
5209 : /* Build coordsys. */
5210 : /* -------------------------------------------------------------------- */
5211 2 : Clear();
5212 :
5213 : /* -------------------------------------------------------------------- */
5214 : /* Set WGS84. */
5215 : /* -------------------------------------------------------------------- */
5216 2 : SetWellKnownGeogCS("WGS84");
5217 :
5218 2 : switch (nProjId)
5219 : {
5220 2 : case 42001: // Auto UTM
5221 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5222 : dfRefLat >= 0.0);
5223 2 : break;
5224 :
5225 0 : case 42002: // Auto TM (strangely very UTM-like).
5226 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5227 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5228 0 : break;
5229 :
5230 0 : case 42003: // Auto Orthographic.
5231 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5232 0 : break;
5233 :
5234 0 : case 42004: // Auto Equirectangular
5235 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5236 0 : break;
5237 :
5238 0 : case 42005:
5239 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5240 0 : break;
5241 :
5242 0 : default:
5243 0 : CPLError(CE_Failure, CPLE_AppDefined,
5244 : "Unsupported projection id in importFromWMSAUTO(): %d",
5245 : nProjId);
5246 0 : return OGRERR_FAILURE;
5247 : }
5248 :
5249 : /* -------------------------------------------------------------------- */
5250 : /* Set units. */
5251 : /* -------------------------------------------------------------------- */
5252 :
5253 2 : switch (nUnitsId)
5254 : {
5255 2 : case 9001:
5256 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5257 2 : break;
5258 :
5259 0 : case 9002:
5260 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5261 0 : break;
5262 :
5263 0 : case 9003:
5264 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5265 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5266 0 : break;
5267 :
5268 0 : default:
5269 0 : CPLError(CE_Failure, CPLE_AppDefined,
5270 : "Unsupported units code (%d).", nUnitsId);
5271 0 : return OGRERR_FAILURE;
5272 : break;
5273 : }
5274 :
5275 2 : return OGRERR_NONE;
5276 : #endif
5277 : }
5278 :
5279 : /************************************************************************/
5280 : /* GetSemiMajor() */
5281 : /************************************************************************/
5282 :
5283 : /**
5284 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5285 : *
5286 : * This method does the same thing as the C function OSRGetSemiMajor().
5287 : *
5288 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5289 : * can be found.
5290 : *
5291 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5292 : */
5293 :
5294 7004 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5295 :
5296 : {
5297 14008 : TAKE_OPTIONAL_LOCK();
5298 :
5299 7004 : if (pnErr != nullptr)
5300 3634 : *pnErr = OGRERR_FAILURE;
5301 :
5302 7004 : d->refreshProjObj();
5303 7004 : if (!d->m_pj_crs)
5304 111 : return SRS_WGS84_SEMIMAJOR;
5305 :
5306 6893 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5307 6893 : if (!ellps)
5308 5 : return SRS_WGS84_SEMIMAJOR;
5309 :
5310 6888 : double dfSemiMajor = 0.0;
5311 6888 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5312 : nullptr, nullptr, nullptr);
5313 6888 : proj_destroy(ellps);
5314 :
5315 6888 : if (dfSemiMajor > 0)
5316 : {
5317 6888 : if (pnErr != nullptr)
5318 3520 : *pnErr = OGRERR_NONE;
5319 6888 : return dfSemiMajor;
5320 : }
5321 :
5322 0 : return SRS_WGS84_SEMIMAJOR;
5323 : }
5324 :
5325 : /************************************************************************/
5326 : /* OSRGetSemiMajor() */
5327 : /************************************************************************/
5328 :
5329 : /**
5330 : * \brief Get spheroid semi major axis.
5331 : *
5332 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5333 : */
5334 89 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5335 :
5336 : {
5337 89 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5338 :
5339 89 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5340 : }
5341 :
5342 : /************************************************************************/
5343 : /* GetInvFlattening() */
5344 : /************************************************************************/
5345 :
5346 : /**
5347 : * \brief Get spheroid inverse flattening.
5348 : *
5349 : * This method does the same thing as the C function OSRGetInvFlattening().
5350 : *
5351 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5352 : * can be found.
5353 : *
5354 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5355 : */
5356 :
5357 4610 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5358 :
5359 : {
5360 9220 : TAKE_OPTIONAL_LOCK();
5361 :
5362 4610 : if (pnErr != nullptr)
5363 3524 : *pnErr = OGRERR_FAILURE;
5364 :
5365 4610 : d->refreshProjObj();
5366 4610 : if (!d->m_pj_crs)
5367 111 : return SRS_WGS84_INVFLATTENING;
5368 :
5369 4499 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5370 4499 : if (!ellps)
5371 2 : return SRS_WGS84_INVFLATTENING;
5372 :
5373 4497 : double dfInvFlattening = -1.0;
5374 4497 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5375 : nullptr, &dfInvFlattening);
5376 4497 : proj_destroy(ellps);
5377 :
5378 4497 : if (dfInvFlattening >= 0.0)
5379 : {
5380 4497 : if (pnErr != nullptr)
5381 3413 : *pnErr = OGRERR_NONE;
5382 4497 : return dfInvFlattening;
5383 : }
5384 :
5385 0 : return SRS_WGS84_INVFLATTENING;
5386 : }
5387 :
5388 : /************************************************************************/
5389 : /* OSRGetInvFlattening() */
5390 : /************************************************************************/
5391 :
5392 : /**
5393 : * \brief Get spheroid inverse flattening.
5394 : *
5395 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5396 : */
5397 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5398 :
5399 : {
5400 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5401 :
5402 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5403 : }
5404 :
5405 : /************************************************************************/
5406 : /* GetEccentricity() */
5407 : /************************************************************************/
5408 :
5409 : /**
5410 : * \brief Get spheroid eccentricity
5411 : *
5412 : * @return eccentricity (or -1 in case of error)
5413 : */
5414 :
5415 0 : double OGRSpatialReference::GetEccentricity() const
5416 :
5417 : {
5418 0 : OGRErr eErr = OGRERR_NONE;
5419 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5420 0 : if (eErr != OGRERR_NONE)
5421 : {
5422 0 : return -1.0;
5423 : }
5424 0 : if (dfInvFlattening == 0.0)
5425 0 : return 0.0;
5426 0 : if (dfInvFlattening < 0.5)
5427 0 : return -1.0;
5428 0 : return sqrt(2.0 / dfInvFlattening -
5429 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5430 : }
5431 :
5432 : /************************************************************************/
5433 : /* GetSquaredEccentricity() */
5434 : /************************************************************************/
5435 :
5436 : /**
5437 : * \brief Get spheroid squared eccentricity
5438 : *
5439 : * @return squared eccentricity (or -1 in case of error)
5440 : */
5441 :
5442 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5443 :
5444 : {
5445 0 : OGRErr eErr = OGRERR_NONE;
5446 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5447 0 : if (eErr != OGRERR_NONE)
5448 : {
5449 0 : return -1.0;
5450 : }
5451 0 : if (dfInvFlattening == 0.0)
5452 0 : return 0.0;
5453 0 : if (dfInvFlattening < 0.5)
5454 0 : return -1.0;
5455 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5456 : }
5457 :
5458 : /************************************************************************/
5459 : /* GetSemiMinor() */
5460 : /************************************************************************/
5461 :
5462 : /**
5463 : * \brief Get spheroid semi minor axis.
5464 : *
5465 : * This method does the same thing as the C function OSRGetSemiMinor().
5466 : *
5467 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5468 : * can be found.
5469 : *
5470 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5471 : */
5472 :
5473 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5474 :
5475 : {
5476 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5477 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5478 :
5479 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5480 : }
5481 :
5482 : /************************************************************************/
5483 : /* OSRGetSemiMinor() */
5484 : /************************************************************************/
5485 :
5486 : /**
5487 : * \brief Get spheroid semi minor axis.
5488 : *
5489 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5490 : */
5491 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5492 :
5493 : {
5494 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5495 :
5496 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5497 : }
5498 :
5499 : /************************************************************************/
5500 : /* SetLocalCS() */
5501 : /************************************************************************/
5502 :
5503 : /**
5504 : * \brief Set the user visible LOCAL_CS name.
5505 : *
5506 : * This method is the same as the C function OSRSetLocalCS().
5507 : *
5508 : * This method will ensure a LOCAL_CS node is created as the root,
5509 : * and set the provided name on it. It must be used before SetLinearUnits().
5510 : *
5511 : * @param pszName the user visible name to assign. Not used as a key.
5512 : *
5513 : * @return OGRERR_NONE on success.
5514 : */
5515 :
5516 2900 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5517 :
5518 : {
5519 5800 : TAKE_OPTIONAL_LOCK();
5520 :
5521 2900 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5522 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5523 : {
5524 2900 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5525 : }
5526 : else
5527 : {
5528 0 : CPLDebug("OGR",
5529 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5530 : "It appears an incompatible object already exists.",
5531 : pszName);
5532 0 : return OGRERR_FAILURE;
5533 : }
5534 :
5535 2900 : return OGRERR_NONE;
5536 : }
5537 :
5538 : /************************************************************************/
5539 : /* OSRSetLocalCS() */
5540 : /************************************************************************/
5541 :
5542 : /**
5543 : * \brief Set the user visible LOCAL_CS name.
5544 : *
5545 : * This function is the same as OGRSpatialReference::SetLocalCS()
5546 : */
5547 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5548 :
5549 : {
5550 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5551 :
5552 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5553 : }
5554 :
5555 : /************************************************************************/
5556 : /* SetGeocCS() */
5557 : /************************************************************************/
5558 :
5559 : /**
5560 : * \brief Set the user visible GEOCCS name.
5561 : *
5562 : * This method is the same as the C function OSRSetGeocCS().
5563 :
5564 : * This method will ensure a GEOCCS node is created as the root,
5565 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5566 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5567 : * the GEOGCS.
5568 : *
5569 : * @param pszName the user visible name to assign. Not used as a key.
5570 : *
5571 : * @return OGRERR_NONE on success.
5572 : *
5573 : */
5574 :
5575 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5576 :
5577 : {
5578 12 : TAKE_OPTIONAL_LOCK();
5579 :
5580 6 : OGRErr eErr = OGRERR_NONE;
5581 6 : d->refreshProjObj();
5582 6 : d->demoteFromBoundCRS();
5583 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5584 : {
5585 3 : d->setPjCRS(proj_create_geocentric_crs(
5586 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5587 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5588 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5589 : "Metre", 1.0));
5590 : }
5591 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5592 : {
5593 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5594 : }
5595 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5596 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5597 : {
5598 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5599 : #if PROJ_VERSION_MAJOR > 7 || \
5600 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5601 : if (datum == nullptr)
5602 : {
5603 : datum =
5604 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5605 : }
5606 : #endif
5607 1 : if (datum == nullptr)
5608 : {
5609 0 : d->undoDemoteFromBoundCRS();
5610 0 : return OGRERR_FAILURE;
5611 : }
5612 :
5613 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5614 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5615 : 0.0);
5616 1 : d->setPjCRS(pj_crs);
5617 :
5618 1 : proj_destroy(datum);
5619 : }
5620 : else
5621 : {
5622 1 : CPLDebug("OGR",
5623 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5624 : "It appears an incompatible object already exists.",
5625 : pszName);
5626 1 : eErr = OGRERR_FAILURE;
5627 : }
5628 6 : d->undoDemoteFromBoundCRS();
5629 :
5630 6 : return eErr;
5631 : }
5632 :
5633 : /************************************************************************/
5634 : /* OSRSetGeocCS() */
5635 : /************************************************************************/
5636 :
5637 : /**
5638 : * \brief Set the user visible PROJCS name.
5639 : *
5640 : * This function is the same as OGRSpatialReference::SetGeocCS()
5641 : *
5642 : */
5643 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5644 :
5645 : {
5646 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5647 :
5648 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5649 : }
5650 :
5651 : /************************************************************************/
5652 : /* SetVertCS() */
5653 : /************************************************************************/
5654 :
5655 : /**
5656 : * \brief Set the user visible VERT_CS name.
5657 : *
5658 : * This method is the same as the C function OSRSetVertCS().
5659 :
5660 : * This method will ensure a VERT_CS node is created if needed. If the
5661 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5662 : * turned into a COMPD_CS.
5663 : *
5664 : * @param pszVertCSName the user visible name of the vertical coordinate
5665 : * system. Not used as a key.
5666 : *
5667 : * @param pszVertDatumName the user visible name of the vertical datum. It
5668 : * is helpful if this matches the EPSG name.
5669 : *
5670 : * @param nVertDatumType the OGC vertical datum type. Ignored
5671 : *
5672 : * @return OGRERR_NONE on success.
5673 : *
5674 : */
5675 :
5676 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5677 : const char *pszVertDatumName,
5678 : int nVertDatumType)
5679 :
5680 : {
5681 1 : TAKE_OPTIONAL_LOCK();
5682 :
5683 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5684 :
5685 1 : d->refreshProjObj();
5686 :
5687 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5688 : pszVertDatumName, nullptr, 0.0);
5689 :
5690 : /* -------------------------------------------------------------------- */
5691 : /* Handle the case where we want to make a compound coordinate */
5692 : /* system. */
5693 : /* -------------------------------------------------------------------- */
5694 1 : if (IsProjected() || IsGeographic())
5695 : {
5696 1 : auto compoundCRS = proj_create_compound_crs(
5697 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5698 1 : proj_destroy(vertCRS);
5699 1 : d->setPjCRS(compoundCRS);
5700 : }
5701 : else
5702 : {
5703 0 : d->setPjCRS(vertCRS);
5704 : }
5705 2 : return OGRERR_NONE;
5706 : }
5707 :
5708 : /************************************************************************/
5709 : /* OSRSetVertCS() */
5710 : /************************************************************************/
5711 :
5712 : /**
5713 : * \brief Setup the vertical coordinate system.
5714 : *
5715 : * This function is the same as OGRSpatialReference::SetVertCS()
5716 : *
5717 : */
5718 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5719 : const char *pszVertDatumName, int nVertDatumType)
5720 :
5721 : {
5722 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5723 :
5724 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5725 0 : nVertDatumType);
5726 : }
5727 :
5728 : /************************************************************************/
5729 : /* SetCompoundCS() */
5730 : /************************************************************************/
5731 :
5732 : /**
5733 : * \brief Setup a compound coordinate system.
5734 : *
5735 : * This method is the same as the C function OSRSetCompoundCS().
5736 :
5737 : * This method is replace the current SRS with a COMPD_CS coordinate system
5738 : * consisting of the passed in horizontal and vertical coordinate systems.
5739 : *
5740 : * @param pszName the name of the compound coordinate system.
5741 : *
5742 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5743 : *
5744 : * @param poVertSRS the vertical SRS (VERT_CS).
5745 : *
5746 : * @return OGRERR_NONE on success.
5747 : */
5748 :
5749 183 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5750 : const OGRSpatialReference *poHorizSRS,
5751 : const OGRSpatialReference *poVertSRS)
5752 :
5753 : {
5754 366 : TAKE_OPTIONAL_LOCK();
5755 :
5756 : /* -------------------------------------------------------------------- */
5757 : /* Verify these are legal horizontal and vertical coordinate */
5758 : /* systems. */
5759 : /* -------------------------------------------------------------------- */
5760 183 : if (!poVertSRS->IsVertical())
5761 : {
5762 0 : CPLError(CE_Failure, CPLE_AppDefined,
5763 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5764 0 : return OGRERR_FAILURE;
5765 : }
5766 183 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5767 : {
5768 0 : CPLError(CE_Failure, CPLE_AppDefined,
5769 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5770 : "GEOGCS.");
5771 0 : return OGRERR_FAILURE;
5772 : }
5773 :
5774 : /* -------------------------------------------------------------------- */
5775 : /* Replace with compound srs. */
5776 : /* -------------------------------------------------------------------- */
5777 183 : Clear();
5778 :
5779 183 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5780 183 : poHorizSRS->d->m_pj_crs,
5781 183 : poVertSRS->d->m_pj_crs);
5782 183 : d->setPjCRS(compoundCRS);
5783 :
5784 183 : return OGRERR_NONE;
5785 : }
5786 :
5787 : /************************************************************************/
5788 : /* OSRSetCompoundCS() */
5789 : /************************************************************************/
5790 :
5791 : /**
5792 : * \brief Setup a compound coordinate system.
5793 : *
5794 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5795 : */
5796 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5797 : OGRSpatialReferenceH hHorizSRS,
5798 : OGRSpatialReferenceH hVertSRS)
5799 :
5800 : {
5801 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5802 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5803 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5804 :
5805 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5806 16 : ToPointer(hVertSRS));
5807 : }
5808 :
5809 : /************************************************************************/
5810 : /* GetCompoundComponent() */
5811 : /************************************************************************/
5812 :
5813 : /**
5814 : * \brief Get a CRS component from a CompoundCRS
5815 : *
5816 : * @param iComponent Index of the CRS component (typically 0 = horizontal, 1 =
5817 : * vertical)
5818 : * @return new OGRSpatialReference object, or NULL in case of error.
5819 : *
5820 : * @since 3.14
5821 : */
5822 :
5823 : std::unique_ptr<OGRSpatialReference>
5824 0 : OGRSpatialReference::GetCompoundComponent(int iComponent) const
5825 : {
5826 0 : std::unique_ptr<OGRSpatialReference> poSRS;
5827 0 : d->refreshProjObj();
5828 0 : d->demoteFromBoundCRS();
5829 0 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
5830 : {
5831 : auto subCrs =
5832 0 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, iComponent);
5833 0 : if (subCrs)
5834 : {
5835 0 : poSRS = std::make_unique<OGRSpatialReference>();
5836 0 : poSRS->d->setPjCRS(subCrs);
5837 : }
5838 : }
5839 0 : d->undoDemoteFromBoundCRS();
5840 0 : return poSRS;
5841 : }
5842 :
5843 : /************************************************************************/
5844 : /* SetProjCS() */
5845 : /************************************************************************/
5846 :
5847 : /**
5848 : * \brief Set the user visible PROJCS name.
5849 : *
5850 : * This method is the same as the C function OSRSetProjCS().
5851 : *
5852 : * This method will ensure a PROJCS node is created as the root,
5853 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5854 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5855 : *
5856 : * @param pszName the user visible name to assign. Not used as a key.
5857 : *
5858 : * @return OGRERR_NONE on success.
5859 : */
5860 :
5861 4910 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5862 :
5863 : {
5864 4910 : TAKE_OPTIONAL_LOCK();
5865 :
5866 4910 : d->refreshProjObj();
5867 4910 : d->demoteFromBoundCRS();
5868 4910 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5869 : {
5870 499 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5871 : }
5872 : else
5873 : {
5874 4411 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5875 : nullptr, nullptr, nullptr,
5876 : nullptr, nullptr, 0, nullptr);
5877 4411 : auto cs = proj_create_cartesian_2D_cs(
5878 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5879 :
5880 4411 : auto projCRS = proj_create_projected_crs(
5881 4411 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5882 4411 : proj_destroy(dummyConv);
5883 4411 : proj_destroy(cs);
5884 :
5885 4411 : d->setPjCRS(projCRS);
5886 : }
5887 4910 : d->undoDemoteFromBoundCRS();
5888 9820 : return OGRERR_NONE;
5889 : }
5890 :
5891 : /************************************************************************/
5892 : /* OSRSetProjCS() */
5893 : /************************************************************************/
5894 :
5895 : /**
5896 : * \brief Set the user visible PROJCS name.
5897 : *
5898 : * This function is the same as OGRSpatialReference::SetProjCS()
5899 : */
5900 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5901 :
5902 : {
5903 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5904 :
5905 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5906 : }
5907 :
5908 : /************************************************************************/
5909 : /* SetProjection() */
5910 : /************************************************************************/
5911 :
5912 : /**
5913 : * \brief Set a projection name.
5914 : *
5915 : * This method is the same as the C function OSRSetProjection().
5916 : *
5917 : * @param pszProjection the projection name, which should be selected from
5918 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5919 : *
5920 : * @return OGRERR_NONE on success.
5921 : */
5922 :
5923 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5924 :
5925 : {
5926 46 : TAKE_OPTIONAL_LOCK();
5927 :
5928 23 : OGR_SRSNode *poGeogCS = nullptr;
5929 :
5930 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5931 : {
5932 4 : poGeogCS = d->m_poRoot;
5933 4 : d->m_poRoot = nullptr;
5934 : }
5935 :
5936 23 : if (!GetAttrNode("PROJCS"))
5937 : {
5938 11 : SetNode("PROJCS", "unnamed");
5939 : }
5940 :
5941 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5942 23 : if (eErr != OGRERR_NONE)
5943 0 : return eErr;
5944 :
5945 23 : if (poGeogCS != nullptr)
5946 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5947 :
5948 23 : return OGRERR_NONE;
5949 : }
5950 :
5951 : /************************************************************************/
5952 : /* OSRSetProjection() */
5953 : /************************************************************************/
5954 :
5955 : /**
5956 : * \brief Set a projection name.
5957 : *
5958 : * This function is the same as OGRSpatialReference::SetProjection()
5959 : */
5960 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5961 :
5962 : {
5963 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5964 :
5965 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5966 : }
5967 :
5968 : /************************************************************************/
5969 : /* GetWKT2ProjectionMethod() */
5970 : /************************************************************************/
5971 :
5972 : /**
5973 : * \brief Returns info on the projection method, based on WKT2 naming
5974 : * conventions.
5975 : *
5976 : * The returned strings are short lived and should be considered to be
5977 : * invalidated by any further call to the GDAL API.
5978 : *
5979 : * @param[out] ppszMethodName Pointer to a string that will receive the
5980 : * projection method name.
5981 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5982 : * receive the name of the authority that defines the projection method.
5983 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5984 : * an authority.
5985 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5986 : * receive the code that defines the projection method.
5987 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5988 : * an authority.
5989 : *
5990 : * @return OGRERR_NONE on success.
5991 : */
5992 : OGRErr
5993 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5994 : const char **ppszMethodAuthName,
5995 : const char **ppszMethodCode) const
5996 : {
5997 2 : TAKE_OPTIONAL_LOCK();
5998 :
5999 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
6000 1 : if (!conv)
6001 0 : return OGRERR_FAILURE;
6002 1 : const char *pszTmpMethodName = "";
6003 1 : const char *pszTmpMethodAuthName = "";
6004 1 : const char *pszTmpMethodCode = "";
6005 1 : int ret = proj_coordoperation_get_method_info(
6006 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
6007 : &pszTmpMethodCode);
6008 : // "Internalize" temporary strings returned by PROJ
6009 1 : CPLAssert(pszTmpMethodName);
6010 1 : if (ppszMethodName)
6011 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
6012 1 : if (ppszMethodAuthName)
6013 0 : *ppszMethodAuthName = pszTmpMethodAuthName
6014 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
6015 0 : : nullptr;
6016 1 : if (ppszMethodCode)
6017 0 : *ppszMethodCode =
6018 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
6019 1 : proj_destroy(conv);
6020 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
6021 : }
6022 :
6023 : /************************************************************************/
6024 : /* SetProjParm() */
6025 : /************************************************************************/
6026 :
6027 : /**
6028 : * \brief Set a projection parameter value.
6029 : *
6030 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
6031 : *
6032 : * This method is the same as the C function OSRSetProjParm().
6033 : *
6034 : * Please check https://gdal.org/proj_list pages for
6035 : * legal parameter names for specific projections.
6036 : *
6037 : *
6038 : * @param pszParamName the parameter name, which should be selected from
6039 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6040 : *
6041 : * @param dfValue value to assign.
6042 : *
6043 : * @return OGRERR_NONE on success.
6044 : */
6045 :
6046 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
6047 : double dfValue)
6048 :
6049 : {
6050 258 : TAKE_OPTIONAL_LOCK();
6051 :
6052 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
6053 :
6054 129 : if (poPROJCS == nullptr)
6055 3 : return OGRERR_FAILURE;
6056 :
6057 126 : char szValue[64] = {'\0'};
6058 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
6059 :
6060 : /* -------------------------------------------------------------------- */
6061 : /* Try to find existing parameter with this name. */
6062 : /* -------------------------------------------------------------------- */
6063 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6064 : {
6065 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
6066 :
6067 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
6068 1242 : poParam->GetChildCount() == 2 &&
6069 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
6070 : {
6071 39 : poParam->GetChild(1)->SetValue(szValue);
6072 39 : return OGRERR_NONE;
6073 : }
6074 : }
6075 :
6076 : /* -------------------------------------------------------------------- */
6077 : /* Otherwise create a new parameter and append. */
6078 : /* -------------------------------------------------------------------- */
6079 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6080 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
6081 87 : poParam->AddChild(new OGR_SRSNode(szValue));
6082 :
6083 87 : poPROJCS->AddChild(poParam);
6084 :
6085 87 : return OGRERR_NONE;
6086 : }
6087 :
6088 : /************************************************************************/
6089 : /* OSRSetProjParm() */
6090 : /************************************************************************/
6091 :
6092 : /**
6093 : * \brief Set a projection parameter value.
6094 : *
6095 : * This function is the same as OGRSpatialReference::SetProjParm()
6096 : */
6097 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6098 : double dfValue)
6099 :
6100 : {
6101 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6102 :
6103 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6104 : }
6105 :
6106 : /************************************************************************/
6107 : /* FindProjParm() */
6108 : /************************************************************************/
6109 :
6110 : /**
6111 : * \brief Return the child index of the named projection parameter on
6112 : * its parent PROJCS node.
6113 : *
6114 : * @param pszParameter projection parameter to look for
6115 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6116 : * the PROJCS node of the SpatialReference object will be searched.
6117 : *
6118 : * @return the child index of the named projection parameter. -1 on failure
6119 : */
6120 5189 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6121 : const OGR_SRSNode *poPROJCS) const
6122 :
6123 : {
6124 10378 : TAKE_OPTIONAL_LOCK();
6125 :
6126 5189 : if (poPROJCS == nullptr)
6127 0 : poPROJCS = GetAttrNode("PROJCS");
6128 :
6129 5189 : if (poPROJCS == nullptr)
6130 0 : return -1;
6131 :
6132 : /* -------------------------------------------------------------------- */
6133 : /* Search for requested parameter. */
6134 : /* -------------------------------------------------------------------- */
6135 5189 : bool bIsWKT2 = false;
6136 34199 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6137 : {
6138 33508 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6139 :
6140 33508 : if (poParameter->GetChildCount() >= 2)
6141 : {
6142 23171 : const char *pszValue = poParameter->GetValue();
6143 38180 : if (EQUAL(pszValue, "PARAMETER") &&
6144 15009 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6145 : pszParameter))
6146 : {
6147 4498 : return iChild;
6148 : }
6149 18673 : else if (EQUAL(pszValue, "METHOD"))
6150 : {
6151 41 : bIsWKT2 = true;
6152 : }
6153 : }
6154 : }
6155 :
6156 : /* -------------------------------------------------------------------- */
6157 : /* Try similar names, for selected parameters. */
6158 : /* -------------------------------------------------------------------- */
6159 691 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6160 : {
6161 324 : if (bIsWKT2)
6162 : {
6163 8 : int iChild = FindProjParm(
6164 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6165 8 : if (iChild == -1)
6166 3 : iChild = FindProjParm(
6167 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6168 8 : return iChild;
6169 : }
6170 316 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6171 : }
6172 :
6173 367 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6174 : {
6175 38 : if (bIsWKT2)
6176 : {
6177 9 : int iChild = FindProjParm(
6178 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6179 9 : if (iChild == -1)
6180 0 : iChild = FindProjParm(
6181 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6182 9 : return iChild;
6183 : }
6184 29 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6185 29 : if (iChild == -1)
6186 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6187 29 : return iChild;
6188 : }
6189 :
6190 329 : return -1;
6191 : }
6192 :
6193 : /************************************************************************/
6194 : /* GetProjParm() */
6195 : /************************************************************************/
6196 :
6197 : /**
6198 : * \brief Fetch a projection parameter value.
6199 : *
6200 : * NOTE: This code should be modified to translate non degree angles into
6201 : * degrees based on the GEOGCS unit. This has not yet been done.
6202 : *
6203 : * This method is the same as the C function OSRGetProjParm().
6204 : *
6205 : * @param pszName the name of the parameter to fetch, from the set of
6206 : * SRS_PP codes in ogr_srs_api.h.
6207 : *
6208 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6209 : *
6210 : * @param pnErr place to put error code on failure. Ignored if NULL.
6211 : *
6212 : * @return value of parameter.
6213 : */
6214 :
6215 5082 : double OGRSpatialReference::GetProjParm(const char *pszName,
6216 : double dfDefaultValue,
6217 : OGRErr *pnErr) const
6218 :
6219 : {
6220 10164 : TAKE_OPTIONAL_LOCK();
6221 :
6222 5082 : d->refreshProjObj();
6223 5082 : GetRoot(); // force update of d->m_bNodesWKT2
6224 :
6225 5082 : if (pnErr != nullptr)
6226 4082 : *pnErr = OGRERR_NONE;
6227 :
6228 : /* -------------------------------------------------------------------- */
6229 : /* Find the desired parameter. */
6230 : /* -------------------------------------------------------------------- */
6231 : const OGR_SRSNode *poPROJCS =
6232 5082 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6233 5082 : if (poPROJCS == nullptr)
6234 : {
6235 258 : if (pnErr != nullptr)
6236 258 : *pnErr = OGRERR_FAILURE;
6237 258 : return dfDefaultValue;
6238 : }
6239 :
6240 4824 : const int iChild = FindProjParm(pszName, poPROJCS);
6241 4824 : if (iChild == -1)
6242 : {
6243 326 : if (IsProjected() && GetAxesCount() == 3)
6244 : {
6245 3 : OGRSpatialReference *poSRSTmp = Clone();
6246 3 : poSRSTmp->DemoteTo2D(nullptr);
6247 : const double dfRet =
6248 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6249 3 : delete poSRSTmp;
6250 3 : return dfRet;
6251 : }
6252 :
6253 323 : if (pnErr != nullptr)
6254 301 : *pnErr = OGRERR_FAILURE;
6255 323 : return dfDefaultValue;
6256 : }
6257 :
6258 4498 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6259 4498 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6260 : }
6261 :
6262 : /************************************************************************/
6263 : /* OSRGetProjParm() */
6264 : /************************************************************************/
6265 :
6266 : /**
6267 : * \brief Fetch a projection parameter value.
6268 : *
6269 : * This function is the same as OGRSpatialReference::GetProjParm()
6270 : */
6271 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6272 : double dfDefaultValue, OGRErr *pnErr)
6273 :
6274 : {
6275 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6276 :
6277 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6278 : }
6279 :
6280 : /************************************************************************/
6281 : /* GetNormProjParm() */
6282 : /************************************************************************/
6283 :
6284 : /**
6285 : * \brief Fetch a normalized projection parameter value.
6286 : *
6287 : * This method is the same as GetProjParm() except that the value of
6288 : * the parameter is "normalized" into degrees or meters depending on
6289 : * whether it is linear or angular.
6290 : *
6291 : * This method is the same as the C function OSRGetNormProjParm().
6292 : *
6293 : * @param pszName the name of the parameter to fetch, from the set of
6294 : * SRS_PP codes in ogr_srs_api.h.
6295 : *
6296 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6297 : *
6298 : * @param pnErr place to put error code on failure. Ignored if NULL.
6299 : *
6300 : * @return value of parameter.
6301 : */
6302 :
6303 4057 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6304 : double dfDefaultValue,
6305 : OGRErr *pnErr) const
6306 :
6307 : {
6308 8114 : TAKE_OPTIONAL_LOCK();
6309 :
6310 4057 : GetNormInfo();
6311 :
6312 4057 : OGRErr nError = OGRERR_NONE;
6313 4057 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6314 4057 : if (pnErr != nullptr)
6315 0 : *pnErr = nError;
6316 :
6317 : // If we got the default just return it unadjusted.
6318 4057 : if (nError != OGRERR_NONE)
6319 559 : return dfRawResult;
6320 :
6321 3498 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6322 8 : dfRawResult *= d->dfToDegrees;
6323 :
6324 3498 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6325 5 : return dfRawResult * d->dfToMeter;
6326 :
6327 3493 : return dfRawResult;
6328 : }
6329 :
6330 : /************************************************************************/
6331 : /* OSRGetNormProjParm() */
6332 : /************************************************************************/
6333 :
6334 : /**
6335 : * \brief This function is the same as OGRSpatialReference::
6336 : *
6337 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6338 : */
6339 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6340 : double dfDefaultValue, OGRErr *pnErr)
6341 :
6342 : {
6343 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6344 :
6345 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6346 : }
6347 :
6348 : /************************************************************************/
6349 : /* SetNormProjParm() */
6350 : /************************************************************************/
6351 :
6352 : /**
6353 : * \brief Set a projection parameter with a normalized value.
6354 : *
6355 : * This method is the same as SetProjParm() except that the value of
6356 : * the parameter passed in is assumed to be in "normalized" form (decimal
6357 : * degrees for angular values, meters for linear values. The values are
6358 : * converted in a form suitable for the GEOGCS and linear units in effect.
6359 : *
6360 : * This method is the same as the C function OSRSetNormProjParm().
6361 : *
6362 : * @param pszName the parameter name, which should be selected from
6363 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6364 : *
6365 : * @param dfValue value to assign.
6366 : *
6367 : * @return OGRERR_NONE on success.
6368 : */
6369 :
6370 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6371 :
6372 : {
6373 182 : TAKE_OPTIONAL_LOCK();
6374 :
6375 91 : GetNormInfo();
6376 :
6377 91 : if (d->dfToDegrees != 0.0 &&
6378 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6379 0 : IsAngularParameter(pszName))
6380 : {
6381 0 : dfValue /= d->dfToDegrees;
6382 : }
6383 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6384 4 : IsLinearParameter(pszName))
6385 4 : dfValue /= d->dfToMeter;
6386 :
6387 182 : return SetProjParm(pszName, dfValue);
6388 : }
6389 :
6390 : /************************************************************************/
6391 : /* OSRSetNormProjParm() */
6392 : /************************************************************************/
6393 :
6394 : /**
6395 : * \brief Set a projection parameter with a normalized value.
6396 : *
6397 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6398 : */
6399 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6400 : double dfValue)
6401 :
6402 : {
6403 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6404 :
6405 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6406 : }
6407 :
6408 : /************************************************************************/
6409 : /* SetTM() */
6410 : /************************************************************************/
6411 :
6412 443 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6413 : double dfScale, double dfFalseEasting,
6414 : double dfFalseNorthing)
6415 :
6416 : {
6417 886 : TAKE_OPTIONAL_LOCK();
6418 :
6419 443 : return d->replaceConversionAndUnref(
6420 : proj_create_conversion_transverse_mercator(
6421 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6422 886 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6423 : }
6424 :
6425 : /************************************************************************/
6426 : /* OSRSetTM() */
6427 : /************************************************************************/
6428 :
6429 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6430 : double dfCenterLong, double dfScale, double dfFalseEasting,
6431 : double dfFalseNorthing)
6432 :
6433 : {
6434 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6435 :
6436 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6437 1 : dfFalseEasting, dfFalseNorthing);
6438 : }
6439 :
6440 : /************************************************************************/
6441 : /* SetTMVariant() */
6442 : /************************************************************************/
6443 :
6444 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6445 : double dfCenterLat,
6446 : double dfCenterLong, double dfScale,
6447 : double dfFalseEasting,
6448 : double dfFalseNorthing)
6449 :
6450 : {
6451 0 : TAKE_OPTIONAL_LOCK();
6452 :
6453 0 : SetProjection(pszVariantName);
6454 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6455 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6456 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6457 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6458 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6459 :
6460 0 : return OGRERR_NONE;
6461 : }
6462 :
6463 : /************************************************************************/
6464 : /* OSRSetTMVariant() */
6465 : /************************************************************************/
6466 :
6467 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6468 : double dfCenterLat, double dfCenterLong, double dfScale,
6469 : double dfFalseEasting, double dfFalseNorthing)
6470 :
6471 : {
6472 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6473 :
6474 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6475 : dfCenterLong, dfScale, dfFalseEasting,
6476 0 : dfFalseNorthing);
6477 : }
6478 :
6479 : /************************************************************************/
6480 : /* SetTMSO() */
6481 : /************************************************************************/
6482 :
6483 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6484 : double dfScale, double dfFalseEasting,
6485 : double dfFalseNorthing)
6486 :
6487 : {
6488 6 : TAKE_OPTIONAL_LOCK();
6489 :
6490 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6491 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6492 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6493 :
6494 3 : const char *pszName = nullptr;
6495 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6496 3 : CPLString osName = pszName ? pszName : "";
6497 :
6498 3 : d->refreshProjObj();
6499 :
6500 3 : d->demoteFromBoundCRS();
6501 :
6502 3 : auto cs = proj_create_cartesian_2D_cs(
6503 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6504 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6505 : auto projCRS =
6506 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6507 3 : d->getGeodBaseCRS(), conv, cs);
6508 3 : proj_destroy(conv);
6509 3 : proj_destroy(cs);
6510 :
6511 3 : d->setPjCRS(projCRS);
6512 :
6513 3 : d->undoDemoteFromBoundCRS();
6514 :
6515 6 : return OGRERR_NONE;
6516 : }
6517 :
6518 : /************************************************************************/
6519 : /* OSRSetTMSO() */
6520 : /************************************************************************/
6521 :
6522 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6523 : double dfCenterLong, double dfScale, double dfFalseEasting,
6524 : double dfFalseNorthing)
6525 :
6526 : {
6527 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6528 :
6529 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6530 0 : dfFalseEasting, dfFalseNorthing);
6531 : }
6532 :
6533 : /************************************************************************/
6534 : /* SetTPED() */
6535 : /************************************************************************/
6536 :
6537 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6538 : double dfLat2, double dfLong2,
6539 : double dfFalseEasting,
6540 : double dfFalseNorthing)
6541 :
6542 : {
6543 2 : TAKE_OPTIONAL_LOCK();
6544 :
6545 1 : return d->replaceConversionAndUnref(
6546 : proj_create_conversion_two_point_equidistant(
6547 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6548 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6549 : }
6550 :
6551 : /************************************************************************/
6552 : /* OSRSetTPED() */
6553 : /************************************************************************/
6554 :
6555 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6556 : double dfLat2, double dfLong2, double dfFalseEasting,
6557 : double dfFalseNorthing)
6558 :
6559 : {
6560 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6561 :
6562 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6563 0 : dfFalseEasting, dfFalseNorthing);
6564 : }
6565 :
6566 : /************************************************************************/
6567 : /* SetTMG() */
6568 : /************************************************************************/
6569 :
6570 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6571 : double dfFalseEasting,
6572 : double dfFalseNorthing)
6573 :
6574 : {
6575 0 : TAKE_OPTIONAL_LOCK();
6576 :
6577 0 : return d->replaceConversionAndUnref(
6578 : proj_create_conversion_tunisia_mapping_grid(
6579 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6580 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6581 : }
6582 :
6583 : /************************************************************************/
6584 : /* OSRSetTMG() */
6585 : /************************************************************************/
6586 :
6587 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6588 : double dfCenterLong, double dfFalseEasting,
6589 : double dfFalseNorthing)
6590 :
6591 : {
6592 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6593 :
6594 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6595 0 : dfFalseNorthing);
6596 : }
6597 :
6598 : /************************************************************************/
6599 : /* SetACEA() */
6600 : /************************************************************************/
6601 :
6602 39 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6603 : double dfCenterLat, double dfCenterLong,
6604 : double dfFalseEasting,
6605 : double dfFalseNorthing)
6606 :
6607 : {
6608 78 : TAKE_OPTIONAL_LOCK();
6609 :
6610 : // Note different order of parameters. The one in PROJ is conformant with
6611 : // EPSG
6612 39 : return d->replaceConversionAndUnref(
6613 : proj_create_conversion_albers_equal_area(
6614 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6615 78 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6616 : }
6617 :
6618 : /************************************************************************/
6619 : /* OSRSetACEA() */
6620 : /************************************************************************/
6621 :
6622 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6623 : double dfCenterLat, double dfCenterLong,
6624 : double dfFalseEasting, double dfFalseNorthing)
6625 :
6626 : {
6627 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6628 :
6629 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6630 0 : dfFalseEasting, dfFalseNorthing);
6631 : }
6632 :
6633 : /************************************************************************/
6634 : /* SetAE() */
6635 : /************************************************************************/
6636 :
6637 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6638 : double dfFalseEasting, double dfFalseNorthing)
6639 :
6640 : {
6641 42 : TAKE_OPTIONAL_LOCK();
6642 :
6643 21 : return d->replaceConversionAndUnref(
6644 : proj_create_conversion_azimuthal_equidistant(
6645 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6646 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6647 : }
6648 :
6649 : /************************************************************************/
6650 : /* OSRSetAE() */
6651 : /************************************************************************/
6652 :
6653 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6654 : double dfCenterLong, double dfFalseEasting,
6655 : double dfFalseNorthing)
6656 :
6657 : {
6658 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6659 :
6660 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6661 0 : dfFalseNorthing);
6662 : }
6663 :
6664 : /************************************************************************/
6665 : /* SetBonne() */
6666 : /************************************************************************/
6667 :
6668 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6669 : double dfFalseEasting,
6670 : double dfFalseNorthing)
6671 :
6672 : {
6673 2 : TAKE_OPTIONAL_LOCK();
6674 :
6675 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6676 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6677 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6678 : }
6679 :
6680 : /************************************************************************/
6681 : /* OSRSetBonne() */
6682 : /************************************************************************/
6683 :
6684 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6685 : double dfCentralMeridian, double dfFalseEasting,
6686 : double dfFalseNorthing)
6687 :
6688 : {
6689 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6690 :
6691 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6692 0 : dfFalseNorthing);
6693 : }
6694 :
6695 : /************************************************************************/
6696 : /* SetCEA() */
6697 : /************************************************************************/
6698 :
6699 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6700 : double dfFalseEasting,
6701 : double dfFalseNorthing)
6702 :
6703 : {
6704 8 : TAKE_OPTIONAL_LOCK();
6705 :
6706 4 : return d->replaceConversionAndUnref(
6707 : proj_create_conversion_lambert_cylindrical_equal_area(
6708 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6709 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6710 : }
6711 :
6712 : /************************************************************************/
6713 : /* OSRSetCEA() */
6714 : /************************************************************************/
6715 :
6716 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6717 : double dfCentralMeridian, double dfFalseEasting,
6718 : double dfFalseNorthing)
6719 :
6720 : {
6721 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6722 :
6723 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6724 0 : dfFalseNorthing);
6725 : }
6726 :
6727 : /************************************************************************/
6728 : /* SetCS() */
6729 : /************************************************************************/
6730 :
6731 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6732 : double dfFalseEasting, double dfFalseNorthing)
6733 :
6734 : {
6735 10 : TAKE_OPTIONAL_LOCK();
6736 :
6737 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6738 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6739 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6740 : }
6741 :
6742 : /************************************************************************/
6743 : /* OSRSetCS() */
6744 : /************************************************************************/
6745 :
6746 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6747 : double dfCenterLong, double dfFalseEasting,
6748 : double dfFalseNorthing)
6749 :
6750 : {
6751 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6752 :
6753 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6754 0 : dfFalseNorthing);
6755 : }
6756 :
6757 : /************************************************************************/
6758 : /* SetEC() */
6759 : /************************************************************************/
6760 :
6761 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6762 : double dfCenterLat, double dfCenterLong,
6763 : double dfFalseEasting, double dfFalseNorthing)
6764 :
6765 : {
6766 14 : TAKE_OPTIONAL_LOCK();
6767 :
6768 : // Note: different order of arguments
6769 7 : return d->replaceConversionAndUnref(
6770 : proj_create_conversion_equidistant_conic(
6771 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6772 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6773 : }
6774 :
6775 : /************************************************************************/
6776 : /* OSRSetEC() */
6777 : /************************************************************************/
6778 :
6779 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6780 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6781 : double dfFalseNorthing)
6782 :
6783 : {
6784 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6785 :
6786 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6787 0 : dfFalseEasting, dfFalseNorthing);
6788 : }
6789 :
6790 : /************************************************************************/
6791 : /* SetEckert() */
6792 : /************************************************************************/
6793 :
6794 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6795 : double dfCentralMeridian,
6796 : double dfFalseEasting,
6797 : double dfFalseNorthing)
6798 :
6799 : {
6800 20 : TAKE_OPTIONAL_LOCK();
6801 :
6802 : PJ *conv;
6803 10 : if (nVariation == 1)
6804 : {
6805 1 : conv = proj_create_conversion_eckert_i(
6806 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6807 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6808 : }
6809 9 : else if (nVariation == 2)
6810 : {
6811 1 : conv = proj_create_conversion_eckert_ii(
6812 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6813 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6814 : }
6815 8 : else if (nVariation == 3)
6816 : {
6817 1 : conv = proj_create_conversion_eckert_iii(
6818 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6819 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6820 : }
6821 7 : else if (nVariation == 4)
6822 : {
6823 3 : conv = proj_create_conversion_eckert_iv(
6824 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6825 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6826 : }
6827 4 : else if (nVariation == 5)
6828 : {
6829 1 : conv = proj_create_conversion_eckert_v(
6830 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6831 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6832 : }
6833 3 : else if (nVariation == 6)
6834 : {
6835 3 : conv = proj_create_conversion_eckert_vi(
6836 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6837 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6838 : }
6839 : else
6840 : {
6841 0 : CPLError(CE_Failure, CPLE_AppDefined,
6842 : "Unsupported Eckert variation (%d).", nVariation);
6843 0 : return OGRERR_UNSUPPORTED_SRS;
6844 : }
6845 :
6846 10 : return d->replaceConversionAndUnref(conv);
6847 : }
6848 :
6849 : /************************************************************************/
6850 : /* OSRSetEckert() */
6851 : /************************************************************************/
6852 :
6853 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6854 : double dfCentralMeridian, double dfFalseEasting,
6855 : double dfFalseNorthing)
6856 :
6857 : {
6858 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6859 :
6860 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6861 0 : dfFalseEasting, dfFalseNorthing);
6862 : }
6863 :
6864 : /************************************************************************/
6865 : /* SetEckertIV() */
6866 : /* */
6867 : /* Deprecated */
6868 : /************************************************************************/
6869 :
6870 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6871 : double dfFalseEasting,
6872 : double dfFalseNorthing)
6873 :
6874 : {
6875 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6876 : }
6877 :
6878 : /************************************************************************/
6879 : /* OSRSetEckertIV() */
6880 : /************************************************************************/
6881 :
6882 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6883 : double dfFalseEasting, double dfFalseNorthing)
6884 :
6885 : {
6886 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6887 :
6888 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6889 0 : dfFalseNorthing);
6890 : }
6891 :
6892 : /************************************************************************/
6893 : /* SetEckertVI() */
6894 : /* */
6895 : /* Deprecated */
6896 : /************************************************************************/
6897 :
6898 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6899 : double dfFalseEasting,
6900 : double dfFalseNorthing)
6901 :
6902 : {
6903 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6904 : }
6905 :
6906 : /************************************************************************/
6907 : /* OSRSetEckertVI() */
6908 : /************************************************************************/
6909 :
6910 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6911 : double dfFalseEasting, double dfFalseNorthing)
6912 :
6913 : {
6914 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6915 :
6916 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6917 0 : dfFalseNorthing);
6918 : }
6919 :
6920 : /************************************************************************/
6921 : /* SetEquirectangular() */
6922 : /************************************************************************/
6923 :
6924 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6925 : double dfCenterLong,
6926 : double dfFalseEasting,
6927 : double dfFalseNorthing)
6928 :
6929 : {
6930 4 : TAKE_OPTIONAL_LOCK();
6931 :
6932 2 : if (dfCenterLat == 0.0)
6933 : {
6934 0 : return d->replaceConversionAndUnref(
6935 : proj_create_conversion_equidistant_cylindrical(
6936 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6937 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6938 : }
6939 :
6940 : // Non-standard extension with non-zero latitude of origin
6941 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6942 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6943 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6944 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6945 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6946 :
6947 2 : return OGRERR_NONE;
6948 : }
6949 :
6950 : /************************************************************************/
6951 : /* OSRSetEquirectangular() */
6952 : /************************************************************************/
6953 :
6954 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6955 : double dfCenterLong, double dfFalseEasting,
6956 : double dfFalseNorthing)
6957 :
6958 : {
6959 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6960 :
6961 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6962 0 : dfFalseEasting, dfFalseNorthing);
6963 : }
6964 :
6965 : /************************************************************************/
6966 : /* SetEquirectangular2() */
6967 : /* Generalized form */
6968 : /************************************************************************/
6969 :
6970 181 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6971 : double dfCenterLong,
6972 : double dfStdParallel1,
6973 : double dfFalseEasting,
6974 : double dfFalseNorthing)
6975 :
6976 : {
6977 362 : TAKE_OPTIONAL_LOCK();
6978 :
6979 181 : if (dfCenterLat == 0.0)
6980 : {
6981 176 : return d->replaceConversionAndUnref(
6982 : proj_create_conversion_equidistant_cylindrical(
6983 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6984 176 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6985 : }
6986 :
6987 : // Non-standard extension with non-zero latitude of origin
6988 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6989 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6990 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6991 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6992 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6993 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6994 :
6995 5 : return OGRERR_NONE;
6996 : }
6997 :
6998 : /************************************************************************/
6999 : /* OSRSetEquirectangular2() */
7000 : /************************************************************************/
7001 :
7002 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
7003 : double dfCenterLong, double dfStdParallel1,
7004 : double dfFalseEasting, double dfFalseNorthing)
7005 :
7006 : {
7007 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
7008 :
7009 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
7010 : dfStdParallel1, dfFalseEasting,
7011 3 : dfFalseNorthing);
7012 : }
7013 :
7014 : /************************************************************************/
7015 : /* SetGS() */
7016 : /************************************************************************/
7017 :
7018 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
7019 : double dfFalseEasting, double dfFalseNorthing)
7020 :
7021 : {
7022 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
7023 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7024 5 : nullptr, 0.0, nullptr, 0.0));
7025 : }
7026 :
7027 : /************************************************************************/
7028 : /* OSRSetGS() */
7029 : /************************************************************************/
7030 :
7031 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7032 : double dfFalseEasting, double dfFalseNorthing)
7033 :
7034 : {
7035 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
7036 :
7037 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
7038 2 : dfFalseNorthing);
7039 : }
7040 :
7041 : /************************************************************************/
7042 : /* SetGH() */
7043 : /************************************************************************/
7044 :
7045 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
7046 : double dfFalseEasting, double dfFalseNorthing)
7047 :
7048 : {
7049 0 : TAKE_OPTIONAL_LOCK();
7050 :
7051 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
7052 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7053 0 : nullptr, 0.0, nullptr, 0.0));
7054 : }
7055 :
7056 : /************************************************************************/
7057 : /* OSRSetGH() */
7058 : /************************************************************************/
7059 :
7060 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7061 : double dfFalseEasting, double dfFalseNorthing)
7062 :
7063 : {
7064 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
7065 :
7066 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
7067 0 : dfFalseNorthing);
7068 : }
7069 :
7070 : /************************************************************************/
7071 : /* SetIGH() */
7072 : /************************************************************************/
7073 :
7074 0 : OGRErr OGRSpatialReference::SetIGH()
7075 :
7076 : {
7077 0 : TAKE_OPTIONAL_LOCK();
7078 :
7079 0 : return d->replaceConversionAndUnref(
7080 : proj_create_conversion_interrupted_goode_homolosine(
7081 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7082 : }
7083 :
7084 : /************************************************************************/
7085 : /* OSRSetIGH() */
7086 : /************************************************************************/
7087 :
7088 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7089 :
7090 : {
7091 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7092 :
7093 0 : return ToPointer(hSRS)->SetIGH();
7094 : }
7095 :
7096 : /************************************************************************/
7097 : /* SetGEOS() */
7098 : /************************************************************************/
7099 :
7100 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7101 : double dfSatelliteHeight,
7102 : double dfFalseEasting,
7103 : double dfFalseNorthing)
7104 :
7105 : {
7106 6 : TAKE_OPTIONAL_LOCK();
7107 :
7108 3 : return d->replaceConversionAndUnref(
7109 : proj_create_conversion_geostationary_satellite_sweep_y(
7110 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7111 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7112 : }
7113 :
7114 : /************************************************************************/
7115 : /* OSRSetGEOS() */
7116 : /************************************************************************/
7117 :
7118 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7119 : double dfSatelliteHeight, double dfFalseEasting,
7120 : double dfFalseNorthing)
7121 :
7122 : {
7123 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7124 :
7125 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7126 0 : dfFalseEasting, dfFalseNorthing);
7127 : }
7128 :
7129 : /************************************************************************/
7130 : /* SetGaussSchreiberTMercator() */
7131 : /************************************************************************/
7132 :
7133 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7134 : double dfCenterLong,
7135 : double dfScale,
7136 : double dfFalseEasting,
7137 : double dfFalseNorthing)
7138 :
7139 : {
7140 0 : TAKE_OPTIONAL_LOCK();
7141 :
7142 0 : return d->replaceConversionAndUnref(
7143 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7144 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7145 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7146 : }
7147 :
7148 : /************************************************************************/
7149 : /* OSRSetGaussSchreiberTMercator() */
7150 : /************************************************************************/
7151 :
7152 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7153 : double dfCenterLat, double dfCenterLong,
7154 : double dfScale, double dfFalseEasting,
7155 : double dfFalseNorthing)
7156 :
7157 : {
7158 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7159 :
7160 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7161 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7162 : }
7163 :
7164 : /************************************************************************/
7165 : /* SetGnomonic() */
7166 : /************************************************************************/
7167 :
7168 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7169 : double dfFalseEasting,
7170 : double dfFalseNorthing)
7171 :
7172 : {
7173 4 : TAKE_OPTIONAL_LOCK();
7174 :
7175 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7176 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7177 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7178 : }
7179 :
7180 : /************************************************************************/
7181 : /* OSRSetGnomonic() */
7182 : /************************************************************************/
7183 :
7184 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7185 : double dfCenterLong, double dfFalseEasting,
7186 : double dfFalseNorthing)
7187 :
7188 : {
7189 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7190 :
7191 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7192 0 : dfFalseEasting, dfFalseNorthing);
7193 : }
7194 :
7195 : /************************************************************************/
7196 : /* SetHOMAC() */
7197 : /************************************************************************/
7198 :
7199 : /**
7200 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7201 : * azimuth angle.
7202 : *
7203 : * This projection corresponds to EPSG projection method 9815, also
7204 : * sometimes known as hotine oblique mercator (variant B).
7205 : *
7206 : * This method does the same thing as the C function OSRSetHOMAC().
7207 : *
7208 : * @param dfCenterLat Latitude of the projection origin.
7209 : * @param dfCenterLong Longitude of the projection origin.
7210 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7211 : * centerline.
7212 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7213 : * @param dfScale Scale factor applies to the projection origin.
7214 : * @param dfFalseEasting False easting.
7215 : * @param dfFalseNorthing False northing.
7216 : *
7217 : * @return OGRERR_NONE on success.
7218 : */
7219 :
7220 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7221 : double dfAzimuth, double dfRectToSkew,
7222 : double dfScale, double dfFalseEasting,
7223 : double dfFalseNorthing)
7224 :
7225 : {
7226 8 : TAKE_OPTIONAL_LOCK();
7227 :
7228 4 : return d->replaceConversionAndUnref(
7229 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7230 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7231 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7232 8 : 0.0, nullptr, 0.0));
7233 : }
7234 :
7235 : /************************************************************************/
7236 : /* OSRSetHOMAC() */
7237 : /************************************************************************/
7238 :
7239 : /**
7240 : * \brief Set an Oblique Mercator projection using azimuth angle.
7241 : *
7242 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7243 : */
7244 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7245 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7246 : double dfScale, double dfFalseEasting,
7247 : double dfFalseNorthing)
7248 :
7249 : {
7250 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7251 :
7252 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7253 : dfRectToSkew, dfScale, dfFalseEasting,
7254 0 : dfFalseNorthing);
7255 : }
7256 :
7257 : /************************************************************************/
7258 : /* SetHOM() */
7259 : /************************************************************************/
7260 :
7261 : /**
7262 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7263 : *
7264 : * This projection corresponds to EPSG projection method 9812, also
7265 : * sometimes known as hotine oblique mercator (variant A)..
7266 : *
7267 : * This method does the same thing as the C function OSRSetHOM().
7268 : *
7269 : * @param dfCenterLat Latitude of the projection origin.
7270 : * @param dfCenterLong Longitude of the projection origin.
7271 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7272 : * centerline.
7273 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7274 : * @param dfScale Scale factor applies to the projection origin.
7275 : * @param dfFalseEasting False easting.
7276 : * @param dfFalseNorthing False northing.
7277 : *
7278 : * @return OGRERR_NONE on success.
7279 : */
7280 :
7281 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7282 : double dfAzimuth, double dfRectToSkew,
7283 : double dfScale, double dfFalseEasting,
7284 : double dfFalseNorthing)
7285 :
7286 : {
7287 26 : TAKE_OPTIONAL_LOCK();
7288 :
7289 13 : return d->replaceConversionAndUnref(
7290 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7291 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7292 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7293 26 : 0.0, nullptr, 0.0));
7294 : }
7295 :
7296 : /************************************************************************/
7297 : /* OSRSetHOM() */
7298 : /************************************************************************/
7299 : /**
7300 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7301 : *
7302 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7303 : */
7304 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7305 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7306 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7307 :
7308 : {
7309 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7310 :
7311 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7312 : dfRectToSkew, dfScale, dfFalseEasting,
7313 0 : dfFalseNorthing);
7314 : }
7315 :
7316 : /************************************************************************/
7317 : /* SetHOM2PNO() */
7318 : /************************************************************************/
7319 :
7320 : /**
7321 : * \brief Set a Hotine Oblique Mercator projection using two points on
7322 : * projection centerline.
7323 : *
7324 : * This method does the same thing as the C function OSRSetHOM2PNO().
7325 : *
7326 : * @param dfCenterLat Latitude of the projection origin.
7327 : * @param dfLat1 Latitude of the first point on center line.
7328 : * @param dfLong1 Longitude of the first point on center line.
7329 : * @param dfLat2 Latitude of the second point on center line.
7330 : * @param dfLong2 Longitude of the second point on center line.
7331 : * @param dfScale Scale factor applies to the projection origin.
7332 : * @param dfFalseEasting False easting.
7333 : * @param dfFalseNorthing False northing.
7334 : *
7335 : * @return OGRERR_NONE on success.
7336 : */
7337 :
7338 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7339 : double dfLong1, double dfLat2,
7340 : double dfLong2, double dfScale,
7341 : double dfFalseEasting,
7342 : double dfFalseNorthing)
7343 :
7344 : {
7345 6 : TAKE_OPTIONAL_LOCK();
7346 :
7347 3 : return d->replaceConversionAndUnref(
7348 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7349 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7350 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7351 6 : 0.0));
7352 : }
7353 :
7354 : /************************************************************************/
7355 : /* OSRSetHOM2PNO() */
7356 : /************************************************************************/
7357 : /**
7358 : * \brief Set a Hotine Oblique Mercator projection using two points on
7359 : * projection centerline.
7360 : *
7361 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7362 : */
7363 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7364 : double dfLat1, double dfLong1, double dfLat2,
7365 : double dfLong2, double dfScale, double dfFalseEasting,
7366 : double dfFalseNorthing)
7367 :
7368 : {
7369 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7370 :
7371 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7372 : dfLong2, dfScale, dfFalseEasting,
7373 0 : dfFalseNorthing);
7374 : }
7375 :
7376 : /************************************************************************/
7377 : /* SetLOM() */
7378 : /************************************************************************/
7379 :
7380 : /**
7381 : * \brief Set a Laborde Oblique Mercator projection.
7382 : *
7383 : * @param dfCenterLat Latitude of the projection origin.
7384 : * @param dfCenterLong Longitude of the projection origin.
7385 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7386 : * centerline.
7387 : * @param dfScale Scale factor on the initial line
7388 : * @param dfFalseEasting False easting.
7389 : * @param dfFalseNorthing False northing.
7390 : *
7391 : * @return OGRERR_NONE on success.
7392 : */
7393 :
7394 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7395 : double dfAzimuth, double dfScale,
7396 : double dfFalseEasting,
7397 : double dfFalseNorthing)
7398 :
7399 : {
7400 0 : TAKE_OPTIONAL_LOCK();
7401 :
7402 0 : return d->replaceConversionAndUnref(
7403 : proj_create_conversion_laborde_oblique_mercator(
7404 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7405 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7406 : }
7407 :
7408 : /************************************************************************/
7409 : /* SetIWMPolyconic() */
7410 : /************************************************************************/
7411 :
7412 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7413 : double dfCenterLong,
7414 : double dfFalseEasting,
7415 : double dfFalseNorthing)
7416 :
7417 : {
7418 0 : TAKE_OPTIONAL_LOCK();
7419 :
7420 0 : return d->replaceConversionAndUnref(
7421 : proj_create_conversion_international_map_world_polyconic(
7422 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7423 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7424 : }
7425 :
7426 : /************************************************************************/
7427 : /* OSRSetIWMPolyconic() */
7428 : /************************************************************************/
7429 :
7430 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7431 : double dfLat2, double dfCenterLong,
7432 : double dfFalseEasting, double dfFalseNorthing)
7433 :
7434 : {
7435 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7436 :
7437 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7438 0 : dfFalseEasting, dfFalseNorthing);
7439 : }
7440 :
7441 : /************************************************************************/
7442 : /* SetKrovak() */
7443 : /************************************************************************/
7444 :
7445 : /** Krovak east-north projection.
7446 : *
7447 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7448 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7449 : */
7450 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7451 : double dfAzimuth,
7452 : double dfPseudoStdParallel1,
7453 : double dfScale, double dfFalseEasting,
7454 : double dfFalseNorthing)
7455 :
7456 : {
7457 6 : TAKE_OPTIONAL_LOCK();
7458 :
7459 3 : return d->replaceConversionAndUnref(
7460 : proj_create_conversion_krovak_north_oriented(
7461 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7462 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7463 6 : nullptr, 0.0, nullptr, 0.0));
7464 : }
7465 :
7466 : /************************************************************************/
7467 : /* OSRSetKrovak() */
7468 : /************************************************************************/
7469 :
7470 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7471 : double dfCenterLong, double dfAzimuth,
7472 : double dfPseudoStdParallel1, double dfScale,
7473 : double dfFalseEasting, double dfFalseNorthing)
7474 :
7475 : {
7476 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7477 :
7478 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7479 : dfPseudoStdParallel1, dfScale,
7480 0 : dfFalseEasting, dfFalseNorthing);
7481 : }
7482 :
7483 : /************************************************************************/
7484 : /* SetLAEA() */
7485 : /************************************************************************/
7486 :
7487 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7488 : double dfFalseEasting,
7489 : double dfFalseNorthing)
7490 :
7491 : {
7492 34 : TAKE_OPTIONAL_LOCK();
7493 :
7494 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7495 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7496 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7497 :
7498 17 : const char *pszName = nullptr;
7499 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7500 17 : CPLString osName = pszName ? pszName : "";
7501 :
7502 17 : d->refreshProjObj();
7503 :
7504 17 : d->demoteFromBoundCRS();
7505 :
7506 17 : auto cs = proj_create_cartesian_2D_cs(
7507 : d->getPROJContext(),
7508 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7509 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7510 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7511 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7512 : : PJ_CART2D_EASTING_NORTHING,
7513 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7514 : auto projCRS =
7515 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7516 17 : d->getGeodBaseCRS(), conv, cs);
7517 17 : proj_destroy(conv);
7518 17 : proj_destroy(cs);
7519 :
7520 17 : d->setPjCRS(projCRS);
7521 :
7522 17 : d->undoDemoteFromBoundCRS();
7523 :
7524 34 : return OGRERR_NONE;
7525 : }
7526 :
7527 : /************************************************************************/
7528 : /* OSRSetLAEA() */
7529 : /************************************************************************/
7530 :
7531 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7532 : double dfCenterLong, double dfFalseEasting,
7533 : double dfFalseNorthing)
7534 :
7535 : {
7536 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7537 :
7538 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7539 0 : dfFalseNorthing);
7540 : }
7541 :
7542 : /************************************************************************/
7543 : /* SetLCC() */
7544 : /************************************************************************/
7545 :
7546 149 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7547 : double dfCenterLat, double dfCenterLong,
7548 : double dfFalseEasting,
7549 : double dfFalseNorthing)
7550 :
7551 : {
7552 298 : TAKE_OPTIONAL_LOCK();
7553 :
7554 149 : return d->replaceConversionAndUnref(
7555 : proj_create_conversion_lambert_conic_conformal_2sp(
7556 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7557 298 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7558 : }
7559 :
7560 : /************************************************************************/
7561 : /* OSRSetLCC() */
7562 : /************************************************************************/
7563 :
7564 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7565 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7566 : double dfFalseNorthing)
7567 :
7568 : {
7569 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7570 :
7571 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7572 1 : dfFalseEasting, dfFalseNorthing);
7573 : }
7574 :
7575 : /************************************************************************/
7576 : /* SetLCC1SP() */
7577 : /************************************************************************/
7578 :
7579 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7580 : double dfScale, double dfFalseEasting,
7581 : double dfFalseNorthing)
7582 :
7583 : {
7584 20 : TAKE_OPTIONAL_LOCK();
7585 :
7586 10 : return d->replaceConversionAndUnref(
7587 : proj_create_conversion_lambert_conic_conformal_1sp(
7588 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7589 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7590 : }
7591 :
7592 : /************************************************************************/
7593 : /* OSRSetLCC1SP() */
7594 : /************************************************************************/
7595 :
7596 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7597 : double dfCenterLong, double dfScale, double dfFalseEasting,
7598 : double dfFalseNorthing)
7599 :
7600 : {
7601 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7602 :
7603 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7604 0 : dfFalseEasting, dfFalseNorthing);
7605 : }
7606 :
7607 : /************************************************************************/
7608 : /* SetLCCB() */
7609 : /************************************************************************/
7610 :
7611 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7612 : double dfCenterLat, double dfCenterLong,
7613 : double dfFalseEasting,
7614 : double dfFalseNorthing)
7615 :
7616 : {
7617 4 : TAKE_OPTIONAL_LOCK();
7618 :
7619 2 : return d->replaceConversionAndUnref(
7620 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7621 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7622 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7623 : }
7624 :
7625 : /************************************************************************/
7626 : /* OSRSetLCCB() */
7627 : /************************************************************************/
7628 :
7629 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7630 : double dfCenterLat, double dfCenterLong,
7631 : double dfFalseEasting, double dfFalseNorthing)
7632 :
7633 : {
7634 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7635 :
7636 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7637 0 : dfFalseEasting, dfFalseNorthing);
7638 : }
7639 :
7640 : /************************************************************************/
7641 : /* SetMC() */
7642 : /************************************************************************/
7643 :
7644 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7645 : double dfFalseEasting, double dfFalseNorthing)
7646 :
7647 : {
7648 8 : TAKE_OPTIONAL_LOCK();
7649 :
7650 : (void)dfCenterLat; // ignored
7651 :
7652 4 : return d->replaceConversionAndUnref(
7653 : proj_create_conversion_miller_cylindrical(
7654 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7655 8 : nullptr, 0, nullptr, 0));
7656 : }
7657 :
7658 : /************************************************************************/
7659 : /* OSRSetMC() */
7660 : /************************************************************************/
7661 :
7662 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7663 : double dfCenterLong, double dfFalseEasting,
7664 : double dfFalseNorthing)
7665 :
7666 : {
7667 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7668 :
7669 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7670 0 : dfFalseNorthing);
7671 : }
7672 :
7673 : /************************************************************************/
7674 : /* SetMercator() */
7675 : /************************************************************************/
7676 :
7677 60 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7678 : double dfScale, double dfFalseEasting,
7679 : double dfFalseNorthing)
7680 :
7681 : {
7682 120 : TAKE_OPTIONAL_LOCK();
7683 :
7684 60 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7685 : {
7686 : // Not sure this is correct, but this is how it has been used
7687 : // historically
7688 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7689 0 : dfFalseNorthing);
7690 : }
7691 60 : return d->replaceConversionAndUnref(
7692 : proj_create_conversion_mercator_variant_a(
7693 : d->getPROJContext(),
7694 : dfCenterLat, // should be zero
7695 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7696 60 : nullptr, 0));
7697 : }
7698 :
7699 : /************************************************************************/
7700 : /* OSRSetMercator() */
7701 : /************************************************************************/
7702 :
7703 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7704 : double dfCenterLong, double dfScale,
7705 : double dfFalseEasting, double dfFalseNorthing)
7706 :
7707 : {
7708 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7709 :
7710 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7711 2 : dfFalseEasting, dfFalseNorthing);
7712 : }
7713 :
7714 : /************************************************************************/
7715 : /* SetMercator2SP() */
7716 : /************************************************************************/
7717 :
7718 30 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7719 : double dfCenterLong,
7720 : double dfFalseEasting,
7721 : double dfFalseNorthing)
7722 :
7723 : {
7724 30 : if (dfCenterLat == 0.0)
7725 : {
7726 29 : return d->replaceConversionAndUnref(
7727 : proj_create_conversion_mercator_variant_b(
7728 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7729 29 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7730 : }
7731 :
7732 1 : TAKE_OPTIONAL_LOCK();
7733 :
7734 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7735 :
7736 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7737 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7738 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7739 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7740 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7741 :
7742 1 : return OGRERR_NONE;
7743 : }
7744 :
7745 : /************************************************************************/
7746 : /* OSRSetMercator2SP() */
7747 : /************************************************************************/
7748 :
7749 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7750 : double dfCenterLat, double dfCenterLong,
7751 : double dfFalseEasting, double dfFalseNorthing)
7752 :
7753 : {
7754 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7755 :
7756 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7757 1 : dfFalseEasting, dfFalseNorthing);
7758 : }
7759 :
7760 : /************************************************************************/
7761 : /* SetMollweide() */
7762 : /************************************************************************/
7763 :
7764 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7765 : double dfFalseEasting,
7766 : double dfFalseNorthing)
7767 :
7768 : {
7769 6 : TAKE_OPTIONAL_LOCK();
7770 :
7771 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7772 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7773 6 : nullptr, 0, nullptr, 0));
7774 : }
7775 :
7776 : /************************************************************************/
7777 : /* OSRSetMollweide() */
7778 : /************************************************************************/
7779 :
7780 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7781 : double dfFalseEasting, double dfFalseNorthing)
7782 :
7783 : {
7784 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7785 :
7786 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7787 0 : dfFalseNorthing);
7788 : }
7789 :
7790 : /************************************************************************/
7791 : /* SetNZMG() */
7792 : /************************************************************************/
7793 :
7794 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7795 : double dfFalseEasting,
7796 : double dfFalseNorthing)
7797 :
7798 : {
7799 14 : TAKE_OPTIONAL_LOCK();
7800 :
7801 7 : return d->replaceConversionAndUnref(
7802 : proj_create_conversion_new_zealand_mapping_grid(
7803 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7804 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7805 : }
7806 :
7807 : /************************************************************************/
7808 : /* OSRSetNZMG() */
7809 : /************************************************************************/
7810 :
7811 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7812 : double dfCenterLong, double dfFalseEasting,
7813 : double dfFalseNorthing)
7814 :
7815 : {
7816 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7817 :
7818 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7819 0 : dfFalseNorthing);
7820 : }
7821 :
7822 : /************************************************************************/
7823 : /* SetOS() */
7824 : /************************************************************************/
7825 :
7826 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7827 : double dfScale, double dfFalseEasting,
7828 : double dfFalseNorthing)
7829 :
7830 : {
7831 12 : TAKE_OPTIONAL_LOCK();
7832 :
7833 6 : return d->replaceConversionAndUnref(
7834 : proj_create_conversion_oblique_stereographic(
7835 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7836 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7837 : }
7838 :
7839 : /************************************************************************/
7840 : /* OSRSetOS() */
7841 : /************************************************************************/
7842 :
7843 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7844 : double dfCMeridian, double dfScale, double dfFalseEasting,
7845 : double dfFalseNorthing)
7846 :
7847 : {
7848 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7849 :
7850 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7851 0 : dfFalseEasting, dfFalseNorthing);
7852 : }
7853 :
7854 : /************************************************************************/
7855 : /* SetOrthographic() */
7856 : /************************************************************************/
7857 :
7858 8 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7859 : double dfCenterLong,
7860 : double dfFalseEasting,
7861 : double dfFalseNorthing)
7862 :
7863 : {
7864 16 : TAKE_OPTIONAL_LOCK();
7865 :
7866 8 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7867 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7868 16 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7869 : }
7870 :
7871 : /************************************************************************/
7872 : /* OSRSetOrthographic() */
7873 : /************************************************************************/
7874 :
7875 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7876 : double dfCenterLong, double dfFalseEasting,
7877 : double dfFalseNorthing)
7878 :
7879 : {
7880 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7881 :
7882 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7883 1 : dfFalseEasting, dfFalseNorthing);
7884 : }
7885 :
7886 : /************************************************************************/
7887 : /* SetPolyconic() */
7888 : /************************************************************************/
7889 :
7890 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7891 : double dfCenterLong,
7892 : double dfFalseEasting,
7893 : double dfFalseNorthing)
7894 :
7895 : {
7896 14 : TAKE_OPTIONAL_LOCK();
7897 :
7898 : // note: it seems that by some definitions this should include a
7899 : // scale_factor parameter.
7900 7 : return d->replaceConversionAndUnref(
7901 : proj_create_conversion_american_polyconic(
7902 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7903 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7904 : }
7905 :
7906 : /************************************************************************/
7907 : /* OSRSetPolyconic() */
7908 : /************************************************************************/
7909 :
7910 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7911 : double dfCenterLong, double dfFalseEasting,
7912 : double dfFalseNorthing)
7913 :
7914 : {
7915 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7916 :
7917 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7918 0 : dfFalseEasting, dfFalseNorthing);
7919 : }
7920 :
7921 : /************************************************************************/
7922 : /* SetPS() */
7923 : /************************************************************************/
7924 :
7925 : /** Sets a Polar Stereographic projection.
7926 : *
7927 : * Two variants are possible:
7928 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7929 : * interpreted as the latitude of origin, combined with the scale factor
7930 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7931 : * is interpreted as the latitude of true scale. In that situation, dfScale
7932 : * must be set to 1 (it is ignored in the projection parameters)
7933 : */
7934 32 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7935 : double dfScale, double dfFalseEasting,
7936 : double dfFalseNorthing)
7937 :
7938 : {
7939 64 : TAKE_OPTIONAL_LOCK();
7940 :
7941 : PJ *conv;
7942 32 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7943 : {
7944 21 : conv = proj_create_conversion_polar_stereographic_variant_b(
7945 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7946 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7947 : }
7948 : else
7949 : {
7950 11 : conv = proj_create_conversion_polar_stereographic_variant_a(
7951 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7952 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7953 : }
7954 :
7955 32 : const char *pszName = nullptr;
7956 32 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7957 32 : CPLString osName = pszName ? pszName : "";
7958 :
7959 32 : d->refreshProjObj();
7960 :
7961 32 : d->demoteFromBoundCRS();
7962 :
7963 32 : auto cs = proj_create_cartesian_2D_cs(
7964 : d->getPROJContext(),
7965 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7966 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7967 32 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7968 : auto projCRS =
7969 32 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7970 32 : d->getGeodBaseCRS(), conv, cs);
7971 32 : proj_destroy(conv);
7972 32 : proj_destroy(cs);
7973 :
7974 32 : d->setPjCRS(projCRS);
7975 :
7976 32 : d->undoDemoteFromBoundCRS();
7977 :
7978 64 : return OGRERR_NONE;
7979 : }
7980 :
7981 : /************************************************************************/
7982 : /* OSRSetPS() */
7983 : /************************************************************************/
7984 :
7985 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7986 : double dfCenterLong, double dfScale, double dfFalseEasting,
7987 : double dfFalseNorthing)
7988 :
7989 : {
7990 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7991 :
7992 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7993 1 : dfFalseEasting, dfFalseNorthing);
7994 : }
7995 :
7996 : /************************************************************************/
7997 : /* SetRobinson() */
7998 : /************************************************************************/
7999 :
8000 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
8001 : double dfFalseEasting,
8002 : double dfFalseNorthing)
8003 :
8004 : {
8005 8 : TAKE_OPTIONAL_LOCK();
8006 :
8007 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
8008 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
8009 8 : nullptr, 0, nullptr, 0));
8010 : }
8011 :
8012 : /************************************************************************/
8013 : /* OSRSetRobinson() */
8014 : /************************************************************************/
8015 :
8016 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
8017 : double dfFalseEasting, double dfFalseNorthing)
8018 :
8019 : {
8020 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
8021 :
8022 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
8023 0 : dfFalseNorthing);
8024 : }
8025 :
8026 : /************************************************************************/
8027 : /* SetSinusoidal() */
8028 : /************************************************************************/
8029 :
8030 37 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
8031 : double dfFalseEasting,
8032 : double dfFalseNorthing)
8033 :
8034 : {
8035 74 : TAKE_OPTIONAL_LOCK();
8036 :
8037 37 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
8038 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
8039 74 : nullptr, 0, nullptr, 0));
8040 : }
8041 :
8042 : /************************************************************************/
8043 : /* OSRSetSinusoidal() */
8044 : /************************************************************************/
8045 :
8046 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
8047 : double dfFalseEasting, double dfFalseNorthing)
8048 :
8049 : {
8050 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
8051 :
8052 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
8053 1 : dfFalseNorthing);
8054 : }
8055 :
8056 : /************************************************************************/
8057 : /* SetStereographic() */
8058 : /************************************************************************/
8059 :
8060 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
8061 : double dfCMeridian, double dfScale,
8062 : double dfFalseEasting,
8063 : double dfFalseNorthing)
8064 :
8065 : {
8066 4 : TAKE_OPTIONAL_LOCK();
8067 :
8068 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
8069 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
8070 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8071 : }
8072 :
8073 : /************************************************************************/
8074 : /* OSRSetStereographic() */
8075 : /************************************************************************/
8076 :
8077 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8078 : double dfCMeridian, double dfScale,
8079 : double dfFalseEasting, double dfFalseNorthing)
8080 :
8081 : {
8082 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8083 :
8084 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8085 0 : dfFalseEasting, dfFalseNorthing);
8086 : }
8087 :
8088 : /************************************************************************/
8089 : /* SetSOC() */
8090 : /* */
8091 : /* NOTE: This definition isn't really used in practice any more */
8092 : /* and should be considered deprecated. It seems that swiss */
8093 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
8094 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
8095 : /* EPSG:2056 and Bug 423. */
8096 : /************************************************************************/
8097 :
8098 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8099 : double dfCentralMeridian,
8100 : double dfFalseEasting,
8101 : double dfFalseNorthing)
8102 :
8103 : {
8104 4 : TAKE_OPTIONAL_LOCK();
8105 :
8106 2 : return d->replaceConversionAndUnref(
8107 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8108 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8109 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8110 4 : 0.0));
8111 : #if 0
8112 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8113 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8114 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8115 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8116 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8117 :
8118 : return OGRERR_NONE;
8119 : #endif
8120 : }
8121 :
8122 : /************************************************************************/
8123 : /* OSRSetSOC() */
8124 : /************************************************************************/
8125 :
8126 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8127 : double dfCentralMeridian, double dfFalseEasting,
8128 : double dfFalseNorthing)
8129 :
8130 : {
8131 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8132 :
8133 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8134 0 : dfFalseEasting, dfFalseNorthing);
8135 : }
8136 :
8137 : /************************************************************************/
8138 : /* SetVDG() */
8139 : /************************************************************************/
8140 :
8141 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8142 : double dfFalseNorthing)
8143 :
8144 : {
8145 4 : TAKE_OPTIONAL_LOCK();
8146 :
8147 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8148 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8149 4 : nullptr, 0, nullptr, 0));
8150 : }
8151 :
8152 : /************************************************************************/
8153 : /* OSRSetVDG() */
8154 : /************************************************************************/
8155 :
8156 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8157 : double dfFalseEasting, double dfFalseNorthing)
8158 :
8159 : {
8160 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8161 :
8162 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8163 0 : dfFalseNorthing);
8164 : }
8165 :
8166 : /************************************************************************/
8167 : /* SetUTM() */
8168 : /************************************************************************/
8169 :
8170 : /**
8171 : * \brief Set UTM projection definition.
8172 : *
8173 : * This will generate a projection definition with the full set of
8174 : * transverse mercator projection parameters for the given UTM zone.
8175 : * If no PROJCS[] description is set yet, one will be set to look
8176 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8177 : *
8178 : * This method is the same as the C function OSRSetUTM().
8179 : *
8180 : * @param nZone UTM zone.
8181 : *
8182 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8183 : * hemisphere.
8184 : *
8185 : * @return OGRERR_NONE on success.
8186 : */
8187 :
8188 313 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8189 :
8190 : {
8191 626 : TAKE_OPTIONAL_LOCK();
8192 :
8193 313 : if (nZone < 0 || nZone > 60)
8194 : {
8195 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8196 0 : return OGRERR_FAILURE;
8197 : }
8198 :
8199 313 : return d->replaceConversionAndUnref(
8200 313 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8201 : }
8202 :
8203 : /************************************************************************/
8204 : /* OSRSetUTM() */
8205 : /************************************************************************/
8206 :
8207 : /**
8208 : * \brief Set UTM projection definition.
8209 : *
8210 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8211 : */
8212 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8213 :
8214 : {
8215 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8216 :
8217 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8218 : }
8219 :
8220 : /************************************************************************/
8221 : /* GetUTMZone() */
8222 : /* */
8223 : /* Returns zero if it isn't UTM. */
8224 : /************************************************************************/
8225 :
8226 : /**
8227 : * \brief Get utm zone information.
8228 : *
8229 : * This is the same as the C function OSRGetUTMZone().
8230 : *
8231 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8232 : * zone which is negative in the southern hemisphere instead of having the
8233 : * pbNorth flag used in the C and C++ interface.
8234 : *
8235 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8236 : * FALSE if southern.
8237 : *
8238 : * @return UTM zone number or zero if this isn't a UTM definition.
8239 : */
8240 :
8241 611 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8242 :
8243 : {
8244 1222 : TAKE_OPTIONAL_LOCK();
8245 :
8246 611 : if (IsProjected() && GetAxesCount() == 3)
8247 : {
8248 1 : OGRSpatialReference *poSRSTmp = Clone();
8249 1 : poSRSTmp->DemoteTo2D(nullptr);
8250 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8251 1 : delete poSRSTmp;
8252 1 : return nZone;
8253 : }
8254 :
8255 610 : const char *pszProjection = GetAttrValue("PROJECTION");
8256 :
8257 610 : if (pszProjection == nullptr ||
8258 530 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8259 278 : return 0;
8260 :
8261 332 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8262 5 : return 0;
8263 :
8264 327 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8265 15 : return 0;
8266 :
8267 312 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8268 6 : return 0;
8269 :
8270 306 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8271 :
8272 306 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8273 0 : return 0;
8274 :
8275 306 : if (pbNorth != nullptr)
8276 240 : *pbNorth = (dfFalseNorthing == 0);
8277 :
8278 : const double dfCentralMeridian =
8279 306 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8280 306 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8281 :
8282 612 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8283 918 : std::isnan(dfZone) ||
8284 306 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8285 0 : return 0;
8286 :
8287 306 : return static_cast<int>(dfZone);
8288 : }
8289 :
8290 : /************************************************************************/
8291 : /* OSRGetUTMZone() */
8292 : /************************************************************************/
8293 :
8294 : /**
8295 : * \brief Get utm zone information.
8296 : *
8297 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8298 : */
8299 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8300 :
8301 : {
8302 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8303 :
8304 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8305 : }
8306 :
8307 : /************************************************************************/
8308 : /* SetWagner() */
8309 : /************************************************************************/
8310 :
8311 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8312 : double dfCenterLat, double dfFalseEasting,
8313 : double dfFalseNorthing)
8314 :
8315 : {
8316 0 : TAKE_OPTIONAL_LOCK();
8317 :
8318 : PJ *conv;
8319 0 : if (nVariation == 1)
8320 : {
8321 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8322 : dfFalseEasting, dfFalseNorthing,
8323 : nullptr, 0.0, nullptr, 0.0);
8324 : }
8325 0 : else if (nVariation == 2)
8326 : {
8327 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8328 : dfFalseEasting, dfFalseNorthing,
8329 : nullptr, 0.0, nullptr, 0.0);
8330 : }
8331 0 : else if (nVariation == 3)
8332 : {
8333 0 : conv = proj_create_conversion_wagner_iii(
8334 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8335 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8336 : }
8337 0 : else if (nVariation == 4)
8338 : {
8339 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8340 : dfFalseEasting, dfFalseNorthing,
8341 : nullptr, 0.0, nullptr, 0.0);
8342 : }
8343 0 : else if (nVariation == 5)
8344 : {
8345 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8346 : dfFalseEasting, dfFalseNorthing,
8347 : nullptr, 0.0, nullptr, 0.0);
8348 : }
8349 0 : else if (nVariation == 6)
8350 : {
8351 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8352 : dfFalseEasting, dfFalseNorthing,
8353 : nullptr, 0.0, nullptr, 0.0);
8354 : }
8355 0 : else if (nVariation == 7)
8356 : {
8357 0 : conv = proj_create_conversion_wagner_vii(
8358 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8359 : 0.0, nullptr, 0.0);
8360 : }
8361 : else
8362 : {
8363 0 : CPLError(CE_Failure, CPLE_AppDefined,
8364 : "Unsupported Wagner variation (%d).", nVariation);
8365 0 : return OGRERR_UNSUPPORTED_SRS;
8366 : }
8367 :
8368 0 : return d->replaceConversionAndUnref(conv);
8369 : }
8370 :
8371 : /************************************************************************/
8372 : /* OSRSetWagner() */
8373 : /************************************************************************/
8374 :
8375 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8376 : double dfCenterLat, double dfFalseEasting,
8377 : double dfFalseNorthing)
8378 :
8379 : {
8380 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8381 :
8382 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8383 0 : dfFalseNorthing);
8384 : }
8385 :
8386 : /************************************************************************/
8387 : /* SetQSC() */
8388 : /************************************************************************/
8389 :
8390 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8391 : {
8392 0 : TAKE_OPTIONAL_LOCK();
8393 :
8394 0 : return d->replaceConversionAndUnref(
8395 : proj_create_conversion_quadrilateralized_spherical_cube(
8396 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8397 0 : 0, nullptr, 0));
8398 : }
8399 :
8400 : /************************************************************************/
8401 : /* OSRSetQSC() */
8402 : /************************************************************************/
8403 :
8404 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8405 : double dfCenterLong)
8406 :
8407 : {
8408 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8409 :
8410 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8411 : }
8412 :
8413 : /************************************************************************/
8414 : /* SetSCH() */
8415 : /************************************************************************/
8416 :
8417 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8418 : double dfPegHeading, double dfPegHgt)
8419 :
8420 : {
8421 0 : TAKE_OPTIONAL_LOCK();
8422 :
8423 0 : return d->replaceConversionAndUnref(
8424 : proj_create_conversion_spherical_cross_track_height(
8425 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8426 0 : nullptr, 0, nullptr, 0));
8427 : }
8428 :
8429 : /************************************************************************/
8430 : /* OSRSetSCH() */
8431 : /************************************************************************/
8432 :
8433 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8434 : double dfPegHeading, double dfPegHgt)
8435 :
8436 : {
8437 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8438 :
8439 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8440 : }
8441 :
8442 : /************************************************************************/
8443 : /* SetVerticalPerspective() */
8444 : /************************************************************************/
8445 :
8446 4 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8447 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8448 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8449 : {
8450 8 : TAKE_OPTIONAL_LOCK();
8451 :
8452 4 : return d->replaceConversionAndUnref(
8453 : proj_create_conversion_vertical_perspective(
8454 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8455 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8456 8 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8457 : }
8458 :
8459 : /************************************************************************/
8460 : /* OSRSetVerticalPerspective() */
8461 : /************************************************************************/
8462 :
8463 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8464 : double dfTopoOriginLat, double dfTopoOriginLon,
8465 : double dfTopoOriginHeight,
8466 : double dfViewPointHeight,
8467 : double dfFalseEasting, double dfFalseNorthing)
8468 :
8469 : {
8470 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8471 :
8472 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8473 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8474 1 : dfFalseEasting, dfFalseNorthing);
8475 : }
8476 :
8477 : /************************************************************************/
8478 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8479 : /************************************************************************/
8480 :
8481 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8482 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8483 : double dfAxisRotation)
8484 : {
8485 4 : TAKE_OPTIONAL_LOCK();
8486 :
8487 2 : d->refreshProjObj();
8488 2 : if (!d->m_pj_crs)
8489 0 : return OGRERR_FAILURE;
8490 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8491 0 : return OGRERR_FAILURE;
8492 2 : auto ctxt = d->getPROJContext();
8493 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8494 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8495 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8496 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8497 2 : d->m_pj_crs, conv, cs));
8498 2 : proj_destroy(conv);
8499 2 : proj_destroy(cs);
8500 2 : return OGRERR_NONE;
8501 : }
8502 :
8503 : /************************************************************************/
8504 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8505 : /************************************************************************/
8506 :
8507 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8508 : const char *pszCRSName, double dfGridNorthPoleLat,
8509 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8510 : {
8511 3 : TAKE_OPTIONAL_LOCK();
8512 :
8513 : #if PROJ_VERSION_MAJOR > 8 || \
8514 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8515 : d->refreshProjObj();
8516 : if (!d->m_pj_crs)
8517 : return OGRERR_FAILURE;
8518 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8519 : return OGRERR_FAILURE;
8520 : auto ctxt = d->getPROJContext();
8521 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8522 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8523 : nullptr, 0);
8524 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8525 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8526 : d->m_pj_crs, conv, cs));
8527 : proj_destroy(conv);
8528 : proj_destroy(cs);
8529 : return OGRERR_NONE;
8530 : #else
8531 : (void)pszCRSName;
8532 3 : SetProjection("Rotated_pole");
8533 3 : SetExtension(
8534 : "PROJCS", "PROJ4",
8535 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8536 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8537 : "+to_meter=0.0174532925199433 "
8538 : "+wktext",
8539 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8540 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8541 : GetSemiMinor(nullptr)));
8542 6 : return OGRERR_NONE;
8543 : #endif
8544 : }
8545 :
8546 : /************************************************************************/
8547 : /* SetAuthority() */
8548 : /************************************************************************/
8549 :
8550 : /**
8551 : * \brief Set the authority for a node.
8552 : *
8553 : * This method is the same as the C function OSRSetAuthority().
8554 : *
8555 : * @param pszTargetKey the partial or complete path to the node to
8556 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8557 : *
8558 : * @param pszAuthority authority name, such as "EPSG".
8559 : *
8560 : * @param nCode code for value with this authority.
8561 : *
8562 : * @return OGRERR_NONE on success.
8563 : */
8564 :
8565 12924 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8566 : const char *pszAuthority, int nCode)
8567 :
8568 : {
8569 25848 : TAKE_OPTIONAL_LOCK();
8570 :
8571 12924 : d->refreshProjObj();
8572 12924 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8573 :
8574 12924 : if (pszTargetKey == nullptr)
8575 : {
8576 265 : if (!d->m_pj_crs)
8577 0 : return OGRERR_FAILURE;
8578 265 : CPLString osCode;
8579 265 : osCode.Printf("%d", nCode);
8580 265 : d->demoteFromBoundCRS();
8581 265 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8582 : pszAuthority, osCode.c_str()));
8583 265 : d->undoDemoteFromBoundCRS();
8584 265 : return OGRERR_NONE;
8585 : }
8586 :
8587 12659 : d->demoteFromBoundCRS();
8588 12659 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8589 : {
8590 4186 : CPLString osCode;
8591 4186 : osCode.Printf("%d", nCode);
8592 : auto newGeogCRS =
8593 4186 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8594 : pszAuthority, osCode.c_str());
8595 :
8596 : auto conv =
8597 4186 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8598 :
8599 4186 : auto projCRS = proj_create_projected_crs(
8600 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8601 4186 : d->getProjCRSCoordSys());
8602 :
8603 : // Preserve existing id on the PROJCRS
8604 4186 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8605 4186 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8606 4186 : if (pszProjCRSAuthName && pszProjCRSCode)
8607 : {
8608 : auto projCRSWithId =
8609 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8610 : pszProjCRSCode);
8611 0 : proj_destroy(projCRS);
8612 0 : projCRS = projCRSWithId;
8613 : }
8614 :
8615 4186 : proj_destroy(newGeogCRS);
8616 4186 : proj_destroy(conv);
8617 :
8618 4186 : d->setPjCRS(projCRS);
8619 4186 : d->undoDemoteFromBoundCRS();
8620 4186 : return OGRERR_NONE;
8621 : }
8622 8473 : d->undoDemoteFromBoundCRS();
8623 :
8624 : /* -------------------------------------------------------------------- */
8625 : /* Find the node below which the authority should be put. */
8626 : /* -------------------------------------------------------------------- */
8627 8473 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8628 :
8629 8473 : if (poNode == nullptr)
8630 0 : return OGRERR_FAILURE;
8631 :
8632 : /* -------------------------------------------------------------------- */
8633 : /* If there is an existing AUTHORITY child blow it away before */
8634 : /* trying to set a new one. */
8635 : /* -------------------------------------------------------------------- */
8636 8473 : int iOldChild = poNode->FindChild("AUTHORITY");
8637 8473 : if (iOldChild != -1)
8638 5 : poNode->DestroyChild(iOldChild);
8639 :
8640 : /* -------------------------------------------------------------------- */
8641 : /* Create a new authority node. */
8642 : /* -------------------------------------------------------------------- */
8643 8473 : char szCode[32] = {};
8644 :
8645 8473 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8646 :
8647 8473 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8648 8473 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8649 8473 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8650 :
8651 8473 : poNode->AddChild(poAuthNode);
8652 :
8653 8473 : return OGRERR_NONE;
8654 : }
8655 :
8656 : /************************************************************************/
8657 : /* OSRSetAuthority() */
8658 : /************************************************************************/
8659 :
8660 : /**
8661 : * \brief Set the authority for a node.
8662 : *
8663 : * This function is the same as OGRSpatialReference::SetAuthority().
8664 : */
8665 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8666 : const char *pszAuthority, int nCode)
8667 :
8668 : {
8669 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8670 :
8671 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8672 : }
8673 :
8674 : /************************************************************************/
8675 : /* GetAuthorityCode() */
8676 : /************************************************************************/
8677 :
8678 : /**
8679 : * \brief Get the authority code for a node.
8680 : *
8681 : * This method is used to query an AUTHORITY[] node from within the
8682 : * WKT tree, and fetch the code value.
8683 : *
8684 : * While in theory values may be non-numeric, for the EPSG authority all
8685 : * code values should be integral.
8686 : *
8687 : * This method is the same as the C function OSRGetAuthorityCode().
8688 : *
8689 : * @param pszTargetKey the partial or complete path to the node to
8690 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8691 : * search for an authority node on the root element.
8692 : *
8693 : * @return value code from authority node, or NULL on failure. The value
8694 : * returned is internal and should not be freed or modified.
8695 : */
8696 :
8697 : const char *
8698 52437 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8699 :
8700 : {
8701 104874 : TAKE_OPTIONAL_LOCK();
8702 :
8703 52437 : d->refreshProjObj();
8704 52437 : const char *pszInputTargetKey = pszTargetKey;
8705 52437 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8706 52437 : if (pszTargetKey == nullptr)
8707 : {
8708 43902 : if (!d->m_pj_crs)
8709 : {
8710 17 : return nullptr;
8711 : }
8712 43885 : d->demoteFromBoundCRS();
8713 43885 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8714 43885 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8715 : {
8716 1387 : auto ctxt = d->getPROJContext();
8717 1387 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8718 1387 : if (cs)
8719 : {
8720 1387 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8721 1387 : proj_destroy(cs);
8722 1387 : if (axisCount == 3)
8723 : {
8724 : // This might come from a COMPD_CS with a VERT_DATUM type =
8725 : // 2002 in which case, using the WKT1 representation will
8726 : // enable us to recover the EPSG code.
8727 14 : pszTargetKey = pszInputTargetKey;
8728 : }
8729 : }
8730 : }
8731 43885 : d->undoDemoteFromBoundCRS();
8732 43885 : if (ret != nullptr || pszTargetKey == nullptr)
8733 : {
8734 43885 : return ret;
8735 : }
8736 : }
8737 :
8738 : // Special key for that context
8739 8539 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8740 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8741 : {
8742 4 : auto ctxt = d->getPROJContext();
8743 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8744 4 : if (crs)
8745 : {
8746 4 : const char *ret = proj_get_id_code(crs, 0);
8747 4 : if (ret)
8748 4 : ret = CPLSPrintf("%s", ret);
8749 4 : proj_destroy(crs);
8750 4 : return ret;
8751 : }
8752 : }
8753 8535 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8754 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8755 : {
8756 4 : auto ctxt = d->getPROJContext();
8757 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8758 4 : if (crs)
8759 : {
8760 4 : const char *ret = proj_get_id_code(crs, 0);
8761 4 : if (ret)
8762 4 : ret = CPLSPrintf("%s", ret);
8763 4 : proj_destroy(crs);
8764 4 : return ret;
8765 : }
8766 : }
8767 :
8768 : /* -------------------------------------------------------------------- */
8769 : /* Find the node below which the authority should be put. */
8770 : /* -------------------------------------------------------------------- */
8771 8527 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8772 :
8773 8527 : if (poNode == nullptr)
8774 106 : return nullptr;
8775 :
8776 : /* -------------------------------------------------------------------- */
8777 : /* Fetch AUTHORITY child if there is one. */
8778 : /* -------------------------------------------------------------------- */
8779 8421 : if (poNode->FindChild("AUTHORITY") == -1)
8780 197 : return nullptr;
8781 :
8782 8224 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8783 :
8784 : /* -------------------------------------------------------------------- */
8785 : /* Create a new authority node. */
8786 : /* -------------------------------------------------------------------- */
8787 8224 : if (poNode->GetChildCount() < 2)
8788 0 : return nullptr;
8789 :
8790 8224 : return poNode->GetChild(1)->GetValue();
8791 : }
8792 :
8793 : /************************************************************************/
8794 : /* OSRGetAuthorityCode() */
8795 : /************************************************************************/
8796 :
8797 : /**
8798 : * \brief Get the authority code for a node.
8799 : *
8800 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8801 : */
8802 830 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8803 : const char *pszTargetKey)
8804 :
8805 : {
8806 830 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8807 :
8808 830 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8809 : }
8810 :
8811 : /************************************************************************/
8812 : /* GetAuthorityName() */
8813 : /************************************************************************/
8814 :
8815 : /**
8816 : * \brief Get the authority name for a node.
8817 : *
8818 : * This method is used to query an AUTHORITY[] node from within the
8819 : * WKT tree, and fetch the authority name value.
8820 : *
8821 : * The most common authority is "EPSG".
8822 : *
8823 : * This method is the same as the C function OSRGetAuthorityName().
8824 : *
8825 : * @param pszTargetKey the partial or complete path to the node to
8826 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8827 : * search for an authority node on the root element.
8828 : *
8829 : * @return value code from authority node, or NULL on failure. The value
8830 : * returned is internal and should not be freed or modified.
8831 : */
8832 :
8833 : const char *
8834 66491 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8835 :
8836 : {
8837 132982 : TAKE_OPTIONAL_LOCK();
8838 :
8839 66491 : d->refreshProjObj();
8840 66491 : const char *pszInputTargetKey = pszTargetKey;
8841 66491 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8842 66491 : if (pszTargetKey == nullptr)
8843 : {
8844 36929 : if (!d->m_pj_crs)
8845 : {
8846 18 : return nullptr;
8847 : }
8848 36911 : d->demoteFromBoundCRS();
8849 36911 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8850 36911 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8851 : {
8852 1008 : auto ctxt = d->getPROJContext();
8853 1008 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8854 1008 : if (cs)
8855 : {
8856 1008 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8857 1008 : proj_destroy(cs);
8858 1008 : if (axisCount == 3)
8859 : {
8860 : // This might come from a COMPD_CS with a VERT_DATUM type =
8861 : // 2002 in which case, using the WKT1 representation will
8862 : // enable us to recover the EPSG code.
8863 14 : pszTargetKey = pszInputTargetKey;
8864 : }
8865 : }
8866 : }
8867 36911 : d->undoDemoteFromBoundCRS();
8868 36911 : if (ret != nullptr || pszTargetKey == nullptr)
8869 : {
8870 36911 : return ret;
8871 : }
8872 : }
8873 :
8874 : // Special key for that context
8875 29566 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8876 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8877 : {
8878 4 : auto ctxt = d->getPROJContext();
8879 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8880 4 : if (crs)
8881 : {
8882 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8883 4 : if (ret)
8884 4 : ret = CPLSPrintf("%s", ret);
8885 4 : proj_destroy(crs);
8886 4 : return ret;
8887 : }
8888 : }
8889 29562 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8890 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8891 : {
8892 4 : auto ctxt = d->getPROJContext();
8893 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8894 4 : if (crs)
8895 : {
8896 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8897 4 : if (ret)
8898 4 : ret = CPLSPrintf("%s", ret);
8899 4 : proj_destroy(crs);
8900 4 : return ret;
8901 : }
8902 : }
8903 :
8904 : /* -------------------------------------------------------------------- */
8905 : /* Find the node below which the authority should be put. */
8906 : /* -------------------------------------------------------------------- */
8907 29554 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8908 :
8909 29554 : if (poNode == nullptr)
8910 12140 : return nullptr;
8911 :
8912 : /* -------------------------------------------------------------------- */
8913 : /* Fetch AUTHORITY child if there is one. */
8914 : /* -------------------------------------------------------------------- */
8915 17414 : if (poNode->FindChild("AUTHORITY") == -1)
8916 1597 : return nullptr;
8917 :
8918 15817 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8919 :
8920 : /* -------------------------------------------------------------------- */
8921 : /* Create a new authority node. */
8922 : /* -------------------------------------------------------------------- */
8923 15817 : if (poNode->GetChildCount() < 2)
8924 0 : return nullptr;
8925 :
8926 15817 : return poNode->GetChild(0)->GetValue();
8927 : }
8928 :
8929 : /************************************************************************/
8930 : /* OSRGetAuthorityName() */
8931 : /************************************************************************/
8932 :
8933 : /**
8934 : * \brief Get the authority name for a node.
8935 : *
8936 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8937 : */
8938 94 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8939 : const char *pszTargetKey)
8940 :
8941 : {
8942 94 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8943 :
8944 94 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8945 : }
8946 :
8947 : /************************************************************************/
8948 : /* GetOGCURN() */
8949 : /************************************************************************/
8950 :
8951 : /**
8952 : * \brief Get a OGC URN string describing the CRS, when possible
8953 : *
8954 : * This method assumes that the CRS has a top-level identifier, or is
8955 : * a compound CRS whose horizontal and vertical parts have a top-level
8956 : * identifier.
8957 : *
8958 : * @return a string to free with CPLFree(), or nullptr when no result can be
8959 : * generated
8960 : *
8961 : * @since GDAL 3.5
8962 : */
8963 :
8964 68 : char *OGRSpatialReference::GetOGCURN() const
8965 :
8966 : {
8967 136 : TAKE_OPTIONAL_LOCK();
8968 :
8969 68 : const char *pszAuthName = GetAuthorityName();
8970 68 : const char *pszAuthCode = GetAuthorityCode();
8971 68 : if (pszAuthName && pszAuthCode)
8972 65 : return CPLStrdup(
8973 65 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8974 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8975 2 : return nullptr;
8976 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8977 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8978 1 : char *pszRet = nullptr;
8979 1 : if (horizCRS && vertCRS)
8980 : {
8981 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8982 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8983 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8984 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8985 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8986 : {
8987 1 : pszRet = CPLStrdup(CPLSPrintf(
8988 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8989 : horizAuthCode, vertAuthName, vertAuthCode));
8990 : }
8991 : }
8992 1 : proj_destroy(horizCRS);
8993 1 : proj_destroy(vertCRS);
8994 1 : return pszRet;
8995 : }
8996 :
8997 : /************************************************************************/
8998 : /* StripVertical() */
8999 : /************************************************************************/
9000 :
9001 : /**
9002 : * \brief Convert a compound cs into a horizontal CS.
9003 : *
9004 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
9005 : * nodes are stripped resulting and only the horizontal coordinate system
9006 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
9007 : *
9008 : * If this is not a compound coordinate system then nothing is changed.
9009 : *
9010 : * This method is the same as the C function OSRStripVertical().
9011 : *
9012 : */
9013 :
9014 47 : OGRErr OGRSpatialReference::StripVertical()
9015 :
9016 : {
9017 94 : TAKE_OPTIONAL_LOCK();
9018 :
9019 47 : d->refreshProjObj();
9020 47 : d->demoteFromBoundCRS();
9021 47 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
9022 : {
9023 0 : d->undoDemoteFromBoundCRS();
9024 0 : return OGRERR_NONE;
9025 : }
9026 47 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9027 47 : if (!horizCRS)
9028 : {
9029 0 : d->undoDemoteFromBoundCRS();
9030 0 : return OGRERR_FAILURE;
9031 : }
9032 :
9033 47 : bool reuseExistingBoundCRS = false;
9034 47 : if (d->m_pj_bound_crs_target)
9035 : {
9036 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
9037 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
9038 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9039 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9040 : }
9041 :
9042 47 : if (reuseExistingBoundCRS)
9043 : {
9044 4 : auto newBoundCRS = proj_crs_create_bound_crs(
9045 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
9046 4 : d->m_pj_bound_crs_co);
9047 4 : proj_destroy(horizCRS);
9048 4 : d->undoDemoteFromBoundCRS();
9049 4 : d->setPjCRS(newBoundCRS);
9050 : }
9051 : else
9052 : {
9053 43 : d->undoDemoteFromBoundCRS();
9054 43 : d->setPjCRS(horizCRS);
9055 : }
9056 :
9057 47 : return OGRERR_NONE;
9058 : }
9059 :
9060 : /************************************************************************/
9061 : /* OSRStripVertical() */
9062 : /************************************************************************/
9063 : /**
9064 : * \brief Convert a compound cs into a horizontal CS.
9065 : *
9066 : * This function is the same as the C++ method
9067 : * OGRSpatialReference::StripVertical().
9068 : */
9069 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
9070 :
9071 : {
9072 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9073 :
9074 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9075 : }
9076 :
9077 : /************************************************************************/
9078 : /* StripTOWGS84IfKnownDatumAndAllowed() */
9079 : /************************************************************************/
9080 :
9081 : /**
9082 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9083 : * and this is allowed by the user.
9084 : *
9085 : * The default behavior is to remove TOWGS84 information if the CRS has a
9086 : * known horizontal datum. This can be disabled by setting the
9087 : * OSR_STRIP_TOWGS84 configuration option to NO.
9088 : *
9089 : * @return true if TOWGS84 has been removed.
9090 : * @since OGR 3.1.0
9091 : */
9092 :
9093 9645 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9094 : {
9095 9645 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9096 : {
9097 9642 : if (StripTOWGS84IfKnownDatum())
9098 : {
9099 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9100 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9101 : "configuration option to NO");
9102 72 : return true;
9103 : }
9104 : }
9105 9573 : return false;
9106 : }
9107 :
9108 : /************************************************************************/
9109 : /* StripTOWGS84IfKnownDatum() */
9110 : /************************************************************************/
9111 :
9112 : /**
9113 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9114 : *
9115 : * @return true if TOWGS84 has been removed.
9116 : * @since OGR 3.1.0
9117 : */
9118 :
9119 9648 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9120 :
9121 : {
9122 19296 : TAKE_OPTIONAL_LOCK();
9123 :
9124 9648 : d->refreshProjObj();
9125 9648 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9126 : {
9127 9556 : return false;
9128 : }
9129 92 : auto ctxt = d->getPROJContext();
9130 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9131 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9132 : {
9133 3 : proj_destroy(baseCRS);
9134 3 : return false;
9135 : }
9136 :
9137 : // Known base CRS code ? Return base CRS
9138 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9139 89 : if (pszCode)
9140 : {
9141 2 : d->setPjCRS(baseCRS);
9142 2 : return true;
9143 : }
9144 :
9145 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9146 : #if PROJ_VERSION_MAJOR > 7 || \
9147 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9148 : if (datum == nullptr)
9149 : {
9150 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9151 : }
9152 : #endif
9153 87 : if (!datum)
9154 : {
9155 0 : proj_destroy(baseCRS);
9156 0 : return false;
9157 : }
9158 :
9159 : // Known datum code ? Return base CRS
9160 87 : pszCode = proj_get_id_code(datum, 0);
9161 87 : if (pszCode)
9162 : {
9163 3 : proj_destroy(datum);
9164 3 : d->setPjCRS(baseCRS);
9165 3 : return true;
9166 : }
9167 :
9168 84 : const char *name = proj_get_name(datum);
9169 84 : if (EQUAL(name, "unknown"))
9170 : {
9171 1 : proj_destroy(datum);
9172 1 : proj_destroy(baseCRS);
9173 1 : return false;
9174 : }
9175 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9176 : PJ_OBJ_LIST *list =
9177 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9178 :
9179 83 : bool knownDatumName = false;
9180 83 : if (list)
9181 : {
9182 83 : if (proj_list_get_count(list) == 1)
9183 : {
9184 70 : knownDatumName = true;
9185 : }
9186 83 : proj_list_destroy(list);
9187 : }
9188 :
9189 83 : proj_destroy(datum);
9190 83 : if (knownDatumName)
9191 : {
9192 70 : d->setPjCRS(baseCRS);
9193 70 : return true;
9194 : }
9195 13 : proj_destroy(baseCRS);
9196 13 : return false;
9197 : }
9198 :
9199 : /************************************************************************/
9200 : /* IsCompound() */
9201 : /************************************************************************/
9202 :
9203 : /**
9204 : * \brief Check if coordinate system is compound.
9205 : *
9206 : * This method is the same as the C function OSRIsCompound().
9207 : *
9208 : * @return TRUE if this is rooted with a COMPD_CS node.
9209 : */
9210 :
9211 43334 : int OGRSpatialReference::IsCompound() const
9212 :
9213 : {
9214 43334 : TAKE_OPTIONAL_LOCK();
9215 :
9216 43334 : d->refreshProjObj();
9217 43334 : d->demoteFromBoundCRS();
9218 43334 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9219 43334 : d->undoDemoteFromBoundCRS();
9220 86668 : return isCompound;
9221 : }
9222 :
9223 : /************************************************************************/
9224 : /* OSRIsCompound() */
9225 : /************************************************************************/
9226 :
9227 : /**
9228 : * \brief Check if the coordinate system is compound.
9229 : *
9230 : * This function is the same as OGRSpatialReference::IsCompound().
9231 : */
9232 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9233 :
9234 : {
9235 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9236 :
9237 5 : return ToPointer(hSRS)->IsCompound();
9238 : }
9239 :
9240 : /************************************************************************/
9241 : /* IsProjected() */
9242 : /************************************************************************/
9243 :
9244 : /**
9245 : * \brief Check if projected coordinate system.
9246 : *
9247 : * This method is the same as the C function OSRIsProjected().
9248 : *
9249 : * @return TRUE if this contains a PROJCS node indicating a it is a
9250 : * projected coordinate system. Also if it is a CompoundCRS made of a
9251 : * ProjectedCRS
9252 : */
9253 :
9254 46298 : int OGRSpatialReference::IsProjected() const
9255 :
9256 : {
9257 46298 : TAKE_OPTIONAL_LOCK();
9258 :
9259 46298 : d->refreshProjObj();
9260 46298 : d->demoteFromBoundCRS();
9261 46298 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9262 46298 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9263 : {
9264 : auto horizCRS =
9265 152 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9266 152 : if (horizCRS)
9267 : {
9268 152 : auto horizCRSType = proj_get_type(horizCRS);
9269 152 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9270 152 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9271 : {
9272 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9273 6 : if (base)
9274 : {
9275 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9276 6 : proj_destroy(base);
9277 : }
9278 : }
9279 152 : proj_destroy(horizCRS);
9280 : }
9281 : }
9282 46298 : d->undoDemoteFromBoundCRS();
9283 92596 : return isProjected;
9284 : }
9285 :
9286 : /************************************************************************/
9287 : /* OSRIsProjected() */
9288 : /************************************************************************/
9289 : /**
9290 : * \brief Check if projected coordinate system.
9291 : *
9292 : * This function is the same as OGRSpatialReference::IsProjected().
9293 : */
9294 451 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9295 :
9296 : {
9297 451 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9298 :
9299 451 : return ToPointer(hSRS)->IsProjected();
9300 : }
9301 :
9302 : /************************************************************************/
9303 : /* IsGeocentric() */
9304 : /************************************************************************/
9305 :
9306 : /**
9307 : * \brief Check if geocentric coordinate system.
9308 : *
9309 : * This method is the same as the C function OSRIsGeocentric().
9310 : *
9311 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9312 : * geocentric coordinate system.
9313 : *
9314 : */
9315 :
9316 18037 : int OGRSpatialReference::IsGeocentric() const
9317 :
9318 : {
9319 18037 : TAKE_OPTIONAL_LOCK();
9320 :
9321 18037 : d->refreshProjObj();
9322 18037 : d->demoteFromBoundCRS();
9323 18037 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9324 18037 : d->undoDemoteFromBoundCRS();
9325 36074 : return isGeocentric;
9326 : }
9327 :
9328 : /************************************************************************/
9329 : /* OSRIsGeocentric() */
9330 : /************************************************************************/
9331 : /**
9332 : * \brief Check if geocentric coordinate system.
9333 : *
9334 : * This function is the same as OGRSpatialReference::IsGeocentric().
9335 : *
9336 : */
9337 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9338 :
9339 : {
9340 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9341 :
9342 2 : return ToPointer(hSRS)->IsGeocentric();
9343 : }
9344 :
9345 : /************************************************************************/
9346 : /* IsEmpty() */
9347 : /************************************************************************/
9348 :
9349 : /**
9350 : * \brief Return if the SRS is not set.
9351 : */
9352 :
9353 120593 : bool OGRSpatialReference::IsEmpty() const
9354 : {
9355 120593 : TAKE_OPTIONAL_LOCK();
9356 :
9357 120593 : d->refreshProjObj();
9358 241186 : return d->m_pj_crs == nullptr;
9359 : }
9360 :
9361 : /************************************************************************/
9362 : /* IsGeographic() */
9363 : /************************************************************************/
9364 :
9365 : /**
9366 : * \brief Check if geographic coordinate system.
9367 : *
9368 : * This method is the same as the C function OSRIsGeographic().
9369 : *
9370 : * @return TRUE if this spatial reference is geographic ... that is the
9371 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9372 : * GeographicCRS
9373 : */
9374 :
9375 64161 : int OGRSpatialReference::IsGeographic() const
9376 :
9377 : {
9378 64161 : TAKE_OPTIONAL_LOCK();
9379 :
9380 64161 : d->refreshProjObj();
9381 64161 : d->demoteFromBoundCRS();
9382 89911 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9383 25750 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9384 64161 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9385 : {
9386 : auto horizCRS =
9387 329 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9388 329 : if (horizCRS)
9389 : {
9390 329 : auto horizCRSType = proj_get_type(horizCRS);
9391 329 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9392 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9393 329 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9394 : {
9395 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9396 13 : if (base)
9397 : {
9398 13 : horizCRSType = proj_get_type(base);
9399 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9400 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9401 13 : proj_destroy(base);
9402 : }
9403 : }
9404 329 : proj_destroy(horizCRS);
9405 : }
9406 : }
9407 64161 : d->undoDemoteFromBoundCRS();
9408 128322 : return isGeog;
9409 : }
9410 :
9411 : /************************************************************************/
9412 : /* OSRIsGeographic() */
9413 : /************************************************************************/
9414 : /**
9415 : * \brief Check if geographic coordinate system.
9416 : *
9417 : * This function is the same as OGRSpatialReference::IsGeographic().
9418 : */
9419 284 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9420 :
9421 : {
9422 284 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9423 :
9424 284 : return ToPointer(hSRS)->IsGeographic();
9425 : }
9426 :
9427 : /************************************************************************/
9428 : /* IsDerivedGeographic() */
9429 : /************************************************************************/
9430 :
9431 : /**
9432 : * \brief Check if the CRS is a derived geographic coordinate system.
9433 : * (for example a rotated long/lat grid)
9434 : *
9435 : * This method is the same as the C function OSRIsDerivedGeographic().
9436 : *
9437 : * @since GDAL 3.1.0 and PROJ 6.3.0
9438 : */
9439 :
9440 15731 : int OGRSpatialReference::IsDerivedGeographic() const
9441 :
9442 : {
9443 15731 : TAKE_OPTIONAL_LOCK();
9444 :
9445 15731 : d->refreshProjObj();
9446 15731 : d->demoteFromBoundCRS();
9447 25762 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9448 10031 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9449 : const bool isDerivedGeographic =
9450 15731 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9451 15731 : d->undoDemoteFromBoundCRS();
9452 31462 : return isDerivedGeographic ? TRUE : FALSE;
9453 : }
9454 :
9455 : /************************************************************************/
9456 : /* OSRIsDerivedGeographic() */
9457 : /************************************************************************/
9458 : /**
9459 : * \brief Check if the CRS is a derived geographic coordinate system.
9460 : * (for example a rotated long/lat grid)
9461 : *
9462 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9463 : */
9464 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9465 :
9466 : {
9467 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9468 :
9469 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9470 : }
9471 :
9472 : /************************************************************************/
9473 : /* IsDerivedProjected() */
9474 : /************************************************************************/
9475 :
9476 : /**
9477 : * \brief Check if the CRS is a derived projected coordinate system.
9478 : *
9479 : * This method is the same as the C function OSRIsDerivedGeographic().
9480 : *
9481 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9482 : */
9483 :
9484 0 : int OGRSpatialReference::IsDerivedProjected() const
9485 :
9486 : {
9487 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9488 : TAKE_OPTIONAL_LOCK();
9489 : d->refreshProjObj();
9490 : d->demoteFromBoundCRS();
9491 : const bool isDerivedProjected =
9492 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9493 : d->undoDemoteFromBoundCRS();
9494 : return isDerivedProjected ? TRUE : FALSE;
9495 : #else
9496 0 : return FALSE;
9497 : #endif
9498 : }
9499 :
9500 : /************************************************************************/
9501 : /* OSRIsDerivedProjected() */
9502 : /************************************************************************/
9503 : /**
9504 : * \brief Check if the CRS is a derived projected coordinate system.
9505 : *
9506 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9507 : *
9508 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9509 : */
9510 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9511 :
9512 : {
9513 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9514 :
9515 0 : return ToPointer(hSRS)->IsDerivedProjected();
9516 : }
9517 :
9518 : /************************************************************************/
9519 : /* IsLocal() */
9520 : /************************************************************************/
9521 :
9522 : /**
9523 : * \brief Check if local coordinate system.
9524 : *
9525 : * This method is the same as the C function OSRIsLocal().
9526 : *
9527 : * @return TRUE if this spatial reference is local ... that is the
9528 : * root is a LOCAL_CS node.
9529 : */
9530 :
9531 8317 : int OGRSpatialReference::IsLocal() const
9532 :
9533 : {
9534 8317 : TAKE_OPTIONAL_LOCK();
9535 8317 : d->refreshProjObj();
9536 16634 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9537 : }
9538 :
9539 : /************************************************************************/
9540 : /* OSRIsLocal() */
9541 : /************************************************************************/
9542 : /**
9543 : * \brief Check if local coordinate system.
9544 : *
9545 : * This function is the same as OGRSpatialReference::IsLocal().
9546 : */
9547 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9548 :
9549 : {
9550 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9551 :
9552 8 : return ToPointer(hSRS)->IsLocal();
9553 : }
9554 :
9555 : /************************************************************************/
9556 : /* IsVertical() */
9557 : /************************************************************************/
9558 :
9559 : /**
9560 : * \brief Check if vertical coordinate system.
9561 : *
9562 : * This method is the same as the C function OSRIsVertical().
9563 : *
9564 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9565 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9566 : * VerticalCRS
9567 : *
9568 : */
9569 :
9570 9336 : int OGRSpatialReference::IsVertical() const
9571 :
9572 : {
9573 9336 : TAKE_OPTIONAL_LOCK();
9574 9336 : d->refreshProjObj();
9575 9336 : d->demoteFromBoundCRS();
9576 9336 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9577 9336 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9578 : {
9579 : auto vertCRS =
9580 34 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9581 34 : if (vertCRS)
9582 : {
9583 34 : const auto vertCRSType = proj_get_type(vertCRS);
9584 34 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9585 34 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9586 : {
9587 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9588 0 : if (base)
9589 : {
9590 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9591 0 : proj_destroy(base);
9592 : }
9593 : }
9594 34 : proj_destroy(vertCRS);
9595 : }
9596 : }
9597 9336 : d->undoDemoteFromBoundCRS();
9598 18672 : return isVertical;
9599 : }
9600 :
9601 : /************************************************************************/
9602 : /* OSRIsVertical() */
9603 : /************************************************************************/
9604 : /**
9605 : * \brief Check if vertical coordinate system.
9606 : *
9607 : * This function is the same as OGRSpatialReference::IsVertical().
9608 : *
9609 : */
9610 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9611 :
9612 : {
9613 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9614 :
9615 0 : return ToPointer(hSRS)->IsVertical();
9616 : }
9617 :
9618 : /************************************************************************/
9619 : /* IsDynamic() */
9620 : /************************************************************************/
9621 :
9622 : /**
9623 : * \brief Check if a CRS is a dynamic CRS.
9624 : *
9625 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9626 : * plate-fixed.
9627 : *
9628 : * This method is the same as the C function OSRIsDynamic().
9629 : *
9630 : * @return true if the CRS is dynamic
9631 : *
9632 : * @since OGR 3.4.0
9633 : *
9634 : * @see HasPointMotionOperation()
9635 : */
9636 :
9637 17208 : bool OGRSpatialReference::IsDynamic() const
9638 :
9639 : {
9640 17208 : TAKE_OPTIONAL_LOCK();
9641 17208 : bool isDynamic = false;
9642 17208 : d->refreshProjObj();
9643 17208 : d->demoteFromBoundCRS();
9644 17208 : auto ctxt = d->getPROJContext();
9645 17208 : PJ *horiz = nullptr;
9646 17208 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9647 : {
9648 99 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9649 : }
9650 17109 : else if (d->m_pj_crs)
9651 : {
9652 16915 : horiz = proj_clone(ctxt, d->m_pj_crs);
9653 : }
9654 17208 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9655 : {
9656 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9657 6 : if (baseCRS)
9658 : {
9659 6 : proj_destroy(horiz);
9660 6 : horiz = baseCRS;
9661 : }
9662 : }
9663 17208 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9664 17208 : if (datum)
9665 : {
9666 16979 : const auto type = proj_get_type(datum);
9667 16979 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9668 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9669 16979 : if (!isDynamic)
9670 : {
9671 16979 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9672 16979 : const char *code = proj_get_id_code(datum, 0);
9673 16979 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9674 16447 : EQUAL(code, "6326"))
9675 : {
9676 10716 : isDynamic = true;
9677 : }
9678 : }
9679 16979 : proj_destroy(datum);
9680 : }
9681 : #if PROJ_VERSION_MAJOR > 7 || \
9682 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9683 : else
9684 : {
9685 : auto ensemble =
9686 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9687 : if (ensemble)
9688 : {
9689 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9690 : if (member)
9691 : {
9692 : const auto type = proj_get_type(member);
9693 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9694 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9695 : proj_destroy(member);
9696 : }
9697 : proj_destroy(ensemble);
9698 : }
9699 : }
9700 : #endif
9701 17208 : proj_destroy(horiz);
9702 17208 : d->undoDemoteFromBoundCRS();
9703 34416 : return isDynamic;
9704 : }
9705 :
9706 : /************************************************************************/
9707 : /* OSRIsDynamic() */
9708 : /************************************************************************/
9709 : /**
9710 : * \brief Check if a CRS is a dynamic CRS.
9711 : *
9712 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9713 : * plate-fixed.
9714 : *
9715 : * This function is the same as OGRSpatialReference::IsDynamic().
9716 : *
9717 : * @since OGR 3.4.0
9718 : */
9719 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9720 :
9721 : {
9722 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9723 :
9724 0 : return ToPointer(hSRS)->IsDynamic();
9725 : }
9726 :
9727 : /************************************************************************/
9728 : /* HasPointMotionOperation() */
9729 : /************************************************************************/
9730 :
9731 : /**
9732 : * \brief Check if a CRS has at least an associated point motion operation.
9733 : *
9734 : * Some CRS are not formally declared as dynamic, but may behave as such
9735 : * in practice due to the presence of point motion operation, to perform
9736 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9737 : *
9738 : * @return true if the CRS has at least an associated point motion operation.
9739 : *
9740 : * @since OGR 3.8.0 and PROJ 9.4.0
9741 : *
9742 : * @see IsDynamic()
9743 : */
9744 :
9745 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9746 :
9747 : {
9748 : #if PROJ_VERSION_MAJOR > 9 || \
9749 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9750 : TAKE_OPTIONAL_LOCK();
9751 : d->refreshProjObj();
9752 : d->demoteFromBoundCRS();
9753 : auto ctxt = d->getPROJContext();
9754 : auto res =
9755 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9756 : d->undoDemoteFromBoundCRS();
9757 : return res;
9758 : #else
9759 5 : return false;
9760 : #endif
9761 : }
9762 :
9763 : /************************************************************************/
9764 : /* OSRHasPointMotionOperation() */
9765 : /************************************************************************/
9766 :
9767 : /**
9768 : * \brief Check if a CRS has at least an associated point motion operation.
9769 : *
9770 : * Some CRS are not formally declared as dynamic, but may behave as such
9771 : * in practice due to the presence of point motion operation, to perform
9772 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9773 : *
9774 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9775 : *
9776 : * @since OGR 3.8.0 and PROJ 9.4.0
9777 : */
9778 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9779 :
9780 : {
9781 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9782 :
9783 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9784 : }
9785 :
9786 : /************************************************************************/
9787 : /* CloneGeogCS() */
9788 : /************************************************************************/
9789 :
9790 : /**
9791 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9792 : * object.
9793 : *
9794 : * @return a new SRS, which becomes the responsibility of the caller.
9795 : */
9796 4648 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9797 :
9798 : {
9799 9296 : TAKE_OPTIONAL_LOCK();
9800 4648 : d->refreshProjObj();
9801 4648 : if (d->m_pj_crs)
9802 : {
9803 4648 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9804 0 : return nullptr;
9805 :
9806 : auto geodCRS =
9807 4648 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9808 4648 : if (geodCRS)
9809 : {
9810 4648 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9811 4648 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9812 : {
9813 : PJ *hub_crs =
9814 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9815 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9816 13 : d->m_pj_crs);
9817 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9818 : geodCRS, hub_crs, co);
9819 13 : proj_destroy(geodCRS);
9820 13 : geodCRS = temp;
9821 13 : proj_destroy(hub_crs);
9822 13 : proj_destroy(co);
9823 : }
9824 :
9825 : /* --------------------------------------------------------------------
9826 : */
9827 : /* We have to reconstruct the GEOGCS node for geocentric */
9828 : /* coordinate systems. */
9829 : /* --------------------------------------------------------------------
9830 : */
9831 4648 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9832 : {
9833 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9834 : #if PROJ_VERSION_MAJOR > 7 || \
9835 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9836 : if (datum == nullptr)
9837 : {
9838 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9839 : geodCRS);
9840 : }
9841 : #endif
9842 0 : if (datum)
9843 : {
9844 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9845 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9846 : nullptr, 0);
9847 0 : auto temp = proj_create_geographic_crs_from_datum(
9848 : d->getPROJContext(), "unnamed", datum, cs);
9849 0 : proj_destroy(datum);
9850 0 : proj_destroy(cs);
9851 0 : proj_destroy(geodCRS);
9852 0 : geodCRS = temp;
9853 : }
9854 : }
9855 :
9856 4648 : poNewSRS->d->setPjCRS(geodCRS);
9857 4648 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9858 3048 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9859 4648 : return poNewSRS;
9860 : }
9861 : }
9862 0 : return nullptr;
9863 : }
9864 :
9865 : /************************************************************************/
9866 : /* OSRCloneGeogCS() */
9867 : /************************************************************************/
9868 : /**
9869 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9870 : * object.
9871 : *
9872 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9873 : */
9874 127 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9875 :
9876 : {
9877 127 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9878 :
9879 127 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9880 : }
9881 :
9882 : /************************************************************************/
9883 : /* IsSameGeogCS() */
9884 : /************************************************************************/
9885 :
9886 : /**
9887 : * \brief Do the GeogCS'es match?
9888 : *
9889 : * This method is the same as the C function OSRIsSameGeogCS().
9890 : *
9891 : * @param poOther the SRS being compared against.
9892 : *
9893 : * @return TRUE if they are the same or FALSE otherwise.
9894 : */
9895 :
9896 8848 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9897 :
9898 : {
9899 8848 : return IsSameGeogCS(poOther, nullptr);
9900 : }
9901 :
9902 : /**
9903 : * \brief Do the GeogCS'es match?
9904 : *
9905 : * This method is the same as the C function OSRIsSameGeogCS().
9906 : *
9907 : * @param poOther the SRS being compared against.
9908 : * @param papszOptions options. ignored
9909 : *
9910 : * @return TRUE if they are the same or FALSE otherwise.
9911 : */
9912 :
9913 8848 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9914 : const char *const *papszOptions) const
9915 :
9916 : {
9917 17696 : TAKE_OPTIONAL_LOCK();
9918 :
9919 8848 : CPL_IGNORE_RET_VAL(papszOptions);
9920 :
9921 8848 : d->refreshProjObj();
9922 8848 : poOther->d->refreshProjObj();
9923 8848 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9924 0 : return FALSE;
9925 8848 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9926 8848 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9927 26544 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9928 8848 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9929 : {
9930 0 : return FALSE;
9931 : }
9932 :
9933 8848 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9934 : auto otherGeodCRS =
9935 8848 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9936 8848 : if (!geodCRS || !otherGeodCRS)
9937 : {
9938 0 : proj_destroy(geodCRS);
9939 0 : proj_destroy(otherGeodCRS);
9940 0 : return FALSE;
9941 : }
9942 :
9943 8848 : int ret = proj_is_equivalent_to(
9944 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9945 :
9946 8848 : proj_destroy(geodCRS);
9947 8848 : proj_destroy(otherGeodCRS);
9948 8848 : return ret;
9949 : }
9950 :
9951 : /************************************************************************/
9952 : /* OSRIsSameGeogCS() */
9953 : /************************************************************************/
9954 :
9955 : /**
9956 : * \brief Do the GeogCS'es match?
9957 : *
9958 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9959 : */
9960 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9961 :
9962 : {
9963 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9964 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9965 :
9966 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9967 : }
9968 :
9969 : /************************************************************************/
9970 : /* IsSameVertCS() */
9971 : /************************************************************************/
9972 :
9973 : /**
9974 : * \brief Do the VertCS'es match?
9975 : *
9976 : * This method is the same as the C function OSRIsSameVertCS().
9977 : *
9978 : * @param poOther the SRS being compared against.
9979 : *
9980 : * @return TRUE if they are the same or FALSE otherwise.
9981 : */
9982 :
9983 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9984 :
9985 : {
9986 0 : TAKE_OPTIONAL_LOCK();
9987 :
9988 : /* -------------------------------------------------------------------- */
9989 : /* Does the datum name match? */
9990 : /* -------------------------------------------------------------------- */
9991 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9992 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9993 :
9994 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9995 0 : !EQUAL(pszThisValue, pszOtherValue))
9996 0 : return FALSE;
9997 :
9998 : /* -------------------------------------------------------------------- */
9999 : /* Do the units match? */
10000 : /* -------------------------------------------------------------------- */
10001 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
10002 0 : if (pszThisValue == nullptr)
10003 0 : pszThisValue = "1.0";
10004 :
10005 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
10006 0 : if (pszOtherValue == nullptr)
10007 0 : pszOtherValue = "1.0";
10008 :
10009 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
10010 0 : return FALSE;
10011 :
10012 0 : return TRUE;
10013 : }
10014 :
10015 : /************************************************************************/
10016 : /* OSRIsSameVertCS() */
10017 : /************************************************************************/
10018 :
10019 : /**
10020 : * \brief Do the VertCS'es match?
10021 : *
10022 : * This function is the same as OGRSpatialReference::IsSameVertCS().
10023 : */
10024 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10025 :
10026 : {
10027 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
10028 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
10029 :
10030 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
10031 : }
10032 :
10033 : /************************************************************************/
10034 : /* IsSame() */
10035 : /************************************************************************/
10036 :
10037 : /**
10038 : * \brief Do these two spatial references describe the same system ?
10039 : *
10040 : * @param poOtherSRS the SRS being compared to.
10041 : *
10042 : * @return TRUE if equivalent or FALSE otherwise.
10043 : */
10044 :
10045 8929 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
10046 :
10047 : {
10048 8929 : return IsSame(poOtherSRS, nullptr);
10049 : }
10050 :
10051 : /**
10052 : * \brief Do these two spatial references describe the same system ?
10053 : *
10054 : * This also takes into account the data axis to CRS axis mapping by default
10055 : *
10056 : * @param poOtherSRS the SRS being compared to.
10057 : * @param papszOptions options. NULL or NULL terminated list of options.
10058 : * Currently supported options are:
10059 : * <ul>
10060 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
10061 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
10062 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
10063 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
10064 : * </ul>
10065 : *
10066 : * @return TRUE if equivalent or FALSE otherwise.
10067 : */
10068 :
10069 22850 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
10070 : const char *const *papszOptions) const
10071 :
10072 : {
10073 45700 : TAKE_OPTIONAL_LOCK();
10074 :
10075 22850 : d->refreshProjObj();
10076 22850 : poOtherSRS->d->refreshProjObj();
10077 22850 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10078 50 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10079 22800 : if (!CPLTestBool(CSLFetchNameValueDef(
10080 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10081 : {
10082 22042 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10083 3715 : return false;
10084 : }
10085 :
10086 19085 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10087 : "IGNORE_COORDINATE_EPOCH", "NO")))
10088 : {
10089 18593 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10090 27 : return false;
10091 : }
10092 :
10093 19058 : bool reboundSelf = false;
10094 19058 : bool reboundOther = false;
10095 19110 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10096 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10097 : {
10098 14 : d->demoteFromBoundCRS();
10099 14 : reboundSelf = true;
10100 : }
10101 38050 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10102 19006 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10103 : {
10104 28 : poOtherSRS->d->demoteFromBoundCRS();
10105 28 : reboundOther = true;
10106 : }
10107 :
10108 19058 : PJ_COMPARISON_CRITERION criterion =
10109 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10110 19058 : const char *pszCriterion = CSLFetchNameValueDef(
10111 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10112 19058 : if (EQUAL(pszCriterion, "STRICT"))
10113 0 : criterion = PJ_COMP_STRICT;
10114 19058 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10115 10376 : criterion = PJ_COMP_EQUIVALENT;
10116 8682 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10117 : {
10118 0 : CPLError(CE_Warning, CPLE_NotSupported,
10119 : "Unsupported value for CRITERION: %s", pszCriterion);
10120 : }
10121 : int ret =
10122 19058 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10123 19058 : if (reboundSelf)
10124 14 : d->undoDemoteFromBoundCRS();
10125 19058 : if (reboundOther)
10126 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10127 :
10128 19058 : return ret;
10129 : }
10130 :
10131 : /************************************************************************/
10132 : /* OSRIsSame() */
10133 : /************************************************************************/
10134 :
10135 : /**
10136 : * \brief Do these two spatial references describe the same system ?
10137 : *
10138 : * This function is the same as OGRSpatialReference::IsSame().
10139 : */
10140 41 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10141 :
10142 : {
10143 41 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10144 41 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10145 :
10146 41 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10147 : }
10148 :
10149 : /************************************************************************/
10150 : /* OSRIsSameEx() */
10151 : /************************************************************************/
10152 :
10153 : /**
10154 : * \brief Do these two spatial references describe the same system ?
10155 : *
10156 : * This function is the same as OGRSpatialReference::IsSame().
10157 : */
10158 672 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10159 : const char *const *papszOptions)
10160 : {
10161 672 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10162 672 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10163 :
10164 672 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10165 : }
10166 :
10167 : /************************************************************************/
10168 : /* convertToOtherProjection() */
10169 : /************************************************************************/
10170 :
10171 : /**
10172 : * \brief Convert to another equivalent projection
10173 : *
10174 : * Currently implemented:
10175 : * <ul>
10176 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10177 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10178 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10179 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10180 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10181 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10182 : * </ul>
10183 : *
10184 : * @param pszTargetProjection target projection.
10185 : * @param papszOptions lists of options. None supported currently.
10186 : * @return a new SRS, or NULL in case of error.
10187 : *
10188 : */
10189 91 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10190 : const char *pszTargetProjection,
10191 : CPL_UNUSED const char *const *papszOptions) const
10192 : {
10193 182 : TAKE_OPTIONAL_LOCK();
10194 :
10195 91 : if (pszTargetProjection == nullptr)
10196 1 : return nullptr;
10197 : int new_code;
10198 90 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10199 : {
10200 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10201 : }
10202 84 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10203 : {
10204 7 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10205 : }
10206 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10207 : {
10208 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10209 : }
10210 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10211 : {
10212 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10213 : }
10214 : else
10215 : {
10216 1 : return nullptr;
10217 : }
10218 :
10219 89 : d->refreshProjObj();
10220 89 : d->demoteFromBoundCRS();
10221 89 : OGRSpatialReference *poNewSRS = nullptr;
10222 89 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10223 : {
10224 : auto conv =
10225 88 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10226 88 : auto new_conv = proj_convert_conversion_to_other_method(
10227 : d->getPROJContext(), conv, new_code, nullptr);
10228 88 : proj_destroy(conv);
10229 88 : if (new_conv)
10230 : {
10231 : auto geodCRS =
10232 74 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10233 74 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10234 74 : d->m_pj_crs);
10235 74 : if (geodCRS && cs)
10236 : {
10237 74 : auto new_proj_crs = proj_create_projected_crs(
10238 74 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10239 : new_conv, cs);
10240 74 : proj_destroy(new_conv);
10241 74 : if (new_proj_crs)
10242 : {
10243 74 : poNewSRS = new OGRSpatialReference();
10244 :
10245 74 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10246 : {
10247 9 : auto boundCRS = proj_crs_create_bound_crs(
10248 : d->getPROJContext(), new_proj_crs,
10249 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10250 9 : if (boundCRS)
10251 : {
10252 9 : proj_destroy(new_proj_crs);
10253 9 : new_proj_crs = boundCRS;
10254 : }
10255 : }
10256 :
10257 74 : poNewSRS->d->setPjCRS(new_proj_crs);
10258 : }
10259 : }
10260 74 : proj_destroy(geodCRS);
10261 74 : proj_destroy(cs);
10262 : }
10263 : }
10264 89 : d->undoDemoteFromBoundCRS();
10265 89 : return poNewSRS;
10266 : }
10267 :
10268 : /************************************************************************/
10269 : /* OSRConvertToOtherProjection() */
10270 : /************************************************************************/
10271 :
10272 : /**
10273 : * \brief Convert to another equivalent projection
10274 : *
10275 : * Currently implemented:
10276 : * <ul>
10277 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10278 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10279 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10280 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10281 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10282 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10283 : * </ul>
10284 : *
10285 : * @param hSRS source SRS
10286 : * @param pszTargetProjection target projection.
10287 : * @param papszOptions lists of options. None supported currently.
10288 : * @return a new SRS, or NULL in case of error.
10289 : *
10290 : */
10291 : OGRSpatialReferenceH
10292 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10293 : const char *pszTargetProjection,
10294 : const char *const *papszOptions)
10295 : {
10296 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10297 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10298 28 : pszTargetProjection, papszOptions));
10299 : }
10300 :
10301 : /************************************************************************/
10302 : /* OSRFindMatches() */
10303 : /************************************************************************/
10304 :
10305 : /**
10306 : * \brief Try to identify a match between the passed SRS and a related SRS
10307 : * in a catalog.
10308 : *
10309 : * Matching may be partial, or may fail.
10310 : * Returned entries will be sorted by decreasing match confidence (first
10311 : * entry has the highest match confidence).
10312 : *
10313 : * The exact way matching is done may change in future versions. Starting with
10314 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10315 : *
10316 : * This function is the same as OGRSpatialReference::FindMatches().
10317 : *
10318 : * @param hSRS SRS to match
10319 : * @param papszOptions NULL terminated list of options or NULL
10320 : * @param pnEntries Output parameter. Number of values in the returned array.
10321 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10322 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10323 : * indicate the confidence in the match. 100 is the highest confidence level.
10324 : * The array must be freed with CPLFree().
10325 : *
10326 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10327 : * with OSRFreeSRSArray()
10328 : *
10329 : */
10330 8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10331 : CSLConstList papszOptions, int *pnEntries,
10332 : int **ppanMatchConfidence)
10333 : {
10334 8 : if (pnEntries)
10335 8 : *pnEntries = 0;
10336 8 : if (ppanMatchConfidence)
10337 8 : *ppanMatchConfidence = nullptr;
10338 8 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10339 :
10340 8 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10341 8 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10342 : }
10343 :
10344 : /************************************************************************/
10345 : /* OSRFreeSRSArray() */
10346 : /************************************************************************/
10347 :
10348 : /**
10349 : * \brief Free return of OSRIdentifyMatches()
10350 : *
10351 : * @param pahSRS array of SRS (must be NULL terminated)
10352 : */
10353 197 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10354 : {
10355 197 : if (pahSRS != nullptr)
10356 : {
10357 1743 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10358 : {
10359 1564 : OSRRelease(pahSRS[i]);
10360 : }
10361 179 : CPLFree(pahSRS);
10362 : }
10363 197 : }
10364 :
10365 : /************************************************************************/
10366 : /* FindBestMatch() */
10367 : /************************************************************************/
10368 :
10369 : /**
10370 : * \brief Try to identify the best match between the passed SRS and a related
10371 : * SRS in a catalog.
10372 : *
10373 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10374 : * of filtering its output.
10375 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10376 : * will be considered. If there is a single match, it is returned.
10377 : * If there are several matches, only return the one under the
10378 : * pszPreferredAuthority, if there is a single one under that authority.
10379 : *
10380 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10381 : * 100). If set to 0, 90 is used.
10382 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10383 : * "EPSG" is used.
10384 : * @param papszOptions NULL terminated list of options or NULL. No option is
10385 : * defined at time of writing.
10386 : *
10387 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10388 : *
10389 : * @since GDAL 3.6
10390 : * @see OGRSpatialReference::FindMatches()
10391 : */
10392 : OGRSpatialReference *
10393 1551 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10394 : const char *pszPreferredAuthority,
10395 : CSLConstList papszOptions) const
10396 : {
10397 3102 : TAKE_OPTIONAL_LOCK();
10398 :
10399 1551 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10400 :
10401 1551 : if (nMinimumMatchConfidence == 0)
10402 0 : nMinimumMatchConfidence = 90;
10403 1551 : if (pszPreferredAuthority == nullptr)
10404 202 : pszPreferredAuthority = "EPSG";
10405 :
10406 : // Try to identify the CRS with the database
10407 1551 : int nEntries = 0;
10408 1551 : int *panConfidence = nullptr;
10409 : OGRSpatialReferenceH *pahSRS =
10410 1551 : FindMatches(nullptr, &nEntries, &panConfidence);
10411 1551 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10412 : {
10413 2774 : std::vector<double> adfTOWGS84(7);
10414 1387 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10415 : {
10416 1386 : adfTOWGS84.clear();
10417 : }
10418 :
10419 1387 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10420 :
10421 : auto poBaseGeogCRS =
10422 1387 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10423 1387 : if (poBaseGeogCRS)
10424 : {
10425 : // If the base geographic SRS of the SRS is EPSG:4326
10426 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10427 : // SRS code
10428 : // Same with EPSG:4258 (ETRS89), since it's the only known
10429 : // TOWGS84[] style transformation to WGS 84, and given the
10430 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10431 : // chance that a non-NULL TOWGS84[] will emerge.
10432 1387 : const char *pszAuthorityName = nullptr;
10433 1387 : const char *pszAuthorityCode = nullptr;
10434 1387 : const char *pszBaseAuthorityName = nullptr;
10435 1387 : const char *pszBaseAuthorityCode = nullptr;
10436 1387 : const char *pszBaseName = poBaseGeogCRS->GetName();
10437 2774 : if (adfTOWGS84 == std::vector<double>(7) &&
10438 1 : (pszAuthorityName = poSRS->GetAuthorityName()) != nullptr &&
10439 1 : EQUAL(pszAuthorityName, "EPSG") &&
10440 1 : (pszAuthorityCode = poSRS->GetAuthorityCode()) != nullptr &&
10441 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName()) !=
10442 1 : nullptr &&
10443 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10444 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode()) !=
10445 2775 : nullptr &&
10446 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10447 1 : EQUAL(pszBaseAuthorityCode, "4258") ||
10448 : // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10449 0 : (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10450 : {
10451 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10452 : }
10453 : }
10454 :
10455 1387 : CPLFree(pahSRS);
10456 1387 : CPLFree(panConfidence);
10457 :
10458 1387 : return poSRS;
10459 : }
10460 : else
10461 : {
10462 : // If there are several matches >= nMinimumMatchConfidence, take the
10463 : // only one that is under pszPreferredAuthority
10464 164 : int iBestEntry = -1;
10465 1678 : for (int i = 0; i < nEntries; i++)
10466 : {
10467 1514 : if (panConfidence[i] >= nMinimumMatchConfidence)
10468 : {
10469 : const char *pszAuthName =
10470 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10471 3 : ->GetAuthorityName();
10472 3 : if (pszAuthName != nullptr &&
10473 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10474 : {
10475 3 : if (iBestEntry < 0)
10476 3 : iBestEntry = i;
10477 : else
10478 : {
10479 0 : iBestEntry = -1;
10480 0 : break;
10481 : }
10482 : }
10483 : }
10484 : }
10485 164 : if (iBestEntry >= 0)
10486 : {
10487 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10488 3 : OSRFreeSRSArray(pahSRS);
10489 3 : CPLFree(panConfidence);
10490 3 : return poRet;
10491 : }
10492 : }
10493 161 : OSRFreeSRSArray(pahSRS);
10494 161 : CPLFree(panConfidence);
10495 161 : return nullptr;
10496 : }
10497 :
10498 : /************************************************************************/
10499 : /* SetTOWGS84() */
10500 : /************************************************************************/
10501 :
10502 : /**
10503 : * \brief Set the Bursa-Wolf conversion to WGS84.
10504 : *
10505 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10506 : * if there is no existing DATUM node. It will replace
10507 : * an existing TOWGS84 node if there is one.
10508 : *
10509 : * The parameters have the same meaning as EPSG transformation 9606
10510 : * (Position Vector 7-param. transformation).
10511 : *
10512 : * This method is the same as the C function OSRSetTOWGS84().
10513 : *
10514 : * @param dfDX X child in meters.
10515 : * @param dfDY Y child in meters.
10516 : * @param dfDZ Z child in meters.
10517 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10518 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10519 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10520 : * @param dfPPM scaling factor (parts per million).
10521 : *
10522 : * @return OGRERR_NONE on success.
10523 : */
10524 :
10525 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10526 : double dfEX, double dfEY, double dfEZ,
10527 : double dfPPM)
10528 :
10529 : {
10530 206 : TAKE_OPTIONAL_LOCK();
10531 :
10532 103 : d->refreshProjObj();
10533 103 : if (d->m_pj_crs == nullptr)
10534 : {
10535 0 : return OGRERR_FAILURE;
10536 : }
10537 :
10538 : // Remove existing BoundCRS
10539 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10540 : {
10541 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10542 0 : if (!baseCRS)
10543 0 : return OGRERR_FAILURE;
10544 0 : d->setPjCRS(baseCRS);
10545 : }
10546 :
10547 : PJ_PARAM_DESCRIPTION params[7];
10548 :
10549 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10550 103 : params[0].auth_name = "EPSG";
10551 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10552 103 : params[0].value = dfDX;
10553 103 : params[0].unit_name = "metre";
10554 103 : params[0].unit_conv_factor = 1.0;
10555 103 : params[0].unit_type = PJ_UT_LINEAR;
10556 :
10557 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10558 103 : params[1].auth_name = "EPSG";
10559 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10560 103 : params[1].value = dfDY;
10561 103 : params[1].unit_name = "metre";
10562 103 : params[1].unit_conv_factor = 1.0;
10563 103 : params[1].unit_type = PJ_UT_LINEAR;
10564 :
10565 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10566 103 : params[2].auth_name = "EPSG";
10567 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10568 103 : params[2].value = dfDZ;
10569 103 : params[2].unit_name = "metre";
10570 103 : params[2].unit_conv_factor = 1.0;
10571 103 : params[2].unit_type = PJ_UT_LINEAR;
10572 :
10573 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10574 103 : params[3].auth_name = "EPSG";
10575 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10576 103 : params[3].value = dfEX;
10577 103 : params[3].unit_name = "arc-second";
10578 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10579 103 : params[3].unit_type = PJ_UT_ANGULAR;
10580 :
10581 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10582 103 : params[4].auth_name = "EPSG";
10583 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10584 103 : params[4].value = dfEY;
10585 103 : params[4].unit_name = "arc-second";
10586 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10587 103 : params[4].unit_type = PJ_UT_ANGULAR;
10588 :
10589 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10590 103 : params[5].auth_name = "EPSG";
10591 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10592 103 : params[5].value = dfEZ;
10593 103 : params[5].unit_name = "arc-second";
10594 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10595 103 : params[5].unit_type = PJ_UT_ANGULAR;
10596 :
10597 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10598 103 : params[6].auth_name = "EPSG";
10599 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10600 103 : params[6].value = dfPPM;
10601 103 : params[6].unit_name = "parts per million";
10602 103 : params[6].unit_conv_factor = 1e-6;
10603 103 : params[6].unit_type = PJ_UT_SCALE;
10604 :
10605 : auto sourceCRS =
10606 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10607 103 : if (!sourceCRS)
10608 : {
10609 0 : return OGRERR_FAILURE;
10610 : }
10611 :
10612 103 : const auto sourceType = proj_get_type(sourceCRS);
10613 :
10614 103 : auto targetCRS = proj_create_from_database(
10615 : d->getPROJContext(), "EPSG",
10616 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10617 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10618 : : "4978",
10619 : PJ_CATEGORY_CRS, false, nullptr);
10620 103 : if (!targetCRS)
10621 : {
10622 0 : proj_destroy(sourceCRS);
10623 0 : return OGRERR_FAILURE;
10624 : }
10625 :
10626 206 : CPLString osMethodCode;
10627 : osMethodCode.Printf("%d",
10628 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10629 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10630 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10631 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10632 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10633 :
10634 103 : auto transf = proj_create_transformation(
10635 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10636 : sourceCRS, targetCRS, nullptr,
10637 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10638 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10639 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10640 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10641 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10642 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10643 103 : proj_destroy(sourceCRS);
10644 103 : if (!transf)
10645 : {
10646 0 : proj_destroy(targetCRS);
10647 0 : return OGRERR_FAILURE;
10648 : }
10649 :
10650 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10651 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10652 103 : proj_destroy(transf);
10653 103 : proj_destroy(targetCRS);
10654 103 : if (!newBoundCRS)
10655 : {
10656 0 : return OGRERR_FAILURE;
10657 : }
10658 :
10659 103 : d->setPjCRS(newBoundCRS);
10660 103 : return OGRERR_NONE;
10661 : }
10662 :
10663 : /************************************************************************/
10664 : /* OSRSetTOWGS84() */
10665 : /************************************************************************/
10666 :
10667 : /**
10668 : * \brief Set the Bursa-Wolf conversion to WGS84.
10669 : *
10670 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10671 : */
10672 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10673 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10674 : double dfPPM)
10675 :
10676 : {
10677 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10678 :
10679 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10680 4 : dfPPM);
10681 : }
10682 :
10683 : /************************************************************************/
10684 : /* GetTOWGS84() */
10685 : /************************************************************************/
10686 :
10687 : /**
10688 : * \brief Fetch TOWGS84 parameters, if available.
10689 : *
10690 : * The parameters have the same meaning as EPSG transformation 9606
10691 : * (Position Vector 7-param. transformation).
10692 : *
10693 : * @param padfCoeff array into which up to 7 coefficients are placed.
10694 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10695 : *
10696 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10697 : * TOWGS84 node available.
10698 : */
10699 :
10700 5040 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10701 :
10702 : {
10703 10080 : TAKE_OPTIONAL_LOCK();
10704 :
10705 5040 : d->refreshProjObj();
10706 5040 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10707 4992 : return OGRERR_FAILURE;
10708 :
10709 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10710 :
10711 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10712 48 : int success = proj_coordoperation_get_towgs84_values(
10713 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10714 48 : proj_destroy(transf);
10715 :
10716 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10717 : }
10718 :
10719 : /************************************************************************/
10720 : /* OSRGetTOWGS84() */
10721 : /************************************************************************/
10722 :
10723 : /**
10724 : * \brief Fetch TOWGS84 parameters, if available.
10725 : *
10726 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10727 : */
10728 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10729 : int nCoeffCount)
10730 :
10731 : {
10732 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10733 :
10734 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10735 : }
10736 :
10737 : /************************************************************************/
10738 : /* IsAngularParameter() */
10739 : /************************************************************************/
10740 :
10741 : /** Is the passed projection parameter an angular one?
10742 : *
10743 : * @return TRUE or FALSE
10744 : */
10745 :
10746 : /* static */
10747 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10748 :
10749 : {
10750 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10751 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10752 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10753 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10754 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10755 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10756 8 : return TRUE;
10757 :
10758 2 : return FALSE;
10759 : }
10760 :
10761 : /************************************************************************/
10762 : /* IsLongitudeParameter() */
10763 : /************************************************************************/
10764 :
10765 : /** Is the passed projection parameter an angular longitude
10766 : * (relative to a prime meridian)?
10767 : *
10768 : * @return TRUE or FALSE
10769 : */
10770 :
10771 : /* static */
10772 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10773 :
10774 : {
10775 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10776 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10777 0 : return TRUE;
10778 :
10779 0 : return FALSE;
10780 : }
10781 :
10782 : /************************************************************************/
10783 : /* IsLinearParameter() */
10784 : /************************************************************************/
10785 :
10786 : /** Is the passed projection parameter an linear one measured in meters or
10787 : * some similar linear measure.
10788 : *
10789 : * @return TRUE or FALSE
10790 : */
10791 :
10792 : /* static */
10793 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10794 :
10795 : {
10796 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10797 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10798 9 : return TRUE;
10799 :
10800 34 : return FALSE;
10801 : }
10802 :
10803 : /************************************************************************/
10804 : /* GetNormInfo() */
10805 : /************************************************************************/
10806 :
10807 : /**
10808 : * \brief Set the internal information for normalizing linear, and angular
10809 : * values.
10810 : */
10811 4148 : void OGRSpatialReference::GetNormInfo() const
10812 :
10813 : {
10814 4148 : TAKE_OPTIONAL_LOCK();
10815 :
10816 4148 : if (d->bNormInfoSet)
10817 2963 : return;
10818 :
10819 : /* -------------------------------------------------------------------- */
10820 : /* Initialize values. */
10821 : /* -------------------------------------------------------------------- */
10822 1185 : d->bNormInfoSet = TRUE;
10823 :
10824 1185 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10825 1185 : d->dfToMeter = GetLinearUnits(nullptr);
10826 1185 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10827 1185 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10828 1182 : d->dfToDegrees = 1.0;
10829 : }
10830 :
10831 : /************************************************************************/
10832 : /* GetExtension() */
10833 : /************************************************************************/
10834 :
10835 : /**
10836 : * \brief Fetch extension value.
10837 : *
10838 : * Fetch the value of the named EXTENSION item for the identified
10839 : * target node.
10840 : *
10841 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10842 : * @param pszName the name of the extension being fetched.
10843 : * @param pszDefault the value to return if the extension is not found.
10844 : *
10845 : * @return node value if successful or pszDefault on failure.
10846 : */
10847 :
10848 13588 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10849 : const char *pszName,
10850 : const char *pszDefault) const
10851 :
10852 : {
10853 27176 : TAKE_OPTIONAL_LOCK();
10854 :
10855 : /* -------------------------------------------------------------------- */
10856 : /* Find the target node. */
10857 : /* -------------------------------------------------------------------- */
10858 : const OGR_SRSNode *poNode =
10859 13588 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10860 :
10861 13588 : if (poNode == nullptr)
10862 2407 : return nullptr;
10863 :
10864 : /* -------------------------------------------------------------------- */
10865 : /* Fetch matching EXTENSION if there is one. */
10866 : /* -------------------------------------------------------------------- */
10867 82355 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10868 : {
10869 71198 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10870 :
10871 71224 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10872 26 : poChild->GetChildCount() >= 2)
10873 : {
10874 26 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10875 24 : return poChild->GetChild(1)->GetValue();
10876 : }
10877 : }
10878 :
10879 11157 : return pszDefault;
10880 : }
10881 :
10882 : /************************************************************************/
10883 : /* SetExtension() */
10884 : /************************************************************************/
10885 : /**
10886 : * \brief Set extension value.
10887 : *
10888 : * Set the value of the named EXTENSION item for the identified
10889 : * target node.
10890 : *
10891 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10892 : * @param pszName the name of the extension being fetched.
10893 : * @param pszValue the value to set
10894 : *
10895 : * @return OGRERR_NONE on success
10896 : */
10897 :
10898 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10899 : const char *pszName,
10900 : const char *pszValue)
10901 :
10902 : {
10903 40 : TAKE_OPTIONAL_LOCK();
10904 :
10905 : /* -------------------------------------------------------------------- */
10906 : /* Find the target node. */
10907 : /* -------------------------------------------------------------------- */
10908 20 : OGR_SRSNode *poNode = nullptr;
10909 :
10910 20 : if (pszTargetKey == nullptr)
10911 0 : poNode = GetRoot();
10912 : else
10913 20 : poNode = GetAttrNode(pszTargetKey);
10914 :
10915 20 : if (poNode == nullptr)
10916 0 : return OGRERR_FAILURE;
10917 :
10918 : /* -------------------------------------------------------------------- */
10919 : /* Fetch matching EXTENSION if there is one. */
10920 : /* -------------------------------------------------------------------- */
10921 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10922 : {
10923 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10924 :
10925 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10926 6 : poChild->GetChildCount() >= 2)
10927 : {
10928 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10929 : {
10930 6 : poChild->GetChild(1)->SetValue(pszValue);
10931 6 : return OGRERR_NONE;
10932 : }
10933 : }
10934 : }
10935 :
10936 : /* -------------------------------------------------------------------- */
10937 : /* Create a new EXTENSION node. */
10938 : /* -------------------------------------------------------------------- */
10939 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10940 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10941 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10942 :
10943 14 : poNode->AddChild(poAuthNode);
10944 :
10945 14 : return OGRERR_NONE;
10946 : }
10947 :
10948 : /************************************************************************/
10949 : /* OSRCleanup() */
10950 : /************************************************************************/
10951 :
10952 : static void CleanupSRSWGS84Mutex();
10953 :
10954 : /**
10955 : * \brief Cleanup cached SRS related memory.
10956 : *
10957 : * This function will attempt to cleanup any cache spatial reference
10958 : * related information, such as cached tables of coordinate systems.
10959 : *
10960 : * This function should not be called concurrently with any other GDAL/OGR
10961 : * function. It is meant at being called once before process termination
10962 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10963 : * thread-specific resources before thread termination.
10964 : */
10965 1308 : void OSRCleanup(void)
10966 :
10967 : {
10968 1308 : OGRCTDumpStatistics();
10969 1308 : CSVDeaccess(nullptr);
10970 1308 : CleanupSRSWGS84Mutex();
10971 1308 : OSRCTCleanCache();
10972 1308 : OSRCleanupTLSContext();
10973 1308 : }
10974 :
10975 : /************************************************************************/
10976 : /* GetAxesCount() */
10977 : /************************************************************************/
10978 :
10979 : /**
10980 : * \brief Return the number of axis of the coordinate system of the CRS.
10981 : *
10982 : * @since GDAL 3.0
10983 : */
10984 40363 : int OGRSpatialReference::GetAxesCount() const
10985 : {
10986 80726 : TAKE_OPTIONAL_LOCK();
10987 :
10988 40363 : int axisCount = 0;
10989 40363 : d->refreshProjObj();
10990 40363 : if (d->m_pj_crs == nullptr)
10991 : {
10992 0 : return 0;
10993 : }
10994 40363 : d->demoteFromBoundCRS();
10995 40363 : auto ctxt = d->getPROJContext();
10996 40363 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10997 : {
10998 96 : for (int i = 0;; i++)
10999 : {
11000 288 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
11001 288 : if (!subCRS)
11002 96 : break;
11003 192 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
11004 : {
11005 18 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
11006 18 : if (baseCRS)
11007 : {
11008 18 : proj_destroy(subCRS);
11009 18 : subCRS = baseCRS;
11010 : }
11011 : }
11012 192 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
11013 192 : if (cs)
11014 : {
11015 192 : axisCount += proj_cs_get_axis_count(ctxt, cs);
11016 192 : proj_destroy(cs);
11017 : }
11018 192 : proj_destroy(subCRS);
11019 192 : }
11020 : }
11021 : else
11022 : {
11023 40267 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11024 40267 : if (cs)
11025 : {
11026 40267 : axisCount = proj_cs_get_axis_count(ctxt, cs);
11027 40267 : proj_destroy(cs);
11028 : }
11029 : }
11030 40363 : d->undoDemoteFromBoundCRS();
11031 40363 : return axisCount;
11032 : }
11033 :
11034 : /************************************************************************/
11035 : /* OSRGetAxesCount() */
11036 : /************************************************************************/
11037 :
11038 : /**
11039 : * \brief Return the number of axis of the coordinate system of the CRS.
11040 : *
11041 : * This method is the equivalent of the C++ method
11042 : * OGRSpatialReference::GetAxesCount()
11043 : *
11044 : * @since GDAL 3.1
11045 : */
11046 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
11047 :
11048 : {
11049 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
11050 :
11051 6 : return ToPointer(hSRS)->GetAxesCount();
11052 : }
11053 :
11054 : /************************************************************************/
11055 : /* GetAxis() */
11056 : /************************************************************************/
11057 :
11058 : /**
11059 : * \brief Fetch the orientation of one axis.
11060 : *
11061 : * Fetches the request axis (iAxis - zero based) from the
11062 : * indicated portion of the coordinate system (pszTargetKey) which
11063 : * should be either "GEOGCS" or "PROJCS".
11064 : *
11065 : * No CPLError is issued on routine failures (such as not finding the AXIS).
11066 : *
11067 : * This method is equivalent to the C function OSRGetAxis().
11068 : *
11069 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11070 : * "GEOGCS").
11071 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11072 : * @param peOrientation location into which to place the fetch orientation, may
11073 : * be NULL.
11074 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11075 : * factor. May be NULL. Only set if pszTargetKey == NULL
11076 : *
11077 : * @return the name of the axis or NULL on failure.
11078 : */
11079 :
11080 9024 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11081 : OGRAxisOrientation *peOrientation,
11082 : double *pdfConvUnit) const
11083 :
11084 : {
11085 18048 : TAKE_OPTIONAL_LOCK();
11086 :
11087 9024 : if (peOrientation != nullptr)
11088 8913 : *peOrientation = OAO_Other;
11089 9024 : if (pdfConvUnit != nullptr)
11090 103 : *pdfConvUnit = 0;
11091 :
11092 9024 : d->refreshProjObj();
11093 9024 : if (d->m_pj_crs == nullptr)
11094 : {
11095 3 : return nullptr;
11096 : }
11097 :
11098 9021 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11099 9021 : if (pszTargetKey == nullptr && iAxis <= 2)
11100 : {
11101 9021 : auto ctxt = d->getPROJContext();
11102 :
11103 9021 : int iAxisModified = iAxis;
11104 :
11105 9021 : d->demoteFromBoundCRS();
11106 :
11107 9021 : PJ *cs = nullptr;
11108 9021 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11109 : {
11110 138 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11111 138 : if (horizCRS)
11112 : {
11113 138 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11114 : {
11115 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11116 6 : if (baseCRS)
11117 : {
11118 6 : proj_destroy(horizCRS);
11119 6 : horizCRS = baseCRS;
11120 : }
11121 : }
11122 138 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11123 138 : proj_destroy(horizCRS);
11124 138 : if (cs)
11125 : {
11126 138 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11127 : {
11128 45 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11129 45 : proj_destroy(cs);
11130 45 : cs = nullptr;
11131 : }
11132 : }
11133 : }
11134 :
11135 138 : if (cs == nullptr)
11136 : {
11137 45 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11138 45 : if (vertCRS)
11139 : {
11140 45 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11141 : {
11142 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11143 30 : if (baseCRS)
11144 : {
11145 30 : proj_destroy(vertCRS);
11146 30 : vertCRS = baseCRS;
11147 : }
11148 : }
11149 :
11150 45 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11151 45 : proj_destroy(vertCRS);
11152 : }
11153 : }
11154 : }
11155 : else
11156 : {
11157 8883 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11158 : }
11159 :
11160 9021 : if (cs)
11161 : {
11162 9021 : const char *pszName = nullptr;
11163 9021 : const char *pszOrientation = nullptr;
11164 9021 : double dfConvFactor = 0.0;
11165 9021 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11166 : &pszOrientation, &dfConvFactor, nullptr,
11167 : nullptr, nullptr);
11168 :
11169 9021 : if (pdfConvUnit != nullptr)
11170 : {
11171 103 : *pdfConvUnit = dfConvFactor;
11172 : }
11173 :
11174 9021 : if (pszName && pszOrientation)
11175 : {
11176 9021 : d->m_osAxisName[iAxis] = pszName;
11177 9021 : if (peOrientation)
11178 : {
11179 8910 : if (EQUAL(pszOrientation, "NORTH"))
11180 5627 : *peOrientation = OAO_North;
11181 3283 : else if (EQUAL(pszOrientation, "EAST"))
11182 3208 : *peOrientation = OAO_East;
11183 75 : else if (EQUAL(pszOrientation, "SOUTH"))
11184 67 : *peOrientation = OAO_South;
11185 8 : else if (EQUAL(pszOrientation, "WEST"))
11186 0 : *peOrientation = OAO_West;
11187 8 : else if (EQUAL(pszOrientation, "UP"))
11188 1 : *peOrientation = OAO_Up;
11189 7 : else if (EQUAL(pszOrientation, "DOWN"))
11190 0 : *peOrientation = OAO_Down;
11191 : }
11192 9021 : proj_destroy(cs);
11193 9021 : d->undoDemoteFromBoundCRS();
11194 9021 : return d->m_osAxisName[iAxis].c_str();
11195 : }
11196 0 : proj_destroy(cs);
11197 : }
11198 0 : d->undoDemoteFromBoundCRS();
11199 : }
11200 :
11201 : /* -------------------------------------------------------------------- */
11202 : /* Find the target node. */
11203 : /* -------------------------------------------------------------------- */
11204 0 : const OGR_SRSNode *poNode = nullptr;
11205 :
11206 0 : if (pszTargetKey == nullptr)
11207 0 : poNode = GetRoot();
11208 : else
11209 0 : poNode = GetAttrNode(pszTargetKey);
11210 :
11211 0 : if (poNode == nullptr)
11212 0 : return nullptr;
11213 :
11214 : /* -------------------------------------------------------------------- */
11215 : /* Find desired child AXIS. */
11216 : /* -------------------------------------------------------------------- */
11217 0 : const OGR_SRSNode *poAxis = nullptr;
11218 0 : const int nChildCount = poNode->GetChildCount();
11219 :
11220 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11221 : {
11222 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11223 :
11224 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11225 0 : continue;
11226 :
11227 0 : if (iAxis == 0)
11228 : {
11229 0 : poAxis = poChild;
11230 0 : break;
11231 : }
11232 0 : iAxis--;
11233 : }
11234 :
11235 0 : if (poAxis == nullptr)
11236 0 : return nullptr;
11237 :
11238 0 : if (poAxis->GetChildCount() < 2)
11239 0 : return nullptr;
11240 :
11241 : /* -------------------------------------------------------------------- */
11242 : /* Extract name and orientation if possible. */
11243 : /* -------------------------------------------------------------------- */
11244 0 : if (peOrientation != nullptr)
11245 : {
11246 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11247 :
11248 0 : if (EQUAL(pszOrientation, "NORTH"))
11249 0 : *peOrientation = OAO_North;
11250 0 : else if (EQUAL(pszOrientation, "EAST"))
11251 0 : *peOrientation = OAO_East;
11252 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11253 0 : *peOrientation = OAO_South;
11254 0 : else if (EQUAL(pszOrientation, "WEST"))
11255 0 : *peOrientation = OAO_West;
11256 0 : else if (EQUAL(pszOrientation, "UP"))
11257 0 : *peOrientation = OAO_Up;
11258 0 : else if (EQUAL(pszOrientation, "DOWN"))
11259 0 : *peOrientation = OAO_Down;
11260 0 : else if (EQUAL(pszOrientation, "OTHER"))
11261 0 : *peOrientation = OAO_Other;
11262 : else
11263 : {
11264 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11265 : pszOrientation);
11266 : }
11267 : }
11268 :
11269 0 : return poAxis->GetChild(0)->GetValue();
11270 : }
11271 :
11272 : /************************************************************************/
11273 : /* OSRGetAxis() */
11274 : /************************************************************************/
11275 :
11276 : /**
11277 : * \brief Fetch the orientation of one axis.
11278 : *
11279 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11280 : */
11281 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11282 : int iAxis, OGRAxisOrientation *peOrientation)
11283 :
11284 : {
11285 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11286 :
11287 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11288 : }
11289 :
11290 : /************************************************************************/
11291 : /* OSRAxisEnumToName() */
11292 : /************************************************************************/
11293 :
11294 : /**
11295 : * \brief Return the string representation for the OGRAxisOrientation
11296 : * enumeration.
11297 : *
11298 : * For example "NORTH" for OAO_North.
11299 : *
11300 : * @return an internal string
11301 : */
11302 400 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11303 :
11304 : {
11305 400 : if (eOrientation == OAO_North)
11306 200 : return "NORTH";
11307 200 : if (eOrientation == OAO_East)
11308 200 : return "EAST";
11309 0 : if (eOrientation == OAO_South)
11310 0 : return "SOUTH";
11311 0 : if (eOrientation == OAO_West)
11312 0 : return "WEST";
11313 0 : if (eOrientation == OAO_Up)
11314 0 : return "UP";
11315 0 : if (eOrientation == OAO_Down)
11316 0 : return "DOWN";
11317 0 : if (eOrientation == OAO_Other)
11318 0 : return "OTHER";
11319 :
11320 0 : return "UNKNOWN";
11321 : }
11322 :
11323 : /************************************************************************/
11324 : /* SetAxes() */
11325 : /************************************************************************/
11326 :
11327 : /**
11328 : * \brief Set the axes for a coordinate system.
11329 : *
11330 : * Set the names, and orientations of the axes for either a projected
11331 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11332 : *
11333 : * This method is equivalent to the C function OSRSetAxes().
11334 : *
11335 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11336 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11337 : * @param eXAxisOrientation normally OAO_East.
11338 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11339 : * @param eYAxisOrientation normally OAO_North.
11340 : *
11341 : * @return OGRERR_NONE on success or an error code.
11342 : */
11343 :
11344 200 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11345 : const char *pszXAxisName,
11346 : OGRAxisOrientation eXAxisOrientation,
11347 : const char *pszYAxisName,
11348 : OGRAxisOrientation eYAxisOrientation)
11349 :
11350 : {
11351 400 : TAKE_OPTIONAL_LOCK();
11352 :
11353 : /* -------------------------------------------------------------------- */
11354 : /* Find the target node. */
11355 : /* -------------------------------------------------------------------- */
11356 200 : OGR_SRSNode *poNode = nullptr;
11357 :
11358 200 : if (pszTargetKey == nullptr)
11359 200 : poNode = GetRoot();
11360 : else
11361 0 : poNode = GetAttrNode(pszTargetKey);
11362 :
11363 200 : if (poNode == nullptr)
11364 0 : return OGRERR_FAILURE;
11365 :
11366 : /* -------------------------------------------------------------------- */
11367 : /* Strip any existing AXIS children. */
11368 : /* -------------------------------------------------------------------- */
11369 600 : while (poNode->FindChild("AXIS") >= 0)
11370 400 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11371 :
11372 : /* -------------------------------------------------------------------- */
11373 : /* Insert desired axes */
11374 : /* -------------------------------------------------------------------- */
11375 200 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11376 :
11377 200 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11378 200 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11379 :
11380 200 : poNode->AddChild(poAxis);
11381 :
11382 200 : poAxis = new OGR_SRSNode("AXIS");
11383 :
11384 200 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11385 200 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11386 :
11387 200 : poNode->AddChild(poAxis);
11388 :
11389 200 : return OGRERR_NONE;
11390 : }
11391 :
11392 : /************************************************************************/
11393 : /* OSRSetAxes() */
11394 : /************************************************************************/
11395 : /**
11396 : * \brief Set the axes for a coordinate system.
11397 : *
11398 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11399 : */
11400 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11401 : const char *pszXAxisName,
11402 : OGRAxisOrientation eXAxisOrientation,
11403 : const char *pszYAxisName,
11404 : OGRAxisOrientation eYAxisOrientation)
11405 : {
11406 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11407 :
11408 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11409 : eXAxisOrientation, pszYAxisName,
11410 0 : eYAxisOrientation);
11411 : }
11412 :
11413 : /************************************************************************/
11414 : /* OSRExportToMICoordSys() */
11415 : /************************************************************************/
11416 : /**
11417 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11418 : *
11419 : * This method is the equivalent of the C++ method
11420 : * OGRSpatialReference::exportToMICoordSys
11421 : */
11422 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11423 :
11424 : {
11425 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11426 :
11427 5 : *ppszReturn = nullptr;
11428 :
11429 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11430 : }
11431 :
11432 : /************************************************************************/
11433 : /* exportToMICoordSys() */
11434 : /************************************************************************/
11435 :
11436 : /**
11437 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11438 : *
11439 : * Note that the returned WKT string should be freed with
11440 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11441 : *
11442 : * This method is the same as the C function OSRExportToMICoordSys().
11443 : *
11444 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11445 : * definition will be assigned.
11446 : *
11447 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11448 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11449 : */
11450 :
11451 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11452 :
11453 : {
11454 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11455 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11456 7 : return OGRERR_NONE;
11457 :
11458 0 : return OGRERR_FAILURE;
11459 : }
11460 :
11461 : /************************************************************************/
11462 : /* OSRImportFromMICoordSys() */
11463 : /************************************************************************/
11464 : /**
11465 : * \brief Import Mapinfo style CoordSys definition.
11466 : *
11467 : * This method is the equivalent of the C++ method
11468 : * OGRSpatialReference::importFromMICoordSys
11469 : */
11470 :
11471 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11472 : const char *pszCoordSys)
11473 :
11474 : {
11475 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11476 :
11477 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11478 : }
11479 :
11480 : /************************************************************************/
11481 : /* importFromMICoordSys() */
11482 : /************************************************************************/
11483 :
11484 : /**
11485 : * \brief Import Mapinfo style CoordSys definition.
11486 : *
11487 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11488 : * definition string.
11489 : *
11490 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11491 : *
11492 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11493 : *
11494 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11495 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11496 : */
11497 :
11498 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11499 :
11500 : {
11501 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11502 :
11503 17 : if (poResult == nullptr)
11504 0 : return OGRERR_FAILURE;
11505 :
11506 17 : *this = *poResult;
11507 17 : delete poResult;
11508 :
11509 17 : return OGRERR_NONE;
11510 : }
11511 :
11512 : /************************************************************************/
11513 : /* OSRCalcInvFlattening() */
11514 : /************************************************************************/
11515 :
11516 : /**
11517 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11518 : *
11519 : * @param dfSemiMajor Semi-major axis length.
11520 : * @param dfSemiMinor Semi-minor axis length.
11521 : *
11522 : * @return inverse flattening, or 0 if both axis are equal.
11523 : */
11524 :
11525 8919 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11526 : {
11527 8919 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11528 27 : return 0;
11529 8892 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11530 : {
11531 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11532 : "OSRCalcInvFlattening(): Wrong input values");
11533 0 : return 0;
11534 : }
11535 :
11536 8892 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11537 : }
11538 :
11539 : /************************************************************************/
11540 : /* OSRCalcInvFlattening() */
11541 : /************************************************************************/
11542 :
11543 : /**
11544 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11545 : *
11546 : * @param dfSemiMajor Semi-major axis length.
11547 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11548 : *
11549 : * @return semi-minor axis
11550 : */
11551 :
11552 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11553 : double dfInvFlattening)
11554 : {
11555 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11556 103 : return dfSemiMajor;
11557 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11558 : {
11559 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11560 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11561 0 : return dfSemiMajor;
11562 : }
11563 :
11564 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11565 : }
11566 :
11567 : /************************************************************************/
11568 : /* GetWGS84SRS() */
11569 : /************************************************************************/
11570 :
11571 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11572 : static CPLMutex *hMutex = nullptr;
11573 :
11574 : /**
11575 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11576 : *
11577 : * Note: the instance will have
11578 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11579 : *
11580 : * The reference counter of the returned object is not increased by this
11581 : * operation.
11582 : *
11583 : * @return instance.
11584 : */
11585 :
11586 1004 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11587 : {
11588 1004 : CPLMutexHolderD(&hMutex);
11589 1004 : if (poSRSWGS84 == nullptr)
11590 : {
11591 5 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11592 5 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11593 : }
11594 2008 : return poSRSWGS84;
11595 : }
11596 :
11597 : /************************************************************************/
11598 : /* CleanupSRSWGS84Mutex() */
11599 : /************************************************************************/
11600 :
11601 1308 : static void CleanupSRSWGS84Mutex()
11602 : {
11603 1308 : if (hMutex != nullptr)
11604 : {
11605 3 : poSRSWGS84->Release();
11606 3 : poSRSWGS84 = nullptr;
11607 3 : CPLDestroyMutex(hMutex);
11608 3 : hMutex = nullptr;
11609 : }
11610 1308 : }
11611 :
11612 : /************************************************************************/
11613 : /* OSRImportFromProj4() */
11614 : /************************************************************************/
11615 : /**
11616 : * \brief Import PROJ coordinate string.
11617 : *
11618 : * This function is the same as OGRSpatialReference::importFromProj4().
11619 : */
11620 218 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11621 :
11622 : {
11623 218 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11624 :
11625 218 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11626 : }
11627 :
11628 : /************************************************************************/
11629 : /* importFromProj4() */
11630 : /************************************************************************/
11631 :
11632 : /**
11633 : * \brief Import PROJ coordinate string.
11634 : *
11635 : * The OGRSpatialReference is initialized from the passed PROJs style
11636 : * coordinate system string.
11637 : *
11638 : * Example:
11639 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11640 : *
11641 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11642 : * a legacy syntax that should be avoided in the future. In particular they will
11643 : * result in CRS objects whose axis order might not correspond to the official
11644 : * EPSG axis order.
11645 : *
11646 : * This method is the equivalent of the C function OSRImportFromProj4().
11647 : *
11648 : * @param pszProj4 the PROJ style string.
11649 : *
11650 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11651 : */
11652 :
11653 777 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11654 :
11655 : {
11656 1554 : TAKE_OPTIONAL_LOCK();
11657 :
11658 777 : if (strlen(pszProj4) >= 10000)
11659 : {
11660 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11661 1 : return OGRERR_CORRUPT_DATA;
11662 : }
11663 :
11664 : /* -------------------------------------------------------------------- */
11665 : /* Clear any existing definition. */
11666 : /* -------------------------------------------------------------------- */
11667 776 : Clear();
11668 :
11669 776 : CPLString osProj4(pszProj4);
11670 776 : if (osProj4.find("type=crs") == std::string::npos)
11671 : {
11672 764 : osProj4 += " +type=crs";
11673 : }
11674 :
11675 778 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11676 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11677 : {
11678 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11679 : "+init=epsg:XXXX syntax is deprecated. It might return "
11680 : "a CRS with a non-EPSG compliant axis order.");
11681 : }
11682 776 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11683 776 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11684 776 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11685 776 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11686 : }
11687 :
11688 : /************************************************************************/
11689 : /* OSRExportToProj4() */
11690 : /************************************************************************/
11691 : /**
11692 : * \brief Export coordinate system in PROJ.4 legacy format.
11693 : *
11694 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11695 : * PROJ >= 6 is significantly different from earlier versions. In particular
11696 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11697 : * will be missing most of the time. PROJ strings to encode CRS should be
11698 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11699 : * is the recommended way.
11700 : *
11701 : * This function is the same as OGRSpatialReference::exportToProj4().
11702 : */
11703 324 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11704 : char **ppszReturn)
11705 :
11706 : {
11707 324 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11708 :
11709 324 : *ppszReturn = nullptr;
11710 :
11711 324 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11712 : }
11713 :
11714 : /************************************************************************/
11715 : /* exportToProj4() */
11716 : /************************************************************************/
11717 :
11718 : /**
11719 : * \brief Export coordinate system in PROJ.4 legacy format.
11720 : *
11721 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11722 : * PROJ >= 6 is significantly different from earlier versions. In particular
11723 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11724 : * will be missing most of the time. PROJ strings to encode CRS should be
11725 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11726 : * representation is the recommended way.
11727 : *
11728 : * Converts the loaded coordinate reference system into PROJ format
11729 : * to the extent possible. The string returned in ppszProj4 should be
11730 : * deallocated by the caller with CPLFree() when no longer needed.
11731 : *
11732 : * LOCAL_CS coordinate systems are not translatable. An empty string
11733 : * will be returned along with OGRERR_NONE.
11734 : *
11735 : * Special processing for Transverse Mercator:
11736 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11737 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11738 : * for the tmerc and utm projection methods, rather than the more accurate
11739 : * method.
11740 : *
11741 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11742 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11743 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11744 : * added. This automatic addition may be disabled by setting the
11745 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11746 : *
11747 : * This method is the equivalent of the C function OSRExportToProj4().
11748 : *
11749 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11750 : * will be assigned.
11751 : *
11752 : * @return OGRERR_NONE on success or an error code on failure.
11753 : */
11754 :
11755 1469 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11756 :
11757 : {
11758 : // In the past calling this method was thread-safe, even if we never
11759 : // guaranteed it. Now proj_as_proj_string() will cache the result
11760 : // internally, so this is no longer thread-safe.
11761 2938 : std::lock_guard oLock(d->m_mutex);
11762 :
11763 1469 : d->refreshProjObj();
11764 1469 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11765 : {
11766 4 : *ppszProj4 = CPLStrdup("");
11767 4 : return OGRERR_FAILURE;
11768 : }
11769 :
11770 : // OSR_USE_ETMERC is here just for legacy
11771 1465 : bool bForceApproxTMerc = false;
11772 1465 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11773 1465 : if (pszUseETMERC && pszUseETMERC[0])
11774 : {
11775 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11776 : "OSR_USE_ETMERC is a legacy configuration option, which "
11777 : "now has only effect when set to NO (YES is the default). "
11778 : "Use OSR_USE_APPROX_TMERC=YES instead");
11779 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11780 : }
11781 : else
11782 : {
11783 : const char *pszUseApproxTMERC =
11784 1465 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11785 1465 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11786 : {
11787 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11788 : }
11789 : }
11790 1465 : const char *options[] = {
11791 1465 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11792 :
11793 1465 : const char *projString = proj_as_proj_string(
11794 1465 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11795 :
11796 1465 : PJ *boundCRS = nullptr;
11797 2926 : if (projString &&
11798 1461 : (strstr(projString, "+datum=") == nullptr ||
11799 2936 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11800 544 : CPLTestBool(
11801 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11802 : {
11803 544 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11804 544 : d->getPROJContext(), d->m_pj_crs, true,
11805 544 : strstr(projString, "+datum=") == nullptr);
11806 544 : if (boundCRS)
11807 : {
11808 174 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11809 : PJ_PROJ_4, options);
11810 : }
11811 : }
11812 :
11813 1465 : if (projString == nullptr)
11814 : {
11815 4 : *ppszProj4 = CPLStrdup("");
11816 4 : proj_destroy(boundCRS);
11817 4 : return OGRERR_FAILURE;
11818 : }
11819 1461 : *ppszProj4 = CPLStrdup(projString);
11820 1461 : proj_destroy(boundCRS);
11821 1461 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11822 1461 : if (pszTypeCrs)
11823 1461 : *pszTypeCrs = '\0';
11824 1461 : return OGRERR_NONE;
11825 : }
11826 :
11827 : /************************************************************************/
11828 : /* morphToESRI() */
11829 : /************************************************************************/
11830 : /**
11831 : * \brief Convert in place to ESRI WKT format.
11832 : *
11833 : * The value nodes of this coordinate system are modified in various manners
11834 : * more closely map onto the ESRI concept of WKT format. This includes
11835 : * renaming a variety of projections and arguments, and stripping out
11836 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11837 : *
11838 : * \note Since GDAL 3.0, this function has only user-visible effects at
11839 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11840 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11841 : *
11842 : * This does the same as the C function OSRMorphToESRI().
11843 : *
11844 : * @return OGRERR_NONE unless something goes badly wrong.
11845 : * @deprecated
11846 : */
11847 :
11848 236 : OGRErr OGRSpatialReference::morphToESRI()
11849 :
11850 : {
11851 236 : TAKE_OPTIONAL_LOCK();
11852 :
11853 236 : d->refreshProjObj();
11854 236 : d->setMorphToESRI(true);
11855 :
11856 472 : return OGRERR_NONE;
11857 : }
11858 :
11859 : /************************************************************************/
11860 : /* OSRMorphToESRI() */
11861 : /************************************************************************/
11862 :
11863 : /**
11864 : * \brief Convert in place to ESRI WKT format.
11865 : *
11866 : * This function is the same as the C++ method
11867 : * OGRSpatialReference::morphToESRI().
11868 : */
11869 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11870 :
11871 : {
11872 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11873 :
11874 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11875 : }
11876 :
11877 : /************************************************************************/
11878 : /* morphFromESRI() */
11879 : /************************************************************************/
11880 :
11881 : /**
11882 : * \brief Convert in place from ESRI WKT format.
11883 : *
11884 : * The value notes of this coordinate system are modified in various manners
11885 : * to adhere more closely to the WKT standard. This mostly involves
11886 : * translating a variety of ESRI names for projections, arguments and
11887 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11888 : * translation of EPSG to WKT for the CT specification.
11889 : *
11890 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11891 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11892 : * effect is to undo the effect of a potential prior call to morphToESRI().
11893 : *
11894 : * This does the same as the C function OSRMorphFromESRI().
11895 : *
11896 : * @return OGRERR_NONE unless something goes badly wrong.
11897 : * @deprecated
11898 : */
11899 :
11900 21 : OGRErr OGRSpatialReference::morphFromESRI()
11901 :
11902 : {
11903 21 : TAKE_OPTIONAL_LOCK();
11904 :
11905 21 : d->refreshProjObj();
11906 21 : d->setMorphToESRI(false);
11907 :
11908 42 : return OGRERR_NONE;
11909 : }
11910 :
11911 : /************************************************************************/
11912 : /* OSRMorphFromESRI() */
11913 : /************************************************************************/
11914 :
11915 : /**
11916 : * \brief Convert in place from ESRI WKT format.
11917 : *
11918 : * This function is the same as the C++ method
11919 : * OGRSpatialReference::morphFromESRI().
11920 : */
11921 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11922 :
11923 : {
11924 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11925 :
11926 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11927 : }
11928 :
11929 : /************************************************************************/
11930 : /* FindMatches() */
11931 : /************************************************************************/
11932 :
11933 : /**
11934 : * \brief Try to identify a match between the passed SRS and a related SRS
11935 : * in a catalog.
11936 : *
11937 : * Matching may be partial, or may fail.
11938 : * Returned entries will be sorted by decreasing match confidence (first
11939 : * entry has the highest match confidence).
11940 : *
11941 : * The exact way matching is done may change in future versions. Starting with
11942 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11943 : *
11944 : * This method is the same as OSRFindMatches().
11945 : *
11946 : * @param papszOptions NULL terminated list of options or NULL
11947 : * @param pnEntries Output parameter. Number of values in the returned array.
11948 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11949 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11950 : * indicate the confidence in the match. 100 is the highest confidence level.
11951 : * The array must be freed with CPLFree().
11952 : *
11953 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11954 : * with OSRFreeSRSArray()
11955 : *
11956 : *
11957 : * @see OGRSpatialReference::FindBestMatch()
11958 : */
11959 : OGRSpatialReferenceH *
11960 1564 : OGRSpatialReference::FindMatches(CSLConstList papszOptions, int *pnEntries,
11961 : int **ppanMatchConfidence) const
11962 : {
11963 3128 : TAKE_OPTIONAL_LOCK();
11964 :
11965 1564 : CPL_IGNORE_RET_VAL(papszOptions);
11966 :
11967 1564 : if (pnEntries)
11968 1564 : *pnEntries = 0;
11969 1564 : if (ppanMatchConfidence)
11970 1564 : *ppanMatchConfidence = nullptr;
11971 :
11972 1564 : d->refreshProjObj();
11973 1564 : if (!d->m_pj_crs)
11974 0 : return nullptr;
11975 :
11976 1564 : int *panConfidence = nullptr;
11977 1564 : auto ctxt = d->getPROJContext();
11978 : auto list =
11979 1564 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11980 1564 : if (!list)
11981 0 : return nullptr;
11982 :
11983 1564 : const int nMatches = proj_list_get_count(list);
11984 :
11985 1564 : if (pnEntries)
11986 1564 : *pnEntries = static_cast<int>(nMatches);
11987 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11988 1564 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11989 1564 : if (ppanMatchConfidence)
11990 : {
11991 1564 : *ppanMatchConfidence =
11992 1564 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11993 : }
11994 :
11995 1564 : bool bSortAgain = false;
11996 :
11997 4510 : for (int i = 0; i < nMatches; i++)
11998 : {
11999 2946 : PJ *obj = proj_list_get(ctxt, list, i);
12000 2946 : CPLAssert(obj);
12001 2946 : OGRSpatialReference *poSRS = new OGRSpatialReference();
12002 2946 : poSRS->d->setPjCRS(obj);
12003 2946 : pahRet[i] = ToHandle(poSRS);
12004 :
12005 : // Identify matches that only differ by axis order
12006 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
12007 2964 : poSRS->GetAxesCount() == 2 &&
12008 2955 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
12009 : {
12010 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
12011 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
12012 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
12013 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
12014 9 : GetAxis(nullptr, 0, &eThisAxis0);
12015 9 : GetAxis(nullptr, 1, &eThisAxis1);
12016 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
12017 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
12018 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
12019 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
12020 : {
12021 : auto pj_crs_normalized =
12022 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
12023 9 : if (pj_crs_normalized)
12024 : {
12025 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
12026 9 : PJ_COMP_EQUIVALENT))
12027 : {
12028 3 : bSortAgain = true;
12029 3 : panConfidence[i] = 90;
12030 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
12031 : }
12032 9 : proj_destroy(pj_crs_normalized);
12033 : }
12034 : }
12035 : }
12036 :
12037 2946 : if (ppanMatchConfidence)
12038 2946 : (*ppanMatchConfidence)[i] = panConfidence[i];
12039 : }
12040 :
12041 1564 : if (bSortAgain)
12042 : {
12043 3 : std::vector<int> anIndices;
12044 12 : for (int i = 0; i < nMatches; ++i)
12045 9 : anIndices.push_back(i);
12046 :
12047 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
12048 9 : [&panConfidence](int i, int j)
12049 9 : { return panConfidence[i] > panConfidence[j]; });
12050 :
12051 : OGRSpatialReferenceH *pahRetSorted =
12052 : static_cast<OGRSpatialReferenceH *>(
12053 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
12054 12 : for (int i = 0; i < nMatches; ++i)
12055 : {
12056 9 : pahRetSorted[i] = pahRet[anIndices[i]];
12057 9 : if (ppanMatchConfidence)
12058 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
12059 : }
12060 3 : CPLFree(pahRet);
12061 3 : pahRet = pahRetSorted;
12062 : }
12063 :
12064 1564 : pahRet[nMatches] = nullptr;
12065 1564 : proj_list_destroy(list);
12066 1564 : proj_int_list_destroy(panConfidence);
12067 :
12068 1564 : return pahRet;
12069 : }
12070 :
12071 : /************************************************************************/
12072 : /* importFromEPSGA() */
12073 : /************************************************************************/
12074 :
12075 : /**
12076 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12077 : * code.
12078 : *
12079 : * This method will initialize the spatial reference based on the
12080 : * passed in EPSG CRS code found in the PROJ database.
12081 : *
12082 : * Since GDAL 3.0, this method is identical to importFromEPSG().
12083 : *
12084 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12085 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12086 : * such method available for the CRS. This behavior might not always be
12087 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12088 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12089 : * The AddGuessedTOWGS84() method can also be used for that purpose.
12090 : *
12091 : * The method will also by default substitute a deprecated EPSG code by its
12092 : * non-deprecated replacement. If this behavior is not desired, the
12093 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12094 : *
12095 : * This method is the same as the C function OSRImportFromEPSGA().
12096 : *
12097 : * @param nCode a CRS code.
12098 : *
12099 : * @return OGRERR_NONE on success, or an error code on failure.
12100 : */
12101 :
12102 49866 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12103 :
12104 : {
12105 99732 : TAKE_OPTIONAL_LOCK();
12106 :
12107 49866 : Clear();
12108 :
12109 : const char *pszUseNonDeprecated =
12110 49866 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12111 : const bool bUseNonDeprecated =
12112 49866 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12113 49866 : const bool bAddTOWGS84 = CPLTestBool(
12114 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12115 49866 : auto tlsCache = OSRGetProjTLSCache();
12116 49866 : if (tlsCache)
12117 : {
12118 : auto cachedObj =
12119 49866 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12120 49866 : if (cachedObj)
12121 : {
12122 39459 : d->setPjCRS(cachedObj);
12123 39459 : return OGRERR_NONE;
12124 : }
12125 : }
12126 :
12127 20814 : CPLString osCode;
12128 10407 : osCode.Printf("%d", nCode);
12129 : PJ *obj;
12130 10407 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12131 10407 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12132 : {
12133 10402 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12134 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12135 : nullptr);
12136 10402 : if (!obj)
12137 : {
12138 25 : return OGRERR_FAILURE;
12139 : }
12140 : }
12141 : else
12142 : {
12143 : // Likely to be an ESRI CRS...
12144 5 : CPLErr eLastErrorType = CE_None;
12145 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12146 5 : std::string osLastErrorMsg;
12147 5 : bool bIsESRI = false;
12148 : {
12149 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12150 5 : CPLErrorReset();
12151 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12152 : osCode.c_str(), PJ_CATEGORY_CRS,
12153 : true, nullptr);
12154 5 : if (!obj)
12155 : {
12156 2 : eLastErrorType = CPLGetLastErrorType();
12157 2 : eLastErrorNum = CPLGetLastErrorNo();
12158 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12159 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12160 : osCode.c_str(), PJ_CATEGORY_CRS,
12161 : true, nullptr);
12162 2 : if (obj)
12163 1 : bIsESRI = true;
12164 : }
12165 : }
12166 5 : if (!obj)
12167 : {
12168 1 : if (eLastErrorType != CE_None)
12169 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12170 : osLastErrorMsg.c_str());
12171 1 : return OGRERR_FAILURE;
12172 : }
12173 4 : if (bIsESRI)
12174 : {
12175 1 : CPLError(CE_Warning, CPLE_AppDefined,
12176 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12177 : "Assuming ESRI:%d was meant",
12178 : nCode, nCode, nCode);
12179 : }
12180 : }
12181 :
12182 10381 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12183 : {
12184 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12185 410 : if (list)
12186 : {
12187 410 : const auto count = proj_list_get_count(list);
12188 410 : if (count == 1)
12189 : {
12190 : auto nonDeprecated =
12191 359 : proj_list_get(d->getPROJContext(), list, 0);
12192 359 : if (nonDeprecated)
12193 : {
12194 359 : if (pszUseNonDeprecated == nullptr)
12195 : {
12196 : const char *pszNewAuth =
12197 359 : proj_get_id_auth_name(nonDeprecated, 0);
12198 : const char *pszNewCode =
12199 359 : proj_get_id_code(nonDeprecated, 0);
12200 359 : CPLError(CE_Warning, CPLE_AppDefined,
12201 : "CRS EPSG:%d is deprecated. "
12202 : "Its non-deprecated replacement %s:%s "
12203 : "will be used instead. "
12204 : "To use the original CRS, set the "
12205 : "OSR_USE_NON_DEPRECATED "
12206 : "configuration option to NO.",
12207 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12208 : pszNewCode ? pszNewCode : "(null)");
12209 : }
12210 359 : proj_destroy(obj);
12211 359 : obj = nonDeprecated;
12212 : }
12213 : }
12214 : }
12215 410 : proj_list_destroy(list);
12216 : }
12217 :
12218 10381 : if (bAddTOWGS84)
12219 : {
12220 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12221 : obj, nullptr);
12222 1 : if (boundCRS)
12223 : {
12224 1 : proj_destroy(obj);
12225 1 : obj = boundCRS;
12226 : }
12227 : }
12228 :
12229 10381 : d->setPjCRS(obj);
12230 :
12231 10381 : if (tlsCache)
12232 : {
12233 10381 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12234 : obj);
12235 : }
12236 :
12237 10381 : return OGRERR_NONE;
12238 : }
12239 :
12240 : /************************************************************************/
12241 : /* AddGuessedTOWGS84() */
12242 : /************************************************************************/
12243 :
12244 : /**
12245 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12246 : * to WGS84.
12247 : *
12248 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12249 : * to WGS84 when there is one and only one such method available for the CRS.
12250 : * Note: this is more restrictive to how GDAL < 3 worked.
12251 : *
12252 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12253 : *
12254 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12255 : * already a transformation to WGS84 or none matching could be found).
12256 : *
12257 : * @since GDAL 3.0.3
12258 : */
12259 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12260 : {
12261 36 : TAKE_OPTIONAL_LOCK();
12262 :
12263 18 : d->refreshProjObj();
12264 18 : if (!d->m_pj_crs)
12265 0 : return OGRERR_FAILURE;
12266 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12267 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12268 18 : if (!boundCRS)
12269 : {
12270 0 : return OGRERR_FAILURE;
12271 : }
12272 18 : d->setPjCRS(boundCRS);
12273 18 : return OGRERR_NONE;
12274 : }
12275 :
12276 : /************************************************************************/
12277 : /* OSRImportFromEPSGA() */
12278 : /************************************************************************/
12279 :
12280 : /**
12281 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12282 : * to WGS84.
12283 : *
12284 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12285 : *
12286 : * @since GDAL 3.0.3
12287 : */
12288 :
12289 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12290 :
12291 : {
12292 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12293 :
12294 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12295 : }
12296 :
12297 : /************************************************************************/
12298 : /* OSRImportFromEPSGA() */
12299 : /************************************************************************/
12300 :
12301 : /**
12302 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12303 : * code.
12304 : *
12305 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12306 : */
12307 :
12308 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12309 :
12310 : {
12311 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12312 :
12313 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12314 : }
12315 :
12316 : /************************************************************************/
12317 : /* importFromEPSG() */
12318 : /************************************************************************/
12319 :
12320 : /**
12321 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12322 : * code.
12323 : *
12324 : * This method will initialize the spatial reference based on the
12325 : * passed in EPSG CRS code found in the PROJ database.
12326 : *
12327 : * This method is the same as the C function OSRImportFromEPSG().
12328 : *
12329 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12330 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12331 : * such method available for the CRS. This behavior might not always be
12332 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12333 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12334 : *
12335 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12336 : *
12337 : * @return OGRERR_NONE on success, or an error code on failure.
12338 : */
12339 :
12340 43558 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12341 :
12342 : {
12343 43558 : return importFromEPSGA(nCode);
12344 : }
12345 :
12346 : /************************************************************************/
12347 : /* OSRImportFromEPSG() */
12348 : /************************************************************************/
12349 :
12350 : /**
12351 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12352 : * code.
12353 : *
12354 : * This function is the same as OGRSpatialReference::importFromEPSG().
12355 : */
12356 :
12357 1514 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12358 :
12359 : {
12360 1514 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12361 :
12362 1514 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12363 : }
12364 :
12365 : /************************************************************************/
12366 : /* EPSGTreatsAsLatLong() */
12367 : /************************************************************************/
12368 :
12369 : /**
12370 : * \brief This method returns TRUE if this geographic coordinate
12371 : * system should be treated as having lat/long coordinate ordering.
12372 : *
12373 : * Currently this returns TRUE for all geographic coordinate systems
12374 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12375 : * also checked that the CRS had belonged to EPSG authority, but this check
12376 : * has now been removed).
12377 : *
12378 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12379 : * geographic CRS imported with importFromEPSG() would cause this method to
12380 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12381 : * is now equivalent to importFromEPSGA().
12382 : *
12383 : * FALSE will be returned for all coordinate systems that are not geographic,
12384 : * or whose axes ordering is not latitude, longitude.
12385 : *
12386 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12387 : *
12388 : * @return TRUE or FALSE.
12389 : */
12390 :
12391 1295 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12392 :
12393 : {
12394 2590 : TAKE_OPTIONAL_LOCK();
12395 :
12396 1295 : if (!IsGeographic())
12397 863 : return FALSE;
12398 :
12399 432 : d->demoteFromBoundCRS();
12400 :
12401 432 : bool ret = false;
12402 432 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12403 : {
12404 : auto horizCRS =
12405 17 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12406 17 : if (horizCRS)
12407 : {
12408 : auto cs =
12409 17 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12410 17 : if (cs)
12411 : {
12412 17 : const char *pszDirection = nullptr;
12413 17 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12414 : nullptr, &pszDirection, nullptr,
12415 17 : nullptr, nullptr, nullptr))
12416 : {
12417 17 : if (EQUAL(pszDirection, "north"))
12418 : {
12419 17 : ret = true;
12420 : }
12421 : }
12422 :
12423 17 : proj_destroy(cs);
12424 : }
12425 :
12426 17 : proj_destroy(horizCRS);
12427 : }
12428 : }
12429 : else
12430 : {
12431 : auto cs =
12432 415 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12433 415 : if (cs)
12434 : {
12435 415 : const char *pszDirection = nullptr;
12436 415 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12437 : nullptr, &pszDirection, nullptr, nullptr,
12438 415 : nullptr, nullptr))
12439 : {
12440 415 : if (EQUAL(pszDirection, "north"))
12441 : {
12442 368 : ret = true;
12443 : }
12444 : }
12445 :
12446 415 : proj_destroy(cs);
12447 : }
12448 : }
12449 432 : d->undoDemoteFromBoundCRS();
12450 :
12451 432 : return ret;
12452 : }
12453 :
12454 : /************************************************************************/
12455 : /* OSREPSGTreatsAsLatLong() */
12456 : /************************************************************************/
12457 :
12458 : /**
12459 : * \brief This function returns TRUE if this geographic coordinate
12460 : * system should be treated as having lat/long coordinate ordering.
12461 : *
12462 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12463 : */
12464 :
12465 208 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12466 :
12467 : {
12468 208 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12469 :
12470 208 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12471 : }
12472 :
12473 : /************************************************************************/
12474 : /* EPSGTreatsAsNorthingEasting() */
12475 : /************************************************************************/
12476 :
12477 : /**
12478 : * \brief This method returns TRUE if this projected coordinate
12479 : * system should be treated as having northing/easting coordinate ordering.
12480 : *
12481 : * Currently this returns TRUE for all projected coordinate systems
12482 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12483 : * also checked that the CRS had belonged to EPSG authority, but this check
12484 : * has now been removed).
12485 : *
12486 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12487 : * projected CRS with northing, easting axis order imported with
12488 : * importFromEPSG() would cause this method to
12489 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12490 : * is now equivalent to importFromEPSGA().
12491 : *
12492 : * FALSE will be returned for all coordinate systems that are not projected,
12493 : * or whose axes ordering is not northing, easting.
12494 : *
12495 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12496 : *
12497 : * @return TRUE or FALSE.
12498 : *
12499 : */
12500 :
12501 950 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12502 :
12503 : {
12504 1900 : TAKE_OPTIONAL_LOCK();
12505 :
12506 950 : if (!IsProjected())
12507 49 : return FALSE;
12508 :
12509 901 : d->demoteFromBoundCRS();
12510 : PJ *projCRS;
12511 901 : const auto ctxt = d->getPROJContext();
12512 901 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12513 : {
12514 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12515 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12516 : {
12517 0 : d->undoDemoteFromBoundCRS();
12518 0 : proj_destroy(projCRS);
12519 0 : return FALSE;
12520 : }
12521 : }
12522 : else
12523 : {
12524 897 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12525 : }
12526 :
12527 901 : bool ret = false;
12528 901 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12529 901 : proj_destroy(projCRS);
12530 901 : d->undoDemoteFromBoundCRS();
12531 :
12532 901 : if (cs)
12533 : {
12534 901 : ret = isNorthEastAxisOrder(ctxt, cs);
12535 901 : proj_destroy(cs);
12536 : }
12537 :
12538 901 : return ret;
12539 : }
12540 :
12541 : /************************************************************************/
12542 : /* OSREPSGTreatsAsNorthingEasting() */
12543 : /************************************************************************/
12544 :
12545 : /**
12546 : * \brief This function returns TRUE if this projected coordinate
12547 : * system should be treated as having northing/easting coordinate ordering.
12548 : *
12549 : * This function is the same as
12550 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12551 : *
12552 : */
12553 :
12554 215 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12555 :
12556 : {
12557 215 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12558 :
12559 215 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12560 : }
12561 :
12562 : /************************************************************************/
12563 : /* ImportFromESRIWisconsinWKT() */
12564 : /* */
12565 : /* Search a ESRI State Plane WKT and import it. */
12566 : /************************************************************************/
12567 :
12568 : // This is only used by the HFA driver and somewhat dubious we really need that
12569 : // Coming from an old ESRI merge
12570 :
12571 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12572 : double centralMeridian,
12573 : double latOfOrigin,
12574 : const char *unitsName,
12575 : const char *crsName)
12576 : {
12577 2 : TAKE_OPTIONAL_LOCK();
12578 :
12579 1 : if (centralMeridian < -93 || centralMeridian > -87)
12580 0 : return OGRERR_FAILURE;
12581 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12582 0 : return OGRERR_FAILURE;
12583 :
12584 : // If the CS name is known.
12585 1 : if (!prjName && !unitsName && crsName)
12586 : {
12587 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12588 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12589 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12590 0 : if (list)
12591 : {
12592 0 : if (proj_list_get_count(list) == 1)
12593 : {
12594 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12595 0 : if (crs)
12596 : {
12597 0 : Clear();
12598 0 : d->setPjCRS(crs);
12599 0 : proj_list_destroy(list);
12600 0 : return OGRERR_NONE;
12601 : }
12602 : }
12603 0 : proj_list_destroy(list);
12604 : }
12605 0 : return OGRERR_FAILURE;
12606 : }
12607 :
12608 1 : if (prjName == nullptr || unitsName == nullptr)
12609 : {
12610 0 : return OGRERR_FAILURE;
12611 : }
12612 :
12613 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12614 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12615 : "NAD_1983_HARN_WISCRS_", &type, 1,
12616 : true, 0, nullptr);
12617 1 : if (list)
12618 : {
12619 1 : const auto listSize = proj_list_get_count(list);
12620 8 : for (int i = 0; i < listSize; i++)
12621 : {
12622 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12623 8 : if (!crs)
12624 : {
12625 7 : continue;
12626 : }
12627 :
12628 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12629 8 : if (!conv)
12630 : {
12631 0 : proj_destroy(crs);
12632 0 : continue;
12633 : }
12634 8 : const char *pszMethodCode = nullptr;
12635 8 : proj_coordoperation_get_method_info(
12636 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12637 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12638 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12639 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12640 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12641 : nMethodCode ==
12642 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12643 : {
12644 3 : proj_destroy(crs);
12645 3 : proj_destroy(conv);
12646 3 : continue;
12647 : }
12648 :
12649 : auto coordSys =
12650 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12651 5 : if (!coordSys)
12652 : {
12653 0 : proj_destroy(crs);
12654 0 : proj_destroy(conv);
12655 0 : continue;
12656 : }
12657 :
12658 5 : double dfConvFactor = 0.0;
12659 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12660 : nullptr, nullptr, &dfConvFactor, nullptr,
12661 : nullptr, nullptr);
12662 5 : proj_destroy(coordSys);
12663 :
12664 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12665 1 : (!EQUAL(unitsName, "meters") &&
12666 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12667 : 1e-10))
12668 : {
12669 4 : proj_destroy(crs);
12670 4 : proj_destroy(conv);
12671 4 : continue;
12672 : }
12673 :
12674 1 : int idx_lat = proj_coordoperation_get_param_index(
12675 : d->getPROJContext(), conv,
12676 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12677 1 : double valueLat = -1000;
12678 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12679 : nullptr, nullptr, nullptr, &valueLat,
12680 : nullptr, nullptr, nullptr, nullptr,
12681 : nullptr, nullptr);
12682 1 : int idx_lon = proj_coordoperation_get_param_index(
12683 : d->getPROJContext(), conv,
12684 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12685 1 : double valueLong = -1000;
12686 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12687 : nullptr, nullptr, nullptr, &valueLong,
12688 : nullptr, nullptr, nullptr, nullptr,
12689 : nullptr, nullptr);
12690 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12691 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12692 : {
12693 1 : Clear();
12694 1 : d->setPjCRS(crs);
12695 1 : proj_list_destroy(list);
12696 1 : proj_destroy(conv);
12697 1 : return OGRERR_NONE;
12698 : }
12699 :
12700 0 : proj_destroy(crs);
12701 0 : proj_destroy(conv);
12702 : }
12703 0 : proj_list_destroy(list);
12704 : }
12705 :
12706 0 : return OGRERR_FAILURE;
12707 : }
12708 :
12709 : /************************************************************************/
12710 : /* GetAxisMappingStrategy() */
12711 : /************************************************************************/
12712 :
12713 : /** \brief Return the data axis to CRS axis mapping strategy.
12714 : *
12715 : * <ul>
12716 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12717 : * lat/long order, the data will still be long/lat ordered. Similarly for
12718 : * a projected CRS with northing/easting order, the data will still be
12719 : * easting/northing ordered.
12720 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12721 : * the CRS axis.
12722 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12723 : * SetDataAxisToSRSAxisMapping()
12724 : * </ul>
12725 : * @return the data axis to CRS axis mapping strategy.
12726 : * @since GDAL 3.0
12727 : */
12728 95 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12729 : {
12730 95 : TAKE_OPTIONAL_LOCK();
12731 :
12732 190 : return d->m_axisMappingStrategy;
12733 : }
12734 :
12735 : /************************************************************************/
12736 : /* OSRGetAxisMappingStrategy() */
12737 : /************************************************************************/
12738 :
12739 : /** \brief Return the data axis to CRS axis mapping strategy.
12740 : *
12741 : * See OGRSpatialReference::GetAxisMappingStrategy()
12742 : * @since GDAL 3.0
12743 : */
12744 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12745 : {
12746 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12747 :
12748 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12749 : }
12750 :
12751 : /************************************************************************/
12752 : /* SetAxisMappingStrategy() */
12753 : /************************************************************************/
12754 :
12755 : /** \brief Set the data axis to CRS axis mapping strategy.
12756 : *
12757 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12758 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12759 : * later being the default value when the option is not set) to control the
12760 : * value of the data axis to CRS axis mapping strategy when a
12761 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12762 : * override this default value.
12763 : *
12764 : * See OGRSpatialReference::GetAxisMappingStrategy()
12765 : * @since GDAL 3.0
12766 : */
12767 95690 : void OGRSpatialReference::SetAxisMappingStrategy(
12768 : OSRAxisMappingStrategy strategy)
12769 : {
12770 191380 : TAKE_OPTIONAL_LOCK();
12771 :
12772 95690 : d->m_axisMappingStrategy = strategy;
12773 95690 : d->refreshAxisMapping();
12774 95690 : }
12775 :
12776 : /************************************************************************/
12777 : /* OSRSetAxisMappingStrategy() */
12778 : /************************************************************************/
12779 :
12780 : /** \brief Set the data axis to CRS axis mapping strategy.
12781 : *
12782 : * See OGRSpatialReference::SetAxisMappingStrategy()
12783 : * @since GDAL 3.0
12784 : */
12785 814 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12786 : OSRAxisMappingStrategy strategy)
12787 : {
12788 814 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12789 :
12790 814 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12791 : }
12792 :
12793 : /************************************************************************/
12794 : /* GetDataAxisToSRSAxisMapping() */
12795 : /************************************************************************/
12796 :
12797 : /** \brief Return the data axis to SRS axis mapping.
12798 : *
12799 : * The number of elements of the vector will be the number of axis of the CRS.
12800 : * Values start at 1.
12801 : *
12802 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12803 : * for the first axis of the CRS.
12804 : *
12805 : * @since GDAL 3.0
12806 : */
12807 17398600 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12808 : {
12809 17398600 : TAKE_OPTIONAL_LOCK();
12810 :
12811 34797200 : return d->m_axisMapping;
12812 : }
12813 :
12814 : /************************************************************************/
12815 : /* OSRGetDataAxisToSRSAxisMapping() */
12816 : /************************************************************************/
12817 :
12818 : /** \brief Return the data axis to SRS axis mapping.
12819 : *
12820 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12821 : *
12822 : * @since GDAL 3.0
12823 : */
12824 105 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12825 : int *pnCount)
12826 : {
12827 105 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12828 105 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12829 :
12830 : const auto &v =
12831 105 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12832 105 : *pnCount = static_cast<int>(v.size());
12833 105 : return v.data();
12834 : }
12835 :
12836 : /************************************************************************/
12837 : /* SetDataAxisToSRSAxisMapping() */
12838 : /************************************************************************/
12839 :
12840 : /** \brief Set a custom data axis to CRS axis mapping.
12841 : *
12842 : * The number of elements of the mapping vector should be the number of axis
12843 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12844 : * check that, beyond checking there are at least 2 elements, so that this
12845 : * method and setting the CRS can be done in any order).
12846 : * This is taken into account by OGRCoordinateTransformation to transform the
12847 : * order of coordinates to the order expected by the CRS before
12848 : * transformation, and back to the data order after transformation.
12849 : *
12850 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12851 : * axis of the CRS. A negative value can also be used to ask for a sign
12852 : * reversal during coordinate transformation (to deal with northing vs southing,
12853 : * easting vs westing, heights vs depths).
12854 : *
12855 : * When used with OGRCoordinateTransformation,
12856 : * - the only valid values for mapping[0] (data axis number for the first axis
12857 : * of the CRS) are 1, 2, -1, -2.
12858 : * - the only valid values for mapping[1] (data axis number for the second axis
12859 : * of the CRS) are 1, 2, -1, -2.
12860 : * - the only valid values mapping[2] are 3 or -3.
12861 : * Note: this method does not validate the values of mapping[].
12862 : *
12863 : * mapping=[2,1] typically expresses the inversion of axis between the data
12864 : * axis and the CRS axis for a 2D CRS.
12865 : *
12866 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12867 : *
12868 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12869 : *
12870 : * @param mapping The new data axis to CRS axis mapping.
12871 : *
12872 : * @since GDAL 3.0
12873 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12874 : */
12875 10500 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12876 : const std::vector<int> &mapping)
12877 : {
12878 21000 : TAKE_OPTIONAL_LOCK();
12879 :
12880 10500 : if (mapping.size() < 2)
12881 0 : return OGRERR_FAILURE;
12882 10500 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12883 10500 : d->m_axisMapping = mapping;
12884 10500 : return OGRERR_NONE;
12885 : }
12886 :
12887 : /************************************************************************/
12888 : /* OSRSetDataAxisToSRSAxisMapping() */
12889 : /************************************************************************/
12890 :
12891 : /** \brief Set a custom data axis to CRS axis mapping.
12892 : *
12893 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12894 : *
12895 : * This is the same as the C++ method
12896 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12897 : *
12898 : * @since GDAL 3.1
12899 : */
12900 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12901 : int nMappingSize, const int *panMapping)
12902 : {
12903 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12904 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12905 : OGRERR_FAILURE);
12906 :
12907 15 : if (nMappingSize < 0)
12908 0 : return OGRERR_FAILURE;
12909 :
12910 30 : std::vector<int> mapping(nMappingSize);
12911 15 : if (nMappingSize)
12912 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12913 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12914 15 : mapping);
12915 : }
12916 :
12917 : /************************************************************************/
12918 : /* GetAreaOfUse() */
12919 : /************************************************************************/
12920 :
12921 : /** \brief Return the area of use of the CRS.
12922 : *
12923 : * This method is the same as the OSRGetAreaOfUse() function.
12924 : *
12925 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12926 : * longitude, expressed in degree. Might be NULL. If the returned value is
12927 : * -1000, the bounding box is unknown.
12928 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12929 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12930 : * the bounding box is unknown.
12931 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12932 : * longitude, expressed in degree. Might be NULL. If the returned value is
12933 : * -1000, the bounding box is unknown.
12934 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12935 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12936 : * the bounding box is unknown.
12937 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12938 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12939 : * invalidated by further calls.
12940 : * @return true in case of success
12941 : * @since GDAL 3.0
12942 : */
12943 82 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12944 : double *pdfSouthLatitudeDeg,
12945 : double *pdfEastLongitudeDeg,
12946 : double *pdfNorthLatitudeDeg,
12947 : const char **ppszAreaName) const
12948 : {
12949 164 : TAKE_OPTIONAL_LOCK();
12950 :
12951 82 : d->refreshProjObj();
12952 82 : if (!d->m_pj_crs)
12953 : {
12954 0 : return false;
12955 : }
12956 82 : d->demoteFromBoundCRS();
12957 82 : const char *pszAreaName = nullptr;
12958 82 : int bSuccess = proj_get_area_of_use(
12959 82 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12960 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12961 : &pszAreaName);
12962 82 : d->undoDemoteFromBoundCRS();
12963 82 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12964 82 : if (ppszAreaName)
12965 28 : *ppszAreaName = d->m_osAreaName.c_str();
12966 82 : return CPL_TO_BOOL(bSuccess);
12967 : }
12968 :
12969 : /************************************************************************/
12970 : /* GetAreaOfUse() */
12971 : /************************************************************************/
12972 :
12973 : /** \brief Return the area of use of the CRS.
12974 : *
12975 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12976 : *
12977 : * @since GDAL 3.0
12978 : */
12979 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12980 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12981 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12982 : {
12983 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12984 :
12985 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12986 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12987 1 : pdfNorthLatitudeDeg, ppszAreaName);
12988 : }
12989 :
12990 : /************************************************************************/
12991 : /* OSRGetCRSInfoListFromDatabase() */
12992 : /************************************************************************/
12993 :
12994 : /** \brief Enumerate CRS objects from the database.
12995 : *
12996 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12997 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12998 : *
12999 : * @param pszAuthName Authority name, used to restrict the search.
13000 : * Or NULL for all authorities.
13001 : * @param params Additional criteria. Must be set to NULL for now.
13002 : * @param pnOutResultCount Output parameter pointing to an integer to receive
13003 : * the size of the result list. Might be NULL
13004 : * @return an array of OSRCRSInfo* pointers to be freed with
13005 : * OSRDestroyCRSInfoList(), or NULL in case of error.
13006 : *
13007 : * @since GDAL 3.0
13008 : */
13009 : OSRCRSInfo **
13010 28 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
13011 : CPL_UNUSED const OSRCRSListParameters *params,
13012 : int *pnOutResultCount)
13013 : {
13014 28 : int nResultCount = 0;
13015 28 : auto projList = proj_get_crs_info_list_from_database(
13016 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
13017 28 : if (pnOutResultCount)
13018 28 : *pnOutResultCount = nResultCount;
13019 28 : if (!projList)
13020 : {
13021 0 : return nullptr;
13022 : }
13023 28 : auto res = new OSRCRSInfo *[nResultCount + 1];
13024 92468 : for (int i = 0; i < nResultCount; i++)
13025 : {
13026 92440 : res[i] = new OSRCRSInfo;
13027 184880 : res[i]->pszAuthName = projList[i]->auth_name
13028 92440 : ? CPLStrdup(projList[i]->auth_name)
13029 : : nullptr;
13030 92440 : res[i]->pszCode =
13031 92440 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
13032 92440 : res[i]->pszName =
13033 92440 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
13034 92440 : res[i]->eType = OSR_CRS_TYPE_OTHER;
13035 92440 : switch (projList[i]->type)
13036 : {
13037 9272 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
13038 9272 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
13039 9272 : break;
13040 2864 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
13041 2864 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
13042 2864 : break;
13043 3200 : case PJ_TYPE_GEOCENTRIC_CRS:
13044 3200 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
13045 3200 : break;
13046 70012 : case PJ_TYPE_PROJECTED_CRS:
13047 70012 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
13048 70012 : break;
13049 2860 : case PJ_TYPE_VERTICAL_CRS:
13050 2860 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
13051 2860 : break;
13052 4232 : case PJ_TYPE_COMPOUND_CRS:
13053 4232 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
13054 4232 : break;
13055 0 : default:
13056 0 : break;
13057 : }
13058 92440 : res[i]->bDeprecated = projList[i]->deprecated;
13059 92440 : res[i]->bBboxValid = projList[i]->bbox_valid;
13060 92440 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
13061 92440 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
13062 92440 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
13063 92440 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
13064 184880 : res[i]->pszAreaName = projList[i]->area_name
13065 92440 : ? CPLStrdup(projList[i]->area_name)
13066 : : nullptr;
13067 92440 : res[i]->pszProjectionMethod =
13068 92440 : projList[i]->projection_method_name
13069 92440 : ? CPLStrdup(projList[i]->projection_method_name)
13070 : : nullptr;
13071 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
13072 : res[i]->pszCelestialBodyName =
13073 : projList[i]->celestial_body_name
13074 : ? CPLStrdup(projList[i]->celestial_body_name)
13075 : : nullptr;
13076 : #else
13077 92440 : res[i]->pszCelestialBodyName =
13078 92440 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13079 184880 : ? CPLStrdup("Earth")
13080 : : nullptr;
13081 : #endif
13082 : }
13083 28 : res[nResultCount] = nullptr;
13084 28 : proj_crs_info_list_destroy(projList);
13085 28 : return res;
13086 : }
13087 :
13088 : /************************************************************************/
13089 : /* OSRDestroyCRSInfoList() */
13090 : /************************************************************************/
13091 :
13092 : /** \brief Destroy the result returned by
13093 : * OSRGetCRSInfoListFromDatabase().
13094 : *
13095 : * @since GDAL 3.0
13096 : */
13097 28 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13098 : {
13099 28 : if (list)
13100 : {
13101 92468 : for (int i = 0; list[i] != nullptr; i++)
13102 : {
13103 92440 : CPLFree(list[i]->pszAuthName);
13104 92440 : CPLFree(list[i]->pszCode);
13105 92440 : CPLFree(list[i]->pszName);
13106 92440 : CPLFree(list[i]->pszAreaName);
13107 92440 : CPLFree(list[i]->pszProjectionMethod);
13108 92440 : CPLFree(list[i]->pszCelestialBodyName);
13109 92440 : delete list[i];
13110 : }
13111 28 : delete[] list;
13112 : }
13113 28 : }
13114 :
13115 : /************************************************************************/
13116 : /* OSRGetAuthorityListFromDatabase() */
13117 : /************************************************************************/
13118 :
13119 : /** \brief Return the list of CRS authorities used in the PROJ database.
13120 : *
13121 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13122 : *
13123 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13124 : *
13125 : * @return nullptr in case of error, or a NULL terminated list of strings to
13126 : * free with CSLDestroy()
13127 : * @since GDAL 3.10
13128 : */
13129 5 : char **OSRGetAuthorityListFromDatabase()
13130 : {
13131 : PROJ_STRING_LIST list =
13132 5 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13133 5 : if (!list)
13134 : {
13135 0 : return nullptr;
13136 : }
13137 5 : int count = 0;
13138 30 : while (list[count])
13139 25 : ++count;
13140 5 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13141 30 : for (int i = 0; i < count; ++i)
13142 25 : res[i] = CPLStrdup(list[i]);
13143 5 : proj_string_list_destroy(list);
13144 5 : return res;
13145 : }
13146 :
13147 : /************************************************************************/
13148 : /* UpdateCoordinateSystemFromGeogCRS() */
13149 : /************************************************************************/
13150 :
13151 : /*! @cond Doxygen_Suppress */
13152 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13153 : *
13154 : * @since GDAL 3.1
13155 : */
13156 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13157 : {
13158 1 : TAKE_OPTIONAL_LOCK();
13159 :
13160 1 : d->refreshProjObj();
13161 1 : if (!d->m_pj_crs)
13162 0 : return;
13163 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13164 0 : return;
13165 1 : if (GetAxesCount() == 3)
13166 0 : return;
13167 1 : auto ctxt = d->getPROJContext();
13168 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13169 1 : if (!baseCRS)
13170 0 : return;
13171 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13172 1 : if (!baseCRSCS)
13173 : {
13174 0 : proj_destroy(baseCRS);
13175 0 : return;
13176 : }
13177 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13178 : {
13179 0 : proj_destroy(baseCRSCS);
13180 0 : proj_destroy(baseCRS);
13181 0 : return;
13182 : }
13183 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13184 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13185 : {
13186 0 : proj_destroy(baseCRSCS);
13187 0 : proj_destroy(baseCRS);
13188 0 : proj_destroy(projCS);
13189 0 : return;
13190 : }
13191 :
13192 : PJ_AXIS_DESCRIPTION axis[3];
13193 4 : for (int i = 0; i < 3; i++)
13194 : {
13195 3 : const char *name = nullptr;
13196 3 : const char *abbreviation = nullptr;
13197 3 : const char *direction = nullptr;
13198 3 : double unit_conv_factor = 0;
13199 3 : const char *unit_name = nullptr;
13200 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13201 : &abbreviation, &direction, &unit_conv_factor,
13202 : &unit_name, nullptr, nullptr);
13203 3 : axis[i].name = CPLStrdup(name);
13204 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13205 3 : axis[i].direction = CPLStrdup(direction);
13206 3 : axis[i].unit_name = CPLStrdup(unit_name);
13207 3 : axis[i].unit_conv_factor = unit_conv_factor;
13208 3 : axis[i].unit_type = PJ_UT_LINEAR;
13209 : }
13210 1 : proj_destroy(baseCRSCS);
13211 1 : proj_destroy(projCS);
13212 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13213 4 : for (int i = 0; i < 3; i++)
13214 : {
13215 3 : CPLFree(axis[i].name);
13216 3 : CPLFree(axis[i].abbreviation);
13217 3 : CPLFree(axis[i].direction);
13218 3 : CPLFree(axis[i].unit_name);
13219 : }
13220 1 : if (!cs)
13221 : {
13222 0 : proj_destroy(baseCRS);
13223 0 : return;
13224 : }
13225 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13226 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13227 : conversion, cs);
13228 1 : proj_destroy(baseCRS);
13229 1 : proj_destroy(conversion);
13230 1 : proj_destroy(cs);
13231 1 : d->setPjCRS(crs);
13232 : }
13233 :
13234 : /*! @endcond */
13235 :
13236 : /************************************************************************/
13237 : /* PromoteTo3D() */
13238 : /************************************************************************/
13239 :
13240 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13241 : *
13242 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13243 : * units.
13244 : *
13245 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13246 : * be used.
13247 : * @return OGRERR_NONE if no error occurred.
13248 : * @since GDAL 3.1 and PROJ 6.3
13249 : */
13250 44 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13251 : {
13252 88 : TAKE_OPTIONAL_LOCK();
13253 :
13254 44 : d->refreshProjObj();
13255 44 : if (!d->m_pj_crs)
13256 0 : return OGRERR_FAILURE;
13257 : auto newPj =
13258 44 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13259 44 : if (!newPj)
13260 0 : return OGRERR_FAILURE;
13261 44 : d->setPjCRS(newPj);
13262 44 : return OGRERR_NONE;
13263 : }
13264 :
13265 : /************************************************************************/
13266 : /* OSRPromoteTo3D() */
13267 : /************************************************************************/
13268 :
13269 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13270 : *
13271 : * See OGRSpatialReference::PromoteTo3D()
13272 : *
13273 : * @since GDAL 3.1 and PROJ 6.3
13274 : */
13275 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13276 : {
13277 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13278 :
13279 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13280 : }
13281 :
13282 : /************************************************************************/
13283 : /* DemoteTo2D() */
13284 : /************************************************************************/
13285 :
13286 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13287 : *
13288 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13289 : * be used.
13290 : * @return OGRERR_NONE if no error occurred.
13291 : * @since GDAL 3.2 and PROJ 6.3
13292 : */
13293 49 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13294 : {
13295 98 : TAKE_OPTIONAL_LOCK();
13296 :
13297 49 : d->refreshProjObj();
13298 49 : if (!d->m_pj_crs)
13299 0 : return OGRERR_FAILURE;
13300 : auto newPj =
13301 49 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13302 49 : if (!newPj)
13303 0 : return OGRERR_FAILURE;
13304 49 : d->setPjCRS(newPj);
13305 49 : return OGRERR_NONE;
13306 : }
13307 :
13308 : /************************************************************************/
13309 : /* OSRDemoteTo2D() */
13310 : /************************************************************************/
13311 :
13312 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13313 : *
13314 : * See OGRSpatialReference::DemoteTo2D()
13315 : *
13316 : * @since GDAL 3.2 and PROJ 6.3
13317 : */
13318 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13319 : {
13320 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13321 :
13322 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13323 : }
13324 :
13325 : /************************************************************************/
13326 : /* GetEPSGGeogCS() */
13327 : /************************************************************************/
13328 :
13329 : /** Try to establish what the EPSG code for this coordinate systems
13330 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13331 : *
13332 : * @return EPSG code
13333 : */
13334 :
13335 346 : int OGRSpatialReference::GetEPSGGeogCS() const
13336 :
13337 : {
13338 692 : TAKE_OPTIONAL_LOCK();
13339 :
13340 : /* -------------------------------------------------------------------- */
13341 : /* Check axis order. */
13342 : /* -------------------------------------------------------------------- */
13343 692 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13344 346 : if (!poGeogCRS)
13345 0 : return -1;
13346 :
13347 346 : bool ret = false;
13348 346 : poGeogCRS->d->demoteFromBoundCRS();
13349 346 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13350 346 : poGeogCRS->d->m_pj_crs);
13351 346 : poGeogCRS->d->undoDemoteFromBoundCRS();
13352 346 : if (cs)
13353 : {
13354 346 : const char *pszDirection = nullptr;
13355 346 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13356 : &pszDirection, nullptr, nullptr, nullptr,
13357 346 : nullptr))
13358 : {
13359 346 : if (EQUAL(pszDirection, "north"))
13360 : {
13361 144 : ret = true;
13362 : }
13363 : }
13364 :
13365 346 : proj_destroy(cs);
13366 : }
13367 346 : if (!ret)
13368 202 : return -1;
13369 :
13370 : /* -------------------------------------------------------------------- */
13371 : /* Do we already have it? */
13372 : /* -------------------------------------------------------------------- */
13373 144 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13374 144 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13375 67 : return atoi(GetAuthorityCode("GEOGCS"));
13376 :
13377 : /* -------------------------------------------------------------------- */
13378 : /* Get the datum and geogcs names. */
13379 : /* -------------------------------------------------------------------- */
13380 :
13381 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13382 77 : const char *pszDatum = GetAttrValue("DATUM");
13383 :
13384 : // We can only operate on coordinate systems with a geogcs.
13385 154 : OGRSpatialReference oSRSTmp;
13386 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13387 : {
13388 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13389 : // export to WKT1, so try to extract the geographic CRS through PROJ
13390 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13391 1 : oSRSTmp.CopyGeogCSFrom(this);
13392 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13393 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13394 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13395 : {
13396 0 : return -1;
13397 : }
13398 : }
13399 :
13400 : // Lookup geographic CRS name
13401 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13402 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13403 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13404 77 : if (list)
13405 : {
13406 77 : const auto listSize = proj_list_get_count(list);
13407 77 : if (listSize == 1)
13408 : {
13409 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13410 49 : if (crs)
13411 : {
13412 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13413 49 : const char *pszCode = proj_get_id_code(crs, 0);
13414 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13415 : {
13416 47 : const int nCode = atoi(pszCode);
13417 47 : proj_destroy(crs);
13418 47 : proj_list_destroy(list);
13419 47 : return nCode;
13420 : }
13421 2 : proj_destroy(crs);
13422 : }
13423 : }
13424 30 : proj_list_destroy(list);
13425 : }
13426 :
13427 : /* -------------------------------------------------------------------- */
13428 : /* Is this a "well known" geographic coordinate system? */
13429 : /* -------------------------------------------------------------------- */
13430 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13431 30 : strstr(pszDatum, "WGS") ||
13432 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13433 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13434 90 : strstr(pszDatum, "World Geodetic System") ||
13435 30 : strstr(pszDatum, "World_Geodetic_System");
13436 :
13437 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13438 30 : strstr(pszDatum, "NAD") ||
13439 30 : strstr(pszGEOGCS, "North American") ||
13440 30 : strstr(pszGEOGCS, "North_American") ||
13441 90 : strstr(pszDatum, "North American") ||
13442 30 : strstr(pszDatum, "North_American");
13443 :
13444 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13445 0 : return 4326;
13446 :
13447 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13448 0 : return 4322;
13449 :
13450 : // This is questionable as there are several 'flavors' of NAD83 that
13451 : // are not the same as 4269
13452 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13453 0 : return 4269;
13454 :
13455 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13456 0 : return 4267;
13457 :
13458 : /* -------------------------------------------------------------------- */
13459 : /* If we know the datum, associate the most likely GCS with */
13460 : /* it. */
13461 : /* -------------------------------------------------------------------- */
13462 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13463 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13464 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13465 0 : GetPrimeMeridian() == 0.0)
13466 : {
13467 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13468 :
13469 0 : if (nDatum >= 6000 && nDatum <= 6999)
13470 0 : return nDatum - 2000;
13471 : }
13472 :
13473 30 : return -1;
13474 : }
13475 :
13476 : /************************************************************************/
13477 : /* SetCoordinateEpoch() */
13478 : /************************************************************************/
13479 :
13480 : /** Set the coordinate epoch, as decimal year.
13481 : *
13482 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13483 : * change with time. To be unambiguous the coordinates must always be qualified
13484 : * with the epoch at which they are valid. The coordinate epoch is not
13485 : * necessarily the epoch at which the observation was collected.
13486 : *
13487 : * Pedantically the coordinate epoch of an observation belongs to the
13488 : * observation, and not to the CRS, however it is often more practical to
13489 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13490 : * CRS (see IsDynamic())
13491 : *
13492 : * This method is the same as the OSRSetCoordinateEpoch() function.
13493 : *
13494 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13495 : * @since OGR 3.4
13496 : */
13497 :
13498 958 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13499 : {
13500 958 : d->m_coordinateEpoch = dfCoordinateEpoch;
13501 958 : }
13502 :
13503 : /************************************************************************/
13504 : /* OSRSetCoordinateEpoch() */
13505 : /************************************************************************/
13506 :
13507 : /** \brief Set the coordinate epoch, as decimal year.
13508 : *
13509 : * See OGRSpatialReference::SetCoordinateEpoch()
13510 : *
13511 : * @since OGR 3.4
13512 : */
13513 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13514 : {
13515 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13516 :
13517 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13518 31 : dfCoordinateEpoch);
13519 : }
13520 :
13521 : /************************************************************************/
13522 : /* GetCoordinateEpoch() */
13523 : /************************************************************************/
13524 :
13525 : /** Return the coordinate epoch, as decimal year.
13526 : *
13527 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13528 : * change with time. To be unambiguous the coordinates must always be qualified
13529 : * with the epoch at which they are valid. The coordinate epoch is not
13530 : * necessarily the epoch at which the observation was collected.
13531 : *
13532 : * Pedantically the coordinate epoch of an observation belongs to the
13533 : * observation, and not to the CRS, however it is often more practical to
13534 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13535 : * CRS (see IsDynamic())
13536 : *
13537 : * This method is the same as the OSRGetCoordinateEpoch() function.
13538 : *
13539 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13540 : * if not set, or relevant.
13541 : * @since OGR 3.4
13542 : */
13543 :
13544 15126 : double OGRSpatialReference::GetCoordinateEpoch() const
13545 : {
13546 15126 : return d->m_coordinateEpoch;
13547 : }
13548 :
13549 : /************************************************************************/
13550 : /* OSRGetCoordinateEpoch() */
13551 : /************************************************************************/
13552 :
13553 : /** \brief Get the coordinate epoch, as decimal year.
13554 : *
13555 : * See OGRSpatialReference::GetCoordinateEpoch()
13556 : *
13557 : * @since OGR 3.4
13558 : */
13559 609 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13560 : {
13561 609 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13562 :
13563 609 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13564 : }
|