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 245160 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 245160 : }
73 :
74 : Listener(const Listener &) = delete;
75 : Listener &operator=(const Listener &) = delete;
76 :
77 : void notifyChange(OGR_SRSNode *) override;
78 : };
79 :
80 : OGRSpatialReference *m_poSelf = nullptr;
81 : PJ *m_pj_crs = nullptr;
82 :
83 : // Temporary state used for object construction
84 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85 : CPLString m_osPrimeMeridianName{};
86 : CPLString m_osAngularUnits{};
87 : CPLString m_osLinearUnits{};
88 : CPLString m_osAxisName[3]{};
89 :
90 : std::vector<std::string> m_wktImportWarnings{};
91 : std::vector<std::string> m_wktImportErrors{};
92 : CPLString m_osAreaName{};
93 : CPLString m_celestialBodyName{};
94 :
95 : bool m_bIsThreadSafe = false;
96 : bool m_bNodesChanged = false;
97 : bool m_bNodesWKT2 = false;
98 : OGR_SRSNode *m_poRoot = nullptr;
99 :
100 : double dfFromGreenwich = 0.0;
101 : double dfToMeter = 0.0;
102 : double dfToDegrees = 0.0;
103 : double m_dfAngularUnitToRadian = 0.0;
104 :
105 : int nRefCount = 1;
106 : int bNormInfoSet = FALSE;
107 :
108 : PJ *m_pj_geod_base_crs_temp = nullptr;
109 : PJ *m_pj_proj_crs_cs_temp = nullptr;
110 :
111 : bool m_pj_crs_modified_during_demote = false;
112 : PJ *m_pj_bound_crs_target = nullptr;
113 : PJ *m_pj_bound_crs_co = nullptr;
114 : PJ *m_pj_crs_backup = nullptr;
115 : OGR_SRSNode *m_poRootBackup = nullptr;
116 :
117 : bool m_bMorphToESRI = false;
118 : bool m_bHasCenterLong = false;
119 :
120 : std::shared_ptr<Listener> m_poListener{};
121 :
122 : std::recursive_mutex m_mutex{};
123 :
124 : OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125 : std::vector<int> m_axisMapping{1, 2, 3};
126 :
127 : double m_coordinateEpoch = 0; // as decimal year
128 :
129 : explicit Private(OGRSpatialReference *poSelf);
130 : ~Private();
131 : Private(const Private &) = delete;
132 : Private &operator=(const Private &) = delete;
133 :
134 2 : void SetThreadSafe()
135 : {
136 2 : m_bIsThreadSafe = true;
137 2 : }
138 :
139 : void clear();
140 : void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141 : void setRoot(OGR_SRSNode *poRoot);
142 : void refreshProjObj();
143 : void nodesChanged();
144 : void refreshRootFromProjObj();
145 : void invalidateNodes();
146 :
147 : void setMorphToESRI(bool b);
148 :
149 : PJ *getGeodBaseCRS();
150 : PJ *getProjCRSCoordSys();
151 :
152 : const char *getProjCRSName();
153 : OGRErr replaceConversionAndUnref(PJ *conv);
154 :
155 : void demoteFromBoundCRS();
156 : void undoDemoteFromBoundCRS();
157 :
158 1222100 : PJ_CONTEXT *getPROJContext()
159 : {
160 1222100 : 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 18335400 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 18335400 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 18335400 : }
184 :
185 18335400 : ~OptionalLockGuard()
186 18335400 : {
187 18335400 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 18335400 : }
190 : };
191 :
192 18335400 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 18335400 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2320120 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2320120 : m_poObj->nodesChanged();
201 2320120 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 245160 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 245160 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 245160 : 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 245159 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 245160 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 245160 : : 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 245160 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
233 245160 : }
234 :
235 975072 : 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 243768 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
241 : PJ_CONTEXT *ctxt;
242 243768 : if (GDALThreadLocalDatasetCacheIsInDestruction())
243 : {
244 185 : pj_context_to_destroy = proj_context_create();
245 185 : ctxt = pj_context_to_destroy;
246 : }
247 : else
248 : {
249 243583 : ctxt = getPROJContext();
250 : }
251 :
252 243768 : proj_assign_context(m_pj_crs, ctxt);
253 243768 : proj_destroy(m_pj_crs);
254 :
255 243768 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
256 243768 : proj_destroy(m_pj_geod_base_crs_temp);
257 :
258 243768 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
259 243768 : proj_destroy(m_pj_proj_crs_cs_temp);
260 :
261 243768 : proj_assign_context(m_pj_bound_crs_target, ctxt);
262 243768 : proj_destroy(m_pj_bound_crs_target);
263 :
264 243768 : proj_assign_context(m_pj_bound_crs_co, ctxt);
265 243768 : proj_destroy(m_pj_bound_crs_co);
266 :
267 243768 : proj_assign_context(m_pj_crs_backup, ctxt);
268 243768 : proj_destroy(m_pj_crs_backup);
269 :
270 243768 : delete m_poRootBackup;
271 243768 : delete m_poRoot;
272 243768 : proj_context_destroy(pj_context_to_destroy);
273 243768 : }
274 :
275 127024 : void OGRSpatialReference::Private::clear()
276 : {
277 127024 : proj_assign_context(m_pj_crs, getPROJContext());
278 127024 : proj_destroy(m_pj_crs);
279 127024 : m_pj_crs = nullptr;
280 :
281 127024 : delete m_poRoot;
282 127024 : m_poRoot = nullptr;
283 127024 : m_bNodesChanged = false;
284 :
285 127024 : m_wktImportWarnings.clear();
286 127024 : m_wktImportErrors.clear();
287 :
288 127024 : m_pj_crs_modified_during_demote = false;
289 127024 : m_pjType = PJ_TYPE_UNKNOWN;
290 127024 : m_osPrimeMeridianName.clear();
291 127024 : m_osAngularUnits.clear();
292 127024 : m_osLinearUnits.clear();
293 :
294 127024 : bNormInfoSet = FALSE;
295 127024 : dfFromGreenwich = 1.0;
296 127024 : dfToMeter = 1.0;
297 127024 : dfToDegrees = 1.0;
298 127024 : m_dfAngularUnitToRadian = 0.0;
299 :
300 127024 : m_bMorphToESRI = false;
301 127024 : m_bHasCenterLong = false;
302 :
303 127024 : m_coordinateEpoch = 0.0;
304 127024 : }
305 :
306 29004 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
307 : {
308 29004 : m_poRoot = poRoot;
309 29004 : if (m_poRoot)
310 : {
311 29004 : m_poRoot->RegisterListener(m_poListener);
312 : }
313 29004 : nodesChanged();
314 29004 : }
315 :
316 192801 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
317 : bool doRefreshAxisMapping)
318 : {
319 192801 : 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 192801 : proj_assign_context(m_pj_crs, ctxt);
337 192801 : proj_destroy(m_pj_crs);
338 192801 : m_pj_crs = pj_crsIn;
339 192801 : if (m_pj_crs)
340 : {
341 192750 : m_pjType = proj_get_type(m_pj_crs);
342 : }
343 192801 : if (m_pj_crs_backup)
344 : {
345 21 : m_pj_crs_modified_during_demote = true;
346 : }
347 192801 : invalidateNodes();
348 192801 : if (doRefreshAxisMapping)
349 : {
350 192781 : refreshAxisMapping();
351 : }
352 192801 : }
353 :
354 782281 : void OGRSpatialReference::Private::refreshProjObj()
355 : {
356 782281 : if (m_bNodesChanged && m_poRoot)
357 : {
358 8685 : char *pszWKT = nullptr;
359 8685 : m_poRoot->exportToWkt(&pszWKT);
360 8685 : auto poRootBackup = m_poRoot;
361 8685 : m_poRoot = nullptr;
362 8685 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
363 8685 : clear();
364 8685 : m_coordinateEpoch = dfCoordinateEpochBackup;
365 8685 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
366 :
367 8685 : 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 8685 : PROJ_STRING_LIST warnings = nullptr;
374 8685 : PROJ_STRING_LIST errors = nullptr;
375 8685 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
376 : &warnings, &errors));
377 16973 : for (auto iter = warnings; iter && *iter; ++iter)
378 : {
379 8288 : m_wktImportWarnings.push_back(*iter);
380 : }
381 8900 : for (auto iter = errors; iter && *iter; ++iter)
382 : {
383 215 : m_wktImportErrors.push_back(*iter);
384 : }
385 8685 : proj_string_list_destroy(warnings);
386 8685 : proj_string_list_destroy(errors);
387 :
388 8685 : CPLFree(pszWKT);
389 :
390 8685 : m_poRoot = poRootBackup;
391 8685 : m_bNodesChanged = false;
392 : }
393 782281 : }
394 :
395 31131 : void OGRSpatialReference::Private::refreshRootFromProjObj()
396 : {
397 31131 : CPLAssert(m_poRoot == nullptr);
398 :
399 31131 : if (m_pj_crs)
400 : {
401 57920 : CPLStringList aosOptions;
402 28960 : if (!m_bMorphToESRI)
403 : {
404 28956 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
405 28956 : aosOptions.SetNameValue("MULTILINE", "NO");
406 : }
407 28960 : aosOptions.SetNameValue("STRICT", "NO");
408 :
409 : const char *pszWKT;
410 : {
411 28960 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
412 28960 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
413 28960 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
414 28960 : aosOptions.List());
415 28960 : m_bNodesWKT2 = false;
416 : }
417 28960 : if (!m_bMorphToESRI && pszWKT == nullptr)
418 : {
419 96 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
420 96 : aosOptions.List());
421 96 : m_bNodesWKT2 = true;
422 : }
423 28960 : if (pszWKT)
424 : {
425 28960 : auto root = new OGR_SRSNode();
426 28960 : setRoot(root);
427 28960 : root->importFromWkt(&pszWKT);
428 28960 : m_bNodesChanged = false;
429 : }
430 : }
431 31131 : }
432 :
433 223311 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
434 : {
435 223311 : const char *pszName1 = nullptr;
436 223311 : const char *pszDirection1 = nullptr;
437 223311 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
438 : nullptr, nullptr, nullptr, nullptr);
439 223311 : const char *pszName2 = nullptr;
440 223311 : const char *pszDirection2 = nullptr;
441 223311 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
442 : nullptr, nullptr, nullptr, nullptr);
443 223311 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
444 99597 : EQUAL(pszDirection2, "east"))
445 : {
446 98170 : return true;
447 : }
448 125141 : if (pszDirection1 && pszDirection2 &&
449 125141 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
450 123731 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
451 2945 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
452 1168 : STARTS_WITH_CI(pszName2, "easting"))
453 : {
454 1168 : return true;
455 : }
456 123973 : return false;
457 : }
458 :
459 283060 : void OGRSpatialReference::Private::refreshAxisMapping()
460 : {
461 283060 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
462 60342 : return;
463 :
464 222718 : bool doUndoDemote = false;
465 222718 : if (m_pj_crs_backup == nullptr)
466 : {
467 222697 : doUndoDemote = true;
468 222697 : demoteFromBoundCRS();
469 : }
470 222718 : const auto ctxt = getPROJContext();
471 222718 : PJ *horizCRS = nullptr;
472 222718 : int axisCount = 0;
473 222718 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
474 : {
475 218 : axisCount = 1;
476 : }
477 222500 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
478 : {
479 1107 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
480 1107 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
481 : {
482 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
483 222 : if (baseCRS)
484 : {
485 222 : proj_destroy(horizCRS);
486 222 : horizCRS = baseCRS;
487 : }
488 : }
489 :
490 1107 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
491 1107 : if (vertCRS)
492 : {
493 1104 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
494 : {
495 398 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
496 398 : if (baseCRS)
497 : {
498 398 : proj_destroy(vertCRS);
499 398 : vertCRS = baseCRS;
500 : }
501 : }
502 :
503 1104 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
504 1104 : if (cs)
505 : {
506 1104 : axisCount += proj_cs_get_axis_count(ctxt, cs);
507 1104 : proj_destroy(cs);
508 : }
509 1104 : proj_destroy(vertCRS);
510 : }
511 : }
512 : else
513 : {
514 221393 : horizCRS = m_pj_crs;
515 : }
516 :
517 222718 : bool bSwitchForGisFriendlyOrder = false;
518 222718 : if (horizCRS)
519 : {
520 222497 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
521 222497 : if (cs)
522 : {
523 222497 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
524 222497 : axisCount += nHorizCSAxisCount;
525 222497 : if (nHorizCSAxisCount >= 2)
526 : {
527 222487 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
528 : }
529 222497 : proj_destroy(cs);
530 : }
531 : }
532 222718 : if (horizCRS != m_pj_crs)
533 : {
534 1325 : proj_destroy(horizCRS);
535 : }
536 222718 : if (doUndoDemote)
537 : {
538 222697 : undoDemoteFromBoundCRS();
539 : }
540 :
541 222718 : m_axisMapping.resize(axisCount);
542 222718 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
543 65311 : !bSwitchForGisFriendlyOrder)
544 : {
545 580206 : for (int i = 0; i < axisCount; i++)
546 : {
547 387318 : m_axisMapping[i] = i + 1;
548 192888 : }
549 : }
550 : else
551 : {
552 29830 : m_axisMapping[0] = 2;
553 29830 : m_axisMapping[1] = 1;
554 29830 : if (axisCount == 3)
555 : {
556 343 : m_axisMapping[2] = 3;
557 : }
558 : }
559 : }
560 :
561 2349130 : void OGRSpatialReference::Private::nodesChanged()
562 : {
563 2349130 : m_bNodesChanged = true;
564 2349130 : }
565 :
566 193057 : void OGRSpatialReference::Private::invalidateNodes()
567 : {
568 193057 : delete m_poRoot;
569 193057 : m_poRoot = nullptr;
570 193057 : m_bNodesChanged = false;
571 193057 : }
572 :
573 256 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
574 : {
575 256 : invalidateNodes();
576 256 : m_bMorphToESRI = b;
577 256 : }
578 :
579 658437 : void OGRSpatialReference::Private::demoteFromBoundCRS()
580 : {
581 658437 : CPLAssert(m_pj_bound_crs_target == nullptr);
582 658437 : CPLAssert(m_pj_bound_crs_co == nullptr);
583 658437 : CPLAssert(m_poRootBackup == nullptr);
584 658437 : CPLAssert(m_pj_crs_backup == nullptr);
585 :
586 658437 : m_pj_crs_modified_during_demote = false;
587 :
588 658437 : if (m_pjType == PJ_TYPE_BOUND_CRS)
589 : {
590 2768 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
591 2768 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
592 2768 : m_pj_bound_crs_co =
593 2768 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
594 :
595 2768 : m_poRootBackup = m_poRoot;
596 2768 : m_poRoot = nullptr;
597 2768 : m_pj_crs_backup = m_pj_crs;
598 2768 : m_pj_crs = baseCRS;
599 2768 : m_pjType = proj_get_type(m_pj_crs);
600 : }
601 658437 : }
602 :
603 658437 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
604 : {
605 658437 : if (m_pj_bound_crs_target)
606 : {
607 2768 : CPLAssert(m_poRoot == nullptr);
608 2768 : CPLAssert(m_pj_crs);
609 2768 : if (!m_pj_crs_modified_during_demote)
610 : {
611 2748 : proj_destroy(m_pj_crs);
612 2748 : m_pj_crs = m_pj_crs_backup;
613 2748 : m_pjType = proj_get_type(m_pj_crs);
614 2748 : m_poRoot = m_poRootBackup;
615 : }
616 : else
617 : {
618 20 : delete m_poRootBackup;
619 20 : m_poRootBackup = nullptr;
620 20 : proj_destroy(m_pj_crs_backup);
621 20 : m_pj_crs_backup = nullptr;
622 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
623 20 : m_pj_bound_crs_target,
624 20 : m_pj_bound_crs_co),
625 : false);
626 : }
627 : }
628 :
629 658437 : m_poRootBackup = nullptr;
630 658437 : m_pj_crs_backup = nullptr;
631 658437 : proj_destroy(m_pj_bound_crs_target);
632 658437 : m_pj_bound_crs_target = nullptr;
633 658437 : proj_destroy(m_pj_bound_crs_co);
634 658437 : m_pj_bound_crs_co = nullptr;
635 658437 : m_pj_crs_modified_during_demote = false;
636 658437 : }
637 :
638 166662 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
639 : const char *pszTargetKey)
640 : {
641 166662 : if (pszTargetKey)
642 : {
643 60866 : demoteFromBoundCRS();
644 60866 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
645 31794 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
646 29150 : EQUAL(pszTargetKey, "GEOGCS"))
647 : {
648 7171 : pszTargetKey = nullptr;
649 : }
650 53695 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
651 20 : EQUAL(pszTargetKey, "GEOCCS"))
652 : {
653 0 : pszTargetKey = nullptr;
654 : }
655 53695 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
656 30405 : EQUAL(pszTargetKey, "PROJCS"))
657 : {
658 4479 : pszTargetKey = nullptr;
659 : }
660 49216 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
661 4 : EQUAL(pszTargetKey, "VERT_CS"))
662 : {
663 2 : pszTargetKey = nullptr;
664 : }
665 60866 : undoDemoteFromBoundCRS();
666 : }
667 166662 : return pszTargetKey;
668 : }
669 :
670 9704 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
671 : {
672 9704 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
673 9651 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
674 : {
675 53 : return m_pj_crs;
676 : }
677 :
678 9651 : auto ctxt = getPROJContext();
679 9651 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
680 : {
681 4446 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
682 4446 : proj_destroy(m_pj_geod_base_crs_temp);
683 4446 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
684 4446 : return m_pj_geod_base_crs_temp;
685 : }
686 :
687 5205 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
688 5205 : proj_destroy(m_pj_geod_base_crs_temp);
689 5205 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
690 : nullptr, 0);
691 5205 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
692 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
693 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
694 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
695 5205 : proj_destroy(cs);
696 :
697 5205 : return m_pj_geod_base_crs_temp;
698 : }
699 :
700 5406 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
701 : {
702 5406 : auto ctxt = getPROJContext();
703 5406 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
704 : {
705 4432 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
706 4432 : proj_destroy(m_pj_proj_crs_cs_temp);
707 4432 : m_pj_proj_crs_cs_temp =
708 4432 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
709 4432 : return m_pj_proj_crs_cs_temp;
710 : }
711 :
712 974 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
713 974 : proj_destroy(m_pj_proj_crs_cs_temp);
714 974 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
715 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
716 974 : return m_pj_proj_crs_cs_temp;
717 : }
718 :
719 5457 : const char *OGRSpatialReference::Private::getProjCRSName()
720 : {
721 5457 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
722 : {
723 4447 : return proj_get_name(m_pj_crs);
724 : }
725 :
726 1010 : return "unnamed";
727 : }
728 :
729 1381 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
730 : {
731 1381 : refreshProjObj();
732 :
733 1381 : demoteFromBoundCRS();
734 :
735 : auto projCRS =
736 1381 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
737 1381 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
738 1381 : proj_destroy(conv);
739 :
740 1381 : setPjCRS(projCRS);
741 :
742 1381 : undoDemoteFromBoundCRS();
743 1381 : return OGRERR_NONE;
744 : }
745 :
746 : /************************************************************************/
747 : /* ToPointer() */
748 : /************************************************************************/
749 :
750 27188 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
751 : {
752 27188 : return OGRSpatialReference::FromHandle(hSRS);
753 : }
754 :
755 : /************************************************************************/
756 : /* ToHandle() */
757 : /************************************************************************/
758 :
759 4824 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
760 : {
761 4824 : return OGRSpatialReference::ToHandle(poSRS);
762 : }
763 :
764 : /************************************************************************/
765 : /* OGRsnPrintDouble() */
766 : /************************************************************************/
767 :
768 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
769 :
770 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
771 :
772 : {
773 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
774 :
775 126 : const size_t nLen = strlen(pszStrBuf);
776 :
777 : // The following hack is intended to truncate some "precision" in cases
778 : // that appear to be roundoff error.
779 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
780 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
781 : {
782 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
783 : }
784 :
785 : // Force to user periods regardless of locale.
786 126 : if (strchr(pszStrBuf, ',') != nullptr)
787 : {
788 0 : char *const pszDelim = strchr(pszStrBuf, ',');
789 0 : *pszDelim = '.';
790 : }
791 126 : }
792 :
793 : /************************************************************************/
794 : /* OGRSpatialReference() */
795 : /************************************************************************/
796 :
797 : /**
798 : * \brief Constructor.
799 : *
800 : * This constructor takes an optional string argument which if passed
801 : * should be a WKT representation of an SRS. Passing this is equivalent
802 : * to not passing it, and then calling importFromWkt() with the WKT string.
803 : *
804 : * Note that newly created objects are given a reference count of one.
805 : *
806 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
807 : * object are assumed to be in the order of the axis of the CRS definition
808 : (which
809 : * for example means latitude first, longitude second for geographic CRS
810 : belonging
811 : * to the EPSG authority). It is possible to define a data axis to CRS axis
812 : * mapping strategy with the SetAxisMappingStrategy() method.
813 : *
814 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
815 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
816 : later
817 : * being the default value when the option is not set) to control the value of
818 : the
819 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
820 : * created. Calling SetAxisMappingStrategy() will override this default value.
821 :
822 : * The C function OSRNewSpatialReference() does the same thing as this
823 : * constructor.
824 : *
825 : * @param pszWKT well known text definition to which the object should
826 : * be initialized, or NULL (the default).
827 : */
828 :
829 242557 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
830 242557 : : d(new Private(this))
831 : {
832 242557 : if (pszWKT != nullptr)
833 281 : importFromWkt(pszWKT);
834 242557 : }
835 :
836 : /************************************************************************/
837 : /* OSRNewSpatialReference() */
838 : /************************************************************************/
839 :
840 : /**
841 : * \brief Constructor.
842 : *
843 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
844 : * object are assumed to be in the order of the axis of the CRS definition
845 : * (which for example means latitude first, longitude second for geographic CRS
846 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
847 : * axis mapping strategy with the SetAxisMappingStrategy() method.
848 : *
849 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
850 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
851 : * later being the default value when the option is not set) to control the
852 : * value of the data axis to CRS axis mapping strategy when a
853 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
854 : * override this default value.
855 : *
856 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
857 : */
858 3369 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
859 :
860 : {
861 3369 : OGRSpatialReference *poSRS = new OGRSpatialReference();
862 :
863 3369 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
864 : {
865 65 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
866 : {
867 0 : delete poSRS;
868 0 : poSRS = nullptr;
869 : }
870 : }
871 :
872 3369 : return ToHandle(poSRS);
873 : }
874 :
875 : /************************************************************************/
876 : /* OGRSpatialReference() */
877 : /************************************************************************/
878 :
879 : /** Copy constructor. See also Clone().
880 : * @param oOther other spatial reference
881 : */
882 2603 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
883 2603 : : d(new Private(this))
884 : {
885 2603 : *this = oOther;
886 2603 : }
887 :
888 : /************************************************************************/
889 : /* OGRSpatialReference() */
890 : /************************************************************************/
891 :
892 : /** Move constructor.
893 : * @param oOther other spatial reference
894 : */
895 29 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
896 29 : : d(std::move(oOther.d))
897 : {
898 29 : }
899 :
900 : /************************************************************************/
901 : /* ~OGRSpatialReference() */
902 : /************************************************************************/
903 :
904 : /**
905 : * \brief OGRSpatialReference destructor.
906 : *
907 : * The C function OSRDestroySpatialReference() does the same thing as this
908 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
909 : *
910 : * @deprecated
911 : */
912 :
913 304204 : OGRSpatialReference::~OGRSpatialReference()
914 :
915 : {
916 304204 : }
917 :
918 : /************************************************************************/
919 : /* DestroySpatialReference() */
920 : /************************************************************************/
921 :
922 : /**
923 : * \brief OGRSpatialReference destructor.
924 : *
925 : * This static method will destroy a OGRSpatialReference. It is
926 : * equivalent to calling delete on the object, but it ensures that the
927 : * deallocation is properly executed within the OGR libraries heap on
928 : * platforms where this can matter (win32).
929 : *
930 : * This function is the same as OSRDestroySpatialReference()
931 : *
932 : * @param poSRS the object to delete
933 : *
934 : */
935 :
936 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
937 : {
938 0 : delete poSRS;
939 0 : }
940 :
941 : /************************************************************************/
942 : /* OSRDestroySpatialReference() */
943 : /************************************************************************/
944 :
945 : /**
946 : * \brief OGRSpatialReference destructor.
947 : *
948 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
949 : * and OGRSpatialReference::DestroySpatialReference()
950 : *
951 : * @param hSRS the object to delete
952 : */
953 9559 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
954 :
955 : {
956 9559 : delete ToPointer(hSRS);
957 9559 : }
958 :
959 : /************************************************************************/
960 : /* Clear() */
961 : /************************************************************************/
962 :
963 : /**
964 : * \brief Wipe current definition.
965 : *
966 : * Returns OGRSpatialReference to a state with no definition, as it
967 : * exists when first created. It does not affect reference counts.
968 : */
969 :
970 118339 : void OGRSpatialReference::Clear()
971 :
972 : {
973 118339 : d->clear();
974 118339 : }
975 :
976 : /************************************************************************/
977 : /* operator=() */
978 : /************************************************************************/
979 :
980 : /** Assignment operator.
981 : * @param oSource SRS to assign to *this
982 : * @return *this
983 : */
984 : OGRSpatialReference &
985 27536 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
986 :
987 : {
988 27536 : if (&oSource != this)
989 : {
990 27535 : Clear();
991 : #ifdef CPPCHECK
992 : // Otherwise cppcheck would protest that nRefCount isn't modified
993 : d->nRefCount = (d->nRefCount + 1) - 1;
994 : #endif
995 :
996 27535 : oSource.d->refreshProjObj();
997 27535 : if (oSource.d->m_pj_crs)
998 27090 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
999 27535 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1000 12784 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1001 14751 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1002 124 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1003 :
1004 27535 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1005 : }
1006 :
1007 27536 : return *this;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* operator=() */
1012 : /************************************************************************/
1013 :
1014 : /** Move assignment operator.
1015 : * @param oSource SRS to assign to *this
1016 : * @return *this
1017 : */
1018 : OGRSpatialReference &
1019 4770 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1020 :
1021 : {
1022 4770 : if (&oSource != this)
1023 : {
1024 4769 : d = std::move(oSource.d);
1025 : }
1026 :
1027 4770 : return *this;
1028 : }
1029 :
1030 : /************************************************************************/
1031 : /* AssignAndSetThreadSafe() */
1032 : /************************************************************************/
1033 :
1034 : /** Assignment method, with thread-safety.
1035 : *
1036 : * Same as an assignment operator, but asking also that the *this instance
1037 : * becomes thread-safe.
1038 : *
1039 : * @param oSource SRS to assign to *this
1040 : * @return *this
1041 : * @since 3.10
1042 : */
1043 :
1044 : OGRSpatialReference &
1045 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1046 : {
1047 2 : *this = oSource;
1048 2 : d->SetThreadSafe();
1049 2 : return *this;
1050 : }
1051 :
1052 : /************************************************************************/
1053 : /* Reference() */
1054 : /************************************************************************/
1055 :
1056 : /**
1057 : * \brief Increments the reference count by one.
1058 : *
1059 : * The reference count is used keep track of the number of OGRGeometry objects
1060 : * referencing this SRS.
1061 : *
1062 : * The method does the same thing as the C function OSRReference().
1063 : *
1064 : * @return the updated reference count.
1065 : */
1066 :
1067 3628910 : int OGRSpatialReference::Reference()
1068 :
1069 : {
1070 3628910 : return CPLAtomicInc(&d->nRefCount);
1071 : }
1072 :
1073 : /************************************************************************/
1074 : /* OSRReference() */
1075 : /************************************************************************/
1076 :
1077 : /**
1078 : * \brief Increments the reference count by one.
1079 : *
1080 : * This function is the same as OGRSpatialReference::Reference()
1081 : */
1082 1009 : int OSRReference(OGRSpatialReferenceH hSRS)
1083 :
1084 : {
1085 1009 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1086 :
1087 1009 : return ToPointer(hSRS)->Reference();
1088 : }
1089 :
1090 : /************************************************************************/
1091 : /* Dereference() */
1092 : /************************************************************************/
1093 :
1094 : /**
1095 : * \brief Decrements the reference count by one.
1096 : *
1097 : * The method does the same thing as the C function OSRDereference().
1098 : *
1099 : * @return the updated reference count.
1100 : */
1101 :
1102 3672730 : int OGRSpatialReference::Dereference()
1103 :
1104 : {
1105 3672730 : if (d->nRefCount <= 0)
1106 0 : CPLDebug("OSR",
1107 : "Dereference() called on an object with refcount %d,"
1108 : "likely already destroyed!",
1109 0 : d->nRefCount);
1110 3672730 : return CPLAtomicDec(&d->nRefCount);
1111 : }
1112 :
1113 : /************************************************************************/
1114 : /* OSRDereference() */
1115 : /************************************************************************/
1116 :
1117 : /**
1118 : * \brief Decrements the reference count by one.
1119 : *
1120 : * This function is the same as OGRSpatialReference::Dereference()
1121 : */
1122 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1123 :
1124 : {
1125 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1126 :
1127 0 : return ToPointer(hSRS)->Dereference();
1128 : }
1129 :
1130 : /************************************************************************/
1131 : /* GetReferenceCount() */
1132 : /************************************************************************/
1133 :
1134 : /**
1135 : * \brief Fetch current reference count.
1136 : *
1137 : * @return the current reference count.
1138 : */
1139 180 : int OGRSpatialReference::GetReferenceCount() const
1140 : {
1141 180 : return d->nRefCount;
1142 : }
1143 :
1144 : /************************************************************************/
1145 : /* Release() */
1146 : /************************************************************************/
1147 :
1148 : /**
1149 : * \brief Decrements the reference count by one, and destroy if zero.
1150 : *
1151 : * The method does the same thing as the C function OSRRelease().
1152 : */
1153 :
1154 3669950 : void OGRSpatialReference::Release()
1155 :
1156 : {
1157 3669950 : if (Dereference() <= 0)
1158 43757 : delete this;
1159 3669950 : }
1160 :
1161 : /************************************************************************/
1162 : /* OSRRelease() */
1163 : /************************************************************************/
1164 :
1165 : /**
1166 : * \brief Decrements the reference count by one, and destroy if zero.
1167 : *
1168 : * This function is the same as OGRSpatialReference::Release()
1169 : */
1170 6760 : void OSRRelease(OGRSpatialReferenceH hSRS)
1171 :
1172 : {
1173 6760 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1174 :
1175 6760 : ToPointer(hSRS)->Release();
1176 : }
1177 :
1178 92911 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1179 : {
1180 92911 : TAKE_OPTIONAL_LOCK();
1181 :
1182 92911 : if (!d->m_poRoot)
1183 : {
1184 27999 : d->refreshRootFromProjObj();
1185 : }
1186 185822 : return d->m_poRoot;
1187 : }
1188 :
1189 8461 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1190 : {
1191 8461 : TAKE_OPTIONAL_LOCK();
1192 :
1193 8461 : if (!d->m_poRoot)
1194 : {
1195 3132 : d->refreshRootFromProjObj();
1196 : }
1197 16922 : return d->m_poRoot;
1198 : }
1199 :
1200 : /************************************************************************/
1201 : /* SetRoot() */
1202 : /************************************************************************/
1203 :
1204 : /**
1205 : * \brief Set the root SRS node.
1206 : *
1207 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1208 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1209 : * is assumed by the OGRSpatialReference.
1210 : *
1211 : * @param poNewRoot object to assign as root.
1212 : */
1213 :
1214 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1215 :
1216 : {
1217 44 : if (d->m_poRoot != poNewRoot)
1218 : {
1219 44 : delete d->m_poRoot;
1220 44 : d->setRoot(poNewRoot);
1221 : }
1222 44 : }
1223 :
1224 : /************************************************************************/
1225 : /* GetAttrNode() */
1226 : /************************************************************************/
1227 :
1228 : /**
1229 : * \brief Find named node in tree.
1230 : *
1231 : * This method does a pre-order traversal of the node tree searching for
1232 : * a node with this exact value (case insensitive), and returns it. Leaf
1233 : * nodes are not considered, under the assumption that they are just
1234 : * attribute value nodes.
1235 : *
1236 : * If a node appears more than once in the tree (such as UNIT for instance),
1237 : * the first encountered will be returned. Use GetNode() on a subtree to be
1238 : * more specific.
1239 : *
1240 : * @param pszNodePath the name of the node to search for. May contain multiple
1241 : * components such as "GEOGCS|UNIT".
1242 : *
1243 : * @return a pointer to the node found, or NULL if none.
1244 : */
1245 :
1246 89779 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1247 :
1248 : {
1249 89779 : if (strchr(pszNodePath, '|') == nullptr)
1250 : {
1251 : // Fast path
1252 49765 : OGR_SRSNode *poNode = GetRoot();
1253 49765 : if (poNode)
1254 48558 : poNode = poNode->GetNode(pszNodePath);
1255 49765 : return poNode;
1256 : }
1257 :
1258 : char **papszPathTokens =
1259 40014 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1260 :
1261 40014 : if (CSLCount(papszPathTokens) < 1)
1262 : {
1263 0 : CSLDestroy(papszPathTokens);
1264 0 : return nullptr;
1265 : }
1266 :
1267 40014 : OGR_SRSNode *poNode = GetRoot();
1268 121582 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1269 : {
1270 81568 : poNode = poNode->GetNode(papszPathTokens[i]);
1271 : }
1272 :
1273 40014 : CSLDestroy(papszPathTokens);
1274 :
1275 40014 : return poNode;
1276 : }
1277 :
1278 : /**
1279 : * \brief Find named node in tree.
1280 : *
1281 : * This method does a pre-order traversal of the node tree searching for
1282 : * a node with this exact value (case insensitive), and returns it. Leaf
1283 : * nodes are not considered, under the assumption that they are just
1284 : * attribute value nodes.
1285 : *
1286 : * If a node appears more than once in the tree (such as UNIT for instance),
1287 : * the first encountered will be returned. Use GetNode() on a subtree to be
1288 : * more specific.
1289 : *
1290 : * @param pszNodePath the name of the node to search for. May contain multiple
1291 : * components such as "GEOGCS|UNIT".
1292 : *
1293 : * @return a pointer to the node found, or NULL if none.
1294 : */
1295 :
1296 : const OGR_SRSNode *
1297 81352 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1298 :
1299 : {
1300 : OGR_SRSNode *poNode =
1301 81352 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1302 :
1303 81352 : return poNode;
1304 : }
1305 :
1306 : /************************************************************************/
1307 : /* GetAttrValue() */
1308 : /************************************************************************/
1309 :
1310 : /**
1311 : * \brief Fetch indicated attribute of named node.
1312 : *
1313 : * This method uses GetAttrNode() to find the named node, and then extracts
1314 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1315 : * would return the second child of the UNIT node, which is normally the
1316 : * length of the linear unit in meters.
1317 : *
1318 : * This method does the same thing as the C function OSRGetAttrValue().
1319 : *
1320 : * @param pszNodeName the tree node to look for (case insensitive).
1321 : * @param iAttr the child of the node to fetch (zero based).
1322 : *
1323 : * @return the requested value, or NULL if it fails for any reason.
1324 : */
1325 :
1326 24428 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1327 : int iAttr) const
1328 :
1329 : {
1330 24428 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1331 24428 : if (poNode == nullptr)
1332 : {
1333 10495 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1334 : {
1335 14 : return GetAttrValue("METHOD", iAttr);
1336 : }
1337 10481 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1338 : {
1339 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1340 : }
1341 10481 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1342 : {
1343 1 : return GetAttrValue("PROJCRS", iAttr);
1344 : }
1345 10480 : return nullptr;
1346 : }
1347 :
1348 13933 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1349 0 : return nullptr;
1350 :
1351 13933 : return poNode->GetChild(iAttr)->GetValue();
1352 : }
1353 :
1354 : /************************************************************************/
1355 : /* OSRGetAttrValue() */
1356 : /************************************************************************/
1357 :
1358 : /**
1359 : * \brief Fetch indicated attribute of named node.
1360 : *
1361 : * This function is the same as OGRSpatialReference::GetAttrValue()
1362 : */
1363 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1364 : const char *pszKey, int iChild)
1365 :
1366 : {
1367 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1368 :
1369 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1370 : }
1371 :
1372 : /************************************************************************/
1373 : /* GetName() */
1374 : /************************************************************************/
1375 :
1376 : /**
1377 : * \brief Return the CRS name.
1378 : *
1379 : * The returned value is only short lived and should not be used after other
1380 : * calls to methods on this object.
1381 : *
1382 : * @since GDAL 3.0
1383 : */
1384 :
1385 8268 : const char *OGRSpatialReference::GetName() const
1386 : {
1387 16536 : TAKE_OPTIONAL_LOCK();
1388 :
1389 8268 : d->refreshProjObj();
1390 8268 : if (!d->m_pj_crs)
1391 113 : return nullptr;
1392 8155 : const char *pszName = proj_get_name(d->m_pj_crs);
1393 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1394 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1395 : {
1396 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1397 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1398 : if (baseCRS)
1399 : {
1400 : pszName = proj_get_name(baseCRS);
1401 : // pszName still remains valid after proj_destroy(), since
1402 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1403 : proj_destroy(baseCRS);
1404 : }
1405 : }
1406 : #endif
1407 8155 : return pszName;
1408 : }
1409 :
1410 : /************************************************************************/
1411 : /* OSRGetName() */
1412 : /************************************************************************/
1413 :
1414 : /**
1415 : * \brief Return the CRS name.
1416 : *
1417 : * The returned value is only short lived and should not be used after other
1418 : * calls to methods on this object.
1419 : *
1420 : * @since GDAL 3.0
1421 : */
1422 44 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1423 :
1424 : {
1425 44 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1426 :
1427 44 : return ToPointer(hSRS)->GetName();
1428 : }
1429 :
1430 : /************************************************************************/
1431 : /* GetCelestialBodyName() */
1432 : /************************************************************************/
1433 :
1434 : /**
1435 : * \brief Return the name of the celestial body of this CRS.
1436 : *
1437 : * e.g. "Earth" for an Earth CRS
1438 : *
1439 : * The returned value is only short lived and should not be used after other
1440 : * calls to methods on this object.
1441 : *
1442 : * @since GDAL 3.12 and PROJ 8.1
1443 : */
1444 :
1445 4 : const char *OGRSpatialReference::GetCelestialBodyName() const
1446 : {
1447 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1448 :
1449 : TAKE_OPTIONAL_LOCK();
1450 :
1451 : d->refreshProjObj();
1452 : if (!d->m_pj_crs)
1453 : return nullptr;
1454 : d->demoteFromBoundCRS();
1455 : const char *name =
1456 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1457 : if (name)
1458 : {
1459 : d->m_celestialBodyName = name;
1460 : }
1461 : d->undoDemoteFromBoundCRS();
1462 : return d->m_celestialBodyName.c_str();
1463 : #else
1464 4 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1465 : 0.05 * SRS_WGS84_SEMIMAJOR)
1466 4 : return "Earth";
1467 0 : const char *pszAuthName = GetAuthorityName(nullptr);
1468 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1469 0 : return "Earth";
1470 0 : return nullptr;
1471 : #endif
1472 : }
1473 :
1474 : /************************************************************************/
1475 : /* OSRGetCelestialBodyName() */
1476 : /************************************************************************/
1477 :
1478 : /**
1479 : * \brief Return the name of the celestial body of this CRS.
1480 : *
1481 : * e.g. "Earth" for an Earth CRS
1482 : *
1483 : * The returned value is only short lived and should not be used after other
1484 : * calls to methods on this object.
1485 : *
1486 : * @since GDAL 3.12 and PROJ 8.1
1487 : */
1488 :
1489 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1490 :
1491 : {
1492 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1493 :
1494 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1495 : }
1496 :
1497 : /************************************************************************/
1498 : /* Clone() */
1499 : /************************************************************************/
1500 :
1501 : /**
1502 : * \brief Make a duplicate of this OGRSpatialReference.
1503 : *
1504 : * This method is the same as the C function OSRClone().
1505 : *
1506 : * @return a new SRS, which becomes the responsibility of the caller.
1507 : */
1508 :
1509 35226 : OGRSpatialReference *OGRSpatialReference::Clone() const
1510 :
1511 : {
1512 35226 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1513 :
1514 35226 : TAKE_OPTIONAL_LOCK();
1515 :
1516 35226 : d->refreshProjObj();
1517 35226 : if (d->m_pj_crs != nullptr)
1518 35168 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1519 35226 : if (d->m_bHasCenterLong && d->m_poRoot)
1520 : {
1521 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1522 : }
1523 35226 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1524 35226 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1525 35226 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1526 70452 : return poNewRef;
1527 : }
1528 :
1529 : /************************************************************************/
1530 : /* OSRClone() */
1531 : /************************************************************************/
1532 :
1533 : /**
1534 : * \brief Make a duplicate of this OGRSpatialReference.
1535 : *
1536 : * This function is the same as OGRSpatialReference::Clone()
1537 : */
1538 1301 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1539 :
1540 : {
1541 1301 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1542 :
1543 1301 : return ToHandle(ToPointer(hSRS)->Clone());
1544 : }
1545 :
1546 : /************************************************************************/
1547 : /* dumpReadable() */
1548 : /************************************************************************/
1549 :
1550 : /** Dump pretty wkt to stdout, mostly for debugging.
1551 : */
1552 0 : void OGRSpatialReference::dumpReadable()
1553 :
1554 : {
1555 0 : char *pszPrettyWkt = nullptr;
1556 :
1557 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1558 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1559 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1560 0 : CPLFree(pszPrettyWkt);
1561 0 : }
1562 :
1563 : /************************************************************************/
1564 : /* exportToPrettyWkt() */
1565 : /************************************************************************/
1566 :
1567 : /**
1568 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1569 : * person.
1570 : *
1571 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1572 : * Issues</a> page for implementation details of WKT 1 in OGR.
1573 : *
1574 : * Note that the returned WKT string should be freed with
1575 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1576 : *
1577 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1578 : * option. Valid values are the one of the FORMAT option of
1579 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1580 : *
1581 : * This method is the same as the C function OSRExportToPrettyWkt().
1582 : *
1583 : * @param ppszResult the resulting string is returned in this pointer.
1584 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1585 : * stripped off.
1586 : *
1587 : * @return OGRERR_NONE if successful.
1588 : */
1589 :
1590 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1591 : int bSimplify) const
1592 :
1593 : {
1594 116 : CPLStringList aosOptions;
1595 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1596 58 : if (bSimplify)
1597 : {
1598 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1599 : }
1600 116 : return exportToWkt(ppszResult, aosOptions.List());
1601 : }
1602 :
1603 : /************************************************************************/
1604 : /* OSRExportToPrettyWkt() */
1605 : /************************************************************************/
1606 :
1607 : /**
1608 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1609 : * person.
1610 : *
1611 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1612 : * option. Valid values are the one of the FORMAT option of
1613 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1614 : *
1615 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1616 : */
1617 :
1618 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1619 : char **ppszReturn, int bSimplify)
1620 :
1621 : {
1622 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1623 :
1624 56 : *ppszReturn = nullptr;
1625 :
1626 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1627 : }
1628 :
1629 : /************************************************************************/
1630 : /* exportToWkt() */
1631 : /************************************************************************/
1632 :
1633 : /**
1634 : * \brief Convert this SRS into WKT 1 format.
1635 : *
1636 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1637 : * Issues</a> page for implementation details of WKT 1 in OGR.
1638 : *
1639 : * Note that the returned WKT string should be freed with
1640 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1641 : *
1642 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1643 : * option. Valid values are the one of the FORMAT option of
1644 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1645 : *
1646 : * This method is the same as the C function OSRExportToWkt().
1647 : *
1648 : * @param ppszResult the resulting string is returned in this pointer.
1649 : *
1650 : * @return OGRERR_NONE if successful.
1651 : */
1652 :
1653 12486 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1654 :
1655 : {
1656 12486 : return exportToWkt(ppszResult, nullptr);
1657 : }
1658 :
1659 : /************************************************************************/
1660 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1661 : /************************************************************************/
1662 :
1663 600 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1664 : bool onlyIfEPSGCode,
1665 : bool canModifyHorizPart)
1666 : {
1667 600 : PJ *ret = nullptr;
1668 600 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1669 : {
1670 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1671 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1672 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1673 26 : vertCRS &&
1674 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1675 : {
1676 : auto boundHoriz =
1677 : canModifyHorizPart
1678 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1679 3 : : proj_clone(ctx, horizCRS);
1680 : auto boundVert =
1681 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1682 3 : if (boundHoriz && boundVert)
1683 : {
1684 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1685 : boundHoriz, boundVert);
1686 : }
1687 3 : proj_destroy(boundHoriz);
1688 3 : proj_destroy(boundVert);
1689 : }
1690 13 : proj_destroy(horizCRS);
1691 13 : proj_destroy(vertCRS);
1692 : }
1693 1134 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1694 547 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1695 : {
1696 242 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1697 : }
1698 600 : return ret;
1699 : }
1700 :
1701 : /************************************************************************/
1702 : /* exportToWkt() */
1703 : /************************************************************************/
1704 :
1705 : /**
1706 : * Convert this SRS into a WKT string.
1707 : *
1708 : * Note that the returned WKT string should be freed with
1709 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1710 : *
1711 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1712 : * Issues</a> page for implementation details of WKT 1 in OGR.
1713 : *
1714 : * @param ppszResult the resulting string is returned in this pointer.
1715 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1716 : * supported options are
1717 : * <ul>
1718 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1719 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1720 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1721 : * node is returned.
1722 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1723 : * node is returned.
1724 : * WKT1 is an alias of WKT1_GDAL.
1725 : * WKT2 will default to the latest revision implemented (currently
1726 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1727 : * </li>
1728 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1729 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1730 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1731 : * height (for example for use with LAS 1.4 WKT1).
1732 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1733 : * </ul>
1734 : *
1735 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1736 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1737 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1738 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1739 : * TOWGS84[] node may be added.
1740 : *
1741 : * @return OGRERR_NONE if successful.
1742 : * @since GDAL 3.0
1743 : */
1744 :
1745 17339 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1746 : const char *const *papszOptions) const
1747 : {
1748 : // In the past calling this method was thread-safe, even if we never
1749 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1750 : // so this is no longer thread-safe.
1751 34678 : std::lock_guard oLock(d->m_mutex);
1752 :
1753 17339 : d->refreshProjObj();
1754 17339 : if (!d->m_pj_crs)
1755 : {
1756 21 : *ppszResult = CPLStrdup("");
1757 21 : return OGRERR_FAILURE;
1758 : }
1759 :
1760 17318 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1761 : {
1762 0 : return d->m_poRoot->exportToWkt(ppszResult);
1763 : }
1764 :
1765 17318 : auto ctxt = d->getPROJContext();
1766 17318 : auto wktFormat = PJ_WKT1_GDAL;
1767 : const char *pszFormat =
1768 17318 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1769 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1770 17318 : if (EQUAL(pszFormat, "DEFAULT"))
1771 14589 : pszFormat = "";
1772 :
1773 17318 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1774 : {
1775 650 : wktFormat = PJ_WKT1_ESRI;
1776 : }
1777 16668 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1778 15923 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1779 : {
1780 750 : wktFormat = PJ_WKT1_GDAL;
1781 : }
1782 15918 : else if (EQUAL(pszFormat, "WKT2_2015"))
1783 : {
1784 317 : wktFormat = PJ_WKT2_2015;
1785 : }
1786 15601 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1787 15229 : EQUAL(pszFormat, "WKT2_2019"))
1788 : {
1789 1241 : wktFormat = PJ_WKT2_2018;
1790 : }
1791 14360 : else if (pszFormat[0] == '\0')
1792 : {
1793 : // cppcheck-suppress knownConditionTrueFalse
1794 14360 : if (IsDerivedGeographic())
1795 : {
1796 2 : wktFormat = PJ_WKT2_2018;
1797 : }
1798 28083 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1799 13725 : GetAxesCount() == 3)
1800 : {
1801 56 : wktFormat = PJ_WKT2_2018;
1802 : }
1803 : }
1804 : else
1805 : {
1806 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1807 0 : *ppszResult = CPLStrdup("");
1808 0 : return OGRERR_FAILURE;
1809 : }
1810 :
1811 34636 : CPLStringList aosOptions;
1812 17318 : if (wktFormat != PJ_WKT1_ESRI)
1813 : {
1814 16668 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1815 : }
1816 : aosOptions.SetNameValue(
1817 17318 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1818 :
1819 17318 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1820 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1821 17318 : if (pszAllowEllpsHeightAsVertCS)
1822 : {
1823 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1824 0 : pszAllowEllpsHeightAsVertCS);
1825 : }
1826 :
1827 17318 : PJ *boundCRS = nullptr;
1828 32370 : if (wktFormat == PJ_WKT1_GDAL &&
1829 15052 : CPLTestBool(CSLFetchNameValueDef(
1830 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1831 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1832 : {
1833 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1834 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1835 : }
1836 :
1837 34636 : CPLErrorAccumulator oErrorAccumulator;
1838 : const char *pszWKT;
1839 : {
1840 17318 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1841 17318 : CPL_IGNORE_RET_VAL(oAccumulator);
1842 17318 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1843 17318 : aosOptions.List());
1844 : }
1845 17320 : for (const auto &oError : oErrorAccumulator.GetErrors())
1846 : {
1847 32 : if (pszFormat[0] == '\0' &&
1848 14 : (oError.msg.find("Unsupported conversion method") !=
1849 2 : std::string::npos ||
1850 2 : oError.msg.find("can only be exported to WKT2") !=
1851 0 : std::string::npos ||
1852 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1853 : std::string::npos))
1854 : {
1855 14 : CPLErrorReset();
1856 : // If we cannot export in the default mode (WKT1), retry with WKT2
1857 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1858 14 : PJ_WKT2_2018, aosOptions.List());
1859 14 : break;
1860 : }
1861 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1862 : }
1863 :
1864 17318 : if (!pszWKT)
1865 : {
1866 2 : *ppszResult = CPLStrdup("");
1867 2 : proj_destroy(boundCRS);
1868 2 : return OGRERR_FAILURE;
1869 : }
1870 :
1871 17316 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1872 : {
1873 5 : OGR_SRSNode oRoot;
1874 5 : oRoot.importFromWkt(&pszWKT);
1875 5 : oRoot.StripNodes("AXIS");
1876 5 : if (EQUAL(pszFormat, "SFSQL"))
1877 : {
1878 3 : oRoot.StripNodes("TOWGS84");
1879 : }
1880 5 : oRoot.StripNodes("AUTHORITY");
1881 5 : oRoot.StripNodes("EXTENSION");
1882 : OGRErr eErr;
1883 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1884 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1885 : else
1886 3 : eErr = oRoot.exportToWkt(ppszResult);
1887 5 : proj_destroy(boundCRS);
1888 5 : return eErr;
1889 : }
1890 :
1891 17311 : *ppszResult = CPLStrdup(pszWKT);
1892 :
1893 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1894 17311 : if (wktFormat == PJ_WKT2_2018)
1895 : {
1896 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1897 : // related to a wrong EPSG code assigned to UTM South conversions
1898 1299 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1899 1299 : if (pszPtr)
1900 : {
1901 318 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1902 318 : const int nZone = atoi(pszPtr);
1903 953 : while (*pszPtr >= '0' && *pszPtr <= '9')
1904 635 : ++pszPtr;
1905 318 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1906 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1907 : {
1908 1 : pszPtr += 3;
1909 1 : int nLevel = 0;
1910 1 : bool bInString = false;
1911 : // Find the ID node corresponding to this CONVERSION node
1912 480 : while (*pszPtr)
1913 : {
1914 480 : if (bInString)
1915 : {
1916 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1917 : {
1918 0 : ++pszPtr;
1919 : }
1920 197 : else if (*pszPtr == '"')
1921 : {
1922 17 : bInString = false;
1923 : }
1924 : }
1925 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1926 : {
1927 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1928 : 17000 + nZone)))
1929 : {
1930 1 : CPLAssert(pszPtr[11] == '7');
1931 1 : CPLAssert(pszPtr[12] == '0');
1932 1 : pszPtr[11] = '6';
1933 1 : pszPtr[12] = '1';
1934 : }
1935 1 : break;
1936 : }
1937 282 : else if (*pszPtr == '"')
1938 : {
1939 17 : bInString = true;
1940 : }
1941 265 : else if (*pszPtr == '[')
1942 : {
1943 17 : ++nLevel;
1944 : }
1945 248 : else if (*pszPtr == ']')
1946 : {
1947 17 : --nLevel;
1948 : }
1949 :
1950 479 : ++pszPtr;
1951 : }
1952 : }
1953 : }
1954 : }
1955 : #endif
1956 :
1957 17311 : proj_destroy(boundCRS);
1958 17311 : return OGRERR_NONE;
1959 : }
1960 :
1961 : /************************************************************************/
1962 : /* exportToWkt() */
1963 : /************************************************************************/
1964 :
1965 : /**
1966 : * Convert this SRS into a WKT string.
1967 : *
1968 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1969 : * Issues</a> page for implementation details of WKT 1 in OGR.
1970 : *
1971 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1972 : * supported options are
1973 : * <ul>
1974 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1975 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1976 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1977 : * node is returned.
1978 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1979 : * node is returned.
1980 : * WKT1 is an alias of WKT1_GDAL.
1981 : * WKT2 will default to the latest revision implemented (currently
1982 : * WKT2_2019)
1983 : * </li>
1984 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1985 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1986 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1987 : * height (for example for use with LAS 1.4 WKT1).
1988 : * Requires PROJ 7.2.1.</li>
1989 : * </ul>
1990 : *
1991 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1992 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1993 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1994 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1995 : * TOWGS84[] node may be added.
1996 : *
1997 : * @return a non-empty string if successful.
1998 : * @since GDAL 3.9
1999 : */
2000 :
2001 : std::string
2002 305 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2003 : {
2004 305 : std::string osWKT;
2005 305 : char *pszWKT = nullptr;
2006 305 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2007 305 : osWKT = pszWKT;
2008 305 : CPLFree(pszWKT);
2009 610 : return osWKT;
2010 : }
2011 :
2012 : /************************************************************************/
2013 : /* OSRExportToWkt() */
2014 : /************************************************************************/
2015 :
2016 : /**
2017 : * \brief Convert this SRS into WKT 1 format.
2018 : *
2019 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2020 : * Issues</a> page for implementation details of WKT in OGR.
2021 : *
2022 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2023 : * option. Valid values are the one of the FORMAT option of
2024 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2025 : *
2026 : * This function is the same as OGRSpatialReference::exportToWkt().
2027 : */
2028 :
2029 984 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2030 :
2031 : {
2032 984 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2033 :
2034 984 : *ppszReturn = nullptr;
2035 :
2036 984 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2037 : }
2038 :
2039 : /************************************************************************/
2040 : /* OSRExportToWktEx() */
2041 : /************************************************************************/
2042 :
2043 : /**
2044 : * \brief Convert this SRS into WKT format.
2045 : *
2046 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2047 : * ppszResult,const char* const* papszOptions ) const
2048 : *
2049 : * @since GDAL 3.0
2050 : */
2051 :
2052 1456 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2053 : const char *const *papszOptions)
2054 : {
2055 1456 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2056 :
2057 1456 : *ppszReturn = nullptr;
2058 :
2059 1456 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2060 : }
2061 :
2062 : /************************************************************************/
2063 : /* exportToPROJJSON() */
2064 : /************************************************************************/
2065 :
2066 : /**
2067 : * Convert this SRS into a PROJJSON string.
2068 : *
2069 : * Note that the returned JSON string should be freed with
2070 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2071 : *
2072 : * @param ppszResult the resulting string is returned in this pointer.
2073 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2074 : * supported options are
2075 : * <ul>
2076 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2077 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2078 : * on).</li>
2079 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2080 : * disable it.</li>
2081 : * </ul>
2082 : *
2083 : * @return OGRERR_NONE if successful.
2084 : * @since GDAL 3.1 and PROJ 6.2
2085 : */
2086 :
2087 2735 : OGRErr OGRSpatialReference::exportToPROJJSON(
2088 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2089 : {
2090 5470 : TAKE_OPTIONAL_LOCK();
2091 :
2092 2735 : d->refreshProjObj();
2093 2735 : if (!d->m_pj_crs)
2094 : {
2095 1 : *ppszResult = nullptr;
2096 1 : return OGRERR_FAILURE;
2097 : }
2098 :
2099 : const char *pszPROJJSON =
2100 2734 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2101 :
2102 2734 : if (!pszPROJJSON)
2103 : {
2104 0 : *ppszResult = CPLStrdup("");
2105 0 : return OGRERR_FAILURE;
2106 : }
2107 :
2108 2734 : *ppszResult = CPLStrdup(pszPROJJSON);
2109 :
2110 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2111 : {
2112 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2113 : // related to a wrong EPSG code assigned to UTM South conversions
2114 2734 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2115 2734 : if (pszPtr)
2116 : {
2117 257 : pszPtr += strlen("\"name\": \"UTM zone ");
2118 257 : const int nZone = atoi(pszPtr);
2119 770 : while (*pszPtr >= '0' && *pszPtr <= '9')
2120 513 : ++pszPtr;
2121 257 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2122 : {
2123 4 : pszPtr += 2;
2124 4 : int nLevel = 0;
2125 4 : bool bInString = false;
2126 : // Find the id node corresponding to this conversion node
2127 5299 : while (*pszPtr)
2128 : {
2129 5299 : if (bInString)
2130 : {
2131 1950 : if (*pszPtr == '\\')
2132 : {
2133 0 : ++pszPtr;
2134 : }
2135 1950 : else if (*pszPtr == '"')
2136 : {
2137 244 : bInString = false;
2138 : }
2139 : }
2140 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2141 : {
2142 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2143 : const char *pszAuthEPSG =
2144 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2145 4 : char *pszCode = strstr(
2146 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2147 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2148 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2149 4 : pszNextEndCurl - pszCode > 0)
2150 : {
2151 4 : CPLAssert(pszCode[9] == '7');
2152 4 : CPLAssert(pszCode[10] == '0');
2153 4 : pszCode[9] = '6';
2154 4 : pszCode[10] = '1';
2155 : }
2156 4 : break;
2157 : }
2158 3345 : else if (*pszPtr == '"')
2159 : {
2160 244 : bInString = true;
2161 : }
2162 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2163 : {
2164 60 : ++nLevel;
2165 : }
2166 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2167 : {
2168 60 : --nLevel;
2169 : }
2170 :
2171 5295 : ++pszPtr;
2172 : }
2173 : }
2174 : }
2175 : }
2176 : #endif
2177 :
2178 2734 : return OGRERR_NONE;
2179 : }
2180 :
2181 : /************************************************************************/
2182 : /* OSRExportToPROJJSON() */
2183 : /************************************************************************/
2184 :
2185 : /**
2186 : * \brief Convert this SRS into PROJJSON format.
2187 : *
2188 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2189 : *
2190 : * @since GDAL 3.1 and PROJ 6.2
2191 : */
2192 :
2193 82 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2194 : const char *const *papszOptions)
2195 : {
2196 82 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2197 :
2198 82 : *ppszReturn = nullptr;
2199 :
2200 82 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2201 : }
2202 :
2203 : /************************************************************************/
2204 : /* importFromWkt() */
2205 : /************************************************************************/
2206 :
2207 : /**
2208 : * \brief Import from WKT string.
2209 : *
2210 : * This method will wipe the existing SRS definition, and
2211 : * reassign it based on the contents of the passed WKT string. Only as
2212 : * much of the input string as needed to construct this SRS is consumed from
2213 : * the input string, and the input string pointer
2214 : * is then updated to point to the remaining (unused) input.
2215 : *
2216 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2217 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2218 : * and the coordinate epoch potentially present used as the coordinate epoch
2219 : * property of the OGRSpatialReference object.
2220 : *
2221 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2222 : * Issues</a> page for implementation details of WKT in OGR.
2223 : *
2224 : * This method is the same as the C function OSRImportFromWkt().
2225 : *
2226 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2227 : * point to remaining unused input text.
2228 : *
2229 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2230 : * fails for any reason.
2231 : */
2232 :
2233 18211 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2234 :
2235 : {
2236 18211 : return importFromWkt(ppszInput, nullptr);
2237 : }
2238 :
2239 : /************************************************************************/
2240 : /* importFromWkt() */
2241 : /************************************************************************/
2242 :
2243 : /*! @cond Doxygen_Suppress */
2244 :
2245 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2246 : CSLConstList papszOptions)
2247 :
2248 : {
2249 21 : return importFromWkt(&pszInput, papszOptions);
2250 : }
2251 :
2252 18232 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2253 : CSLConstList papszOptions)
2254 :
2255 : {
2256 36464 : TAKE_OPTIONAL_LOCK();
2257 :
2258 18232 : if (!ppszInput || !*ppszInput)
2259 0 : return OGRERR_FAILURE;
2260 :
2261 18232 : if (strlen(*ppszInput) > 100 * 1000 &&
2262 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2263 : {
2264 0 : CPLError(CE_Failure, CPLE_NotSupported,
2265 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2266 : "You can remove this limitation by definition the "
2267 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2268 0 : return OGRERR_FAILURE;
2269 : }
2270 :
2271 18232 : Clear();
2272 :
2273 18232 : bool canCache = false;
2274 18232 : auto tlsCache = OSRGetProjTLSCache();
2275 36464 : std::string osWkt;
2276 18232 : if (**ppszInput)
2277 : {
2278 17676 : osWkt = *ppszInput;
2279 17676 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2280 17676 : if (cachedObj)
2281 : {
2282 15790 : d->setPjCRS(cachedObj);
2283 : }
2284 : else
2285 : {
2286 3772 : CPLStringList aosOptions(papszOptions);
2287 1886 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2288 1886 : aosOptions.SetNameValue("STRICT", "NO");
2289 1886 : PROJ_STRING_LIST warnings = nullptr;
2290 1886 : PROJ_STRING_LIST errors = nullptr;
2291 1886 : auto ctxt = d->getPROJContext();
2292 1886 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2293 : &warnings, &errors);
2294 1886 : d->setPjCRS(pj);
2295 :
2296 1939 : for (auto iter = warnings; iter && *iter; ++iter)
2297 : {
2298 53 : d->m_wktImportWarnings.push_back(*iter);
2299 : }
2300 2123 : for (auto iter = errors; iter && *iter; ++iter)
2301 : {
2302 237 : d->m_wktImportErrors.push_back(*iter);
2303 237 : if (!d->m_pj_crs)
2304 : {
2305 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2306 : }
2307 : }
2308 1886 : if (warnings == nullptr && errors == nullptr)
2309 : {
2310 1605 : canCache = true;
2311 : }
2312 1886 : proj_string_list_destroy(warnings);
2313 1886 : proj_string_list_destroy(errors);
2314 : }
2315 : }
2316 18232 : if (!d->m_pj_crs)
2317 590 : return OGRERR_CORRUPT_DATA;
2318 :
2319 : // Only accept CRS objects
2320 17642 : if (!proj_is_crs(d->m_pj_crs))
2321 : {
2322 0 : Clear();
2323 0 : return OGRERR_CORRUPT_DATA;
2324 : }
2325 :
2326 17642 : if (canCache)
2327 : {
2328 1605 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2329 : }
2330 :
2331 17642 : if (strstr(*ppszInput, "CENTER_LONG"))
2332 : {
2333 0 : auto poRoot = new OGR_SRSNode();
2334 0 : d->setRoot(poRoot);
2335 0 : const char *pszTmp = *ppszInput;
2336 0 : poRoot->importFromWkt(&pszTmp);
2337 0 : d->m_bHasCenterLong = true;
2338 : }
2339 :
2340 : // TODO? we don't really update correctly since we assume that the
2341 : // passed string is only WKT.
2342 17642 : *ppszInput += strlen(*ppszInput);
2343 17642 : return OGRERR_NONE;
2344 :
2345 : #if no_longer_implemented_for_now
2346 : /* -------------------------------------------------------------------- */
2347 : /* The following seems to try and detect and unconsumed */
2348 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2349 : /* import and attach it to the existing root. Likely we will */
2350 : /* need to extend this somewhat to bring it into an acceptable */
2351 : /* OGRSpatialReference organization at some point. */
2352 : /* -------------------------------------------------------------------- */
2353 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2354 : {
2355 : if (((*ppszInput)[0]) == ',')
2356 : (*ppszInput)++;
2357 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2358 : poRoot->AddChild(poNewChild);
2359 : return poNewChild->importFromWkt(ppszInput);
2360 : }
2361 : #endif
2362 : }
2363 :
2364 : /*! @endcond */
2365 :
2366 : /**
2367 : * \brief Import from WKT string.
2368 : *
2369 : * This method will wipe the existing SRS definition, and
2370 : * reassign it based on the contents of the passed WKT string. Only as
2371 : * much of the input string as needed to construct this SRS is consumed from
2372 : * the input string, and the input string pointer
2373 : * is then updated to point to the remaining (unused) input.
2374 : *
2375 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2376 : * Issues</a> page for implementation details of WKT in OGR.
2377 : *
2378 : * This method is the same as the C function OSRImportFromWkt().
2379 : *
2380 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2381 : * point to remaining unused input text.
2382 : *
2383 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2384 : * fails for any reason.
2385 : * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2386 : * char*)
2387 : */
2388 :
2389 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2390 :
2391 : {
2392 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2393 : }
2394 :
2395 : /**
2396 : * \brief Import from WKT string.
2397 : *
2398 : * This method will wipe the existing SRS definition, and
2399 : * reassign it based on the contents of the passed WKT string. Only as
2400 : * much of the input string as needed to construct this SRS is consumed from
2401 : * the input string, and the input string pointer
2402 : * is then updated to point to the remaining (unused) input.
2403 : *
2404 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2405 : * Issues</a> page for implementation details of WKT in OGR.
2406 : *
2407 : * @param pszInput Input WKT
2408 : *
2409 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2410 : * fails for any reason.
2411 : */
2412 :
2413 17921 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2414 : {
2415 17921 : return importFromWkt(&pszInput);
2416 : }
2417 :
2418 : /************************************************************************/
2419 : /* Validate() */
2420 : /************************************************************************/
2421 :
2422 : /**
2423 : * \brief Validate CRS imported with importFromWkt() or with modified with
2424 : * direct node manipulations. Otherwise the CRS should be always valid.
2425 : *
2426 : * This method attempts to verify that the spatial reference system is
2427 : * well formed, and consists of known tokens. The validation is not
2428 : * comprehensive.
2429 : *
2430 : * This method is the same as the C function OSRValidate().
2431 : *
2432 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2433 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2434 : * but contains non-standard PROJECTION[] values.
2435 : */
2436 :
2437 116 : OGRErr OGRSpatialReference::Validate() const
2438 :
2439 : {
2440 232 : TAKE_OPTIONAL_LOCK();
2441 :
2442 154 : for (const auto &str : d->m_wktImportErrors)
2443 : {
2444 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2445 : }
2446 116 : for (const auto &str : d->m_wktImportWarnings)
2447 : {
2448 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2449 : }
2450 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2451 : {
2452 37 : return OGRERR_CORRUPT_DATA;
2453 : }
2454 79 : if (!d->m_wktImportWarnings.empty())
2455 : {
2456 0 : return OGRERR_UNSUPPORTED_SRS;
2457 : }
2458 79 : return OGRERR_NONE;
2459 : }
2460 :
2461 : /************************************************************************/
2462 : /* OSRValidate() */
2463 : /************************************************************************/
2464 : /**
2465 : * \brief Validate SRS tokens.
2466 : *
2467 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2468 : */
2469 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2470 :
2471 : {
2472 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2473 :
2474 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2475 : }
2476 :
2477 : /************************************************************************/
2478 : /* OSRImportFromWkt() */
2479 : /************************************************************************/
2480 :
2481 : /**
2482 : * \brief Import from WKT string.
2483 : *
2484 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2485 : * Issues</a> page for implementation details of WKT in OGR.
2486 : *
2487 : * This function is the same as OGRSpatialReference::importFromWkt().
2488 : */
2489 :
2490 290 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2491 :
2492 : {
2493 290 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2494 :
2495 290 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2496 : }
2497 :
2498 : /************************************************************************/
2499 : /* SetNode() */
2500 : /************************************************************************/
2501 :
2502 : /**
2503 : * \brief Set attribute value in spatial reference.
2504 : *
2505 : * Missing intermediate nodes in the path will be created if not already
2506 : * in existence. If the attribute has no children one will be created and
2507 : * assigned the value otherwise the zeroth child will be assigned the value.
2508 : *
2509 : * This method does the same as the C function OSRSetAttrValue().
2510 : *
2511 : * @param pszNodePath full path to attribute to be set. For instance
2512 : * "PROJCS|GEOGCS|UNIT".
2513 : *
2514 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2515 : * This may be NULL if you just want to force creation of the intermediate
2516 : * path.
2517 : *
2518 : * @return OGRERR_NONE on success.
2519 : */
2520 :
2521 583 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2522 : const char *pszNewNodeValue)
2523 :
2524 : {
2525 1166 : TAKE_OPTIONAL_LOCK();
2526 :
2527 : char **papszPathTokens =
2528 583 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2529 :
2530 583 : if (CSLCount(papszPathTokens) < 1)
2531 : {
2532 0 : CSLDestroy(papszPathTokens);
2533 0 : return OGRERR_FAILURE;
2534 : }
2535 :
2536 1018 : if (GetRoot() == nullptr ||
2537 435 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2538 : {
2539 268 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2540 116 : CSLCount(papszPathTokens) == 1)
2541 : {
2542 116 : CSLDestroy(papszPathTokens);
2543 116 : return SetProjCS(pszNewNodeValue);
2544 : }
2545 : else
2546 : {
2547 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2548 : }
2549 : }
2550 :
2551 467 : OGR_SRSNode *poNode = GetRoot();
2552 725 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2553 : {
2554 258 : int j = 0; // Used after for.
2555 :
2556 645 : for (; j < poNode->GetChildCount(); j++)
2557 : {
2558 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2559 : {
2560 198 : poNode = poNode->GetChild(j);
2561 198 : j = -1;
2562 198 : break;
2563 : }
2564 : }
2565 :
2566 258 : if (j != -1)
2567 : {
2568 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2569 60 : poNode->AddChild(poNewNode);
2570 60 : poNode = poNewNode;
2571 : }
2572 : }
2573 :
2574 467 : CSLDestroy(papszPathTokens);
2575 :
2576 467 : if (pszNewNodeValue != nullptr)
2577 : {
2578 467 : if (poNode->GetChildCount() > 0)
2579 371 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2580 : else
2581 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2582 : };
2583 467 : return OGRERR_NONE;
2584 : }
2585 :
2586 : /************************************************************************/
2587 : /* OSRSetAttrValue() */
2588 : /************************************************************************/
2589 :
2590 : /**
2591 : * \brief Set attribute value in spatial reference.
2592 : *
2593 : * This function is the same as OGRSpatialReference::SetNode()
2594 : */
2595 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2596 : const char *pszPath, const char *pszValue)
2597 :
2598 : {
2599 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2600 :
2601 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2602 : }
2603 :
2604 : /************************************************************************/
2605 : /* SetNode() */
2606 : /************************************************************************/
2607 :
2608 : /**
2609 : * \brief Set attribute value in spatial reference.
2610 : *
2611 : * Missing intermediate nodes in the path will be created if not already
2612 : * in existence. If the attribute has no children one will be created and
2613 : * assigned the value otherwise the zeroth child will be assigned the value.
2614 : *
2615 : * This method does the same as the C function OSRSetAttrValue().
2616 : *
2617 : * @param pszNodePath full path to attribute to be set. For instance
2618 : * "PROJCS|GEOGCS|UNIT".
2619 : *
2620 : * @param dfValue value to be assigned to node.
2621 : *
2622 : * @return OGRERR_NONE on success.
2623 : */
2624 :
2625 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2626 :
2627 : {
2628 0 : char szValue[64] = {'\0'};
2629 :
2630 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2631 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2632 : else
2633 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2634 :
2635 0 : return SetNode(pszNodePath, szValue);
2636 : }
2637 :
2638 : /************************************************************************/
2639 : /* SetAngularUnits() */
2640 : /************************************************************************/
2641 :
2642 : /**
2643 : * \brief Set the angular units for the geographic coordinate system.
2644 : *
2645 : * This method creates a UNIT subnode with the specified values as a
2646 : * child of the GEOGCS node.
2647 : *
2648 : * This method does the same as the C function OSRSetAngularUnits().
2649 : *
2650 : * @param pszUnitsName the units name to be used. Some preferred units
2651 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2652 : *
2653 : * @param dfInRadians the value to multiple by an angle in the indicated
2654 : * units to transform to radians. Some standard conversion factors can
2655 : * be found in ogr_srs_api.h.
2656 : *
2657 : * @return OGRERR_NONE on success.
2658 : */
2659 :
2660 1435 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2661 : double dfInRadians)
2662 :
2663 : {
2664 2870 : TAKE_OPTIONAL_LOCK();
2665 :
2666 1435 : d->bNormInfoSet = FALSE;
2667 :
2668 1435 : d->refreshProjObj();
2669 1435 : if (!d->m_pj_crs)
2670 0 : return OGRERR_FAILURE;
2671 1435 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2672 1435 : if (!geodCRS)
2673 0 : return OGRERR_FAILURE;
2674 1435 : proj_destroy(geodCRS);
2675 1435 : d->demoteFromBoundCRS();
2676 1435 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2677 : pszUnitsName, dfInRadians,
2678 : nullptr, nullptr));
2679 1435 : d->undoDemoteFromBoundCRS();
2680 :
2681 1435 : d->m_osAngularUnits = pszUnitsName;
2682 1435 : d->m_dfAngularUnitToRadian = dfInRadians;
2683 :
2684 1435 : return OGRERR_NONE;
2685 : }
2686 :
2687 : /************************************************************************/
2688 : /* OSRSetAngularUnits() */
2689 : /************************************************************************/
2690 :
2691 : /**
2692 : * \brief Set the angular units for the geographic coordinate system.
2693 : *
2694 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2695 : */
2696 48 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2697 : double dfInRadians)
2698 :
2699 : {
2700 48 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2701 :
2702 48 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2703 : }
2704 :
2705 : /************************************************************************/
2706 : /* GetAngularUnits() */
2707 : /************************************************************************/
2708 :
2709 : /**
2710 : * \brief Fetch angular geographic coordinate system units.
2711 : *
2712 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2713 : * will be assumed. This method only checks directly under the GEOGCS node
2714 : * for units.
2715 : *
2716 : * This method does the same thing as the C function OSRGetAngularUnits().
2717 : *
2718 : * @param ppszName a pointer to be updated with the pointer to the units name.
2719 : * The returned value remains internal to the OGRSpatialReference and should
2720 : * not be freed, or modified. It may be invalidated on the next
2721 : * OGRSpatialReference call.
2722 : *
2723 : * @return the value to multiply by angular distances to transform them to
2724 : * radians.
2725 : */
2726 :
2727 8509 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2728 :
2729 : {
2730 17018 : TAKE_OPTIONAL_LOCK();
2731 :
2732 8509 : d->refreshProjObj();
2733 :
2734 8509 : if (!d->m_osAngularUnits.empty())
2735 : {
2736 2374 : if (ppszName != nullptr)
2737 182 : *ppszName = d->m_osAngularUnits.c_str();
2738 2374 : return d->m_dfAngularUnitToRadian;
2739 : }
2740 :
2741 : do
2742 : {
2743 6135 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2744 : {
2745 113 : break;
2746 : }
2747 :
2748 : auto geodCRS =
2749 6024 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2750 6024 : if (!geodCRS)
2751 : {
2752 0 : break;
2753 : }
2754 : auto coordSys =
2755 6024 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2756 6024 : proj_destroy(geodCRS);
2757 6024 : if (!coordSys)
2758 : {
2759 0 : break;
2760 : }
2761 6024 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2762 : PJ_CS_TYPE_ELLIPSOIDAL)
2763 : {
2764 2 : proj_destroy(coordSys);
2765 2 : break;
2766 : }
2767 :
2768 6022 : double dfConvFactor = 0.0;
2769 6022 : const char *pszUnitName = nullptr;
2770 6022 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2771 : nullptr, nullptr, &dfConvFactor,
2772 : &pszUnitName, nullptr, nullptr))
2773 : {
2774 0 : proj_destroy(coordSys);
2775 0 : break;
2776 : }
2777 :
2778 6022 : d->m_osAngularUnits = pszUnitName;
2779 :
2780 6022 : proj_destroy(coordSys);
2781 6022 : d->m_dfAngularUnitToRadian = dfConvFactor;
2782 : } while (false);
2783 :
2784 6135 : if (d->m_osAngularUnits.empty())
2785 : {
2786 113 : d->m_osAngularUnits = "degree";
2787 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2788 : }
2789 :
2790 6135 : if (ppszName != nullptr)
2791 3195 : *ppszName = d->m_osAngularUnits.c_str();
2792 6135 : return d->m_dfAngularUnitToRadian;
2793 : }
2794 :
2795 : /**
2796 : * \brief Fetch angular geographic coordinate system units.
2797 : *
2798 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2799 : * will be assumed. This method only checks directly under the GEOGCS node
2800 : * for units.
2801 : *
2802 : * This method does the same thing as the C function OSRGetAngularUnits().
2803 : *
2804 : * @param ppszName a pointer to be updated with the pointer to the units name.
2805 : * The returned value remains internal to the OGRSpatialReference and should
2806 : * not be freed, or modified. It may be invalidated on the next
2807 : * OGRSpatialReference call.
2808 : *
2809 : * @return the value to multiply by angular distances to transform them to
2810 : * radians.
2811 : * @deprecated Use GetAngularUnits(const char**) const.
2812 : */
2813 :
2814 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2815 :
2816 : {
2817 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2818 : }
2819 :
2820 : /************************************************************************/
2821 : /* OSRGetAngularUnits() */
2822 : /************************************************************************/
2823 :
2824 : /**
2825 : * \brief Fetch angular geographic coordinate system units.
2826 : *
2827 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2828 : */
2829 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2830 :
2831 : {
2832 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2833 :
2834 1 : return ToPointer(hSRS)->GetAngularUnits(
2835 1 : const_cast<const char **>(ppszName));
2836 : }
2837 :
2838 : /************************************************************************/
2839 : /* SetLinearUnitsAndUpdateParameters() */
2840 : /************************************************************************/
2841 :
2842 : /**
2843 : * \brief Set the linear units for the projection.
2844 : *
2845 : * This method creates a UNIT subnode with the specified values as a
2846 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2847 : * SetLinearUnits() method, but it also updates all existing linear
2848 : * projection parameter values from the old units to the new units.
2849 : *
2850 : * @param pszName the units name to be used. Some preferred units
2851 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2852 : * and SRS_UL_US_FOOT.
2853 : *
2854 : * @param dfInMeters the value to multiple by a length in the indicated
2855 : * units to transform to meters. Some standard conversion factors can
2856 : * be found in ogr_srs_api.h.
2857 : *
2858 : * @param pszUnitAuthority Unit authority name. Or nullptr
2859 : *
2860 : * @param pszUnitCode Unit code. Or nullptr
2861 : *
2862 : * @return OGRERR_NONE on success.
2863 : */
2864 :
2865 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2866 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2867 : const char *pszUnitCode)
2868 :
2869 : {
2870 78 : TAKE_OPTIONAL_LOCK();
2871 :
2872 39 : if (dfInMeters <= 0.0)
2873 0 : return OGRERR_FAILURE;
2874 :
2875 39 : d->refreshProjObj();
2876 39 : if (!d->m_pj_crs)
2877 0 : return OGRERR_FAILURE;
2878 :
2879 39 : d->demoteFromBoundCRS();
2880 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2881 : {
2882 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2883 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2884 : pszUnitAuthority, pszUnitCode, true));
2885 : }
2886 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2887 : pszName, dfInMeters,
2888 : pszUnitAuthority, pszUnitCode));
2889 39 : d->undoDemoteFromBoundCRS();
2890 :
2891 39 : d->m_osLinearUnits = pszName;
2892 39 : d->dfToMeter = dfInMeters;
2893 :
2894 39 : return OGRERR_NONE;
2895 : }
2896 :
2897 : /************************************************************************/
2898 : /* OSRSetLinearUnitsAndUpdateParameters() */
2899 : /************************************************************************/
2900 :
2901 : /**
2902 : * \brief Set the linear units for the projection.
2903 : *
2904 : * This function is the same as
2905 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2906 : */
2907 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2908 : const char *pszUnits,
2909 : double dfInMeters)
2910 :
2911 : {
2912 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2913 : OGRERR_FAILURE);
2914 :
2915 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2916 1 : dfInMeters);
2917 : }
2918 :
2919 : /************************************************************************/
2920 : /* SetLinearUnits() */
2921 : /************************************************************************/
2922 :
2923 : /**
2924 : * \brief Set the linear units for the projection.
2925 : *
2926 : * This method creates a UNIT subnode with the specified values as a
2927 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2928 : * Geographic 3D CRS the vertical axis units will be set.
2929 : *
2930 : * This method does the same as the C function OSRSetLinearUnits().
2931 : *
2932 : * @param pszUnitsName the units name to be used. Some preferred units
2933 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2934 : * and SRS_UL_US_FOOT.
2935 : *
2936 : * @param dfInMeters the value to multiple by a length in the indicated
2937 : * units to transform to meters. Some standard conversion factors can
2938 : * be found in ogr_srs_api.h.
2939 : *
2940 : * @return OGRERR_NONE on success.
2941 : */
2942 :
2943 7318 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2944 : double dfInMeters)
2945 :
2946 : {
2947 7318 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2948 : }
2949 :
2950 : /************************************************************************/
2951 : /* OSRSetLinearUnits() */
2952 : /************************************************************************/
2953 :
2954 : /**
2955 : * \brief Set the linear units for the projection.
2956 : *
2957 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2958 : */
2959 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2960 : double dfInMeters)
2961 :
2962 : {
2963 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2964 :
2965 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2966 : }
2967 :
2968 : /************************************************************************/
2969 : /* SetTargetLinearUnits() */
2970 : /************************************************************************/
2971 :
2972 : /**
2973 : * \brief Set the linear units for the projection.
2974 : *
2975 : * This method creates a UNIT subnode with the specified values as a
2976 : * child of the target node.
2977 : *
2978 : * This method does the same as the C function OSRSetTargetLinearUnits().
2979 : *
2980 : * @param pszTargetKey the keyword to set the linear units for.
2981 : * i.e. "PROJCS" or "VERT_CS"
2982 : *
2983 : * @param pszUnitsName the units name to be used. Some preferred units
2984 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2985 : * and SRS_UL_US_FOOT.
2986 : *
2987 : * @param dfInMeters the value to multiple by a length in the indicated
2988 : * units to transform to meters. Some standard conversion factors can
2989 : * be found in ogr_srs_api.h.
2990 : *
2991 : * @param pszUnitAuthority Unit authority name. Or nullptr
2992 : *
2993 : * @param pszUnitCode Unit code. Or nullptr
2994 : *
2995 : * @return OGRERR_NONE on success.
2996 : *
2997 : */
2998 :
2999 11686 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3000 : const char *pszUnitsName,
3001 : double dfInMeters,
3002 : const char *pszUnitAuthority,
3003 : const char *pszUnitCode)
3004 :
3005 : {
3006 23372 : TAKE_OPTIONAL_LOCK();
3007 :
3008 11686 : if (dfInMeters <= 0.0)
3009 0 : return OGRERR_FAILURE;
3010 :
3011 11686 : d->refreshProjObj();
3012 11686 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3013 11686 : if (pszTargetKey == nullptr)
3014 : {
3015 11686 : if (!d->m_pj_crs)
3016 0 : return OGRERR_FAILURE;
3017 :
3018 11686 : d->demoteFromBoundCRS();
3019 11686 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3020 : {
3021 17574 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3022 8787 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3023 : pszUnitAuthority, pszUnitCode, false));
3024 : }
3025 23372 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3026 11686 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3027 : pszUnitAuthority, pszUnitCode));
3028 11686 : d->undoDemoteFromBoundCRS();
3029 :
3030 11686 : d->m_osLinearUnits = pszUnitsName;
3031 11686 : d->dfToMeter = dfInMeters;
3032 :
3033 11686 : return OGRERR_NONE;
3034 : }
3035 :
3036 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3037 :
3038 0 : if (poCS == nullptr)
3039 0 : return OGRERR_FAILURE;
3040 :
3041 0 : char szValue[128] = {'\0'};
3042 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3043 0 : dfInMeters > std::numeric_limits<int>::min() &&
3044 0 : dfInMeters == static_cast<int>(dfInMeters))
3045 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3046 : else
3047 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3048 :
3049 0 : OGR_SRSNode *poUnits = nullptr;
3050 0 : if (poCS->FindChild("UNIT") >= 0)
3051 : {
3052 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3053 0 : if (poUnits->GetChildCount() < 2)
3054 0 : return OGRERR_FAILURE;
3055 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3056 0 : poUnits->GetChild(1)->SetValue(szValue);
3057 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3058 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3059 : }
3060 : else
3061 : {
3062 0 : poUnits = new OGR_SRSNode("UNIT");
3063 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3064 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3065 :
3066 0 : poCS->AddChild(poUnits);
3067 : }
3068 :
3069 0 : return OGRERR_NONE;
3070 : }
3071 :
3072 : /************************************************************************/
3073 : /* OSRSetLinearUnits() */
3074 : /************************************************************************/
3075 :
3076 : /**
3077 : * \brief Set the linear units for the target node.
3078 : *
3079 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3080 : *
3081 : */
3082 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3083 : const char *pszTargetKey, const char *pszUnits,
3084 : double dfInMeters)
3085 :
3086 : {
3087 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3088 :
3089 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3090 1 : dfInMeters);
3091 : }
3092 :
3093 : /************************************************************************/
3094 : /* GetLinearUnits() */
3095 : /************************************************************************/
3096 :
3097 : /**
3098 : * \brief Fetch linear projection units.
3099 : *
3100 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3101 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3102 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3103 : * axis units will be returned.
3104 : *
3105 : * This method does the same thing as the C function OSRGetLinearUnits()
3106 : *
3107 : * @param ppszName a pointer to be updated with the pointer to the units name.
3108 : * The returned value remains internal to the OGRSpatialReference and should
3109 : * not be freed, or modified. It may be invalidated on the next
3110 : * OGRSpatialReference call.
3111 : *
3112 : * @return the value to multiply by linear distances to transform them to
3113 : * meters.
3114 : * @deprecated Use GetLinearUnits(const char**) const.
3115 : */
3116 :
3117 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3118 :
3119 : {
3120 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3121 : }
3122 :
3123 : /**
3124 : * \brief Fetch linear projection units.
3125 : *
3126 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3127 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3128 : * for units.
3129 : *
3130 : * This method does the same thing as the C function OSRGetLinearUnits()
3131 : *
3132 : * @param ppszName a pointer to be updated with the pointer to the units name.
3133 : * The returned value remains internal to the OGRSpatialReference and should
3134 : * not be freed, or modified. It may be invalidated on the next
3135 : * OGRSpatialReference call.
3136 : *
3137 : * @return the value to multiply by linear distances to transform them to
3138 : * meters.
3139 : */
3140 :
3141 20728 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3142 :
3143 : {
3144 20728 : return GetTargetLinearUnits(nullptr, ppszName);
3145 : }
3146 :
3147 : /************************************************************************/
3148 : /* OSRGetLinearUnits() */
3149 : /************************************************************************/
3150 :
3151 : /**
3152 : * \brief Fetch linear projection units.
3153 : *
3154 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3155 : */
3156 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3157 :
3158 : {
3159 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3160 :
3161 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3162 : }
3163 :
3164 : /************************************************************************/
3165 : /* GetTargetLinearUnits() */
3166 : /************************************************************************/
3167 :
3168 : /**
3169 : * \brief Fetch linear units for target.
3170 : *
3171 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3172 : *
3173 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3174 : *
3175 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3176 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3177 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3178 : * @param ppszName a pointer to be updated with the pointer to the units name.
3179 : * The returned value remains internal to the OGRSpatialReference and should not
3180 : * be freed, or modified. It may be invalidated on the next
3181 : * OGRSpatialReference call. ppszName can be set to NULL.
3182 : *
3183 : * @return the value to multiply by linear distances to transform them to
3184 : * meters.
3185 : *
3186 : * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3187 : * const.
3188 : */
3189 :
3190 20879 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3191 : const char **ppszName) const
3192 :
3193 : {
3194 41758 : TAKE_OPTIONAL_LOCK();
3195 :
3196 20879 : d->refreshProjObj();
3197 :
3198 20879 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3199 20879 : if (pszTargetKey == nullptr)
3200 : {
3201 : // Use cached result if available
3202 20788 : if (!d->m_osLinearUnits.empty())
3203 : {
3204 8881 : if (ppszName)
3205 8080 : *ppszName = d->m_osLinearUnits.c_str();
3206 8881 : return d->dfToMeter;
3207 : }
3208 :
3209 : while (true)
3210 : {
3211 11907 : if (d->m_pj_crs == nullptr)
3212 : {
3213 242 : break;
3214 : }
3215 :
3216 11665 : d->demoteFromBoundCRS();
3217 11665 : PJ *coordSys = nullptr;
3218 11665 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3219 : {
3220 37 : for (int iComponent = 0; iComponent < 2; iComponent++)
3221 : {
3222 37 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3223 37 : d->m_pj_crs, iComponent);
3224 37 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3225 : {
3226 : auto temp =
3227 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3228 0 : proj_destroy(subCRS);
3229 0 : subCRS = temp;
3230 : }
3231 74 : if (subCRS &&
3232 37 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3233 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3234 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3235 : {
3236 31 : coordSys = proj_crs_get_coordinate_system(
3237 : d->getPROJContext(), subCRS);
3238 31 : proj_destroy(subCRS);
3239 31 : break;
3240 : }
3241 6 : else if (subCRS)
3242 : {
3243 6 : proj_destroy(subCRS);
3244 : }
3245 : }
3246 31 : if (coordSys == nullptr)
3247 : {
3248 0 : d->undoDemoteFromBoundCRS();
3249 0 : break;
3250 : }
3251 : }
3252 : else
3253 : {
3254 11634 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3255 11634 : d->m_pj_crs);
3256 : }
3257 :
3258 11665 : d->undoDemoteFromBoundCRS();
3259 11665 : if (!coordSys)
3260 : {
3261 0 : break;
3262 : }
3263 11665 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3264 :
3265 11665 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3266 2247 : csType != PJ_CS_TYPE_VERTICAL &&
3267 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3268 : csType != PJ_CS_TYPE_SPHERICAL)
3269 : {
3270 0 : proj_destroy(coordSys);
3271 0 : break;
3272 : }
3273 :
3274 11665 : int axis = 0;
3275 :
3276 11665 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3277 : csType == PJ_CS_TYPE_SPHERICAL)
3278 : {
3279 : const int axisCount =
3280 2247 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3281 :
3282 2247 : if (axisCount == 3)
3283 : {
3284 4 : axis = 2;
3285 : }
3286 : else
3287 : {
3288 2243 : proj_destroy(coordSys);
3289 2243 : break;
3290 : }
3291 : }
3292 :
3293 9422 : double dfConvFactor = 0.0;
3294 9422 : const char *pszUnitName = nullptr;
3295 9422 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3296 : nullptr, nullptr, nullptr, &dfConvFactor,
3297 : &pszUnitName, nullptr, nullptr))
3298 : {
3299 0 : proj_destroy(coordSys);
3300 0 : break;
3301 : }
3302 :
3303 9422 : d->m_osLinearUnits = pszUnitName;
3304 9422 : d->dfToMeter = dfConvFactor;
3305 9422 : if (ppszName)
3306 1234 : *ppszName = d->m_osLinearUnits.c_str();
3307 :
3308 9422 : proj_destroy(coordSys);
3309 9422 : return dfConvFactor;
3310 : }
3311 :
3312 2485 : d->m_osLinearUnits = "unknown";
3313 2485 : d->dfToMeter = 1.0;
3314 :
3315 2485 : if (ppszName != nullptr)
3316 2299 : *ppszName = d->m_osLinearUnits.c_str();
3317 2485 : return 1.0;
3318 : }
3319 :
3320 91 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3321 :
3322 91 : if (ppszName != nullptr)
3323 37 : *ppszName = "unknown";
3324 :
3325 91 : if (poCS == nullptr)
3326 53 : return 1.0;
3327 :
3328 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3329 : {
3330 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3331 :
3332 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3333 : {
3334 38 : if (ppszName != nullptr)
3335 37 : *ppszName = poChild->GetChild(0)->GetValue();
3336 :
3337 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3338 : }
3339 : }
3340 :
3341 0 : return 1.0;
3342 : }
3343 :
3344 : /**
3345 : * \brief Fetch linear units for target.
3346 : *
3347 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3348 : *
3349 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3350 : *
3351 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3352 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3353 : * GEOCCS and VERT_CS are looked up)
3354 : * @param ppszName a pointer to be updated with the pointer to the units name.
3355 : * The returned value remains internal to the OGRSpatialReference and should not
3356 : * be freed, or modified. It may be invalidated on the next
3357 : * OGRSpatialReference call. ppszName can be set to NULL.
3358 : *
3359 : * @return the value to multiply by linear distances to transform them to
3360 : * meters.
3361 : *
3362 : */
3363 :
3364 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3365 : char **ppszName) const
3366 :
3367 : {
3368 0 : return GetTargetLinearUnits(pszTargetKey,
3369 0 : const_cast<const char **>(ppszName));
3370 : }
3371 :
3372 : /************************************************************************/
3373 : /* OSRGetTargetLinearUnits() */
3374 : /************************************************************************/
3375 :
3376 : /**
3377 : * \brief Fetch linear projection units.
3378 : *
3379 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3380 : *
3381 : */
3382 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3383 : const char *pszTargetKey, char **ppszName)
3384 :
3385 : {
3386 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3387 :
3388 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3389 4 : pszTargetKey, const_cast<const char **>(ppszName));
3390 : }
3391 :
3392 : /************************************************************************/
3393 : /* GetPrimeMeridian() */
3394 : /************************************************************************/
3395 :
3396 : /**
3397 : * \brief Fetch prime meridian info.
3398 : *
3399 : * Returns the offset of the prime meridian from greenwich in degrees,
3400 : * and the prime meridian name (if requested). If no PRIMEM value exists
3401 : * in the coordinate system definition a value of "Greenwich" and an
3402 : * offset of 0.0 is assumed.
3403 : *
3404 : * If the prime meridian name is returned, the pointer is to an internal
3405 : * copy of the name. It should not be freed, altered or depended on after
3406 : * the next OGR call.
3407 : *
3408 : * This method is the same as the C function OSRGetPrimeMeridian().
3409 : *
3410 : * @param ppszName return location for prime meridian name. If NULL, name
3411 : * is not returned.
3412 : *
3413 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3414 : * degrees.
3415 : * @deprecated Use GetPrimeMeridian(const char**) const.
3416 : */
3417 :
3418 1533 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3419 :
3420 : {
3421 3066 : TAKE_OPTIONAL_LOCK();
3422 :
3423 1533 : d->refreshProjObj();
3424 :
3425 1533 : if (!d->m_osPrimeMeridianName.empty())
3426 : {
3427 87 : if (ppszName != nullptr)
3428 11 : *ppszName = d->m_osPrimeMeridianName.c_str();
3429 87 : return d->dfFromGreenwich;
3430 : }
3431 :
3432 : while (true)
3433 : {
3434 1446 : if (!d->m_pj_crs)
3435 0 : break;
3436 :
3437 1446 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3438 1446 : if (!pm)
3439 0 : break;
3440 :
3441 1446 : d->m_osPrimeMeridianName = proj_get_name(pm);
3442 1446 : if (ppszName)
3443 20 : *ppszName = d->m_osPrimeMeridianName.c_str();
3444 1446 : double dfLongitude = 0.0;
3445 1446 : double dfConvFactor = 0.0;
3446 1446 : proj_prime_meridian_get_parameters(
3447 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3448 1446 : proj_destroy(pm);
3449 2892 : d->dfFromGreenwich =
3450 1446 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3451 1446 : return d->dfFromGreenwich;
3452 : }
3453 :
3454 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3455 0 : d->dfFromGreenwich = 0.0;
3456 0 : if (ppszName != nullptr)
3457 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3458 0 : return d->dfFromGreenwich;
3459 : }
3460 :
3461 : /**
3462 : * \brief Fetch prime meridian info.
3463 : *
3464 : * Returns the offset of the prime meridian from greenwich in degrees,
3465 : * and the prime meridian name (if requested). If no PRIMEM value exists
3466 : * in the coordinate system definition a value of "Greenwich" and an
3467 : * offset of 0.0 is assumed.
3468 : *
3469 : * If the prime meridian name is returned, the pointer is to an internal
3470 : * copy of the name. It should not be freed, altered or depended on after
3471 : * the next OGR call.
3472 : *
3473 : * This method is the same as the C function OSRGetPrimeMeridian().
3474 : *
3475 : * @param ppszName return location for prime meridian name. If NULL, name
3476 : * is not returned.
3477 : *
3478 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3479 : * degrees.
3480 : */
3481 :
3482 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3483 :
3484 : {
3485 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3486 : }
3487 :
3488 : /************************************************************************/
3489 : /* OSRGetPrimeMeridian() */
3490 : /************************************************************************/
3491 :
3492 : /**
3493 : * \brief Fetch prime meridian info.
3494 : *
3495 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3496 : */
3497 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3498 :
3499 : {
3500 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3501 :
3502 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3503 0 : const_cast<const char **>(ppszName));
3504 : }
3505 :
3506 : /************************************************************************/
3507 : /* SetGeogCS() */
3508 : /************************************************************************/
3509 :
3510 : /**
3511 : * \brief Set geographic coordinate system.
3512 : *
3513 : * This method is used to set the datum, ellipsoid, prime meridian and
3514 : * angular units for a geographic coordinate system. It can be used on its
3515 : * own to establish a geographic spatial reference, or applied to a
3516 : * projected coordinate system to establish the underlying geographic
3517 : * coordinate system.
3518 : *
3519 : * This method does the same as the C function OSRSetGeogCS().
3520 : *
3521 : * @param pszGeogName user visible name for the geographic coordinate system
3522 : * (not to serve as a key).
3523 : *
3524 : * @param pszDatumName key name for this datum. The OpenGIS specification
3525 : * lists some known values, and otherwise EPSG datum names with a standard
3526 : * transformation are considered legal keys.
3527 : *
3528 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3529 : *
3530 : * @param dfSemiMajor the semi major axis of the spheroid.
3531 : *
3532 : * @param dfInvFlattening the inverse flattening for the spheroid.
3533 : * This can be computed from the semi minor axis as
3534 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3535 : *
3536 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3537 : * If this is NULL a default value of "Greenwich" will be used.
3538 : *
3539 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3540 : * meridian. Always in Degrees
3541 : *
3542 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3543 : * standard names). If NULL a value of "degrees" will be assumed.
3544 : *
3545 : * @param dfConvertToRadians value to multiply angular units by to transform
3546 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3547 : * pszAngularUnits is NULL.
3548 : *
3549 : * @return OGRERR_NONE on success.
3550 : */
3551 :
3552 9596 : OGRErr OGRSpatialReference::SetGeogCS(
3553 : const char *pszGeogName, const char *pszDatumName,
3554 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3555 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3556 : double dfConvertToRadians)
3557 :
3558 : {
3559 19192 : TAKE_OPTIONAL_LOCK();
3560 :
3561 9596 : d->bNormInfoSet = FALSE;
3562 9596 : d->m_osAngularUnits.clear();
3563 9596 : d->m_dfAngularUnitToRadian = 0.0;
3564 9596 : d->m_osPrimeMeridianName.clear();
3565 9596 : d->dfFromGreenwich = 0.0;
3566 :
3567 : /* -------------------------------------------------------------------- */
3568 : /* For a geocentric coordinate system we want to set the datum */
3569 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3570 : /* temporary srs and use the copy method which has special */
3571 : /* handling for GEOCCS. */
3572 : /* -------------------------------------------------------------------- */
3573 9596 : if (IsGeocentric())
3574 : {
3575 4 : OGRSpatialReference oGCS;
3576 :
3577 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3578 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3579 : dfConvertToRadians);
3580 2 : return CopyGeogCSFrom(&oGCS);
3581 : }
3582 :
3583 9594 : auto cs = proj_create_ellipsoidal_2D_cs(
3584 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3585 : dfConvertToRadians);
3586 : // Prime meridian expressed in Degree
3587 9594 : auto obj = proj_create_geographic_crs(
3588 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3589 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3590 9594 : proj_destroy(cs);
3591 :
3592 14504 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3593 4910 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3594 : {
3595 4684 : d->setPjCRS(obj);
3596 : }
3597 4910 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3598 : {
3599 9820 : d->setPjCRS(
3600 4910 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3601 4910 : proj_destroy(obj);
3602 : }
3603 : else
3604 : {
3605 0 : proj_destroy(obj);
3606 : }
3607 :
3608 9594 : return OGRERR_NONE;
3609 : }
3610 :
3611 : /************************************************************************/
3612 : /* OSRSetGeogCS() */
3613 : /************************************************************************/
3614 :
3615 : /**
3616 : * \brief Set geographic coordinate system.
3617 : *
3618 : * This function is the same as OGRSpatialReference::SetGeogCS()
3619 : */
3620 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3621 : const char *pszDatumName, const char *pszSpheroidName,
3622 : double dfSemiMajor, double dfInvFlattening,
3623 : const char *pszPMName, double dfPMOffset,
3624 : const char *pszAngularUnits, double dfConvertToRadians)
3625 :
3626 : {
3627 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3628 :
3629 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3630 : pszSpheroidName, dfSemiMajor,
3631 : dfInvFlattening, pszPMName, dfPMOffset,
3632 18 : pszAngularUnits, dfConvertToRadians);
3633 : }
3634 :
3635 : /************************************************************************/
3636 : /* SetWellKnownGeogCS() */
3637 : /************************************************************************/
3638 :
3639 : /**
3640 : * \brief Set a GeogCS based on well known name.
3641 : *
3642 : * This may be called on an empty OGRSpatialReference to make a geographic
3643 : * coordinate system, or on something with an existing PROJCS node to
3644 : * set the underlying geographic coordinate system of a projected coordinate
3645 : * system.
3646 : *
3647 : * The following well known text values are currently supported,
3648 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3649 : * <ul>
3650 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3651 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3652 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3653 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3654 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3655 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3656 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3657 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3658 : * </ul>
3659 : *
3660 : * @param pszName name of well known geographic coordinate system.
3661 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3662 : * recognised, the target object is already initialized, or an EPSG value
3663 : * can't be successfully looked up.
3664 : */
3665 :
3666 3494 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3667 :
3668 : {
3669 6988 : TAKE_OPTIONAL_LOCK();
3670 :
3671 : /* -------------------------------------------------------------------- */
3672 : /* Check for EPSG authority numbers. */
3673 : /* -------------------------------------------------------------------- */
3674 3494 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3675 : {
3676 84 : OGRSpatialReference oSRS2;
3677 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3678 42 : if (eErr != OGRERR_NONE)
3679 0 : return eErr;
3680 :
3681 42 : if (!oSRS2.IsGeographic())
3682 0 : return OGRERR_FAILURE;
3683 :
3684 42 : return CopyGeogCSFrom(&oSRS2);
3685 : }
3686 :
3687 : /* -------------------------------------------------------------------- */
3688 : /* Check for simple names. */
3689 : /* -------------------------------------------------------------------- */
3690 3452 : const char *pszWKT = nullptr;
3691 :
3692 3452 : if (EQUAL(pszName, "WGS84"))
3693 : {
3694 2634 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3695 : }
3696 818 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3697 : {
3698 636 : pszWKT =
3699 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3700 : "ELLIPSOID[\"WGS "
3701 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3702 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3703 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3704 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3705 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3706 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3707 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3708 : "ID[\"OGC\",\"CRS84\"]]";
3709 : }
3710 182 : else if (EQUAL(pszName, "WGS72"))
3711 19 : pszWKT =
3712 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3713 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3714 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3715 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3716 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3717 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3718 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3719 :
3720 163 : else if (EQUAL(pszName, "NAD27"))
3721 135 : pszWKT =
3722 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3723 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3724 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3725 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3726 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3727 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3728 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3729 :
3730 28 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3731 0 : pszWKT =
3732 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3733 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3734 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3735 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3736 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3737 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3738 :
3739 28 : else if (EQUAL(pszName, "NAD83"))
3740 24 : pszWKT =
3741 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3742 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3743 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3744 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3745 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3746 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3747 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3748 : "\"EPSG\",\"4269\"]]";
3749 :
3750 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3751 0 : pszWKT =
3752 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3753 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3754 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3755 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3756 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3757 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3758 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3759 :
3760 : else
3761 4 : return OGRERR_FAILURE;
3762 :
3763 : /* -------------------------------------------------------------------- */
3764 : /* Import the WKT */
3765 : /* -------------------------------------------------------------------- */
3766 6896 : OGRSpatialReference oSRS2;
3767 3448 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3768 3448 : if (eErr != OGRERR_NONE)
3769 0 : return eErr;
3770 :
3771 : /* -------------------------------------------------------------------- */
3772 : /* Copy over. */
3773 : /* -------------------------------------------------------------------- */
3774 3448 : return CopyGeogCSFrom(&oSRS2);
3775 : }
3776 :
3777 : /************************************************************************/
3778 : /* OSRSetWellKnownGeogCS() */
3779 : /************************************************************************/
3780 :
3781 : /**
3782 : * \brief Set a GeogCS based on well known name.
3783 : *
3784 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3785 : */
3786 155 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3787 :
3788 : {
3789 155 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3790 :
3791 155 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3792 : }
3793 :
3794 : /************************************************************************/
3795 : /* CopyGeogCSFrom() */
3796 : /************************************************************************/
3797 :
3798 : /**
3799 : * \brief Copy GEOGCS from another OGRSpatialReference.
3800 : *
3801 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3802 : * If this object has a PROJCS root already, the GEOGCS is installed within
3803 : * it, otherwise it is installed as the root.
3804 : *
3805 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3806 : *
3807 : * @return OGRERR_NONE on success or an error code.
3808 : */
3809 :
3810 4102 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3811 :
3812 : {
3813 8204 : TAKE_OPTIONAL_LOCK();
3814 :
3815 4102 : d->bNormInfoSet = FALSE;
3816 4102 : d->m_osAngularUnits.clear();
3817 4102 : d->m_dfAngularUnitToRadian = 0.0;
3818 4102 : d->m_osPrimeMeridianName.clear();
3819 4102 : d->dfFromGreenwich = 0.0;
3820 :
3821 4102 : d->refreshProjObj();
3822 4102 : poSrcSRS->d->refreshProjObj();
3823 4102 : if (!poSrcSRS->d->m_pj_crs)
3824 : {
3825 1 : return OGRERR_FAILURE;
3826 : }
3827 : auto geodCRS =
3828 4101 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3829 4101 : if (!geodCRS)
3830 : {
3831 0 : return OGRERR_FAILURE;
3832 : }
3833 :
3834 : /* -------------------------------------------------------------------- */
3835 : /* Handle geocentric coordinate systems specially. We just */
3836 : /* want to copy the DATUM. */
3837 : /* -------------------------------------------------------------------- */
3838 4101 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3839 : {
3840 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3841 : #if PROJ_VERSION_MAJOR > 7 || \
3842 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3843 : if (datum == nullptr)
3844 : {
3845 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3846 : }
3847 : #endif
3848 3 : if (datum == nullptr)
3849 : {
3850 0 : proj_destroy(geodCRS);
3851 0 : return OGRERR_FAILURE;
3852 : }
3853 :
3854 3 : const char *pszUnitName = nullptr;
3855 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3856 :
3857 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3858 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3859 : unitConvFactor);
3860 3 : proj_destroy(datum);
3861 :
3862 3 : d->setPjCRS(pj_crs);
3863 : }
3864 :
3865 4098 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3866 : {
3867 319 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3868 319 : d->m_pj_crs, geodCRS);
3869 319 : d->setPjCRS(pj_crs);
3870 : }
3871 :
3872 : else
3873 : {
3874 3779 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3875 : }
3876 :
3877 : // Apply TOWGS84 of source CRS
3878 4101 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3879 : {
3880 : auto target =
3881 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3882 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3883 1 : poSrcSRS->d->m_pj_crs);
3884 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3885 : target, co));
3886 1 : proj_destroy(target);
3887 1 : proj_destroy(co);
3888 : }
3889 :
3890 4101 : proj_destroy(geodCRS);
3891 :
3892 4101 : return OGRERR_NONE;
3893 : }
3894 :
3895 : /************************************************************************/
3896 : /* OSRCopyGeogCSFrom() */
3897 : /************************************************************************/
3898 :
3899 : /**
3900 : * \brief Copy GEOGCS from another OGRSpatialReference.
3901 : *
3902 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3903 : */
3904 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3905 : const OGRSpatialReferenceH hSrcSRS)
3906 :
3907 : {
3908 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3909 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3910 :
3911 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3912 : }
3913 :
3914 : /************************************************************************/
3915 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3916 : /************************************************************************/
3917 :
3918 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3919 : *
3920 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3921 : */
3922 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3923 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3924 :
3925 : /**
3926 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3927 : */
3928 2819 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3929 : {
3930 2819 : return SET_FROM_USER_INPUT_LIMITATIONS;
3931 : }
3932 :
3933 : /************************************************************************/
3934 : /* RemoveIDFromMemberOfEnsembles() */
3935 : /************************************************************************/
3936 :
3937 : // cppcheck-suppress constParameterReference
3938 243 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3939 : {
3940 : // Remove "id" from members of datum ensembles for compatibility with
3941 : // older PROJ versions
3942 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3943 : // and https://github.com/OSGeo/PROJ/pull/3221
3944 243 : if (obj.GetType() == CPLJSONObject::Type::Object)
3945 : {
3946 300 : for (auto &subObj : obj.GetChildren())
3947 : {
3948 235 : RemoveIDFromMemberOfEnsembles(subObj);
3949 : }
3950 : }
3951 198 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3952 198 : obj.GetName() == "members")
3953 : {
3954 60 : for (auto &subObj : obj.ToArray())
3955 : {
3956 52 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3957 : {
3958 51 : subObj.Delete("id");
3959 : }
3960 : }
3961 : }
3962 243 : }
3963 :
3964 : /************************************************************************/
3965 : /* SetFromUserInput() */
3966 : /************************************************************************/
3967 :
3968 : /**
3969 : * \brief Set spatial reference from various text formats.
3970 : *
3971 : * This method will examine the provided input, and try to deduce the
3972 : * format, and then use it to initialize the spatial reference system. It
3973 : * may take the following forms:
3974 : *
3975 : * <ol>
3976 : * <li> Well Known Text definition - passed on to importFromWkt().
3977 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3978 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3979 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3980 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3981 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3982 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3983 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3984 : * WGS84 or WGS72.
3985 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3986 : * <li> PROJJSON (PROJ >= 6.2)
3987 : * </ol>
3988 : *
3989 : * It is expected that this method will be extended in the future to support
3990 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3991 : * State Plane definitions.
3992 : *
3993 : * This method is intended to be flexible, but by its nature it is
3994 : * imprecise as it must guess information about the format intended. When
3995 : * possible applications should call the specific method appropriate if the
3996 : * input is known to be in a particular format.
3997 : *
3998 : * This method does the same thing as the OSRSetFromUserInput() function.
3999 : *
4000 : * @param pszDefinition text definition to try to deduce SRS from.
4001 : *
4002 : * @return OGRERR_NONE on success, or an error code if the name isn't
4003 : * recognised, the definition is corrupt, or an EPSG value can't be
4004 : * successfully looked up.
4005 : */
4006 :
4007 20988 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4008 : {
4009 20988 : return SetFromUserInput(pszDefinition, nullptr);
4010 : }
4011 :
4012 : /**
4013 : * \brief Set spatial reference from various text formats.
4014 : *
4015 : * This method will examine the provided input, and try to deduce the
4016 : * format, and then use it to initialize the spatial reference system. It
4017 : * may take the following forms:
4018 : *
4019 : * <ol>
4020 : * <li> Well Known Text definition - passed on to importFromWkt().
4021 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4022 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4023 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4024 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4025 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4026 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4027 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4028 : * WGS84 or WGS72.
4029 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4030 : * <li> PROJJSON (PROJ >= 6.2)
4031 : * </ol>
4032 : *
4033 : * It is expected that this method will be extended in the future to support
4034 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4035 : * State Plane definitions.
4036 : *
4037 : * This method is intended to be flexible, but by its nature it is
4038 : * imprecise as it must guess information about the format intended. When
4039 : * possible applications should call the specific method appropriate if the
4040 : * input is known to be in a particular format.
4041 : *
4042 : * This method does the same thing as the OSRSetFromUserInput() and
4043 : * OSRSetFromUserInputEx() functions.
4044 : *
4045 : * @param pszDefinition text definition to try to deduce SRS from.
4046 : *
4047 : * @param papszOptions NULL terminated list of options, or NULL.
4048 : * <ol>
4049 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4050 : * Whether http:// or https:// access is allowed. Defaults to YES.
4051 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4052 : * Whether reading a file using the Virtual File System layer is allowed
4053 : * (can also involve network access). Defaults to YES.
4054 : * </ol>
4055 : *
4056 : * @return OGRERR_NONE on success, or an error code if the name isn't
4057 : * recognised, the definition is corrupt, or an EPSG value can't be
4058 : * successfully looked up.
4059 : */
4060 :
4061 28205 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4062 : CSLConstList papszOptions)
4063 : {
4064 56410 : TAKE_OPTIONAL_LOCK();
4065 :
4066 : // Skip leading white space
4067 28207 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4068 2 : pszDefinition++;
4069 :
4070 28205 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4071 : {
4072 1 : pszDefinition += 6;
4073 : }
4074 :
4075 : /* -------------------------------------------------------------------- */
4076 : /* Is it a recognised syntax? */
4077 : /* -------------------------------------------------------------------- */
4078 28205 : const char *const wktKeywords[] = {
4079 : // WKT1
4080 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4081 : // WKT2"
4082 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4083 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4084 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4085 423519 : for (const char *keyword : wktKeywords)
4086 : {
4087 404351 : if (STARTS_WITH_CI(pszDefinition, keyword))
4088 : {
4089 9037 : return importFromWkt(pszDefinition);
4090 : }
4091 : }
4092 :
4093 19168 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4094 19168 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4095 : {
4096 11412 : OGRErr eStatus = OGRERR_NONE;
4097 :
4098 11412 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4099 : {
4100 : // Use proj_create() as it allows things like EPSG:3157+4617
4101 : // that are not normally supported by the below code that
4102 : // builds manually a compound CRS
4103 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4104 62 : if (!pj)
4105 : {
4106 1 : return OGRERR_FAILURE;
4107 : }
4108 61 : Clear();
4109 61 : d->setPjCRS(pj);
4110 61 : return OGRERR_NONE;
4111 : }
4112 : else
4113 : {
4114 : eStatus =
4115 11350 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4116 : }
4117 :
4118 11350 : return eStatus;
4119 : }
4120 :
4121 7756 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4122 7036 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4123 7035 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4124 6977 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4125 6977 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4126 6977 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4127 779 : return importFromURN(pszDefinition);
4128 :
4129 6977 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4130 6975 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4131 6974 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4132 1637 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4133 1636 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4134 5341 : return importFromCRSURL(pszDefinition);
4135 :
4136 1636 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4137 1 : return importFromWMSAUTO(pszDefinition);
4138 :
4139 : // WMS/WCS OGC codes like OGC:CRS84.
4140 1635 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4141 76 : return SetWellKnownGeogCS(pszDefinition + 4);
4142 :
4143 1559 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4144 1 : return SetWellKnownGeogCS(pszDefinition);
4145 :
4146 1558 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4147 : {
4148 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4149 0 : char *pszCode = strstr(pszFile, ",") + 1;
4150 :
4151 0 : pszCode[-1] = '\0';
4152 :
4153 0 : OGRErr err = importFromDict(pszFile, pszCode);
4154 0 : CPLFree(pszFile);
4155 :
4156 0 : return err;
4157 : }
4158 :
4159 1558 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4160 1553 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4161 : {
4162 521 : Clear();
4163 521 : return SetWellKnownGeogCS(pszDefinition);
4164 : }
4165 :
4166 : // PROJJSON
4167 1037 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4168 179 : (strstr(pszDefinition, "GeodeticCRS") ||
4169 179 : strstr(pszDefinition, "GeographicCRS") ||
4170 129 : strstr(pszDefinition, "ProjectedCRS") ||
4171 0 : strstr(pszDefinition, "VerticalCRS") ||
4172 0 : strstr(pszDefinition, "BoundCRS") ||
4173 0 : strstr(pszDefinition, "CompoundCRS") ||
4174 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4175 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4176 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4177 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4178 0 : strstr(pszDefinition, "EngineeringCRS") ||
4179 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4180 0 : strstr(pszDefinition, "ParametricCRS") ||
4181 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4182 0 : strstr(pszDefinition, "TemporalCRS") ||
4183 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4184 : {
4185 : PJ *pj;
4186 179 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4187 : {
4188 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4189 : // a unknown id.
4190 8 : CPLJSONDocument oCRSDoc;
4191 8 : if (!oCRSDoc.LoadMemory(pszDefinition))
4192 0 : return OGRERR_CORRUPT_DATA;
4193 8 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4194 8 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4195 8 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4196 : }
4197 : else
4198 : {
4199 171 : pj = proj_create(d->getPROJContext(), pszDefinition);
4200 : }
4201 179 : if (!pj)
4202 : {
4203 2 : return OGRERR_FAILURE;
4204 : }
4205 177 : Clear();
4206 177 : d->setPjCRS(pj);
4207 177 : return OGRERR_NONE;
4208 : }
4209 :
4210 858 : if (strstr(pszDefinition, "+proj") != nullptr ||
4211 406 : strstr(pszDefinition, "+init") != nullptr)
4212 452 : return importFromProj4(pszDefinition);
4213 :
4214 406 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4215 381 : STARTS_WITH_CI(pszDefinition, "https://"))
4216 : {
4217 26 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4218 : "ALLOW_NETWORK_ACCESS", "YES")))
4219 0 : return importFromUrl(pszDefinition);
4220 :
4221 26 : CPLError(CE_Failure, CPLE_AppDefined,
4222 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4223 : pszDefinition);
4224 26 : return OGRERR_FAILURE;
4225 : }
4226 :
4227 380 : if (EQUAL(pszDefinition, "osgb:BNG"))
4228 : {
4229 8 : return importFromEPSG(27700);
4230 : }
4231 :
4232 : // Used by German CityGML files
4233 372 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4234 : {
4235 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4236 0 : return SetFromUserInput("EPSG:25832+5783");
4237 : }
4238 372 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4239 : {
4240 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4241 3 : return SetFromUserInput("EPSG:25832+7837");
4242 : }
4243 :
4244 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4245 369 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4246 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4247 369 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4248 8 : return importFromEPSG(6668); // JGD2011
4249 361 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4250 : {
4251 : // FIXME when EPSG attributes a CRS code
4252 3 : return importFromWkt(
4253 : "GEOGCRS[\"JGD2024\",\n"
4254 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4255 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4256 : " LENGTHUNIT[\"metre\",1]]],\n"
4257 : " PRIMEM[\"Greenwich\",0,\n"
4258 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4259 : " CS[ellipsoidal,2],\n"
4260 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4261 : " ORDER[1],\n"
4262 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4263 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4264 : " ORDER[2],\n"
4265 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4266 : " USAGE[\n"
4267 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4268 : " AREA[\"Japan - onshore and offshore.\"],\n"
4269 3 : " BBOX[17.09,122.38,46.05,157.65]]]");
4270 : }
4271 :
4272 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4273 358 : const char *pszDot = strrchr(pszDefinition, ':');
4274 358 : if (pszDot)
4275 : {
4276 125 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4277 : auto authorities =
4278 125 : proj_get_authorities_from_database(d->getPROJContext());
4279 125 : if (authorities)
4280 : {
4281 125 : std::set<std::string> aosCandidateAuthorities;
4282 290 : for (auto iter = authorities; *iter; ++iter)
4283 : {
4284 288 : if (*iter == osPrefix)
4285 : {
4286 123 : aosCandidateAuthorities.clear();
4287 123 : aosCandidateAuthorities.insert(*iter);
4288 123 : break;
4289 : }
4290 : // Deal with "IAU_2015" as authority in the list and input
4291 : // "IAU:code"
4292 165 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4293 165 : 0 &&
4294 0 : (*iter)[osPrefix.size()] == '_')
4295 : {
4296 0 : aosCandidateAuthorities.insert(*iter);
4297 : }
4298 : // Deal with "IAU_2015" as authority in the list and input
4299 : // "IAU:2015:code"
4300 330 : else if (osPrefix.find(':') != std::string::npos &&
4301 165 : osPrefix.size() == strlen(*iter) &&
4302 165 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4303 : {
4304 0 : aosCandidateAuthorities.clear();
4305 0 : aosCandidateAuthorities.insert(*iter);
4306 0 : break;
4307 : }
4308 : }
4309 :
4310 125 : proj_string_list_destroy(authorities);
4311 :
4312 125 : if (!aosCandidateAuthorities.empty())
4313 : {
4314 123 : auto obj = proj_create_from_database(
4315 : d->getPROJContext(),
4316 123 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4317 : PJ_CATEGORY_CRS, false, nullptr);
4318 123 : if (!obj)
4319 : {
4320 16 : return OGRERR_FAILURE;
4321 : }
4322 107 : Clear();
4323 107 : d->setPjCRS(obj);
4324 107 : return OGRERR_NONE;
4325 : }
4326 : }
4327 : }
4328 :
4329 : /* -------------------------------------------------------------------- */
4330 : /* Try to open it as a file. */
4331 : /* -------------------------------------------------------------------- */
4332 235 : if (!CPLTestBool(
4333 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4334 : {
4335 : VSIStatBufL sStat;
4336 40 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4337 20 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4338 : {
4339 0 : CPLError(CE_Failure, CPLE_AppDefined,
4340 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4341 : pszDefinition);
4342 0 : return OGRERR_FAILURE;
4343 : }
4344 : // We used to silently return an error without a CE_Failure message
4345 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4346 20 : return OGRERR_CORRUPT_DATA;
4347 : }
4348 :
4349 430 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4350 215 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4351 215 : if (fp == nullptr)
4352 212 : return OGRERR_CORRUPT_DATA;
4353 :
4354 3 : const size_t nBufMax = 100000;
4355 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4356 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4357 3 : VSIFCloseL(fp);
4358 :
4359 3 : if (nBytes == nBufMax - 1)
4360 : {
4361 0 : CPLDebug("OGR",
4362 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4363 : "but it is to large for our generous buffer. Is it really "
4364 : "just a WKT definition?",
4365 : pszDefinition);
4366 0 : CPLFree(pszBuffer);
4367 0 : return OGRERR_FAILURE;
4368 : }
4369 :
4370 3 : pszBuffer[nBytes] = '\0';
4371 :
4372 3 : char *pszBufPtr = pszBuffer;
4373 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4374 0 : pszBufPtr++;
4375 :
4376 3 : OGRErr err = OGRERR_NONE;
4377 3 : if (pszBufPtr[0] == '<')
4378 0 : err = importFromXML(pszBufPtr);
4379 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4380 3 : strstr(pszBuffer, "+init") != nullptr) &&
4381 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4382 0 : strstr(pszBuffer, "extension") == nullptr))
4383 0 : err = importFromProj4(pszBufPtr);
4384 : else
4385 : {
4386 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4387 : {
4388 0 : pszBufPtr += 6;
4389 : }
4390 :
4391 : // coverity[tainted_data]
4392 3 : err = importFromWkt(pszBufPtr);
4393 : }
4394 :
4395 3 : CPLFree(pszBuffer);
4396 :
4397 3 : return err;
4398 : }
4399 :
4400 : /************************************************************************/
4401 : /* OSRSetFromUserInput() */
4402 : /************************************************************************/
4403 :
4404 : /**
4405 : * \brief Set spatial reference from various text formats.
4406 : *
4407 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4408 : *
4409 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4410 : */
4411 359 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4412 : const char *pszDef)
4413 :
4414 : {
4415 359 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4416 :
4417 359 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4418 : }
4419 :
4420 : /************************************************************************/
4421 : /* OSRSetFromUserInputEx() */
4422 : /************************************************************************/
4423 :
4424 : /**
4425 : * \brief Set spatial reference from various text formats.
4426 : *
4427 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4428 : *
4429 : * @since GDAL 3.9
4430 : */
4431 1163 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4432 : CSLConstList papszOptions)
4433 :
4434 : {
4435 1163 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4436 :
4437 1163 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4438 : }
4439 :
4440 : /************************************************************************/
4441 : /* ImportFromUrl() */
4442 : /************************************************************************/
4443 :
4444 : /**
4445 : * \brief Set spatial reference from a URL.
4446 : *
4447 : * This method will download the spatial reference at a given URL and
4448 : * feed it into SetFromUserInput for you.
4449 : *
4450 : * This method does the same thing as the OSRImportFromUrl() function.
4451 : *
4452 : * @param pszUrl text definition to try to deduce SRS from.
4453 : *
4454 : * @return OGRERR_NONE on success, or an error code with the curl
4455 : * error message if it is unable to download data.
4456 : */
4457 :
4458 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4459 :
4460 : {
4461 10 : TAKE_OPTIONAL_LOCK();
4462 :
4463 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4464 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4465 : {
4466 2 : CPLError(CE_Failure, CPLE_AppDefined,
4467 : "The given string is not recognized as a URL"
4468 : "starting with 'http://' -- %s",
4469 : pszUrl);
4470 2 : return OGRERR_FAILURE;
4471 : }
4472 :
4473 : /* -------------------------------------------------------------------- */
4474 : /* Fetch the result. */
4475 : /* -------------------------------------------------------------------- */
4476 3 : CPLErrorReset();
4477 :
4478 6 : std::string osUrl(pszUrl);
4479 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4480 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4481 : // to query WKT. To allow a static server to be used, rather append a
4482 : // "ogcwkt/" suffix.
4483 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4484 5 : "http://spatialreference.org/ref/"})
4485 : {
4486 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4487 : {
4488 : const CPLStringList aosTokens(
4489 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4490 3 : if (aosTokens.size() == 2)
4491 : {
4492 2 : osUrl = "https://spatialreference.org/ref/";
4493 2 : osUrl += aosTokens[0]; // authority
4494 2 : osUrl += '/';
4495 2 : osUrl += aosTokens[1]; // code
4496 2 : osUrl += "/ogcwkt/";
4497 : }
4498 3 : break;
4499 : }
4500 : }
4501 :
4502 3 : const char *pszTimeout = "TIMEOUT=10";
4503 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4504 :
4505 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4506 :
4507 : /* -------------------------------------------------------------------- */
4508 : /* Try to handle errors. */
4509 : /* -------------------------------------------------------------------- */
4510 :
4511 3 : if (psResult == nullptr)
4512 0 : return OGRERR_FAILURE;
4513 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4514 3 : psResult->pabyData == nullptr)
4515 : {
4516 0 : if (CPLGetLastErrorNo() == 0)
4517 : {
4518 0 : CPLError(CE_Failure, CPLE_AppDefined,
4519 : "No data was returned from the given URL");
4520 : }
4521 0 : CPLHTTPDestroyResult(psResult);
4522 0 : return OGRERR_FAILURE;
4523 : }
4524 :
4525 3 : if (psResult->nStatus != 0)
4526 : {
4527 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4528 : psResult->nStatus, psResult->pszErrBuf);
4529 0 : CPLHTTPDestroyResult(psResult);
4530 0 : return OGRERR_FAILURE;
4531 : }
4532 :
4533 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4534 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4535 3 : STARTS_WITH_CI(pszData, "https://"))
4536 : {
4537 0 : CPLError(CE_Failure, CPLE_AppDefined,
4538 : "The data that was downloaded also starts with 'http://' "
4539 : "and cannot be passed into SetFromUserInput. Is this "
4540 : "really a spatial reference definition? ");
4541 0 : CPLHTTPDestroyResult(psResult);
4542 0 : return OGRERR_FAILURE;
4543 : }
4544 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4545 : {
4546 0 : CPLHTTPDestroyResult(psResult);
4547 0 : return OGRERR_FAILURE;
4548 : }
4549 :
4550 3 : CPLHTTPDestroyResult(psResult);
4551 3 : return OGRERR_NONE;
4552 : }
4553 :
4554 : /************************************************************************/
4555 : /* OSRimportFromUrl() */
4556 : /************************************************************************/
4557 :
4558 : /**
4559 : * \brief Set spatial reference from a URL.
4560 : *
4561 : * This function is the same as OGRSpatialReference::importFromUrl()
4562 : */
4563 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4564 :
4565 : {
4566 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4567 :
4568 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4569 : }
4570 :
4571 : /************************************************************************/
4572 : /* importFromURNPart() */
4573 : /************************************************************************/
4574 5552 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4575 : const char *pszCode,
4576 : const char *pszURN)
4577 : {
4578 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4579 : (void)this;
4580 : (void)pszAuthority;
4581 : (void)pszCode;
4582 : (void)pszURN;
4583 : return OGRERR_FAILURE;
4584 : #else
4585 : /* -------------------------------------------------------------------- */
4586 : /* Is this an EPSG code? Note that we import it with EPSG */
4587 : /* preferred axis ordering for geographic coordinate systems. */
4588 : /* -------------------------------------------------------------------- */
4589 5552 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4590 4990 : return importFromEPSGA(atoi(pszCode));
4591 :
4592 : /* -------------------------------------------------------------------- */
4593 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4594 : /* -------------------------------------------------------------------- */
4595 562 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4596 0 : return importFromDict("IAU2000.wkt", pszCode);
4597 :
4598 : /* -------------------------------------------------------------------- */
4599 : /* Is this an OGC code? */
4600 : /* -------------------------------------------------------------------- */
4601 562 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4602 : {
4603 1 : CPLError(CE_Failure, CPLE_AppDefined,
4604 : "URN %s has unrecognized authority.", pszURN);
4605 1 : return OGRERR_FAILURE;
4606 : }
4607 :
4608 561 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4609 549 : return SetWellKnownGeogCS(pszCode);
4610 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4611 0 : return SetWellKnownGeogCS(pszCode);
4612 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4613 0 : return SetWellKnownGeogCS(pszCode);
4614 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4615 10 : return SetWellKnownGeogCS("CRS84");
4616 :
4617 : /* -------------------------------------------------------------------- */
4618 : /* Handle auto codes. We need to convert from format */
4619 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4620 : /* -------------------------------------------------------------------- */
4621 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4622 : {
4623 2 : char szWMSAuto[100] = {'\0'};
4624 :
4625 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4626 0 : return OGRERR_FAILURE;
4627 :
4628 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4629 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4630 : {
4631 26 : if (szWMSAuto[i] == ':')
4632 4 : szWMSAuto[i] = ',';
4633 : }
4634 :
4635 2 : return importFromWMSAUTO(szWMSAuto);
4636 : }
4637 :
4638 : /* -------------------------------------------------------------------- */
4639 : /* Not a recognise OGC item. */
4640 : /* -------------------------------------------------------------------- */
4641 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4642 : pszURN);
4643 :
4644 0 : return OGRERR_FAILURE;
4645 : #endif
4646 : }
4647 :
4648 : /************************************************************************/
4649 : /* importFromURN() */
4650 : /* */
4651 : /* See OGC recommendation paper 06-023r1 or later for details. */
4652 : /************************************************************************/
4653 :
4654 : /**
4655 : * \brief Initialize from OGC URN.
4656 : *
4657 : * Initializes this spatial reference from a coordinate system defined
4658 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4659 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4660 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4661 : *
4662 : * This method is also support through SetFromUserInput() which can
4663 : * normally be used for URNs.
4664 : *
4665 : * @param pszURN the urn string.
4666 : *
4667 : * @return OGRERR_NONE on success or an error code.
4668 : */
4669 :
4670 840 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4671 :
4672 : {
4673 840 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4674 1595 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4675 755 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4676 : CPL_VALUE_INTEGER)
4677 : {
4678 752 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4679 : }
4680 :
4681 176 : TAKE_OPTIONAL_LOCK();
4682 :
4683 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4684 :
4685 : // PROJ 8.2.0 has support for IAU codes now.
4686 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4687 : /* -------------------------------------------------------------------- */
4688 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4689 : /* -------------------------------------------------------------------- */
4690 : const char *pszIAU = strstr(pszURN, "IAU");
4691 : if (pszIAU)
4692 : {
4693 : const char *pszCode = strchr(pszIAU, ':');
4694 : if (pszCode)
4695 : {
4696 : ++pszCode;
4697 : if (*pszCode == ':')
4698 : ++pszCode;
4699 : return importFromDict("IAU2000.wkt", pszCode);
4700 : }
4701 : }
4702 : #endif
4703 :
4704 : if (strlen(pszURN) >= 1000)
4705 : {
4706 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4707 : return OGRERR_CORRUPT_DATA;
4708 : }
4709 : auto obj = proj_create(d->getPROJContext(), pszURN);
4710 : if (!obj)
4711 : {
4712 : return OGRERR_FAILURE;
4713 : }
4714 : Clear();
4715 : d->setPjCRS(obj);
4716 : return OGRERR_NONE;
4717 : #else
4718 88 : const char *pszCur = nullptr;
4719 :
4720 88 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4721 23 : pszCur = pszURN + 16;
4722 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4723 1 : pszCur = pszURN + 20;
4724 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4725 62 : pszCur = pszURN + 18;
4726 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4727 0 : pszCur = pszURN + 16;
4728 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4729 0 : pszCur = pszURN + 20;
4730 : else
4731 : {
4732 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4733 : pszURN);
4734 2 : return OGRERR_FAILURE;
4735 : }
4736 :
4737 : /* -------------------------------------------------------------------- */
4738 : /* Clear any existing definition. */
4739 : /* -------------------------------------------------------------------- */
4740 86 : Clear();
4741 :
4742 : /* -------------------------------------------------------------------- */
4743 : /* Find code (ignoring version) out of string like: */
4744 : /* */
4745 : /* authority:[version]:code */
4746 : /* -------------------------------------------------------------------- */
4747 86 : const char *pszAuthority = pszCur;
4748 :
4749 : // skip authority
4750 414 : while (*pszCur != ':' && *pszCur)
4751 328 : pszCur++;
4752 86 : if (*pszCur == ':')
4753 86 : pszCur++;
4754 :
4755 : // skip version
4756 86 : const char *pszBeforeVersion = pszCur;
4757 387 : while (*pszCur != ':' && *pszCur)
4758 301 : pszCur++;
4759 86 : if (*pszCur == ':')
4760 58 : pszCur++;
4761 : else
4762 : // We come here in the case, the content to parse is authority:code
4763 : // (instead of authority::code) which is probably illegal according to
4764 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4765 : // for example in what is returned by GeoServer.
4766 28 : pszCur = pszBeforeVersion;
4767 :
4768 86 : const char *pszCode = pszCur;
4769 :
4770 86 : const char *pszComma = strchr(pszCur, ',');
4771 86 : if (pszComma == nullptr)
4772 85 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4773 :
4774 : // There's a second part with the vertical SRS.
4775 1 : pszCur = pszComma + 1;
4776 1 : if (!STARTS_WITH(pszCur, "crs:"))
4777 : {
4778 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4779 : pszURN);
4780 0 : return OGRERR_FAILURE;
4781 : }
4782 :
4783 1 : pszCur += 4;
4784 :
4785 1 : char *pszFirstCode = CPLStrdup(pszCode);
4786 1 : pszFirstCode[pszComma - pszCode] = '\0';
4787 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4788 1 : CPLFree(pszFirstCode);
4789 :
4790 : // Do we want to turn this into a compound definition
4791 : // with a vertical datum?
4792 1 : if (eStatus != OGRERR_NONE)
4793 0 : return eStatus;
4794 :
4795 : /* -------------------------------------------------------------------- */
4796 : /* Find code (ignoring version) out of string like: */
4797 : /* */
4798 : /* authority:[version]:code */
4799 : /* -------------------------------------------------------------------- */
4800 1 : pszAuthority = pszCur;
4801 :
4802 : // skip authority
4803 5 : while (*pszCur != ':' && *pszCur)
4804 4 : pszCur++;
4805 1 : if (*pszCur == ':')
4806 1 : pszCur++;
4807 :
4808 : // skip version
4809 1 : pszBeforeVersion = pszCur;
4810 1 : while (*pszCur != ':' && *pszCur)
4811 0 : pszCur++;
4812 1 : if (*pszCur == ':')
4813 1 : pszCur++;
4814 : else
4815 0 : pszCur = pszBeforeVersion;
4816 :
4817 1 : pszCode = pszCur;
4818 :
4819 2 : OGRSpatialReference oVertSRS;
4820 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4821 1 : if (eStatus == OGRERR_NONE)
4822 : {
4823 1 : OGRSpatialReference oHorizSRS(*this);
4824 :
4825 1 : Clear();
4826 :
4827 1 : oHorizSRS.d->refreshProjObj();
4828 1 : oVertSRS.d->refreshProjObj();
4829 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4830 0 : return OGRERR_FAILURE;
4831 :
4832 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4833 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4834 :
4835 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4836 1 : osName += " + ";
4837 1 : osName += pszVertName ? pszVertName : "";
4838 :
4839 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4840 : }
4841 :
4842 1 : return eStatus;
4843 : #endif
4844 : }
4845 :
4846 : /************************************************************************/
4847 : /* importFromCRSURL() */
4848 : /* */
4849 : /* See OGC Best Practice document 11-135 for details. */
4850 : /************************************************************************/
4851 :
4852 : /**
4853 : * \brief Initialize from OGC URL.
4854 : *
4855 : * Initializes this spatial reference from a coordinate system defined
4856 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4857 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4858 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4859 : *
4860 : * This method is also supported through SetFromUserInput() which can
4861 : * normally be used for URLs.
4862 : *
4863 : * @param pszURL the URL string.
4864 : *
4865 : * @return OGRERR_NONE on success or an error code.
4866 : */
4867 :
4868 5478 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4869 :
4870 : {
4871 10956 : TAKE_OPTIONAL_LOCK();
4872 :
4873 : #if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4874 5478 : if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4875 : {
4876 12 : PJ *obj = proj_create(
4877 : d->getPROJContext(),
4878 : "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4879 : " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4880 : " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4881 : " MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4882 : " MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4883 : " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4884 : " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4885 : " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4886 : " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4887 : " MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4888 : " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4889 : " LENGTHUNIT[\"metre\",1]],\n"
4890 : " ENSEMBLEACCURACY[2.0]],\n"
4891 : " PRIMEM[\"Greenwich\",0,\n"
4892 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4893 : " CS[ellipsoidal,3],\n"
4894 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4895 : " ORDER[1],\n"
4896 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4897 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4898 : " ORDER[2],\n"
4899 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4900 : " AXIS[\"ellipsoidal height (h)\",up,\n"
4901 : " ORDER[3],\n"
4902 : " LENGTHUNIT[\"metre\",1]],\n"
4903 : " USAGE[\n"
4904 : " SCOPE[\"3D system frequently used in GIS, Web APIs and "
4905 : "Web applications\"],\n"
4906 : " AREA[\"World.\"],\n"
4907 : " BBOX[-90,-180,90,180]],\n"
4908 : " ID[\"OGC\",\"CRS84h\"]]");
4909 12 : if (!obj)
4910 : {
4911 0 : return OGRERR_FAILURE;
4912 : }
4913 12 : Clear();
4914 12 : d->setPjCRS(obj);
4915 12 : return OGRERR_NONE;
4916 : }
4917 : #endif
4918 :
4919 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4920 : if (strlen(pszURL) >= 10000)
4921 : {
4922 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4923 : return OGRERR_CORRUPT_DATA;
4924 : }
4925 :
4926 : PJ *obj;
4927 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4928 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4929 : {
4930 : obj = proj_create(
4931 : d->getPROJContext(),
4932 : CPLSPrintf("IAU:%s",
4933 : pszURL +
4934 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4935 : }
4936 : else
4937 : #endif
4938 : {
4939 : obj = proj_create(d->getPROJContext(), pszURL);
4940 : }
4941 : if (!obj)
4942 : {
4943 : return OGRERR_FAILURE;
4944 : }
4945 : Clear();
4946 : d->setPjCRS(obj);
4947 : return OGRERR_NONE;
4948 : #else
4949 5466 : const char *pszCur = nullptr;
4950 :
4951 5466 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4952 2 : pszCur = pszURL + 26;
4953 5464 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4954 1 : pszCur = pszURL + 27;
4955 5463 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4956 5462 : pszCur = pszURL + 30;
4957 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4958 1 : pszCur = pszURL + 31;
4959 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4960 0 : pszCur = pszURL + 23;
4961 : else
4962 : {
4963 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4964 : pszURL);
4965 0 : return OGRERR_FAILURE;
4966 : }
4967 :
4968 5466 : if (*pszCur == '\0')
4969 : {
4970 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4971 0 : return OGRERR_FAILURE;
4972 : }
4973 :
4974 : /* -------------------------------------------------------------------- */
4975 : /* Clear any existing definition. */
4976 : /* -------------------------------------------------------------------- */
4977 5466 : Clear();
4978 :
4979 5466 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4980 : {
4981 : /* --------------------------------------------------------------------
4982 : */
4983 : /* It's a compound CRS, of the form: */
4984 : /* */
4985 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4986 : /* --------------------------------------------------------------------
4987 : */
4988 1 : pszCur += 12;
4989 :
4990 : // Extract each component CRS URL.
4991 1 : int iComponentUrl = 2;
4992 :
4993 2 : CPLString osName = "";
4994 1 : Clear();
4995 :
4996 3 : while (iComponentUrl != -1)
4997 : {
4998 2 : char searchStr[15] = {};
4999 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5000 :
5001 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
5002 :
5003 : // Figure out the next component URL.
5004 2 : char *pszComponentUrl = nullptr;
5005 :
5006 2 : if (pszUrlEnd)
5007 : {
5008 1 : size_t nLen = pszUrlEnd - pszCur;
5009 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5010 1 : strncpy(pszComponentUrl, pszCur, nLen);
5011 1 : pszComponentUrl[nLen] = '\0';
5012 :
5013 1 : ++iComponentUrl;
5014 1 : pszCur += nLen + strlen(searchStr);
5015 : }
5016 : else
5017 : {
5018 1 : if (iComponentUrl == 2)
5019 : {
5020 0 : CPLError(CE_Failure, CPLE_AppDefined,
5021 : "Compound CRS URLs must have at least two "
5022 : "component CRSs.");
5023 0 : return OGRERR_FAILURE;
5024 : }
5025 : else
5026 : {
5027 1 : pszComponentUrl = CPLStrdup(pszCur);
5028 : // no more components
5029 1 : iComponentUrl = -1;
5030 : }
5031 : }
5032 :
5033 2 : OGRSpatialReference oComponentSRS;
5034 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5035 :
5036 2 : CPLFree(pszComponentUrl);
5037 2 : pszComponentUrl = nullptr;
5038 :
5039 2 : if (eStatus == OGRERR_NONE)
5040 : {
5041 2 : if (osName.length() != 0)
5042 : {
5043 1 : osName += " + ";
5044 : }
5045 2 : osName += oComponentSRS.GetRoot()->GetValue();
5046 2 : SetNode("COMPD_CS", osName);
5047 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5048 : }
5049 : else
5050 0 : return eStatus;
5051 : }
5052 :
5053 1 : return OGRERR_NONE;
5054 : }
5055 :
5056 : /* -------------------------------------------------------------------- */
5057 : /* It's a normal CRS URL, of the form: */
5058 : /* */
5059 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5060 : /* -------------------------------------------------------------------- */
5061 5465 : ++pszCur;
5062 5465 : const char *pszAuthority = pszCur;
5063 :
5064 : // skip authority
5065 126776 : while (*pszCur != '/' && *pszCur)
5066 121311 : pszCur++;
5067 5465 : if (*pszCur == '/')
5068 5464 : pszCur++;
5069 :
5070 : // skip version
5071 11991 : while (*pszCur != '/' && *pszCur)
5072 6526 : pszCur++;
5073 5465 : if (*pszCur == '/')
5074 5464 : pszCur++;
5075 :
5076 5465 : const char *pszCode = pszCur;
5077 :
5078 5465 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5079 : #endif
5080 : }
5081 :
5082 : /************************************************************************/
5083 : /* importFromWMSAUTO() */
5084 : /************************************************************************/
5085 :
5086 : /**
5087 : * \brief Initialize from WMSAUTO string.
5088 : *
5089 : * Note that the WMS 1.3 specification does not include the
5090 : * units code, while apparently earlier specs do. We try to
5091 : * guess around this.
5092 : *
5093 : * @param pszDefinition the WMSAUTO string
5094 : *
5095 : * @return OGRERR_NONE on success or an error code.
5096 : */
5097 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5098 :
5099 : {
5100 6 : TAKE_OPTIONAL_LOCK();
5101 :
5102 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5103 : if (strlen(pszDefinition) >= 10000)
5104 : {
5105 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5106 : return OGRERR_CORRUPT_DATA;
5107 : }
5108 :
5109 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5110 : if (!obj)
5111 : {
5112 : return OGRERR_FAILURE;
5113 : }
5114 : Clear();
5115 : d->setPjCRS(obj);
5116 : return OGRERR_NONE;
5117 : #else
5118 : int nProjId, nUnitsId;
5119 3 : double dfRefLong, dfRefLat = 0.0;
5120 :
5121 : /* -------------------------------------------------------------------- */
5122 : /* Tokenize */
5123 : /* -------------------------------------------------------------------- */
5124 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5125 3 : pszDefinition += 5;
5126 :
5127 : char **papszTokens =
5128 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5129 :
5130 3 : if (CSLCount(papszTokens) == 4)
5131 : {
5132 0 : nProjId = atoi(papszTokens[0]);
5133 0 : nUnitsId = atoi(papszTokens[1]);
5134 0 : dfRefLong = CPLAtof(papszTokens[2]);
5135 0 : dfRefLat = CPLAtof(papszTokens[3]);
5136 : }
5137 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5138 : {
5139 0 : nProjId = atoi(papszTokens[0]);
5140 0 : nUnitsId = atoi(papszTokens[1]);
5141 0 : dfRefLong = CPLAtof(papszTokens[2]);
5142 0 : dfRefLat = 0.0;
5143 : }
5144 3 : else if (CSLCount(papszTokens) == 3)
5145 : {
5146 2 : nProjId = atoi(papszTokens[0]);
5147 2 : nUnitsId = 9001;
5148 2 : dfRefLong = CPLAtof(papszTokens[1]);
5149 2 : dfRefLat = CPLAtof(papszTokens[2]);
5150 : }
5151 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5152 : {
5153 0 : nProjId = atoi(papszTokens[0]);
5154 0 : nUnitsId = 9001;
5155 0 : dfRefLong = CPLAtof(papszTokens[1]);
5156 : }
5157 : else
5158 : {
5159 1 : CSLDestroy(papszTokens);
5160 1 : CPLError(CE_Failure, CPLE_AppDefined,
5161 : "AUTO projection has wrong number of arguments, expected\n"
5162 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5163 : "AUTO:proj_id,ref_long,ref_lat");
5164 1 : return OGRERR_FAILURE;
5165 : }
5166 :
5167 2 : CSLDestroy(papszTokens);
5168 2 : papszTokens = nullptr;
5169 :
5170 : /* -------------------------------------------------------------------- */
5171 : /* Build coordsys. */
5172 : /* -------------------------------------------------------------------- */
5173 2 : Clear();
5174 :
5175 : /* -------------------------------------------------------------------- */
5176 : /* Set WGS84. */
5177 : /* -------------------------------------------------------------------- */
5178 2 : SetWellKnownGeogCS("WGS84");
5179 :
5180 2 : switch (nProjId)
5181 : {
5182 2 : case 42001: // Auto UTM
5183 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5184 : dfRefLat >= 0.0);
5185 2 : break;
5186 :
5187 0 : case 42002: // Auto TM (strangely very UTM-like).
5188 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5189 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5190 0 : break;
5191 :
5192 0 : case 42003: // Auto Orthographic.
5193 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5194 0 : break;
5195 :
5196 0 : case 42004: // Auto Equirectangular
5197 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5198 0 : break;
5199 :
5200 0 : case 42005:
5201 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5202 0 : break;
5203 :
5204 0 : default:
5205 0 : CPLError(CE_Failure, CPLE_AppDefined,
5206 : "Unsupported projection id in importFromWMSAUTO(): %d",
5207 : nProjId);
5208 0 : return OGRERR_FAILURE;
5209 : }
5210 :
5211 : /* -------------------------------------------------------------------- */
5212 : /* Set units. */
5213 : /* -------------------------------------------------------------------- */
5214 :
5215 2 : switch (nUnitsId)
5216 : {
5217 2 : case 9001:
5218 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5219 2 : break;
5220 :
5221 0 : case 9002:
5222 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5223 0 : break;
5224 :
5225 0 : case 9003:
5226 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5227 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5228 0 : break;
5229 :
5230 0 : default:
5231 0 : CPLError(CE_Failure, CPLE_AppDefined,
5232 : "Unsupported units code (%d).", nUnitsId);
5233 0 : return OGRERR_FAILURE;
5234 : break;
5235 : }
5236 :
5237 2 : return OGRERR_NONE;
5238 : #endif
5239 : }
5240 :
5241 : /************************************************************************/
5242 : /* GetSemiMajor() */
5243 : /************************************************************************/
5244 :
5245 : /**
5246 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5247 : *
5248 : * This method does the same thing as the C function OSRGetSemiMajor().
5249 : *
5250 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5251 : * can be found.
5252 : *
5253 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5254 : */
5255 :
5256 6817 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5257 :
5258 : {
5259 13634 : TAKE_OPTIONAL_LOCK();
5260 :
5261 6817 : if (pnErr != nullptr)
5262 3533 : *pnErr = OGRERR_FAILURE;
5263 :
5264 6817 : d->refreshProjObj();
5265 6817 : if (!d->m_pj_crs)
5266 111 : return SRS_WGS84_SEMIMAJOR;
5267 :
5268 6706 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5269 6706 : if (!ellps)
5270 5 : return SRS_WGS84_SEMIMAJOR;
5271 :
5272 6701 : double dfSemiMajor = 0.0;
5273 6701 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5274 : nullptr, nullptr, nullptr);
5275 6701 : proj_destroy(ellps);
5276 :
5277 6701 : if (dfSemiMajor > 0)
5278 : {
5279 6701 : if (pnErr != nullptr)
5280 3419 : *pnErr = OGRERR_NONE;
5281 6701 : return dfSemiMajor;
5282 : }
5283 :
5284 0 : return SRS_WGS84_SEMIMAJOR;
5285 : }
5286 :
5287 : /************************************************************************/
5288 : /* OSRGetSemiMajor() */
5289 : /************************************************************************/
5290 :
5291 : /**
5292 : * \brief Get spheroid semi major axis.
5293 : *
5294 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5295 : */
5296 87 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5297 :
5298 : {
5299 87 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5300 :
5301 87 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5302 : }
5303 :
5304 : /************************************************************************/
5305 : /* GetInvFlattening() */
5306 : /************************************************************************/
5307 :
5308 : /**
5309 : * \brief Get spheroid inverse flattening.
5310 : *
5311 : * This method does the same thing as the C function OSRGetInvFlattening().
5312 : *
5313 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5314 : * can be found.
5315 : *
5316 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5317 : */
5318 :
5319 4505 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5320 :
5321 : {
5322 9010 : TAKE_OPTIONAL_LOCK();
5323 :
5324 4505 : if (pnErr != nullptr)
5325 3425 : *pnErr = OGRERR_FAILURE;
5326 :
5327 4505 : d->refreshProjObj();
5328 4505 : if (!d->m_pj_crs)
5329 111 : return SRS_WGS84_INVFLATTENING;
5330 :
5331 4394 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5332 4394 : if (!ellps)
5333 2 : return SRS_WGS84_INVFLATTENING;
5334 :
5335 4392 : double dfInvFlattening = -1.0;
5336 4392 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5337 : nullptr, &dfInvFlattening);
5338 4392 : proj_destroy(ellps);
5339 :
5340 4392 : if (dfInvFlattening >= 0.0)
5341 : {
5342 4392 : if (pnErr != nullptr)
5343 3314 : *pnErr = OGRERR_NONE;
5344 4392 : return dfInvFlattening;
5345 : }
5346 :
5347 0 : return SRS_WGS84_INVFLATTENING;
5348 : }
5349 :
5350 : /************************************************************************/
5351 : /* OSRGetInvFlattening() */
5352 : /************************************************************************/
5353 :
5354 : /**
5355 : * \brief Get spheroid inverse flattening.
5356 : *
5357 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5358 : */
5359 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5360 :
5361 : {
5362 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5363 :
5364 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5365 : }
5366 :
5367 : /************************************************************************/
5368 : /* GetEccentricity() */
5369 : /************************************************************************/
5370 :
5371 : /**
5372 : * \brief Get spheroid eccentricity
5373 : *
5374 : * @return eccentricity (or -1 in case of error)
5375 : */
5376 :
5377 0 : double OGRSpatialReference::GetEccentricity() const
5378 :
5379 : {
5380 0 : OGRErr eErr = OGRERR_NONE;
5381 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5382 0 : if (eErr != OGRERR_NONE)
5383 : {
5384 0 : return -1.0;
5385 : }
5386 0 : if (dfInvFlattening == 0.0)
5387 0 : return 0.0;
5388 0 : if (dfInvFlattening < 0.5)
5389 0 : return -1.0;
5390 0 : return sqrt(2.0 / dfInvFlattening -
5391 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5392 : }
5393 :
5394 : /************************************************************************/
5395 : /* GetSquaredEccentricity() */
5396 : /************************************************************************/
5397 :
5398 : /**
5399 : * \brief Get spheroid squared eccentricity
5400 : *
5401 : * @return squared eccentricity (or -1 in case of error)
5402 : */
5403 :
5404 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5405 :
5406 : {
5407 0 : OGRErr eErr = OGRERR_NONE;
5408 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5409 0 : if (eErr != OGRERR_NONE)
5410 : {
5411 0 : return -1.0;
5412 : }
5413 0 : if (dfInvFlattening == 0.0)
5414 0 : return 0.0;
5415 0 : if (dfInvFlattening < 0.5)
5416 0 : return -1.0;
5417 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5418 : }
5419 :
5420 : /************************************************************************/
5421 : /* GetSemiMinor() */
5422 : /************************************************************************/
5423 :
5424 : /**
5425 : * \brief Get spheroid semi minor axis.
5426 : *
5427 : * This method does the same thing as the C function OSRGetSemiMinor().
5428 : *
5429 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5430 : * can be found.
5431 : *
5432 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5433 : */
5434 :
5435 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5436 :
5437 : {
5438 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5439 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5440 :
5441 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5442 : }
5443 :
5444 : /************************************************************************/
5445 : /* OSRGetSemiMinor() */
5446 : /************************************************************************/
5447 :
5448 : /**
5449 : * \brief Get spheroid semi minor axis.
5450 : *
5451 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5452 : */
5453 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5454 :
5455 : {
5456 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5457 :
5458 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5459 : }
5460 :
5461 : /************************************************************************/
5462 : /* SetLocalCS() */
5463 : /************************************************************************/
5464 :
5465 : /**
5466 : * \brief Set the user visible LOCAL_CS name.
5467 : *
5468 : * This method is the same as the C function OSRSetLocalCS().
5469 : *
5470 : * This method will ensure a LOCAL_CS node is created as the root,
5471 : * and set the provided name on it. It must be used before SetLinearUnits().
5472 : *
5473 : * @param pszName the user visible name to assign. Not used as a key.
5474 : *
5475 : * @return OGRERR_NONE on success.
5476 : */
5477 :
5478 2898 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5479 :
5480 : {
5481 5796 : TAKE_OPTIONAL_LOCK();
5482 :
5483 2898 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5484 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5485 : {
5486 2898 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5487 : }
5488 : else
5489 : {
5490 0 : CPLDebug("OGR",
5491 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5492 : "It appears an incompatible object already exists.",
5493 : pszName);
5494 0 : return OGRERR_FAILURE;
5495 : }
5496 :
5497 2898 : return OGRERR_NONE;
5498 : }
5499 :
5500 : /************************************************************************/
5501 : /* OSRSetLocalCS() */
5502 : /************************************************************************/
5503 :
5504 : /**
5505 : * \brief Set the user visible LOCAL_CS name.
5506 : *
5507 : * This function is the same as OGRSpatialReference::SetLocalCS()
5508 : */
5509 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5510 :
5511 : {
5512 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5513 :
5514 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5515 : }
5516 :
5517 : /************************************************************************/
5518 : /* SetGeocCS() */
5519 : /************************************************************************/
5520 :
5521 : /**
5522 : * \brief Set the user visible GEOCCS name.
5523 : *
5524 : * This method is the same as the C function OSRSetGeocCS().
5525 :
5526 : * This method will ensure a GEOCCS node is created as the root,
5527 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5528 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5529 : * the GEOGCS.
5530 : *
5531 : * @param pszName the user visible name to assign. Not used as a key.
5532 : *
5533 : * @return OGRERR_NONE on success.
5534 : *
5535 : */
5536 :
5537 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5538 :
5539 : {
5540 12 : TAKE_OPTIONAL_LOCK();
5541 :
5542 6 : OGRErr eErr = OGRERR_NONE;
5543 6 : d->refreshProjObj();
5544 6 : d->demoteFromBoundCRS();
5545 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5546 : {
5547 3 : d->setPjCRS(proj_create_geocentric_crs(
5548 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5549 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5550 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5551 : "Metre", 1.0));
5552 : }
5553 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5554 : {
5555 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5556 : }
5557 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5558 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5559 : {
5560 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5561 : #if PROJ_VERSION_MAJOR > 7 || \
5562 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5563 : if (datum == nullptr)
5564 : {
5565 : datum =
5566 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5567 : }
5568 : #endif
5569 1 : if (datum == nullptr)
5570 : {
5571 0 : d->undoDemoteFromBoundCRS();
5572 0 : return OGRERR_FAILURE;
5573 : }
5574 :
5575 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5576 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5577 : 0.0);
5578 1 : d->setPjCRS(pj_crs);
5579 :
5580 1 : proj_destroy(datum);
5581 : }
5582 : else
5583 : {
5584 1 : CPLDebug("OGR",
5585 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5586 : "It appears an incompatible object already exists.",
5587 : pszName);
5588 1 : eErr = OGRERR_FAILURE;
5589 : }
5590 6 : d->undoDemoteFromBoundCRS();
5591 :
5592 6 : return eErr;
5593 : }
5594 :
5595 : /************************************************************************/
5596 : /* OSRSetGeocCS() */
5597 : /************************************************************************/
5598 :
5599 : /**
5600 : * \brief Set the user visible PROJCS name.
5601 : *
5602 : * This function is the same as OGRSpatialReference::SetGeocCS()
5603 : *
5604 : */
5605 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5606 :
5607 : {
5608 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5609 :
5610 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5611 : }
5612 :
5613 : /************************************************************************/
5614 : /* SetVertCS() */
5615 : /************************************************************************/
5616 :
5617 : /**
5618 : * \brief Set the user visible VERT_CS name.
5619 : *
5620 : * This method is the same as the C function OSRSetVertCS().
5621 :
5622 : * This method will ensure a VERT_CS node is created if needed. If the
5623 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5624 : * turned into a COMPD_CS.
5625 : *
5626 : * @param pszVertCSName the user visible name of the vertical coordinate
5627 : * system. Not used as a key.
5628 : *
5629 : * @param pszVertDatumName the user visible name of the vertical datum. It
5630 : * is helpful if this matches the EPSG name.
5631 : *
5632 : * @param nVertDatumType the OGC vertical datum type. Ignored
5633 : *
5634 : * @return OGRERR_NONE on success.
5635 : *
5636 : */
5637 :
5638 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5639 : const char *pszVertDatumName,
5640 : int nVertDatumType)
5641 :
5642 : {
5643 1 : TAKE_OPTIONAL_LOCK();
5644 :
5645 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5646 :
5647 1 : d->refreshProjObj();
5648 :
5649 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5650 : pszVertDatumName, nullptr, 0.0);
5651 :
5652 : /* -------------------------------------------------------------------- */
5653 : /* Handle the case where we want to make a compound coordinate */
5654 : /* system. */
5655 : /* -------------------------------------------------------------------- */
5656 1 : if (IsProjected() || IsGeographic())
5657 : {
5658 1 : auto compoundCRS = proj_create_compound_crs(
5659 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5660 1 : proj_destroy(vertCRS);
5661 1 : d->setPjCRS(compoundCRS);
5662 : }
5663 : else
5664 : {
5665 0 : d->setPjCRS(vertCRS);
5666 : }
5667 2 : return OGRERR_NONE;
5668 : }
5669 :
5670 : /************************************************************************/
5671 : /* OSRSetVertCS() */
5672 : /************************************************************************/
5673 :
5674 : /**
5675 : * \brief Setup the vertical coordinate system.
5676 : *
5677 : * This function is the same as OGRSpatialReference::SetVertCS()
5678 : *
5679 : */
5680 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5681 : const char *pszVertDatumName, int nVertDatumType)
5682 :
5683 : {
5684 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5685 :
5686 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5687 0 : nVertDatumType);
5688 : }
5689 :
5690 : /************************************************************************/
5691 : /* SetCompoundCS() */
5692 : /************************************************************************/
5693 :
5694 : /**
5695 : * \brief Setup a compound coordinate system.
5696 : *
5697 : * This method is the same as the C function OSRSetCompoundCS().
5698 :
5699 : * This method is replace the current SRS with a COMPD_CS coordinate system
5700 : * consisting of the passed in horizontal and vertical coordinate systems.
5701 : *
5702 : * @param pszName the name of the compound coordinate system.
5703 : *
5704 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5705 : *
5706 : * @param poVertSRS the vertical SRS (VERT_CS).
5707 : *
5708 : * @return OGRERR_NONE on success.
5709 : */
5710 :
5711 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5712 : const OGRSpatialReference *poHorizSRS,
5713 : const OGRSpatialReference *poVertSRS)
5714 :
5715 : {
5716 184 : TAKE_OPTIONAL_LOCK();
5717 :
5718 : /* -------------------------------------------------------------------- */
5719 : /* Verify these are legal horizontal and vertical coordinate */
5720 : /* systems. */
5721 : /* -------------------------------------------------------------------- */
5722 92 : if (!poVertSRS->IsVertical())
5723 : {
5724 0 : CPLError(CE_Failure, CPLE_AppDefined,
5725 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5726 0 : return OGRERR_FAILURE;
5727 : }
5728 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5729 : {
5730 0 : CPLError(CE_Failure, CPLE_AppDefined,
5731 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5732 : "GEOGCS.");
5733 0 : return OGRERR_FAILURE;
5734 : }
5735 :
5736 : /* -------------------------------------------------------------------- */
5737 : /* Replace with compound srs. */
5738 : /* -------------------------------------------------------------------- */
5739 92 : Clear();
5740 :
5741 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5742 92 : poHorizSRS->d->m_pj_crs,
5743 92 : poVertSRS->d->m_pj_crs);
5744 92 : d->setPjCRS(compoundCRS);
5745 :
5746 92 : return OGRERR_NONE;
5747 : }
5748 :
5749 : /************************************************************************/
5750 : /* OSRSetCompoundCS() */
5751 : /************************************************************************/
5752 :
5753 : /**
5754 : * \brief Setup a compound coordinate system.
5755 : *
5756 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5757 : */
5758 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5759 : OGRSpatialReferenceH hHorizSRS,
5760 : OGRSpatialReferenceH hVertSRS)
5761 :
5762 : {
5763 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5764 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5765 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5766 :
5767 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5768 16 : ToPointer(hVertSRS));
5769 : }
5770 :
5771 : /************************************************************************/
5772 : /* SetProjCS() */
5773 : /************************************************************************/
5774 :
5775 : /**
5776 : * \brief Set the user visible PROJCS name.
5777 : *
5778 : * This method is the same as the C function OSRSetProjCS().
5779 : *
5780 : * This method will ensure a PROJCS node is created as the root,
5781 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5782 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5783 : *
5784 : * @param pszName the user visible name to assign. Not used as a key.
5785 : *
5786 : * @return OGRERR_NONE on success.
5787 : */
5788 :
5789 4735 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5790 :
5791 : {
5792 4735 : TAKE_OPTIONAL_LOCK();
5793 :
5794 4735 : d->refreshProjObj();
5795 4735 : d->demoteFromBoundCRS();
5796 4735 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5797 : {
5798 487 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5799 : }
5800 : else
5801 : {
5802 4248 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5803 : nullptr, nullptr, nullptr,
5804 : nullptr, nullptr, 0, nullptr);
5805 4248 : auto cs = proj_create_cartesian_2D_cs(
5806 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5807 :
5808 4248 : auto projCRS = proj_create_projected_crs(
5809 4248 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5810 4248 : proj_destroy(dummyConv);
5811 4248 : proj_destroy(cs);
5812 :
5813 4248 : d->setPjCRS(projCRS);
5814 : }
5815 4735 : d->undoDemoteFromBoundCRS();
5816 9470 : return OGRERR_NONE;
5817 : }
5818 :
5819 : /************************************************************************/
5820 : /* OSRSetProjCS() */
5821 : /************************************************************************/
5822 :
5823 : /**
5824 : * \brief Set the user visible PROJCS name.
5825 : *
5826 : * This function is the same as OGRSpatialReference::SetProjCS()
5827 : */
5828 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5829 :
5830 : {
5831 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5832 :
5833 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5834 : }
5835 :
5836 : /************************************************************************/
5837 : /* SetProjection() */
5838 : /************************************************************************/
5839 :
5840 : /**
5841 : * \brief Set a projection name.
5842 : *
5843 : * This method is the same as the C function OSRSetProjection().
5844 : *
5845 : * @param pszProjection the projection name, which should be selected from
5846 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5847 : *
5848 : * @return OGRERR_NONE on success.
5849 : */
5850 :
5851 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5852 :
5853 : {
5854 46 : TAKE_OPTIONAL_LOCK();
5855 :
5856 23 : OGR_SRSNode *poGeogCS = nullptr;
5857 :
5858 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5859 : {
5860 4 : poGeogCS = d->m_poRoot;
5861 4 : d->m_poRoot = nullptr;
5862 : }
5863 :
5864 23 : if (!GetAttrNode("PROJCS"))
5865 : {
5866 11 : SetNode("PROJCS", "unnamed");
5867 : }
5868 :
5869 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5870 23 : if (eErr != OGRERR_NONE)
5871 0 : return eErr;
5872 :
5873 23 : if (poGeogCS != nullptr)
5874 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5875 :
5876 23 : return OGRERR_NONE;
5877 : }
5878 :
5879 : /************************************************************************/
5880 : /* OSRSetProjection() */
5881 : /************************************************************************/
5882 :
5883 : /**
5884 : * \brief Set a projection name.
5885 : *
5886 : * This function is the same as OGRSpatialReference::SetProjection()
5887 : */
5888 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5889 :
5890 : {
5891 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5892 :
5893 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5894 : }
5895 :
5896 : /************************************************************************/
5897 : /* GetWKT2ProjectionMethod() */
5898 : /************************************************************************/
5899 :
5900 : /**
5901 : * \brief Returns info on the projection method, based on WKT2 naming
5902 : * conventions.
5903 : *
5904 : * The returned strings are short lived and should be considered to be
5905 : * invalidated by any further call to the GDAL API.
5906 : *
5907 : * @param[out] ppszMethodName Pointer to a string that will receive the
5908 : * projection method name.
5909 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5910 : * receive the name of the authority that defines the projection method.
5911 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5912 : * an authority.
5913 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5914 : * receive the code that defines the projection method.
5915 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5916 : * an authority.
5917 : *
5918 : * @return OGRERR_NONE on success.
5919 : */
5920 : OGRErr
5921 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5922 : const char **ppszMethodAuthName,
5923 : const char **ppszMethodCode) const
5924 : {
5925 2 : TAKE_OPTIONAL_LOCK();
5926 :
5927 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5928 1 : if (!conv)
5929 0 : return OGRERR_FAILURE;
5930 1 : const char *pszTmpMethodName = "";
5931 1 : const char *pszTmpMethodAuthName = "";
5932 1 : const char *pszTmpMethodCode = "";
5933 1 : int ret = proj_coordoperation_get_method_info(
5934 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5935 : &pszTmpMethodCode);
5936 : // "Internalize" temporary strings returned by PROJ
5937 1 : CPLAssert(pszTmpMethodName);
5938 1 : if (ppszMethodName)
5939 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5940 1 : if (ppszMethodAuthName)
5941 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5942 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5943 0 : : nullptr;
5944 1 : if (ppszMethodCode)
5945 0 : *ppszMethodCode =
5946 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5947 1 : proj_destroy(conv);
5948 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5949 : }
5950 :
5951 : /************************************************************************/
5952 : /* SetProjParm() */
5953 : /************************************************************************/
5954 :
5955 : /**
5956 : * \brief Set a projection parameter value.
5957 : *
5958 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5959 : *
5960 : * This method is the same as the C function OSRSetProjParm().
5961 : *
5962 : * Please check https://gdal.org/proj_list pages for
5963 : * legal parameter names for specific projections.
5964 : *
5965 : *
5966 : * @param pszParamName the parameter name, which should be selected from
5967 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5968 : *
5969 : * @param dfValue value to assign.
5970 : *
5971 : * @return OGRERR_NONE on success.
5972 : */
5973 :
5974 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5975 : double dfValue)
5976 :
5977 : {
5978 258 : TAKE_OPTIONAL_LOCK();
5979 :
5980 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5981 :
5982 129 : if (poPROJCS == nullptr)
5983 3 : return OGRERR_FAILURE;
5984 :
5985 126 : char szValue[64] = {'\0'};
5986 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5987 :
5988 : /* -------------------------------------------------------------------- */
5989 : /* Try to find existing parameter with this name. */
5990 : /* -------------------------------------------------------------------- */
5991 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5992 : {
5993 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5994 :
5995 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5996 1242 : poParam->GetChildCount() == 2 &&
5997 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5998 : {
5999 39 : poParam->GetChild(1)->SetValue(szValue);
6000 39 : return OGRERR_NONE;
6001 : }
6002 : }
6003 :
6004 : /* -------------------------------------------------------------------- */
6005 : /* Otherwise create a new parameter and append. */
6006 : /* -------------------------------------------------------------------- */
6007 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6008 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
6009 87 : poParam->AddChild(new OGR_SRSNode(szValue));
6010 :
6011 87 : poPROJCS->AddChild(poParam);
6012 :
6013 87 : return OGRERR_NONE;
6014 : }
6015 :
6016 : /************************************************************************/
6017 : /* OSRSetProjParm() */
6018 : /************************************************************************/
6019 :
6020 : /**
6021 : * \brief Set a projection parameter value.
6022 : *
6023 : * This function is the same as OGRSpatialReference::SetProjParm()
6024 : */
6025 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6026 : double dfValue)
6027 :
6028 : {
6029 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6030 :
6031 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6032 : }
6033 :
6034 : /************************************************************************/
6035 : /* FindProjParm() */
6036 : /************************************************************************/
6037 :
6038 : /**
6039 : * \brief Return the child index of the named projection parameter on
6040 : * its parent PROJCS node.
6041 : *
6042 : * @param pszParameter projection parameter to look for
6043 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6044 : * the PROJCS node of the SpatialReference object will be searched.
6045 : *
6046 : * @return the child index of the named projection parameter. -1 on failure
6047 : */
6048 5117 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6049 : const OGR_SRSNode *poPROJCS) const
6050 :
6051 : {
6052 10234 : TAKE_OPTIONAL_LOCK();
6053 :
6054 5117 : if (poPROJCS == nullptr)
6055 0 : poPROJCS = GetAttrNode("PROJCS");
6056 :
6057 5117 : if (poPROJCS == nullptr)
6058 0 : return -1;
6059 :
6060 : /* -------------------------------------------------------------------- */
6061 : /* Search for requested parameter. */
6062 : /* -------------------------------------------------------------------- */
6063 5117 : bool bIsWKT2 = false;
6064 33545 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6065 : {
6066 32894 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6067 :
6068 32894 : if (poParameter->GetChildCount() >= 2)
6069 : {
6070 22701 : const char *pszValue = poParameter->GetValue();
6071 37512 : if (EQUAL(pszValue, "PARAMETER") &&
6072 14811 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6073 : pszParameter))
6074 : {
6075 4466 : return iChild;
6076 : }
6077 18235 : else if (EQUAL(pszValue, "METHOD"))
6078 : {
6079 41 : bIsWKT2 = true;
6080 : }
6081 : }
6082 : }
6083 :
6084 : /* -------------------------------------------------------------------- */
6085 : /* Try similar names, for selected parameters. */
6086 : /* -------------------------------------------------------------------- */
6087 651 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6088 : {
6089 304 : if (bIsWKT2)
6090 : {
6091 8 : int iChild = FindProjParm(
6092 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6093 8 : if (iChild == -1)
6094 3 : iChild = FindProjParm(
6095 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6096 8 : return iChild;
6097 : }
6098 296 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6099 : }
6100 :
6101 347 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6102 : {
6103 38 : if (bIsWKT2)
6104 : {
6105 9 : int iChild = FindProjParm(
6106 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6107 9 : if (iChild == -1)
6108 0 : iChild = FindProjParm(
6109 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6110 9 : return iChild;
6111 : }
6112 29 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6113 29 : if (iChild == -1)
6114 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6115 29 : return iChild;
6116 : }
6117 :
6118 309 : return -1;
6119 : }
6120 :
6121 : /************************************************************************/
6122 : /* GetProjParm() */
6123 : /************************************************************************/
6124 :
6125 : /**
6126 : * \brief Fetch a projection parameter value.
6127 : *
6128 : * NOTE: This code should be modified to translate non degree angles into
6129 : * degrees based on the GEOGCS unit. This has not yet been done.
6130 : *
6131 : * This method is the same as the C function OSRGetProjParm().
6132 : *
6133 : * @param pszName the name of the parameter to fetch, from the set of
6134 : * SRS_PP codes in ogr_srs_api.h.
6135 : *
6136 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6137 : *
6138 : * @param pnErr place to put error code on failure. Ignored if NULL.
6139 : *
6140 : * @return value of parameter.
6141 : */
6142 :
6143 5030 : double OGRSpatialReference::GetProjParm(const char *pszName,
6144 : double dfDefaultValue,
6145 : OGRErr *pnErr) const
6146 :
6147 : {
6148 10060 : TAKE_OPTIONAL_LOCK();
6149 :
6150 5030 : d->refreshProjObj();
6151 5030 : GetRoot(); // force update of d->m_bNodesWKT2
6152 :
6153 5030 : if (pnErr != nullptr)
6154 4031 : *pnErr = OGRERR_NONE;
6155 :
6156 : /* -------------------------------------------------------------------- */
6157 : /* Find the desired parameter. */
6158 : /* -------------------------------------------------------------------- */
6159 : const OGR_SRSNode *poPROJCS =
6160 5030 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6161 5030 : if (poPROJCS == nullptr)
6162 : {
6163 258 : if (pnErr != nullptr)
6164 258 : *pnErr = OGRERR_FAILURE;
6165 258 : return dfDefaultValue;
6166 : }
6167 :
6168 4772 : const int iChild = FindProjParm(pszName, poPROJCS);
6169 4772 : if (iChild == -1)
6170 : {
6171 306 : if (IsProjected() && GetAxesCount() == 3)
6172 : {
6173 3 : OGRSpatialReference *poSRSTmp = Clone();
6174 3 : poSRSTmp->DemoteTo2D(nullptr);
6175 : const double dfRet =
6176 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6177 3 : delete poSRSTmp;
6178 3 : return dfRet;
6179 : }
6180 :
6181 303 : if (pnErr != nullptr)
6182 281 : *pnErr = OGRERR_FAILURE;
6183 303 : return dfDefaultValue;
6184 : }
6185 :
6186 4466 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6187 4466 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6188 : }
6189 :
6190 : /************************************************************************/
6191 : /* OSRGetProjParm() */
6192 : /************************************************************************/
6193 :
6194 : /**
6195 : * \brief Fetch a projection parameter value.
6196 : *
6197 : * This function is the same as OGRSpatialReference::GetProjParm()
6198 : */
6199 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6200 : double dfDefaultValue, OGRErr *pnErr)
6201 :
6202 : {
6203 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6204 :
6205 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6206 : }
6207 :
6208 : /************************************************************************/
6209 : /* GetNormProjParm() */
6210 : /************************************************************************/
6211 :
6212 : /**
6213 : * \brief Fetch a normalized projection parameter value.
6214 : *
6215 : * This method is the same as GetProjParm() except that the value of
6216 : * the parameter is "normalized" into degrees or meters depending on
6217 : * whether it is linear or angular.
6218 : *
6219 : * This method is the same as the C function OSRGetNormProjParm().
6220 : *
6221 : * @param pszName the name of the parameter to fetch, from the set of
6222 : * SRS_PP codes in ogr_srs_api.h.
6223 : *
6224 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6225 : *
6226 : * @param pnErr place to put error code on failure. Ignored if NULL.
6227 : *
6228 : * @return value of parameter.
6229 : */
6230 :
6231 4006 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6232 : double dfDefaultValue,
6233 : OGRErr *pnErr) const
6234 :
6235 : {
6236 8012 : TAKE_OPTIONAL_LOCK();
6237 :
6238 4006 : GetNormInfo();
6239 :
6240 4006 : OGRErr nError = OGRERR_NONE;
6241 4006 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6242 4006 : if (pnErr != nullptr)
6243 0 : *pnErr = nError;
6244 :
6245 : // If we got the default just return it unadjusted.
6246 4006 : if (nError != OGRERR_NONE)
6247 539 : return dfRawResult;
6248 :
6249 3467 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6250 8 : dfRawResult *= d->dfToDegrees;
6251 :
6252 3467 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6253 5 : return dfRawResult * d->dfToMeter;
6254 :
6255 3462 : return dfRawResult;
6256 : }
6257 :
6258 : /************************************************************************/
6259 : /* OSRGetNormProjParm() */
6260 : /************************************************************************/
6261 :
6262 : /**
6263 : * \brief This function is the same as OGRSpatialReference::
6264 : *
6265 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6266 : */
6267 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6268 : double dfDefaultValue, OGRErr *pnErr)
6269 :
6270 : {
6271 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6272 :
6273 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6274 : }
6275 :
6276 : /************************************************************************/
6277 : /* SetNormProjParm() */
6278 : /************************************************************************/
6279 :
6280 : /**
6281 : * \brief Set a projection parameter with a normalized value.
6282 : *
6283 : * This method is the same as SetProjParm() except that the value of
6284 : * the parameter passed in is assumed to be in "normalized" form (decimal
6285 : * degrees for angular values, meters for linear values. The values are
6286 : * converted in a form suitable for the GEOGCS and linear units in effect.
6287 : *
6288 : * This method is the same as the C function OSRSetNormProjParm().
6289 : *
6290 : * @param pszName the parameter name, which should be selected from
6291 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6292 : *
6293 : * @param dfValue value to assign.
6294 : *
6295 : * @return OGRERR_NONE on success.
6296 : */
6297 :
6298 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6299 :
6300 : {
6301 182 : TAKE_OPTIONAL_LOCK();
6302 :
6303 91 : GetNormInfo();
6304 :
6305 91 : if (d->dfToDegrees != 0.0 &&
6306 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6307 0 : IsAngularParameter(pszName))
6308 : {
6309 0 : dfValue /= d->dfToDegrees;
6310 : }
6311 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6312 4 : IsLinearParameter(pszName))
6313 4 : dfValue /= d->dfToMeter;
6314 :
6315 182 : return SetProjParm(pszName, dfValue);
6316 : }
6317 :
6318 : /************************************************************************/
6319 : /* OSRSetNormProjParm() */
6320 : /************************************************************************/
6321 :
6322 : /**
6323 : * \brief Set a projection parameter with a normalized value.
6324 : *
6325 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6326 : */
6327 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6328 : double dfValue)
6329 :
6330 : {
6331 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6332 :
6333 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6334 : }
6335 :
6336 : /************************************************************************/
6337 : /* SetTM() */
6338 : /************************************************************************/
6339 :
6340 442 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6341 : double dfScale, double dfFalseEasting,
6342 : double dfFalseNorthing)
6343 :
6344 : {
6345 884 : TAKE_OPTIONAL_LOCK();
6346 :
6347 442 : return d->replaceConversionAndUnref(
6348 : proj_create_conversion_transverse_mercator(
6349 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6350 884 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6351 : }
6352 :
6353 : /************************************************************************/
6354 : /* OSRSetTM() */
6355 : /************************************************************************/
6356 :
6357 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6358 : double dfCenterLong, double dfScale, double dfFalseEasting,
6359 : double dfFalseNorthing)
6360 :
6361 : {
6362 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6363 :
6364 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6365 1 : dfFalseEasting, dfFalseNorthing);
6366 : }
6367 :
6368 : /************************************************************************/
6369 : /* SetTMVariant() */
6370 : /************************************************************************/
6371 :
6372 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6373 : double dfCenterLat,
6374 : double dfCenterLong, double dfScale,
6375 : double dfFalseEasting,
6376 : double dfFalseNorthing)
6377 :
6378 : {
6379 0 : TAKE_OPTIONAL_LOCK();
6380 :
6381 0 : SetProjection(pszVariantName);
6382 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6383 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6384 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6385 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6386 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6387 :
6388 0 : return OGRERR_NONE;
6389 : }
6390 :
6391 : /************************************************************************/
6392 : /* OSRSetTMVariant() */
6393 : /************************************************************************/
6394 :
6395 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6396 : double dfCenterLat, double dfCenterLong, double dfScale,
6397 : double dfFalseEasting, double dfFalseNorthing)
6398 :
6399 : {
6400 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6401 :
6402 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6403 : dfCenterLong, dfScale, dfFalseEasting,
6404 0 : dfFalseNorthing);
6405 : }
6406 :
6407 : /************************************************************************/
6408 : /* SetTMSO() */
6409 : /************************************************************************/
6410 :
6411 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6412 : double dfScale, double dfFalseEasting,
6413 : double dfFalseNorthing)
6414 :
6415 : {
6416 6 : TAKE_OPTIONAL_LOCK();
6417 :
6418 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6419 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6420 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6421 :
6422 3 : const char *pszName = nullptr;
6423 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6424 3 : CPLString osName = pszName ? pszName : "";
6425 :
6426 3 : d->refreshProjObj();
6427 :
6428 3 : d->demoteFromBoundCRS();
6429 :
6430 3 : auto cs = proj_create_cartesian_2D_cs(
6431 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6432 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6433 : auto projCRS =
6434 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6435 3 : d->getGeodBaseCRS(), conv, cs);
6436 3 : proj_destroy(conv);
6437 3 : proj_destroy(cs);
6438 :
6439 3 : d->setPjCRS(projCRS);
6440 :
6441 3 : d->undoDemoteFromBoundCRS();
6442 :
6443 6 : return OGRERR_NONE;
6444 : }
6445 :
6446 : /************************************************************************/
6447 : /* OSRSetTMSO() */
6448 : /************************************************************************/
6449 :
6450 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6451 : double dfCenterLong, double dfScale, double dfFalseEasting,
6452 : double dfFalseNorthing)
6453 :
6454 : {
6455 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6456 :
6457 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6458 0 : dfFalseEasting, dfFalseNorthing);
6459 : }
6460 :
6461 : /************************************************************************/
6462 : /* SetTPED() */
6463 : /************************************************************************/
6464 :
6465 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6466 : double dfLat2, double dfLong2,
6467 : double dfFalseEasting,
6468 : double dfFalseNorthing)
6469 :
6470 : {
6471 2 : TAKE_OPTIONAL_LOCK();
6472 :
6473 1 : return d->replaceConversionAndUnref(
6474 : proj_create_conversion_two_point_equidistant(
6475 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6476 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6477 : }
6478 :
6479 : /************************************************************************/
6480 : /* OSRSetTPED() */
6481 : /************************************************************************/
6482 :
6483 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6484 : double dfLat2, double dfLong2, double dfFalseEasting,
6485 : double dfFalseNorthing)
6486 :
6487 : {
6488 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6489 :
6490 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6491 0 : dfFalseEasting, dfFalseNorthing);
6492 : }
6493 :
6494 : /************************************************************************/
6495 : /* SetTMG() */
6496 : /************************************************************************/
6497 :
6498 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6499 : double dfFalseEasting,
6500 : double dfFalseNorthing)
6501 :
6502 : {
6503 0 : TAKE_OPTIONAL_LOCK();
6504 :
6505 0 : return d->replaceConversionAndUnref(
6506 : proj_create_conversion_tunisia_mapping_grid(
6507 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6508 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6509 : }
6510 :
6511 : /************************************************************************/
6512 : /* OSRSetTMG() */
6513 : /************************************************************************/
6514 :
6515 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6516 : double dfCenterLong, double dfFalseEasting,
6517 : double dfFalseNorthing)
6518 :
6519 : {
6520 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6521 :
6522 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6523 0 : dfFalseNorthing);
6524 : }
6525 :
6526 : /************************************************************************/
6527 : /* SetACEA() */
6528 : /************************************************************************/
6529 :
6530 39 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6531 : double dfCenterLat, double dfCenterLong,
6532 : double dfFalseEasting,
6533 : double dfFalseNorthing)
6534 :
6535 : {
6536 78 : TAKE_OPTIONAL_LOCK();
6537 :
6538 : // Note different order of parameters. The one in PROJ is conformant with
6539 : // EPSG
6540 39 : return d->replaceConversionAndUnref(
6541 : proj_create_conversion_albers_equal_area(
6542 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6543 78 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6544 : }
6545 :
6546 : /************************************************************************/
6547 : /* OSRSetACEA() */
6548 : /************************************************************************/
6549 :
6550 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6551 : double dfCenterLat, double dfCenterLong,
6552 : double dfFalseEasting, double dfFalseNorthing)
6553 :
6554 : {
6555 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6556 :
6557 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6558 0 : dfFalseEasting, dfFalseNorthing);
6559 : }
6560 :
6561 : /************************************************************************/
6562 : /* SetAE() */
6563 : /************************************************************************/
6564 :
6565 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6566 : double dfFalseEasting, double dfFalseNorthing)
6567 :
6568 : {
6569 42 : TAKE_OPTIONAL_LOCK();
6570 :
6571 21 : return d->replaceConversionAndUnref(
6572 : proj_create_conversion_azimuthal_equidistant(
6573 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6574 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6575 : }
6576 :
6577 : /************************************************************************/
6578 : /* OSRSetAE() */
6579 : /************************************************************************/
6580 :
6581 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6582 : double dfCenterLong, double dfFalseEasting,
6583 : double dfFalseNorthing)
6584 :
6585 : {
6586 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6587 :
6588 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6589 0 : dfFalseNorthing);
6590 : }
6591 :
6592 : /************************************************************************/
6593 : /* SetBonne() */
6594 : /************************************************************************/
6595 :
6596 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6597 : double dfFalseEasting,
6598 : double dfFalseNorthing)
6599 :
6600 : {
6601 2 : TAKE_OPTIONAL_LOCK();
6602 :
6603 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6604 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6605 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6606 : }
6607 :
6608 : /************************************************************************/
6609 : /* OSRSetBonne() */
6610 : /************************************************************************/
6611 :
6612 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6613 : double dfCentralMeridian, double dfFalseEasting,
6614 : double dfFalseNorthing)
6615 :
6616 : {
6617 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6618 :
6619 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6620 0 : dfFalseNorthing);
6621 : }
6622 :
6623 : /************************************************************************/
6624 : /* SetCEA() */
6625 : /************************************************************************/
6626 :
6627 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6628 : double dfFalseEasting,
6629 : double dfFalseNorthing)
6630 :
6631 : {
6632 8 : TAKE_OPTIONAL_LOCK();
6633 :
6634 4 : return d->replaceConversionAndUnref(
6635 : proj_create_conversion_lambert_cylindrical_equal_area(
6636 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6637 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6638 : }
6639 :
6640 : /************************************************************************/
6641 : /* OSRSetCEA() */
6642 : /************************************************************************/
6643 :
6644 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6645 : double dfCentralMeridian, double dfFalseEasting,
6646 : double dfFalseNorthing)
6647 :
6648 : {
6649 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6650 :
6651 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6652 0 : dfFalseNorthing);
6653 : }
6654 :
6655 : /************************************************************************/
6656 : /* SetCS() */
6657 : /************************************************************************/
6658 :
6659 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6660 : double dfFalseEasting, double dfFalseNorthing)
6661 :
6662 : {
6663 10 : TAKE_OPTIONAL_LOCK();
6664 :
6665 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6666 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6667 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6668 : }
6669 :
6670 : /************************************************************************/
6671 : /* OSRSetCS() */
6672 : /************************************************************************/
6673 :
6674 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6675 : double dfCenterLong, double dfFalseEasting,
6676 : double dfFalseNorthing)
6677 :
6678 : {
6679 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6680 :
6681 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6682 0 : dfFalseNorthing);
6683 : }
6684 :
6685 : /************************************************************************/
6686 : /* SetEC() */
6687 : /************************************************************************/
6688 :
6689 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6690 : double dfCenterLat, double dfCenterLong,
6691 : double dfFalseEasting, double dfFalseNorthing)
6692 :
6693 : {
6694 14 : TAKE_OPTIONAL_LOCK();
6695 :
6696 : // Note: different order of arguments
6697 7 : return d->replaceConversionAndUnref(
6698 : proj_create_conversion_equidistant_conic(
6699 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6700 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6701 : }
6702 :
6703 : /************************************************************************/
6704 : /* OSRSetEC() */
6705 : /************************************************************************/
6706 :
6707 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6708 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6709 : double dfFalseNorthing)
6710 :
6711 : {
6712 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6713 :
6714 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6715 0 : dfFalseEasting, dfFalseNorthing);
6716 : }
6717 :
6718 : /************************************************************************/
6719 : /* SetEckert() */
6720 : /************************************************************************/
6721 :
6722 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6723 : double dfCentralMeridian,
6724 : double dfFalseEasting,
6725 : double dfFalseNorthing)
6726 :
6727 : {
6728 20 : TAKE_OPTIONAL_LOCK();
6729 :
6730 : PJ *conv;
6731 10 : if (nVariation == 1)
6732 : {
6733 1 : conv = proj_create_conversion_eckert_i(
6734 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6735 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6736 : }
6737 9 : else if (nVariation == 2)
6738 : {
6739 1 : conv = proj_create_conversion_eckert_ii(
6740 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6741 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6742 : }
6743 8 : else if (nVariation == 3)
6744 : {
6745 1 : conv = proj_create_conversion_eckert_iii(
6746 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6747 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6748 : }
6749 7 : else if (nVariation == 4)
6750 : {
6751 3 : conv = proj_create_conversion_eckert_iv(
6752 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6753 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6754 : }
6755 4 : else if (nVariation == 5)
6756 : {
6757 1 : conv = proj_create_conversion_eckert_v(
6758 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6759 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6760 : }
6761 3 : else if (nVariation == 6)
6762 : {
6763 3 : conv = proj_create_conversion_eckert_vi(
6764 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6765 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6766 : }
6767 : else
6768 : {
6769 0 : CPLError(CE_Failure, CPLE_AppDefined,
6770 : "Unsupported Eckert variation (%d).", nVariation);
6771 0 : return OGRERR_UNSUPPORTED_SRS;
6772 : }
6773 :
6774 10 : return d->replaceConversionAndUnref(conv);
6775 : }
6776 :
6777 : /************************************************************************/
6778 : /* OSRSetEckert() */
6779 : /************************************************************************/
6780 :
6781 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6782 : double dfCentralMeridian, double dfFalseEasting,
6783 : double dfFalseNorthing)
6784 :
6785 : {
6786 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6787 :
6788 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6789 0 : dfFalseEasting, dfFalseNorthing);
6790 : }
6791 :
6792 : /************************************************************************/
6793 : /* SetEckertIV() */
6794 : /* */
6795 : /* Deprecated */
6796 : /************************************************************************/
6797 :
6798 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6799 : double dfFalseEasting,
6800 : double dfFalseNorthing)
6801 :
6802 : {
6803 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6804 : }
6805 :
6806 : /************************************************************************/
6807 : /* OSRSetEckertIV() */
6808 : /************************************************************************/
6809 :
6810 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6811 : double dfFalseEasting, double dfFalseNorthing)
6812 :
6813 : {
6814 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6815 :
6816 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6817 0 : dfFalseNorthing);
6818 : }
6819 :
6820 : /************************************************************************/
6821 : /* SetEckertVI() */
6822 : /* */
6823 : /* Deprecated */
6824 : /************************************************************************/
6825 :
6826 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6827 : double dfFalseEasting,
6828 : double dfFalseNorthing)
6829 :
6830 : {
6831 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6832 : }
6833 :
6834 : /************************************************************************/
6835 : /* OSRSetEckertVI() */
6836 : /************************************************************************/
6837 :
6838 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6839 : double dfFalseEasting, double dfFalseNorthing)
6840 :
6841 : {
6842 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6843 :
6844 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6845 0 : dfFalseNorthing);
6846 : }
6847 :
6848 : /************************************************************************/
6849 : /* SetEquirectangular() */
6850 : /************************************************************************/
6851 :
6852 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6853 : double dfCenterLong,
6854 : double dfFalseEasting,
6855 : double dfFalseNorthing)
6856 :
6857 : {
6858 4 : TAKE_OPTIONAL_LOCK();
6859 :
6860 2 : if (dfCenterLat == 0.0)
6861 : {
6862 0 : return d->replaceConversionAndUnref(
6863 : proj_create_conversion_equidistant_cylindrical(
6864 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6865 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6866 : }
6867 :
6868 : // Non-standard extension with non-zero latitude of origin
6869 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6870 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6871 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6872 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6873 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6874 :
6875 2 : return OGRERR_NONE;
6876 : }
6877 :
6878 : /************************************************************************/
6879 : /* OSRSetEquirectangular() */
6880 : /************************************************************************/
6881 :
6882 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6883 : double dfCenterLong, double dfFalseEasting,
6884 : double dfFalseNorthing)
6885 :
6886 : {
6887 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6888 :
6889 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6890 0 : dfFalseEasting, dfFalseNorthing);
6891 : }
6892 :
6893 : /************************************************************************/
6894 : /* SetEquirectangular2() */
6895 : /* Generalized form */
6896 : /************************************************************************/
6897 :
6898 179 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6899 : double dfCenterLong,
6900 : double dfStdParallel1,
6901 : double dfFalseEasting,
6902 : double dfFalseNorthing)
6903 :
6904 : {
6905 358 : TAKE_OPTIONAL_LOCK();
6906 :
6907 179 : if (dfCenterLat == 0.0)
6908 : {
6909 174 : return d->replaceConversionAndUnref(
6910 : proj_create_conversion_equidistant_cylindrical(
6911 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6912 174 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6913 : }
6914 :
6915 : // Non-standard extension with non-zero latitude of origin
6916 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6917 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6918 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6919 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6920 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6921 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6922 :
6923 5 : return OGRERR_NONE;
6924 : }
6925 :
6926 : /************************************************************************/
6927 : /* OSRSetEquirectangular2() */
6928 : /************************************************************************/
6929 :
6930 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6931 : double dfCenterLong, double dfStdParallel1,
6932 : double dfFalseEasting, double dfFalseNorthing)
6933 :
6934 : {
6935 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6936 :
6937 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6938 : dfStdParallel1, dfFalseEasting,
6939 3 : dfFalseNorthing);
6940 : }
6941 :
6942 : /************************************************************************/
6943 : /* SetGS() */
6944 : /************************************************************************/
6945 :
6946 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6947 : double dfFalseEasting, double dfFalseNorthing)
6948 :
6949 : {
6950 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6951 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6952 5 : nullptr, 0.0, nullptr, 0.0));
6953 : }
6954 :
6955 : /************************************************************************/
6956 : /* OSRSetGS() */
6957 : /************************************************************************/
6958 :
6959 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6960 : double dfFalseEasting, double dfFalseNorthing)
6961 :
6962 : {
6963 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6964 :
6965 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6966 2 : dfFalseNorthing);
6967 : }
6968 :
6969 : /************************************************************************/
6970 : /* SetGH() */
6971 : /************************************************************************/
6972 :
6973 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6974 : double dfFalseEasting, double dfFalseNorthing)
6975 :
6976 : {
6977 0 : TAKE_OPTIONAL_LOCK();
6978 :
6979 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6980 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6981 0 : nullptr, 0.0, nullptr, 0.0));
6982 : }
6983 :
6984 : /************************************************************************/
6985 : /* OSRSetGH() */
6986 : /************************************************************************/
6987 :
6988 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6989 : double dfFalseEasting, double dfFalseNorthing)
6990 :
6991 : {
6992 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6993 :
6994 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6995 0 : dfFalseNorthing);
6996 : }
6997 :
6998 : /************************************************************************/
6999 : /* SetIGH() */
7000 : /************************************************************************/
7001 :
7002 0 : OGRErr OGRSpatialReference::SetIGH()
7003 :
7004 : {
7005 0 : TAKE_OPTIONAL_LOCK();
7006 :
7007 0 : return d->replaceConversionAndUnref(
7008 : proj_create_conversion_interrupted_goode_homolosine(
7009 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7010 : }
7011 :
7012 : /************************************************************************/
7013 : /* OSRSetIGH() */
7014 : /************************************************************************/
7015 :
7016 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7017 :
7018 : {
7019 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7020 :
7021 0 : return ToPointer(hSRS)->SetIGH();
7022 : }
7023 :
7024 : /************************************************************************/
7025 : /* SetGEOS() */
7026 : /************************************************************************/
7027 :
7028 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7029 : double dfSatelliteHeight,
7030 : double dfFalseEasting,
7031 : double dfFalseNorthing)
7032 :
7033 : {
7034 6 : TAKE_OPTIONAL_LOCK();
7035 :
7036 3 : return d->replaceConversionAndUnref(
7037 : proj_create_conversion_geostationary_satellite_sweep_y(
7038 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7039 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7040 : }
7041 :
7042 : /************************************************************************/
7043 : /* OSRSetGEOS() */
7044 : /************************************************************************/
7045 :
7046 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7047 : double dfSatelliteHeight, double dfFalseEasting,
7048 : double dfFalseNorthing)
7049 :
7050 : {
7051 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7052 :
7053 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7054 0 : dfFalseEasting, dfFalseNorthing);
7055 : }
7056 :
7057 : /************************************************************************/
7058 : /* SetGaussSchreiberTMercator() */
7059 : /************************************************************************/
7060 :
7061 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7062 : double dfCenterLong,
7063 : double dfScale,
7064 : double dfFalseEasting,
7065 : double dfFalseNorthing)
7066 :
7067 : {
7068 0 : TAKE_OPTIONAL_LOCK();
7069 :
7070 0 : return d->replaceConversionAndUnref(
7071 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7072 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7073 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7074 : }
7075 :
7076 : /************************************************************************/
7077 : /* OSRSetGaussSchreiberTMercator() */
7078 : /************************************************************************/
7079 :
7080 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7081 : double dfCenterLat, double dfCenterLong,
7082 : double dfScale, double dfFalseEasting,
7083 : double dfFalseNorthing)
7084 :
7085 : {
7086 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7087 :
7088 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7089 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7090 : }
7091 :
7092 : /************************************************************************/
7093 : /* SetGnomonic() */
7094 : /************************************************************************/
7095 :
7096 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7097 : double dfFalseEasting,
7098 : double dfFalseNorthing)
7099 :
7100 : {
7101 4 : TAKE_OPTIONAL_LOCK();
7102 :
7103 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7104 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7105 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7106 : }
7107 :
7108 : /************************************************************************/
7109 : /* OSRSetGnomonic() */
7110 : /************************************************************************/
7111 :
7112 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7113 : double dfCenterLong, double dfFalseEasting,
7114 : double dfFalseNorthing)
7115 :
7116 : {
7117 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7118 :
7119 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7120 0 : dfFalseEasting, dfFalseNorthing);
7121 : }
7122 :
7123 : /************************************************************************/
7124 : /* SetHOMAC() */
7125 : /************************************************************************/
7126 :
7127 : /**
7128 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7129 : * azimuth angle.
7130 : *
7131 : * This projection corresponds to EPSG projection method 9815, also
7132 : * sometimes known as hotine oblique mercator (variant B).
7133 : *
7134 : * This method does the same thing as the C function OSRSetHOMAC().
7135 : *
7136 : * @param dfCenterLat Latitude of the projection origin.
7137 : * @param dfCenterLong Longitude of the projection origin.
7138 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7139 : * centerline.
7140 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7141 : * @param dfScale Scale factor applies to the projection origin.
7142 : * @param dfFalseEasting False easting.
7143 : * @param dfFalseNorthing False northing.
7144 : *
7145 : * @return OGRERR_NONE on success.
7146 : */
7147 :
7148 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7149 : double dfAzimuth, double dfRectToSkew,
7150 : double dfScale, double dfFalseEasting,
7151 : double dfFalseNorthing)
7152 :
7153 : {
7154 8 : TAKE_OPTIONAL_LOCK();
7155 :
7156 4 : return d->replaceConversionAndUnref(
7157 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7158 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7159 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7160 8 : 0.0, nullptr, 0.0));
7161 : }
7162 :
7163 : /************************************************************************/
7164 : /* OSRSetHOMAC() */
7165 : /************************************************************************/
7166 :
7167 : /**
7168 : * \brief Set an Oblique Mercator projection using azimuth angle.
7169 : *
7170 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7171 : */
7172 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7173 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7174 : double dfScale, double dfFalseEasting,
7175 : double dfFalseNorthing)
7176 :
7177 : {
7178 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7179 :
7180 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7181 : dfRectToSkew, dfScale, dfFalseEasting,
7182 0 : dfFalseNorthing);
7183 : }
7184 :
7185 : /************************************************************************/
7186 : /* SetHOM() */
7187 : /************************************************************************/
7188 :
7189 : /**
7190 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7191 : *
7192 : * This projection corresponds to EPSG projection method 9812, also
7193 : * sometimes known as hotine oblique mercator (variant A)..
7194 : *
7195 : * This method does the same thing as the C function OSRSetHOM().
7196 : *
7197 : * @param dfCenterLat Latitude of the projection origin.
7198 : * @param dfCenterLong Longitude of the projection origin.
7199 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7200 : * centerline.
7201 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7202 : * @param dfScale Scale factor applies to the projection origin.
7203 : * @param dfFalseEasting False easting.
7204 : * @param dfFalseNorthing False northing.
7205 : *
7206 : * @return OGRERR_NONE on success.
7207 : */
7208 :
7209 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7210 : double dfAzimuth, double dfRectToSkew,
7211 : double dfScale, double dfFalseEasting,
7212 : double dfFalseNorthing)
7213 :
7214 : {
7215 26 : TAKE_OPTIONAL_LOCK();
7216 :
7217 13 : return d->replaceConversionAndUnref(
7218 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7219 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7220 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7221 26 : 0.0, nullptr, 0.0));
7222 : }
7223 :
7224 : /************************************************************************/
7225 : /* OSRSetHOM() */
7226 : /************************************************************************/
7227 : /**
7228 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7229 : *
7230 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7231 : */
7232 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7233 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7234 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7235 :
7236 : {
7237 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7238 :
7239 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7240 : dfRectToSkew, dfScale, dfFalseEasting,
7241 0 : dfFalseNorthing);
7242 : }
7243 :
7244 : /************************************************************************/
7245 : /* SetHOM2PNO() */
7246 : /************************************************************************/
7247 :
7248 : /**
7249 : * \brief Set a Hotine Oblique Mercator projection using two points on
7250 : * projection centerline.
7251 : *
7252 : * This method does the same thing as the C function OSRSetHOM2PNO().
7253 : *
7254 : * @param dfCenterLat Latitude of the projection origin.
7255 : * @param dfLat1 Latitude of the first point on center line.
7256 : * @param dfLong1 Longitude of the first point on center line.
7257 : * @param dfLat2 Latitude of the second point on center line.
7258 : * @param dfLong2 Longitude of the second point on center line.
7259 : * @param dfScale Scale factor applies to the projection origin.
7260 : * @param dfFalseEasting False easting.
7261 : * @param dfFalseNorthing False northing.
7262 : *
7263 : * @return OGRERR_NONE on success.
7264 : */
7265 :
7266 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7267 : double dfLong1, double dfLat2,
7268 : double dfLong2, double dfScale,
7269 : double dfFalseEasting,
7270 : double dfFalseNorthing)
7271 :
7272 : {
7273 6 : TAKE_OPTIONAL_LOCK();
7274 :
7275 3 : return d->replaceConversionAndUnref(
7276 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7277 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7278 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7279 6 : 0.0));
7280 : }
7281 :
7282 : /************************************************************************/
7283 : /* OSRSetHOM2PNO() */
7284 : /************************************************************************/
7285 : /**
7286 : * \brief Set a Hotine Oblique Mercator projection using two points on
7287 : * projection centerline.
7288 : *
7289 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7290 : */
7291 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7292 : double dfLat1, double dfLong1, double dfLat2,
7293 : double dfLong2, double dfScale, double dfFalseEasting,
7294 : double dfFalseNorthing)
7295 :
7296 : {
7297 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7298 :
7299 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7300 : dfLong2, dfScale, dfFalseEasting,
7301 0 : dfFalseNorthing);
7302 : }
7303 :
7304 : /************************************************************************/
7305 : /* SetLOM() */
7306 : /************************************************************************/
7307 :
7308 : /**
7309 : * \brief Set a Laborde Oblique Mercator projection.
7310 : *
7311 : * @param dfCenterLat Latitude of the projection origin.
7312 : * @param dfCenterLong Longitude of the projection origin.
7313 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7314 : * centerline.
7315 : * @param dfScale Scale factor on the initial line
7316 : * @param dfFalseEasting False easting.
7317 : * @param dfFalseNorthing False northing.
7318 : *
7319 : * @return OGRERR_NONE on success.
7320 : */
7321 :
7322 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7323 : double dfAzimuth, double dfScale,
7324 : double dfFalseEasting,
7325 : double dfFalseNorthing)
7326 :
7327 : {
7328 0 : TAKE_OPTIONAL_LOCK();
7329 :
7330 0 : return d->replaceConversionAndUnref(
7331 : proj_create_conversion_laborde_oblique_mercator(
7332 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7333 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7334 : }
7335 :
7336 : /************************************************************************/
7337 : /* SetIWMPolyconic() */
7338 : /************************************************************************/
7339 :
7340 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7341 : double dfCenterLong,
7342 : double dfFalseEasting,
7343 : double dfFalseNorthing)
7344 :
7345 : {
7346 0 : TAKE_OPTIONAL_LOCK();
7347 :
7348 0 : return d->replaceConversionAndUnref(
7349 : proj_create_conversion_international_map_world_polyconic(
7350 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7351 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7352 : }
7353 :
7354 : /************************************************************************/
7355 : /* OSRSetIWMPolyconic() */
7356 : /************************************************************************/
7357 :
7358 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7359 : double dfLat2, double dfCenterLong,
7360 : double dfFalseEasting, double dfFalseNorthing)
7361 :
7362 : {
7363 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7364 :
7365 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7366 0 : dfFalseEasting, dfFalseNorthing);
7367 : }
7368 :
7369 : /************************************************************************/
7370 : /* SetKrovak() */
7371 : /************************************************************************/
7372 :
7373 : /** Krovak east-north projection.
7374 : *
7375 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7376 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7377 : */
7378 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7379 : double dfAzimuth,
7380 : double dfPseudoStdParallel1,
7381 : double dfScale, double dfFalseEasting,
7382 : double dfFalseNorthing)
7383 :
7384 : {
7385 6 : TAKE_OPTIONAL_LOCK();
7386 :
7387 3 : return d->replaceConversionAndUnref(
7388 : proj_create_conversion_krovak_north_oriented(
7389 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7390 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7391 6 : nullptr, 0.0, nullptr, 0.0));
7392 : }
7393 :
7394 : /************************************************************************/
7395 : /* OSRSetKrovak() */
7396 : /************************************************************************/
7397 :
7398 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7399 : double dfCenterLong, double dfAzimuth,
7400 : double dfPseudoStdParallel1, double dfScale,
7401 : double dfFalseEasting, double dfFalseNorthing)
7402 :
7403 : {
7404 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7405 :
7406 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7407 : dfPseudoStdParallel1, dfScale,
7408 0 : dfFalseEasting, dfFalseNorthing);
7409 : }
7410 :
7411 : /************************************************************************/
7412 : /* SetLAEA() */
7413 : /************************************************************************/
7414 :
7415 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7416 : double dfFalseEasting,
7417 : double dfFalseNorthing)
7418 :
7419 : {
7420 34 : TAKE_OPTIONAL_LOCK();
7421 :
7422 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7423 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7424 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7425 :
7426 17 : const char *pszName = nullptr;
7427 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7428 17 : CPLString osName = pszName ? pszName : "";
7429 :
7430 17 : d->refreshProjObj();
7431 :
7432 17 : d->demoteFromBoundCRS();
7433 :
7434 17 : auto cs = proj_create_cartesian_2D_cs(
7435 : d->getPROJContext(),
7436 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7437 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7438 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7439 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7440 : : PJ_CART2D_EASTING_NORTHING,
7441 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7442 : auto projCRS =
7443 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7444 17 : d->getGeodBaseCRS(), conv, cs);
7445 17 : proj_destroy(conv);
7446 17 : proj_destroy(cs);
7447 :
7448 17 : d->setPjCRS(projCRS);
7449 :
7450 17 : d->undoDemoteFromBoundCRS();
7451 :
7452 34 : return OGRERR_NONE;
7453 : }
7454 :
7455 : /************************************************************************/
7456 : /* OSRSetLAEA() */
7457 : /************************************************************************/
7458 :
7459 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7460 : double dfCenterLong, double dfFalseEasting,
7461 : double dfFalseNorthing)
7462 :
7463 : {
7464 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7465 :
7466 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7467 0 : dfFalseNorthing);
7468 : }
7469 :
7470 : /************************************************************************/
7471 : /* SetLCC() */
7472 : /************************************************************************/
7473 :
7474 148 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7475 : double dfCenterLat, double dfCenterLong,
7476 : double dfFalseEasting,
7477 : double dfFalseNorthing)
7478 :
7479 : {
7480 296 : TAKE_OPTIONAL_LOCK();
7481 :
7482 148 : return d->replaceConversionAndUnref(
7483 : proj_create_conversion_lambert_conic_conformal_2sp(
7484 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7485 296 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7486 : }
7487 :
7488 : /************************************************************************/
7489 : /* OSRSetLCC() */
7490 : /************************************************************************/
7491 :
7492 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7493 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7494 : double dfFalseNorthing)
7495 :
7496 : {
7497 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7498 :
7499 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7500 1 : dfFalseEasting, dfFalseNorthing);
7501 : }
7502 :
7503 : /************************************************************************/
7504 : /* SetLCC1SP() */
7505 : /************************************************************************/
7506 :
7507 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7508 : double dfScale, double dfFalseEasting,
7509 : double dfFalseNorthing)
7510 :
7511 : {
7512 20 : TAKE_OPTIONAL_LOCK();
7513 :
7514 10 : return d->replaceConversionAndUnref(
7515 : proj_create_conversion_lambert_conic_conformal_1sp(
7516 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7517 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7518 : }
7519 :
7520 : /************************************************************************/
7521 : /* OSRSetLCC1SP() */
7522 : /************************************************************************/
7523 :
7524 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7525 : double dfCenterLong, double dfScale, double dfFalseEasting,
7526 : double dfFalseNorthing)
7527 :
7528 : {
7529 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7530 :
7531 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7532 0 : dfFalseEasting, dfFalseNorthing);
7533 : }
7534 :
7535 : /************************************************************************/
7536 : /* SetLCCB() */
7537 : /************************************************************************/
7538 :
7539 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7540 : double dfCenterLat, double dfCenterLong,
7541 : double dfFalseEasting,
7542 : double dfFalseNorthing)
7543 :
7544 : {
7545 4 : TAKE_OPTIONAL_LOCK();
7546 :
7547 2 : return d->replaceConversionAndUnref(
7548 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7549 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7550 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7551 : }
7552 :
7553 : /************************************************************************/
7554 : /* OSRSetLCCB() */
7555 : /************************************************************************/
7556 :
7557 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7558 : double dfCenterLat, double dfCenterLong,
7559 : double dfFalseEasting, double dfFalseNorthing)
7560 :
7561 : {
7562 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7563 :
7564 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7565 0 : dfFalseEasting, dfFalseNorthing);
7566 : }
7567 :
7568 : /************************************************************************/
7569 : /* SetMC() */
7570 : /************************************************************************/
7571 :
7572 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7573 : double dfFalseEasting, double dfFalseNorthing)
7574 :
7575 : {
7576 8 : TAKE_OPTIONAL_LOCK();
7577 :
7578 : (void)dfCenterLat; // ignored
7579 :
7580 4 : return d->replaceConversionAndUnref(
7581 : proj_create_conversion_miller_cylindrical(
7582 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7583 8 : nullptr, 0, nullptr, 0));
7584 : }
7585 :
7586 : /************************************************************************/
7587 : /* OSRSetMC() */
7588 : /************************************************************************/
7589 :
7590 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7591 : double dfCenterLong, double dfFalseEasting,
7592 : double dfFalseNorthing)
7593 :
7594 : {
7595 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7596 :
7597 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7598 0 : dfFalseNorthing);
7599 : }
7600 :
7601 : /************************************************************************/
7602 : /* SetMercator() */
7603 : /************************************************************************/
7604 :
7605 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7606 : double dfScale, double dfFalseEasting,
7607 : double dfFalseNorthing)
7608 :
7609 : {
7610 118 : TAKE_OPTIONAL_LOCK();
7611 :
7612 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7613 : {
7614 : // Not sure this is correct, but this is how it has been used
7615 : // historically
7616 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7617 0 : dfFalseNorthing);
7618 : }
7619 59 : return d->replaceConversionAndUnref(
7620 : proj_create_conversion_mercator_variant_a(
7621 : d->getPROJContext(),
7622 : dfCenterLat, // should be zero
7623 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7624 59 : nullptr, 0));
7625 : }
7626 :
7627 : /************************************************************************/
7628 : /* OSRSetMercator() */
7629 : /************************************************************************/
7630 :
7631 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7632 : double dfCenterLong, double dfScale,
7633 : double dfFalseEasting, double dfFalseNorthing)
7634 :
7635 : {
7636 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7637 :
7638 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7639 2 : dfFalseEasting, dfFalseNorthing);
7640 : }
7641 :
7642 : /************************************************************************/
7643 : /* SetMercator2SP() */
7644 : /************************************************************************/
7645 :
7646 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7647 : double dfCenterLong,
7648 : double dfFalseEasting,
7649 : double dfFalseNorthing)
7650 :
7651 : {
7652 31 : if (dfCenterLat == 0.0)
7653 : {
7654 30 : return d->replaceConversionAndUnref(
7655 : proj_create_conversion_mercator_variant_b(
7656 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7657 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7658 : }
7659 :
7660 1 : TAKE_OPTIONAL_LOCK();
7661 :
7662 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7663 :
7664 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7665 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7666 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7667 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7668 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7669 :
7670 1 : return OGRERR_NONE;
7671 : }
7672 :
7673 : /************************************************************************/
7674 : /* OSRSetMercator2SP() */
7675 : /************************************************************************/
7676 :
7677 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7678 : double dfCenterLat, double dfCenterLong,
7679 : double dfFalseEasting, double dfFalseNorthing)
7680 :
7681 : {
7682 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7683 :
7684 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7685 1 : dfFalseEasting, dfFalseNorthing);
7686 : }
7687 :
7688 : /************************************************************************/
7689 : /* SetMollweide() */
7690 : /************************************************************************/
7691 :
7692 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7693 : double dfFalseEasting,
7694 : double dfFalseNorthing)
7695 :
7696 : {
7697 6 : TAKE_OPTIONAL_LOCK();
7698 :
7699 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7700 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7701 6 : nullptr, 0, nullptr, 0));
7702 : }
7703 :
7704 : /************************************************************************/
7705 : /* OSRSetMollweide() */
7706 : /************************************************************************/
7707 :
7708 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7709 : double dfFalseEasting, double dfFalseNorthing)
7710 :
7711 : {
7712 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7713 :
7714 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7715 0 : dfFalseNorthing);
7716 : }
7717 :
7718 : /************************************************************************/
7719 : /* SetNZMG() */
7720 : /************************************************************************/
7721 :
7722 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7723 : double dfFalseEasting,
7724 : double dfFalseNorthing)
7725 :
7726 : {
7727 14 : TAKE_OPTIONAL_LOCK();
7728 :
7729 7 : return d->replaceConversionAndUnref(
7730 : proj_create_conversion_new_zealand_mapping_grid(
7731 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7732 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7733 : }
7734 :
7735 : /************************************************************************/
7736 : /* OSRSetNZMG() */
7737 : /************************************************************************/
7738 :
7739 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7740 : double dfCenterLong, double dfFalseEasting,
7741 : double dfFalseNorthing)
7742 :
7743 : {
7744 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7745 :
7746 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7747 0 : dfFalseNorthing);
7748 : }
7749 :
7750 : /************************************************************************/
7751 : /* SetOS() */
7752 : /************************************************************************/
7753 :
7754 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7755 : double dfScale, double dfFalseEasting,
7756 : double dfFalseNorthing)
7757 :
7758 : {
7759 12 : TAKE_OPTIONAL_LOCK();
7760 :
7761 6 : return d->replaceConversionAndUnref(
7762 : proj_create_conversion_oblique_stereographic(
7763 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7764 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7765 : }
7766 :
7767 : /************************************************************************/
7768 : /* OSRSetOS() */
7769 : /************************************************************************/
7770 :
7771 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7772 : double dfCMeridian, double dfScale, double dfFalseEasting,
7773 : double dfFalseNorthing)
7774 :
7775 : {
7776 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7777 :
7778 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7779 0 : dfFalseEasting, dfFalseNorthing);
7780 : }
7781 :
7782 : /************************************************************************/
7783 : /* SetOrthographic() */
7784 : /************************************************************************/
7785 :
7786 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7787 : double dfCenterLong,
7788 : double dfFalseEasting,
7789 : double dfFalseNorthing)
7790 :
7791 : {
7792 14 : TAKE_OPTIONAL_LOCK();
7793 :
7794 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7795 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7796 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7797 : }
7798 :
7799 : /************************************************************************/
7800 : /* OSRSetOrthographic() */
7801 : /************************************************************************/
7802 :
7803 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7804 : double dfCenterLong, double dfFalseEasting,
7805 : double dfFalseNorthing)
7806 :
7807 : {
7808 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7809 :
7810 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7811 1 : dfFalseEasting, dfFalseNorthing);
7812 : }
7813 :
7814 : /************************************************************************/
7815 : /* SetPolyconic() */
7816 : /************************************************************************/
7817 :
7818 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7819 : double dfCenterLong,
7820 : double dfFalseEasting,
7821 : double dfFalseNorthing)
7822 :
7823 : {
7824 14 : TAKE_OPTIONAL_LOCK();
7825 :
7826 : // note: it seems that by some definitions this should include a
7827 : // scale_factor parameter.
7828 7 : return d->replaceConversionAndUnref(
7829 : proj_create_conversion_american_polyconic(
7830 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7831 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7832 : }
7833 :
7834 : /************************************************************************/
7835 : /* OSRSetPolyconic() */
7836 : /************************************************************************/
7837 :
7838 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7839 : double dfCenterLong, double dfFalseEasting,
7840 : double dfFalseNorthing)
7841 :
7842 : {
7843 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7844 :
7845 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7846 0 : dfFalseEasting, dfFalseNorthing);
7847 : }
7848 :
7849 : /************************************************************************/
7850 : /* SetPS() */
7851 : /************************************************************************/
7852 :
7853 : /** Sets a Polar Stereographic projection.
7854 : *
7855 : * Two variants are possible:
7856 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7857 : * interpreted as the latitude of origin, combined with the scale factor
7858 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7859 : * is interpreted as the latitude of true scale. In that situation, dfScale
7860 : * must be set to 1 (it is ignored in the projection parameters)
7861 : */
7862 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7863 : double dfScale, double dfFalseEasting,
7864 : double dfFalseNorthing)
7865 :
7866 : {
7867 60 : TAKE_OPTIONAL_LOCK();
7868 :
7869 : PJ *conv;
7870 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7871 : {
7872 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7873 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7874 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7875 : }
7876 : else
7877 : {
7878 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7879 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7880 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7881 : }
7882 :
7883 30 : const char *pszName = nullptr;
7884 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7885 30 : CPLString osName = pszName ? pszName : "";
7886 :
7887 30 : d->refreshProjObj();
7888 :
7889 30 : d->demoteFromBoundCRS();
7890 :
7891 30 : auto cs = proj_create_cartesian_2D_cs(
7892 : d->getPROJContext(),
7893 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7894 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7895 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7896 : auto projCRS =
7897 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7898 30 : d->getGeodBaseCRS(), conv, cs);
7899 30 : proj_destroy(conv);
7900 30 : proj_destroy(cs);
7901 :
7902 30 : d->setPjCRS(projCRS);
7903 :
7904 30 : d->undoDemoteFromBoundCRS();
7905 :
7906 60 : return OGRERR_NONE;
7907 : }
7908 :
7909 : /************************************************************************/
7910 : /* OSRSetPS() */
7911 : /************************************************************************/
7912 :
7913 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7914 : double dfCenterLong, double dfScale, double dfFalseEasting,
7915 : double dfFalseNorthing)
7916 :
7917 : {
7918 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7919 :
7920 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7921 1 : dfFalseEasting, dfFalseNorthing);
7922 : }
7923 :
7924 : /************************************************************************/
7925 : /* SetRobinson() */
7926 : /************************************************************************/
7927 :
7928 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7929 : double dfFalseEasting,
7930 : double dfFalseNorthing)
7931 :
7932 : {
7933 8 : TAKE_OPTIONAL_LOCK();
7934 :
7935 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7936 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7937 8 : nullptr, 0, nullptr, 0));
7938 : }
7939 :
7940 : /************************************************************************/
7941 : /* OSRSetRobinson() */
7942 : /************************************************************************/
7943 :
7944 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7945 : double dfFalseEasting, double dfFalseNorthing)
7946 :
7947 : {
7948 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7949 :
7950 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7951 0 : dfFalseNorthing);
7952 : }
7953 :
7954 : /************************************************************************/
7955 : /* SetSinusoidal() */
7956 : /************************************************************************/
7957 :
7958 35 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7959 : double dfFalseEasting,
7960 : double dfFalseNorthing)
7961 :
7962 : {
7963 70 : TAKE_OPTIONAL_LOCK();
7964 :
7965 35 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7966 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7967 70 : nullptr, 0, nullptr, 0));
7968 : }
7969 :
7970 : /************************************************************************/
7971 : /* OSRSetSinusoidal() */
7972 : /************************************************************************/
7973 :
7974 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7975 : double dfFalseEasting, double dfFalseNorthing)
7976 :
7977 : {
7978 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7979 :
7980 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7981 1 : dfFalseNorthing);
7982 : }
7983 :
7984 : /************************************************************************/
7985 : /* SetStereographic() */
7986 : /************************************************************************/
7987 :
7988 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7989 : double dfCMeridian, double dfScale,
7990 : double dfFalseEasting,
7991 : double dfFalseNorthing)
7992 :
7993 : {
7994 4 : TAKE_OPTIONAL_LOCK();
7995 :
7996 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7997 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7998 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7999 : }
8000 :
8001 : /************************************************************************/
8002 : /* OSRSetStereographic() */
8003 : /************************************************************************/
8004 :
8005 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8006 : double dfCMeridian, double dfScale,
8007 : double dfFalseEasting, double dfFalseNorthing)
8008 :
8009 : {
8010 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8011 :
8012 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8013 0 : dfFalseEasting, dfFalseNorthing);
8014 : }
8015 :
8016 : /************************************************************************/
8017 : /* SetSOC() */
8018 : /* */
8019 : /* NOTE: This definition isn't really used in practice any more */
8020 : /* and should be considered deprecated. It seems that swiss */
8021 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
8022 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
8023 : /* EPSG:2056 and Bug 423. */
8024 : /************************************************************************/
8025 :
8026 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8027 : double dfCentralMeridian,
8028 : double dfFalseEasting,
8029 : double dfFalseNorthing)
8030 :
8031 : {
8032 4 : TAKE_OPTIONAL_LOCK();
8033 :
8034 2 : return d->replaceConversionAndUnref(
8035 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8036 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8037 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8038 4 : 0.0));
8039 : #if 0
8040 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8041 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8042 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8043 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8044 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8045 :
8046 : return OGRERR_NONE;
8047 : #endif
8048 : }
8049 :
8050 : /************************************************************************/
8051 : /* OSRSetSOC() */
8052 : /************************************************************************/
8053 :
8054 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8055 : double dfCentralMeridian, double dfFalseEasting,
8056 : double dfFalseNorthing)
8057 :
8058 : {
8059 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8060 :
8061 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8062 0 : dfFalseEasting, dfFalseNorthing);
8063 : }
8064 :
8065 : /************************************************************************/
8066 : /* SetVDG() */
8067 : /************************************************************************/
8068 :
8069 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8070 : double dfFalseNorthing)
8071 :
8072 : {
8073 4 : TAKE_OPTIONAL_LOCK();
8074 :
8075 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8076 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8077 4 : nullptr, 0, nullptr, 0));
8078 : }
8079 :
8080 : /************************************************************************/
8081 : /* OSRSetVDG() */
8082 : /************************************************************************/
8083 :
8084 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8085 : double dfFalseEasting, double dfFalseNorthing)
8086 :
8087 : {
8088 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8089 :
8090 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8091 0 : dfFalseNorthing);
8092 : }
8093 :
8094 : /************************************************************************/
8095 : /* SetUTM() */
8096 : /************************************************************************/
8097 :
8098 : /**
8099 : * \brief Set UTM projection definition.
8100 : *
8101 : * This will generate a projection definition with the full set of
8102 : * transverse mercator projection parameters for the given UTM zone.
8103 : * If no PROJCS[] description is set yet, one will be set to look
8104 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8105 : *
8106 : * This method is the same as the C function OSRSetUTM().
8107 : *
8108 : * @param nZone UTM zone.
8109 : *
8110 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8111 : * hemisphere.
8112 : *
8113 : * @return OGRERR_NONE on success.
8114 : */
8115 :
8116 313 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8117 :
8118 : {
8119 626 : TAKE_OPTIONAL_LOCK();
8120 :
8121 313 : if (nZone < 0 || nZone > 60)
8122 : {
8123 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8124 0 : return OGRERR_FAILURE;
8125 : }
8126 :
8127 313 : return d->replaceConversionAndUnref(
8128 313 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8129 : }
8130 :
8131 : /************************************************************************/
8132 : /* OSRSetUTM() */
8133 : /************************************************************************/
8134 :
8135 : /**
8136 : * \brief Set UTM projection definition.
8137 : *
8138 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8139 : */
8140 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8141 :
8142 : {
8143 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8144 :
8145 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8146 : }
8147 :
8148 : /************************************************************************/
8149 : /* GetUTMZone() */
8150 : /* */
8151 : /* Returns zero if it isn't UTM. */
8152 : /************************************************************************/
8153 :
8154 : /**
8155 : * \brief Get utm zone information.
8156 : *
8157 : * This is the same as the C function OSRGetUTMZone().
8158 : *
8159 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8160 : * zone which is negative in the southern hemisphere instead of having the
8161 : * pbNorth flag used in the C and C++ interface.
8162 : *
8163 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8164 : * FALSE if southern.
8165 : *
8166 : * @return UTM zone number or zero if this isn't a UTM definition.
8167 : */
8168 :
8169 610 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8170 :
8171 : {
8172 1220 : TAKE_OPTIONAL_LOCK();
8173 :
8174 610 : if (IsProjected() && GetAxesCount() == 3)
8175 : {
8176 1 : OGRSpatialReference *poSRSTmp = Clone();
8177 1 : poSRSTmp->DemoteTo2D(nullptr);
8178 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8179 1 : delete poSRSTmp;
8180 1 : return nZone;
8181 : }
8182 :
8183 609 : const char *pszProjection = GetAttrValue("PROJECTION");
8184 :
8185 609 : if (pszProjection == nullptr ||
8186 529 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8187 278 : return 0;
8188 :
8189 331 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8190 5 : return 0;
8191 :
8192 326 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8193 15 : return 0;
8194 :
8195 311 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8196 6 : return 0;
8197 :
8198 305 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8199 :
8200 305 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8201 0 : return 0;
8202 :
8203 305 : if (pbNorth != nullptr)
8204 239 : *pbNorth = (dfFalseNorthing == 0);
8205 :
8206 : const double dfCentralMeridian =
8207 305 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8208 305 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8209 :
8210 610 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8211 915 : std::isnan(dfZone) ||
8212 305 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8213 0 : return 0;
8214 :
8215 305 : return static_cast<int>(dfZone);
8216 : }
8217 :
8218 : /************************************************************************/
8219 : /* OSRGetUTMZone() */
8220 : /************************************************************************/
8221 :
8222 : /**
8223 : * \brief Get utm zone information.
8224 : *
8225 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8226 : */
8227 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8228 :
8229 : {
8230 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8231 :
8232 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8233 : }
8234 :
8235 : /************************************************************************/
8236 : /* SetWagner() */
8237 : /************************************************************************/
8238 :
8239 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8240 : double dfCenterLat, double dfFalseEasting,
8241 : double dfFalseNorthing)
8242 :
8243 : {
8244 0 : TAKE_OPTIONAL_LOCK();
8245 :
8246 : PJ *conv;
8247 0 : if (nVariation == 1)
8248 : {
8249 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8250 : dfFalseEasting, dfFalseNorthing,
8251 : nullptr, 0.0, nullptr, 0.0);
8252 : }
8253 0 : else if (nVariation == 2)
8254 : {
8255 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8256 : dfFalseEasting, dfFalseNorthing,
8257 : nullptr, 0.0, nullptr, 0.0);
8258 : }
8259 0 : else if (nVariation == 3)
8260 : {
8261 0 : conv = proj_create_conversion_wagner_iii(
8262 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8263 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8264 : }
8265 0 : else if (nVariation == 4)
8266 : {
8267 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8268 : dfFalseEasting, dfFalseNorthing,
8269 : nullptr, 0.0, nullptr, 0.0);
8270 : }
8271 0 : else if (nVariation == 5)
8272 : {
8273 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8274 : dfFalseEasting, dfFalseNorthing,
8275 : nullptr, 0.0, nullptr, 0.0);
8276 : }
8277 0 : else if (nVariation == 6)
8278 : {
8279 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8280 : dfFalseEasting, dfFalseNorthing,
8281 : nullptr, 0.0, nullptr, 0.0);
8282 : }
8283 0 : else if (nVariation == 7)
8284 : {
8285 0 : conv = proj_create_conversion_wagner_vii(
8286 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8287 : 0.0, nullptr, 0.0);
8288 : }
8289 : else
8290 : {
8291 0 : CPLError(CE_Failure, CPLE_AppDefined,
8292 : "Unsupported Wagner variation (%d).", nVariation);
8293 0 : return OGRERR_UNSUPPORTED_SRS;
8294 : }
8295 :
8296 0 : return d->replaceConversionAndUnref(conv);
8297 : }
8298 :
8299 : /************************************************************************/
8300 : /* OSRSetWagner() */
8301 : /************************************************************************/
8302 :
8303 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8304 : double dfCenterLat, double dfFalseEasting,
8305 : double dfFalseNorthing)
8306 :
8307 : {
8308 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8309 :
8310 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8311 0 : dfFalseNorthing);
8312 : }
8313 :
8314 : /************************************************************************/
8315 : /* SetQSC() */
8316 : /************************************************************************/
8317 :
8318 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8319 : {
8320 0 : TAKE_OPTIONAL_LOCK();
8321 :
8322 0 : return d->replaceConversionAndUnref(
8323 : proj_create_conversion_quadrilateralized_spherical_cube(
8324 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8325 0 : 0, nullptr, 0));
8326 : }
8327 :
8328 : /************************************************************************/
8329 : /* OSRSetQSC() */
8330 : /************************************************************************/
8331 :
8332 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8333 : double dfCenterLong)
8334 :
8335 : {
8336 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8337 :
8338 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8339 : }
8340 :
8341 : /************************************************************************/
8342 : /* SetSCH() */
8343 : /************************************************************************/
8344 :
8345 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8346 : double dfPegHeading, double dfPegHgt)
8347 :
8348 : {
8349 0 : TAKE_OPTIONAL_LOCK();
8350 :
8351 0 : return d->replaceConversionAndUnref(
8352 : proj_create_conversion_spherical_cross_track_height(
8353 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8354 0 : nullptr, 0, nullptr, 0));
8355 : }
8356 :
8357 : /************************************************************************/
8358 : /* OSRSetSCH() */
8359 : /************************************************************************/
8360 :
8361 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8362 : double dfPegHeading, double dfPegHgt)
8363 :
8364 : {
8365 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8366 :
8367 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8368 : }
8369 :
8370 : /************************************************************************/
8371 : /* SetVerticalPerspective() */
8372 : /************************************************************************/
8373 :
8374 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8375 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8376 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8377 : {
8378 6 : TAKE_OPTIONAL_LOCK();
8379 :
8380 3 : return d->replaceConversionAndUnref(
8381 : proj_create_conversion_vertical_perspective(
8382 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8383 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8384 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8385 : }
8386 :
8387 : /************************************************************************/
8388 : /* OSRSetVerticalPerspective() */
8389 : /************************************************************************/
8390 :
8391 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8392 : double dfTopoOriginLat, double dfTopoOriginLon,
8393 : double dfTopoOriginHeight,
8394 : double dfViewPointHeight,
8395 : double dfFalseEasting, double dfFalseNorthing)
8396 :
8397 : {
8398 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8399 :
8400 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8401 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8402 1 : dfFalseEasting, dfFalseNorthing);
8403 : }
8404 :
8405 : /************************************************************************/
8406 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8407 : /************************************************************************/
8408 :
8409 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8410 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8411 : double dfAxisRotation)
8412 : {
8413 4 : TAKE_OPTIONAL_LOCK();
8414 :
8415 2 : d->refreshProjObj();
8416 2 : if (!d->m_pj_crs)
8417 0 : return OGRERR_FAILURE;
8418 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8419 0 : return OGRERR_FAILURE;
8420 2 : auto ctxt = d->getPROJContext();
8421 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8422 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8423 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8424 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8425 2 : d->m_pj_crs, conv, cs));
8426 2 : proj_destroy(conv);
8427 2 : proj_destroy(cs);
8428 2 : return OGRERR_NONE;
8429 : }
8430 :
8431 : /************************************************************************/
8432 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8433 : /************************************************************************/
8434 :
8435 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8436 : const char *pszCRSName, double dfGridNorthPoleLat,
8437 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8438 : {
8439 3 : TAKE_OPTIONAL_LOCK();
8440 :
8441 : #if PROJ_VERSION_MAJOR > 8 || \
8442 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8443 : d->refreshProjObj();
8444 : if (!d->m_pj_crs)
8445 : return OGRERR_FAILURE;
8446 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8447 : return OGRERR_FAILURE;
8448 : auto ctxt = d->getPROJContext();
8449 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8450 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8451 : nullptr, 0);
8452 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8453 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8454 : d->m_pj_crs, conv, cs));
8455 : proj_destroy(conv);
8456 : proj_destroy(cs);
8457 : return OGRERR_NONE;
8458 : #else
8459 : (void)pszCRSName;
8460 3 : SetProjection("Rotated_pole");
8461 3 : SetExtension(
8462 : "PROJCS", "PROJ4",
8463 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8464 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8465 : "+to_meter=0.0174532925199433 "
8466 : "+wktext",
8467 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8468 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8469 : GetSemiMinor(nullptr)));
8470 6 : return OGRERR_NONE;
8471 : #endif
8472 : }
8473 :
8474 : /************************************************************************/
8475 : /* SetAuthority() */
8476 : /************************************************************************/
8477 :
8478 : /**
8479 : * \brief Set the authority for a node.
8480 : *
8481 : * This method is the same as the C function OSRSetAuthority().
8482 : *
8483 : * @param pszTargetKey the partial or complete path to the node to
8484 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8485 : *
8486 : * @param pszAuthority authority name, such as "EPSG".
8487 : *
8488 : * @param nCode code for value with this authority.
8489 : *
8490 : * @return OGRERR_NONE on success.
8491 : */
8492 :
8493 12436 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8494 : const char *pszAuthority, int nCode)
8495 :
8496 : {
8497 24872 : TAKE_OPTIONAL_LOCK();
8498 :
8499 12436 : d->refreshProjObj();
8500 12436 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8501 :
8502 12436 : if (pszTargetKey == nullptr)
8503 : {
8504 263 : if (!d->m_pj_crs)
8505 0 : return OGRERR_FAILURE;
8506 263 : CPLString osCode;
8507 263 : osCode.Printf("%d", nCode);
8508 263 : d->demoteFromBoundCRS();
8509 263 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8510 : pszAuthority, osCode.c_str()));
8511 263 : d->undoDemoteFromBoundCRS();
8512 263 : return OGRERR_NONE;
8513 : }
8514 :
8515 12173 : d->demoteFromBoundCRS();
8516 12173 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8517 : {
8518 4025 : CPLString osCode;
8519 4025 : osCode.Printf("%d", nCode);
8520 : auto newGeogCRS =
8521 4025 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8522 : pszAuthority, osCode.c_str());
8523 :
8524 : auto conv =
8525 4025 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8526 :
8527 4025 : auto projCRS = proj_create_projected_crs(
8528 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8529 4025 : d->getProjCRSCoordSys());
8530 :
8531 : // Preserve existing id on the PROJCRS
8532 4025 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8533 4025 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8534 4025 : if (pszProjCRSAuthName && pszProjCRSCode)
8535 : {
8536 : auto projCRSWithId =
8537 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8538 : pszProjCRSCode);
8539 0 : proj_destroy(projCRS);
8540 0 : projCRS = projCRSWithId;
8541 : }
8542 :
8543 4025 : proj_destroy(newGeogCRS);
8544 4025 : proj_destroy(conv);
8545 :
8546 4025 : d->setPjCRS(projCRS);
8547 4025 : d->undoDemoteFromBoundCRS();
8548 4025 : return OGRERR_NONE;
8549 : }
8550 8148 : d->undoDemoteFromBoundCRS();
8551 :
8552 : /* -------------------------------------------------------------------- */
8553 : /* Find the node below which the authority should be put. */
8554 : /* -------------------------------------------------------------------- */
8555 8148 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8556 :
8557 8148 : if (poNode == nullptr)
8558 0 : return OGRERR_FAILURE;
8559 :
8560 : /* -------------------------------------------------------------------- */
8561 : /* If there is an existing AUTHORITY child blow it away before */
8562 : /* trying to set a new one. */
8563 : /* -------------------------------------------------------------------- */
8564 8148 : int iOldChild = poNode->FindChild("AUTHORITY");
8565 8148 : if (iOldChild != -1)
8566 5 : poNode->DestroyChild(iOldChild);
8567 :
8568 : /* -------------------------------------------------------------------- */
8569 : /* Create a new authority node. */
8570 : /* -------------------------------------------------------------------- */
8571 8148 : char szCode[32] = {};
8572 :
8573 8148 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8574 :
8575 8148 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8576 8148 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8577 8148 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8578 :
8579 8148 : poNode->AddChild(poAuthNode);
8580 :
8581 8148 : return OGRERR_NONE;
8582 : }
8583 :
8584 : /************************************************************************/
8585 : /* OSRSetAuthority() */
8586 : /************************************************************************/
8587 :
8588 : /**
8589 : * \brief Set the authority for a node.
8590 : *
8591 : * This function is the same as OGRSpatialReference::SetAuthority().
8592 : */
8593 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8594 : const char *pszAuthority, int nCode)
8595 :
8596 : {
8597 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8598 :
8599 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8600 : }
8601 :
8602 : /************************************************************************/
8603 : /* GetAuthorityCode() */
8604 : /************************************************************************/
8605 :
8606 : /**
8607 : * \brief Get the authority code for a node.
8608 : *
8609 : * This method is used to query an AUTHORITY[] node from within the
8610 : * WKT tree, and fetch the code value.
8611 : *
8612 : * While in theory values may be non-numeric, for the EPSG authority all
8613 : * code values should be integral.
8614 : *
8615 : * This method is the same as the C function OSRGetAuthorityCode().
8616 : *
8617 : * @param pszTargetKey the partial or complete path to the node to
8618 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8619 : * search for an authority node on the root element.
8620 : *
8621 : * @return value code from authority node, or NULL on failure. The value
8622 : * returned is internal and should not be freed or modified.
8623 : */
8624 :
8625 : const char *
8626 49738 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8627 :
8628 : {
8629 99476 : TAKE_OPTIONAL_LOCK();
8630 :
8631 49738 : d->refreshProjObj();
8632 49738 : const char *pszInputTargetKey = pszTargetKey;
8633 49738 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8634 49738 : if (pszTargetKey == nullptr)
8635 : {
8636 41508 : if (!d->m_pj_crs)
8637 : {
8638 17 : return nullptr;
8639 : }
8640 41491 : d->demoteFromBoundCRS();
8641 41491 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8642 41491 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8643 : {
8644 1379 : auto ctxt = d->getPROJContext();
8645 1379 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8646 1379 : if (cs)
8647 : {
8648 1379 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8649 1379 : proj_destroy(cs);
8650 1379 : if (axisCount == 3)
8651 : {
8652 : // This might come from a COMPD_CS with a VERT_DATUM type =
8653 : // 2002 in which case, using the WKT1 representation will
8654 : // enable us to recover the EPSG code.
8655 14 : pszTargetKey = pszInputTargetKey;
8656 : }
8657 : }
8658 : }
8659 41491 : d->undoDemoteFromBoundCRS();
8660 41491 : if (ret != nullptr || pszTargetKey == nullptr)
8661 : {
8662 41491 : return ret;
8663 : }
8664 : }
8665 :
8666 : // Special key for that context
8667 8234 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8668 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8669 : {
8670 4 : auto ctxt = d->getPROJContext();
8671 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8672 4 : if (crs)
8673 : {
8674 4 : const char *ret = proj_get_id_code(crs, 0);
8675 4 : if (ret)
8676 4 : ret = CPLSPrintf("%s", ret);
8677 4 : proj_destroy(crs);
8678 4 : return ret;
8679 : }
8680 : }
8681 8230 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8682 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8683 : {
8684 4 : auto ctxt = d->getPROJContext();
8685 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8686 4 : if (crs)
8687 : {
8688 4 : const char *ret = proj_get_id_code(crs, 0);
8689 4 : if (ret)
8690 4 : ret = CPLSPrintf("%s", ret);
8691 4 : proj_destroy(crs);
8692 4 : return ret;
8693 : }
8694 : }
8695 :
8696 : /* -------------------------------------------------------------------- */
8697 : /* Find the node below which the authority should be put. */
8698 : /* -------------------------------------------------------------------- */
8699 8222 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8700 :
8701 8222 : if (poNode == nullptr)
8702 105 : return nullptr;
8703 :
8704 : /* -------------------------------------------------------------------- */
8705 : /* Fetch AUTHORITY child if there is one. */
8706 : /* -------------------------------------------------------------------- */
8707 8117 : if (poNode->FindChild("AUTHORITY") == -1)
8708 197 : return nullptr;
8709 :
8710 7920 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8711 :
8712 : /* -------------------------------------------------------------------- */
8713 : /* Create a new authority node. */
8714 : /* -------------------------------------------------------------------- */
8715 7920 : if (poNode->GetChildCount() < 2)
8716 0 : return nullptr;
8717 :
8718 7920 : return poNode->GetChild(1)->GetValue();
8719 : }
8720 :
8721 : /************************************************************************/
8722 : /* OSRGetAuthorityCode() */
8723 : /************************************************************************/
8724 :
8725 : /**
8726 : * \brief Get the authority code for a node.
8727 : *
8728 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8729 : */
8730 875 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8731 : const char *pszTargetKey)
8732 :
8733 : {
8734 875 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8735 :
8736 875 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8737 : }
8738 :
8739 : /************************************************************************/
8740 : /* GetAuthorityName() */
8741 : /************************************************************************/
8742 :
8743 : /**
8744 : * \brief Get the authority name for a node.
8745 : *
8746 : * This method is used to query an AUTHORITY[] node from within the
8747 : * WKT tree, and fetch the authority name value.
8748 : *
8749 : * The most common authority is "EPSG".
8750 : *
8751 : * This method is the same as the C function OSRGetAuthorityName().
8752 : *
8753 : * @param pszTargetKey the partial or complete path to the node to
8754 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8755 : * search for an authority node on the root element.
8756 : *
8757 : * @return value code from authority node, or NULL on failure. The value
8758 : * returned is internal and should not be freed or modified.
8759 : */
8760 :
8761 : const char *
8762 63478 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8763 :
8764 : {
8765 126956 : TAKE_OPTIONAL_LOCK();
8766 :
8767 63478 : d->refreshProjObj();
8768 63478 : const char *pszInputTargetKey = pszTargetKey;
8769 63478 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8770 63478 : if (pszTargetKey == nullptr)
8771 : {
8772 34758 : if (!d->m_pj_crs)
8773 : {
8774 18 : return nullptr;
8775 : }
8776 34740 : d->demoteFromBoundCRS();
8777 34740 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8778 34740 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8779 : {
8780 1002 : auto ctxt = d->getPROJContext();
8781 1002 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8782 1002 : if (cs)
8783 : {
8784 1002 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8785 1002 : proj_destroy(cs);
8786 1002 : if (axisCount == 3)
8787 : {
8788 : // This might come from a COMPD_CS with a VERT_DATUM type =
8789 : // 2002 in which case, using the WKT1 representation will
8790 : // enable us to recover the EPSG code.
8791 14 : pszTargetKey = pszInputTargetKey;
8792 : }
8793 : }
8794 : }
8795 34740 : d->undoDemoteFromBoundCRS();
8796 34740 : if (ret != nullptr || pszTargetKey == nullptr)
8797 : {
8798 34740 : return ret;
8799 : }
8800 : }
8801 :
8802 : // Special key for that context
8803 28724 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8804 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8805 : {
8806 4 : auto ctxt = d->getPROJContext();
8807 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8808 4 : if (crs)
8809 : {
8810 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8811 4 : if (ret)
8812 4 : ret = CPLSPrintf("%s", ret);
8813 4 : proj_destroy(crs);
8814 4 : return ret;
8815 : }
8816 : }
8817 28720 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8818 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8819 : {
8820 4 : auto ctxt = d->getPROJContext();
8821 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8822 4 : if (crs)
8823 : {
8824 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8825 4 : if (ret)
8826 4 : ret = CPLSPrintf("%s", ret);
8827 4 : proj_destroy(crs);
8828 4 : return ret;
8829 : }
8830 : }
8831 :
8832 : /* -------------------------------------------------------------------- */
8833 : /* Find the node below which the authority should be put. */
8834 : /* -------------------------------------------------------------------- */
8835 28712 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8836 :
8837 28712 : if (poNode == nullptr)
8838 11885 : return nullptr;
8839 :
8840 : /* -------------------------------------------------------------------- */
8841 : /* Fetch AUTHORITY child if there is one. */
8842 : /* -------------------------------------------------------------------- */
8843 16827 : if (poNode->FindChild("AUTHORITY") == -1)
8844 1595 : return nullptr;
8845 :
8846 15232 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8847 :
8848 : /* -------------------------------------------------------------------- */
8849 : /* Create a new authority node. */
8850 : /* -------------------------------------------------------------------- */
8851 15232 : if (poNode->GetChildCount() < 2)
8852 0 : return nullptr;
8853 :
8854 15232 : return poNode->GetChild(0)->GetValue();
8855 : }
8856 :
8857 : /************************************************************************/
8858 : /* OSRGetAuthorityName() */
8859 : /************************************************************************/
8860 :
8861 : /**
8862 : * \brief Get the authority name for a node.
8863 : *
8864 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8865 : */
8866 190 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8867 : const char *pszTargetKey)
8868 :
8869 : {
8870 190 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8871 :
8872 190 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8873 : }
8874 :
8875 : /************************************************************************/
8876 : /* GetOGCURN() */
8877 : /************************************************************************/
8878 :
8879 : /**
8880 : * \brief Get a OGC URN string describing the CRS, when possible
8881 : *
8882 : * This method assumes that the CRS has a top-level identifier, or is
8883 : * a compound CRS whose horizontal and vertical parts have a top-level
8884 : * identifier.
8885 : *
8886 : * @return a string to free with CPLFree(), or nullptr when no result can be
8887 : * generated
8888 : *
8889 : * @since GDAL 3.5
8890 : */
8891 :
8892 61 : char *OGRSpatialReference::GetOGCURN() const
8893 :
8894 : {
8895 122 : TAKE_OPTIONAL_LOCK();
8896 :
8897 61 : const char *pszAuthName = GetAuthorityName(nullptr);
8898 61 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8899 61 : if (pszAuthName && pszAuthCode)
8900 58 : return CPLStrdup(
8901 58 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8902 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8903 2 : return nullptr;
8904 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8905 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8906 1 : char *pszRet = nullptr;
8907 1 : if (horizCRS && vertCRS)
8908 : {
8909 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8910 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8911 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8912 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8913 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8914 : {
8915 1 : pszRet = CPLStrdup(CPLSPrintf(
8916 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8917 : horizAuthCode, vertAuthName, vertAuthCode));
8918 : }
8919 : }
8920 1 : proj_destroy(horizCRS);
8921 1 : proj_destroy(vertCRS);
8922 1 : return pszRet;
8923 : }
8924 :
8925 : /************************************************************************/
8926 : /* StripVertical() */
8927 : /************************************************************************/
8928 :
8929 : /**
8930 : * \brief Convert a compound cs into a horizontal CS.
8931 : *
8932 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8933 : * nodes are stripped resulting and only the horizontal coordinate system
8934 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8935 : *
8936 : * If this is not a compound coordinate system then nothing is changed.
8937 : *
8938 : * This method is the same as the C function OSRStripVertical().
8939 : *
8940 : */
8941 :
8942 46 : OGRErr OGRSpatialReference::StripVertical()
8943 :
8944 : {
8945 92 : TAKE_OPTIONAL_LOCK();
8946 :
8947 46 : d->refreshProjObj();
8948 46 : d->demoteFromBoundCRS();
8949 46 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8950 : {
8951 0 : d->undoDemoteFromBoundCRS();
8952 0 : return OGRERR_NONE;
8953 : }
8954 46 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8955 46 : if (!horizCRS)
8956 : {
8957 0 : d->undoDemoteFromBoundCRS();
8958 0 : return OGRERR_FAILURE;
8959 : }
8960 :
8961 46 : bool reuseExistingBoundCRS = false;
8962 46 : if (d->m_pj_bound_crs_target)
8963 : {
8964 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8965 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8966 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8967 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8968 : }
8969 :
8970 46 : if (reuseExistingBoundCRS)
8971 : {
8972 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8973 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8974 4 : d->m_pj_bound_crs_co);
8975 4 : proj_destroy(horizCRS);
8976 4 : d->undoDemoteFromBoundCRS();
8977 4 : d->setPjCRS(newBoundCRS);
8978 : }
8979 : else
8980 : {
8981 42 : d->undoDemoteFromBoundCRS();
8982 42 : d->setPjCRS(horizCRS);
8983 : }
8984 :
8985 46 : return OGRERR_NONE;
8986 : }
8987 :
8988 : /************************************************************************/
8989 : /* OSRStripVertical() */
8990 : /************************************************************************/
8991 : /**
8992 : * \brief Convert a compound cs into a horizontal CS.
8993 : *
8994 : * This function is the same as the C++ method
8995 : * OGRSpatialReference::StripVertical().
8996 : */
8997 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8998 :
8999 : {
9000 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9001 :
9002 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9003 : }
9004 :
9005 : /************************************************************************/
9006 : /* StripTOWGS84IfKnownDatumAndAllowed() */
9007 : /************************************************************************/
9008 :
9009 : /**
9010 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9011 : * and this is allowed by the user.
9012 : *
9013 : * The default behavior is to remove TOWGS84 information if the CRS has a
9014 : * known horizontal datum. This can be disabled by setting the
9015 : * OSR_STRIP_TOWGS84 configuration option to NO.
9016 : *
9017 : * @return true if TOWGS84 has been removed.
9018 : * @since OGR 3.1.0
9019 : */
9020 :
9021 9344 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9022 : {
9023 9344 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9024 : {
9025 9341 : if (StripTOWGS84IfKnownDatum())
9026 : {
9027 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9028 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9029 : "configuration option to NO");
9030 72 : return true;
9031 : }
9032 : }
9033 9272 : return false;
9034 : }
9035 :
9036 : /************************************************************************/
9037 : /* StripTOWGS84IfKnownDatum() */
9038 : /************************************************************************/
9039 :
9040 : /**
9041 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9042 : *
9043 : * @return true if TOWGS84 has been removed.
9044 : * @since OGR 3.1.0
9045 : */
9046 :
9047 9347 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9048 :
9049 : {
9050 18694 : TAKE_OPTIONAL_LOCK();
9051 :
9052 9347 : d->refreshProjObj();
9053 9347 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9054 : {
9055 9255 : return false;
9056 : }
9057 92 : auto ctxt = d->getPROJContext();
9058 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9059 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9060 : {
9061 3 : proj_destroy(baseCRS);
9062 3 : return false;
9063 : }
9064 :
9065 : // Known base CRS code ? Return base CRS
9066 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9067 89 : if (pszCode)
9068 : {
9069 2 : d->setPjCRS(baseCRS);
9070 2 : return true;
9071 : }
9072 :
9073 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9074 : #if PROJ_VERSION_MAJOR > 7 || \
9075 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9076 : if (datum == nullptr)
9077 : {
9078 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9079 : }
9080 : #endif
9081 87 : if (!datum)
9082 : {
9083 0 : proj_destroy(baseCRS);
9084 0 : return false;
9085 : }
9086 :
9087 : // Known datum code ? Return base CRS
9088 87 : pszCode = proj_get_id_code(datum, 0);
9089 87 : if (pszCode)
9090 : {
9091 3 : proj_destroy(datum);
9092 3 : d->setPjCRS(baseCRS);
9093 3 : return true;
9094 : }
9095 :
9096 84 : const char *name = proj_get_name(datum);
9097 84 : if (EQUAL(name, "unknown"))
9098 : {
9099 1 : proj_destroy(datum);
9100 1 : proj_destroy(baseCRS);
9101 1 : return false;
9102 : }
9103 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9104 : PJ_OBJ_LIST *list =
9105 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9106 :
9107 83 : bool knownDatumName = false;
9108 83 : if (list)
9109 : {
9110 83 : if (proj_list_get_count(list) == 1)
9111 : {
9112 70 : knownDatumName = true;
9113 : }
9114 83 : proj_list_destroy(list);
9115 : }
9116 :
9117 83 : proj_destroy(datum);
9118 83 : if (knownDatumName)
9119 : {
9120 70 : d->setPjCRS(baseCRS);
9121 70 : return true;
9122 : }
9123 13 : proj_destroy(baseCRS);
9124 13 : return false;
9125 : }
9126 :
9127 : /************************************************************************/
9128 : /* IsCompound() */
9129 : /************************************************************************/
9130 :
9131 : /**
9132 : * \brief Check if coordinate system is compound.
9133 : *
9134 : * This method is the same as the C function OSRIsCompound().
9135 : *
9136 : * @return TRUE if this is rooted with a COMPD_CS node.
9137 : */
9138 :
9139 41723 : int OGRSpatialReference::IsCompound() const
9140 :
9141 : {
9142 41723 : TAKE_OPTIONAL_LOCK();
9143 :
9144 41723 : d->refreshProjObj();
9145 41723 : d->demoteFromBoundCRS();
9146 41723 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9147 41723 : d->undoDemoteFromBoundCRS();
9148 83446 : return isCompound;
9149 : }
9150 :
9151 : /************************************************************************/
9152 : /* OSRIsCompound() */
9153 : /************************************************************************/
9154 :
9155 : /**
9156 : * \brief Check if the coordinate system is compound.
9157 : *
9158 : * This function is the same as OGRSpatialReference::IsCompound().
9159 : */
9160 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9161 :
9162 : {
9163 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9164 :
9165 5 : return ToPointer(hSRS)->IsCompound();
9166 : }
9167 :
9168 : /************************************************************************/
9169 : /* IsProjected() */
9170 : /************************************************************************/
9171 :
9172 : /**
9173 : * \brief Check if projected coordinate system.
9174 : *
9175 : * This method is the same as the C function OSRIsProjected().
9176 : *
9177 : * @return TRUE if this contains a PROJCS node indicating a it is a
9178 : * projected coordinate system. Also if it is a CompoundCRS made of a
9179 : * ProjectedCRS
9180 : */
9181 :
9182 44625 : int OGRSpatialReference::IsProjected() const
9183 :
9184 : {
9185 44625 : TAKE_OPTIONAL_LOCK();
9186 :
9187 44625 : d->refreshProjObj();
9188 44625 : d->demoteFromBoundCRS();
9189 44625 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9190 44625 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9191 : {
9192 : auto horizCRS =
9193 150 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9194 150 : if (horizCRS)
9195 : {
9196 150 : auto horizCRSType = proj_get_type(horizCRS);
9197 150 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9198 150 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9199 : {
9200 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9201 6 : if (base)
9202 : {
9203 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9204 6 : proj_destroy(base);
9205 : }
9206 : }
9207 150 : proj_destroy(horizCRS);
9208 : }
9209 : }
9210 44625 : d->undoDemoteFromBoundCRS();
9211 89250 : return isProjected;
9212 : }
9213 :
9214 : /************************************************************************/
9215 : /* OSRIsProjected() */
9216 : /************************************************************************/
9217 : /**
9218 : * \brief Check if projected coordinate system.
9219 : *
9220 : * This function is the same as OGRSpatialReference::IsProjected().
9221 : */
9222 413 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9223 :
9224 : {
9225 413 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9226 :
9227 413 : return ToPointer(hSRS)->IsProjected();
9228 : }
9229 :
9230 : /************************************************************************/
9231 : /* IsGeocentric() */
9232 : /************************************************************************/
9233 :
9234 : /**
9235 : * \brief Check if geocentric coordinate system.
9236 : *
9237 : * This method is the same as the C function OSRIsGeocentric().
9238 : *
9239 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9240 : * geocentric coordinate system.
9241 : *
9242 : */
9243 :
9244 17583 : int OGRSpatialReference::IsGeocentric() const
9245 :
9246 : {
9247 17583 : TAKE_OPTIONAL_LOCK();
9248 :
9249 17583 : d->refreshProjObj();
9250 17583 : d->demoteFromBoundCRS();
9251 17583 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9252 17583 : d->undoDemoteFromBoundCRS();
9253 35166 : return isGeocentric;
9254 : }
9255 :
9256 : /************************************************************************/
9257 : /* OSRIsGeocentric() */
9258 : /************************************************************************/
9259 : /**
9260 : * \brief Check if geocentric coordinate system.
9261 : *
9262 : * This function is the same as OGRSpatialReference::IsGeocentric().
9263 : *
9264 : */
9265 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9266 :
9267 : {
9268 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9269 :
9270 2 : return ToPointer(hSRS)->IsGeocentric();
9271 : }
9272 :
9273 : /************************************************************************/
9274 : /* IsEmpty() */
9275 : /************************************************************************/
9276 :
9277 : /**
9278 : * \brief Return if the SRS is not set.
9279 : */
9280 :
9281 117475 : bool OGRSpatialReference::IsEmpty() const
9282 : {
9283 117475 : TAKE_OPTIONAL_LOCK();
9284 :
9285 117475 : d->refreshProjObj();
9286 234950 : return d->m_pj_crs == nullptr;
9287 : }
9288 :
9289 : /************************************************************************/
9290 : /* IsGeographic() */
9291 : /************************************************************************/
9292 :
9293 : /**
9294 : * \brief Check if geographic coordinate system.
9295 : *
9296 : * This method is the same as the C function OSRIsGeographic().
9297 : *
9298 : * @return TRUE if this spatial reference is geographic ... that is the
9299 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9300 : * GeographicCRS
9301 : */
9302 :
9303 61779 : int OGRSpatialReference::IsGeographic() const
9304 :
9305 : {
9306 61779 : TAKE_OPTIONAL_LOCK();
9307 :
9308 61779 : d->refreshProjObj();
9309 61779 : d->demoteFromBoundCRS();
9310 86507 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9311 24728 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9312 61779 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9313 : {
9314 : auto horizCRS =
9315 300 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9316 300 : if (horizCRS)
9317 : {
9318 300 : auto horizCRSType = proj_get_type(horizCRS);
9319 300 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9320 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9321 300 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9322 : {
9323 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9324 13 : if (base)
9325 : {
9326 13 : horizCRSType = proj_get_type(base);
9327 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9328 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9329 13 : proj_destroy(base);
9330 : }
9331 : }
9332 300 : proj_destroy(horizCRS);
9333 : }
9334 : }
9335 61779 : d->undoDemoteFromBoundCRS();
9336 123558 : return isGeog;
9337 : }
9338 :
9339 : /************************************************************************/
9340 : /* OSRIsGeographic() */
9341 : /************************************************************************/
9342 : /**
9343 : * \brief Check if geographic coordinate system.
9344 : *
9345 : * This function is the same as OGRSpatialReference::IsGeographic().
9346 : */
9347 304 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9348 :
9349 : {
9350 304 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9351 :
9352 304 : return ToPointer(hSRS)->IsGeographic();
9353 : }
9354 :
9355 : /************************************************************************/
9356 : /* IsDerivedGeographic() */
9357 : /************************************************************************/
9358 :
9359 : /**
9360 : * \brief Check if the CRS is a derived geographic coordinate system.
9361 : * (for example a rotated long/lat grid)
9362 : *
9363 : * This method is the same as the C function OSRIsDerivedGeographic().
9364 : *
9365 : * @since GDAL 3.1.0 and PROJ 6.3.0
9366 : */
9367 :
9368 15462 : int OGRSpatialReference::IsDerivedGeographic() const
9369 :
9370 : {
9371 15462 : TAKE_OPTIONAL_LOCK();
9372 :
9373 15462 : d->refreshProjObj();
9374 15462 : d->demoteFromBoundCRS();
9375 25228 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9376 9766 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9377 : const bool isDerivedGeographic =
9378 15462 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9379 15462 : d->undoDemoteFromBoundCRS();
9380 30924 : return isDerivedGeographic ? TRUE : FALSE;
9381 : }
9382 :
9383 : /************************************************************************/
9384 : /* OSRIsDerivedGeographic() */
9385 : /************************************************************************/
9386 : /**
9387 : * \brief Check if the CRS is a derived geographic coordinate system.
9388 : * (for example a rotated long/lat grid)
9389 : *
9390 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9391 : */
9392 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9393 :
9394 : {
9395 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9396 :
9397 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9398 : }
9399 :
9400 : /************************************************************************/
9401 : /* IsDerivedProjected() */
9402 : /************************************************************************/
9403 :
9404 : /**
9405 : * \brief Check if the CRS is a derived projected coordinate system.
9406 : *
9407 : * This method is the same as the C function OSRIsDerivedGeographic().
9408 : *
9409 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9410 : */
9411 :
9412 0 : int OGRSpatialReference::IsDerivedProjected() const
9413 :
9414 : {
9415 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9416 : TAKE_OPTIONAL_LOCK();
9417 : d->refreshProjObj();
9418 : d->demoteFromBoundCRS();
9419 : const bool isDerivedProjected =
9420 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9421 : d->undoDemoteFromBoundCRS();
9422 : return isDerivedProjected ? TRUE : FALSE;
9423 : #else
9424 0 : return FALSE;
9425 : #endif
9426 : }
9427 :
9428 : /************************************************************************/
9429 : /* OSRIsDerivedProjected() */
9430 : /************************************************************************/
9431 : /**
9432 : * \brief Check if the CRS is a derived projected coordinate system.
9433 : *
9434 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9435 : *
9436 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9437 : */
9438 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9439 :
9440 : {
9441 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9442 :
9443 0 : return ToPointer(hSRS)->IsDerivedProjected();
9444 : }
9445 :
9446 : /************************************************************************/
9447 : /* IsLocal() */
9448 : /************************************************************************/
9449 :
9450 : /**
9451 : * \brief Check if local coordinate system.
9452 : *
9453 : * This method is the same as the C function OSRIsLocal().
9454 : *
9455 : * @return TRUE if this spatial reference is local ... that is the
9456 : * root is a LOCAL_CS node.
9457 : */
9458 :
9459 8110 : int OGRSpatialReference::IsLocal() const
9460 :
9461 : {
9462 8110 : TAKE_OPTIONAL_LOCK();
9463 8110 : d->refreshProjObj();
9464 16220 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9465 : }
9466 :
9467 : /************************************************************************/
9468 : /* OSRIsLocal() */
9469 : /************************************************************************/
9470 : /**
9471 : * \brief Check if local coordinate system.
9472 : *
9473 : * This function is the same as OGRSpatialReference::IsLocal().
9474 : */
9475 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9476 :
9477 : {
9478 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9479 :
9480 8 : return ToPointer(hSRS)->IsLocal();
9481 : }
9482 :
9483 : /************************************************************************/
9484 : /* IsVertical() */
9485 : /************************************************************************/
9486 :
9487 : /**
9488 : * \brief Check if vertical coordinate system.
9489 : *
9490 : * This method is the same as the C function OSRIsVertical().
9491 : *
9492 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9493 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9494 : * VerticalCRS
9495 : *
9496 : */
9497 :
9498 8989 : int OGRSpatialReference::IsVertical() const
9499 :
9500 : {
9501 8989 : TAKE_OPTIONAL_LOCK();
9502 8989 : d->refreshProjObj();
9503 8989 : d->demoteFromBoundCRS();
9504 8989 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9505 8989 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9506 : {
9507 : auto vertCRS =
9508 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9509 33 : if (vertCRS)
9510 : {
9511 33 : const auto vertCRSType = proj_get_type(vertCRS);
9512 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9513 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9514 : {
9515 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9516 0 : if (base)
9517 : {
9518 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9519 0 : proj_destroy(base);
9520 : }
9521 : }
9522 33 : proj_destroy(vertCRS);
9523 : }
9524 : }
9525 8989 : d->undoDemoteFromBoundCRS();
9526 17978 : return isVertical;
9527 : }
9528 :
9529 : /************************************************************************/
9530 : /* OSRIsVertical() */
9531 : /************************************************************************/
9532 : /**
9533 : * \brief Check if vertical coordinate system.
9534 : *
9535 : * This function is the same as OGRSpatialReference::IsVertical().
9536 : *
9537 : */
9538 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9539 :
9540 : {
9541 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9542 :
9543 0 : return ToPointer(hSRS)->IsVertical();
9544 : }
9545 :
9546 : /************************************************************************/
9547 : /* IsDynamic() */
9548 : /************************************************************************/
9549 :
9550 : /**
9551 : * \brief Check if a CRS is a dynamic CRS.
9552 : *
9553 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9554 : * plate-fixed.
9555 : *
9556 : * This method is the same as the C function OSRIsDynamic().
9557 : *
9558 : * @return true if the CRS is dynamic
9559 : *
9560 : * @since OGR 3.4.0
9561 : *
9562 : * @see HasPointMotionOperation()
9563 : */
9564 :
9565 16359 : bool OGRSpatialReference::IsDynamic() const
9566 :
9567 : {
9568 16359 : TAKE_OPTIONAL_LOCK();
9569 16359 : bool isDynamic = false;
9570 16359 : d->refreshProjObj();
9571 16359 : d->demoteFromBoundCRS();
9572 16359 : auto ctxt = d->getPROJContext();
9573 16359 : PJ *horiz = nullptr;
9574 16359 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9575 : {
9576 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9577 : }
9578 16263 : else if (d->m_pj_crs)
9579 : {
9580 16069 : horiz = proj_clone(ctxt, d->m_pj_crs);
9581 : }
9582 16359 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9583 : {
9584 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9585 6 : if (baseCRS)
9586 : {
9587 6 : proj_destroy(horiz);
9588 6 : horiz = baseCRS;
9589 : }
9590 : }
9591 16359 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9592 16359 : if (datum)
9593 : {
9594 16130 : const auto type = proj_get_type(datum);
9595 16130 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9596 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9597 16130 : if (!isDynamic)
9598 : {
9599 16130 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9600 16130 : const char *code = proj_get_id_code(datum, 0);
9601 16130 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9602 15651 : EQUAL(code, "6326"))
9603 : {
9604 10231 : isDynamic = true;
9605 : }
9606 : }
9607 16130 : proj_destroy(datum);
9608 : }
9609 : #if PROJ_VERSION_MAJOR > 7 || \
9610 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9611 : else
9612 : {
9613 : auto ensemble =
9614 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9615 : if (ensemble)
9616 : {
9617 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9618 : if (member)
9619 : {
9620 : const auto type = proj_get_type(member);
9621 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9622 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9623 : proj_destroy(member);
9624 : }
9625 : proj_destroy(ensemble);
9626 : }
9627 : }
9628 : #endif
9629 16359 : proj_destroy(horiz);
9630 16359 : d->undoDemoteFromBoundCRS();
9631 32718 : return isDynamic;
9632 : }
9633 :
9634 : /************************************************************************/
9635 : /* OSRIsDynamic() */
9636 : /************************************************************************/
9637 : /**
9638 : * \brief Check if a CRS is a dynamic CRS.
9639 : *
9640 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9641 : * plate-fixed.
9642 : *
9643 : * This function is the same as OGRSpatialReference::IsDynamic().
9644 : *
9645 : * @since OGR 3.4.0
9646 : */
9647 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9648 :
9649 : {
9650 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9651 :
9652 0 : return ToPointer(hSRS)->IsDynamic();
9653 : }
9654 :
9655 : /************************************************************************/
9656 : /* HasPointMotionOperation() */
9657 : /************************************************************************/
9658 :
9659 : /**
9660 : * \brief Check if a CRS has at least an associated point motion operation.
9661 : *
9662 : * Some CRS are not formally declared as dynamic, but may behave as such
9663 : * in practice due to the presence of point motion operation, to perform
9664 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9665 : *
9666 : * @return true if the CRS has at least an associated point motion operation.
9667 : *
9668 : * @since OGR 3.8.0 and PROJ 9.4.0
9669 : *
9670 : * @see IsDynamic()
9671 : */
9672 :
9673 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9674 :
9675 : {
9676 : #if PROJ_VERSION_MAJOR > 9 || \
9677 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9678 : TAKE_OPTIONAL_LOCK();
9679 : d->refreshProjObj();
9680 : d->demoteFromBoundCRS();
9681 : auto ctxt = d->getPROJContext();
9682 : auto res =
9683 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9684 : d->undoDemoteFromBoundCRS();
9685 : return res;
9686 : #else
9687 5 : return false;
9688 : #endif
9689 : }
9690 :
9691 : /************************************************************************/
9692 : /* OSRHasPointMotionOperation() */
9693 : /************************************************************************/
9694 :
9695 : /**
9696 : * \brief Check if a CRS has at least an associated point motion operation.
9697 : *
9698 : * Some CRS are not formally declared as dynamic, but may behave as such
9699 : * in practice due to the presence of point motion operation, to perform
9700 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9701 : *
9702 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9703 : *
9704 : * @since OGR 3.8.0 and PROJ 9.4.0
9705 : */
9706 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9707 :
9708 : {
9709 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9710 :
9711 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9712 : }
9713 :
9714 : /************************************************************************/
9715 : /* CloneGeogCS() */
9716 : /************************************************************************/
9717 :
9718 : /**
9719 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9720 : * object.
9721 : *
9722 : * @return a new SRS, which becomes the responsibility of the caller.
9723 : */
9724 4345 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9725 :
9726 : {
9727 8690 : TAKE_OPTIONAL_LOCK();
9728 4345 : d->refreshProjObj();
9729 4345 : if (d->m_pj_crs)
9730 : {
9731 4345 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9732 0 : return nullptr;
9733 :
9734 : auto geodCRS =
9735 4345 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9736 4345 : if (geodCRS)
9737 : {
9738 4345 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9739 4345 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9740 : {
9741 : PJ *hub_crs =
9742 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9743 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9744 13 : d->m_pj_crs);
9745 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9746 : geodCRS, hub_crs, co);
9747 13 : proj_destroy(geodCRS);
9748 13 : geodCRS = temp;
9749 13 : proj_destroy(hub_crs);
9750 13 : proj_destroy(co);
9751 : }
9752 :
9753 : /* --------------------------------------------------------------------
9754 : */
9755 : /* We have to reconstruct the GEOGCS node for geocentric */
9756 : /* coordinate systems. */
9757 : /* --------------------------------------------------------------------
9758 : */
9759 4345 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9760 : {
9761 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9762 : #if PROJ_VERSION_MAJOR > 7 || \
9763 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9764 : if (datum == nullptr)
9765 : {
9766 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9767 : geodCRS);
9768 : }
9769 : #endif
9770 0 : if (datum)
9771 : {
9772 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9773 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9774 : nullptr, 0);
9775 0 : auto temp = proj_create_geographic_crs_from_datum(
9776 : d->getPROJContext(), "unnamed", datum, cs);
9777 0 : proj_destroy(datum);
9778 0 : proj_destroy(cs);
9779 0 : proj_destroy(geodCRS);
9780 0 : geodCRS = temp;
9781 : }
9782 : }
9783 :
9784 4345 : poNewSRS->d->setPjCRS(geodCRS);
9785 4345 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9786 2871 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9787 4345 : return poNewSRS;
9788 : }
9789 : }
9790 0 : return nullptr;
9791 : }
9792 :
9793 : /************************************************************************/
9794 : /* OSRCloneGeogCS() */
9795 : /************************************************************************/
9796 : /**
9797 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9798 : * object.
9799 : *
9800 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9801 : */
9802 126 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9803 :
9804 : {
9805 126 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9806 :
9807 126 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9808 : }
9809 :
9810 : /************************************************************************/
9811 : /* IsSameGeogCS() */
9812 : /************************************************************************/
9813 :
9814 : /**
9815 : * \brief Do the GeogCS'es match?
9816 : *
9817 : * This method is the same as the C function OSRIsSameGeogCS().
9818 : *
9819 : * @param poOther the SRS being compared against.
9820 : *
9821 : * @return TRUE if they are the same or FALSE otherwise.
9822 : */
9823 :
9824 8628 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9825 :
9826 : {
9827 8628 : return IsSameGeogCS(poOther, nullptr);
9828 : }
9829 :
9830 : /**
9831 : * \brief Do the GeogCS'es match?
9832 : *
9833 : * This method is the same as the C function OSRIsSameGeogCS().
9834 : *
9835 : * @param poOther the SRS being compared against.
9836 : * @param papszOptions options. ignored
9837 : *
9838 : * @return TRUE if they are the same or FALSE otherwise.
9839 : */
9840 :
9841 8628 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9842 : const char *const *papszOptions) const
9843 :
9844 : {
9845 17256 : TAKE_OPTIONAL_LOCK();
9846 :
9847 8628 : CPL_IGNORE_RET_VAL(papszOptions);
9848 :
9849 8628 : d->refreshProjObj();
9850 8628 : poOther->d->refreshProjObj();
9851 8628 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9852 0 : return FALSE;
9853 8628 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9854 8628 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9855 25884 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9856 8628 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9857 : {
9858 0 : return FALSE;
9859 : }
9860 :
9861 8628 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9862 : auto otherGeodCRS =
9863 8628 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9864 8628 : if (!geodCRS || !otherGeodCRS)
9865 : {
9866 0 : proj_destroy(geodCRS);
9867 0 : proj_destroy(otherGeodCRS);
9868 0 : return FALSE;
9869 : }
9870 :
9871 8628 : int ret = proj_is_equivalent_to(
9872 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9873 :
9874 8628 : proj_destroy(geodCRS);
9875 8628 : proj_destroy(otherGeodCRS);
9876 8628 : return ret;
9877 : }
9878 :
9879 : /************************************************************************/
9880 : /* OSRIsSameGeogCS() */
9881 : /************************************************************************/
9882 :
9883 : /**
9884 : * \brief Do the GeogCS'es match?
9885 : *
9886 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9887 : */
9888 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9889 :
9890 : {
9891 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9892 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9893 :
9894 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9895 : }
9896 :
9897 : /************************************************************************/
9898 : /* IsSameVertCS() */
9899 : /************************************************************************/
9900 :
9901 : /**
9902 : * \brief Do the VertCS'es match?
9903 : *
9904 : * This method is the same as the C function OSRIsSameVertCS().
9905 : *
9906 : * @param poOther the SRS being compared against.
9907 : *
9908 : * @return TRUE if they are the same or FALSE otherwise.
9909 : */
9910 :
9911 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9912 :
9913 : {
9914 0 : TAKE_OPTIONAL_LOCK();
9915 :
9916 : /* -------------------------------------------------------------------- */
9917 : /* Does the datum name match? */
9918 : /* -------------------------------------------------------------------- */
9919 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9920 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9921 :
9922 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9923 0 : !EQUAL(pszThisValue, pszOtherValue))
9924 0 : return FALSE;
9925 :
9926 : /* -------------------------------------------------------------------- */
9927 : /* Do the units match? */
9928 : /* -------------------------------------------------------------------- */
9929 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9930 0 : if (pszThisValue == nullptr)
9931 0 : pszThisValue = "1.0";
9932 :
9933 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9934 0 : if (pszOtherValue == nullptr)
9935 0 : pszOtherValue = "1.0";
9936 :
9937 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9938 0 : return FALSE;
9939 :
9940 0 : return TRUE;
9941 : }
9942 :
9943 : /************************************************************************/
9944 : /* OSRIsSameVertCS() */
9945 : /************************************************************************/
9946 :
9947 : /**
9948 : * \brief Do the VertCS'es match?
9949 : *
9950 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9951 : */
9952 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9953 :
9954 : {
9955 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9956 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9957 :
9958 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9959 : }
9960 :
9961 : /************************************************************************/
9962 : /* IsSame() */
9963 : /************************************************************************/
9964 :
9965 : /**
9966 : * \brief Do these two spatial references describe the same system ?
9967 : *
9968 : * @param poOtherSRS the SRS being compared to.
9969 : *
9970 : * @return TRUE if equivalent or FALSE otherwise.
9971 : */
9972 :
9973 23248 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9974 :
9975 : {
9976 23248 : return IsSame(poOtherSRS, nullptr);
9977 : }
9978 :
9979 : /**
9980 : * \brief Do these two spatial references describe the same system ?
9981 : *
9982 : * This also takes into account the data axis to CRS axis mapping by default
9983 : *
9984 : * @param poOtherSRS the SRS being compared to.
9985 : * @param papszOptions options. NULL or NULL terminated list of options.
9986 : * Currently supported options are:
9987 : * <ul>
9988 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9989 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9990 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9991 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9992 : * </ul>
9993 : *
9994 : * @return TRUE if equivalent or FALSE otherwise.
9995 : */
9996 :
9997 36088 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9998 : const char *const *papszOptions) const
9999 :
10000 : {
10001 72176 : TAKE_OPTIONAL_LOCK();
10002 :
10003 36088 : d->refreshProjObj();
10004 36088 : poOtherSRS->d->refreshProjObj();
10005 36088 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10006 51 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10007 36037 : if (!CPLTestBool(CSLFetchNameValueDef(
10008 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10009 : {
10010 35387 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10011 3315 : return false;
10012 : }
10013 :
10014 32722 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10015 : "IGNORE_COORDINATE_EPOCH", "NO")))
10016 : {
10017 32330 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10018 27 : return false;
10019 : }
10020 :
10021 32695 : bool reboundSelf = false;
10022 32695 : bool reboundOther = false;
10023 32747 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10024 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10025 : {
10026 14 : d->demoteFromBoundCRS();
10027 14 : reboundSelf = true;
10028 : }
10029 65324 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10030 32643 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10031 : {
10032 28 : poOtherSRS->d->demoteFromBoundCRS();
10033 28 : reboundOther = true;
10034 : }
10035 :
10036 32695 : PJ_COMPARISON_CRITERION criterion =
10037 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10038 32695 : const char *pszCriterion = CSLFetchNameValueDef(
10039 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10040 32695 : if (EQUAL(pszCriterion, "STRICT"))
10041 0 : criterion = PJ_COMP_STRICT;
10042 32695 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10043 9662 : criterion = PJ_COMP_EQUIVALENT;
10044 23033 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10045 : {
10046 0 : CPLError(CE_Warning, CPLE_NotSupported,
10047 : "Unsupported value for CRITERION: %s", pszCriterion);
10048 : }
10049 : int ret =
10050 32695 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10051 32695 : if (reboundSelf)
10052 14 : d->undoDemoteFromBoundCRS();
10053 32695 : if (reboundOther)
10054 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10055 :
10056 32695 : return ret;
10057 : }
10058 :
10059 : /************************************************************************/
10060 : /* OSRIsSame() */
10061 : /************************************************************************/
10062 :
10063 : /**
10064 : * \brief Do these two spatial references describe the same system ?
10065 : *
10066 : * This function is the same as OGRSpatialReference::IsSame().
10067 : */
10068 35 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10069 :
10070 : {
10071 35 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10072 35 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10073 :
10074 35 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10075 : }
10076 :
10077 : /************************************************************************/
10078 : /* OSRIsSameEx() */
10079 : /************************************************************************/
10080 :
10081 : /**
10082 : * \brief Do these two spatial references describe the same system ?
10083 : *
10084 : * This function is the same as OGRSpatialReference::IsSame().
10085 : */
10086 644 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10087 : const char *const *papszOptions)
10088 : {
10089 644 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10090 644 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10091 :
10092 644 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10093 : }
10094 :
10095 : /************************************************************************/
10096 : /* convertToOtherProjection() */
10097 : /************************************************************************/
10098 :
10099 : /**
10100 : * \brief Convert to another equivalent projection
10101 : *
10102 : * Currently implemented:
10103 : * <ul>
10104 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10105 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10106 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10107 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10108 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10109 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10110 : * </ul>
10111 : *
10112 : * @param pszTargetProjection target projection.
10113 : * @param papszOptions lists of options. None supported currently.
10114 : * @return a new SRS, or NULL in case of error.
10115 : *
10116 : */
10117 91 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10118 : const char *pszTargetProjection,
10119 : CPL_UNUSED const char *const *papszOptions) const
10120 : {
10121 182 : TAKE_OPTIONAL_LOCK();
10122 :
10123 91 : if (pszTargetProjection == nullptr)
10124 1 : return nullptr;
10125 : int new_code;
10126 90 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10127 : {
10128 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10129 : }
10130 84 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10131 : {
10132 7 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10133 : }
10134 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10135 : {
10136 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10137 : }
10138 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10139 : {
10140 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10141 : }
10142 : else
10143 : {
10144 1 : return nullptr;
10145 : }
10146 :
10147 89 : d->refreshProjObj();
10148 89 : d->demoteFromBoundCRS();
10149 89 : OGRSpatialReference *poNewSRS = nullptr;
10150 89 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10151 : {
10152 : auto conv =
10153 88 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10154 88 : auto new_conv = proj_convert_conversion_to_other_method(
10155 : d->getPROJContext(), conv, new_code, nullptr);
10156 88 : proj_destroy(conv);
10157 88 : if (new_conv)
10158 : {
10159 : auto geodCRS =
10160 74 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10161 74 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10162 74 : d->m_pj_crs);
10163 74 : if (geodCRS && cs)
10164 : {
10165 74 : auto new_proj_crs = proj_create_projected_crs(
10166 74 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10167 : new_conv, cs);
10168 74 : proj_destroy(new_conv);
10169 74 : if (new_proj_crs)
10170 : {
10171 74 : poNewSRS = new OGRSpatialReference();
10172 :
10173 74 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10174 : {
10175 9 : auto boundCRS = proj_crs_create_bound_crs(
10176 : d->getPROJContext(), new_proj_crs,
10177 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10178 9 : if (boundCRS)
10179 : {
10180 9 : proj_destroy(new_proj_crs);
10181 9 : new_proj_crs = boundCRS;
10182 : }
10183 : }
10184 :
10185 74 : poNewSRS->d->setPjCRS(new_proj_crs);
10186 : }
10187 : }
10188 74 : proj_destroy(geodCRS);
10189 74 : proj_destroy(cs);
10190 : }
10191 : }
10192 89 : d->undoDemoteFromBoundCRS();
10193 89 : return poNewSRS;
10194 : }
10195 :
10196 : /************************************************************************/
10197 : /* OSRConvertToOtherProjection() */
10198 : /************************************************************************/
10199 :
10200 : /**
10201 : * \brief Convert to another equivalent projection
10202 : *
10203 : * Currently implemented:
10204 : * <ul>
10205 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10206 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10207 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10208 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10209 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10210 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10211 : * </ul>
10212 : *
10213 : * @param hSRS source SRS
10214 : * @param pszTargetProjection target projection.
10215 : * @param papszOptions lists of options. None supported currently.
10216 : * @return a new SRS, or NULL in case of error.
10217 : *
10218 : */
10219 : OGRSpatialReferenceH
10220 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10221 : const char *pszTargetProjection,
10222 : const char *const *papszOptions)
10223 : {
10224 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10225 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10226 28 : pszTargetProjection, papszOptions));
10227 : }
10228 :
10229 : /************************************************************************/
10230 : /* OSRFindMatches() */
10231 : /************************************************************************/
10232 :
10233 : /**
10234 : * \brief Try to identify a match between the passed SRS and a related SRS
10235 : * in a catalog.
10236 : *
10237 : * Matching may be partial, or may fail.
10238 : * Returned entries will be sorted by decreasing match confidence (first
10239 : * entry has the highest match confidence).
10240 : *
10241 : * The exact way matching is done may change in future versions. Starting with
10242 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10243 : *
10244 : * This function is the same as OGRSpatialReference::FindMatches().
10245 : *
10246 : * @param hSRS SRS to match
10247 : * @param papszOptions NULL terminated list of options or NULL
10248 : * @param pnEntries Output parameter. Number of values in the returned array.
10249 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10250 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10251 : * indicate the confidence in the match. 100 is the highest confidence level.
10252 : * The array must be freed with CPLFree().
10253 : *
10254 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10255 : * with OSRFreeSRSArray()
10256 : *
10257 : */
10258 10 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10259 : CSLConstList papszOptions, int *pnEntries,
10260 : int **ppanMatchConfidence)
10261 : {
10262 10 : if (pnEntries)
10263 10 : *pnEntries = 0;
10264 10 : if (ppanMatchConfidence)
10265 10 : *ppanMatchConfidence = nullptr;
10266 10 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10267 :
10268 10 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10269 10 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10270 : }
10271 :
10272 : /************************************************************************/
10273 : /* OSRFreeSRSArray() */
10274 : /************************************************************************/
10275 :
10276 : /**
10277 : * \brief Free return of OSRIdentifyMatches()
10278 : *
10279 : * @param pahSRS array of SRS (must be NULL terminated)
10280 : */
10281 197 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10282 : {
10283 197 : if (pahSRS != nullptr)
10284 : {
10285 1744 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10286 : {
10287 1565 : OSRRelease(pahSRS[i]);
10288 : }
10289 179 : CPLFree(pahSRS);
10290 : }
10291 197 : }
10292 :
10293 : /************************************************************************/
10294 : /* FindBestMatch() */
10295 : /************************************************************************/
10296 :
10297 : /**
10298 : * \brief Try to identify the best match between the passed SRS and a related
10299 : * SRS in a catalog.
10300 : *
10301 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10302 : * of filtering its output.
10303 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10304 : * will be considered. If there is a single match, it is returned.
10305 : * If there are several matches, only return the one under the
10306 : * pszPreferredAuthority, if there is a single one under that authority.
10307 : *
10308 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10309 : * 100). If set to 0, 90 is used.
10310 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10311 : * "EPSG" is used.
10312 : * @param papszOptions NULL terminated list of options or NULL. No option is
10313 : * defined at time of writing.
10314 : *
10315 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10316 : *
10317 : * @since GDAL 3.6
10318 : * @see OGRSpatialReference::FindMatches()
10319 : */
10320 : OGRSpatialReference *
10321 1467 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10322 : const char *pszPreferredAuthority,
10323 : CSLConstList papszOptions) const
10324 : {
10325 2934 : TAKE_OPTIONAL_LOCK();
10326 :
10327 1467 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10328 :
10329 1467 : if (nMinimumMatchConfidence == 0)
10330 0 : nMinimumMatchConfidence = 90;
10331 1467 : if (pszPreferredAuthority == nullptr)
10332 200 : pszPreferredAuthority = "EPSG";
10333 :
10334 : // Try to identify the CRS with the database
10335 1467 : int nEntries = 0;
10336 1467 : int *panConfidence = nullptr;
10337 : OGRSpatialReferenceH *pahSRS =
10338 1467 : FindMatches(nullptr, &nEntries, &panConfidence);
10339 1467 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10340 : {
10341 2606 : std::vector<double> adfTOWGS84(7);
10342 1303 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10343 : {
10344 1302 : adfTOWGS84.clear();
10345 : }
10346 :
10347 1303 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10348 :
10349 : auto poBaseGeogCRS =
10350 1303 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10351 1303 : if (poBaseGeogCRS)
10352 : {
10353 : // If the base geographic SRS of the SRS is EPSG:4326
10354 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10355 : // SRS code
10356 : // Same with EPSG:4258 (ETRS89), since it's the only known
10357 : // TOWGS84[] style transformation to WGS 84, and given the
10358 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10359 : // chance that a non-NULL TOWGS84[] will emerge.
10360 1303 : const char *pszAuthorityName = nullptr;
10361 1303 : const char *pszAuthorityCode = nullptr;
10362 1303 : const char *pszBaseAuthorityName = nullptr;
10363 1303 : const char *pszBaseAuthorityCode = nullptr;
10364 1303 : const char *pszBaseName = poBaseGeogCRS->GetName();
10365 2606 : if (adfTOWGS84 == std::vector<double>(7) &&
10366 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) !=
10367 1 : nullptr &&
10368 1 : EQUAL(pszAuthorityName, "EPSG") &&
10369 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) !=
10370 1 : nullptr &&
10371 : (pszBaseAuthorityName =
10372 1 : poBaseGeogCRS->GetAuthorityName(nullptr)) != nullptr &&
10373 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10374 : (pszBaseAuthorityCode =
10375 2608 : poBaseGeogCRS->GetAuthorityCode(nullptr)) != nullptr &&
10376 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10377 1 : EQUAL(pszBaseAuthorityCode, "4258") ||
10378 : // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10379 0 : (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10380 : {
10381 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10382 : }
10383 : }
10384 :
10385 1303 : CPLFree(pahSRS);
10386 1303 : CPLFree(panConfidence);
10387 :
10388 1303 : return poSRS;
10389 : }
10390 : else
10391 : {
10392 : // If there are several matches >= nMinimumMatchConfidence, take the
10393 : // only one that is under pszPreferredAuthority
10394 164 : int iBestEntry = -1;
10395 1679 : for (int i = 0; i < nEntries; i++)
10396 : {
10397 1515 : if (panConfidence[i] >= nMinimumMatchConfidence)
10398 : {
10399 : const char *pszAuthName =
10400 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10401 3 : ->GetAuthorityName(nullptr);
10402 3 : if (pszAuthName != nullptr &&
10403 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10404 : {
10405 3 : if (iBestEntry < 0)
10406 3 : iBestEntry = i;
10407 : else
10408 : {
10409 0 : iBestEntry = -1;
10410 0 : break;
10411 : }
10412 : }
10413 : }
10414 : }
10415 164 : if (iBestEntry >= 0)
10416 : {
10417 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10418 3 : OSRFreeSRSArray(pahSRS);
10419 3 : CPLFree(panConfidence);
10420 3 : return poRet;
10421 : }
10422 : }
10423 161 : OSRFreeSRSArray(pahSRS);
10424 161 : CPLFree(panConfidence);
10425 161 : return nullptr;
10426 : }
10427 :
10428 : /************************************************************************/
10429 : /* SetTOWGS84() */
10430 : /************************************************************************/
10431 :
10432 : /**
10433 : * \brief Set the Bursa-Wolf conversion to WGS84.
10434 : *
10435 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10436 : * if there is no existing DATUM node. It will replace
10437 : * an existing TOWGS84 node if there is one.
10438 : *
10439 : * The parameters have the same meaning as EPSG transformation 9606
10440 : * (Position Vector 7-param. transformation).
10441 : *
10442 : * This method is the same as the C function OSRSetTOWGS84().
10443 : *
10444 : * @param dfDX X child in meters.
10445 : * @param dfDY Y child in meters.
10446 : * @param dfDZ Z child in meters.
10447 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10448 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10449 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10450 : * @param dfPPM scaling factor (parts per million).
10451 : *
10452 : * @return OGRERR_NONE on success.
10453 : */
10454 :
10455 103 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10456 : double dfEX, double dfEY, double dfEZ,
10457 : double dfPPM)
10458 :
10459 : {
10460 206 : TAKE_OPTIONAL_LOCK();
10461 :
10462 103 : d->refreshProjObj();
10463 103 : if (d->m_pj_crs == nullptr)
10464 : {
10465 0 : return OGRERR_FAILURE;
10466 : }
10467 :
10468 : // Remove existing BoundCRS
10469 103 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10470 : {
10471 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10472 0 : if (!baseCRS)
10473 0 : return OGRERR_FAILURE;
10474 0 : d->setPjCRS(baseCRS);
10475 : }
10476 :
10477 : PJ_PARAM_DESCRIPTION params[7];
10478 :
10479 103 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10480 103 : params[0].auth_name = "EPSG";
10481 103 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10482 103 : params[0].value = dfDX;
10483 103 : params[0].unit_name = "metre";
10484 103 : params[0].unit_conv_factor = 1.0;
10485 103 : params[0].unit_type = PJ_UT_LINEAR;
10486 :
10487 103 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10488 103 : params[1].auth_name = "EPSG";
10489 103 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10490 103 : params[1].value = dfDY;
10491 103 : params[1].unit_name = "metre";
10492 103 : params[1].unit_conv_factor = 1.0;
10493 103 : params[1].unit_type = PJ_UT_LINEAR;
10494 :
10495 103 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10496 103 : params[2].auth_name = "EPSG";
10497 103 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10498 103 : params[2].value = dfDZ;
10499 103 : params[2].unit_name = "metre";
10500 103 : params[2].unit_conv_factor = 1.0;
10501 103 : params[2].unit_type = PJ_UT_LINEAR;
10502 :
10503 103 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10504 103 : params[3].auth_name = "EPSG";
10505 103 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10506 103 : params[3].value = dfEX;
10507 103 : params[3].unit_name = "arc-second";
10508 103 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10509 103 : params[3].unit_type = PJ_UT_ANGULAR;
10510 :
10511 103 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10512 103 : params[4].auth_name = "EPSG";
10513 103 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10514 103 : params[4].value = dfEY;
10515 103 : params[4].unit_name = "arc-second";
10516 103 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10517 103 : params[4].unit_type = PJ_UT_ANGULAR;
10518 :
10519 103 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10520 103 : params[5].auth_name = "EPSG";
10521 103 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10522 103 : params[5].value = dfEZ;
10523 103 : params[5].unit_name = "arc-second";
10524 103 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10525 103 : params[5].unit_type = PJ_UT_ANGULAR;
10526 :
10527 103 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10528 103 : params[6].auth_name = "EPSG";
10529 103 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10530 103 : params[6].value = dfPPM;
10531 103 : params[6].unit_name = "parts per million";
10532 103 : params[6].unit_conv_factor = 1e-6;
10533 103 : params[6].unit_type = PJ_UT_SCALE;
10534 :
10535 : auto sourceCRS =
10536 103 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10537 103 : if (!sourceCRS)
10538 : {
10539 0 : return OGRERR_FAILURE;
10540 : }
10541 :
10542 103 : const auto sourceType = proj_get_type(sourceCRS);
10543 :
10544 103 : auto targetCRS = proj_create_from_database(
10545 : d->getPROJContext(), "EPSG",
10546 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10547 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10548 : : "4978",
10549 : PJ_CATEGORY_CRS, false, nullptr);
10550 103 : if (!targetCRS)
10551 : {
10552 0 : proj_destroy(sourceCRS);
10553 0 : return OGRERR_FAILURE;
10554 : }
10555 :
10556 206 : CPLString osMethodCode;
10557 : osMethodCode.Printf("%d",
10558 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10559 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10560 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10561 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10562 103 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10563 :
10564 103 : auto transf = proj_create_transformation(
10565 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10566 : sourceCRS, targetCRS, nullptr,
10567 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10568 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10569 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10570 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10571 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10572 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10573 103 : proj_destroy(sourceCRS);
10574 103 : if (!transf)
10575 : {
10576 0 : proj_destroy(targetCRS);
10577 0 : return OGRERR_FAILURE;
10578 : }
10579 :
10580 103 : auto newBoundCRS = proj_crs_create_bound_crs(
10581 103 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10582 103 : proj_destroy(transf);
10583 103 : proj_destroy(targetCRS);
10584 103 : if (!newBoundCRS)
10585 : {
10586 0 : return OGRERR_FAILURE;
10587 : }
10588 :
10589 103 : d->setPjCRS(newBoundCRS);
10590 103 : return OGRERR_NONE;
10591 : }
10592 :
10593 : /************************************************************************/
10594 : /* OSRSetTOWGS84() */
10595 : /************************************************************************/
10596 :
10597 : /**
10598 : * \brief Set the Bursa-Wolf conversion to WGS84.
10599 : *
10600 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10601 : */
10602 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10603 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10604 : double dfPPM)
10605 :
10606 : {
10607 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10608 :
10609 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10610 4 : dfPPM);
10611 : }
10612 :
10613 : /************************************************************************/
10614 : /* GetTOWGS84() */
10615 : /************************************************************************/
10616 :
10617 : /**
10618 : * \brief Fetch TOWGS84 parameters, if available.
10619 : *
10620 : * The parameters have the same meaning as EPSG transformation 9606
10621 : * (Position Vector 7-param. transformation).
10622 : *
10623 : * @param padfCoeff array into which up to 7 coefficients are placed.
10624 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10625 : *
10626 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10627 : * TOWGS84 node available.
10628 : */
10629 :
10630 4856 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10631 :
10632 : {
10633 9712 : TAKE_OPTIONAL_LOCK();
10634 :
10635 4856 : d->refreshProjObj();
10636 4856 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10637 4808 : return OGRERR_FAILURE;
10638 :
10639 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10640 :
10641 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10642 48 : int success = proj_coordoperation_get_towgs84_values(
10643 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10644 48 : proj_destroy(transf);
10645 :
10646 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10647 : }
10648 :
10649 : /************************************************************************/
10650 : /* OSRGetTOWGS84() */
10651 : /************************************************************************/
10652 :
10653 : /**
10654 : * \brief Fetch TOWGS84 parameters, if available.
10655 : *
10656 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10657 : */
10658 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10659 : int nCoeffCount)
10660 :
10661 : {
10662 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10663 :
10664 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10665 : }
10666 :
10667 : /************************************************************************/
10668 : /* IsAngularParameter() */
10669 : /************************************************************************/
10670 :
10671 : /** Is the passed projection parameter an angular one?
10672 : *
10673 : * @return TRUE or FALSE
10674 : */
10675 :
10676 : /* static */
10677 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10678 :
10679 : {
10680 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10681 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10682 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10683 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10684 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10685 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10686 8 : return TRUE;
10687 :
10688 2 : return FALSE;
10689 : }
10690 :
10691 : /************************************************************************/
10692 : /* IsLongitudeParameter() */
10693 : /************************************************************************/
10694 :
10695 : /** Is the passed projection parameter an angular longitude
10696 : * (relative to a prime meridian)?
10697 : *
10698 : * @return TRUE or FALSE
10699 : */
10700 :
10701 : /* static */
10702 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10703 :
10704 : {
10705 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10706 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10707 0 : return TRUE;
10708 :
10709 0 : return FALSE;
10710 : }
10711 :
10712 : /************************************************************************/
10713 : /* IsLinearParameter() */
10714 : /************************************************************************/
10715 :
10716 : /** Is the passed projection parameter an linear one measured in meters or
10717 : * some similar linear measure.
10718 : *
10719 : * @return TRUE or FALSE
10720 : */
10721 :
10722 : /* static */
10723 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10724 :
10725 : {
10726 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10727 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10728 9 : return TRUE;
10729 :
10730 34 : return FALSE;
10731 : }
10732 :
10733 : /************************************************************************/
10734 : /* GetNormInfo() */
10735 : /************************************************************************/
10736 :
10737 : /**
10738 : * \brief Set the internal information for normalizing linear, and angular
10739 : * values.
10740 : */
10741 4097 : void OGRSpatialReference::GetNormInfo() const
10742 :
10743 : {
10744 4097 : TAKE_OPTIONAL_LOCK();
10745 :
10746 4097 : if (d->bNormInfoSet)
10747 2922 : return;
10748 :
10749 : /* -------------------------------------------------------------------- */
10750 : /* Initialize values. */
10751 : /* -------------------------------------------------------------------- */
10752 1175 : d->bNormInfoSet = TRUE;
10753 :
10754 1175 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10755 1175 : d->dfToMeter = GetLinearUnits(nullptr);
10756 1175 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10757 1175 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10758 1172 : d->dfToDegrees = 1.0;
10759 : }
10760 :
10761 : /************************************************************************/
10762 : /* GetExtension() */
10763 : /************************************************************************/
10764 :
10765 : /**
10766 : * \brief Fetch extension value.
10767 : *
10768 : * Fetch the value of the named EXTENSION item for the identified
10769 : * target node.
10770 : *
10771 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10772 : * @param pszName the name of the extension being fetched.
10773 : * @param pszDefault the value to return if the extension is not found.
10774 : *
10775 : * @return node value if successful or pszDefault on failure.
10776 : */
10777 :
10778 12649 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10779 : const char *pszName,
10780 : const char *pszDefault) const
10781 :
10782 : {
10783 25298 : TAKE_OPTIONAL_LOCK();
10784 :
10785 : /* -------------------------------------------------------------------- */
10786 : /* Find the target node. */
10787 : /* -------------------------------------------------------------------- */
10788 : const OGR_SRSNode *poNode =
10789 12649 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10790 :
10791 12649 : if (poNode == nullptr)
10792 2359 : return nullptr;
10793 :
10794 : /* -------------------------------------------------------------------- */
10795 : /* Fetch matching EXTENSION if there is one. */
10796 : /* -------------------------------------------------------------------- */
10797 76047 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10798 : {
10799 65781 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10800 :
10801 65807 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10802 26 : poChild->GetChildCount() >= 2)
10803 : {
10804 26 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10805 24 : return poChild->GetChild(1)->GetValue();
10806 : }
10807 : }
10808 :
10809 10266 : return pszDefault;
10810 : }
10811 :
10812 : /************************************************************************/
10813 : /* SetExtension() */
10814 : /************************************************************************/
10815 : /**
10816 : * \brief Set extension value.
10817 : *
10818 : * Set the value of the named EXTENSION item for the identified
10819 : * target node.
10820 : *
10821 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10822 : * @param pszName the name of the extension being fetched.
10823 : * @param pszValue the value to set
10824 : *
10825 : * @return OGRERR_NONE on success
10826 : */
10827 :
10828 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10829 : const char *pszName,
10830 : const char *pszValue)
10831 :
10832 : {
10833 40 : TAKE_OPTIONAL_LOCK();
10834 :
10835 : /* -------------------------------------------------------------------- */
10836 : /* Find the target node. */
10837 : /* -------------------------------------------------------------------- */
10838 20 : OGR_SRSNode *poNode = nullptr;
10839 :
10840 20 : if (pszTargetKey == nullptr)
10841 0 : poNode = GetRoot();
10842 : else
10843 20 : poNode = GetAttrNode(pszTargetKey);
10844 :
10845 20 : if (poNode == nullptr)
10846 0 : return OGRERR_FAILURE;
10847 :
10848 : /* -------------------------------------------------------------------- */
10849 : /* Fetch matching EXTENSION if there is one. */
10850 : /* -------------------------------------------------------------------- */
10851 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10852 : {
10853 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10854 :
10855 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10856 6 : poChild->GetChildCount() >= 2)
10857 : {
10858 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10859 : {
10860 6 : poChild->GetChild(1)->SetValue(pszValue);
10861 6 : return OGRERR_NONE;
10862 : }
10863 : }
10864 : }
10865 :
10866 : /* -------------------------------------------------------------------- */
10867 : /* Create a new EXTENSION node. */
10868 : /* -------------------------------------------------------------------- */
10869 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10870 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10871 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10872 :
10873 14 : poNode->AddChild(poAuthNode);
10874 :
10875 14 : return OGRERR_NONE;
10876 : }
10877 :
10878 : /************************************************************************/
10879 : /* OSRCleanup() */
10880 : /************************************************************************/
10881 :
10882 : static void CleanupSRSWGS84Mutex();
10883 :
10884 : /**
10885 : * \brief Cleanup cached SRS related memory.
10886 : *
10887 : * This function will attempt to cleanup any cache spatial reference
10888 : * related information, such as cached tables of coordinate systems.
10889 : *
10890 : * This function should not be called concurrently with any other GDAL/OGR
10891 : * function. It is meant at being called once before process termination
10892 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10893 : * thread-specific resources before thread termination.
10894 : */
10895 1134 : void OSRCleanup(void)
10896 :
10897 : {
10898 1134 : OGRCTDumpStatistics();
10899 1134 : CSVDeaccess(nullptr);
10900 1134 : CleanupSRSWGS84Mutex();
10901 1134 : OSRCTCleanCache();
10902 1134 : OSRCleanupTLSContext();
10903 1134 : }
10904 :
10905 : /************************************************************************/
10906 : /* GetAxesCount() */
10907 : /************************************************************************/
10908 :
10909 : /**
10910 : * \brief Return the number of axis of the coordinate system of the CRS.
10911 : *
10912 : * @since GDAL 3.0
10913 : */
10914 38508 : int OGRSpatialReference::GetAxesCount() const
10915 : {
10916 77016 : TAKE_OPTIONAL_LOCK();
10917 :
10918 38508 : int axisCount = 0;
10919 38508 : d->refreshProjObj();
10920 38508 : if (d->m_pj_crs == nullptr)
10921 : {
10922 0 : return 0;
10923 : }
10924 38508 : d->demoteFromBoundCRS();
10925 38508 : auto ctxt = d->getPROJContext();
10926 38508 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10927 : {
10928 38 : for (int i = 0;; i++)
10929 : {
10930 114 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10931 114 : if (!subCRS)
10932 38 : break;
10933 76 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10934 : {
10935 18 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10936 18 : if (baseCRS)
10937 : {
10938 18 : proj_destroy(subCRS);
10939 18 : subCRS = baseCRS;
10940 : }
10941 : }
10942 76 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10943 76 : if (cs)
10944 : {
10945 76 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10946 76 : proj_destroy(cs);
10947 : }
10948 76 : proj_destroy(subCRS);
10949 76 : }
10950 : }
10951 : else
10952 : {
10953 38470 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10954 38470 : if (cs)
10955 : {
10956 38470 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10957 38470 : proj_destroy(cs);
10958 : }
10959 : }
10960 38508 : d->undoDemoteFromBoundCRS();
10961 38508 : return axisCount;
10962 : }
10963 :
10964 : /************************************************************************/
10965 : /* OSRGetAxesCount() */
10966 : /************************************************************************/
10967 :
10968 : /**
10969 : * \brief Return the number of axis of the coordinate system of the CRS.
10970 : *
10971 : * This method is the equivalent of the C++ method
10972 : * OGRSpatialReference::GetAxesCount()
10973 : *
10974 : * @since GDAL 3.1
10975 : */
10976 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10977 :
10978 : {
10979 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10980 :
10981 6 : return ToPointer(hSRS)->GetAxesCount();
10982 : }
10983 :
10984 : /************************************************************************/
10985 : /* GetAxis() */
10986 : /************************************************************************/
10987 :
10988 : /**
10989 : * \brief Fetch the orientation of one axis.
10990 : *
10991 : * Fetches the request axis (iAxis - zero based) from the
10992 : * indicated portion of the coordinate system (pszTargetKey) which
10993 : * should be either "GEOGCS" or "PROJCS".
10994 : *
10995 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10996 : *
10997 : * This method is equivalent to the C function OSRGetAxis().
10998 : *
10999 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11000 : * "GEOGCS").
11001 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11002 : * @param peOrientation location into which to place the fetch orientation, may
11003 : * be NULL.
11004 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11005 : * factor. May be NULL. Only set if pszTargetKey == NULL
11006 : *
11007 : * @return the name of the axis or NULL on failure.
11008 : */
11009 :
11010 8448 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11011 : OGRAxisOrientation *peOrientation,
11012 : double *pdfConvUnit) const
11013 :
11014 : {
11015 16896 : TAKE_OPTIONAL_LOCK();
11016 :
11017 8448 : if (peOrientation != nullptr)
11018 8339 : *peOrientation = OAO_Other;
11019 8448 : if (pdfConvUnit != nullptr)
11020 101 : *pdfConvUnit = 0;
11021 :
11022 8448 : d->refreshProjObj();
11023 8448 : if (d->m_pj_crs == nullptr)
11024 : {
11025 3 : return nullptr;
11026 : }
11027 :
11028 8445 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11029 8445 : if (pszTargetKey == nullptr && iAxis <= 2)
11030 : {
11031 8445 : auto ctxt = d->getPROJContext();
11032 :
11033 8445 : int iAxisModified = iAxis;
11034 :
11035 8445 : d->demoteFromBoundCRS();
11036 :
11037 8445 : PJ *cs = nullptr;
11038 8445 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11039 : {
11040 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11041 134 : if (horizCRS)
11042 : {
11043 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11044 : {
11045 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11046 6 : if (baseCRS)
11047 : {
11048 6 : proj_destroy(horizCRS);
11049 6 : horizCRS = baseCRS;
11050 : }
11051 : }
11052 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11053 134 : proj_destroy(horizCRS);
11054 134 : if (cs)
11055 : {
11056 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11057 : {
11058 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11059 44 : proj_destroy(cs);
11060 44 : cs = nullptr;
11061 : }
11062 : }
11063 : }
11064 :
11065 134 : if (cs == nullptr)
11066 : {
11067 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11068 44 : if (vertCRS)
11069 : {
11070 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11071 : {
11072 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11073 30 : if (baseCRS)
11074 : {
11075 30 : proj_destroy(vertCRS);
11076 30 : vertCRS = baseCRS;
11077 : }
11078 : }
11079 :
11080 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11081 44 : proj_destroy(vertCRS);
11082 : }
11083 : }
11084 : }
11085 : else
11086 : {
11087 8311 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11088 : }
11089 :
11090 8445 : if (cs)
11091 : {
11092 8445 : const char *pszName = nullptr;
11093 8445 : const char *pszOrientation = nullptr;
11094 8445 : double dfConvFactor = 0.0;
11095 8445 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11096 : &pszOrientation, &dfConvFactor, nullptr,
11097 : nullptr, nullptr);
11098 :
11099 8445 : if (pdfConvUnit != nullptr)
11100 : {
11101 101 : *pdfConvUnit = dfConvFactor;
11102 : }
11103 :
11104 8445 : if (pszName && pszOrientation)
11105 : {
11106 8445 : d->m_osAxisName[iAxis] = pszName;
11107 8445 : if (peOrientation)
11108 : {
11109 8336 : if (EQUAL(pszOrientation, "NORTH"))
11110 5353 : *peOrientation = OAO_North;
11111 2983 : else if (EQUAL(pszOrientation, "EAST"))
11112 2907 : *peOrientation = OAO_East;
11113 76 : else if (EQUAL(pszOrientation, "SOUTH"))
11114 65 : *peOrientation = OAO_South;
11115 11 : else if (EQUAL(pszOrientation, "WEST"))
11116 0 : *peOrientation = OAO_West;
11117 11 : else if (EQUAL(pszOrientation, "UP"))
11118 1 : *peOrientation = OAO_Up;
11119 10 : else if (EQUAL(pszOrientation, "DOWN"))
11120 0 : *peOrientation = OAO_Down;
11121 : }
11122 8445 : proj_destroy(cs);
11123 8445 : d->undoDemoteFromBoundCRS();
11124 8445 : return d->m_osAxisName[iAxis].c_str();
11125 : }
11126 0 : proj_destroy(cs);
11127 : }
11128 0 : d->undoDemoteFromBoundCRS();
11129 : }
11130 :
11131 : /* -------------------------------------------------------------------- */
11132 : /* Find the target node. */
11133 : /* -------------------------------------------------------------------- */
11134 0 : const OGR_SRSNode *poNode = nullptr;
11135 :
11136 0 : if (pszTargetKey == nullptr)
11137 0 : poNode = GetRoot();
11138 : else
11139 0 : poNode = GetAttrNode(pszTargetKey);
11140 :
11141 0 : if (poNode == nullptr)
11142 0 : return nullptr;
11143 :
11144 : /* -------------------------------------------------------------------- */
11145 : /* Find desired child AXIS. */
11146 : /* -------------------------------------------------------------------- */
11147 0 : const OGR_SRSNode *poAxis = nullptr;
11148 0 : const int nChildCount = poNode->GetChildCount();
11149 :
11150 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11151 : {
11152 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11153 :
11154 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11155 0 : continue;
11156 :
11157 0 : if (iAxis == 0)
11158 : {
11159 0 : poAxis = poChild;
11160 0 : break;
11161 : }
11162 0 : iAxis--;
11163 : }
11164 :
11165 0 : if (poAxis == nullptr)
11166 0 : return nullptr;
11167 :
11168 0 : if (poAxis->GetChildCount() < 2)
11169 0 : return nullptr;
11170 :
11171 : /* -------------------------------------------------------------------- */
11172 : /* Extract name and orientation if possible. */
11173 : /* -------------------------------------------------------------------- */
11174 0 : if (peOrientation != nullptr)
11175 : {
11176 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11177 :
11178 0 : if (EQUAL(pszOrientation, "NORTH"))
11179 0 : *peOrientation = OAO_North;
11180 0 : else if (EQUAL(pszOrientation, "EAST"))
11181 0 : *peOrientation = OAO_East;
11182 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11183 0 : *peOrientation = OAO_South;
11184 0 : else if (EQUAL(pszOrientation, "WEST"))
11185 0 : *peOrientation = OAO_West;
11186 0 : else if (EQUAL(pszOrientation, "UP"))
11187 0 : *peOrientation = OAO_Up;
11188 0 : else if (EQUAL(pszOrientation, "DOWN"))
11189 0 : *peOrientation = OAO_Down;
11190 0 : else if (EQUAL(pszOrientation, "OTHER"))
11191 0 : *peOrientation = OAO_Other;
11192 : else
11193 : {
11194 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11195 : pszOrientation);
11196 : }
11197 : }
11198 :
11199 0 : return poAxis->GetChild(0)->GetValue();
11200 : }
11201 :
11202 : /************************************************************************/
11203 : /* OSRGetAxis() */
11204 : /************************************************************************/
11205 :
11206 : /**
11207 : * \brief Fetch the orientation of one axis.
11208 : *
11209 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11210 : */
11211 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11212 : int iAxis, OGRAxisOrientation *peOrientation)
11213 :
11214 : {
11215 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11216 :
11217 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11218 : }
11219 :
11220 : /************************************************************************/
11221 : /* OSRAxisEnumToName() */
11222 : /************************************************************************/
11223 :
11224 : /**
11225 : * \brief Return the string representation for the OGRAxisOrientation
11226 : * enumeration.
11227 : *
11228 : * For example "NORTH" for OAO_North.
11229 : *
11230 : * @return an internal string
11231 : */
11232 396 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11233 :
11234 : {
11235 396 : if (eOrientation == OAO_North)
11236 198 : return "NORTH";
11237 198 : if (eOrientation == OAO_East)
11238 198 : return "EAST";
11239 0 : if (eOrientation == OAO_South)
11240 0 : return "SOUTH";
11241 0 : if (eOrientation == OAO_West)
11242 0 : return "WEST";
11243 0 : if (eOrientation == OAO_Up)
11244 0 : return "UP";
11245 0 : if (eOrientation == OAO_Down)
11246 0 : return "DOWN";
11247 0 : if (eOrientation == OAO_Other)
11248 0 : return "OTHER";
11249 :
11250 0 : return "UNKNOWN";
11251 : }
11252 :
11253 : /************************************************************************/
11254 : /* SetAxes() */
11255 : /************************************************************************/
11256 :
11257 : /**
11258 : * \brief Set the axes for a coordinate system.
11259 : *
11260 : * Set the names, and orientations of the axes for either a projected
11261 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11262 : *
11263 : * This method is equivalent to the C function OSRSetAxes().
11264 : *
11265 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11266 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11267 : * @param eXAxisOrientation normally OAO_East.
11268 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11269 : * @param eYAxisOrientation normally OAO_North.
11270 : *
11271 : * @return OGRERR_NONE on success or an error code.
11272 : */
11273 :
11274 198 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11275 : const char *pszXAxisName,
11276 : OGRAxisOrientation eXAxisOrientation,
11277 : const char *pszYAxisName,
11278 : OGRAxisOrientation eYAxisOrientation)
11279 :
11280 : {
11281 396 : TAKE_OPTIONAL_LOCK();
11282 :
11283 : /* -------------------------------------------------------------------- */
11284 : /* Find the target node. */
11285 : /* -------------------------------------------------------------------- */
11286 198 : OGR_SRSNode *poNode = nullptr;
11287 :
11288 198 : if (pszTargetKey == nullptr)
11289 198 : poNode = GetRoot();
11290 : else
11291 0 : poNode = GetAttrNode(pszTargetKey);
11292 :
11293 198 : if (poNode == nullptr)
11294 0 : return OGRERR_FAILURE;
11295 :
11296 : /* -------------------------------------------------------------------- */
11297 : /* Strip any existing AXIS children. */
11298 : /* -------------------------------------------------------------------- */
11299 594 : while (poNode->FindChild("AXIS") >= 0)
11300 396 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11301 :
11302 : /* -------------------------------------------------------------------- */
11303 : /* Insert desired axes */
11304 : /* -------------------------------------------------------------------- */
11305 198 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11306 :
11307 198 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11308 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11309 :
11310 198 : poNode->AddChild(poAxis);
11311 :
11312 198 : poAxis = new OGR_SRSNode("AXIS");
11313 :
11314 198 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11315 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11316 :
11317 198 : poNode->AddChild(poAxis);
11318 :
11319 198 : return OGRERR_NONE;
11320 : }
11321 :
11322 : /************************************************************************/
11323 : /* OSRSetAxes() */
11324 : /************************************************************************/
11325 : /**
11326 : * \brief Set the axes for a coordinate system.
11327 : *
11328 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11329 : */
11330 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11331 : const char *pszXAxisName,
11332 : OGRAxisOrientation eXAxisOrientation,
11333 : const char *pszYAxisName,
11334 : OGRAxisOrientation eYAxisOrientation)
11335 : {
11336 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11337 :
11338 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11339 : eXAxisOrientation, pszYAxisName,
11340 0 : eYAxisOrientation);
11341 : }
11342 :
11343 : /************************************************************************/
11344 : /* OSRExportToMICoordSys() */
11345 : /************************************************************************/
11346 : /**
11347 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11348 : *
11349 : * This method is the equivalent of the C++ method
11350 : * OGRSpatialReference::exportToMICoordSys
11351 : */
11352 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11353 :
11354 : {
11355 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11356 :
11357 5 : *ppszReturn = nullptr;
11358 :
11359 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11360 : }
11361 :
11362 : /************************************************************************/
11363 : /* exportToMICoordSys() */
11364 : /************************************************************************/
11365 :
11366 : /**
11367 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11368 : *
11369 : * Note that the returned WKT string should be freed with
11370 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11371 : *
11372 : * This method is the same as the C function OSRExportToMICoordSys().
11373 : *
11374 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11375 : * definition will be assigned.
11376 : *
11377 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11378 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11379 : */
11380 :
11381 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11382 :
11383 : {
11384 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11385 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11386 7 : return OGRERR_NONE;
11387 :
11388 0 : return OGRERR_FAILURE;
11389 : }
11390 :
11391 : /************************************************************************/
11392 : /* OSRImportFromMICoordSys() */
11393 : /************************************************************************/
11394 : /**
11395 : * \brief Import Mapinfo style CoordSys definition.
11396 : *
11397 : * This method is the equivalent of the C++ method
11398 : * OGRSpatialReference::importFromMICoordSys
11399 : */
11400 :
11401 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11402 : const char *pszCoordSys)
11403 :
11404 : {
11405 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11406 :
11407 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11408 : }
11409 :
11410 : /************************************************************************/
11411 : /* importFromMICoordSys() */
11412 : /************************************************************************/
11413 :
11414 : /**
11415 : * \brief Import Mapinfo style CoordSys definition.
11416 : *
11417 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11418 : * definition string.
11419 : *
11420 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11421 : *
11422 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11423 : *
11424 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11425 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11426 : */
11427 :
11428 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11429 :
11430 : {
11431 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11432 :
11433 17 : if (poResult == nullptr)
11434 0 : return OGRERR_FAILURE;
11435 :
11436 17 : *this = *poResult;
11437 17 : delete poResult;
11438 :
11439 17 : return OGRERR_NONE;
11440 : }
11441 :
11442 : /************************************************************************/
11443 : /* OSRCalcInvFlattening() */
11444 : /************************************************************************/
11445 :
11446 : /**
11447 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11448 : *
11449 : * @param dfSemiMajor Semi-major axis length.
11450 : * @param dfSemiMinor Semi-minor axis length.
11451 : *
11452 : * @return inverse flattening, or 0 if both axis are equal.
11453 : */
11454 :
11455 8697 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11456 : {
11457 8697 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11458 27 : return 0;
11459 8670 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11460 : {
11461 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11462 : "OSRCalcInvFlattening(): Wrong input values");
11463 0 : return 0;
11464 : }
11465 :
11466 8670 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11467 : }
11468 :
11469 : /************************************************************************/
11470 : /* OSRCalcInvFlattening() */
11471 : /************************************************************************/
11472 :
11473 : /**
11474 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11475 : *
11476 : * @param dfSemiMajor Semi-major axis length.
11477 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11478 : *
11479 : * @return semi-minor axis
11480 : */
11481 :
11482 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11483 : double dfInvFlattening)
11484 : {
11485 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11486 103 : return dfSemiMajor;
11487 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11488 : {
11489 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11490 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11491 0 : return dfSemiMajor;
11492 : }
11493 :
11494 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11495 : }
11496 :
11497 : /************************************************************************/
11498 : /* GetWGS84SRS() */
11499 : /************************************************************************/
11500 :
11501 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11502 : static CPLMutex *hMutex = nullptr;
11503 :
11504 : /**
11505 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11506 : *
11507 : * Note: the instance will have
11508 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11509 : *
11510 : * The reference counter of the returned object is not increased by this
11511 : * operation.
11512 : *
11513 : * @return instance.
11514 : */
11515 :
11516 1003 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11517 : {
11518 1003 : CPLMutexHolderD(&hMutex);
11519 1003 : if (poSRSWGS84 == nullptr)
11520 : {
11521 5 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11522 5 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11523 : }
11524 2006 : return poSRSWGS84;
11525 : }
11526 :
11527 : /************************************************************************/
11528 : /* CleanupSRSWGS84Mutex() */
11529 : /************************************************************************/
11530 :
11531 1134 : static void CleanupSRSWGS84Mutex()
11532 : {
11533 1134 : if (hMutex != nullptr)
11534 : {
11535 3 : poSRSWGS84->Release();
11536 3 : poSRSWGS84 = nullptr;
11537 3 : CPLDestroyMutex(hMutex);
11538 3 : hMutex = nullptr;
11539 : }
11540 1134 : }
11541 :
11542 : /************************************************************************/
11543 : /* OSRImportFromProj4() */
11544 : /************************************************************************/
11545 : /**
11546 : * \brief Import PROJ coordinate string.
11547 : *
11548 : * This function is the same as OGRSpatialReference::importFromProj4().
11549 : */
11550 220 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11551 :
11552 : {
11553 220 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11554 :
11555 220 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11556 : }
11557 :
11558 : /************************************************************************/
11559 : /* importFromProj4() */
11560 : /************************************************************************/
11561 :
11562 : /**
11563 : * \brief Import PROJ coordinate string.
11564 : *
11565 : * The OGRSpatialReference is initialized from the passed PROJs style
11566 : * coordinate system string.
11567 : *
11568 : * Example:
11569 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11570 : *
11571 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11572 : * a legacy syntax that should be avoided in the future. In particular they will
11573 : * result in CRS objects whose axis order might not correspond to the official
11574 : * EPSG axis order.
11575 : *
11576 : * This method is the equivalent of the C function OSRImportFromProj4().
11577 : *
11578 : * @param pszProj4 the PROJ style string.
11579 : *
11580 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11581 : */
11582 :
11583 766 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11584 :
11585 : {
11586 1532 : TAKE_OPTIONAL_LOCK();
11587 :
11588 766 : if (strlen(pszProj4) >= 10000)
11589 : {
11590 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11591 1 : return OGRERR_CORRUPT_DATA;
11592 : }
11593 :
11594 : /* -------------------------------------------------------------------- */
11595 : /* Clear any existing definition. */
11596 : /* -------------------------------------------------------------------- */
11597 765 : Clear();
11598 :
11599 765 : CPLString osProj4(pszProj4);
11600 765 : if (osProj4.find("type=crs") == std::string::npos)
11601 : {
11602 756 : osProj4 += " +type=crs";
11603 : }
11604 :
11605 767 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11606 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11607 : {
11608 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11609 : "+init=epsg:XXXX syntax is deprecated. It might return "
11610 : "a CRS with a non-EPSG compliant axis order.");
11611 : }
11612 765 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11613 765 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11614 765 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11615 765 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11616 : }
11617 :
11618 : /************************************************************************/
11619 : /* OSRExportToProj4() */
11620 : /************************************************************************/
11621 : /**
11622 : * \brief Export coordinate system in PROJ.4 legacy format.
11623 : *
11624 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11625 : * PROJ >= 6 is significantly different from earlier versions. In particular
11626 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11627 : * will be missing most of the time. PROJ strings to encode CRS should be
11628 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11629 : * is the recommended way.
11630 : *
11631 : * This function is the same as OGRSpatialReference::exportToProj4().
11632 : */
11633 455 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11634 : char **ppszReturn)
11635 :
11636 : {
11637 455 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11638 :
11639 455 : *ppszReturn = nullptr;
11640 :
11641 455 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11642 : }
11643 :
11644 : /************************************************************************/
11645 : /* exportToProj4() */
11646 : /************************************************************************/
11647 :
11648 : /**
11649 : * \brief Export coordinate system in PROJ.4 legacy format.
11650 : *
11651 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11652 : * PROJ >= 6 is significantly different from earlier versions. In particular
11653 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11654 : * will be missing most of the time. PROJ strings to encode CRS should be
11655 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11656 : * representation is the recommended way.
11657 : *
11658 : * Converts the loaded coordinate reference system into PROJ format
11659 : * to the extent possible. The string returned in ppszProj4 should be
11660 : * deallocated by the caller with CPLFree() when no longer needed.
11661 : *
11662 : * LOCAL_CS coordinate systems are not translatable. An empty string
11663 : * will be returned along with OGRERR_NONE.
11664 : *
11665 : * Special processing for Transverse Mercator:
11666 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11667 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11668 : * for the tmerc and utm projection methods, rather than the more accurate
11669 : * method.
11670 : *
11671 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11672 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11673 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11674 : * added. This automatic addition may be disabled by setting the
11675 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11676 : *
11677 : * This method is the equivalent of the C function OSRExportToProj4().
11678 : *
11679 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11680 : * will be assigned.
11681 : *
11682 : * @return OGRERR_NONE on success or an error code on failure.
11683 : */
11684 :
11685 1534 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11686 :
11687 : {
11688 : // In the past calling this method was thread-safe, even if we never
11689 : // guaranteed it. Now proj_as_proj_string() will cache the result
11690 : // internally, so this is no longer thread-safe.
11691 3068 : std::lock_guard oLock(d->m_mutex);
11692 :
11693 1534 : d->refreshProjObj();
11694 1534 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11695 : {
11696 4 : *ppszProj4 = CPLStrdup("");
11697 4 : return OGRERR_FAILURE;
11698 : }
11699 :
11700 : // OSR_USE_ETMERC is here just for legacy
11701 1530 : bool bForceApproxTMerc = false;
11702 1530 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11703 1530 : if (pszUseETMERC && pszUseETMERC[0])
11704 : {
11705 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11706 : "OSR_USE_ETMERC is a legacy configuration option, which "
11707 : "now has only effect when set to NO (YES is the default). "
11708 : "Use OSR_USE_APPROX_TMERC=YES instead");
11709 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11710 : }
11711 : else
11712 : {
11713 : const char *pszUseApproxTMERC =
11714 1530 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11715 1530 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11716 : {
11717 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11718 : }
11719 : }
11720 1530 : const char *options[] = {
11721 1530 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11722 :
11723 1530 : const char *projString = proj_as_proj_string(
11724 1530 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11725 :
11726 1530 : PJ *boundCRS = nullptr;
11727 3056 : if (projString &&
11728 1526 : (strstr(projString, "+datum=") == nullptr ||
11729 3066 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11730 582 : CPLTestBool(
11731 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11732 : {
11733 582 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11734 582 : d->getPROJContext(), d->m_pj_crs, true,
11735 582 : strstr(projString, "+datum=") == nullptr);
11736 582 : if (boundCRS)
11737 : {
11738 227 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11739 : PJ_PROJ_4, options);
11740 : }
11741 : }
11742 :
11743 1530 : if (projString == nullptr)
11744 : {
11745 4 : *ppszProj4 = CPLStrdup("");
11746 4 : proj_destroy(boundCRS);
11747 4 : return OGRERR_FAILURE;
11748 : }
11749 1526 : *ppszProj4 = CPLStrdup(projString);
11750 1526 : proj_destroy(boundCRS);
11751 1526 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11752 1526 : if (pszTypeCrs)
11753 1526 : *pszTypeCrs = '\0';
11754 1526 : return OGRERR_NONE;
11755 : }
11756 :
11757 : /************************************************************************/
11758 : /* morphToESRI() */
11759 : /************************************************************************/
11760 : /**
11761 : * \brief Convert in place to ESRI WKT format.
11762 : *
11763 : * The value nodes of this coordinate system are modified in various manners
11764 : * more closely map onto the ESRI concept of WKT format. This includes
11765 : * renaming a variety of projections and arguments, and stripping out
11766 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11767 : *
11768 : * \note Since GDAL 3.0, this function has only user-visible effects at
11769 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11770 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11771 : *
11772 : * This does the same as the C function OSRMorphToESRI().
11773 : *
11774 : * @return OGRERR_NONE unless something goes badly wrong.
11775 : * @deprecated
11776 : */
11777 :
11778 235 : OGRErr OGRSpatialReference::morphToESRI()
11779 :
11780 : {
11781 235 : TAKE_OPTIONAL_LOCK();
11782 :
11783 235 : d->refreshProjObj();
11784 235 : d->setMorphToESRI(true);
11785 :
11786 470 : return OGRERR_NONE;
11787 : }
11788 :
11789 : /************************************************************************/
11790 : /* OSRMorphToESRI() */
11791 : /************************************************************************/
11792 :
11793 : /**
11794 : * \brief Convert in place to ESRI WKT format.
11795 : *
11796 : * This function is the same as the C++ method
11797 : * OGRSpatialReference::morphToESRI().
11798 : */
11799 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11800 :
11801 : {
11802 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11803 :
11804 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11805 : }
11806 :
11807 : /************************************************************************/
11808 : /* morphFromESRI() */
11809 : /************************************************************************/
11810 :
11811 : /**
11812 : * \brief Convert in place from ESRI WKT format.
11813 : *
11814 : * The value notes of this coordinate system are modified in various manners
11815 : * to adhere more closely to the WKT standard. This mostly involves
11816 : * translating a variety of ESRI names for projections, arguments and
11817 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11818 : * translation of EPSG to WKT for the CT specification.
11819 : *
11820 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11821 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11822 : * effect is to undo the effect of a potential prior call to morphToESRI().
11823 : *
11824 : * This does the same as the C function OSRMorphFromESRI().
11825 : *
11826 : * @return OGRERR_NONE unless something goes badly wrong.
11827 : * @deprecated
11828 : */
11829 :
11830 21 : OGRErr OGRSpatialReference::morphFromESRI()
11831 :
11832 : {
11833 21 : TAKE_OPTIONAL_LOCK();
11834 :
11835 21 : d->refreshProjObj();
11836 21 : d->setMorphToESRI(false);
11837 :
11838 42 : return OGRERR_NONE;
11839 : }
11840 :
11841 : /************************************************************************/
11842 : /* OSRMorphFromESRI() */
11843 : /************************************************************************/
11844 :
11845 : /**
11846 : * \brief Convert in place from ESRI WKT format.
11847 : *
11848 : * This function is the same as the C++ method
11849 : * OGRSpatialReference::morphFromESRI().
11850 : */
11851 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11852 :
11853 : {
11854 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11855 :
11856 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11857 : }
11858 :
11859 : /************************************************************************/
11860 : /* FindMatches() */
11861 : /************************************************************************/
11862 :
11863 : /**
11864 : * \brief Try to identify a match between the passed SRS and a related SRS
11865 : * in a catalog.
11866 : *
11867 : * Matching may be partial, or may fail.
11868 : * Returned entries will be sorted by decreasing match confidence (first
11869 : * entry has the highest match confidence).
11870 : *
11871 : * The exact way matching is done may change in future versions. Starting with
11872 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11873 : *
11874 : * This method is the same as OSRFindMatches().
11875 : *
11876 : * @param papszOptions NULL terminated list of options or NULL
11877 : * @param pnEntries Output parameter. Number of values in the returned array.
11878 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11879 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11880 : * indicate the confidence in the match. 100 is the highest confidence level.
11881 : * The array must be freed with CPLFree().
11882 : *
11883 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11884 : * with OSRFreeSRSArray()
11885 : *
11886 : *
11887 : * @see OGRSpatialReference::FindBestMatch()
11888 : */
11889 : OGRSpatialReferenceH *
11890 1482 : OGRSpatialReference::FindMatches(CSLConstList papszOptions, int *pnEntries,
11891 : int **ppanMatchConfidence) const
11892 : {
11893 2964 : TAKE_OPTIONAL_LOCK();
11894 :
11895 1482 : CPL_IGNORE_RET_VAL(papszOptions);
11896 :
11897 1482 : if (pnEntries)
11898 1482 : *pnEntries = 0;
11899 1482 : if (ppanMatchConfidence)
11900 1482 : *ppanMatchConfidence = nullptr;
11901 :
11902 1482 : d->refreshProjObj();
11903 1482 : if (!d->m_pj_crs)
11904 0 : return nullptr;
11905 :
11906 1482 : int *panConfidence = nullptr;
11907 1482 : auto ctxt = d->getPROJContext();
11908 : auto list =
11909 1482 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11910 1482 : if (!list)
11911 0 : return nullptr;
11912 :
11913 1482 : const int nMatches = proj_list_get_count(list);
11914 :
11915 1482 : if (pnEntries)
11916 1482 : *pnEntries = static_cast<int>(nMatches);
11917 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11918 1482 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11919 1482 : if (ppanMatchConfidence)
11920 : {
11921 1482 : *ppanMatchConfidence =
11922 1482 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11923 : }
11924 :
11925 1482 : bool bSortAgain = false;
11926 :
11927 4350 : for (int i = 0; i < nMatches; i++)
11928 : {
11929 2868 : PJ *obj = proj_list_get(ctxt, list, i);
11930 2868 : CPLAssert(obj);
11931 2868 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11932 2868 : poSRS->d->setPjCRS(obj);
11933 2868 : pahRet[i] = ToHandle(poSRS);
11934 :
11935 : // Identify matches that only differ by axis order
11936 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11937 2886 : poSRS->GetAxesCount() == 2 &&
11938 2877 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11939 : {
11940 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11941 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11942 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11943 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11944 9 : GetAxis(nullptr, 0, &eThisAxis0);
11945 9 : GetAxis(nullptr, 1, &eThisAxis1);
11946 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11947 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11948 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11949 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11950 : {
11951 : auto pj_crs_normalized =
11952 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11953 9 : if (pj_crs_normalized)
11954 : {
11955 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11956 9 : PJ_COMP_EQUIVALENT))
11957 : {
11958 3 : bSortAgain = true;
11959 3 : panConfidence[i] = 90;
11960 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11961 : }
11962 9 : proj_destroy(pj_crs_normalized);
11963 : }
11964 : }
11965 : }
11966 :
11967 2868 : if (ppanMatchConfidence)
11968 2868 : (*ppanMatchConfidence)[i] = panConfidence[i];
11969 : }
11970 :
11971 1482 : if (bSortAgain)
11972 : {
11973 3 : std::vector<int> anIndices;
11974 12 : for (int i = 0; i < nMatches; ++i)
11975 9 : anIndices.push_back(i);
11976 :
11977 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11978 9 : [&panConfidence](int i, int j)
11979 9 : { return panConfidence[i] > panConfidence[j]; });
11980 :
11981 : OGRSpatialReferenceH *pahRetSorted =
11982 : static_cast<OGRSpatialReferenceH *>(
11983 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11984 12 : for (int i = 0; i < nMatches; ++i)
11985 : {
11986 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11987 9 : if (ppanMatchConfidence)
11988 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11989 : }
11990 3 : CPLFree(pahRet);
11991 3 : pahRet = pahRetSorted;
11992 : }
11993 :
11994 1482 : pahRet[nMatches] = nullptr;
11995 1482 : proj_list_destroy(list);
11996 1482 : proj_int_list_destroy(panConfidence);
11997 :
11998 1482 : return pahRet;
11999 : }
12000 :
12001 : /************************************************************************/
12002 : /* importFromEPSGA() */
12003 : /************************************************************************/
12004 :
12005 : /**
12006 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12007 : * code.
12008 : *
12009 : * This method will initialize the spatial reference based on the
12010 : * passed in EPSG CRS code found in the PROJ database.
12011 : *
12012 : * Since GDAL 3.0, this method is identical to importFromEPSG().
12013 : *
12014 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12015 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12016 : * such method available for the CRS. This behavior might not always be
12017 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12018 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12019 : * The AddGuessedTOWGS84() method can also be used for that purpose.
12020 : *
12021 : * The method will also by default substitute a deprecated EPSG code by its
12022 : * non-deprecated replacement. If this behavior is not desired, the
12023 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12024 : *
12025 : * This method is the same as the C function OSRImportFromEPSGA().
12026 : *
12027 : * @param nCode a CRS code.
12028 : *
12029 : * @return OGRERR_NONE on success, or an error code on failure.
12030 : */
12031 :
12032 46311 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12033 :
12034 : {
12035 92622 : TAKE_OPTIONAL_LOCK();
12036 :
12037 46311 : Clear();
12038 :
12039 : const char *pszUseNonDeprecated =
12040 46311 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12041 : const bool bUseNonDeprecated =
12042 46311 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12043 46311 : const bool bAddTOWGS84 = CPLTestBool(
12044 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12045 46311 : auto tlsCache = OSRGetProjTLSCache();
12046 46311 : if (tlsCache)
12047 : {
12048 : auto cachedObj =
12049 46311 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12050 46311 : if (cachedObj)
12051 : {
12052 36093 : d->setPjCRS(cachedObj);
12053 36093 : return OGRERR_NONE;
12054 : }
12055 : }
12056 :
12057 20436 : CPLString osCode;
12058 10218 : osCode.Printf("%d", nCode);
12059 : PJ *obj;
12060 10218 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12061 10218 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12062 : {
12063 10213 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12064 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12065 : nullptr);
12066 10213 : if (!obj)
12067 : {
12068 24 : return OGRERR_FAILURE;
12069 : }
12070 : }
12071 : else
12072 : {
12073 : // Likely to be an ESRI CRS...
12074 5 : CPLErr eLastErrorType = CE_None;
12075 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12076 5 : std::string osLastErrorMsg;
12077 5 : bool bIsESRI = false;
12078 : {
12079 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12080 5 : CPLErrorReset();
12081 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12082 : osCode.c_str(), PJ_CATEGORY_CRS,
12083 : true, nullptr);
12084 5 : if (!obj)
12085 : {
12086 2 : eLastErrorType = CPLGetLastErrorType();
12087 2 : eLastErrorNum = CPLGetLastErrorNo();
12088 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12089 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12090 : osCode.c_str(), PJ_CATEGORY_CRS,
12091 : true, nullptr);
12092 2 : if (obj)
12093 1 : bIsESRI = true;
12094 : }
12095 : }
12096 5 : if (!obj)
12097 : {
12098 1 : if (eLastErrorType != CE_None)
12099 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12100 : osLastErrorMsg.c_str());
12101 1 : return OGRERR_FAILURE;
12102 : }
12103 4 : if (bIsESRI)
12104 : {
12105 1 : CPLError(CE_Warning, CPLE_AppDefined,
12106 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12107 : "Assuming ESRI:%d was meant",
12108 : nCode, nCode, nCode);
12109 : }
12110 : }
12111 :
12112 10193 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12113 : {
12114 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12115 410 : if (list)
12116 : {
12117 410 : const auto count = proj_list_get_count(list);
12118 410 : if (count == 1)
12119 : {
12120 : auto nonDeprecated =
12121 359 : proj_list_get(d->getPROJContext(), list, 0);
12122 359 : if (nonDeprecated)
12123 : {
12124 359 : if (pszUseNonDeprecated == nullptr)
12125 : {
12126 : const char *pszNewAuth =
12127 359 : proj_get_id_auth_name(nonDeprecated, 0);
12128 : const char *pszNewCode =
12129 359 : proj_get_id_code(nonDeprecated, 0);
12130 359 : CPLError(CE_Warning, CPLE_AppDefined,
12131 : "CRS EPSG:%d is deprecated. "
12132 : "Its non-deprecated replacement %s:%s "
12133 : "will be used instead. "
12134 : "To use the original CRS, set the "
12135 : "OSR_USE_NON_DEPRECATED "
12136 : "configuration option to NO.",
12137 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12138 : pszNewCode ? pszNewCode : "(null)");
12139 : }
12140 359 : proj_destroy(obj);
12141 359 : obj = nonDeprecated;
12142 : }
12143 : }
12144 : }
12145 410 : proj_list_destroy(list);
12146 : }
12147 :
12148 10193 : if (bAddTOWGS84)
12149 : {
12150 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12151 : obj, nullptr);
12152 1 : if (boundCRS)
12153 : {
12154 1 : proj_destroy(obj);
12155 1 : obj = boundCRS;
12156 : }
12157 : }
12158 :
12159 10193 : d->setPjCRS(obj);
12160 :
12161 10193 : if (tlsCache)
12162 : {
12163 10193 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12164 : obj);
12165 : }
12166 :
12167 10193 : return OGRERR_NONE;
12168 : }
12169 :
12170 : /************************************************************************/
12171 : /* AddGuessedTOWGS84() */
12172 : /************************************************************************/
12173 :
12174 : /**
12175 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12176 : * to WGS84.
12177 : *
12178 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12179 : * to WGS84 when there is one and only one such method available for the CRS.
12180 : * Note: this is more restrictive to how GDAL < 3 worked.
12181 : *
12182 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12183 : *
12184 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12185 : * already a transformation to WGS84 or none matching could be found).
12186 : *
12187 : * @since GDAL 3.0.3
12188 : */
12189 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12190 : {
12191 36 : TAKE_OPTIONAL_LOCK();
12192 :
12193 18 : d->refreshProjObj();
12194 18 : if (!d->m_pj_crs)
12195 0 : return OGRERR_FAILURE;
12196 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12197 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12198 18 : if (!boundCRS)
12199 : {
12200 0 : return OGRERR_FAILURE;
12201 : }
12202 18 : d->setPjCRS(boundCRS);
12203 18 : return OGRERR_NONE;
12204 : }
12205 :
12206 : /************************************************************************/
12207 : /* OSRImportFromEPSGA() */
12208 : /************************************************************************/
12209 :
12210 : /**
12211 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12212 : * to WGS84.
12213 : *
12214 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12215 : *
12216 : * @since GDAL 3.0.3
12217 : */
12218 :
12219 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12220 :
12221 : {
12222 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12223 :
12224 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12225 : }
12226 :
12227 : /************************************************************************/
12228 : /* OSRImportFromEPSGA() */
12229 : /************************************************************************/
12230 :
12231 : /**
12232 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12233 : * code.
12234 : *
12235 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12236 : */
12237 :
12238 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12239 :
12240 : {
12241 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12242 :
12243 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12244 : }
12245 :
12246 : /************************************************************************/
12247 : /* importFromEPSG() */
12248 : /************************************************************************/
12249 :
12250 : /**
12251 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12252 : * code.
12253 : *
12254 : * This method will initialize the spatial reference based on the
12255 : * passed in EPSG CRS code found in the PROJ database.
12256 : *
12257 : * This method is the same as the C function OSRImportFromEPSG().
12258 : *
12259 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12260 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12261 : * such method available for the CRS. This behavior might not always be
12262 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12263 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12264 : *
12265 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12266 : *
12267 : * @return OGRERR_NONE on success, or an error code on failure.
12268 : */
12269 :
12270 41286 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12271 :
12272 : {
12273 41286 : return importFromEPSGA(nCode);
12274 : }
12275 :
12276 : /************************************************************************/
12277 : /* OSRImportFromEPSG() */
12278 : /************************************************************************/
12279 :
12280 : /**
12281 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12282 : * code.
12283 : *
12284 : * This function is the same as OGRSpatialReference::importFromEPSG().
12285 : */
12286 :
12287 1565 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12288 :
12289 : {
12290 1565 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12291 :
12292 1565 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12293 : }
12294 :
12295 : /************************************************************************/
12296 : /* EPSGTreatsAsLatLong() */
12297 : /************************************************************************/
12298 :
12299 : /**
12300 : * \brief This method returns TRUE if this geographic coordinate
12301 : * system should be treated as having lat/long coordinate ordering.
12302 : *
12303 : * Currently this returns TRUE for all geographic coordinate systems
12304 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12305 : * also checked that the CRS had belonged to EPSG authority, but this check
12306 : * has now been removed).
12307 : *
12308 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12309 : * geographic CRS imported with importFromEPSG() would cause this method to
12310 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12311 : * is now equivalent to importFromEPSGA().
12312 : *
12313 : * FALSE will be returned for all coordinate systems that are not geographic,
12314 : * or whose axes ordering is not latitude, longitude.
12315 : *
12316 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12317 : *
12318 : * @return TRUE or FALSE.
12319 : */
12320 :
12321 1127 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12322 :
12323 : {
12324 2254 : TAKE_OPTIONAL_LOCK();
12325 :
12326 1127 : if (!IsGeographic())
12327 786 : return FALSE;
12328 :
12329 341 : d->demoteFromBoundCRS();
12330 :
12331 341 : bool ret = false;
12332 341 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12333 : {
12334 : auto horizCRS =
12335 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12336 3 : if (horizCRS)
12337 : {
12338 : auto cs =
12339 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12340 3 : if (cs)
12341 : {
12342 3 : const char *pszDirection = nullptr;
12343 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12344 : nullptr, &pszDirection, nullptr,
12345 3 : nullptr, nullptr, nullptr))
12346 : {
12347 3 : if (EQUAL(pszDirection, "north"))
12348 : {
12349 3 : ret = true;
12350 : }
12351 : }
12352 :
12353 3 : proj_destroy(cs);
12354 : }
12355 :
12356 3 : proj_destroy(horizCRS);
12357 : }
12358 : }
12359 : else
12360 : {
12361 : auto cs =
12362 338 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12363 338 : if (cs)
12364 : {
12365 338 : const char *pszDirection = nullptr;
12366 338 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12367 : nullptr, &pszDirection, nullptr, nullptr,
12368 338 : nullptr, nullptr))
12369 : {
12370 338 : if (EQUAL(pszDirection, "north"))
12371 : {
12372 292 : ret = true;
12373 : }
12374 : }
12375 :
12376 338 : proj_destroy(cs);
12377 : }
12378 : }
12379 341 : d->undoDemoteFromBoundCRS();
12380 :
12381 341 : return ret;
12382 : }
12383 :
12384 : /************************************************************************/
12385 : /* OSREPSGTreatsAsLatLong() */
12386 : /************************************************************************/
12387 :
12388 : /**
12389 : * \brief This function returns TRUE if this geographic coordinate
12390 : * system should be treated as having lat/long coordinate ordering.
12391 : *
12392 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12393 : */
12394 :
12395 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12396 :
12397 : {
12398 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12399 :
12400 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12401 : }
12402 :
12403 : /************************************************************************/
12404 : /* EPSGTreatsAsNorthingEasting() */
12405 : /************************************************************************/
12406 :
12407 : /**
12408 : * \brief This method returns TRUE if this projected coordinate
12409 : * system should be treated as having northing/easting coordinate ordering.
12410 : *
12411 : * Currently this returns TRUE for all projected coordinate systems
12412 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12413 : * also checked that the CRS had belonged to EPSG authority, but this check
12414 : * has now been removed).
12415 : *
12416 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12417 : * projected CRS with northing, easting axis order imported with
12418 : * importFromEPSG() would cause this method to
12419 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12420 : * is now equivalent to importFromEPSGA().
12421 : *
12422 : * FALSE will be returned for all coordinate systems that are not projected,
12423 : * or whose axes ordering is not northing, easting.
12424 : *
12425 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12426 : *
12427 : * @return TRUE or FALSE.
12428 : *
12429 : */
12430 :
12431 872 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12432 :
12433 : {
12434 1744 : TAKE_OPTIONAL_LOCK();
12435 :
12436 872 : if (!IsProjected())
12437 48 : return FALSE;
12438 :
12439 824 : d->demoteFromBoundCRS();
12440 : PJ *projCRS;
12441 824 : const auto ctxt = d->getPROJContext();
12442 824 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12443 : {
12444 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12445 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12446 : {
12447 0 : d->undoDemoteFromBoundCRS();
12448 0 : proj_destroy(projCRS);
12449 0 : return FALSE;
12450 : }
12451 : }
12452 : else
12453 : {
12454 820 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12455 : }
12456 :
12457 824 : bool ret = false;
12458 824 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12459 824 : proj_destroy(projCRS);
12460 824 : d->undoDemoteFromBoundCRS();
12461 :
12462 824 : if (cs)
12463 : {
12464 824 : ret = isNorthEastAxisOrder(ctxt, cs);
12465 824 : proj_destroy(cs);
12466 : }
12467 :
12468 824 : return ret;
12469 : }
12470 :
12471 : /************************************************************************/
12472 : /* OSREPSGTreatsAsNorthingEasting() */
12473 : /************************************************************************/
12474 :
12475 : /**
12476 : * \brief This function returns TRUE if this projected coordinate
12477 : * system should be treated as having northing/easting coordinate ordering.
12478 : *
12479 : * This function is the same as
12480 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12481 : *
12482 : */
12483 :
12484 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12485 :
12486 : {
12487 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12488 :
12489 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12490 : }
12491 :
12492 : /************************************************************************/
12493 : /* ImportFromESRIWisconsinWKT() */
12494 : /* */
12495 : /* Search a ESRI State Plane WKT and import it. */
12496 : /************************************************************************/
12497 :
12498 : // This is only used by the HFA driver and somewhat dubious we really need that
12499 : // Coming from an old ESRI merge
12500 :
12501 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12502 : double centralMeridian,
12503 : double latOfOrigin,
12504 : const char *unitsName,
12505 : const char *crsName)
12506 : {
12507 2 : TAKE_OPTIONAL_LOCK();
12508 :
12509 1 : if (centralMeridian < -93 || centralMeridian > -87)
12510 0 : return OGRERR_FAILURE;
12511 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12512 0 : return OGRERR_FAILURE;
12513 :
12514 : // If the CS name is known.
12515 1 : if (!prjName && !unitsName && crsName)
12516 : {
12517 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12518 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12519 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12520 0 : if (list)
12521 : {
12522 0 : if (proj_list_get_count(list) == 1)
12523 : {
12524 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12525 0 : if (crs)
12526 : {
12527 0 : Clear();
12528 0 : d->setPjCRS(crs);
12529 0 : proj_list_destroy(list);
12530 0 : return OGRERR_NONE;
12531 : }
12532 : }
12533 0 : proj_list_destroy(list);
12534 : }
12535 0 : return OGRERR_FAILURE;
12536 : }
12537 :
12538 1 : if (prjName == nullptr || unitsName == nullptr)
12539 : {
12540 0 : return OGRERR_FAILURE;
12541 : }
12542 :
12543 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12544 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12545 : "NAD_1983_HARN_WISCRS_", &type, 1,
12546 : true, 0, nullptr);
12547 1 : if (list)
12548 : {
12549 1 : const auto listSize = proj_list_get_count(list);
12550 8 : for (int i = 0; i < listSize; i++)
12551 : {
12552 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12553 8 : if (!crs)
12554 : {
12555 7 : continue;
12556 : }
12557 :
12558 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12559 8 : if (!conv)
12560 : {
12561 0 : proj_destroy(crs);
12562 0 : continue;
12563 : }
12564 8 : const char *pszMethodCode = nullptr;
12565 8 : proj_coordoperation_get_method_info(
12566 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12567 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12568 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12569 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12570 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12571 : nMethodCode ==
12572 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12573 : {
12574 3 : proj_destroy(crs);
12575 3 : proj_destroy(conv);
12576 3 : continue;
12577 : }
12578 :
12579 : auto coordSys =
12580 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12581 5 : if (!coordSys)
12582 : {
12583 0 : proj_destroy(crs);
12584 0 : proj_destroy(conv);
12585 0 : continue;
12586 : }
12587 :
12588 5 : double dfConvFactor = 0.0;
12589 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12590 : nullptr, nullptr, &dfConvFactor, nullptr,
12591 : nullptr, nullptr);
12592 5 : proj_destroy(coordSys);
12593 :
12594 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12595 1 : (!EQUAL(unitsName, "meters") &&
12596 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12597 : 1e-10))
12598 : {
12599 4 : proj_destroy(crs);
12600 4 : proj_destroy(conv);
12601 4 : continue;
12602 : }
12603 :
12604 1 : int idx_lat = proj_coordoperation_get_param_index(
12605 : d->getPROJContext(), conv,
12606 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12607 1 : double valueLat = -1000;
12608 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12609 : nullptr, nullptr, nullptr, &valueLat,
12610 : nullptr, nullptr, nullptr, nullptr,
12611 : nullptr, nullptr);
12612 1 : int idx_lon = proj_coordoperation_get_param_index(
12613 : d->getPROJContext(), conv,
12614 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12615 1 : double valueLong = -1000;
12616 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12617 : nullptr, nullptr, nullptr, &valueLong,
12618 : nullptr, nullptr, nullptr, nullptr,
12619 : nullptr, nullptr);
12620 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12621 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12622 : {
12623 1 : Clear();
12624 1 : d->setPjCRS(crs);
12625 1 : proj_list_destroy(list);
12626 1 : proj_destroy(conv);
12627 1 : return OGRERR_NONE;
12628 : }
12629 :
12630 0 : proj_destroy(crs);
12631 0 : proj_destroy(conv);
12632 : }
12633 0 : proj_list_destroy(list);
12634 : }
12635 :
12636 0 : return OGRERR_FAILURE;
12637 : }
12638 :
12639 : /************************************************************************/
12640 : /* GetAxisMappingStrategy() */
12641 : /************************************************************************/
12642 :
12643 : /** \brief Return the data axis to CRS axis mapping strategy.
12644 : *
12645 : * <ul>
12646 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12647 : * lat/long order, the data will still be long/lat ordered. Similarly for
12648 : * a projected CRS with northing/easting order, the data will still be
12649 : * easting/northing ordered.
12650 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12651 : * the CRS axis.
12652 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12653 : * SetDataAxisToSRSAxisMapping()
12654 : * </ul>
12655 : * @return the data axis to CRS axis mapping strategy.
12656 : * @since GDAL 3.0
12657 : */
12658 93 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12659 : {
12660 93 : TAKE_OPTIONAL_LOCK();
12661 :
12662 186 : return d->m_axisMappingStrategy;
12663 : }
12664 :
12665 : /************************************************************************/
12666 : /* OSRGetAxisMappingStrategy() */
12667 : /************************************************************************/
12668 :
12669 : /** \brief Return the data axis to CRS axis mapping strategy.
12670 : *
12671 : * See OGRSpatialReference::GetAxisMappingStrategy()
12672 : * @since GDAL 3.0
12673 : */
12674 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12675 : {
12676 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12677 :
12678 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12679 : }
12680 :
12681 : /************************************************************************/
12682 : /* SetAxisMappingStrategy() */
12683 : /************************************************************************/
12684 :
12685 : /** \brief Set the data axis to CRS axis mapping strategy.
12686 : *
12687 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12688 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12689 : * later being the default value when the option is not set) to control the
12690 : * value of the data axis to CRS axis mapping strategy when a
12691 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12692 : * override this default value.
12693 : *
12694 : * See OGRSpatialReference::GetAxisMappingStrategy()
12695 : * @since GDAL 3.0
12696 : */
12697 90279 : void OGRSpatialReference::SetAxisMappingStrategy(
12698 : OSRAxisMappingStrategy strategy)
12699 : {
12700 180558 : TAKE_OPTIONAL_LOCK();
12701 :
12702 90279 : d->m_axisMappingStrategy = strategy;
12703 90279 : d->refreshAxisMapping();
12704 90279 : }
12705 :
12706 : /************************************************************************/
12707 : /* OSRSetAxisMappingStrategy() */
12708 : /************************************************************************/
12709 :
12710 : /** \brief Set the data axis to CRS axis mapping strategy.
12711 : *
12712 : * See OGRSpatialReference::SetAxisMappingStrategy()
12713 : * @since GDAL 3.0
12714 : */
12715 888 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12716 : OSRAxisMappingStrategy strategy)
12717 : {
12718 888 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12719 :
12720 888 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12721 : }
12722 :
12723 : /************************************************************************/
12724 : /* GetDataAxisToSRSAxisMapping() */
12725 : /************************************************************************/
12726 :
12727 : /** \brief Return the data axis to SRS axis mapping.
12728 : *
12729 : * The number of elements of the vector will be the number of axis of the CRS.
12730 : * Values start at 1.
12731 : *
12732 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12733 : * for the first axis of the CRS.
12734 : *
12735 : * @since GDAL 3.0
12736 : */
12737 17300100 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12738 : {
12739 17300100 : TAKE_OPTIONAL_LOCK();
12740 :
12741 34600200 : return d->m_axisMapping;
12742 : }
12743 :
12744 : /************************************************************************/
12745 : /* OSRGetDataAxisToSRSAxisMapping() */
12746 : /************************************************************************/
12747 :
12748 : /** \brief Return the data axis to SRS axis mapping.
12749 : *
12750 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12751 : *
12752 : * @since GDAL 3.0
12753 : */
12754 232 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12755 : int *pnCount)
12756 : {
12757 232 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12758 232 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12759 :
12760 : const auto &v =
12761 232 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12762 232 : *pnCount = static_cast<int>(v.size());
12763 232 : return v.data();
12764 : }
12765 :
12766 : /************************************************************************/
12767 : /* SetDataAxisToSRSAxisMapping() */
12768 : /************************************************************************/
12769 :
12770 : /** \brief Set a custom data axis to CRS axis mapping.
12771 : *
12772 : * The number of elements of the mapping vector should be the number of axis
12773 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12774 : * check that, beyond checking there are at least 2 elements, so that this
12775 : * method and setting the CRS can be done in any order).
12776 : * This is taken into account by OGRCoordinateTransformation to transform the
12777 : * order of coordinates to the order expected by the CRS before
12778 : * transformation, and back to the data order after transformation.
12779 : *
12780 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12781 : * axis of the CRS. A negative value can also be used to ask for a sign
12782 : * reversal during coordinate transformation (to deal with northing vs southing,
12783 : * easting vs westing, heights vs depths).
12784 : *
12785 : * When used with OGRCoordinateTransformation,
12786 : * - the only valid values for mapping[0] (data axis number for the first axis
12787 : * of the CRS) are 1, 2, -1, -2.
12788 : * - the only valid values for mapping[1] (data axis number for the second axis
12789 : * of the CRS) are 1, 2, -1, -2.
12790 : * - the only valid values mapping[2] are 3 or -3.
12791 : * Note: this method does not validate the values of mapping[].
12792 : *
12793 : * mapping=[2,1] typically expresses the inversion of axis between the data
12794 : * axis and the CRS axis for a 2D CRS.
12795 : *
12796 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12797 : *
12798 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12799 : *
12800 : * @param mapping The new data axis to CRS axis mapping.
12801 : *
12802 : * @since GDAL 3.0
12803 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12804 : */
12805 9842 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12806 : const std::vector<int> &mapping)
12807 : {
12808 19684 : TAKE_OPTIONAL_LOCK();
12809 :
12810 9842 : if (mapping.size() < 2)
12811 0 : return OGRERR_FAILURE;
12812 9842 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12813 9842 : d->m_axisMapping = mapping;
12814 9842 : return OGRERR_NONE;
12815 : }
12816 :
12817 : /************************************************************************/
12818 : /* OSRSetDataAxisToSRSAxisMapping() */
12819 : /************************************************************************/
12820 :
12821 : /** \brief Set a custom data axis to CRS axis mapping.
12822 : *
12823 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12824 : *
12825 : * This is the same as the C++ method
12826 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12827 : *
12828 : * @since GDAL 3.1
12829 : */
12830 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12831 : int nMappingSize, const int *panMapping)
12832 : {
12833 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12834 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12835 : OGRERR_FAILURE);
12836 :
12837 15 : if (nMappingSize < 0)
12838 0 : return OGRERR_FAILURE;
12839 :
12840 30 : std::vector<int> mapping(nMappingSize);
12841 15 : if (nMappingSize)
12842 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12843 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12844 15 : mapping);
12845 : }
12846 :
12847 : /************************************************************************/
12848 : /* GetAreaOfUse() */
12849 : /************************************************************************/
12850 :
12851 : /** \brief Return the area of use of the CRS.
12852 : *
12853 : * This method is the same as the OSRGetAreaOfUse() function.
12854 : *
12855 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12856 : * longitude, expressed in degree. Might be NULL. If the returned value is
12857 : * -1000, the bounding box is unknown.
12858 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12859 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12860 : * the bounding box is unknown.
12861 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12862 : * longitude, expressed in degree. Might be NULL. If the returned value is
12863 : * -1000, the bounding box is unknown.
12864 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12865 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12866 : * the bounding box is unknown.
12867 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12868 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12869 : * invalidated by further calls.
12870 : * @return true in case of success
12871 : * @since GDAL 3.0
12872 : */
12873 53 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12874 : double *pdfSouthLatitudeDeg,
12875 : double *pdfEastLongitudeDeg,
12876 : double *pdfNorthLatitudeDeg,
12877 : const char **ppszAreaName) const
12878 : {
12879 106 : TAKE_OPTIONAL_LOCK();
12880 :
12881 53 : d->refreshProjObj();
12882 53 : if (!d->m_pj_crs)
12883 : {
12884 0 : return false;
12885 : }
12886 53 : d->demoteFromBoundCRS();
12887 53 : const char *pszAreaName = nullptr;
12888 53 : int bSuccess = proj_get_area_of_use(
12889 53 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12890 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12891 : &pszAreaName);
12892 53 : d->undoDemoteFromBoundCRS();
12893 53 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12894 53 : if (ppszAreaName)
12895 1 : *ppszAreaName = d->m_osAreaName.c_str();
12896 53 : return CPL_TO_BOOL(bSuccess);
12897 : }
12898 :
12899 : /************************************************************************/
12900 : /* GetAreaOfUse() */
12901 : /************************************************************************/
12902 :
12903 : /** \brief Return the area of use of the CRS.
12904 : *
12905 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12906 : *
12907 : * @since GDAL 3.0
12908 : */
12909 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12910 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12911 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12912 : {
12913 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12914 :
12915 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12916 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12917 1 : pdfNorthLatitudeDeg, ppszAreaName);
12918 : }
12919 :
12920 : /************************************************************************/
12921 : /* OSRGetCRSInfoListFromDatabase() */
12922 : /************************************************************************/
12923 :
12924 : /** \brief Enumerate CRS objects from the database.
12925 : *
12926 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12927 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12928 : *
12929 : * @param pszAuthName Authority name, used to restrict the search.
12930 : * Or NULL for all authorities.
12931 : * @param params Additional criteria. Must be set to NULL for now.
12932 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12933 : * the size of the result list. Might be NULL
12934 : * @return an array of OSRCRSInfo* pointers to be freed with
12935 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12936 : *
12937 : * @since GDAL 3.0
12938 : */
12939 : OSRCRSInfo **
12940 24 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12941 : CPL_UNUSED const OSRCRSListParameters *params,
12942 : int *pnOutResultCount)
12943 : {
12944 24 : int nResultCount = 0;
12945 24 : auto projList = proj_get_crs_info_list_from_database(
12946 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12947 24 : if (pnOutResultCount)
12948 24 : *pnOutResultCount = nResultCount;
12949 24 : if (!projList)
12950 : {
12951 0 : return nullptr;
12952 : }
12953 24 : auto res = new OSRCRSInfo *[nResultCount + 1];
12954 89181 : for (int i = 0; i < nResultCount; i++)
12955 : {
12956 89157 : res[i] = new OSRCRSInfo;
12957 178314 : res[i]->pszAuthName = projList[i]->auth_name
12958 89157 : ? CPLStrdup(projList[i]->auth_name)
12959 : : nullptr;
12960 89157 : res[i]->pszCode =
12961 89157 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12962 89157 : res[i]->pszName =
12963 89157 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12964 89157 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12965 89157 : switch (projList[i]->type)
12966 : {
12967 8727 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12968 8727 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12969 8727 : break;
12970 2811 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12971 2811 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12972 2811 : break;
12973 3066 : case PJ_TYPE_GEOCENTRIC_CRS:
12974 3066 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12975 3066 : break;
12976 67740 : case PJ_TYPE_PROJECTED_CRS:
12977 67740 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12978 67740 : break;
12979 2808 : case PJ_TYPE_VERTICAL_CRS:
12980 2808 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12981 2808 : break;
12982 4005 : case PJ_TYPE_COMPOUND_CRS:
12983 4005 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12984 4005 : break;
12985 0 : default:
12986 0 : break;
12987 : }
12988 89157 : res[i]->bDeprecated = projList[i]->deprecated;
12989 89157 : res[i]->bBboxValid = projList[i]->bbox_valid;
12990 89157 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12991 89157 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12992 89157 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12993 89157 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12994 178314 : res[i]->pszAreaName = projList[i]->area_name
12995 89157 : ? CPLStrdup(projList[i]->area_name)
12996 : : nullptr;
12997 89157 : res[i]->pszProjectionMethod =
12998 89157 : projList[i]->projection_method_name
12999 89157 : ? CPLStrdup(projList[i]->projection_method_name)
13000 : : nullptr;
13001 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
13002 : res[i]->pszCelestialBodyName =
13003 : projList[i]->celestial_body_name
13004 : ? CPLStrdup(projList[i]->celestial_body_name)
13005 : : nullptr;
13006 : #else
13007 89157 : res[i]->pszCelestialBodyName =
13008 89157 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13009 178314 : ? CPLStrdup("Earth")
13010 : : nullptr;
13011 : #endif
13012 : }
13013 24 : res[nResultCount] = nullptr;
13014 24 : proj_crs_info_list_destroy(projList);
13015 24 : return res;
13016 : }
13017 :
13018 : /************************************************************************/
13019 : /* OSRDestroyCRSInfoList() */
13020 : /************************************************************************/
13021 :
13022 : /** \brief Destroy the result returned by
13023 : * OSRGetCRSInfoListFromDatabase().
13024 : *
13025 : * @since GDAL 3.0
13026 : */
13027 24 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13028 : {
13029 24 : if (list)
13030 : {
13031 89181 : for (int i = 0; list[i] != nullptr; i++)
13032 : {
13033 89157 : CPLFree(list[i]->pszAuthName);
13034 89157 : CPLFree(list[i]->pszCode);
13035 89157 : CPLFree(list[i]->pszName);
13036 89157 : CPLFree(list[i]->pszAreaName);
13037 89157 : CPLFree(list[i]->pszProjectionMethod);
13038 89157 : CPLFree(list[i]->pszCelestialBodyName);
13039 89157 : delete list[i];
13040 : }
13041 24 : delete[] list;
13042 : }
13043 24 : }
13044 :
13045 : /************************************************************************/
13046 : /* OSRGetAuthorityListFromDatabase() */
13047 : /************************************************************************/
13048 :
13049 : /** \brief Return the list of CRS authorities used in the PROJ database.
13050 : *
13051 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13052 : *
13053 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13054 : *
13055 : * @return nullptr in case of error, or a NULL terminated list of strings to
13056 : * free with CSLDestroy()
13057 : * @since GDAL 3.10
13058 : */
13059 4 : char **OSRGetAuthorityListFromDatabase()
13060 : {
13061 : PROJ_STRING_LIST list =
13062 4 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13063 4 : if (!list)
13064 : {
13065 0 : return nullptr;
13066 : }
13067 4 : int count = 0;
13068 24 : while (list[count])
13069 20 : ++count;
13070 4 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13071 24 : for (int i = 0; i < count; ++i)
13072 20 : res[i] = CPLStrdup(list[i]);
13073 4 : proj_string_list_destroy(list);
13074 4 : return res;
13075 : }
13076 :
13077 : /************************************************************************/
13078 : /* UpdateCoordinateSystemFromGeogCRS() */
13079 : /************************************************************************/
13080 :
13081 : /*! @cond Doxygen_Suppress */
13082 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13083 : *
13084 : * @since GDAL 3.1
13085 : */
13086 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13087 : {
13088 1 : TAKE_OPTIONAL_LOCK();
13089 :
13090 1 : d->refreshProjObj();
13091 1 : if (!d->m_pj_crs)
13092 0 : return;
13093 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13094 0 : return;
13095 1 : if (GetAxesCount() == 3)
13096 0 : return;
13097 1 : auto ctxt = d->getPROJContext();
13098 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13099 1 : if (!baseCRS)
13100 0 : return;
13101 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13102 1 : if (!baseCRSCS)
13103 : {
13104 0 : proj_destroy(baseCRS);
13105 0 : return;
13106 : }
13107 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13108 : {
13109 0 : proj_destroy(baseCRSCS);
13110 0 : proj_destroy(baseCRS);
13111 0 : return;
13112 : }
13113 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13114 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13115 : {
13116 0 : proj_destroy(baseCRSCS);
13117 0 : proj_destroy(baseCRS);
13118 0 : proj_destroy(projCS);
13119 0 : return;
13120 : }
13121 :
13122 : PJ_AXIS_DESCRIPTION axis[3];
13123 4 : for (int i = 0; i < 3; i++)
13124 : {
13125 3 : const char *name = nullptr;
13126 3 : const char *abbreviation = nullptr;
13127 3 : const char *direction = nullptr;
13128 3 : double unit_conv_factor = 0;
13129 3 : const char *unit_name = nullptr;
13130 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13131 : &abbreviation, &direction, &unit_conv_factor,
13132 : &unit_name, nullptr, nullptr);
13133 3 : axis[i].name = CPLStrdup(name);
13134 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13135 3 : axis[i].direction = CPLStrdup(direction);
13136 3 : axis[i].unit_name = CPLStrdup(unit_name);
13137 3 : axis[i].unit_conv_factor = unit_conv_factor;
13138 3 : axis[i].unit_type = PJ_UT_LINEAR;
13139 : }
13140 1 : proj_destroy(baseCRSCS);
13141 1 : proj_destroy(projCS);
13142 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13143 4 : for (int i = 0; i < 3; i++)
13144 : {
13145 3 : CPLFree(axis[i].name);
13146 3 : CPLFree(axis[i].abbreviation);
13147 3 : CPLFree(axis[i].direction);
13148 3 : CPLFree(axis[i].unit_name);
13149 : }
13150 1 : if (!cs)
13151 : {
13152 0 : proj_destroy(baseCRS);
13153 0 : return;
13154 : }
13155 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13156 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13157 : conversion, cs);
13158 1 : proj_destroy(baseCRS);
13159 1 : proj_destroy(conversion);
13160 1 : proj_destroy(cs);
13161 1 : d->setPjCRS(crs);
13162 : }
13163 :
13164 : /*! @endcond */
13165 :
13166 : /************************************************************************/
13167 : /* PromoteTo3D() */
13168 : /************************************************************************/
13169 :
13170 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13171 : *
13172 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13173 : * units.
13174 : *
13175 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13176 : * be used.
13177 : * @return OGRERR_NONE if no error occurred.
13178 : * @since GDAL 3.1 and PROJ 6.3
13179 : */
13180 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13181 : {
13182 84 : TAKE_OPTIONAL_LOCK();
13183 :
13184 42 : d->refreshProjObj();
13185 42 : if (!d->m_pj_crs)
13186 0 : return OGRERR_FAILURE;
13187 : auto newPj =
13188 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13189 42 : if (!newPj)
13190 0 : return OGRERR_FAILURE;
13191 42 : d->setPjCRS(newPj);
13192 42 : return OGRERR_NONE;
13193 : }
13194 :
13195 : /************************************************************************/
13196 : /* OSRPromoteTo3D() */
13197 : /************************************************************************/
13198 :
13199 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13200 : *
13201 : * See OGRSpatialReference::PromoteTo3D()
13202 : *
13203 : * @since GDAL 3.1 and PROJ 6.3
13204 : */
13205 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13206 : {
13207 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13208 :
13209 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13210 : }
13211 :
13212 : /************************************************************************/
13213 : /* DemoteTo2D() */
13214 : /************************************************************************/
13215 :
13216 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13217 : *
13218 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13219 : * be used.
13220 : * @return OGRERR_NONE if no error occurred.
13221 : * @since GDAL 3.2 and PROJ 6.3
13222 : */
13223 47 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13224 : {
13225 94 : TAKE_OPTIONAL_LOCK();
13226 :
13227 47 : d->refreshProjObj();
13228 47 : if (!d->m_pj_crs)
13229 0 : return OGRERR_FAILURE;
13230 : auto newPj =
13231 47 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13232 47 : if (!newPj)
13233 0 : return OGRERR_FAILURE;
13234 47 : d->setPjCRS(newPj);
13235 47 : return OGRERR_NONE;
13236 : }
13237 :
13238 : /************************************************************************/
13239 : /* OSRDemoteTo2D() */
13240 : /************************************************************************/
13241 :
13242 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13243 : *
13244 : * See OGRSpatialReference::DemoteTo2D()
13245 : *
13246 : * @since GDAL 3.2 and PROJ 6.3
13247 : */
13248 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13249 : {
13250 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13251 :
13252 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13253 : }
13254 :
13255 : /************************************************************************/
13256 : /* GetEPSGGeogCS() */
13257 : /************************************************************************/
13258 :
13259 : /** Try to establish what the EPSG code for this coordinate systems
13260 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13261 : *
13262 : * @return EPSG code
13263 : */
13264 :
13265 342 : int OGRSpatialReference::GetEPSGGeogCS() const
13266 :
13267 : {
13268 684 : TAKE_OPTIONAL_LOCK();
13269 :
13270 : /* -------------------------------------------------------------------- */
13271 : /* Check axis order. */
13272 : /* -------------------------------------------------------------------- */
13273 684 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13274 342 : if (!poGeogCRS)
13275 0 : return -1;
13276 :
13277 342 : bool ret = false;
13278 342 : poGeogCRS->d->demoteFromBoundCRS();
13279 342 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13280 342 : poGeogCRS->d->m_pj_crs);
13281 342 : poGeogCRS->d->undoDemoteFromBoundCRS();
13282 342 : if (cs)
13283 : {
13284 342 : const char *pszDirection = nullptr;
13285 342 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13286 : &pszDirection, nullptr, nullptr, nullptr,
13287 342 : nullptr))
13288 : {
13289 342 : if (EQUAL(pszDirection, "north"))
13290 : {
13291 143 : ret = true;
13292 : }
13293 : }
13294 :
13295 342 : proj_destroy(cs);
13296 : }
13297 342 : if (!ret)
13298 199 : return -1;
13299 :
13300 : /* -------------------------------------------------------------------- */
13301 : /* Do we already have it? */
13302 : /* -------------------------------------------------------------------- */
13303 143 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13304 143 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13305 66 : return atoi(GetAuthorityCode("GEOGCS"));
13306 :
13307 : /* -------------------------------------------------------------------- */
13308 : /* Get the datum and geogcs names. */
13309 : /* -------------------------------------------------------------------- */
13310 :
13311 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13312 77 : const char *pszDatum = GetAttrValue("DATUM");
13313 :
13314 : // We can only operate on coordinate systems with a geogcs.
13315 154 : OGRSpatialReference oSRSTmp;
13316 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13317 : {
13318 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13319 : // export to WKT1, so try to extract the geographic CRS through PROJ
13320 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13321 1 : oSRSTmp.CopyGeogCSFrom(this);
13322 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13323 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13324 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13325 : {
13326 0 : return -1;
13327 : }
13328 : }
13329 :
13330 : // Lookup geographic CRS name
13331 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13332 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13333 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13334 77 : if (list)
13335 : {
13336 77 : const auto listSize = proj_list_get_count(list);
13337 77 : if (listSize == 1)
13338 : {
13339 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13340 49 : if (crs)
13341 : {
13342 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13343 49 : const char *pszCode = proj_get_id_code(crs, 0);
13344 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13345 : {
13346 47 : const int nCode = atoi(pszCode);
13347 47 : proj_destroy(crs);
13348 47 : proj_list_destroy(list);
13349 47 : return nCode;
13350 : }
13351 2 : proj_destroy(crs);
13352 : }
13353 : }
13354 30 : proj_list_destroy(list);
13355 : }
13356 :
13357 : /* -------------------------------------------------------------------- */
13358 : /* Is this a "well known" geographic coordinate system? */
13359 : /* -------------------------------------------------------------------- */
13360 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13361 30 : strstr(pszDatum, "WGS") ||
13362 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13363 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13364 90 : strstr(pszDatum, "World Geodetic System") ||
13365 30 : strstr(pszDatum, "World_Geodetic_System");
13366 :
13367 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13368 30 : strstr(pszDatum, "NAD") ||
13369 30 : strstr(pszGEOGCS, "North American") ||
13370 30 : strstr(pszGEOGCS, "North_American") ||
13371 90 : strstr(pszDatum, "North American") ||
13372 30 : strstr(pszDatum, "North_American");
13373 :
13374 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13375 0 : return 4326;
13376 :
13377 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13378 0 : return 4322;
13379 :
13380 : // This is questionable as there are several 'flavors' of NAD83 that
13381 : // are not the same as 4269
13382 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13383 0 : return 4269;
13384 :
13385 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13386 0 : return 4267;
13387 :
13388 : /* -------------------------------------------------------------------- */
13389 : /* If we know the datum, associate the most likely GCS with */
13390 : /* it. */
13391 : /* -------------------------------------------------------------------- */
13392 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13393 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13394 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13395 0 : GetPrimeMeridian() == 0.0)
13396 : {
13397 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13398 :
13399 0 : if (nDatum >= 6000 && nDatum <= 6999)
13400 0 : return nDatum - 2000;
13401 : }
13402 :
13403 30 : return -1;
13404 : }
13405 :
13406 : /************************************************************************/
13407 : /* SetCoordinateEpoch() */
13408 : /************************************************************************/
13409 :
13410 : /** Set the coordinate epoch, as decimal year.
13411 : *
13412 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13413 : * change with time. To be unambiguous the coordinates must always be qualified
13414 : * with the epoch at which they are valid. The coordinate epoch is not
13415 : * necessarily the epoch at which the observation was collected.
13416 : *
13417 : * Pedantically the coordinate epoch of an observation belongs to the
13418 : * observation, and not to the CRS, however it is often more practical to
13419 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13420 : * CRS (see IsDynamic())
13421 : *
13422 : * This method is the same as the OSRSetCoordinateEpoch() function.
13423 : *
13424 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13425 : * @since OGR 3.4
13426 : */
13427 :
13428 876 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13429 : {
13430 876 : d->m_coordinateEpoch = dfCoordinateEpoch;
13431 876 : }
13432 :
13433 : /************************************************************************/
13434 : /* OSRSetCoordinateEpoch() */
13435 : /************************************************************************/
13436 :
13437 : /** \brief Set the coordinate epoch, as decimal year.
13438 : *
13439 : * See OGRSpatialReference::SetCoordinateEpoch()
13440 : *
13441 : * @since OGR 3.4
13442 : */
13443 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13444 : {
13445 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13446 :
13447 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13448 31 : dfCoordinateEpoch);
13449 : }
13450 :
13451 : /************************************************************************/
13452 : /* GetCoordinateEpoch() */
13453 : /************************************************************************/
13454 :
13455 : /** Return the coordinate epoch, as decimal year.
13456 : *
13457 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13458 : * change with time. To be unambiguous the coordinates must always be qualified
13459 : * with the epoch at which they are valid. The coordinate epoch is not
13460 : * necessarily the epoch at which the observation was collected.
13461 : *
13462 : * Pedantically the coordinate epoch of an observation belongs to the
13463 : * observation, and not to the CRS, however it is often more practical to
13464 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13465 : * CRS (see IsDynamic())
13466 : *
13467 : * This method is the same as the OSRGetCoordinateEpoch() function.
13468 : *
13469 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13470 : * if not set, or relevant.
13471 : * @since OGR 3.4
13472 : */
13473 :
13474 14205 : double OGRSpatialReference::GetCoordinateEpoch() const
13475 : {
13476 14205 : return d->m_coordinateEpoch;
13477 : }
13478 :
13479 : /************************************************************************/
13480 : /* OSRGetCoordinateEpoch() */
13481 : /************************************************************************/
13482 :
13483 : /** \brief Get the coordinate epoch, as decimal year.
13484 : *
13485 : * See OGRSpatialReference::GetCoordinateEpoch()
13486 : *
13487 : * @since OGR 3.4
13488 : */
13489 730 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13490 : {
13491 730 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13492 :
13493 730 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13494 : }
|