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 : // Exists since 8.0.1
49 : #ifndef PROJ_AT_LEAST_VERSION
50 : #define PROJ_COMPUTE_VERSION(maj, min, patch) \
51 : ((maj)*10000 + (min)*100 + (patch))
52 : #define PROJ_VERSION_NUMBER \
53 : PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, \
54 : PROJ_VERSION_PATCH)
55 : #define PROJ_AT_LEAST_VERSION(maj, min, patch) \
56 : (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
57 : #endif
58 :
59 : #define STRINGIFY(s) #s
60 : #define XSTRINGIFY(s) STRINGIFY(s)
61 :
62 : struct OGRSpatialReference::Private
63 : {
64 : struct Listener : public OGR_SRSNode::Listener
65 : {
66 : OGRSpatialReference::Private *m_poObj = nullptr;
67 :
68 194853 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
69 : {
70 194869 : }
71 :
72 : Listener(const Listener &) = delete;
73 : Listener &operator=(const Listener &) = delete;
74 :
75 1764200 : void notifyChange(OGR_SRSNode *) override
76 : {
77 1764200 : m_poObj->nodesChanged();
78 1764200 : }
79 : };
80 :
81 : OGRSpatialReference *m_poSelf = nullptr;
82 : PJ *m_pj_crs = nullptr;
83 :
84 : // Temporary state used for object construction
85 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
86 : CPLString m_osPrimeMeridianName{};
87 : CPLString m_osAngularUnits{};
88 : CPLString m_osLinearUnits{};
89 : CPLString m_osAxisName[3]{};
90 :
91 : std::vector<std::string> m_wktImportWarnings{};
92 : std::vector<std::string> m_wktImportErrors{};
93 : CPLString m_osAreaName{};
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 985422 : PJ_CONTEXT *getPROJContext()
159 : {
160 985422 : 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 4449770 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 4449770 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 4449770 : }
184 :
185 4449780 : ~OptionalLockGuard()
186 4449780 : {
187 4449780 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 4449780 : }
190 : };
191 :
192 4449800 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 4449800 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 : #define TAKE_OPTIONAL_LOCK() \
199 : auto lock = d->GetOptionalLockGuard(); \
200 : CPL_IGNORE_RET_VAL(lock)
201 :
202 194838 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
203 : {
204 : const char *pszDefaultAMS =
205 194838 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
206 194888 : if (pszDefaultAMS)
207 : {
208 1 : if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
209 0 : return OAMS_AUTHORITY_COMPLIANT;
210 1 : else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
211 1 : return OAMS_TRADITIONAL_GIS_ORDER;
212 : else
213 : {
214 0 : CPLError(CE_Failure, CPLE_AppDefined,
215 : "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
216 : pszDefaultAMS);
217 : }
218 : }
219 194887 : return OAMS_AUTHORITY_COMPLIANT;
220 : }
221 :
222 194865 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
223 : : m_poSelf(poSelf),
224 194865 : m_poListener(std::shared_ptr<Listener>(new Listener(this)))
225 : {
226 : // Get the default value for m_axisMappingStrategy from the
227 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
228 194758 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
229 194888 : }
230 :
231 776355 : OGRSpatialReference::Private::~Private()
232 : {
233 : // In case we destroy the object not in the thread that created it,
234 : // we need to reassign the PROJ context. Having the context bundled inside
235 : // PJ* deeply sucks...
236 194105 : auto ctxt = getPROJContext();
237 :
238 194107 : proj_assign_context(m_pj_crs, ctxt);
239 194107 : proj_destroy(m_pj_crs);
240 :
241 194107 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
242 194107 : proj_destroy(m_pj_geod_base_crs_temp);
243 :
244 194107 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
245 194107 : proj_destroy(m_pj_proj_crs_cs_temp);
246 :
247 194107 : proj_assign_context(m_pj_bound_crs_target, ctxt);
248 194107 : proj_destroy(m_pj_bound_crs_target);
249 :
250 194107 : proj_assign_context(m_pj_bound_crs_co, ctxt);
251 194107 : proj_destroy(m_pj_bound_crs_co);
252 :
253 194107 : proj_assign_context(m_pj_crs_backup, ctxt);
254 194107 : proj_destroy(m_pj_crs_backup);
255 :
256 194105 : delete m_poRootBackup;
257 194107 : delete m_poRoot;
258 194087 : }
259 :
260 98354 : void OGRSpatialReference::Private::clear()
261 : {
262 98354 : proj_assign_context(m_pj_crs, getPROJContext());
263 98355 : proj_destroy(m_pj_crs);
264 98355 : m_pj_crs = nullptr;
265 :
266 98355 : delete m_poRoot;
267 98355 : m_poRoot = nullptr;
268 98355 : m_bNodesChanged = false;
269 :
270 98355 : m_wktImportWarnings.clear();
271 98354 : m_wktImportErrors.clear();
272 :
273 98353 : m_pj_crs_modified_during_demote = false;
274 98353 : m_pjType = PJ_TYPE_UNKNOWN;
275 98353 : m_osPrimeMeridianName.clear();
276 98354 : m_osAngularUnits.clear();
277 98353 : m_osLinearUnits.clear();
278 :
279 98353 : bNormInfoSet = FALSE;
280 98353 : dfFromGreenwich = 1.0;
281 98353 : dfToMeter = 1.0;
282 98353 : dfToDegrees = 1.0;
283 98353 : m_dfAngularUnitToRadian = 0.0;
284 :
285 98353 : m_bMorphToESRI = false;
286 98353 : m_bHasCenterLong = false;
287 :
288 98353 : m_coordinateEpoch = 0.0;
289 98353 : }
290 :
291 22591 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
292 : {
293 22591 : m_poRoot = poRoot;
294 22591 : if (m_poRoot)
295 : {
296 22591 : m_poRoot->RegisterListener(m_poListener);
297 : }
298 22591 : nodesChanged();
299 22591 : }
300 :
301 157129 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
302 : bool doRefreshAxisMapping)
303 : {
304 157129 : auto ctxt = getPROJContext();
305 :
306 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
307 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
308 : {
309 : const double dfEpoch =
310 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
311 : if (!std::isnan(dfEpoch))
312 : {
313 : m_poSelf->SetCoordinateEpoch(dfEpoch);
314 : }
315 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
316 : proj_destroy(pj_crsIn);
317 : pj_crsIn = crs;
318 : }
319 : #endif
320 :
321 157130 : proj_assign_context(m_pj_crs, ctxt);
322 157130 : proj_destroy(m_pj_crs);
323 157130 : m_pj_crs = pj_crsIn;
324 157130 : if (m_pj_crs)
325 : {
326 157076 : m_pjType = proj_get_type(m_pj_crs);
327 : }
328 157129 : if (m_pj_crs_backup)
329 : {
330 19 : m_pj_crs_modified_during_demote = true;
331 : }
332 157129 : invalidateNodes();
333 157130 : if (doRefreshAxisMapping)
334 : {
335 157112 : refreshAxisMapping();
336 : }
337 157127 : }
338 :
339 589152 : void OGRSpatialReference::Private::refreshProjObj()
340 : {
341 589152 : if (m_bNodesChanged && m_poRoot)
342 : {
343 6897 : char *pszWKT = nullptr;
344 6897 : m_poRoot->exportToWkt(&pszWKT);
345 6897 : auto poRootBackup = m_poRoot;
346 6897 : m_poRoot = nullptr;
347 6897 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
348 6897 : clear();
349 6897 : m_coordinateEpoch = dfCoordinateEpochBackup;
350 6897 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
351 :
352 6897 : const char *const options[] = {
353 : "STRICT=NO",
354 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
355 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
356 : #endif
357 : nullptr
358 : };
359 6897 : PROJ_STRING_LIST warnings = nullptr;
360 6897 : PROJ_STRING_LIST errors = nullptr;
361 6897 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
362 : &warnings, &errors));
363 13499 : for (auto iter = warnings; iter && *iter; ++iter)
364 : {
365 6602 : m_wktImportWarnings.push_back(*iter);
366 : }
367 7087 : for (auto iter = errors; iter && *iter; ++iter)
368 : {
369 190 : m_wktImportErrors.push_back(*iter);
370 : }
371 6897 : proj_string_list_destroy(warnings);
372 6897 : proj_string_list_destroy(errors);
373 :
374 6897 : CPLFree(pszWKT);
375 :
376 6897 : m_poRoot = poRootBackup;
377 6897 : m_bNodesChanged = false;
378 : }
379 589152 : }
380 :
381 24686 : void OGRSpatialReference::Private::refreshRootFromProjObj()
382 : {
383 24686 : CPLAssert(m_poRoot == nullptr);
384 :
385 24686 : if (m_pj_crs)
386 : {
387 45098 : CPLStringList aosOptions;
388 22549 : if (!m_bMorphToESRI)
389 : {
390 22545 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
391 22545 : aosOptions.SetNameValue("MULTILINE", "NO");
392 : }
393 22549 : aosOptions.SetNameValue("STRICT", "NO");
394 :
395 : const char *pszWKT;
396 : {
397 22549 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
398 22549 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
399 22549 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
400 22549 : aosOptions.List());
401 22549 : m_bNodesWKT2 = false;
402 : }
403 22549 : if (!m_bMorphToESRI && pszWKT == nullptr)
404 : {
405 53 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
406 53 : aosOptions.List());
407 53 : m_bNodesWKT2 = true;
408 : }
409 22549 : if (pszWKT)
410 : {
411 22549 : auto root = new OGR_SRSNode();
412 22549 : setRoot(root);
413 22549 : root->importFromWkt(&pszWKT);
414 22549 : m_bNodesChanged = false;
415 : }
416 : }
417 24686 : }
418 :
419 181376 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
420 : {
421 181376 : const char *pszName1 = nullptr;
422 181376 : const char *pszDirection1 = nullptr;
423 181376 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
424 : nullptr, nullptr, nullptr, nullptr);
425 181374 : const char *pszName2 = nullptr;
426 181374 : const char *pszDirection2 = nullptr;
427 181374 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
428 : nullptr, nullptr, nullptr, nullptr);
429 181377 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
430 81337 : EQUAL(pszDirection2, "east"))
431 : {
432 80775 : return true;
433 : }
434 100602 : if (pszDirection1 && pszDirection2 &&
435 100601 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
436 100056 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
437 1081 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
438 249 : STARTS_WITH_CI(pszName2, "easting"))
439 : {
440 249 : return true;
441 : }
442 100353 : return false;
443 : }
444 :
445 233437 : void OGRSpatialReference::Private::refreshAxisMapping()
446 : {
447 233437 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
448 52405 : return;
449 :
450 181032 : bool doUndoDemote = false;
451 181032 : if (m_pj_crs_backup == nullptr)
452 : {
453 181013 : doUndoDemote = true;
454 181013 : demoteFromBoundCRS();
455 : }
456 181032 : const auto ctxt = getPROJContext();
457 181033 : PJ *horizCRS = nullptr;
458 181033 : int axisCount = 0;
459 181033 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
460 : {
461 218 : axisCount = 1;
462 : }
463 180815 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
464 : {
465 1061 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
466 1061 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
467 : {
468 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
469 222 : if (baseCRS)
470 : {
471 222 : proj_destroy(horizCRS);
472 222 : horizCRS = baseCRS;
473 : }
474 : }
475 :
476 1061 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
477 1061 : if (vertCRS)
478 : {
479 1058 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
480 : {
481 373 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
482 373 : if (baseCRS)
483 : {
484 373 : proj_destroy(vertCRS);
485 373 : vertCRS = baseCRS;
486 : }
487 : }
488 :
489 1058 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
490 1058 : if (cs)
491 : {
492 1058 : axisCount += proj_cs_get_axis_count(ctxt, cs);
493 1058 : proj_destroy(cs);
494 : }
495 1058 : proj_destroy(vertCRS);
496 : }
497 : }
498 : else
499 : {
500 179754 : horizCRS = m_pj_crs;
501 : }
502 :
503 181033 : bool bSwitchForGisFriendlyOrder = false;
504 181033 : if (horizCRS)
505 : {
506 180812 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
507 180812 : if (cs)
508 : {
509 180812 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
510 180812 : axisCount += nHorizCSAxisCount;
511 180812 : if (nHorizCSAxisCount >= 2)
512 : {
513 180802 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
514 : }
515 180809 : proj_destroy(cs);
516 : }
517 : }
518 181032 : if (horizCRS != m_pj_crs)
519 : {
520 1279 : proj_destroy(horizCRS);
521 : }
522 181032 : if (doUndoDemote)
523 : {
524 181012 : undoDemoteFromBoundCRS();
525 : }
526 :
527 181032 : m_axisMapping.resize(axisCount);
528 181031 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
529 57302 : !bSwitchForGisFriendlyOrder)
530 : {
531 467868 : for (int i = 0; i < axisCount; i++)
532 : {
533 312356 : m_axisMapping[i] = i + 1;
534 155512 : }
535 : }
536 : else
537 : {
538 25519 : m_axisMapping[0] = 2;
539 25519 : m_axisMapping[1] = 1;
540 25519 : if (axisCount == 3)
541 : {
542 326 : m_axisMapping[2] = 3;
543 : }
544 : }
545 : }
546 :
547 1786790 : void OGRSpatialReference::Private::nodesChanged()
548 : {
549 1786790 : m_bNodesChanged = true;
550 1786790 : }
551 :
552 157429 : void OGRSpatialReference::Private::invalidateNodes()
553 : {
554 157429 : delete m_poRoot;
555 157429 : m_poRoot = nullptr;
556 157429 : m_bNodesChanged = false;
557 157429 : }
558 :
559 299 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
560 : {
561 299 : invalidateNodes();
562 299 : m_bMorphToESRI = b;
563 299 : }
564 :
565 514679 : void OGRSpatialReference::Private::demoteFromBoundCRS()
566 : {
567 514679 : CPLAssert(m_pj_bound_crs_target == nullptr);
568 514679 : CPLAssert(m_pj_bound_crs_co == nullptr);
569 514679 : CPLAssert(m_poRootBackup == nullptr);
570 514679 : CPLAssert(m_pj_crs_backup == nullptr);
571 :
572 514679 : m_pj_crs_modified_during_demote = false;
573 :
574 514679 : if (m_pjType == PJ_TYPE_BOUND_CRS)
575 : {
576 2841 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
577 2841 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
578 2841 : m_pj_bound_crs_co =
579 2841 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
580 :
581 2841 : m_poRootBackup = m_poRoot;
582 2841 : m_poRoot = nullptr;
583 2841 : m_pj_crs_backup = m_pj_crs;
584 2841 : m_pj_crs = baseCRS;
585 2841 : m_pjType = proj_get_type(m_pj_crs);
586 : }
587 514679 : }
588 :
589 514678 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
590 : {
591 514678 : if (m_pj_bound_crs_target)
592 : {
593 2841 : CPLAssert(m_poRoot == nullptr);
594 2841 : CPLAssert(m_pj_crs);
595 2841 : if (!m_pj_crs_modified_during_demote)
596 : {
597 2823 : proj_destroy(m_pj_crs);
598 2823 : m_pj_crs = m_pj_crs_backup;
599 2823 : m_pjType = proj_get_type(m_pj_crs);
600 2823 : m_poRoot = m_poRootBackup;
601 : }
602 : else
603 : {
604 18 : delete m_poRootBackup;
605 18 : m_poRootBackup = nullptr;
606 18 : proj_destroy(m_pj_crs_backup);
607 18 : m_pj_crs_backup = nullptr;
608 18 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
609 18 : m_pj_bound_crs_target,
610 18 : m_pj_bound_crs_co),
611 : false);
612 : }
613 : }
614 :
615 514678 : m_poRootBackup = nullptr;
616 514678 : m_pj_crs_backup = nullptr;
617 514678 : proj_destroy(m_pj_bound_crs_target);
618 514679 : m_pj_bound_crs_target = nullptr;
619 514679 : proj_destroy(m_pj_bound_crs_co);
620 514676 : m_pj_bound_crs_co = nullptr;
621 514676 : m_pj_crs_modified_during_demote = false;
622 514676 : }
623 :
624 109143 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
625 : const char *pszTargetKey)
626 : {
627 109143 : if (pszTargetKey)
628 : {
629 51405 : demoteFromBoundCRS();
630 51405 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
631 25272 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
632 26207 : EQUAL(pszTargetKey, "GEOGCS"))
633 : {
634 5785 : pszTargetKey = nullptr;
635 : }
636 45620 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
637 20 : EQUAL(pszTargetKey, "GEOCCS"))
638 : {
639 0 : pszTargetKey = nullptr;
640 : }
641 45620 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
642 23895 : EQUAL(pszTargetKey, "PROJCS"))
643 : {
644 3416 : pszTargetKey = nullptr;
645 : }
646 42204 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
647 4 : EQUAL(pszTargetKey, "VERT_CS"))
648 : {
649 2 : pszTargetKey = nullptr;
650 : }
651 51405 : undoDemoteFromBoundCRS();
652 : }
653 109143 : return pszTargetKey;
654 : }
655 :
656 7947 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
657 : {
658 7947 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
659 7876 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
660 : {
661 71 : return m_pj_crs;
662 : }
663 :
664 7876 : auto ctxt = getPROJContext();
665 7876 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
666 : {
667 3550 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
668 3550 : proj_destroy(m_pj_geod_base_crs_temp);
669 3550 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
670 3550 : return m_pj_geod_base_crs_temp;
671 : }
672 :
673 4326 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
674 4326 : proj_destroy(m_pj_geod_base_crs_temp);
675 4326 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
676 : nullptr, 0);
677 4326 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
678 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
679 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
680 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
681 4326 : proj_destroy(cs);
682 :
683 4326 : return m_pj_geod_base_crs_temp;
684 : }
685 :
686 4523 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
687 : {
688 4523 : auto ctxt = getPROJContext();
689 4523 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
690 : {
691 3537 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
692 3537 : proj_destroy(m_pj_proj_crs_cs_temp);
693 3537 : m_pj_proj_crs_cs_temp =
694 3537 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
695 3537 : return m_pj_proj_crs_cs_temp;
696 : }
697 :
698 986 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
699 986 : proj_destroy(m_pj_proj_crs_cs_temp);
700 986 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
701 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
702 986 : return m_pj_proj_crs_cs_temp;
703 : }
704 :
705 4573 : const char *OGRSpatialReference::Private::getProjCRSName()
706 : {
707 4573 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
708 : {
709 3551 : return proj_get_name(m_pj_crs);
710 : }
711 :
712 1022 : return "unnamed";
713 : }
714 :
715 1362 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
716 : {
717 1362 : refreshProjObj();
718 :
719 1362 : demoteFromBoundCRS();
720 :
721 : auto projCRS =
722 1362 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
723 1362 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
724 1362 : proj_destroy(conv);
725 :
726 1362 : setPjCRS(projCRS);
727 :
728 1362 : undoDemoteFromBoundCRS();
729 1362 : return OGRERR_NONE;
730 : }
731 :
732 : /************************************************************************/
733 : /* ToPointer() */
734 : /************************************************************************/
735 :
736 24040 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
737 : {
738 24040 : return OGRSpatialReference::FromHandle(hSRS);
739 : }
740 :
741 : /************************************************************************/
742 : /* ToHandle() */
743 : /************************************************************************/
744 :
745 4004 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
746 : {
747 4004 : return OGRSpatialReference::ToHandle(poSRS);
748 : }
749 :
750 : /************************************************************************/
751 : /* OGRsnPrintDouble() */
752 : /************************************************************************/
753 :
754 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
755 :
756 128 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
757 :
758 : {
759 128 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
760 :
761 128 : const size_t nLen = strlen(pszStrBuf);
762 :
763 : // The following hack is intended to truncate some "precision" in cases
764 : // that appear to be roundoff error.
765 128 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
766 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
767 : {
768 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
769 : }
770 :
771 : // Force to user periods regardless of locale.
772 128 : if (strchr(pszStrBuf, ',') != nullptr)
773 : {
774 0 : char *const pszDelim = strchr(pszStrBuf, ',');
775 0 : *pszDelim = '.';
776 : }
777 128 : }
778 :
779 : /************************************************************************/
780 : /* OGRSpatialReference() */
781 : /************************************************************************/
782 :
783 : /**
784 : * \brief Constructor.
785 : *
786 : * This constructor takes an optional string argument which if passed
787 : * should be a WKT representation of an SRS. Passing this is equivalent
788 : * to not passing it, and then calling importFromWkt() with the WKT string.
789 : *
790 : * Note that newly created objects are given a reference count of one.
791 : *
792 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
793 : * object are assumed to be in the order of the axis of the CRS definition
794 : (which
795 : * for example means latitude first, longitude second for geographic CRS
796 : belonging
797 : * to the EPSG authority). It is possible to define a data axis to CRS axis
798 : * mapping strategy with the SetAxisMappingStrategy() method.
799 : *
800 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
801 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
802 : later
803 : * being the default value when the option is not set) to control the value of
804 : the
805 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
806 : * created. Calling SetAxisMappingStrategy() will override this default value.
807 :
808 : * The C function OSRNewSpatialReference() does the same thing as this
809 : * constructor.
810 : *
811 : * @param pszWKT well known text definition to which the object should
812 : * be initialized, or NULL (the default).
813 : */
814 :
815 192655 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
816 192655 : : d(new Private(this))
817 : {
818 192614 : if (pszWKT != nullptr)
819 1066 : importFromWkt(pszWKT);
820 192614 : }
821 :
822 : /************************************************************************/
823 : /* OSRNewSpatialReference() */
824 : /************************************************************************/
825 :
826 : /**
827 : * \brief Constructor.
828 : *
829 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
830 : * object are assumed to be in the order of the axis of the CRS definition
831 : * (which for example means latitude first, longitude second for geographic CRS
832 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
833 : * axis mapping strategy with the SetAxisMappingStrategy() method.
834 : *
835 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
836 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
837 : * later being the default value when the option is not set) to control the
838 : * value of the data axis to CRS axis mapping strategy when a
839 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
840 : * override this default value.
841 : *
842 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
843 : */
844 2813 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
845 :
846 : {
847 2813 : OGRSpatialReference *poSRS = new OGRSpatialReference();
848 :
849 2813 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
850 : {
851 306 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
852 : {
853 1 : delete poSRS;
854 1 : poSRS = nullptr;
855 : }
856 : }
857 :
858 2813 : return ToHandle(poSRS);
859 : }
860 :
861 : /************************************************************************/
862 : /* OGRSpatialReference() */
863 : /************************************************************************/
864 :
865 : /** Copy constructor. See also Clone().
866 : * @param oOther other spatial reference
867 : */
868 2205 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
869 2205 : : d(new Private(this))
870 : {
871 2205 : *this = oOther;
872 2205 : }
873 :
874 : /************************************************************************/
875 : /* OGRSpatialReference() */
876 : /************************************************************************/
877 :
878 : /** Move constructor.
879 : * @param oOther other spatial reference
880 : */
881 28 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
882 28 : : d(std::move(oOther.d))
883 : {
884 28 : }
885 :
886 : /************************************************************************/
887 : /* ~OGRSpatialReference() */
888 : /************************************************************************/
889 :
890 : /**
891 : * \brief OGRSpatialReference destructor.
892 : *
893 : * The C function OSRDestroySpatialReference() does the same thing as this
894 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
895 : *
896 : * @deprecated
897 : */
898 :
899 246570 : OGRSpatialReference::~OGRSpatialReference()
900 :
901 : {
902 246558 : }
903 :
904 : /************************************************************************/
905 : /* DestroySpatialReference() */
906 : /************************************************************************/
907 :
908 : /**
909 : * \brief OGRSpatialReference destructor.
910 : *
911 : * This static method will destroy a OGRSpatialReference. It is
912 : * equivalent to calling delete on the object, but it ensures that the
913 : * deallocation is properly executed within the OGR libraries heap on
914 : * platforms where this can matter (win32).
915 : *
916 : * This function is the same as OSRDestroySpatialReference()
917 : *
918 : * @param poSRS the object to delete
919 : *
920 : * @since GDAL 1.7.0
921 : */
922 :
923 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
924 : {
925 0 : delete poSRS;
926 0 : }
927 :
928 : /************************************************************************/
929 : /* OSRDestroySpatialReference() */
930 : /************************************************************************/
931 :
932 : /**
933 : * \brief OGRSpatialReference destructor.
934 : *
935 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
936 : * and OGRSpatialReference::DestroySpatialReference()
937 : *
938 : * @param hSRS the object to delete
939 : */
940 8039 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
941 :
942 : {
943 8039 : delete ToPointer(hSRS);
944 8039 : }
945 :
946 : /************************************************************************/
947 : /* Clear() */
948 : /************************************************************************/
949 :
950 : /**
951 : * \brief Wipe current definition.
952 : *
953 : * Returns OGRSpatialReference to a state with no definition, as it
954 : * exists when first created. It does not affect reference counts.
955 : */
956 :
957 91455 : void OGRSpatialReference::Clear()
958 :
959 : {
960 91455 : d->clear();
961 91456 : }
962 :
963 : /************************************************************************/
964 : /* operator=() */
965 : /************************************************************************/
966 :
967 : /** Assignment operator.
968 : * @param oSource SRS to assign to *this
969 : * @return *this
970 : */
971 : OGRSpatialReference &
972 22695 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
973 :
974 : {
975 22695 : if (&oSource != this)
976 : {
977 22694 : Clear();
978 : #ifdef CPPCHECK
979 : // Otherwise cppcheck would protest that nRefCount isn't modified
980 : d->nRefCount = (d->nRefCount + 1) - 1;
981 : #endif
982 :
983 22694 : oSource.d->refreshProjObj();
984 22694 : if (oSource.d->m_pj_crs)
985 22405 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
986 22693 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
987 10405 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
988 12289 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
989 110 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
990 :
991 22694 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
992 : }
993 :
994 22695 : return *this;
995 : }
996 :
997 : /************************************************************************/
998 : /* operator=() */
999 : /************************************************************************/
1000 :
1001 : /** Move assignment operator.
1002 : * @param oSource SRS to assign to *this
1003 : * @return *this
1004 : */
1005 : OGRSpatialReference &
1006 3934 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1007 :
1008 : {
1009 3934 : if (&oSource != this)
1010 : {
1011 3933 : d = std::move(oSource.d);
1012 : }
1013 :
1014 3934 : return *this;
1015 : }
1016 :
1017 : /************************************************************************/
1018 : /* AssignAndSetThreadSafe() */
1019 : /************************************************************************/
1020 :
1021 : /** Assignment method, with thread-safety.
1022 : *
1023 : * Same as an assignment operator, but asking also that the *this instance
1024 : * becomes thread-safe.
1025 : *
1026 : * @param oSource SRS to assign to *this
1027 : * @return *this
1028 : * @since 3.10
1029 : */
1030 :
1031 : OGRSpatialReference &
1032 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1033 : {
1034 2 : *this = oSource;
1035 2 : d->SetThreadSafe();
1036 2 : return *this;
1037 : }
1038 :
1039 : /************************************************************************/
1040 : /* Reference() */
1041 : /************************************************************************/
1042 :
1043 : /**
1044 : * \brief Increments the reference count by one.
1045 : *
1046 : * The reference count is used keep track of the number of OGRGeometry objects
1047 : * referencing this SRS.
1048 : *
1049 : * The method does the same thing as the C function OSRReference().
1050 : *
1051 : * @return the updated reference count.
1052 : */
1053 :
1054 4024380 : int OGRSpatialReference::Reference()
1055 :
1056 : {
1057 4024380 : return CPLAtomicInc(&d->nRefCount);
1058 : }
1059 :
1060 : /************************************************************************/
1061 : /* OSRReference() */
1062 : /************************************************************************/
1063 :
1064 : /**
1065 : * \brief Increments the reference count by one.
1066 : *
1067 : * This function is the same as OGRSpatialReference::Reference()
1068 : */
1069 923 : int OSRReference(OGRSpatialReferenceH hSRS)
1070 :
1071 : {
1072 923 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1073 :
1074 923 : return ToPointer(hSRS)->Reference();
1075 : }
1076 :
1077 : /************************************************************************/
1078 : /* Dereference() */
1079 : /************************************************************************/
1080 :
1081 : /**
1082 : * \brief Decrements the reference count by one.
1083 : *
1084 : * The method does the same thing as the C function OSRDereference().
1085 : *
1086 : * @return the updated reference count.
1087 : */
1088 :
1089 4062340 : int OGRSpatialReference::Dereference()
1090 :
1091 : {
1092 4062340 : if (d->nRefCount <= 0)
1093 0 : CPLDebug("OSR",
1094 : "Dereference() called on an object with refcount %d,"
1095 : "likely already destroyed!",
1096 0 : d->nRefCount);
1097 4062340 : return CPLAtomicDec(&d->nRefCount);
1098 : }
1099 :
1100 : /************************************************************************/
1101 : /* OSRDereference() */
1102 : /************************************************************************/
1103 :
1104 : /**
1105 : * \brief Decrements the reference count by one.
1106 : *
1107 : * This function is the same as OGRSpatialReference::Dereference()
1108 : */
1109 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1110 :
1111 : {
1112 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1113 :
1114 0 : return ToPointer(hSRS)->Dereference();
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* GetReferenceCount() */
1119 : /************************************************************************/
1120 :
1121 : /**
1122 : * \brief Fetch current reference count.
1123 : *
1124 : * @return the current reference count.
1125 : */
1126 176 : int OGRSpatialReference::GetReferenceCount() const
1127 : {
1128 176 : return d->nRefCount;
1129 : }
1130 :
1131 : /************************************************************************/
1132 : /* Release() */
1133 : /************************************************************************/
1134 :
1135 : /**
1136 : * \brief Decrements the reference count by one, and destroy if zero.
1137 : *
1138 : * The method does the same thing as the C function OSRRelease().
1139 : */
1140 :
1141 4059580 : void OGRSpatialReference::Release()
1142 :
1143 : {
1144 4059580 : if (Dereference() <= 0)
1145 37902 : delete this;
1146 4059580 : }
1147 :
1148 : /************************************************************************/
1149 : /* OSRRelease() */
1150 : /************************************************************************/
1151 :
1152 : /**
1153 : * \brief Decrements the reference count by one, and destroy if zero.
1154 : *
1155 : * This function is the same as OGRSpatialReference::Release()
1156 : */
1157 6634 : void OSRRelease(OGRSpatialReferenceH hSRS)
1158 :
1159 : {
1160 6634 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1161 :
1162 6634 : ToPointer(hSRS)->Release();
1163 : }
1164 :
1165 78954 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1166 : {
1167 78954 : TAKE_OPTIONAL_LOCK();
1168 :
1169 78954 : if (!d->m_poRoot)
1170 : {
1171 22219 : d->refreshRootFromProjObj();
1172 : }
1173 157908 : return d->m_poRoot;
1174 : }
1175 :
1176 7150 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1177 : {
1178 7150 : TAKE_OPTIONAL_LOCK();
1179 :
1180 7150 : if (!d->m_poRoot)
1181 : {
1182 2467 : d->refreshRootFromProjObj();
1183 : }
1184 14300 : return d->m_poRoot;
1185 : }
1186 :
1187 : /************************************************************************/
1188 : /* SetRoot() */
1189 : /************************************************************************/
1190 :
1191 : /**
1192 : * \brief Set the root SRS node.
1193 : *
1194 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1195 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1196 : * is assumed by the OGRSpatialReference.
1197 : *
1198 : * @param poNewRoot object to assign as root.
1199 : */
1200 :
1201 42 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1202 :
1203 : {
1204 42 : if (d->m_poRoot != poNewRoot)
1205 : {
1206 42 : delete d->m_poRoot;
1207 42 : d->setRoot(poNewRoot);
1208 : }
1209 42 : }
1210 :
1211 : /************************************************************************/
1212 : /* GetAttrNode() */
1213 : /************************************************************************/
1214 :
1215 : /**
1216 : * \brief Find named node in tree.
1217 : *
1218 : * This method does a pre-order traversal of the node tree searching for
1219 : * a node with this exact value (case insensitive), and returns it. Leaf
1220 : * nodes are not considered, under the assumption that they are just
1221 : * attribute value nodes.
1222 : *
1223 : * If a node appears more than once in the tree (such as UNIT for instance),
1224 : * the first encountered will be returned. Use GetNode() on a subtree to be
1225 : * more specific.
1226 : *
1227 : * @param pszNodePath the name of the node to search for. May contain multiple
1228 : * components such as "GEOGCS|UNIT".
1229 : *
1230 : * @return a pointer to the node found, or NULL if none.
1231 : */
1232 :
1233 75961 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1234 :
1235 : {
1236 75961 : if (strchr(pszNodePath, '|') == nullptr)
1237 : {
1238 : // Fast path
1239 41177 : OGR_SRSNode *poNode = GetRoot();
1240 41177 : if (poNode)
1241 39990 : poNode = poNode->GetNode(pszNodePath);
1242 41177 : return poNode;
1243 : }
1244 :
1245 : char **papszPathTokens =
1246 34784 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1247 :
1248 34784 : if (CSLCount(papszPathTokens) < 1)
1249 : {
1250 0 : CSLDestroy(papszPathTokens);
1251 0 : return nullptr;
1252 : }
1253 :
1254 34784 : OGR_SRSNode *poNode = GetRoot();
1255 104368 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1256 : {
1257 69584 : poNode = poNode->GetNode(papszPathTokens[i]);
1258 : }
1259 :
1260 34784 : CSLDestroy(papszPathTokens);
1261 :
1262 34784 : return poNode;
1263 : }
1264 :
1265 : /**
1266 : * \brief Find named node in tree.
1267 : *
1268 : * This method does a pre-order traversal of the node tree searching for
1269 : * a node with this exact value (case insensitive), and returns it. Leaf
1270 : * nodes are not considered, under the assumption that they are just
1271 : * attribute value nodes.
1272 : *
1273 : * If a node appears more than once in the tree (such as UNIT for instance),
1274 : * the first encountered will be returned. Use GetNode() on a subtree to be
1275 : * more specific.
1276 : *
1277 : * @param pszNodePath the name of the node to search for. May contain multiple
1278 : * components such as "GEOGCS|UNIT".
1279 : *
1280 : * @return a pointer to the node found, or NULL if none.
1281 : */
1282 :
1283 : const OGR_SRSNode *
1284 69288 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1285 :
1286 : {
1287 : OGR_SRSNode *poNode =
1288 69288 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1289 :
1290 69288 : return poNode;
1291 : }
1292 :
1293 : /************************************************************************/
1294 : /* GetAttrValue() */
1295 : /************************************************************************/
1296 :
1297 : /**
1298 : * \brief Fetch indicated attribute of named node.
1299 : *
1300 : * This method uses GetAttrNode() to find the named node, and then extracts
1301 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1302 : * would return the second child of the UNIT node, which is normally the
1303 : * length of the linear unit in meters.
1304 : *
1305 : * This method does the same thing as the C function OSRGetAttrValue().
1306 : *
1307 : * @param pszNodeName the tree node to look for (case insensitive).
1308 : * @param iAttr the child of the node to fetch (zero based).
1309 : *
1310 : * @return the requested value, or NULL if it fails for any reason.
1311 : */
1312 :
1313 21109 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1314 : int iAttr) const
1315 :
1316 : {
1317 21109 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1318 21109 : if (poNode == nullptr)
1319 : {
1320 9567 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1321 : {
1322 8 : return GetAttrValue("METHOD", iAttr);
1323 : }
1324 9559 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1325 : {
1326 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1327 : }
1328 9559 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1329 : {
1330 1 : return GetAttrValue("PROJCRS", iAttr);
1331 : }
1332 9558 : return nullptr;
1333 : }
1334 :
1335 11542 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1336 0 : return nullptr;
1337 :
1338 11542 : return poNode->GetChild(iAttr)->GetValue();
1339 : }
1340 :
1341 : /************************************************************************/
1342 : /* OSRGetAttrValue() */
1343 : /************************************************************************/
1344 :
1345 : /**
1346 : * \brief Fetch indicated attribute of named node.
1347 : *
1348 : * This function is the same as OGRSpatialReference::GetAttrValue()
1349 : */
1350 62 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1351 : const char *pszKey, int iChild)
1352 :
1353 : {
1354 62 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1355 :
1356 62 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1357 : }
1358 :
1359 : /************************************************************************/
1360 : /* GetName() */
1361 : /************************************************************************/
1362 :
1363 : /**
1364 : * \brief Return the CRS name.
1365 : *
1366 : * The returned value is only short lived and should not be used after other
1367 : * calls to methods on this object.
1368 : *
1369 : * @since GDAL 3.0
1370 : */
1371 :
1372 4960 : const char *OGRSpatialReference::GetName() const
1373 : {
1374 9920 : TAKE_OPTIONAL_LOCK();
1375 :
1376 4960 : d->refreshProjObj();
1377 4960 : if (!d->m_pj_crs)
1378 113 : return nullptr;
1379 4847 : const char *pszName = proj_get_name(d->m_pj_crs);
1380 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1381 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1382 : {
1383 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1384 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1385 : if (baseCRS)
1386 : {
1387 : pszName = proj_get_name(baseCRS);
1388 : // pszName still remains valid after proj_destroy(), since
1389 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1390 : proj_destroy(baseCRS);
1391 : }
1392 : }
1393 : #endif
1394 4847 : return pszName;
1395 : }
1396 :
1397 : /************************************************************************/
1398 : /* OSRGetName() */
1399 : /************************************************************************/
1400 :
1401 : /**
1402 : * \brief Return the CRS name.
1403 : *
1404 : * The returned value is only short lived and should not be used after other
1405 : * calls to methods on this object.
1406 : *
1407 : * @since GDAL 3.0
1408 : */
1409 39 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1410 :
1411 : {
1412 39 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1413 :
1414 39 : return ToPointer(hSRS)->GetName();
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* Clone() */
1419 : /************************************************************************/
1420 :
1421 : /**
1422 : * \brief Make a duplicate of this OGRSpatialReference.
1423 : *
1424 : * This method is the same as the C function OSRClone().
1425 : *
1426 : * @return a new SRS, which becomes the responsibility of the caller.
1427 : */
1428 :
1429 26948 : OGRSpatialReference *OGRSpatialReference::Clone() const
1430 :
1431 : {
1432 26948 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1433 :
1434 26948 : TAKE_OPTIONAL_LOCK();
1435 :
1436 26948 : d->refreshProjObj();
1437 26949 : if (d->m_pj_crs != nullptr)
1438 26898 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1439 26949 : if (d->m_bHasCenterLong && d->m_poRoot)
1440 : {
1441 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1442 : }
1443 26949 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1444 26947 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1445 26947 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1446 53897 : return poNewRef;
1447 : }
1448 :
1449 : /************************************************************************/
1450 : /* OSRClone() */
1451 : /************************************************************************/
1452 :
1453 : /**
1454 : * \brief Make a duplicate of this OGRSpatialReference.
1455 : *
1456 : * This function is the same as OGRSpatialReference::Clone()
1457 : */
1458 1020 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1459 :
1460 : {
1461 1020 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1462 :
1463 1020 : return ToHandle(ToPointer(hSRS)->Clone());
1464 : }
1465 :
1466 : /************************************************************************/
1467 : /* dumpReadable() */
1468 : /************************************************************************/
1469 :
1470 : /** Dump pretty wkt to stdout, mostly for debugging.
1471 : */
1472 0 : void OGRSpatialReference::dumpReadable()
1473 :
1474 : {
1475 0 : char *pszPrettyWkt = nullptr;
1476 :
1477 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1478 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1479 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1480 0 : CPLFree(pszPrettyWkt);
1481 0 : }
1482 :
1483 : /************************************************************************/
1484 : /* exportToPrettyWkt() */
1485 : /************************************************************************/
1486 :
1487 : /**
1488 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1489 : * person.
1490 : *
1491 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1492 : * Issues</a> page for implementation details of WKT 1 in OGR.
1493 : *
1494 : * Note that the returned WKT string should be freed with
1495 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1496 : *
1497 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1498 : * option. Valid values are the one of the FORMAT option of
1499 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1500 : *
1501 : * This method is the same as the C function OSRExportToPrettyWkt().
1502 : *
1503 : * @param ppszResult the resulting string is returned in this pointer.
1504 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1505 : * stripped off.
1506 : *
1507 : * @return OGRERR_NONE if successful.
1508 : */
1509 :
1510 57 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1511 : int bSimplify) const
1512 :
1513 : {
1514 114 : CPLStringList aosOptions;
1515 57 : aosOptions.SetNameValue("MULTILINE", "YES");
1516 57 : if (bSimplify)
1517 : {
1518 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1519 : }
1520 114 : return exportToWkt(ppszResult, aosOptions.List());
1521 : }
1522 :
1523 : /************************************************************************/
1524 : /* OSRExportToPrettyWkt() */
1525 : /************************************************************************/
1526 :
1527 : /**
1528 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1529 : * person.
1530 : *
1531 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1532 : * option. Valid values are the one of the FORMAT option of
1533 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1534 : *
1535 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1536 : */
1537 :
1538 55 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1539 : char **ppszReturn, int bSimplify)
1540 :
1541 : {
1542 55 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1543 :
1544 55 : *ppszReturn = nullptr;
1545 :
1546 55 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1547 : }
1548 :
1549 : /************************************************************************/
1550 : /* exportToWkt() */
1551 : /************************************************************************/
1552 :
1553 : /**
1554 : * \brief Convert this SRS into WKT 1 format.
1555 : *
1556 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1557 : * Issues</a> page for implementation details of WKT 1 in OGR.
1558 : *
1559 : * Note that the returned WKT string should be freed with
1560 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1561 : *
1562 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1563 : * option. Valid values are the one of the FORMAT option of
1564 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1565 : *
1566 : * This method is the same as the C function OSRExportToWkt().
1567 : *
1568 : * @param ppszResult the resulting string is returned in this pointer.
1569 : *
1570 : * @return OGRERR_NONE if successful.
1571 : */
1572 :
1573 12992 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1574 :
1575 : {
1576 12992 : return exportToWkt(ppszResult, nullptr);
1577 : }
1578 :
1579 : /************************************************************************/
1580 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1581 : /************************************************************************/
1582 :
1583 559 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1584 : bool onlyIfEPSGCode,
1585 : bool canModifyHorizPart)
1586 : {
1587 559 : PJ *ret = nullptr;
1588 559 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1589 : {
1590 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1591 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1592 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1593 26 : vertCRS &&
1594 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1595 : {
1596 : auto boundHoriz =
1597 : canModifyHorizPart
1598 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1599 3 : : proj_clone(ctx, horizCRS);
1600 : auto boundVert =
1601 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1602 3 : if (boundHoriz && boundVert)
1603 : {
1604 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1605 : boundHoriz, boundVert);
1606 : }
1607 3 : proj_destroy(boundHoriz);
1608 3 : proj_destroy(boundVert);
1609 : }
1610 13 : proj_destroy(horizCRS);
1611 13 : proj_destroy(vertCRS);
1612 : }
1613 1050 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1614 504 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1615 : {
1616 231 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1617 : }
1618 559 : return ret;
1619 : }
1620 :
1621 : /************************************************************************/
1622 : /* exportToWkt() */
1623 : /************************************************************************/
1624 :
1625 : /**
1626 : * Convert this SRS into a WKT string.
1627 : *
1628 : * Note that the returned WKT string should be freed with
1629 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1630 : *
1631 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1632 : * Issues</a> page for implementation details of WKT 1 in OGR.
1633 : *
1634 : * @param ppszResult the resulting string is returned in this pointer.
1635 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1636 : * supported options are
1637 : * <ul>
1638 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1639 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1640 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1641 : * node is returned.
1642 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1643 : * node is returned.
1644 : * WKT1 is an alias of WKT1_GDAL.
1645 : * WKT2 will default to the latest revision implemented (currently
1646 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1647 : * </li>
1648 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1649 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1650 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1651 : * height (for example for use with LAS 1.4 WKT1).
1652 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1653 : * </ul>
1654 : *
1655 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1656 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1657 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1658 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1659 : * TOWGS84[] node may be added.
1660 : *
1661 : * @return OGRERR_NONE if successful.
1662 : * @since GDAL 3.0
1663 : */
1664 :
1665 16959 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1666 : const char *const *papszOptions) const
1667 : {
1668 : // In the past calling this method was thread-safe, even if we never
1669 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1670 : // so this is no longer thread-safe.
1671 33918 : std::lock_guard oLock(d->m_mutex);
1672 :
1673 16959 : d->refreshProjObj();
1674 16959 : if (!d->m_pj_crs)
1675 : {
1676 21 : *ppszResult = CPLStrdup("");
1677 21 : return OGRERR_FAILURE;
1678 : }
1679 :
1680 16938 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1681 : {
1682 0 : return d->m_poRoot->exportToWkt(ppszResult);
1683 : }
1684 :
1685 16938 : auto ctxt = d->getPROJContext();
1686 16938 : auto wktFormat = PJ_WKT1_GDAL;
1687 : const char *pszFormat =
1688 16938 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1689 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1690 16938 : if (EQUAL(pszFormat, "DEFAULT"))
1691 14747 : pszFormat = "";
1692 :
1693 16938 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1694 : {
1695 574 : wktFormat = PJ_WKT1_ESRI;
1696 : }
1697 16364 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1698 15750 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1699 : {
1700 619 : wktFormat = PJ_WKT1_GDAL;
1701 : }
1702 15745 : else if (EQUAL(pszFormat, "WKT2_2015"))
1703 : {
1704 244 : wktFormat = PJ_WKT2_2015;
1705 : }
1706 15501 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1707 15214 : EQUAL(pszFormat, "WKT2_2019"))
1708 : {
1709 1020 : wktFormat = PJ_WKT2_2018;
1710 : }
1711 14481 : else if (pszFormat[0] == '\0')
1712 : {
1713 : // cppcheck-suppress knownConditionTrueFalse
1714 14481 : if (IsDerivedGeographic())
1715 : {
1716 2 : wktFormat = PJ_WKT2_2018;
1717 : }
1718 28328 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1719 13849 : GetAxesCount() == 3)
1720 : {
1721 56 : wktFormat = PJ_WKT2_2018;
1722 : }
1723 : }
1724 : else
1725 : {
1726 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1727 0 : *ppszResult = CPLStrdup("");
1728 0 : return OGRERR_FAILURE;
1729 : }
1730 :
1731 33876 : CPLStringList aosOptions;
1732 16938 : if (wktFormat != PJ_WKT1_ESRI)
1733 : {
1734 16364 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1735 : }
1736 : aosOptions.SetNameValue(
1737 16938 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1738 :
1739 16938 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1740 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1741 16938 : if (pszAllowEllpsHeightAsVertCS)
1742 : {
1743 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1744 0 : pszAllowEllpsHeightAsVertCS);
1745 : }
1746 :
1747 16938 : PJ *boundCRS = nullptr;
1748 31980 : if (wktFormat == PJ_WKT1_GDAL &&
1749 15042 : CPLTestBool(CSLFetchNameValueDef(
1750 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1751 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1752 : {
1753 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1754 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1755 : }
1756 :
1757 33876 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
1758 16938 : CPLInstallErrorHandlerAccumulator(aoErrors);
1759 16938 : const char *pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1760 16938 : wktFormat, aosOptions.List());
1761 16938 : CPLUninstallErrorHandlerAccumulator();
1762 16940 : for (const auto &oError : aoErrors)
1763 : {
1764 32 : if (pszFormat[0] == '\0' &&
1765 14 : (oError.msg.find("Unsupported conversion method") !=
1766 2 : std::string::npos ||
1767 2 : oError.msg.find("can only be exported to WKT2") !=
1768 0 : std::string::npos ||
1769 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1770 : std::string::npos))
1771 : {
1772 14 : CPLErrorReset();
1773 : // If we cannot export in the default mode (WKT1), retry with WKT2
1774 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1775 14 : PJ_WKT2_2018, aosOptions.List());
1776 14 : break;
1777 : }
1778 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1779 : }
1780 :
1781 16938 : if (!pszWKT)
1782 : {
1783 2 : *ppszResult = CPLStrdup("");
1784 2 : proj_destroy(boundCRS);
1785 2 : return OGRERR_FAILURE;
1786 : }
1787 :
1788 16936 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1789 : {
1790 5 : OGR_SRSNode oRoot;
1791 5 : oRoot.importFromWkt(&pszWKT);
1792 5 : oRoot.StripNodes("AXIS");
1793 5 : if (EQUAL(pszFormat, "SFSQL"))
1794 : {
1795 3 : oRoot.StripNodes("TOWGS84");
1796 : }
1797 5 : oRoot.StripNodes("AUTHORITY");
1798 5 : oRoot.StripNodes("EXTENSION");
1799 : OGRErr eErr;
1800 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1801 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1802 : else
1803 3 : eErr = oRoot.exportToWkt(ppszResult);
1804 5 : proj_destroy(boundCRS);
1805 5 : return eErr;
1806 : }
1807 :
1808 16931 : *ppszResult = CPLStrdup(pszWKT);
1809 :
1810 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1811 16931 : if (wktFormat == PJ_WKT2_2018)
1812 : {
1813 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1814 : // related to a wrong EPSG code assigned to UTM South conversions
1815 1078 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1816 1078 : if (pszPtr)
1817 : {
1818 252 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1819 252 : const int nZone = atoi(pszPtr);
1820 755 : while (*pszPtr >= '0' && *pszPtr <= '9')
1821 503 : ++pszPtr;
1822 252 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1823 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1824 : {
1825 1 : pszPtr += 3;
1826 1 : int nLevel = 0;
1827 1 : bool bInString = false;
1828 : // Find the ID node corresponding to this CONVERSION node
1829 480 : while (*pszPtr)
1830 : {
1831 480 : if (bInString)
1832 : {
1833 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1834 : {
1835 0 : ++pszPtr;
1836 : }
1837 197 : else if (*pszPtr == '"')
1838 : {
1839 17 : bInString = false;
1840 : }
1841 : }
1842 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1843 : {
1844 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1845 : 17000 + nZone)))
1846 : {
1847 1 : CPLAssert(pszPtr[11] == '7');
1848 1 : CPLAssert(pszPtr[12] == '0');
1849 1 : pszPtr[11] = '6';
1850 1 : pszPtr[12] = '1';
1851 : }
1852 1 : break;
1853 : }
1854 282 : else if (*pszPtr == '"')
1855 : {
1856 17 : bInString = true;
1857 : }
1858 265 : else if (*pszPtr == '[')
1859 : {
1860 17 : ++nLevel;
1861 : }
1862 248 : else if (*pszPtr == ']')
1863 : {
1864 17 : --nLevel;
1865 : }
1866 :
1867 479 : ++pszPtr;
1868 : }
1869 : }
1870 : }
1871 : }
1872 : #endif
1873 :
1874 16931 : proj_destroy(boundCRS);
1875 16931 : return OGRERR_NONE;
1876 : }
1877 :
1878 : /************************************************************************/
1879 : /* exportToWkt() */
1880 : /************************************************************************/
1881 :
1882 : /**
1883 : * Convert this SRS into a WKT string.
1884 : *
1885 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1886 : * Issues</a> page for implementation details of WKT 1 in OGR.
1887 : *
1888 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1889 : * supported options are
1890 : * <ul>
1891 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1892 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1893 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1894 : * node is returned.
1895 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1896 : * node is returned.
1897 : * WKT1 is an alias of WKT1_GDAL.
1898 : * WKT2 will default to the latest revision implemented (currently
1899 : * WKT2_2019)
1900 : * </li>
1901 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1902 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1903 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1904 : * height (for example for use with LAS 1.4 WKT1).
1905 : * Requires PROJ 7.2.1.</li>
1906 : * </ul>
1907 : *
1908 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1909 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1910 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1911 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1912 : * TOWGS84[] node may be added.
1913 : *
1914 : * @return a non-empty string if successful.
1915 : * @since GDAL 3.9
1916 : */
1917 :
1918 : std::string
1919 92 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1920 : {
1921 92 : std::string osWKT;
1922 92 : char *pszWKT = nullptr;
1923 92 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1924 92 : osWKT = pszWKT;
1925 92 : CPLFree(pszWKT);
1926 184 : return osWKT;
1927 : }
1928 :
1929 : /************************************************************************/
1930 : /* OSRExportToWkt() */
1931 : /************************************************************************/
1932 :
1933 : /**
1934 : * \brief Convert this SRS into WKT 1 format.
1935 : *
1936 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1937 : * Issues</a> page for implementation details of WKT in OGR.
1938 : *
1939 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1940 : * option. Valid values are the one of the FORMAT option of
1941 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1942 : *
1943 : * This function is the same as OGRSpatialReference::exportToWkt().
1944 : */
1945 :
1946 871 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1947 :
1948 : {
1949 871 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1950 :
1951 871 : *ppszReturn = nullptr;
1952 :
1953 871 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
1954 : }
1955 :
1956 : /************************************************************************/
1957 : /* OSRExportToWktEx() */
1958 : /************************************************************************/
1959 :
1960 : /**
1961 : * \brief Convert this SRS into WKT format.
1962 : *
1963 : * This function is the same as OGRSpatialReference::exportToWkt(char **
1964 : * ppszResult,const char* const* papszOptions ) const
1965 : *
1966 : * @since GDAL 3.0
1967 : */
1968 :
1969 1288 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1970 : const char *const *papszOptions)
1971 : {
1972 1288 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1973 :
1974 1288 : *ppszReturn = nullptr;
1975 :
1976 1288 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
1977 : }
1978 :
1979 : /************************************************************************/
1980 : /* exportToPROJJSON() */
1981 : /************************************************************************/
1982 :
1983 : /**
1984 : * Convert this SRS into a PROJJSON string.
1985 : *
1986 : * Note that the returned JSON string should be freed with
1987 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1988 : *
1989 : * @param ppszResult the resulting string is returned in this pointer.
1990 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1991 : * supported options are
1992 : * <ul>
1993 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
1994 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1995 : * on).</li>
1996 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
1997 : * disable it.</li>
1998 : * </ul>
1999 : *
2000 : * @return OGRERR_NONE if successful.
2001 : * @since GDAL 3.1 and PROJ 6.2
2002 : */
2003 :
2004 2142 : OGRErr OGRSpatialReference::exportToPROJJSON(
2005 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2006 : {
2007 4284 : TAKE_OPTIONAL_LOCK();
2008 :
2009 2142 : d->refreshProjObj();
2010 2142 : if (!d->m_pj_crs)
2011 : {
2012 0 : *ppszResult = nullptr;
2013 0 : return OGRERR_FAILURE;
2014 : }
2015 :
2016 : const char *pszPROJJSON =
2017 2142 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2018 :
2019 2142 : if (!pszPROJJSON)
2020 : {
2021 0 : *ppszResult = CPLStrdup("");
2022 0 : return OGRERR_FAILURE;
2023 : }
2024 :
2025 2142 : *ppszResult = CPLStrdup(pszPROJJSON);
2026 :
2027 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2028 : {
2029 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2030 : // related to a wrong EPSG code assigned to UTM South conversions
2031 2142 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2032 2142 : if (pszPtr)
2033 : {
2034 223 : pszPtr += strlen("\"name\": \"UTM zone ");
2035 223 : const int nZone = atoi(pszPtr);
2036 668 : while (*pszPtr >= '0' && *pszPtr <= '9')
2037 445 : ++pszPtr;
2038 223 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2039 : {
2040 3 : pszPtr += 2;
2041 3 : int nLevel = 0;
2042 3 : bool bInString = false;
2043 : // Find the id node corresponding to this conversion node
2044 3966 : while (*pszPtr)
2045 : {
2046 3966 : if (bInString)
2047 : {
2048 1462 : if (*pszPtr == '\\')
2049 : {
2050 0 : ++pszPtr;
2051 : }
2052 1462 : else if (*pszPtr == '"')
2053 : {
2054 183 : bInString = false;
2055 : }
2056 : }
2057 2504 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2058 : {
2059 3 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2060 : const char *pszAuthEPSG =
2061 3 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2062 3 : char *pszCode = strstr(
2063 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2064 3 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2065 3 : pszNextEndCurl - pszAuthEPSG > 0 &&
2066 3 : pszNextEndCurl - pszCode > 0)
2067 : {
2068 3 : CPLAssert(pszCode[9] == '7');
2069 3 : CPLAssert(pszCode[10] == '0');
2070 3 : pszCode[9] = '6';
2071 3 : pszCode[10] = '1';
2072 : }
2073 3 : break;
2074 : }
2075 2501 : else if (*pszPtr == '"')
2076 : {
2077 183 : bInString = true;
2078 : }
2079 2318 : else if (*pszPtr == '{' || *pszPtr == '[')
2080 : {
2081 45 : ++nLevel;
2082 : }
2083 2273 : else if (*pszPtr == '}' || *pszPtr == ']')
2084 : {
2085 45 : --nLevel;
2086 : }
2087 :
2088 3963 : ++pszPtr;
2089 : }
2090 : }
2091 : }
2092 : }
2093 : #endif
2094 :
2095 2142 : return OGRERR_NONE;
2096 : }
2097 :
2098 : /************************************************************************/
2099 : /* OSRExportToPROJJSON() */
2100 : /************************************************************************/
2101 :
2102 : /**
2103 : * \brief Convert this SRS into PROJJSON format.
2104 : *
2105 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2106 : *
2107 : * @since GDAL 3.1 and PROJ 6.2
2108 : */
2109 :
2110 53 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2111 : const char *const *papszOptions)
2112 : {
2113 53 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2114 :
2115 53 : *ppszReturn = nullptr;
2116 :
2117 53 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2118 : }
2119 :
2120 : /************************************************************************/
2121 : /* importFromWkt() */
2122 : /************************************************************************/
2123 :
2124 : /**
2125 : * \brief Import from WKT string.
2126 : *
2127 : * This method will wipe the existing SRS definition, and
2128 : * reassign it based on the contents of the passed WKT string. Only as
2129 : * much of the input string as needed to construct this SRS is consumed from
2130 : * the input string, and the input string pointer
2131 : * is then updated to point to the remaining (unused) input.
2132 : *
2133 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2134 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2135 : * and the coordinate epoch potentially present used as the coordinate epoch
2136 : * property of the OGRSpatialReference object.
2137 : *
2138 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2139 : * Issues</a> page for implementation details of WKT in OGR.
2140 : *
2141 : * This method is the same as the C function OSRImportFromWkt().
2142 : *
2143 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2144 : * point to remaining unused input text.
2145 : *
2146 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2147 : * fails for any reason.
2148 : * @since GDAL 2.3
2149 : */
2150 :
2151 17615 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2152 :
2153 : {
2154 17615 : return importFromWkt(ppszInput, nullptr);
2155 : }
2156 :
2157 : /************************************************************************/
2158 : /* importFromWkt() */
2159 : /************************************************************************/
2160 :
2161 : /*! @cond Doxygen_Suppress */
2162 :
2163 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2164 : CSLConstList papszOptions)
2165 :
2166 : {
2167 21 : return importFromWkt(&pszInput, papszOptions);
2168 : }
2169 :
2170 17636 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2171 : CSLConstList papszOptions)
2172 :
2173 : {
2174 35272 : TAKE_OPTIONAL_LOCK();
2175 :
2176 17636 : if (!ppszInput || !*ppszInput)
2177 0 : return OGRERR_FAILURE;
2178 :
2179 17636 : if (strlen(*ppszInput) > 100 * 1000 &&
2180 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2181 : {
2182 0 : CPLError(CE_Failure, CPLE_NotSupported,
2183 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2184 : "You can remove this limitation by definition the "
2185 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2186 0 : return OGRERR_FAILURE;
2187 : }
2188 :
2189 17636 : Clear();
2190 :
2191 17636 : bool canCache = false;
2192 17636 : auto tlsCache = OSRGetProjTLSCache();
2193 35272 : std::string osWkt;
2194 17636 : if (**ppszInput)
2195 : {
2196 17099 : osWkt = *ppszInput;
2197 17099 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2198 17099 : if (cachedObj)
2199 : {
2200 15403 : d->setPjCRS(cachedObj);
2201 : }
2202 : else
2203 : {
2204 3392 : CPLStringList aosOptions(papszOptions);
2205 1696 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2206 1696 : aosOptions.SetNameValue("STRICT", "NO");
2207 1696 : PROJ_STRING_LIST warnings = nullptr;
2208 1696 : PROJ_STRING_LIST errors = nullptr;
2209 1696 : auto ctxt = d->getPROJContext();
2210 1696 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2211 : &warnings, &errors);
2212 1696 : d->setPjCRS(pj);
2213 :
2214 1746 : for (auto iter = warnings; iter && *iter; ++iter)
2215 : {
2216 50 : d->m_wktImportWarnings.push_back(*iter);
2217 : }
2218 1939 : for (auto iter = errors; iter && *iter; ++iter)
2219 : {
2220 243 : d->m_wktImportErrors.push_back(*iter);
2221 243 : if (!d->m_pj_crs)
2222 : {
2223 37 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2224 : }
2225 : }
2226 1696 : if (warnings == nullptr && errors == nullptr)
2227 : {
2228 1410 : canCache = true;
2229 : }
2230 1696 : proj_string_list_destroy(warnings);
2231 1696 : proj_string_list_destroy(errors);
2232 : }
2233 : }
2234 17636 : if (!d->m_pj_crs)
2235 574 : return OGRERR_CORRUPT_DATA;
2236 :
2237 : // Only accept CRS objects
2238 17062 : if (!proj_is_crs(d->m_pj_crs))
2239 : {
2240 0 : Clear();
2241 0 : return OGRERR_CORRUPT_DATA;
2242 : }
2243 :
2244 17062 : if (canCache)
2245 : {
2246 1410 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2247 : }
2248 :
2249 17062 : if (strstr(*ppszInput, "CENTER_LONG"))
2250 : {
2251 0 : auto poRoot = new OGR_SRSNode();
2252 0 : d->setRoot(poRoot);
2253 0 : const char *pszTmp = *ppszInput;
2254 0 : poRoot->importFromWkt(&pszTmp);
2255 0 : d->m_bHasCenterLong = true;
2256 : }
2257 :
2258 : // TODO? we don't really update correctly since we assume that the
2259 : // passed string is only WKT.
2260 17062 : *ppszInput += strlen(*ppszInput);
2261 17062 : return OGRERR_NONE;
2262 :
2263 : #if no_longer_implemented_for_now
2264 : /* -------------------------------------------------------------------- */
2265 : /* The following seems to try and detect and unconsumed */
2266 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2267 : /* import and attach it to the existing root. Likely we will */
2268 : /* need to extend this somewhat to bring it into an acceptable */
2269 : /* OGRSpatialReference organization at some point. */
2270 : /* -------------------------------------------------------------------- */
2271 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2272 : {
2273 : if (((*ppszInput)[0]) == ',')
2274 : (*ppszInput)++;
2275 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2276 : poRoot->AddChild(poNewChild);
2277 : return poNewChild->importFromWkt(ppszInput);
2278 : }
2279 : #endif
2280 : }
2281 :
2282 : /*! @endcond */
2283 :
2284 : /**
2285 : * \brief Import from WKT string.
2286 : *
2287 : * This method will wipe the existing SRS definition, and
2288 : * reassign it based on the contents of the passed WKT string. Only as
2289 : * much of the input string as needed to construct this SRS is consumed from
2290 : * the input string, and the input string pointer
2291 : * is then updated to point to the remaining (unused) input.
2292 : *
2293 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2294 : * Issues</a> page for implementation details of WKT in OGR.
2295 : *
2296 : * This method is the same as the C function OSRImportFromWkt().
2297 : *
2298 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2299 : * point to remaining unused input text.
2300 : *
2301 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2302 : * fails for any reason.
2303 : * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2304 : * char*)
2305 : */
2306 :
2307 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2308 :
2309 : {
2310 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2311 : }
2312 :
2313 : /**
2314 : * \brief Import from WKT string.
2315 : *
2316 : * This method will wipe the existing SRS definition, and
2317 : * reassign it based on the contents of the passed WKT string. Only as
2318 : * much of the input string as needed to construct this SRS is consumed from
2319 : * the input string, and the input string pointer
2320 : * is then updated to point to the remaining (unused) input.
2321 : *
2322 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2323 : * Issues</a> page for implementation details of WKT in OGR.
2324 : *
2325 : * @param pszInput Input WKT
2326 : *
2327 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2328 : * fails for any reason.
2329 : * @since GDAL 2.3
2330 : */
2331 :
2332 17323 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2333 : {
2334 17323 : return importFromWkt(&pszInput);
2335 : }
2336 :
2337 : /************************************************************************/
2338 : /* Validate() */
2339 : /************************************************************************/
2340 :
2341 : /**
2342 : * \brief Validate CRS imported with importFromWkt() or with modified with
2343 : * direct node manipulations. Otherwise the CRS should be always valid.
2344 : *
2345 : * This method attempts to verify that the spatial reference system is
2346 : * well formed, and consists of known tokens. The validation is not
2347 : * comprehensive.
2348 : *
2349 : * This method is the same as the C function OSRValidate().
2350 : *
2351 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2352 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2353 : * but contains non-standard PROJECTION[] values.
2354 : */
2355 :
2356 116 : OGRErr OGRSpatialReference::Validate() const
2357 :
2358 : {
2359 232 : TAKE_OPTIONAL_LOCK();
2360 :
2361 154 : for (const auto &str : d->m_wktImportErrors)
2362 : {
2363 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2364 : }
2365 116 : for (const auto &str : d->m_wktImportWarnings)
2366 : {
2367 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2368 : }
2369 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2370 : {
2371 37 : return OGRERR_CORRUPT_DATA;
2372 : }
2373 79 : if (!d->m_wktImportWarnings.empty())
2374 : {
2375 0 : return OGRERR_UNSUPPORTED_SRS;
2376 : }
2377 79 : return OGRERR_NONE;
2378 : }
2379 :
2380 : /************************************************************************/
2381 : /* OSRValidate() */
2382 : /************************************************************************/
2383 : /**
2384 : * \brief Validate SRS tokens.
2385 : *
2386 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2387 : */
2388 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2389 :
2390 : {
2391 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2392 :
2393 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2394 : }
2395 :
2396 : /************************************************************************/
2397 : /* OSRImportFromWkt() */
2398 : /************************************************************************/
2399 :
2400 : /**
2401 : * \brief Import from WKT string.
2402 : *
2403 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2404 : * Issues</a> page for implementation details of WKT in OGR.
2405 : *
2406 : * This function is the same as OGRSpatialReference::importFromWkt().
2407 : */
2408 :
2409 292 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2410 :
2411 : {
2412 292 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2413 :
2414 292 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2415 : }
2416 :
2417 : /************************************************************************/
2418 : /* SetNode() */
2419 : /************************************************************************/
2420 :
2421 : /**
2422 : * \brief Set attribute value in spatial reference.
2423 : *
2424 : * Missing intermediate nodes in the path will be created if not already
2425 : * in existence. If the attribute has no children one will be created and
2426 : * assigned the value otherwise the zeroth child will be assigned the value.
2427 : *
2428 : * This method does the same as the C function OSRSetAttrValue().
2429 : *
2430 : * @param pszNodePath full path to attribute to be set. For instance
2431 : * "PROJCS|GEOGCS|UNIT".
2432 : *
2433 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2434 : * This may be NULL if you just want to force creation of the intermediate
2435 : * path.
2436 : *
2437 : * @return OGRERR_NONE on success.
2438 : */
2439 :
2440 545 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2441 : const char *pszNewNodeValue)
2442 :
2443 : {
2444 1090 : TAKE_OPTIONAL_LOCK();
2445 :
2446 : char **papszPathTokens =
2447 545 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2448 :
2449 545 : if (CSLCount(papszPathTokens) < 1)
2450 : {
2451 0 : CSLDestroy(papszPathTokens);
2452 0 : return OGRERR_FAILURE;
2453 : }
2454 :
2455 954 : if (GetRoot() == nullptr ||
2456 409 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2457 : {
2458 246 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2459 106 : CSLCount(papszPathTokens) == 1)
2460 : {
2461 106 : CSLDestroy(papszPathTokens);
2462 106 : return SetProjCS(pszNewNodeValue);
2463 : }
2464 : else
2465 : {
2466 34 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2467 : }
2468 : }
2469 :
2470 439 : OGR_SRSNode *poNode = GetRoot();
2471 683 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2472 : {
2473 244 : int j = 0; // Used after for.
2474 :
2475 603 : for (; j < poNode->GetChildCount(); j++)
2476 : {
2477 551 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2478 : {
2479 192 : poNode = poNode->GetChild(j);
2480 192 : j = -1;
2481 192 : break;
2482 : }
2483 : }
2484 :
2485 244 : if (j != -1)
2486 : {
2487 52 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2488 52 : poNode->AddChild(poNewNode);
2489 52 : poNode = poNewNode;
2490 : }
2491 : }
2492 :
2493 439 : CSLDestroy(papszPathTokens);
2494 :
2495 439 : if (pszNewNodeValue != nullptr)
2496 : {
2497 439 : if (poNode->GetChildCount() > 0)
2498 353 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2499 : else
2500 86 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2501 : };
2502 439 : return OGRERR_NONE;
2503 : }
2504 :
2505 : /************************************************************************/
2506 : /* OSRSetAttrValue() */
2507 : /************************************************************************/
2508 :
2509 : /**
2510 : * \brief Set attribute value in spatial reference.
2511 : *
2512 : * This function is the same as OGRSpatialReference::SetNode()
2513 : */
2514 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2515 : const char *pszPath, const char *pszValue)
2516 :
2517 : {
2518 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2519 :
2520 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2521 : }
2522 :
2523 : /************************************************************************/
2524 : /* SetNode() */
2525 : /************************************************************************/
2526 :
2527 : /**
2528 : * \brief Set attribute value in spatial reference.
2529 : *
2530 : * Missing intermediate nodes in the path will be created if not already
2531 : * in existence. If the attribute has no children one will be created and
2532 : * assigned the value otherwise the zeroth child will be assigned the value.
2533 : *
2534 : * This method does the same as the C function OSRSetAttrValue().
2535 : *
2536 : * @param pszNodePath full path to attribute to be set. For instance
2537 : * "PROJCS|GEOGCS|UNIT".
2538 : *
2539 : * @param dfValue value to be assigned to node.
2540 : *
2541 : * @return OGRERR_NONE on success.
2542 : */
2543 :
2544 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2545 :
2546 : {
2547 0 : char szValue[64] = {'\0'};
2548 :
2549 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2550 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2551 : else
2552 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2553 :
2554 0 : return SetNode(pszNodePath, szValue);
2555 : }
2556 :
2557 : /************************************************************************/
2558 : /* SetAngularUnits() */
2559 : /************************************************************************/
2560 :
2561 : /**
2562 : * \brief Set the angular units for the geographic coordinate system.
2563 : *
2564 : * This method creates a UNIT subnode with the specified values as a
2565 : * child of the GEOGCS node.
2566 : *
2567 : * This method does the same as the C function OSRSetAngularUnits().
2568 : *
2569 : * @param pszUnitsName the units name to be used. Some preferred units
2570 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2571 : *
2572 : * @param dfInRadians the value to multiple by an angle in the indicated
2573 : * units to transform to radians. Some standard conversion factors can
2574 : * be found in ogr_srs_api.h.
2575 : *
2576 : * @return OGRERR_NONE on success.
2577 : */
2578 :
2579 1029 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2580 : double dfInRadians)
2581 :
2582 : {
2583 2058 : TAKE_OPTIONAL_LOCK();
2584 :
2585 1029 : d->bNormInfoSet = FALSE;
2586 :
2587 1029 : d->refreshProjObj();
2588 1029 : if (!d->m_pj_crs)
2589 0 : return OGRERR_FAILURE;
2590 1029 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2591 1029 : if (!geodCRS)
2592 0 : return OGRERR_FAILURE;
2593 1029 : proj_destroy(geodCRS);
2594 1029 : d->demoteFromBoundCRS();
2595 1029 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2596 : pszUnitsName, dfInRadians,
2597 : nullptr, nullptr));
2598 1029 : d->undoDemoteFromBoundCRS();
2599 :
2600 1029 : d->m_osAngularUnits = pszUnitsName;
2601 1029 : d->m_dfAngularUnitToRadian = dfInRadians;
2602 :
2603 1029 : return OGRERR_NONE;
2604 : }
2605 :
2606 : /************************************************************************/
2607 : /* OSRSetAngularUnits() */
2608 : /************************************************************************/
2609 :
2610 : /**
2611 : * \brief Set the angular units for the geographic coordinate system.
2612 : *
2613 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2614 : */
2615 37 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2616 : double dfInRadians)
2617 :
2618 : {
2619 37 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2620 :
2621 37 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2622 : }
2623 :
2624 : /************************************************************************/
2625 : /* GetAngularUnits() */
2626 : /************************************************************************/
2627 :
2628 : /**
2629 : * \brief Fetch angular geographic coordinate system units.
2630 : *
2631 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2632 : * will be assumed. This method only checks directly under the GEOGCS node
2633 : * for units.
2634 : *
2635 : * This method does the same thing as the C function OSRGetAngularUnits().
2636 : *
2637 : * @param ppszName a pointer to be updated with the pointer to the units name.
2638 : * The returned value remains internal to the OGRSpatialReference and should
2639 : * not be freed, or modified. It may be invalidated on the next
2640 : * OGRSpatialReference call.
2641 : *
2642 : * @return the value to multiply by angular distances to transform them to
2643 : * radians.
2644 : * @since GDAL 2.3.0
2645 : */
2646 :
2647 6621 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2648 :
2649 : {
2650 13242 : TAKE_OPTIONAL_LOCK();
2651 :
2652 6621 : d->refreshProjObj();
2653 :
2654 6621 : if (!d->m_osAngularUnits.empty())
2655 : {
2656 1884 : if (ppszName != nullptr)
2657 140 : *ppszName = d->m_osAngularUnits.c_str();
2658 1884 : return d->m_dfAngularUnitToRadian;
2659 : }
2660 :
2661 : do
2662 : {
2663 4737 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2664 : {
2665 113 : break;
2666 : }
2667 :
2668 : auto geodCRS =
2669 4626 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2670 4626 : if (!geodCRS)
2671 : {
2672 0 : break;
2673 : }
2674 : auto coordSys =
2675 4626 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2676 4626 : proj_destroy(geodCRS);
2677 4626 : if (!coordSys)
2678 : {
2679 0 : break;
2680 : }
2681 4626 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2682 : PJ_CS_TYPE_ELLIPSOIDAL)
2683 : {
2684 2 : proj_destroy(coordSys);
2685 2 : break;
2686 : }
2687 :
2688 4624 : double dfConvFactor = 0.0;
2689 4624 : const char *pszUnitName = nullptr;
2690 4624 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2691 : nullptr, nullptr, &dfConvFactor,
2692 : &pszUnitName, nullptr, nullptr))
2693 : {
2694 0 : proj_destroy(coordSys);
2695 0 : break;
2696 : }
2697 :
2698 4624 : d->m_osAngularUnits = pszUnitName;
2699 :
2700 4624 : proj_destroy(coordSys);
2701 4624 : d->m_dfAngularUnitToRadian = dfConvFactor;
2702 : } while (false);
2703 :
2704 4737 : if (d->m_osAngularUnits.empty())
2705 : {
2706 113 : d->m_osAngularUnits = "degree";
2707 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2708 : }
2709 :
2710 4737 : if (ppszName != nullptr)
2711 2842 : *ppszName = d->m_osAngularUnits.c_str();
2712 4737 : return d->m_dfAngularUnitToRadian;
2713 : }
2714 :
2715 : /**
2716 : * \brief Fetch angular geographic coordinate system units.
2717 : *
2718 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2719 : * will be assumed. This method only checks directly under the GEOGCS node
2720 : * for units.
2721 : *
2722 : * This method does the same thing as the C function OSRGetAngularUnits().
2723 : *
2724 : * @param ppszName a pointer to be updated with the pointer to the units name.
2725 : * The returned value remains internal to the OGRSpatialReference and should
2726 : * not be freed, or modified. It may be invalidated on the next
2727 : * OGRSpatialReference call.
2728 : *
2729 : * @return the value to multiply by angular distances to transform them to
2730 : * radians.
2731 : * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2732 : */
2733 :
2734 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2735 :
2736 : {
2737 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2738 : }
2739 :
2740 : /************************************************************************/
2741 : /* OSRGetAngularUnits() */
2742 : /************************************************************************/
2743 :
2744 : /**
2745 : * \brief Fetch angular geographic coordinate system units.
2746 : *
2747 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2748 : */
2749 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2750 :
2751 : {
2752 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2753 :
2754 1 : return ToPointer(hSRS)->GetAngularUnits(
2755 1 : const_cast<const char **>(ppszName));
2756 : }
2757 :
2758 : /************************************************************************/
2759 : /* SetLinearUnitsAndUpdateParameters() */
2760 : /************************************************************************/
2761 :
2762 : /**
2763 : * \brief Set the linear units for the projection.
2764 : *
2765 : * This method creates a UNIT subnode with the specified values as a
2766 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2767 : * SetLinearUnits() method, but it also updates all existing linear
2768 : * projection parameter values from the old units to the new units.
2769 : *
2770 : * @param pszName the units name to be used. Some preferred units
2771 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2772 : * and SRS_UL_US_FOOT.
2773 : *
2774 : * @param dfInMeters the value to multiple by a length in the indicated
2775 : * units to transform to meters. Some standard conversion factors can
2776 : * be found in ogr_srs_api.h.
2777 : *
2778 : * @param pszUnitAuthority Unit authority name. Or nullptr
2779 : *
2780 : * @param pszUnitCode Unit code. Or nullptr
2781 : *
2782 : * @return OGRERR_NONE on success.
2783 : */
2784 :
2785 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2786 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2787 : const char *pszUnitCode)
2788 :
2789 : {
2790 78 : TAKE_OPTIONAL_LOCK();
2791 :
2792 39 : if (dfInMeters <= 0.0)
2793 0 : return OGRERR_FAILURE;
2794 :
2795 39 : d->refreshProjObj();
2796 39 : if (!d->m_pj_crs)
2797 0 : return OGRERR_FAILURE;
2798 :
2799 39 : d->demoteFromBoundCRS();
2800 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2801 : {
2802 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2803 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2804 : pszUnitAuthority, pszUnitCode, true));
2805 : }
2806 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2807 : pszName, dfInMeters,
2808 : pszUnitAuthority, pszUnitCode));
2809 39 : d->undoDemoteFromBoundCRS();
2810 :
2811 39 : d->m_osLinearUnits = pszName;
2812 39 : d->dfToMeter = dfInMeters;
2813 :
2814 39 : return OGRERR_NONE;
2815 : }
2816 :
2817 : /************************************************************************/
2818 : /* OSRSetLinearUnitsAndUpdateParameters() */
2819 : /************************************************************************/
2820 :
2821 : /**
2822 : * \brief Set the linear units for the projection.
2823 : *
2824 : * This function is the same as
2825 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2826 : */
2827 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2828 : const char *pszUnits,
2829 : double dfInMeters)
2830 :
2831 : {
2832 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2833 : OGRERR_FAILURE);
2834 :
2835 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2836 1 : dfInMeters);
2837 : }
2838 :
2839 : /************************************************************************/
2840 : /* SetLinearUnits() */
2841 : /************************************************************************/
2842 :
2843 : /**
2844 : * \brief Set the linear units for the projection.
2845 : *
2846 : * This method creates a UNIT subnode with the specified values as a
2847 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2848 : * Geographic 3D CRS the vertical axis units will be set.
2849 : *
2850 : * This method does the same as the C function OSRSetLinearUnits().
2851 : *
2852 : * @param pszUnitsName the units name to be used. Some preferred units
2853 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2854 : * and SRS_UL_US_FOOT.
2855 : *
2856 : * @param dfInMeters the value to multiple by a length in the indicated
2857 : * units to transform to meters. Some standard conversion factors can
2858 : * be found in ogr_srs_api.h.
2859 : *
2860 : * @return OGRERR_NONE on success.
2861 : */
2862 :
2863 6431 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2864 : double dfInMeters)
2865 :
2866 : {
2867 6431 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2868 : }
2869 :
2870 : /************************************************************************/
2871 : /* OSRSetLinearUnits() */
2872 : /************************************************************************/
2873 :
2874 : /**
2875 : * \brief Set the linear units for the projection.
2876 : *
2877 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2878 : */
2879 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2880 : double dfInMeters)
2881 :
2882 : {
2883 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2884 :
2885 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2886 : }
2887 :
2888 : /************************************************************************/
2889 : /* SetTargetLinearUnits() */
2890 : /************************************************************************/
2891 :
2892 : /**
2893 : * \brief Set the linear units for the projection.
2894 : *
2895 : * This method creates a UNIT subnode with the specified values as a
2896 : * child of the target node.
2897 : *
2898 : * This method does the same as the C function OSRSetTargetLinearUnits().
2899 : *
2900 : * @param pszTargetKey the keyword to set the linear units for.
2901 : * i.e. "PROJCS" or "VERT_CS"
2902 : *
2903 : * @param pszUnitsName the units name to be used. Some preferred units
2904 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2905 : * and SRS_UL_US_FOOT.
2906 : *
2907 : * @param dfInMeters the value to multiple by a length in the indicated
2908 : * units to transform to meters. Some standard conversion factors can
2909 : * be found in ogr_srs_api.h.
2910 : *
2911 : * @param pszUnitAuthority Unit authority name. Or nullptr
2912 : *
2913 : * @param pszUnitCode Unit code. Or nullptr
2914 : *
2915 : * @return OGRERR_NONE on success.
2916 : *
2917 : * @since OGR 1.9.0
2918 : */
2919 :
2920 9928 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2921 : const char *pszUnitsName,
2922 : double dfInMeters,
2923 : const char *pszUnitAuthority,
2924 : const char *pszUnitCode)
2925 :
2926 : {
2927 19856 : TAKE_OPTIONAL_LOCK();
2928 :
2929 9928 : if (dfInMeters <= 0.0)
2930 0 : return OGRERR_FAILURE;
2931 :
2932 9928 : d->refreshProjObj();
2933 9928 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2934 9928 : if (pszTargetKey == nullptr)
2935 : {
2936 9928 : if (!d->m_pj_crs)
2937 0 : return OGRERR_FAILURE;
2938 :
2939 9928 : d->demoteFromBoundCRS();
2940 9928 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2941 : {
2942 14076 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2943 7038 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2944 : pszUnitAuthority, pszUnitCode, false));
2945 : }
2946 19856 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
2947 9928 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2948 : pszUnitAuthority, pszUnitCode));
2949 9928 : d->undoDemoteFromBoundCRS();
2950 :
2951 9928 : d->m_osLinearUnits = pszUnitsName;
2952 9928 : d->dfToMeter = dfInMeters;
2953 :
2954 9928 : return OGRERR_NONE;
2955 : }
2956 :
2957 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
2958 :
2959 0 : if (poCS == nullptr)
2960 0 : return OGRERR_FAILURE;
2961 :
2962 0 : char szValue[128] = {'\0'};
2963 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
2964 0 : dfInMeters > std::numeric_limits<int>::min() &&
2965 0 : dfInMeters == static_cast<int>(dfInMeters))
2966 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
2967 : else
2968 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
2969 :
2970 0 : OGR_SRSNode *poUnits = nullptr;
2971 0 : if (poCS->FindChild("UNIT") >= 0)
2972 : {
2973 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
2974 0 : if (poUnits->GetChildCount() < 2)
2975 0 : return OGRERR_FAILURE;
2976 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
2977 0 : poUnits->GetChild(1)->SetValue(szValue);
2978 0 : if (poUnits->FindChild("AUTHORITY") != -1)
2979 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
2980 : }
2981 : else
2982 : {
2983 0 : poUnits = new OGR_SRSNode("UNIT");
2984 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
2985 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
2986 :
2987 0 : poCS->AddChild(poUnits);
2988 : }
2989 :
2990 0 : return OGRERR_NONE;
2991 : }
2992 :
2993 : /************************************************************************/
2994 : /* OSRSetLinearUnits() */
2995 : /************************************************************************/
2996 :
2997 : /**
2998 : * \brief Set the linear units for the target node.
2999 : *
3000 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3001 : *
3002 : * @since OGR 1.9.0
3003 : */
3004 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3005 : const char *pszTargetKey, const char *pszUnits,
3006 : double dfInMeters)
3007 :
3008 : {
3009 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3010 :
3011 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3012 1 : dfInMeters);
3013 : }
3014 :
3015 : /************************************************************************/
3016 : /* GetLinearUnits() */
3017 : /************************************************************************/
3018 :
3019 : /**
3020 : * \brief Fetch linear projection units.
3021 : *
3022 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3023 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3024 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3025 : * axis units will be returned.
3026 : *
3027 : * This method does the same thing as the C function OSRGetLinearUnits()
3028 : *
3029 : * @param ppszName a pointer to be updated with the pointer to the units name.
3030 : * The returned value remains internal to the OGRSpatialReference and should
3031 : * not be freed, or modified. It may be invalidated on the next
3032 : * OGRSpatialReference call.
3033 : *
3034 : * @return the value to multiply by linear distances to transform them to
3035 : * meters.
3036 : * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3037 : */
3038 :
3039 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3040 :
3041 : {
3042 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3043 : }
3044 :
3045 : /**
3046 : * \brief Fetch linear projection units.
3047 : *
3048 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3049 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3050 : * for units.
3051 : *
3052 : * This method does the same thing as the C function OSRGetLinearUnits()
3053 : *
3054 : * @param ppszName a pointer to be updated with the pointer to the units name.
3055 : * The returned value remains internal to the OGRSpatialReference and should
3056 : * not be freed, or modified. It may be invalidated on the next
3057 : * OGRSpatialReference call.
3058 : *
3059 : * @return the value to multiply by linear distances to transform them to
3060 : * meters.
3061 : * @since GDAL 2.3.0
3062 : */
3063 :
3064 14749 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3065 :
3066 : {
3067 14749 : return GetTargetLinearUnits(nullptr, ppszName);
3068 : }
3069 :
3070 : /************************************************************************/
3071 : /* OSRGetLinearUnits() */
3072 : /************************************************************************/
3073 :
3074 : /**
3075 : * \brief Fetch linear projection units.
3076 : *
3077 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3078 : */
3079 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3080 :
3081 : {
3082 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3083 :
3084 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3085 : }
3086 :
3087 : /************************************************************************/
3088 : /* GetTargetLinearUnits() */
3089 : /************************************************************************/
3090 :
3091 : /**
3092 : * \brief Fetch linear units for target.
3093 : *
3094 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3095 : *
3096 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3097 : *
3098 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3099 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3100 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3101 : * @param ppszName a pointer to be updated with the pointer to the units name.
3102 : * The returned value remains internal to the OGRSpatialReference and should not
3103 : * be freed, or modified. It may be invalidated on the next
3104 : * OGRSpatialReference call. ppszName can be set to NULL.
3105 : *
3106 : * @return the value to multiply by linear distances to transform them to
3107 : * meters.
3108 : *
3109 : * @since OGR 1.9.0
3110 : * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3111 : * const.
3112 : */
3113 :
3114 14994 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3115 : const char **ppszName) const
3116 :
3117 : {
3118 29988 : TAKE_OPTIONAL_LOCK();
3119 :
3120 14994 : d->refreshProjObj();
3121 :
3122 14994 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3123 14994 : if (pszTargetKey == nullptr)
3124 : {
3125 : // Use cached result if available
3126 14811 : if (!d->m_osLinearUnits.empty())
3127 : {
3128 7006 : if (ppszName)
3129 6341 : *ppszName = d->m_osLinearUnits.c_str();
3130 7006 : return d->dfToMeter;
3131 : }
3132 :
3133 : while (true)
3134 : {
3135 7805 : if (d->m_pj_crs == nullptr)
3136 : {
3137 231 : break;
3138 : }
3139 :
3140 7574 : d->demoteFromBoundCRS();
3141 7574 : PJ *coordSys = nullptr;
3142 7574 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3143 : {
3144 30 : for (int iComponent = 0; iComponent < 2; iComponent++)
3145 : {
3146 30 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3147 30 : d->m_pj_crs, iComponent);
3148 30 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3149 : {
3150 : auto temp =
3151 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3152 0 : proj_destroy(subCRS);
3153 0 : subCRS = temp;
3154 : }
3155 60 : if (subCRS &&
3156 30 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3157 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3158 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3159 : {
3160 24 : coordSys = proj_crs_get_coordinate_system(
3161 : d->getPROJContext(), subCRS);
3162 24 : proj_destroy(subCRS);
3163 24 : break;
3164 : }
3165 6 : else if (subCRS)
3166 : {
3167 6 : proj_destroy(subCRS);
3168 : }
3169 : }
3170 24 : if (coordSys == nullptr)
3171 : {
3172 0 : d->undoDemoteFromBoundCRS();
3173 0 : break;
3174 : }
3175 : }
3176 : else
3177 : {
3178 7550 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3179 7550 : d->m_pj_crs);
3180 : }
3181 :
3182 7574 : d->undoDemoteFromBoundCRS();
3183 7574 : if (!coordSys)
3184 : {
3185 0 : break;
3186 : }
3187 7574 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3188 :
3189 7574 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3190 2107 : csType != PJ_CS_TYPE_VERTICAL &&
3191 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3192 : csType != PJ_CS_TYPE_SPHERICAL)
3193 : {
3194 0 : proj_destroy(coordSys);
3195 0 : break;
3196 : }
3197 :
3198 7574 : int axis = 0;
3199 :
3200 7574 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3201 : csType == PJ_CS_TYPE_SPHERICAL)
3202 : {
3203 : const int axisCount =
3204 2107 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3205 :
3206 2107 : if (axisCount == 3)
3207 : {
3208 4 : axis = 2;
3209 : }
3210 : else
3211 : {
3212 2103 : proj_destroy(coordSys);
3213 2103 : break;
3214 : }
3215 : }
3216 :
3217 5471 : double dfConvFactor = 0.0;
3218 5471 : const char *pszUnitName = nullptr;
3219 5471 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3220 : nullptr, nullptr, nullptr, &dfConvFactor,
3221 : &pszUnitName, nullptr, nullptr))
3222 : {
3223 0 : proj_destroy(coordSys);
3224 0 : break;
3225 : }
3226 :
3227 5471 : d->m_osLinearUnits = pszUnitName;
3228 5471 : d->dfToMeter = dfConvFactor;
3229 5471 : if (ppszName)
3230 997 : *ppszName = d->m_osLinearUnits.c_str();
3231 :
3232 5471 : proj_destroy(coordSys);
3233 5471 : return dfConvFactor;
3234 : }
3235 :
3236 2334 : d->m_osLinearUnits = "unknown";
3237 2334 : d->dfToMeter = 1.0;
3238 :
3239 2334 : if (ppszName != nullptr)
3240 2155 : *ppszName = d->m_osLinearUnits.c_str();
3241 2334 : return 1.0;
3242 : }
3243 :
3244 183 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3245 :
3246 183 : if (ppszName != nullptr)
3247 35 : *ppszName = "unknown";
3248 :
3249 183 : if (poCS == nullptr)
3250 147 : return 1.0;
3251 :
3252 108 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3253 : {
3254 108 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3255 :
3256 108 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3257 : {
3258 36 : if (ppszName != nullptr)
3259 35 : *ppszName = poChild->GetChild(0)->GetValue();
3260 :
3261 36 : return CPLAtof(poChild->GetChild(1)->GetValue());
3262 : }
3263 : }
3264 :
3265 0 : return 1.0;
3266 : }
3267 :
3268 : /**
3269 : * \brief Fetch linear units for target.
3270 : *
3271 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3272 : *
3273 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3274 : *
3275 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3276 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3277 : * GEOCCS and VERT_CS are looked up)
3278 : * @param ppszName a pointer to be updated with the pointer to the units name.
3279 : * The returned value remains internal to the OGRSpatialReference and should not
3280 : * be freed, or modified. It may be invalidated on the next
3281 : * OGRSpatialReference call. ppszName can be set to NULL.
3282 : *
3283 : * @return the value to multiply by linear distances to transform them to
3284 : * meters.
3285 : *
3286 : * @since GDAL 2.3.0
3287 : */
3288 :
3289 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3290 : char **ppszName) const
3291 :
3292 : {
3293 0 : return GetTargetLinearUnits(pszTargetKey,
3294 0 : const_cast<const char **>(ppszName));
3295 : }
3296 :
3297 : /************************************************************************/
3298 : /* OSRGetTargetLinearUnits() */
3299 : /************************************************************************/
3300 :
3301 : /**
3302 : * \brief Fetch linear projection units.
3303 : *
3304 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3305 : *
3306 : * @since OGR 1.9.0
3307 : */
3308 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3309 : const char *pszTargetKey, char **ppszName)
3310 :
3311 : {
3312 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3313 :
3314 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3315 4 : pszTargetKey, const_cast<const char **>(ppszName));
3316 : }
3317 :
3318 : /************************************************************************/
3319 : /* GetPrimeMeridian() */
3320 : /************************************************************************/
3321 :
3322 : /**
3323 : * \brief Fetch prime meridian info.
3324 : *
3325 : * Returns the offset of the prime meridian from greenwich in degrees,
3326 : * and the prime meridian name (if requested). If no PRIMEM value exists
3327 : * in the coordinate system definition a value of "Greenwich" and an
3328 : * offset of 0.0 is assumed.
3329 : *
3330 : * If the prime meridian name is returned, the pointer is to an internal
3331 : * copy of the name. It should not be freed, altered or depended on after
3332 : * the next OGR call.
3333 : *
3334 : * This method is the same as the C function OSRGetPrimeMeridian().
3335 : *
3336 : * @param ppszName return location for prime meridian name. If NULL, name
3337 : * is not returned.
3338 : *
3339 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3340 : * degrees.
3341 : * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3342 : */
3343 :
3344 1370 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3345 :
3346 : {
3347 2740 : TAKE_OPTIONAL_LOCK();
3348 :
3349 1370 : d->refreshProjObj();
3350 :
3351 1370 : if (!d->m_osPrimeMeridianName.empty())
3352 : {
3353 61 : if (ppszName != nullptr)
3354 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3355 61 : return d->dfFromGreenwich;
3356 : }
3357 :
3358 : while (true)
3359 : {
3360 1309 : if (!d->m_pj_crs)
3361 0 : break;
3362 :
3363 1309 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3364 1309 : if (!pm)
3365 0 : break;
3366 :
3367 1309 : d->m_osPrimeMeridianName = proj_get_name(pm);
3368 1309 : if (ppszName)
3369 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3370 1309 : double dfLongitude = 0.0;
3371 1309 : double dfConvFactor = 0.0;
3372 1309 : proj_prime_meridian_get_parameters(
3373 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3374 1309 : proj_destroy(pm);
3375 2618 : d->dfFromGreenwich =
3376 1309 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3377 1309 : return d->dfFromGreenwich;
3378 : }
3379 :
3380 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3381 0 : d->dfFromGreenwich = 0.0;
3382 0 : if (ppszName != nullptr)
3383 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3384 0 : return d->dfFromGreenwich;
3385 : }
3386 :
3387 : /**
3388 : * \brief Fetch prime meridian info.
3389 : *
3390 : * Returns the offset of the prime meridian from greenwich in degrees,
3391 : * and the prime meridian name (if requested). If no PRIMEM value exists
3392 : * in the coordinate system definition a value of "Greenwich" and an
3393 : * offset of 0.0 is assumed.
3394 : *
3395 : * If the prime meridian name is returned, the pointer is to an internal
3396 : * copy of the name. It should not be freed, altered or depended on after
3397 : * the next OGR call.
3398 : *
3399 : * This method is the same as the C function OSRGetPrimeMeridian().
3400 : *
3401 : * @param ppszName return location for prime meridian name. If NULL, name
3402 : * is not returned.
3403 : *
3404 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3405 : * degrees.
3406 : * @since GDAL 2.3.0
3407 : */
3408 :
3409 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3410 :
3411 : {
3412 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3413 : }
3414 :
3415 : /************************************************************************/
3416 : /* OSRGetPrimeMeridian() */
3417 : /************************************************************************/
3418 :
3419 : /**
3420 : * \brief Fetch prime meridian info.
3421 : *
3422 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3423 : */
3424 1 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3425 :
3426 : {
3427 1 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3428 :
3429 1 : return ToPointer(hSRS)->GetPrimeMeridian(
3430 1 : const_cast<const char **>(ppszName));
3431 : }
3432 :
3433 : /************************************************************************/
3434 : /* SetGeogCS() */
3435 : /************************************************************************/
3436 :
3437 : /**
3438 : * \brief Set geographic coordinate system.
3439 : *
3440 : * This method is used to set the datum, ellipsoid, prime meridian and
3441 : * angular units for a geographic coordinate system. It can be used on its
3442 : * own to establish a geographic spatial reference, or applied to a
3443 : * projected coordinate system to establish the underlying geographic
3444 : * coordinate system.
3445 : *
3446 : * This method does the same as the C function OSRSetGeogCS().
3447 : *
3448 : * @param pszGeogName user visible name for the geographic coordinate system
3449 : * (not to serve as a key).
3450 : *
3451 : * @param pszDatumName key name for this datum. The OpenGIS specification
3452 : * lists some known values, and otherwise EPSG datum names with a standard
3453 : * transformation are considered legal keys.
3454 : *
3455 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3456 : *
3457 : * @param dfSemiMajor the semi major axis of the spheroid.
3458 : *
3459 : * @param dfInvFlattening the inverse flattening for the spheroid.
3460 : * This can be computed from the semi minor axis as
3461 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3462 : *
3463 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3464 : * If this is NULL a default value of "Greenwich" will be used.
3465 : *
3466 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3467 : * meridian. Always in Degrees
3468 : *
3469 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3470 : * standard names). If NULL a value of "degrees" will be assumed.
3471 : *
3472 : * @param dfConvertToRadians value to multiply angular units by to transform
3473 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3474 : * pszAngularUnits is NULL.
3475 : *
3476 : * @return OGRERR_NONE on success.
3477 : */
3478 :
3479 8123 : OGRErr OGRSpatialReference::SetGeogCS(
3480 : const char *pszGeogName, const char *pszDatumName,
3481 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3482 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3483 : double dfConvertToRadians)
3484 :
3485 : {
3486 16247 : TAKE_OPTIONAL_LOCK();
3487 :
3488 8123 : d->bNormInfoSet = FALSE;
3489 8123 : d->m_osAngularUnits.clear();
3490 8123 : d->m_dfAngularUnitToRadian = 0.0;
3491 8123 : d->m_osPrimeMeridianName.clear();
3492 8124 : d->dfFromGreenwich = 0.0;
3493 :
3494 : /* -------------------------------------------------------------------- */
3495 : /* For a geocentric coordinate system we want to set the datum */
3496 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3497 : /* temporary srs and use the copy method which has special */
3498 : /* handling for GEOCCS. */
3499 : /* -------------------------------------------------------------------- */
3500 8124 : if (IsGeocentric())
3501 : {
3502 4 : OGRSpatialReference oGCS;
3503 :
3504 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3505 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3506 : dfConvertToRadians);
3507 2 : return CopyGeogCSFrom(&oGCS);
3508 : }
3509 :
3510 8122 : auto cs = proj_create_ellipsoidal_2D_cs(
3511 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3512 : dfConvertToRadians);
3513 : // Prime meridian expressed in Degree
3514 8122 : auto obj = proj_create_geographic_crs(
3515 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3516 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3517 8122 : proj_destroy(cs);
3518 :
3519 12144 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3520 4023 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3521 : {
3522 4099 : d->setPjCRS(obj);
3523 : }
3524 4023 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3525 : {
3526 8046 : d->setPjCRS(
3527 4023 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3528 4023 : proj_destroy(obj);
3529 : }
3530 : else
3531 : {
3532 0 : proj_destroy(obj);
3533 : }
3534 :
3535 8122 : return OGRERR_NONE;
3536 : }
3537 :
3538 : /************************************************************************/
3539 : /* OSRSetGeogCS() */
3540 : /************************************************************************/
3541 :
3542 : /**
3543 : * \brief Set geographic coordinate system.
3544 : *
3545 : * This function is the same as OGRSpatialReference::SetGeogCS()
3546 : */
3547 26 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3548 : const char *pszDatumName, const char *pszSpheroidName,
3549 : double dfSemiMajor, double dfInvFlattening,
3550 : const char *pszPMName, double dfPMOffset,
3551 : const char *pszAngularUnits, double dfConvertToRadians)
3552 :
3553 : {
3554 26 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3555 :
3556 26 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3557 : pszSpheroidName, dfSemiMajor,
3558 : dfInvFlattening, pszPMName, dfPMOffset,
3559 26 : pszAngularUnits, dfConvertToRadians);
3560 : }
3561 :
3562 : /************************************************************************/
3563 : /* SetWellKnownGeogCS() */
3564 : /************************************************************************/
3565 :
3566 : /**
3567 : * \brief Set a GeogCS based on well known name.
3568 : *
3569 : * This may be called on an empty OGRSpatialReference to make a geographic
3570 : * coordinate system, or on something with an existing PROJCS node to
3571 : * set the underlying geographic coordinate system of a projected coordinate
3572 : * system.
3573 : *
3574 : * The following well known text values are currently supported,
3575 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3576 : * <ul>
3577 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3578 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3579 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3580 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3581 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3582 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3583 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3584 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3585 : * </ul>
3586 : *
3587 : * @param pszName name of well known geographic coordinate system.
3588 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3589 : * recognised, the target object is already initialized, or an EPSG value
3590 : * can't be successfully looked up.
3591 : */
3592 :
3593 2945 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3594 :
3595 : {
3596 5890 : TAKE_OPTIONAL_LOCK();
3597 :
3598 : /* -------------------------------------------------------------------- */
3599 : /* Check for EPSG authority numbers. */
3600 : /* -------------------------------------------------------------------- */
3601 2945 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3602 : {
3603 84 : OGRSpatialReference oSRS2;
3604 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3605 42 : if (eErr != OGRERR_NONE)
3606 0 : return eErr;
3607 :
3608 42 : if (!oSRS2.IsGeographic())
3609 0 : return OGRERR_FAILURE;
3610 :
3611 42 : return CopyGeogCSFrom(&oSRS2);
3612 : }
3613 :
3614 : /* -------------------------------------------------------------------- */
3615 : /* Check for simple names. */
3616 : /* -------------------------------------------------------------------- */
3617 2903 : const char *pszWKT = nullptr;
3618 :
3619 2903 : if (EQUAL(pszName, "WGS84"))
3620 : {
3621 2033 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3622 : }
3623 870 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3624 : {
3625 104 : pszWKT =
3626 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3627 : "ELLIPSOID[\"WGS "
3628 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3629 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3630 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3631 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3632 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3633 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3634 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3635 : "ID[\"OGC\",\"CRS84\"]]";
3636 : }
3637 766 : else if (EQUAL(pszName, "WGS72"))
3638 50 : pszWKT =
3639 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3640 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3641 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3642 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3643 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3644 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3645 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3646 :
3647 716 : else if (EQUAL(pszName, "NAD27"))
3648 175 : pszWKT =
3649 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3650 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3651 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3652 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3653 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3654 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3655 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3656 :
3657 541 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3658 0 : pszWKT =
3659 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3660 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3661 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3662 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3663 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3664 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3665 :
3666 541 : else if (EQUAL(pszName, "NAD83"))
3667 537 : pszWKT =
3668 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3669 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3670 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3671 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3672 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3673 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3674 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3675 : "\"EPSG\",\"4269\"]]";
3676 :
3677 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3678 0 : pszWKT =
3679 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3680 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3681 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3682 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3683 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3684 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3685 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3686 :
3687 : else
3688 4 : return OGRERR_FAILURE;
3689 :
3690 : /* -------------------------------------------------------------------- */
3691 : /* Import the WKT */
3692 : /* -------------------------------------------------------------------- */
3693 5798 : OGRSpatialReference oSRS2;
3694 2899 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3695 2899 : if (eErr != OGRERR_NONE)
3696 0 : return eErr;
3697 :
3698 : /* -------------------------------------------------------------------- */
3699 : /* Copy over. */
3700 : /* -------------------------------------------------------------------- */
3701 2899 : return CopyGeogCSFrom(&oSRS2);
3702 : }
3703 :
3704 : /************************************************************************/
3705 : /* OSRSetWellKnownGeogCS() */
3706 : /************************************************************************/
3707 :
3708 : /**
3709 : * \brief Set a GeogCS based on well known name.
3710 : *
3711 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3712 : */
3713 119 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3714 :
3715 : {
3716 119 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3717 :
3718 119 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3719 : }
3720 :
3721 : /************************************************************************/
3722 : /* CopyGeogCSFrom() */
3723 : /************************************************************************/
3724 :
3725 : /**
3726 : * \brief Copy GEOGCS from another OGRSpatialReference.
3727 : *
3728 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3729 : * If this object has a PROJCS root already, the GEOGCS is installed within
3730 : * it, otherwise it is installed as the root.
3731 : *
3732 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3733 : *
3734 : * @return OGRERR_NONE on success or an error code.
3735 : */
3736 :
3737 3522 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3738 :
3739 : {
3740 7044 : TAKE_OPTIONAL_LOCK();
3741 :
3742 3522 : d->bNormInfoSet = FALSE;
3743 3522 : d->m_osAngularUnits.clear();
3744 3522 : d->m_dfAngularUnitToRadian = 0.0;
3745 3522 : d->m_osPrimeMeridianName.clear();
3746 3522 : d->dfFromGreenwich = 0.0;
3747 :
3748 3522 : d->refreshProjObj();
3749 3522 : poSrcSRS->d->refreshProjObj();
3750 3522 : if (!poSrcSRS->d->m_pj_crs)
3751 : {
3752 0 : return OGRERR_FAILURE;
3753 : }
3754 : auto geodCRS =
3755 3522 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3756 3522 : if (!geodCRS)
3757 : {
3758 0 : return OGRERR_FAILURE;
3759 : }
3760 :
3761 : /* -------------------------------------------------------------------- */
3762 : /* Handle geocentric coordinate systems specially. We just */
3763 : /* want to copy the DATUM. */
3764 : /* -------------------------------------------------------------------- */
3765 3522 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3766 : {
3767 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3768 : #if PROJ_VERSION_MAJOR > 7 || \
3769 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3770 : if (datum == nullptr)
3771 : {
3772 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3773 : }
3774 : #endif
3775 3 : if (datum == nullptr)
3776 : {
3777 0 : proj_destroy(geodCRS);
3778 0 : return OGRERR_FAILURE;
3779 : }
3780 :
3781 3 : const char *pszUnitName = nullptr;
3782 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3783 :
3784 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3785 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3786 : unitConvFactor);
3787 3 : proj_destroy(datum);
3788 :
3789 3 : d->setPjCRS(pj_crs);
3790 : }
3791 :
3792 3519 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3793 : {
3794 327 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3795 327 : d->m_pj_crs, geodCRS);
3796 327 : d->setPjCRS(pj_crs);
3797 : }
3798 :
3799 : else
3800 : {
3801 3192 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3802 : }
3803 :
3804 : // Apply TOWGS84 of source CRS
3805 3522 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3806 : {
3807 : auto target =
3808 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3809 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3810 1 : poSrcSRS->d->m_pj_crs);
3811 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3812 : target, co));
3813 1 : proj_destroy(target);
3814 1 : proj_destroy(co);
3815 : }
3816 :
3817 3522 : proj_destroy(geodCRS);
3818 :
3819 3522 : return OGRERR_NONE;
3820 : }
3821 :
3822 : /************************************************************************/
3823 : /* OSRCopyGeogCSFrom() */
3824 : /************************************************************************/
3825 :
3826 : /**
3827 : * \brief Copy GEOGCS from another OGRSpatialReference.
3828 : *
3829 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3830 : */
3831 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3832 : const OGRSpatialReferenceH hSrcSRS)
3833 :
3834 : {
3835 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3836 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3837 :
3838 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3839 : }
3840 :
3841 : /************************************************************************/
3842 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3843 : /************************************************************************/
3844 :
3845 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3846 : *
3847 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3848 : */
3849 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3850 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3851 :
3852 : /**
3853 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3854 : */
3855 2420 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3856 : {
3857 2420 : return SET_FROM_USER_INPUT_LIMITATIONS;
3858 : }
3859 :
3860 : /************************************************************************/
3861 : /* RemoveIDFromMemberOfEnsembles() */
3862 : /************************************************************************/
3863 :
3864 : // cppcheck-suppress constParameterReference
3865 125 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3866 : {
3867 : // Remove "id" from members of datum ensembles for compatibility with
3868 : // older PROJ versions
3869 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3870 : // and https://github.com/OSGeo/PROJ/pull/3221
3871 125 : if (obj.GetType() == CPLJSONObject::Type::Object)
3872 : {
3873 152 : for (auto &subObj : obj.GetChildren())
3874 : {
3875 120 : RemoveIDFromMemberOfEnsembles(subObj);
3876 : }
3877 : }
3878 103 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3879 103 : obj.GetName() == "members")
3880 : {
3881 35 : for (auto &subObj : obj.ToArray())
3882 : {
3883 30 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3884 : {
3885 29 : subObj.Delete("id");
3886 : }
3887 : }
3888 : }
3889 125 : }
3890 :
3891 : /************************************************************************/
3892 : /* SetFromUserInput() */
3893 : /************************************************************************/
3894 :
3895 : /**
3896 : * \brief Set spatial reference from various text formats.
3897 : *
3898 : * This method will examine the provided input, and try to deduce the
3899 : * format, and then use it to initialize the spatial reference system. It
3900 : * may take the following forms:
3901 : *
3902 : * <ol>
3903 : * <li> Well Known Text definition - passed on to importFromWkt().
3904 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3905 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3906 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3907 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3908 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3909 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3910 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3911 : * WGS84 or WGS72.
3912 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3913 : * <li> PROJJSON (PROJ >= 6.2)
3914 : * </ol>
3915 : *
3916 : * It is expected that this method will be extended in the future to support
3917 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3918 : * State Plane definitions.
3919 : *
3920 : * This method is intended to be flexible, but by its nature it is
3921 : * imprecise as it must guess information about the format intended. When
3922 : * possible applications should call the specific method appropriate if the
3923 : * input is known to be in a particular format.
3924 : *
3925 : * This method does the same thing as the OSRSetFromUserInput() function.
3926 : *
3927 : * @param pszDefinition text definition to try to deduce SRS from.
3928 : *
3929 : * @return OGRERR_NONE on success, or an error code if the name isn't
3930 : * recognised, the definition is corrupt, or an EPSG value can't be
3931 : * successfully looked up.
3932 : */
3933 :
3934 13579 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3935 : {
3936 13579 : return SetFromUserInput(pszDefinition, nullptr);
3937 : }
3938 :
3939 : /**
3940 : * \brief Set spatial reference from various text formats.
3941 : *
3942 : * This method will examine the provided input, and try to deduce the
3943 : * format, and then use it to initialize the spatial reference system. It
3944 : * may take the following forms:
3945 : *
3946 : * <ol>
3947 : * <li> Well Known Text definition - passed on to importFromWkt().
3948 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3949 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3950 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3951 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3952 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3953 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3954 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3955 : * WGS84 or WGS72.
3956 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3957 : * <li> PROJJSON (PROJ >= 6.2)
3958 : * </ol>
3959 : *
3960 : * It is expected that this method will be extended in the future to support
3961 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3962 : * State Plane definitions.
3963 : *
3964 : * This method is intended to be flexible, but by its nature it is
3965 : * imprecise as it must guess information about the format intended. When
3966 : * possible applications should call the specific method appropriate if the
3967 : * input is known to be in a particular format.
3968 : *
3969 : * This method does the same thing as the OSRSetFromUserInput() and
3970 : * OSRSetFromUserInputEx() functions.
3971 : *
3972 : * @param pszDefinition text definition to try to deduce SRS from.
3973 : *
3974 : * @param papszOptions NULL terminated list of options, or NULL.
3975 : * <ol>
3976 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
3977 : * Whether http:// or https:// access is allowed. Defaults to YES.
3978 : * <li> ALLOW_FILE_ACCESS=YES/NO.
3979 : * Whether reading a file using the Virtual File System layer is allowed
3980 : * (can also involve network access). Defaults to YES.
3981 : * </ol>
3982 : *
3983 : * @return OGRERR_NONE on success, or an error code if the name isn't
3984 : * recognised, the definition is corrupt, or an EPSG value can't be
3985 : * successfully looked up.
3986 : */
3987 :
3988 17479 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
3989 : CSLConstList papszOptions)
3990 : {
3991 34958 : TAKE_OPTIONAL_LOCK();
3992 :
3993 : // Skip leading white space
3994 17481 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
3995 2 : pszDefinition++;
3996 :
3997 17479 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
3998 : {
3999 1 : pszDefinition += 6;
4000 : }
4001 :
4002 : /* -------------------------------------------------------------------- */
4003 : /* Is it a recognised syntax? */
4004 : /* -------------------------------------------------------------------- */
4005 17479 : const char *const wktKeywords[] = {
4006 : // WKT1
4007 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4008 : // WKT2"
4009 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4010 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4011 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4012 217127 : for (const char *keyword : wktKeywords)
4013 : {
4014 207581 : if (STARTS_WITH_CI(pszDefinition, keyword))
4015 : {
4016 7933 : return importFromWkt(pszDefinition);
4017 : }
4018 : }
4019 :
4020 9546 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4021 9546 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4022 : {
4023 7330 : OGRErr eStatus = OGRERR_NONE;
4024 :
4025 7330 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4026 : {
4027 : // Use proj_create() as it allows things like EPSG:3157+4617
4028 : // that are not normally supported by the below code that
4029 : // builds manually a compound CRS
4030 57 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4031 57 : if (!pj)
4032 : {
4033 0 : return OGRERR_FAILURE;
4034 : }
4035 57 : Clear();
4036 57 : d->setPjCRS(pj);
4037 57 : return OGRERR_NONE;
4038 : }
4039 : else
4040 : {
4041 : eStatus =
4042 7273 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4043 : }
4044 :
4045 7273 : return eStatus;
4046 : }
4047 :
4048 2216 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4049 1562 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4050 1561 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4051 1503 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4052 1503 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4053 1503 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4054 713 : return importFromURN(pszDefinition);
4055 :
4056 1503 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4057 1501 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4058 1500 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4059 858 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4060 857 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4061 646 : return importFromCRSURL(pszDefinition);
4062 :
4063 857 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4064 1 : return importFromWMSAUTO(pszDefinition);
4065 :
4066 : // WMS/WCS OGC codes like OGC:CRS84.
4067 856 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4068 27 : return SetWellKnownGeogCS(pszDefinition + 4);
4069 :
4070 829 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4071 1 : return SetWellKnownGeogCS(pszDefinition);
4072 :
4073 828 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4074 : {
4075 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4076 0 : char *pszCode = strstr(pszFile, ",") + 1;
4077 :
4078 0 : pszCode[-1] = '\0';
4079 :
4080 0 : OGRErr err = importFromDict(pszFile, pszCode);
4081 0 : CPLFree(pszFile);
4082 :
4083 0 : return err;
4084 : }
4085 :
4086 828 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4087 826 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4088 : {
4089 250 : Clear();
4090 250 : return SetWellKnownGeogCS(pszDefinition);
4091 : }
4092 :
4093 : // PROJJSON
4094 578 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4095 51 : (strstr(pszDefinition, "GeodeticCRS") ||
4096 51 : strstr(pszDefinition, "GeographicCRS") ||
4097 21 : strstr(pszDefinition, "ProjectedCRS") ||
4098 0 : strstr(pszDefinition, "VerticalCRS") ||
4099 0 : strstr(pszDefinition, "BoundCRS") ||
4100 0 : strstr(pszDefinition, "CompoundCRS") ||
4101 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4102 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4103 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4104 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4105 0 : strstr(pszDefinition, "EngineeringCRS") ||
4106 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4107 0 : strstr(pszDefinition, "ParametricCRS") ||
4108 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4109 0 : strstr(pszDefinition, "TemporalCRS") ||
4110 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4111 : {
4112 : PJ *pj;
4113 51 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4114 : {
4115 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4116 : // a unknown id.
4117 5 : CPLJSONDocument oCRSDoc;
4118 5 : if (!oCRSDoc.LoadMemory(pszDefinition))
4119 0 : return OGRERR_CORRUPT_DATA;
4120 5 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4121 5 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4122 5 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4123 : }
4124 : else
4125 : {
4126 46 : pj = proj_create(d->getPROJContext(), pszDefinition);
4127 : }
4128 51 : if (!pj)
4129 : {
4130 2 : return OGRERR_FAILURE;
4131 : }
4132 49 : Clear();
4133 49 : d->setPjCRS(pj);
4134 49 : return OGRERR_NONE;
4135 : }
4136 :
4137 527 : if (strstr(pszDefinition, "+proj") != nullptr ||
4138 196 : strstr(pszDefinition, "+init") != nullptr)
4139 331 : return importFromProj4(pszDefinition);
4140 :
4141 196 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4142 196 : STARTS_WITH_CI(pszDefinition, "https://"))
4143 : {
4144 1 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4145 : "ALLOW_NETWORK_ACCESS", "YES")))
4146 0 : return importFromUrl(pszDefinition);
4147 :
4148 1 : CPLError(CE_Failure, CPLE_AppDefined,
4149 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4150 : pszDefinition);
4151 1 : return OGRERR_FAILURE;
4152 : }
4153 :
4154 195 : if (EQUAL(pszDefinition, "osgb:BNG"))
4155 : {
4156 13 : return importFromEPSG(27700);
4157 : }
4158 :
4159 : // Used by German CityGML files
4160 182 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4161 : {
4162 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4163 0 : return SetFromUserInput("EPSG:25832+5783");
4164 : }
4165 182 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4166 : {
4167 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4168 4 : return SetFromUserInput("EPSG:25832+7837");
4169 : }
4170 :
4171 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4172 178 : const char *pszDot = strrchr(pszDefinition, ':');
4173 178 : if (pszDot)
4174 : {
4175 108 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4176 : auto authorities =
4177 108 : proj_get_authorities_from_database(d->getPROJContext());
4178 108 : if (authorities)
4179 : {
4180 108 : std::set<std::string> aosCandidateAuthorities;
4181 234 : for (auto iter = authorities; *iter; ++iter)
4182 : {
4183 231 : if (*iter == osPrefix)
4184 : {
4185 105 : aosCandidateAuthorities.clear();
4186 105 : aosCandidateAuthorities.insert(*iter);
4187 105 : break;
4188 : }
4189 : // Deal with "IAU_2015" as authority in the list and input
4190 : // "IAU:code"
4191 126 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4192 126 : 0 &&
4193 0 : (*iter)[osPrefix.size()] == '_')
4194 : {
4195 0 : aosCandidateAuthorities.insert(*iter);
4196 : }
4197 : // Deal with "IAU_2015" as authority in the list and input
4198 : // "IAU:2015:code"
4199 252 : else if (osPrefix.find(':') != std::string::npos &&
4200 126 : osPrefix.size() == strlen(*iter) &&
4201 126 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4202 : {
4203 0 : aosCandidateAuthorities.clear();
4204 0 : aosCandidateAuthorities.insert(*iter);
4205 0 : break;
4206 : }
4207 : }
4208 :
4209 108 : proj_string_list_destroy(authorities);
4210 :
4211 108 : if (!aosCandidateAuthorities.empty())
4212 : {
4213 105 : auto obj = proj_create_from_database(
4214 : d->getPROJContext(),
4215 105 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4216 : PJ_CATEGORY_CRS, false, nullptr);
4217 105 : if (!obj)
4218 : {
4219 3 : return OGRERR_FAILURE;
4220 : }
4221 102 : Clear();
4222 102 : d->setPjCRS(obj);
4223 102 : return OGRERR_NONE;
4224 : }
4225 : }
4226 : }
4227 :
4228 : /* -------------------------------------------------------------------- */
4229 : /* Try to open it as a file. */
4230 : /* -------------------------------------------------------------------- */
4231 73 : if (!CPLTestBool(
4232 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4233 : {
4234 : VSIStatBufL sStat;
4235 24 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4236 12 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4237 : {
4238 0 : CPLError(CE_Failure, CPLE_AppDefined,
4239 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4240 : pszDefinition);
4241 0 : return OGRERR_FAILURE;
4242 : }
4243 : // We used to silently return an error without a CE_Failure message
4244 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4245 12 : return OGRERR_CORRUPT_DATA;
4246 : }
4247 :
4248 122 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4249 61 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4250 61 : if (fp == nullptr)
4251 56 : return OGRERR_CORRUPT_DATA;
4252 :
4253 5 : const size_t nBufMax = 100000;
4254 5 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4255 5 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4256 5 : VSIFCloseL(fp);
4257 :
4258 5 : if (nBytes == nBufMax - 1)
4259 : {
4260 0 : CPLDebug("OGR",
4261 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4262 : "but it is to large for our generous buffer. Is it really "
4263 : "just a WKT definition?",
4264 : pszDefinition);
4265 0 : CPLFree(pszBuffer);
4266 0 : return OGRERR_FAILURE;
4267 : }
4268 :
4269 5 : pszBuffer[nBytes] = '\0';
4270 :
4271 5 : char *pszBufPtr = pszBuffer;
4272 5 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4273 0 : pszBufPtr++;
4274 :
4275 5 : OGRErr err = OGRERR_NONE;
4276 5 : if (pszBufPtr[0] == '<')
4277 0 : err = importFromXML(pszBufPtr);
4278 5 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4279 5 : strstr(pszBuffer, "+init") != nullptr) &&
4280 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4281 0 : strstr(pszBuffer, "extension") == nullptr))
4282 0 : err = importFromProj4(pszBufPtr);
4283 : else
4284 : {
4285 5 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4286 : {
4287 3 : pszBufPtr += 6;
4288 : }
4289 :
4290 : // coverity[tainted_data]
4291 5 : err = importFromWkt(pszBufPtr);
4292 : }
4293 :
4294 5 : CPLFree(pszBuffer);
4295 :
4296 5 : return err;
4297 : }
4298 :
4299 : /************************************************************************/
4300 : /* OSRSetFromUserInput() */
4301 : /************************************************************************/
4302 :
4303 : /**
4304 : * \brief Set spatial reference from various text formats.
4305 : *
4306 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4307 : *
4308 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4309 : */
4310 249 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4311 : const char *pszDef)
4312 :
4313 : {
4314 249 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4315 :
4316 249 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4317 : }
4318 :
4319 : /************************************************************************/
4320 : /* OSRSetFromUserInputEx() */
4321 : /************************************************************************/
4322 :
4323 : /**
4324 : * \brief Set spatial reference from various text formats.
4325 : *
4326 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4327 : *
4328 : * @since GDAL 3.9
4329 : */
4330 704 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4331 : CSLConstList papszOptions)
4332 :
4333 : {
4334 704 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4335 :
4336 704 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4337 : }
4338 :
4339 : /************************************************************************/
4340 : /* ImportFromUrl() */
4341 : /************************************************************************/
4342 :
4343 : /**
4344 : * \brief Set spatial reference from a URL.
4345 : *
4346 : * This method will download the spatial reference at a given URL and
4347 : * feed it into SetFromUserInput for you.
4348 : *
4349 : * This method does the same thing as the OSRImportFromUrl() function.
4350 : *
4351 : * @param pszUrl text definition to try to deduce SRS from.
4352 : *
4353 : * @return OGRERR_NONE on success, or an error code with the curl
4354 : * error message if it is unable to download data.
4355 : */
4356 :
4357 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4358 :
4359 : {
4360 10 : TAKE_OPTIONAL_LOCK();
4361 :
4362 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4363 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4364 : {
4365 2 : CPLError(CE_Failure, CPLE_AppDefined,
4366 : "The given string is not recognized as a URL"
4367 : "starting with 'http://' -- %s",
4368 : pszUrl);
4369 2 : return OGRERR_FAILURE;
4370 : }
4371 :
4372 : /* -------------------------------------------------------------------- */
4373 : /* Fetch the result. */
4374 : /* -------------------------------------------------------------------- */
4375 3 : CPLErrorReset();
4376 :
4377 6 : std::string osUrl(pszUrl);
4378 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4379 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4380 : // to query WKT. To allow a static server to be used, rather append a
4381 : // "ogcwkt/" suffix.
4382 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4383 5 : "http://spatialreference.org/ref/"})
4384 : {
4385 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4386 : {
4387 : const CPLStringList aosTokens(
4388 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4389 3 : if (aosTokens.size() == 2)
4390 : {
4391 2 : osUrl = "https://spatialreference.org/ref/";
4392 2 : osUrl += aosTokens[0]; // authority
4393 2 : osUrl += '/';
4394 2 : osUrl += aosTokens[1]; // code
4395 2 : osUrl += "/ogcwkt/";
4396 : }
4397 3 : break;
4398 : }
4399 : }
4400 :
4401 3 : const char *pszTimeout = "TIMEOUT=10";
4402 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4403 :
4404 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4405 :
4406 : /* -------------------------------------------------------------------- */
4407 : /* Try to handle errors. */
4408 : /* -------------------------------------------------------------------- */
4409 :
4410 3 : if (psResult == nullptr)
4411 0 : return OGRERR_FAILURE;
4412 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4413 3 : psResult->pabyData == nullptr)
4414 : {
4415 0 : if (CPLGetLastErrorNo() == 0)
4416 : {
4417 0 : CPLError(CE_Failure, CPLE_AppDefined,
4418 : "No data was returned from the given URL");
4419 : }
4420 0 : CPLHTTPDestroyResult(psResult);
4421 0 : return OGRERR_FAILURE;
4422 : }
4423 :
4424 3 : if (psResult->nStatus != 0)
4425 : {
4426 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4427 : psResult->nStatus, psResult->pszErrBuf);
4428 0 : CPLHTTPDestroyResult(psResult);
4429 0 : return OGRERR_FAILURE;
4430 : }
4431 :
4432 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4433 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4434 3 : STARTS_WITH_CI(pszData, "https://"))
4435 : {
4436 0 : CPLError(CE_Failure, CPLE_AppDefined,
4437 : "The data that was downloaded also starts with 'http://' "
4438 : "and cannot be passed into SetFromUserInput. Is this "
4439 : "really a spatial reference definition? ");
4440 0 : CPLHTTPDestroyResult(psResult);
4441 0 : return OGRERR_FAILURE;
4442 : }
4443 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4444 : {
4445 0 : CPLHTTPDestroyResult(psResult);
4446 0 : return OGRERR_FAILURE;
4447 : }
4448 :
4449 3 : CPLHTTPDestroyResult(psResult);
4450 3 : return OGRERR_NONE;
4451 : }
4452 :
4453 : /************************************************************************/
4454 : /* OSRimportFromUrl() */
4455 : /************************************************************************/
4456 :
4457 : /**
4458 : * \brief Set spatial reference from a URL.
4459 : *
4460 : * This function is the same as OGRSpatialReference::importFromUrl()
4461 : */
4462 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4463 :
4464 : {
4465 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4466 :
4467 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4468 : }
4469 :
4470 : /************************************************************************/
4471 : /* importFromURNPart() */
4472 : /************************************************************************/
4473 1521 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4474 : const char *pszCode,
4475 : const char *pszURN)
4476 : {
4477 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4478 : (void)this;
4479 : (void)pszAuthority;
4480 : (void)pszCode;
4481 : (void)pszURN;
4482 : return OGRERR_FAILURE;
4483 : #else
4484 : /* -------------------------------------------------------------------- */
4485 : /* Is this an EPSG code? Note that we import it with EPSG */
4486 : /* preferred axis ordering for geographic coordinate systems. */
4487 : /* -------------------------------------------------------------------- */
4488 1521 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4489 1442 : return importFromEPSGA(atoi(pszCode));
4490 :
4491 : /* -------------------------------------------------------------------- */
4492 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4493 : /* -------------------------------------------------------------------- */
4494 79 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4495 0 : return importFromDict("IAU2000.wkt", pszCode);
4496 :
4497 : /* -------------------------------------------------------------------- */
4498 : /* Is this an OGC code? */
4499 : /* -------------------------------------------------------------------- */
4500 79 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4501 : {
4502 1 : CPLError(CE_Failure, CPLE_AppDefined,
4503 : "URN %s has unrecognized authority.", pszURN);
4504 1 : return OGRERR_FAILURE;
4505 : }
4506 :
4507 78 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4508 68 : return SetWellKnownGeogCS(pszCode);
4509 10 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4510 0 : return SetWellKnownGeogCS(pszCode);
4511 10 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4512 0 : return SetWellKnownGeogCS(pszCode);
4513 10 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4514 8 : return SetWellKnownGeogCS("CRS84");
4515 :
4516 : /* -------------------------------------------------------------------- */
4517 : /* Handle auto codes. We need to convert from format */
4518 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4519 : /* -------------------------------------------------------------------- */
4520 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4521 : {
4522 2 : char szWMSAuto[100] = {'\0'};
4523 :
4524 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4525 0 : return OGRERR_FAILURE;
4526 :
4527 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4528 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4529 : {
4530 26 : if (szWMSAuto[i] == ':')
4531 4 : szWMSAuto[i] = ',';
4532 : }
4533 :
4534 2 : return importFromWMSAUTO(szWMSAuto);
4535 : }
4536 :
4537 : /* -------------------------------------------------------------------- */
4538 : /* Not a recognise OGC item. */
4539 : /* -------------------------------------------------------------------- */
4540 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4541 : pszURN);
4542 :
4543 0 : return OGRERR_FAILURE;
4544 : #endif
4545 : }
4546 :
4547 : /************************************************************************/
4548 : /* importFromURN() */
4549 : /* */
4550 : /* See OGC recommendation paper 06-023r1 or later for details. */
4551 : /************************************************************************/
4552 :
4553 : /**
4554 : * \brief Initialize from OGC URN.
4555 : *
4556 : * Initializes this spatial reference from a coordinate system defined
4557 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4558 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4559 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4560 : *
4561 : * This method is also support through SetFromUserInput() which can
4562 : * normally be used for URNs.
4563 : *
4564 : * @param pszURN the urn string.
4565 : *
4566 : * @return OGRERR_NONE on success or an error code.
4567 : */
4568 :
4569 774 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4570 :
4571 : {
4572 1548 : TAKE_OPTIONAL_LOCK();
4573 :
4574 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4575 :
4576 : // PROJ 8.2.0 has support for IAU codes now.
4577 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4578 : /* -------------------------------------------------------------------- */
4579 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4580 : /* -------------------------------------------------------------------- */
4581 : const char *pszIAU = strstr(pszURN, "IAU");
4582 : if (pszIAU)
4583 : {
4584 : const char *pszCode = strchr(pszIAU, ':');
4585 : if (pszCode)
4586 : {
4587 : ++pszCode;
4588 : if (*pszCode == ':')
4589 : ++pszCode;
4590 : return importFromDict("IAU2000.wkt", pszCode);
4591 : }
4592 : }
4593 : #endif
4594 :
4595 : if (strlen(pszURN) >= 1000)
4596 : {
4597 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4598 : return OGRERR_CORRUPT_DATA;
4599 : }
4600 : auto obj = proj_create(d->getPROJContext(), pszURN);
4601 : if (!obj)
4602 : {
4603 : return OGRERR_FAILURE;
4604 : }
4605 : Clear();
4606 : d->setPjCRS(obj);
4607 : return OGRERR_NONE;
4608 : #else
4609 774 : const char *pszCur = nullptr;
4610 :
4611 774 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4612 709 : pszCur = pszURN + 16;
4613 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4614 1 : pszCur = pszURN + 20;
4615 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4616 62 : pszCur = pszURN + 18;
4617 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4618 0 : pszCur = pszURN + 16;
4619 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4620 0 : pszCur = pszURN + 20;
4621 : else
4622 : {
4623 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4624 : pszURN);
4625 2 : return OGRERR_FAILURE;
4626 : }
4627 :
4628 : /* -------------------------------------------------------------------- */
4629 : /* Clear any existing definition. */
4630 : /* -------------------------------------------------------------------- */
4631 772 : Clear();
4632 :
4633 : /* -------------------------------------------------------------------- */
4634 : /* Find code (ignoring version) out of string like: */
4635 : /* */
4636 : /* authority:[version]:code */
4637 : /* -------------------------------------------------------------------- */
4638 772 : const char *pszAuthority = pszCur;
4639 :
4640 : // skip authority
4641 3843 : while (*pszCur != ':' && *pszCur)
4642 3071 : pszCur++;
4643 772 : if (*pszCur == ':')
4644 772 : pszCur++;
4645 :
4646 : // skip version
4647 772 : const char *pszBeforeVersion = pszCur;
4648 1068 : while (*pszCur != ':' && *pszCur)
4649 296 : pszCur++;
4650 772 : if (*pszCur == ':')
4651 744 : pszCur++;
4652 : else
4653 : // We come here in the case, the content to parse is authority:code
4654 : // (instead of authority::code) which is probably illegal according to
4655 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4656 : // for example in what is returned by GeoServer.
4657 28 : pszCur = pszBeforeVersion;
4658 :
4659 772 : const char *pszCode = pszCur;
4660 :
4661 772 : const char *pszComma = strchr(pszCur, ',');
4662 772 : if (pszComma == nullptr)
4663 771 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4664 :
4665 : // There's a second part with the vertical SRS.
4666 1 : pszCur = pszComma + 1;
4667 1 : if (!STARTS_WITH(pszCur, "crs:"))
4668 : {
4669 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4670 : pszURN);
4671 0 : return OGRERR_FAILURE;
4672 : }
4673 :
4674 1 : pszCur += 4;
4675 :
4676 1 : char *pszFirstCode = CPLStrdup(pszCode);
4677 1 : pszFirstCode[pszComma - pszCode] = '\0';
4678 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4679 1 : CPLFree(pszFirstCode);
4680 :
4681 : // Do we want to turn this into a compound definition
4682 : // with a vertical datum?
4683 1 : if (eStatus != OGRERR_NONE)
4684 0 : return eStatus;
4685 :
4686 : /* -------------------------------------------------------------------- */
4687 : /* Find code (ignoring version) out of string like: */
4688 : /* */
4689 : /* authority:[version]:code */
4690 : /* -------------------------------------------------------------------- */
4691 1 : pszAuthority = pszCur;
4692 :
4693 : // skip authority
4694 5 : while (*pszCur != ':' && *pszCur)
4695 4 : pszCur++;
4696 1 : if (*pszCur == ':')
4697 1 : pszCur++;
4698 :
4699 : // skip version
4700 1 : pszBeforeVersion = pszCur;
4701 1 : while (*pszCur != ':' && *pszCur)
4702 0 : pszCur++;
4703 1 : if (*pszCur == ':')
4704 1 : pszCur++;
4705 : else
4706 0 : pszCur = pszBeforeVersion;
4707 :
4708 1 : pszCode = pszCur;
4709 :
4710 2 : OGRSpatialReference oVertSRS;
4711 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4712 1 : if (eStatus == OGRERR_NONE)
4713 : {
4714 1 : OGRSpatialReference oHorizSRS(*this);
4715 :
4716 1 : Clear();
4717 :
4718 1 : oHorizSRS.d->refreshProjObj();
4719 1 : oVertSRS.d->refreshProjObj();
4720 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4721 0 : return OGRERR_FAILURE;
4722 :
4723 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4724 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4725 :
4726 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4727 1 : osName += " + ";
4728 1 : osName += pszVertName ? pszVertName : "";
4729 :
4730 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4731 : }
4732 :
4733 1 : return eStatus;
4734 : #endif
4735 : }
4736 :
4737 : /************************************************************************/
4738 : /* importFromCRSURL() */
4739 : /* */
4740 : /* See OGC Best Practice document 11-135 for details. */
4741 : /************************************************************************/
4742 :
4743 : /**
4744 : * \brief Initialize from OGC URL.
4745 : *
4746 : * Initializes this spatial reference from a coordinate system defined
4747 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4748 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4749 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4750 : *
4751 : * This method is also supported through SetFromUserInput() which can
4752 : * normally be used for URLs.
4753 : *
4754 : * @param pszURL the URL string.
4755 : *
4756 : * @return OGRERR_NONE on success or an error code.
4757 : */
4758 :
4759 749 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4760 :
4761 : {
4762 1498 : TAKE_OPTIONAL_LOCK();
4763 :
4764 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4765 : if (strlen(pszURL) >= 10000)
4766 : {
4767 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4768 : return OGRERR_CORRUPT_DATA;
4769 : }
4770 :
4771 : PJ *obj;
4772 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4773 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4774 : {
4775 : obj = proj_create(
4776 : d->getPROJContext(),
4777 : CPLSPrintf("IAU:%s",
4778 : pszURL +
4779 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4780 : }
4781 : else
4782 : #endif
4783 : {
4784 : obj = proj_create(d->getPROJContext(), pszURL);
4785 : }
4786 : if (!obj)
4787 : {
4788 : return OGRERR_FAILURE;
4789 : }
4790 : Clear();
4791 : d->setPjCRS(obj);
4792 : return OGRERR_NONE;
4793 : #else
4794 749 : const char *pszCur = nullptr;
4795 :
4796 749 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4797 2 : pszCur = pszURL + 26;
4798 747 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4799 1 : pszCur = pszURL + 27;
4800 746 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4801 745 : pszCur = pszURL + 30;
4802 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4803 1 : pszCur = pszURL + 31;
4804 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4805 0 : pszCur = pszURL + 23;
4806 : else
4807 : {
4808 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4809 : pszURL);
4810 0 : return OGRERR_FAILURE;
4811 : }
4812 :
4813 749 : if (*pszCur == '\0')
4814 : {
4815 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4816 0 : return OGRERR_FAILURE;
4817 : }
4818 :
4819 : /* -------------------------------------------------------------------- */
4820 : /* Clear any existing definition. */
4821 : /* -------------------------------------------------------------------- */
4822 749 : Clear();
4823 :
4824 749 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4825 : {
4826 : /* --------------------------------------------------------------------
4827 : */
4828 : /* It's a compound CRS, of the form: */
4829 : /* */
4830 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4831 : /* --------------------------------------------------------------------
4832 : */
4833 1 : pszCur += 12;
4834 :
4835 : // Extract each component CRS URL.
4836 1 : int iComponentUrl = 2;
4837 :
4838 2 : CPLString osName = "";
4839 1 : Clear();
4840 :
4841 3 : while (iComponentUrl != -1)
4842 : {
4843 2 : char searchStr[15] = {};
4844 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4845 :
4846 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
4847 :
4848 : // Figure out the next component URL.
4849 2 : char *pszComponentUrl = nullptr;
4850 :
4851 2 : if (pszUrlEnd)
4852 : {
4853 1 : size_t nLen = pszUrlEnd - pszCur;
4854 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4855 1 : strncpy(pszComponentUrl, pszCur, nLen);
4856 1 : pszComponentUrl[nLen] = '\0';
4857 :
4858 1 : ++iComponentUrl;
4859 1 : pszCur += nLen + strlen(searchStr);
4860 : }
4861 : else
4862 : {
4863 1 : if (iComponentUrl == 2)
4864 : {
4865 0 : CPLError(CE_Failure, CPLE_AppDefined,
4866 : "Compound CRS URLs must have at least two "
4867 : "component CRSs.");
4868 0 : return OGRERR_FAILURE;
4869 : }
4870 : else
4871 : {
4872 1 : pszComponentUrl = CPLStrdup(pszCur);
4873 : // no more components
4874 1 : iComponentUrl = -1;
4875 : }
4876 : }
4877 :
4878 2 : OGRSpatialReference oComponentSRS;
4879 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4880 :
4881 2 : CPLFree(pszComponentUrl);
4882 2 : pszComponentUrl = nullptr;
4883 :
4884 2 : if (eStatus == OGRERR_NONE)
4885 : {
4886 2 : if (osName.length() != 0)
4887 : {
4888 1 : osName += " + ";
4889 : }
4890 2 : osName += oComponentSRS.GetRoot()->GetValue();
4891 2 : SetNode("COMPD_CS", osName);
4892 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4893 : }
4894 : else
4895 0 : return eStatus;
4896 : }
4897 :
4898 1 : return OGRERR_NONE;
4899 : }
4900 :
4901 : /* -------------------------------------------------------------------- */
4902 : /* It's a normal CRS URL, of the form: */
4903 : /* */
4904 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
4905 : /* -------------------------------------------------------------------- */
4906 748 : ++pszCur;
4907 748 : const char *pszAuthority = pszCur;
4908 :
4909 : // skip authority
4910 103675 : while (*pszCur != '/' && *pszCur)
4911 102927 : pszCur++;
4912 748 : if (*pszCur == '/')
4913 747 : pszCur++;
4914 :
4915 : // skip version
4916 1617 : while (*pszCur != '/' && *pszCur)
4917 869 : pszCur++;
4918 748 : if (*pszCur == '/')
4919 747 : pszCur++;
4920 :
4921 748 : const char *pszCode = pszCur;
4922 :
4923 748 : return importFromURNPart(pszAuthority, pszCode, pszURL);
4924 : #endif
4925 : }
4926 :
4927 : /************************************************************************/
4928 : /* importFromWMSAUTO() */
4929 : /************************************************************************/
4930 :
4931 : /**
4932 : * \brief Initialize from WMSAUTO string.
4933 : *
4934 : * Note that the WMS 1.3 specification does not include the
4935 : * units code, while apparently earlier specs do. We try to
4936 : * guess around this.
4937 : *
4938 : * @param pszDefinition the WMSAUTO string
4939 : *
4940 : * @return OGRERR_NONE on success or an error code.
4941 : */
4942 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4943 :
4944 : {
4945 6 : TAKE_OPTIONAL_LOCK();
4946 :
4947 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4948 : if (strlen(pszDefinition) >= 10000)
4949 : {
4950 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4951 : return OGRERR_CORRUPT_DATA;
4952 : }
4953 :
4954 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
4955 : if (!obj)
4956 : {
4957 : return OGRERR_FAILURE;
4958 : }
4959 : Clear();
4960 : d->setPjCRS(obj);
4961 : return OGRERR_NONE;
4962 : #else
4963 : int nProjId, nUnitsId;
4964 3 : double dfRefLong, dfRefLat = 0.0;
4965 :
4966 : /* -------------------------------------------------------------------- */
4967 : /* Tokenize */
4968 : /* -------------------------------------------------------------------- */
4969 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4970 3 : pszDefinition += 5;
4971 :
4972 : char **papszTokens =
4973 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
4974 :
4975 3 : if (CSLCount(papszTokens) == 4)
4976 : {
4977 0 : nProjId = atoi(papszTokens[0]);
4978 0 : nUnitsId = atoi(papszTokens[1]);
4979 0 : dfRefLong = CPLAtof(papszTokens[2]);
4980 0 : dfRefLat = CPLAtof(papszTokens[3]);
4981 : }
4982 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
4983 : {
4984 0 : nProjId = atoi(papszTokens[0]);
4985 0 : nUnitsId = atoi(papszTokens[1]);
4986 0 : dfRefLong = CPLAtof(papszTokens[2]);
4987 0 : dfRefLat = 0.0;
4988 : }
4989 3 : else if (CSLCount(papszTokens) == 3)
4990 : {
4991 2 : nProjId = atoi(papszTokens[0]);
4992 2 : nUnitsId = 9001;
4993 2 : dfRefLong = CPLAtof(papszTokens[1]);
4994 2 : dfRefLat = CPLAtof(papszTokens[2]);
4995 : }
4996 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
4997 : {
4998 0 : nProjId = atoi(papszTokens[0]);
4999 0 : nUnitsId = 9001;
5000 0 : dfRefLong = CPLAtof(papszTokens[1]);
5001 : }
5002 : else
5003 : {
5004 1 : CSLDestroy(papszTokens);
5005 1 : CPLError(CE_Failure, CPLE_AppDefined,
5006 : "AUTO projection has wrong number of arguments, expected\n"
5007 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5008 : "AUTO:proj_id,ref_long,ref_lat");
5009 1 : return OGRERR_FAILURE;
5010 : }
5011 :
5012 2 : CSLDestroy(papszTokens);
5013 2 : papszTokens = nullptr;
5014 :
5015 : /* -------------------------------------------------------------------- */
5016 : /* Build coordsys. */
5017 : /* -------------------------------------------------------------------- */
5018 2 : Clear();
5019 :
5020 : /* -------------------------------------------------------------------- */
5021 : /* Set WGS84. */
5022 : /* -------------------------------------------------------------------- */
5023 2 : SetWellKnownGeogCS("WGS84");
5024 :
5025 2 : switch (nProjId)
5026 : {
5027 2 : case 42001: // Auto UTM
5028 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5029 : dfRefLat >= 0.0);
5030 2 : break;
5031 :
5032 0 : case 42002: // Auto TM (strangely very UTM-like).
5033 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5034 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5035 0 : break;
5036 :
5037 0 : case 42003: // Auto Orthographic.
5038 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5039 0 : break;
5040 :
5041 0 : case 42004: // Auto Equirectangular
5042 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5043 0 : break;
5044 :
5045 0 : case 42005:
5046 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5047 0 : break;
5048 :
5049 0 : default:
5050 0 : CPLError(CE_Failure, CPLE_AppDefined,
5051 : "Unsupported projection id in importFromWMSAUTO(): %d",
5052 : nProjId);
5053 0 : return OGRERR_FAILURE;
5054 : }
5055 :
5056 : /* -------------------------------------------------------------------- */
5057 : /* Set units. */
5058 : /* -------------------------------------------------------------------- */
5059 :
5060 2 : switch (nUnitsId)
5061 : {
5062 2 : case 9001:
5063 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5064 2 : break;
5065 :
5066 0 : case 9002:
5067 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5068 0 : break;
5069 :
5070 0 : case 9003:
5071 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5072 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5073 0 : break;
5074 :
5075 0 : default:
5076 0 : CPLError(CE_Failure, CPLE_AppDefined,
5077 : "Unsupported units code (%d).", nUnitsId);
5078 0 : return OGRERR_FAILURE;
5079 : break;
5080 : }
5081 :
5082 2 : return OGRERR_NONE;
5083 : #endif
5084 : }
5085 :
5086 : /************************************************************************/
5087 : /* GetSemiMajor() */
5088 : /************************************************************************/
5089 :
5090 : /**
5091 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5092 : *
5093 : * This method does the same thing as the C function OSRGetSemiMajor().
5094 : *
5095 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5096 : * can be found.
5097 : *
5098 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5099 : */
5100 :
5101 5816 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5102 :
5103 : {
5104 11632 : TAKE_OPTIONAL_LOCK();
5105 :
5106 5816 : if (pnErr != nullptr)
5107 3129 : *pnErr = OGRERR_FAILURE;
5108 :
5109 5816 : d->refreshProjObj();
5110 5816 : if (!d->m_pj_crs)
5111 111 : return SRS_WGS84_SEMIMAJOR;
5112 :
5113 5705 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5114 5705 : if (!ellps)
5115 5 : return SRS_WGS84_SEMIMAJOR;
5116 :
5117 5700 : double dfSemiMajor = 0.0;
5118 5700 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5119 : nullptr, nullptr, nullptr);
5120 5700 : proj_destroy(ellps);
5121 :
5122 5700 : if (dfSemiMajor > 0)
5123 : {
5124 5700 : if (pnErr != nullptr)
5125 3015 : *pnErr = OGRERR_NONE;
5126 5700 : return dfSemiMajor;
5127 : }
5128 :
5129 0 : return SRS_WGS84_SEMIMAJOR;
5130 : }
5131 :
5132 : /************************************************************************/
5133 : /* OSRGetSemiMajor() */
5134 : /************************************************************************/
5135 :
5136 : /**
5137 : * \brief Get spheroid semi major axis.
5138 : *
5139 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5140 : */
5141 59 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5142 :
5143 : {
5144 59 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5145 :
5146 59 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5147 : }
5148 :
5149 : /************************************************************************/
5150 : /* GetInvFlattening() */
5151 : /************************************************************************/
5152 :
5153 : /**
5154 : * \brief Get spheroid inverse flattening.
5155 : *
5156 : * This method does the same thing as the C function OSRGetInvFlattening().
5157 : *
5158 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5159 : * can be found.
5160 : *
5161 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5162 : */
5163 :
5164 4112 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5165 :
5166 : {
5167 8224 : TAKE_OPTIONAL_LOCK();
5168 :
5169 4112 : if (pnErr != nullptr)
5170 3056 : *pnErr = OGRERR_FAILURE;
5171 :
5172 4112 : d->refreshProjObj();
5173 4112 : if (!d->m_pj_crs)
5174 111 : return SRS_WGS84_INVFLATTENING;
5175 :
5176 4001 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5177 4001 : if (!ellps)
5178 2 : return SRS_WGS84_INVFLATTENING;
5179 :
5180 3999 : double dfInvFlattening = -1.0;
5181 3999 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5182 : nullptr, &dfInvFlattening);
5183 3999 : proj_destroy(ellps);
5184 :
5185 3999 : if (dfInvFlattening >= 0.0)
5186 : {
5187 3999 : if (pnErr != nullptr)
5188 2945 : *pnErr = OGRERR_NONE;
5189 3999 : return dfInvFlattening;
5190 : }
5191 :
5192 0 : return SRS_WGS84_INVFLATTENING;
5193 : }
5194 :
5195 : /************************************************************************/
5196 : /* OSRGetInvFlattening() */
5197 : /************************************************************************/
5198 :
5199 : /**
5200 : * \brief Get spheroid inverse flattening.
5201 : *
5202 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5203 : */
5204 11 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5205 :
5206 : {
5207 11 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5208 :
5209 11 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5210 : }
5211 :
5212 : /************************************************************************/
5213 : /* GetEccentricity() */
5214 : /************************************************************************/
5215 :
5216 : /**
5217 : * \brief Get spheroid eccentricity
5218 : *
5219 : * @return eccentricity (or -1 in case of error)
5220 : * @since GDAL 2.3
5221 : */
5222 :
5223 0 : double OGRSpatialReference::GetEccentricity() const
5224 :
5225 : {
5226 0 : OGRErr eErr = OGRERR_NONE;
5227 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5228 0 : if (eErr != OGRERR_NONE)
5229 : {
5230 0 : return -1.0;
5231 : }
5232 0 : if (dfInvFlattening == 0.0)
5233 0 : return 0.0;
5234 0 : if (dfInvFlattening < 0.5)
5235 0 : return -1.0;
5236 0 : return sqrt(2.0 / dfInvFlattening -
5237 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5238 : }
5239 :
5240 : /************************************************************************/
5241 : /* GetSquaredEccentricity() */
5242 : /************************************************************************/
5243 :
5244 : /**
5245 : * \brief Get spheroid squared eccentricity
5246 : *
5247 : * @return squared eccentricity (or -1 in case of error)
5248 : * @since GDAL 2.3
5249 : */
5250 :
5251 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5252 :
5253 : {
5254 0 : OGRErr eErr = OGRERR_NONE;
5255 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5256 0 : if (eErr != OGRERR_NONE)
5257 : {
5258 0 : return -1.0;
5259 : }
5260 0 : if (dfInvFlattening == 0.0)
5261 0 : return 0.0;
5262 0 : if (dfInvFlattening < 0.5)
5263 0 : return -1.0;
5264 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5265 : }
5266 :
5267 : /************************************************************************/
5268 : /* GetSemiMinor() */
5269 : /************************************************************************/
5270 :
5271 : /**
5272 : * \brief Get spheroid semi minor axis.
5273 : *
5274 : * This method does the same thing as the C function OSRGetSemiMinor().
5275 : *
5276 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5277 : * can be found.
5278 : *
5279 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5280 : */
5281 :
5282 631 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5283 :
5284 : {
5285 631 : const double dfSemiMajor = GetSemiMajor(pnErr);
5286 631 : const double dfInvFlattening = GetInvFlattening(pnErr);
5287 :
5288 631 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5289 : }
5290 :
5291 : /************************************************************************/
5292 : /* OSRGetSemiMinor() */
5293 : /************************************************************************/
5294 :
5295 : /**
5296 : * \brief Get spheroid semi minor axis.
5297 : *
5298 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5299 : */
5300 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5301 :
5302 : {
5303 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5304 :
5305 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5306 : }
5307 :
5308 : /************************************************************************/
5309 : /* SetLocalCS() */
5310 : /************************************************************************/
5311 :
5312 : /**
5313 : * \brief Set the user visible LOCAL_CS name.
5314 : *
5315 : * This method is the same as the C function OSRSetLocalCS().
5316 : *
5317 : * This method will ensure a LOCAL_CS node is created as the root,
5318 : * and set the provided name on it. It must be used before SetLinearUnits().
5319 : *
5320 : * @param pszName the user visible name to assign. Not used as a key.
5321 : *
5322 : * @return OGRERR_NONE on success.
5323 : */
5324 :
5325 2888 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5326 :
5327 : {
5328 5776 : TAKE_OPTIONAL_LOCK();
5329 :
5330 2888 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5331 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5332 : {
5333 2888 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5334 : }
5335 : else
5336 : {
5337 0 : CPLDebug("OGR",
5338 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5339 : "It appears an incompatible object already exists.",
5340 : pszName);
5341 0 : return OGRERR_FAILURE;
5342 : }
5343 :
5344 2888 : return OGRERR_NONE;
5345 : }
5346 :
5347 : /************************************************************************/
5348 : /* OSRSetLocalCS() */
5349 : /************************************************************************/
5350 :
5351 : /**
5352 : * \brief Set the user visible LOCAL_CS name.
5353 : *
5354 : * This function is the same as OGRSpatialReference::SetLocalCS()
5355 : */
5356 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5357 :
5358 : {
5359 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5360 :
5361 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5362 : }
5363 :
5364 : /************************************************************************/
5365 : /* SetGeocCS() */
5366 : /************************************************************************/
5367 :
5368 : /**
5369 : * \brief Set the user visible GEOCCS name.
5370 : *
5371 : * This method is the same as the C function OSRSetGeocCS().
5372 :
5373 : * This method will ensure a GEOCCS node is created as the root,
5374 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5375 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5376 : * the GEOGCS.
5377 : *
5378 : * @param pszName the user visible name to assign. Not used as a key.
5379 : *
5380 : * @return OGRERR_NONE on success.
5381 : *
5382 : * @since OGR 1.9.0
5383 : */
5384 :
5385 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5386 :
5387 : {
5388 12 : TAKE_OPTIONAL_LOCK();
5389 :
5390 6 : OGRErr eErr = OGRERR_NONE;
5391 6 : d->refreshProjObj();
5392 6 : d->demoteFromBoundCRS();
5393 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5394 : {
5395 3 : d->setPjCRS(proj_create_geocentric_crs(
5396 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5397 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5398 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5399 : "Metre", 1.0));
5400 : }
5401 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5402 : {
5403 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5404 : }
5405 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5406 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5407 : {
5408 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5409 : #if PROJ_VERSION_MAJOR > 7 || \
5410 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5411 : if (datum == nullptr)
5412 : {
5413 : datum =
5414 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5415 : }
5416 : #endif
5417 1 : if (datum == nullptr)
5418 : {
5419 0 : d->undoDemoteFromBoundCRS();
5420 0 : return OGRERR_FAILURE;
5421 : }
5422 :
5423 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5424 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5425 : 0.0);
5426 1 : d->setPjCRS(pj_crs);
5427 :
5428 1 : proj_destroy(datum);
5429 : }
5430 : else
5431 : {
5432 1 : CPLDebug("OGR",
5433 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5434 : "It appears an incompatible object already exists.",
5435 : pszName);
5436 1 : eErr = OGRERR_FAILURE;
5437 : }
5438 6 : d->undoDemoteFromBoundCRS();
5439 :
5440 6 : return eErr;
5441 : }
5442 :
5443 : /************************************************************************/
5444 : /* OSRSetGeocCS() */
5445 : /************************************************************************/
5446 :
5447 : /**
5448 : * \brief Set the user visible PROJCS name.
5449 : *
5450 : * This function is the same as OGRSpatialReference::SetGeocCS()
5451 : *
5452 : * @since OGR 1.9.0
5453 : */
5454 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5455 :
5456 : {
5457 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5458 :
5459 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5460 : }
5461 :
5462 : /************************************************************************/
5463 : /* SetVertCS() */
5464 : /************************************************************************/
5465 :
5466 : /**
5467 : * \brief Set the user visible VERT_CS name.
5468 : *
5469 : * This method is the same as the C function OSRSetVertCS().
5470 :
5471 : * This method will ensure a VERT_CS node is created if needed. If the
5472 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5473 : * turned into a COMPD_CS.
5474 : *
5475 : * @param pszVertCSName the user visible name of the vertical coordinate
5476 : * system. Not used as a key.
5477 : *
5478 : * @param pszVertDatumName the user visible name of the vertical datum. It
5479 : * is helpful if this matches the EPSG name.
5480 : *
5481 : * @param nVertDatumType the OGC vertical datum type. Ignored
5482 : *
5483 : * @return OGRERR_NONE on success.
5484 : *
5485 : * @since OGR 1.9.0
5486 : */
5487 :
5488 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5489 : const char *pszVertDatumName,
5490 : int nVertDatumType)
5491 :
5492 : {
5493 1 : TAKE_OPTIONAL_LOCK();
5494 :
5495 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5496 :
5497 1 : d->refreshProjObj();
5498 :
5499 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5500 : pszVertDatumName, nullptr, 0.0);
5501 :
5502 : /* -------------------------------------------------------------------- */
5503 : /* Handle the case where we want to make a compound coordinate */
5504 : /* system. */
5505 : /* -------------------------------------------------------------------- */
5506 1 : if (IsProjected() || IsGeographic())
5507 : {
5508 1 : auto compoundCRS = proj_create_compound_crs(
5509 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5510 1 : proj_destroy(vertCRS);
5511 1 : d->setPjCRS(compoundCRS);
5512 : }
5513 : else
5514 : {
5515 0 : d->setPjCRS(vertCRS);
5516 : }
5517 2 : return OGRERR_NONE;
5518 : }
5519 :
5520 : /************************************************************************/
5521 : /* OSRSetVertCS() */
5522 : /************************************************************************/
5523 :
5524 : /**
5525 : * \brief Setup the vertical coordinate system.
5526 : *
5527 : * This function is the same as OGRSpatialReference::SetVertCS()
5528 : *
5529 : * @since OGR 1.9.0
5530 : */
5531 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5532 : const char *pszVertDatumName, int nVertDatumType)
5533 :
5534 : {
5535 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5536 :
5537 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5538 0 : nVertDatumType);
5539 : }
5540 :
5541 : /************************************************************************/
5542 : /* SetCompoundCS() */
5543 : /************************************************************************/
5544 :
5545 : /**
5546 : * \brief Setup a compound coordinate system.
5547 : *
5548 : * This method is the same as the C function OSRSetCompoundCS().
5549 :
5550 : * This method is replace the current SRS with a COMPD_CS coordinate system
5551 : * consisting of the passed in horizontal and vertical coordinate systems.
5552 : *
5553 : * @param pszName the name of the compound coordinate system.
5554 : *
5555 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5556 : *
5557 : * @param poVertSRS the vertical SRS (VERT_CS).
5558 : *
5559 : * @return OGRERR_NONE on success.
5560 : */
5561 :
5562 93 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5563 : const OGRSpatialReference *poHorizSRS,
5564 : const OGRSpatialReference *poVertSRS)
5565 :
5566 : {
5567 186 : TAKE_OPTIONAL_LOCK();
5568 :
5569 : /* -------------------------------------------------------------------- */
5570 : /* Verify these are legal horizontal and vertical coordinate */
5571 : /* systems. */
5572 : /* -------------------------------------------------------------------- */
5573 93 : if (!poVertSRS->IsVertical())
5574 : {
5575 0 : CPLError(CE_Failure, CPLE_AppDefined,
5576 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5577 0 : return OGRERR_FAILURE;
5578 : }
5579 93 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5580 : {
5581 0 : CPLError(CE_Failure, CPLE_AppDefined,
5582 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5583 : "GEOGCS.");
5584 0 : return OGRERR_FAILURE;
5585 : }
5586 :
5587 : /* -------------------------------------------------------------------- */
5588 : /* Replace with compound srs. */
5589 : /* -------------------------------------------------------------------- */
5590 93 : Clear();
5591 :
5592 93 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5593 93 : poHorizSRS->d->m_pj_crs,
5594 93 : poVertSRS->d->m_pj_crs);
5595 93 : d->setPjCRS(compoundCRS);
5596 :
5597 93 : return OGRERR_NONE;
5598 : }
5599 :
5600 : /************************************************************************/
5601 : /* OSRSetCompoundCS() */
5602 : /************************************************************************/
5603 :
5604 : /**
5605 : * \brief Setup a compound coordinate system.
5606 : *
5607 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5608 : */
5609 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5610 : OGRSpatialReferenceH hHorizSRS,
5611 : OGRSpatialReferenceH hVertSRS)
5612 :
5613 : {
5614 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5615 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5616 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5617 :
5618 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5619 16 : ToPointer(hVertSRS));
5620 : }
5621 :
5622 : /************************************************************************/
5623 : /* SetProjCS() */
5624 : /************************************************************************/
5625 :
5626 : /**
5627 : * \brief Set the user visible PROJCS name.
5628 : *
5629 : * This method is the same as the C function OSRSetProjCS().
5630 : *
5631 : * This method will ensure a PROJCS node is created as the root,
5632 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5633 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5634 : *
5635 : * @param pszName the user visible name to assign. Not used as a key.
5636 : *
5637 : * @return OGRERR_NONE on success.
5638 : */
5639 :
5640 3856 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5641 :
5642 : {
5643 3856 : TAKE_OPTIONAL_LOCK();
5644 :
5645 3856 : d->refreshProjObj();
5646 3856 : d->demoteFromBoundCRS();
5647 3856 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5648 : {
5649 481 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5650 : }
5651 : else
5652 : {
5653 3375 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5654 : nullptr, nullptr, nullptr,
5655 : nullptr, nullptr, 0, nullptr);
5656 3375 : auto cs = proj_create_cartesian_2D_cs(
5657 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5658 :
5659 3375 : auto projCRS = proj_create_projected_crs(
5660 3375 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5661 3375 : proj_destroy(dummyConv);
5662 3375 : proj_destroy(cs);
5663 :
5664 3375 : d->setPjCRS(projCRS);
5665 : }
5666 3856 : d->undoDemoteFromBoundCRS();
5667 7712 : return OGRERR_NONE;
5668 : }
5669 :
5670 : /************************************************************************/
5671 : /* OSRSetProjCS() */
5672 : /************************************************************************/
5673 :
5674 : /**
5675 : * \brief Set the user visible PROJCS name.
5676 : *
5677 : * This function is the same as OGRSpatialReference::SetProjCS()
5678 : */
5679 7 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5680 :
5681 : {
5682 7 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5683 :
5684 7 : return ToPointer(hSRS)->SetProjCS(pszName);
5685 : }
5686 :
5687 : /************************************************************************/
5688 : /* SetProjection() */
5689 : /************************************************************************/
5690 :
5691 : /**
5692 : * \brief Set a projection name.
5693 : *
5694 : * This method is the same as the C function OSRSetProjection().
5695 : *
5696 : * @param pszProjection the projection name, which should be selected from
5697 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5698 : *
5699 : * @return OGRERR_NONE on success.
5700 : */
5701 :
5702 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5703 :
5704 : {
5705 46 : TAKE_OPTIONAL_LOCK();
5706 :
5707 23 : OGR_SRSNode *poGeogCS = nullptr;
5708 :
5709 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5710 : {
5711 4 : poGeogCS = d->m_poRoot;
5712 4 : d->m_poRoot = nullptr;
5713 : }
5714 :
5715 23 : if (!GetAttrNode("PROJCS"))
5716 : {
5717 11 : SetNode("PROJCS", "unnamed");
5718 : }
5719 :
5720 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5721 23 : if (eErr != OGRERR_NONE)
5722 0 : return eErr;
5723 :
5724 23 : if (poGeogCS != nullptr)
5725 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5726 :
5727 23 : return OGRERR_NONE;
5728 : }
5729 :
5730 : /************************************************************************/
5731 : /* OSRSetProjection() */
5732 : /************************************************************************/
5733 :
5734 : /**
5735 : * \brief Set a projection name.
5736 : *
5737 : * This function is the same as OGRSpatialReference::SetProjection()
5738 : */
5739 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5740 :
5741 : {
5742 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5743 :
5744 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5745 : }
5746 :
5747 : /************************************************************************/
5748 : /* GetWKT2ProjectionMethod() */
5749 : /************************************************************************/
5750 :
5751 : /**
5752 : * \brief Returns info on the projection method, based on WKT2 naming
5753 : * conventions.
5754 : *
5755 : * The returned strings are short lived and should be considered to be
5756 : * invalidated by any further call to the GDAL API.
5757 : *
5758 : * @param[out] ppszMethodName Pointer to a string that will receive the
5759 : * projection method name.
5760 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5761 : * receive the name of the authority that defines the projection method.
5762 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5763 : * an authority.
5764 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5765 : * receive the code that defines the projection method.
5766 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5767 : * an authority.
5768 : *
5769 : * @return OGRERR_NONE on success.
5770 : */
5771 : OGRErr
5772 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5773 : const char **ppszMethodAuthName,
5774 : const char **ppszMethodCode) const
5775 : {
5776 2 : TAKE_OPTIONAL_LOCK();
5777 :
5778 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5779 1 : if (!conv)
5780 0 : return OGRERR_FAILURE;
5781 1 : const char *pszTmpMethodName = "";
5782 1 : const char *pszTmpMethodAuthName = "";
5783 1 : const char *pszTmpMethodCode = "";
5784 1 : int ret = proj_coordoperation_get_method_info(
5785 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5786 : &pszTmpMethodCode);
5787 : // "Internalize" temporary strings returned by PROJ
5788 1 : CPLAssert(pszTmpMethodName);
5789 1 : if (ppszMethodName)
5790 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5791 1 : if (ppszMethodAuthName)
5792 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5793 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5794 0 : : nullptr;
5795 1 : if (ppszMethodCode)
5796 0 : *ppszMethodCode =
5797 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5798 1 : proj_destroy(conv);
5799 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5800 : }
5801 :
5802 : /************************************************************************/
5803 : /* SetProjParm() */
5804 : /************************************************************************/
5805 :
5806 : /**
5807 : * \brief Set a projection parameter value.
5808 : *
5809 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5810 : *
5811 : * This method is the same as the C function OSRSetProjParm().
5812 : *
5813 : * Please check https://gdal.org/proj_list pages for
5814 : * legal parameter names for specific projections.
5815 : *
5816 : *
5817 : * @param pszParamName the parameter name, which should be selected from
5818 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5819 : *
5820 : * @param dfValue value to assign.
5821 : *
5822 : * @return OGRERR_NONE on success.
5823 : */
5824 :
5825 133 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5826 : double dfValue)
5827 :
5828 : {
5829 266 : TAKE_OPTIONAL_LOCK();
5830 :
5831 133 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5832 :
5833 133 : if (poPROJCS == nullptr)
5834 5 : return OGRERR_FAILURE;
5835 :
5836 128 : char szValue[64] = {'\0'};
5837 128 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5838 :
5839 : /* -------------------------------------------------------------------- */
5840 : /* Try to find existing parameter with this name. */
5841 : /* -------------------------------------------------------------------- */
5842 1040 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5843 : {
5844 953 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5845 :
5846 1256 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5847 1256 : poParam->GetChildCount() == 2 &&
5848 303 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5849 : {
5850 41 : poParam->GetChild(1)->SetValue(szValue);
5851 41 : return OGRERR_NONE;
5852 : }
5853 : }
5854 :
5855 : /* -------------------------------------------------------------------- */
5856 : /* Otherwise create a new parameter and append. */
5857 : /* -------------------------------------------------------------------- */
5858 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5859 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
5860 87 : poParam->AddChild(new OGR_SRSNode(szValue));
5861 :
5862 87 : poPROJCS->AddChild(poParam);
5863 :
5864 87 : return OGRERR_NONE;
5865 : }
5866 :
5867 : /************************************************************************/
5868 : /* OSRSetProjParm() */
5869 : /************************************************************************/
5870 :
5871 : /**
5872 : * \brief Set a projection parameter value.
5873 : *
5874 : * This function is the same as OGRSpatialReference::SetProjParm()
5875 : */
5876 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5877 : double dfValue)
5878 :
5879 : {
5880 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5881 :
5882 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5883 : }
5884 :
5885 : /************************************************************************/
5886 : /* FindProjParm() */
5887 : /************************************************************************/
5888 :
5889 : /**
5890 : * \brief Return the child index of the named projection parameter on
5891 : * its parent PROJCS node.
5892 : *
5893 : * @param pszParameter projection parameter to look for
5894 : * @param poPROJCS projection CS node to look in. If NULL is passed,
5895 : * the PROJCS node of the SpatialReference object will be searched.
5896 : *
5897 : * @return the child index of the named projection parameter. -1 on failure
5898 : */
5899 4432 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
5900 : const OGR_SRSNode *poPROJCS) const
5901 :
5902 : {
5903 8864 : TAKE_OPTIONAL_LOCK();
5904 :
5905 4432 : if (poPROJCS == nullptr)
5906 0 : poPROJCS = GetAttrNode("PROJCS");
5907 :
5908 4432 : if (poPROJCS == nullptr)
5909 0 : return -1;
5910 :
5911 : /* -------------------------------------------------------------------- */
5912 : /* Search for requested parameter. */
5913 : /* -------------------------------------------------------------------- */
5914 4432 : bool bIsWKT2 = false;
5915 28266 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5916 : {
5917 27855 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5918 :
5919 27855 : if (poParameter->GetChildCount() >= 2)
5920 : {
5921 19016 : const char *pszValue = poParameter->GetValue();
5922 31959 : if (EQUAL(pszValue, "PARAMETER") &&
5923 12943 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5924 : pszParameter))
5925 : {
5926 4021 : return iChild;
5927 : }
5928 14995 : else if (EQUAL(pszValue, "METHOD"))
5929 : {
5930 25 : bIsWKT2 = true;
5931 : }
5932 : }
5933 : }
5934 :
5935 : /* -------------------------------------------------------------------- */
5936 : /* Try similar names, for selected parameters. */
5937 : /* -------------------------------------------------------------------- */
5938 411 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5939 : {
5940 184 : if (bIsWKT2)
5941 : {
5942 4 : int iChild = FindProjParm(
5943 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5944 4 : if (iChild == -1)
5945 3 : iChild = FindProjParm(
5946 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5947 4 : return iChild;
5948 : }
5949 180 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5950 : }
5951 :
5952 227 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5953 : {
5954 27 : if (bIsWKT2)
5955 : {
5956 5 : int iChild = FindProjParm(
5957 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5958 5 : if (iChild == -1)
5959 0 : iChild = FindProjParm(
5960 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5961 5 : return iChild;
5962 : }
5963 22 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5964 22 : if (iChild == -1)
5965 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5966 22 : return iChild;
5967 : }
5968 :
5969 200 : return -1;
5970 : }
5971 :
5972 : /************************************************************************/
5973 : /* GetProjParm() */
5974 : /************************************************************************/
5975 :
5976 : /**
5977 : * \brief Fetch a projection parameter value.
5978 : *
5979 : * NOTE: This code should be modified to translate non degree angles into
5980 : * degrees based on the GEOGCS unit. This has not yet been done.
5981 : *
5982 : * This method is the same as the C function OSRGetProjParm().
5983 : *
5984 : * @param pszName the name of the parameter to fetch, from the set of
5985 : * SRS_PP codes in ogr_srs_api.h.
5986 : *
5987 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
5988 : *
5989 : * @param pnErr place to put error code on failure. Ignored if NULL.
5990 : *
5991 : * @return value of parameter.
5992 : */
5993 :
5994 4467 : double OGRSpatialReference::GetProjParm(const char *pszName,
5995 : double dfDefaultValue,
5996 : OGRErr *pnErr) const
5997 :
5998 : {
5999 8934 : TAKE_OPTIONAL_LOCK();
6000 :
6001 4467 : d->refreshProjObj();
6002 4467 : GetRoot(); // force update of d->m_bNodesWKT2
6003 :
6004 4467 : if (pnErr != nullptr)
6005 3486 : *pnErr = OGRERR_NONE;
6006 :
6007 : /* -------------------------------------------------------------------- */
6008 : /* Find the desired parameter. */
6009 : /* -------------------------------------------------------------------- */
6010 : const OGR_SRSNode *poPROJCS =
6011 4467 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6012 4467 : if (poPROJCS == nullptr)
6013 : {
6014 249 : if (pnErr != nullptr)
6015 248 : *pnErr = OGRERR_FAILURE;
6016 249 : return dfDefaultValue;
6017 : }
6018 :
6019 4218 : const int iChild = FindProjParm(pszName, poPROJCS);
6020 4218 : if (iChild == -1)
6021 : {
6022 197 : if (IsProjected() && GetAxesCount() == 3)
6023 : {
6024 3 : OGRSpatialReference *poSRSTmp = Clone();
6025 3 : poSRSTmp->DemoteTo2D(nullptr);
6026 : const double dfRet =
6027 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6028 3 : delete poSRSTmp;
6029 3 : return dfRet;
6030 : }
6031 :
6032 194 : if (pnErr != nullptr)
6033 172 : *pnErr = OGRERR_FAILURE;
6034 194 : return dfDefaultValue;
6035 : }
6036 :
6037 4021 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6038 4021 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6039 : }
6040 :
6041 : /************************************************************************/
6042 : /* OSRGetProjParm() */
6043 : /************************************************************************/
6044 :
6045 : /**
6046 : * \brief Fetch a projection parameter value.
6047 : *
6048 : * This function is the same as OGRSpatialReference::GetProjParm()
6049 : */
6050 91 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6051 : double dfDefaultValue, OGRErr *pnErr)
6052 :
6053 : {
6054 91 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6055 :
6056 91 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6057 : }
6058 :
6059 : /************************************************************************/
6060 : /* GetNormProjParm() */
6061 : /************************************************************************/
6062 :
6063 : /**
6064 : * \brief Fetch a normalized projection parameter value.
6065 : *
6066 : * This method is the same as GetProjParm() except that the value of
6067 : * the parameter is "normalized" into degrees or meters depending on
6068 : * whether it is linear or angular.
6069 : *
6070 : * This method is the same as the C function OSRGetNormProjParm().
6071 : *
6072 : * @param pszName the name of the parameter to fetch, from the set of
6073 : * SRS_PP codes in ogr_srs_api.h.
6074 : *
6075 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6076 : *
6077 : * @param pnErr place to put error code on failure. Ignored if NULL.
6078 : *
6079 : * @return value of parameter.
6080 : */
6081 :
6082 3460 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6083 : double dfDefaultValue,
6084 : OGRErr *pnErr) const
6085 :
6086 : {
6087 6920 : TAKE_OPTIONAL_LOCK();
6088 :
6089 3460 : GetNormInfo();
6090 :
6091 3460 : OGRErr nError = OGRERR_NONE;
6092 3460 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6093 3460 : if (pnErr != nullptr)
6094 0 : *pnErr = nError;
6095 :
6096 : // If we got the default just return it unadjusted.
6097 3460 : if (nError != OGRERR_NONE)
6098 420 : return dfRawResult;
6099 :
6100 3040 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6101 8 : dfRawResult *= d->dfToDegrees;
6102 :
6103 3040 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6104 5 : return dfRawResult * d->dfToMeter;
6105 :
6106 3035 : return dfRawResult;
6107 : }
6108 :
6109 : /************************************************************************/
6110 : /* OSRGetNormProjParm() */
6111 : /************************************************************************/
6112 :
6113 : /**
6114 : * \brief This function is the same as OGRSpatialReference::
6115 : *
6116 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6117 : */
6118 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6119 : double dfDefaultValue, OGRErr *pnErr)
6120 :
6121 : {
6122 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6123 :
6124 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6125 : }
6126 :
6127 : /************************************************************************/
6128 : /* SetNormProjParm() */
6129 : /************************************************************************/
6130 :
6131 : /**
6132 : * \brief Set a projection parameter with a normalized value.
6133 : *
6134 : * This method is the same as SetProjParm() except that the value of
6135 : * the parameter passed in is assumed to be in "normalized" form (decimal
6136 : * degrees for angular values, meters for linear values. The values are
6137 : * converted in a form suitable for the GEOGCS and linear units in effect.
6138 : *
6139 : * This method is the same as the C function OSRSetNormProjParm().
6140 : *
6141 : * @param pszName the parameter name, which should be selected from
6142 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6143 : *
6144 : * @param dfValue value to assign.
6145 : *
6146 : * @return OGRERR_NONE on success.
6147 : */
6148 :
6149 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6150 :
6151 : {
6152 182 : TAKE_OPTIONAL_LOCK();
6153 :
6154 91 : GetNormInfo();
6155 :
6156 91 : if (d->dfToDegrees != 0.0 &&
6157 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6158 0 : IsAngularParameter(pszName))
6159 : {
6160 0 : dfValue /= d->dfToDegrees;
6161 : }
6162 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6163 4 : IsLinearParameter(pszName))
6164 4 : dfValue /= d->dfToMeter;
6165 :
6166 182 : return SetProjParm(pszName, dfValue);
6167 : }
6168 :
6169 : /************************************************************************/
6170 : /* OSRSetNormProjParm() */
6171 : /************************************************************************/
6172 :
6173 : /**
6174 : * \brief Set a projection parameter with a normalized value.
6175 : *
6176 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6177 : */
6178 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6179 : double dfValue)
6180 :
6181 : {
6182 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6183 :
6184 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6185 : }
6186 :
6187 : /************************************************************************/
6188 : /* SetTM() */
6189 : /************************************************************************/
6190 :
6191 407 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6192 : double dfScale, double dfFalseEasting,
6193 : double dfFalseNorthing)
6194 :
6195 : {
6196 814 : TAKE_OPTIONAL_LOCK();
6197 :
6198 407 : return d->replaceConversionAndUnref(
6199 : proj_create_conversion_transverse_mercator(
6200 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6201 814 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6202 : }
6203 :
6204 : /************************************************************************/
6205 : /* OSRSetTM() */
6206 : /************************************************************************/
6207 :
6208 5 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6209 : double dfCenterLong, double dfScale, double dfFalseEasting,
6210 : double dfFalseNorthing)
6211 :
6212 : {
6213 5 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6214 :
6215 5 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6216 5 : dfFalseEasting, dfFalseNorthing);
6217 : }
6218 :
6219 : /************************************************************************/
6220 : /* SetTMVariant() */
6221 : /************************************************************************/
6222 :
6223 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6224 : double dfCenterLat,
6225 : double dfCenterLong, double dfScale,
6226 : double dfFalseEasting,
6227 : double dfFalseNorthing)
6228 :
6229 : {
6230 0 : TAKE_OPTIONAL_LOCK();
6231 :
6232 0 : SetProjection(pszVariantName);
6233 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6234 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6235 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6236 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6237 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6238 :
6239 0 : return OGRERR_NONE;
6240 : }
6241 :
6242 : /************************************************************************/
6243 : /* OSRSetTMVariant() */
6244 : /************************************************************************/
6245 :
6246 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6247 : double dfCenterLat, double dfCenterLong, double dfScale,
6248 : double dfFalseEasting, double dfFalseNorthing)
6249 :
6250 : {
6251 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6252 :
6253 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6254 : dfCenterLong, dfScale, dfFalseEasting,
6255 0 : dfFalseNorthing);
6256 : }
6257 :
6258 : /************************************************************************/
6259 : /* SetTMSO() */
6260 : /************************************************************************/
6261 :
6262 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6263 : double dfScale, double dfFalseEasting,
6264 : double dfFalseNorthing)
6265 :
6266 : {
6267 6 : TAKE_OPTIONAL_LOCK();
6268 :
6269 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6270 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6271 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6272 :
6273 3 : const char *pszName = nullptr;
6274 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6275 3 : CPLString osName = pszName ? pszName : "";
6276 :
6277 3 : d->refreshProjObj();
6278 :
6279 3 : d->demoteFromBoundCRS();
6280 :
6281 3 : auto cs = proj_create_cartesian_2D_cs(
6282 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6283 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6284 : auto projCRS =
6285 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6286 3 : d->getGeodBaseCRS(), conv, cs);
6287 3 : proj_destroy(conv);
6288 3 : proj_destroy(cs);
6289 :
6290 3 : d->setPjCRS(projCRS);
6291 :
6292 3 : d->undoDemoteFromBoundCRS();
6293 :
6294 6 : return OGRERR_NONE;
6295 : }
6296 :
6297 : /************************************************************************/
6298 : /* OSRSetTMSO() */
6299 : /************************************************************************/
6300 :
6301 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6302 : double dfCenterLong, double dfScale, double dfFalseEasting,
6303 : double dfFalseNorthing)
6304 :
6305 : {
6306 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6307 :
6308 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6309 0 : dfFalseEasting, dfFalseNorthing);
6310 : }
6311 :
6312 : /************************************************************************/
6313 : /* SetTPED() */
6314 : /************************************************************************/
6315 :
6316 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6317 : double dfLat2, double dfLong2,
6318 : double dfFalseEasting,
6319 : double dfFalseNorthing)
6320 :
6321 : {
6322 2 : TAKE_OPTIONAL_LOCK();
6323 :
6324 1 : return d->replaceConversionAndUnref(
6325 : proj_create_conversion_two_point_equidistant(
6326 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6327 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6328 : }
6329 :
6330 : /************************************************************************/
6331 : /* OSRSetTPED() */
6332 : /************************************************************************/
6333 :
6334 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6335 : double dfLat2, double dfLong2, double dfFalseEasting,
6336 : double dfFalseNorthing)
6337 :
6338 : {
6339 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6340 :
6341 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6342 0 : dfFalseEasting, dfFalseNorthing);
6343 : }
6344 :
6345 : /************************************************************************/
6346 : /* SetTMG() */
6347 : /************************************************************************/
6348 :
6349 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6350 : double dfFalseEasting,
6351 : double dfFalseNorthing)
6352 :
6353 : {
6354 0 : TAKE_OPTIONAL_LOCK();
6355 :
6356 0 : return d->replaceConversionAndUnref(
6357 : proj_create_conversion_tunisia_mapping_grid(
6358 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6359 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6360 : }
6361 :
6362 : /************************************************************************/
6363 : /* OSRSetTMG() */
6364 : /************************************************************************/
6365 :
6366 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6367 : double dfCenterLong, double dfFalseEasting,
6368 : double dfFalseNorthing)
6369 :
6370 : {
6371 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6372 :
6373 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6374 0 : dfFalseNorthing);
6375 : }
6376 :
6377 : /************************************************************************/
6378 : /* SetACEA() */
6379 : /************************************************************************/
6380 :
6381 40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6382 : double dfCenterLat, double dfCenterLong,
6383 : double dfFalseEasting,
6384 : double dfFalseNorthing)
6385 :
6386 : {
6387 80 : TAKE_OPTIONAL_LOCK();
6388 :
6389 : // Note different order of parameters. The one in PROJ is conformant with
6390 : // EPSG
6391 40 : return d->replaceConversionAndUnref(
6392 : proj_create_conversion_albers_equal_area(
6393 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6394 80 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6395 : }
6396 :
6397 : /************************************************************************/
6398 : /* OSRSetACEA() */
6399 : /************************************************************************/
6400 :
6401 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6402 : double dfCenterLat, double dfCenterLong,
6403 : double dfFalseEasting, double dfFalseNorthing)
6404 :
6405 : {
6406 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6407 :
6408 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6409 0 : dfFalseEasting, dfFalseNorthing);
6410 : }
6411 :
6412 : /************************************************************************/
6413 : /* SetAE() */
6414 : /************************************************************************/
6415 :
6416 19 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6417 : double dfFalseEasting, double dfFalseNorthing)
6418 :
6419 : {
6420 38 : TAKE_OPTIONAL_LOCK();
6421 :
6422 19 : return d->replaceConversionAndUnref(
6423 : proj_create_conversion_azimuthal_equidistant(
6424 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6425 38 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6426 : }
6427 :
6428 : /************************************************************************/
6429 : /* OSRSetAE() */
6430 : /************************************************************************/
6431 :
6432 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6433 : double dfCenterLong, double dfFalseEasting,
6434 : double dfFalseNorthing)
6435 :
6436 : {
6437 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6438 :
6439 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6440 0 : dfFalseNorthing);
6441 : }
6442 :
6443 : /************************************************************************/
6444 : /* SetBonne() */
6445 : /************************************************************************/
6446 :
6447 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6448 : double dfFalseEasting,
6449 : double dfFalseNorthing)
6450 :
6451 : {
6452 2 : TAKE_OPTIONAL_LOCK();
6453 :
6454 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6455 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6456 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6457 : }
6458 :
6459 : /************************************************************************/
6460 : /* OSRSetBonne() */
6461 : /************************************************************************/
6462 :
6463 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6464 : double dfCentralMeridian, double dfFalseEasting,
6465 : double dfFalseNorthing)
6466 :
6467 : {
6468 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6469 :
6470 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6471 0 : dfFalseNorthing);
6472 : }
6473 :
6474 : /************************************************************************/
6475 : /* SetCEA() */
6476 : /************************************************************************/
6477 :
6478 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6479 : double dfFalseEasting,
6480 : double dfFalseNorthing)
6481 :
6482 : {
6483 8 : TAKE_OPTIONAL_LOCK();
6484 :
6485 4 : return d->replaceConversionAndUnref(
6486 : proj_create_conversion_lambert_cylindrical_equal_area(
6487 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6488 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6489 : }
6490 :
6491 : /************************************************************************/
6492 : /* OSRSetCEA() */
6493 : /************************************************************************/
6494 :
6495 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6496 : double dfCentralMeridian, double dfFalseEasting,
6497 : double dfFalseNorthing)
6498 :
6499 : {
6500 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6501 :
6502 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6503 0 : dfFalseNorthing);
6504 : }
6505 :
6506 : /************************************************************************/
6507 : /* SetCS() */
6508 : /************************************************************************/
6509 :
6510 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6511 : double dfFalseEasting, double dfFalseNorthing)
6512 :
6513 : {
6514 10 : TAKE_OPTIONAL_LOCK();
6515 :
6516 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6517 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6518 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6519 : }
6520 :
6521 : /************************************************************************/
6522 : /* OSRSetCS() */
6523 : /************************************************************************/
6524 :
6525 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6526 : double dfCenterLong, double dfFalseEasting,
6527 : double dfFalseNorthing)
6528 :
6529 : {
6530 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6531 :
6532 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6533 0 : dfFalseNorthing);
6534 : }
6535 :
6536 : /************************************************************************/
6537 : /* SetEC() */
6538 : /************************************************************************/
6539 :
6540 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6541 : double dfCenterLat, double dfCenterLong,
6542 : double dfFalseEasting, double dfFalseNorthing)
6543 :
6544 : {
6545 14 : TAKE_OPTIONAL_LOCK();
6546 :
6547 : // Note: different order of arguments
6548 7 : return d->replaceConversionAndUnref(
6549 : proj_create_conversion_equidistant_conic(
6550 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6551 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6552 : }
6553 :
6554 : /************************************************************************/
6555 : /* OSRSetEC() */
6556 : /************************************************************************/
6557 :
6558 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6559 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6560 : double dfFalseNorthing)
6561 :
6562 : {
6563 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6564 :
6565 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6566 0 : dfFalseEasting, dfFalseNorthing);
6567 : }
6568 :
6569 : /************************************************************************/
6570 : /* SetEckert() */
6571 : /************************************************************************/
6572 :
6573 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6574 : double dfCentralMeridian,
6575 : double dfFalseEasting,
6576 : double dfFalseNorthing)
6577 :
6578 : {
6579 20 : TAKE_OPTIONAL_LOCK();
6580 :
6581 : PJ *conv;
6582 10 : if (nVariation == 1)
6583 : {
6584 1 : conv = proj_create_conversion_eckert_i(
6585 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6586 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6587 : }
6588 9 : else if (nVariation == 2)
6589 : {
6590 1 : conv = proj_create_conversion_eckert_ii(
6591 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6592 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6593 : }
6594 8 : else if (nVariation == 3)
6595 : {
6596 1 : conv = proj_create_conversion_eckert_iii(
6597 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6598 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6599 : }
6600 7 : else if (nVariation == 4)
6601 : {
6602 3 : conv = proj_create_conversion_eckert_iv(
6603 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6604 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6605 : }
6606 4 : else if (nVariation == 5)
6607 : {
6608 1 : conv = proj_create_conversion_eckert_v(
6609 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6610 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6611 : }
6612 3 : else if (nVariation == 6)
6613 : {
6614 3 : conv = proj_create_conversion_eckert_vi(
6615 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6616 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6617 : }
6618 : else
6619 : {
6620 0 : CPLError(CE_Failure, CPLE_AppDefined,
6621 : "Unsupported Eckert variation (%d).", nVariation);
6622 0 : return OGRERR_UNSUPPORTED_SRS;
6623 : }
6624 :
6625 10 : return d->replaceConversionAndUnref(conv);
6626 : }
6627 :
6628 : /************************************************************************/
6629 : /* OSRSetEckert() */
6630 : /************************************************************************/
6631 :
6632 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6633 : double dfCentralMeridian, double dfFalseEasting,
6634 : double dfFalseNorthing)
6635 :
6636 : {
6637 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6638 :
6639 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6640 0 : dfFalseEasting, dfFalseNorthing);
6641 : }
6642 :
6643 : /************************************************************************/
6644 : /* SetEckertIV() */
6645 : /* */
6646 : /* Deprecated */
6647 : /************************************************************************/
6648 :
6649 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6650 : double dfFalseEasting,
6651 : double dfFalseNorthing)
6652 :
6653 : {
6654 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6655 : }
6656 :
6657 : /************************************************************************/
6658 : /* OSRSetEckertIV() */
6659 : /************************************************************************/
6660 :
6661 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6662 : double dfFalseEasting, double dfFalseNorthing)
6663 :
6664 : {
6665 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6666 :
6667 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6668 0 : dfFalseNorthing);
6669 : }
6670 :
6671 : /************************************************************************/
6672 : /* SetEckertVI() */
6673 : /* */
6674 : /* Deprecated */
6675 : /************************************************************************/
6676 :
6677 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6678 : double dfFalseEasting,
6679 : double dfFalseNorthing)
6680 :
6681 : {
6682 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6683 : }
6684 :
6685 : /************************************************************************/
6686 : /* OSRSetEckertVI() */
6687 : /************************************************************************/
6688 :
6689 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6690 : double dfFalseEasting, double dfFalseNorthing)
6691 :
6692 : {
6693 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6694 :
6695 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6696 0 : dfFalseNorthing);
6697 : }
6698 :
6699 : /************************************************************************/
6700 : /* SetEquirectangular() */
6701 : /************************************************************************/
6702 :
6703 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6704 : double dfCenterLong,
6705 : double dfFalseEasting,
6706 : double dfFalseNorthing)
6707 :
6708 : {
6709 4 : TAKE_OPTIONAL_LOCK();
6710 :
6711 2 : if (dfCenterLat == 0.0)
6712 : {
6713 0 : return d->replaceConversionAndUnref(
6714 : proj_create_conversion_equidistant_cylindrical(
6715 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6716 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6717 : }
6718 :
6719 : // Non-standard extension with non-zero latitude of origin
6720 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6721 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6722 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6723 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6724 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6725 :
6726 2 : return OGRERR_NONE;
6727 : }
6728 :
6729 : /************************************************************************/
6730 : /* OSRSetEquirectangular() */
6731 : /************************************************************************/
6732 :
6733 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6734 : double dfCenterLong, double dfFalseEasting,
6735 : double dfFalseNorthing)
6736 :
6737 : {
6738 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6739 :
6740 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6741 0 : dfFalseEasting, dfFalseNorthing);
6742 : }
6743 :
6744 : /************************************************************************/
6745 : /* SetEquirectangular2() */
6746 : /* Generalized form */
6747 : /************************************************************************/
6748 :
6749 174 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6750 : double dfCenterLong,
6751 : double dfStdParallel1,
6752 : double dfFalseEasting,
6753 : double dfFalseNorthing)
6754 :
6755 : {
6756 348 : TAKE_OPTIONAL_LOCK();
6757 :
6758 174 : if (dfCenterLat == 0.0)
6759 : {
6760 169 : return d->replaceConversionAndUnref(
6761 : proj_create_conversion_equidistant_cylindrical(
6762 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6763 169 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6764 : }
6765 :
6766 : // Non-standard extension with non-zero latitude of origin
6767 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6768 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6769 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6770 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6771 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6772 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6773 :
6774 5 : return OGRERR_NONE;
6775 : }
6776 :
6777 : /************************************************************************/
6778 : /* OSRSetEquirectangular2() */
6779 : /************************************************************************/
6780 :
6781 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6782 : double dfCenterLong, double dfStdParallel1,
6783 : double dfFalseEasting, double dfFalseNorthing)
6784 :
6785 : {
6786 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6787 :
6788 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6789 : dfStdParallel1, dfFalseEasting,
6790 3 : dfFalseNorthing);
6791 : }
6792 :
6793 : /************************************************************************/
6794 : /* SetGS() */
6795 : /************************************************************************/
6796 :
6797 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6798 : double dfFalseEasting, double dfFalseNorthing)
6799 :
6800 : {
6801 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6802 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6803 5 : nullptr, 0.0, nullptr, 0.0));
6804 : }
6805 :
6806 : /************************************************************************/
6807 : /* OSRSetGS() */
6808 : /************************************************************************/
6809 :
6810 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6811 : double dfFalseEasting, double dfFalseNorthing)
6812 :
6813 : {
6814 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6815 :
6816 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6817 2 : dfFalseNorthing);
6818 : }
6819 :
6820 : /************************************************************************/
6821 : /* SetGH() */
6822 : /************************************************************************/
6823 :
6824 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6825 : double dfFalseEasting, double dfFalseNorthing)
6826 :
6827 : {
6828 0 : TAKE_OPTIONAL_LOCK();
6829 :
6830 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6831 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6832 0 : nullptr, 0.0, nullptr, 0.0));
6833 : }
6834 :
6835 : /************************************************************************/
6836 : /* OSRSetGH() */
6837 : /************************************************************************/
6838 :
6839 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6840 : double dfFalseEasting, double dfFalseNorthing)
6841 :
6842 : {
6843 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6844 :
6845 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6846 0 : dfFalseNorthing);
6847 : }
6848 :
6849 : /************************************************************************/
6850 : /* SetIGH() */
6851 : /************************************************************************/
6852 :
6853 0 : OGRErr OGRSpatialReference::SetIGH()
6854 :
6855 : {
6856 0 : TAKE_OPTIONAL_LOCK();
6857 :
6858 0 : return d->replaceConversionAndUnref(
6859 : proj_create_conversion_interrupted_goode_homolosine(
6860 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6861 : }
6862 :
6863 : /************************************************************************/
6864 : /* OSRSetIGH() */
6865 : /************************************************************************/
6866 :
6867 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6868 :
6869 : {
6870 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6871 :
6872 0 : return ToPointer(hSRS)->SetIGH();
6873 : }
6874 :
6875 : /************************************************************************/
6876 : /* SetGEOS() */
6877 : /************************************************************************/
6878 :
6879 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6880 : double dfSatelliteHeight,
6881 : double dfFalseEasting,
6882 : double dfFalseNorthing)
6883 :
6884 : {
6885 6 : TAKE_OPTIONAL_LOCK();
6886 :
6887 3 : return d->replaceConversionAndUnref(
6888 : proj_create_conversion_geostationary_satellite_sweep_y(
6889 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6890 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6891 : }
6892 :
6893 : /************************************************************************/
6894 : /* OSRSetGEOS() */
6895 : /************************************************************************/
6896 :
6897 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6898 : double dfSatelliteHeight, double dfFalseEasting,
6899 : double dfFalseNorthing)
6900 :
6901 : {
6902 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6903 :
6904 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6905 0 : dfFalseEasting, dfFalseNorthing);
6906 : }
6907 :
6908 : /************************************************************************/
6909 : /* SetGaussSchreiberTMercator() */
6910 : /************************************************************************/
6911 :
6912 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6913 : double dfCenterLong,
6914 : double dfScale,
6915 : double dfFalseEasting,
6916 : double dfFalseNorthing)
6917 :
6918 : {
6919 0 : TAKE_OPTIONAL_LOCK();
6920 :
6921 0 : return d->replaceConversionAndUnref(
6922 : proj_create_conversion_gauss_schreiber_transverse_mercator(
6923 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6924 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6925 : }
6926 :
6927 : /************************************************************************/
6928 : /* OSRSetGaussSchreiberTMercator() */
6929 : /************************************************************************/
6930 :
6931 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6932 : double dfCenterLat, double dfCenterLong,
6933 : double dfScale, double dfFalseEasting,
6934 : double dfFalseNorthing)
6935 :
6936 : {
6937 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6938 :
6939 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6940 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6941 : }
6942 :
6943 : /************************************************************************/
6944 : /* SetGnomonic() */
6945 : /************************************************************************/
6946 :
6947 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6948 : double dfFalseEasting,
6949 : double dfFalseNorthing)
6950 :
6951 : {
6952 4 : TAKE_OPTIONAL_LOCK();
6953 :
6954 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6955 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6956 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6957 : }
6958 :
6959 : /************************************************************************/
6960 : /* OSRSetGnomonic() */
6961 : /************************************************************************/
6962 :
6963 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6964 : double dfCenterLong, double dfFalseEasting,
6965 : double dfFalseNorthing)
6966 :
6967 : {
6968 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6969 :
6970 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6971 0 : dfFalseEasting, dfFalseNorthing);
6972 : }
6973 :
6974 : /************************************************************************/
6975 : /* SetHOMAC() */
6976 : /************************************************************************/
6977 :
6978 : /**
6979 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6980 : * azimuth angle.
6981 : *
6982 : * This projection corresponds to EPSG projection method 9815, also
6983 : * sometimes known as hotine oblique mercator (variant B).
6984 : *
6985 : * This method does the same thing as the C function OSRSetHOMAC().
6986 : *
6987 : * @param dfCenterLat Latitude of the projection origin.
6988 : * @param dfCenterLong Longitude of the projection origin.
6989 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6990 : * centerline.
6991 : * @param dfRectToSkew Angle from Rectified to Skew Grid
6992 : * @param dfScale Scale factor applies to the projection origin.
6993 : * @param dfFalseEasting False easting.
6994 : * @param dfFalseNorthing False northing.
6995 : *
6996 : * @return OGRERR_NONE on success.
6997 : */
6998 :
6999 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7000 : double dfAzimuth, double dfRectToSkew,
7001 : double dfScale, double dfFalseEasting,
7002 : double dfFalseNorthing)
7003 :
7004 : {
7005 8 : TAKE_OPTIONAL_LOCK();
7006 :
7007 4 : return d->replaceConversionAndUnref(
7008 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7009 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7010 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7011 8 : 0.0, nullptr, 0.0));
7012 : }
7013 :
7014 : /************************************************************************/
7015 : /* OSRSetHOMAC() */
7016 : /************************************************************************/
7017 :
7018 : /**
7019 : * \brief Set an Oblique Mercator projection using azimuth angle.
7020 : *
7021 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7022 : */
7023 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7024 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7025 : double dfScale, double dfFalseEasting,
7026 : double dfFalseNorthing)
7027 :
7028 : {
7029 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7030 :
7031 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7032 : dfRectToSkew, dfScale, dfFalseEasting,
7033 0 : dfFalseNorthing);
7034 : }
7035 :
7036 : /************************************************************************/
7037 : /* SetHOM() */
7038 : /************************************************************************/
7039 :
7040 : /**
7041 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7042 : *
7043 : * This projection corresponds to EPSG projection method 9812, also
7044 : * sometimes known as hotine oblique mercator (variant A)..
7045 : *
7046 : * This method does the same thing as the C function OSRSetHOM().
7047 : *
7048 : * @param dfCenterLat Latitude of the projection origin.
7049 : * @param dfCenterLong Longitude of the projection origin.
7050 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7051 : * centerline.
7052 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7053 : * @param dfScale Scale factor applies to the projection origin.
7054 : * @param dfFalseEasting False easting.
7055 : * @param dfFalseNorthing False northing.
7056 : *
7057 : * @return OGRERR_NONE on success.
7058 : */
7059 :
7060 12 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7061 : double dfAzimuth, double dfRectToSkew,
7062 : double dfScale, double dfFalseEasting,
7063 : double dfFalseNorthing)
7064 :
7065 : {
7066 24 : TAKE_OPTIONAL_LOCK();
7067 :
7068 12 : return d->replaceConversionAndUnref(
7069 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7070 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7071 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7072 24 : 0.0, nullptr, 0.0));
7073 : }
7074 :
7075 : /************************************************************************/
7076 : /* OSRSetHOM() */
7077 : /************************************************************************/
7078 : /**
7079 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7080 : *
7081 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7082 : */
7083 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7084 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7085 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7086 :
7087 : {
7088 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7089 :
7090 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7091 : dfRectToSkew, dfScale, dfFalseEasting,
7092 0 : dfFalseNorthing);
7093 : }
7094 :
7095 : /************************************************************************/
7096 : /* SetHOM2PNO() */
7097 : /************************************************************************/
7098 :
7099 : /**
7100 : * \brief Set a Hotine Oblique Mercator projection using two points on
7101 : * projection centerline.
7102 : *
7103 : * This method does the same thing as the C function OSRSetHOM2PNO().
7104 : *
7105 : * @param dfCenterLat Latitude of the projection origin.
7106 : * @param dfLat1 Latitude of the first point on center line.
7107 : * @param dfLong1 Longitude of the first point on center line.
7108 : * @param dfLat2 Latitude of the second point on center line.
7109 : * @param dfLong2 Longitude of the second point on center line.
7110 : * @param dfScale Scale factor applies to the projection origin.
7111 : * @param dfFalseEasting False easting.
7112 : * @param dfFalseNorthing False northing.
7113 : *
7114 : * @return OGRERR_NONE on success.
7115 : */
7116 :
7117 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7118 : double dfLong1, double dfLat2,
7119 : double dfLong2, double dfScale,
7120 : double dfFalseEasting,
7121 : double dfFalseNorthing)
7122 :
7123 : {
7124 6 : TAKE_OPTIONAL_LOCK();
7125 :
7126 3 : return d->replaceConversionAndUnref(
7127 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7128 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7129 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7130 6 : 0.0));
7131 : }
7132 :
7133 : /************************************************************************/
7134 : /* OSRSetHOM2PNO() */
7135 : /************************************************************************/
7136 : /**
7137 : * \brief Set a Hotine Oblique Mercator projection using two points on
7138 : * projection centerline.
7139 : *
7140 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7141 : */
7142 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7143 : double dfLat1, double dfLong1, double dfLat2,
7144 : double dfLong2, double dfScale, double dfFalseEasting,
7145 : double dfFalseNorthing)
7146 :
7147 : {
7148 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7149 :
7150 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7151 : dfLong2, dfScale, dfFalseEasting,
7152 0 : dfFalseNorthing);
7153 : }
7154 :
7155 : /************************************************************************/
7156 : /* SetLOM() */
7157 : /************************************************************************/
7158 :
7159 : /**
7160 : * \brief Set a Laborde Oblique Mercator projection.
7161 : *
7162 : * @param dfCenterLat Latitude of the projection origin.
7163 : * @param dfCenterLong Longitude of the projection origin.
7164 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7165 : * centerline.
7166 : * @param dfScale Scale factor on the initiali line
7167 : * @param dfFalseEasting False easting.
7168 : * @param dfFalseNorthing False northing.
7169 : *
7170 : * @return OGRERR_NONE on success.
7171 : */
7172 :
7173 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7174 : double dfAzimuth, double dfScale,
7175 : double dfFalseEasting,
7176 : double dfFalseNorthing)
7177 :
7178 : {
7179 0 : TAKE_OPTIONAL_LOCK();
7180 :
7181 0 : return d->replaceConversionAndUnref(
7182 : proj_create_conversion_laborde_oblique_mercator(
7183 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7184 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7185 : }
7186 :
7187 : /************************************************************************/
7188 : /* SetIWMPolyconic() */
7189 : /************************************************************************/
7190 :
7191 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7192 : double dfCenterLong,
7193 : double dfFalseEasting,
7194 : double dfFalseNorthing)
7195 :
7196 : {
7197 0 : TAKE_OPTIONAL_LOCK();
7198 :
7199 0 : return d->replaceConversionAndUnref(
7200 : proj_create_conversion_international_map_world_polyconic(
7201 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7202 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7203 : }
7204 :
7205 : /************************************************************************/
7206 : /* OSRSetIWMPolyconic() */
7207 : /************************************************************************/
7208 :
7209 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7210 : double dfLat2, double dfCenterLong,
7211 : double dfFalseEasting, double dfFalseNorthing)
7212 :
7213 : {
7214 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7215 :
7216 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7217 0 : dfFalseEasting, dfFalseNorthing);
7218 : }
7219 :
7220 : /************************************************************************/
7221 : /* SetKrovak() */
7222 : /************************************************************************/
7223 :
7224 : /** Krovak east-north projection.
7225 : *
7226 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7227 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7228 : */
7229 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7230 : double dfAzimuth,
7231 : double dfPseudoStdParallel1,
7232 : double dfScale, double dfFalseEasting,
7233 : double dfFalseNorthing)
7234 :
7235 : {
7236 6 : TAKE_OPTIONAL_LOCK();
7237 :
7238 3 : return d->replaceConversionAndUnref(
7239 : proj_create_conversion_krovak_north_oriented(
7240 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7241 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7242 6 : nullptr, 0.0, nullptr, 0.0));
7243 : }
7244 :
7245 : /************************************************************************/
7246 : /* OSRSetKrovak() */
7247 : /************************************************************************/
7248 :
7249 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7250 : double dfCenterLong, double dfAzimuth,
7251 : double dfPseudoStdParallel1, double dfScale,
7252 : double dfFalseEasting, double dfFalseNorthing)
7253 :
7254 : {
7255 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7256 :
7257 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7258 : dfPseudoStdParallel1, dfScale,
7259 0 : dfFalseEasting, dfFalseNorthing);
7260 : }
7261 :
7262 : /************************************************************************/
7263 : /* SetLAEA() */
7264 : /************************************************************************/
7265 :
7266 16 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7267 : double dfFalseEasting,
7268 : double dfFalseNorthing)
7269 :
7270 : {
7271 32 : TAKE_OPTIONAL_LOCK();
7272 :
7273 16 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7274 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7275 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7276 :
7277 16 : const char *pszName = nullptr;
7278 16 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7279 16 : CPLString osName = pszName ? pszName : "";
7280 :
7281 16 : d->refreshProjObj();
7282 :
7283 16 : d->demoteFromBoundCRS();
7284 :
7285 16 : auto cs = proj_create_cartesian_2D_cs(
7286 : d->getPROJContext(),
7287 16 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7288 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7289 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7290 13 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7291 : : PJ_CART2D_EASTING_NORTHING,
7292 16 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7293 : auto projCRS =
7294 16 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7295 16 : d->getGeodBaseCRS(), conv, cs);
7296 16 : proj_destroy(conv);
7297 16 : proj_destroy(cs);
7298 :
7299 16 : d->setPjCRS(projCRS);
7300 :
7301 16 : d->undoDemoteFromBoundCRS();
7302 :
7303 32 : return OGRERR_NONE;
7304 : }
7305 :
7306 : /************************************************************************/
7307 : /* OSRSetLAEA() */
7308 : /************************************************************************/
7309 :
7310 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7311 : double dfCenterLong, double dfFalseEasting,
7312 : double dfFalseNorthing)
7313 :
7314 : {
7315 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7316 :
7317 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7318 0 : dfFalseNorthing);
7319 : }
7320 :
7321 : /************************************************************************/
7322 : /* SetLCC() */
7323 : /************************************************************************/
7324 :
7325 149 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7326 : double dfCenterLat, double dfCenterLong,
7327 : double dfFalseEasting,
7328 : double dfFalseNorthing)
7329 :
7330 : {
7331 298 : TAKE_OPTIONAL_LOCK();
7332 :
7333 149 : return d->replaceConversionAndUnref(
7334 : proj_create_conversion_lambert_conic_conformal_2sp(
7335 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7336 298 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7337 : }
7338 :
7339 : /************************************************************************/
7340 : /* OSRSetLCC() */
7341 : /************************************************************************/
7342 :
7343 3 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7344 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7345 : double dfFalseNorthing)
7346 :
7347 : {
7348 3 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7349 :
7350 3 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7351 3 : dfFalseEasting, dfFalseNorthing);
7352 : }
7353 :
7354 : /************************************************************************/
7355 : /* SetLCC1SP() */
7356 : /************************************************************************/
7357 :
7358 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7359 : double dfScale, double dfFalseEasting,
7360 : double dfFalseNorthing)
7361 :
7362 : {
7363 20 : TAKE_OPTIONAL_LOCK();
7364 :
7365 10 : return d->replaceConversionAndUnref(
7366 : proj_create_conversion_lambert_conic_conformal_1sp(
7367 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7368 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7369 : }
7370 :
7371 : /************************************************************************/
7372 : /* OSRSetLCC1SP() */
7373 : /************************************************************************/
7374 :
7375 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7376 : double dfCenterLong, double dfScale, double dfFalseEasting,
7377 : double dfFalseNorthing)
7378 :
7379 : {
7380 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7381 :
7382 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7383 0 : dfFalseEasting, dfFalseNorthing);
7384 : }
7385 :
7386 : /************************************************************************/
7387 : /* SetLCCB() */
7388 : /************************************************************************/
7389 :
7390 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7391 : double dfCenterLat, double dfCenterLong,
7392 : double dfFalseEasting,
7393 : double dfFalseNorthing)
7394 :
7395 : {
7396 4 : TAKE_OPTIONAL_LOCK();
7397 :
7398 2 : return d->replaceConversionAndUnref(
7399 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7400 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7401 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7402 : }
7403 :
7404 : /************************************************************************/
7405 : /* OSRSetLCCB() */
7406 : /************************************************************************/
7407 :
7408 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7409 : double dfCenterLat, double dfCenterLong,
7410 : double dfFalseEasting, double dfFalseNorthing)
7411 :
7412 : {
7413 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7414 :
7415 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7416 0 : dfFalseEasting, dfFalseNorthing);
7417 : }
7418 :
7419 : /************************************************************************/
7420 : /* SetMC() */
7421 : /************************************************************************/
7422 :
7423 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7424 : double dfFalseEasting, double dfFalseNorthing)
7425 :
7426 : {
7427 8 : TAKE_OPTIONAL_LOCK();
7428 :
7429 : (void)dfCenterLat; // ignored
7430 :
7431 4 : return d->replaceConversionAndUnref(
7432 : proj_create_conversion_miller_cylindrical(
7433 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7434 8 : nullptr, 0, nullptr, 0));
7435 : }
7436 :
7437 : /************************************************************************/
7438 : /* OSRSetMC() */
7439 : /************************************************************************/
7440 :
7441 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7442 : double dfCenterLong, double dfFalseEasting,
7443 : double dfFalseNorthing)
7444 :
7445 : {
7446 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7447 :
7448 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7449 0 : dfFalseNorthing);
7450 : }
7451 :
7452 : /************************************************************************/
7453 : /* SetMercator() */
7454 : /************************************************************************/
7455 :
7456 56 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7457 : double dfScale, double dfFalseEasting,
7458 : double dfFalseNorthing)
7459 :
7460 : {
7461 112 : TAKE_OPTIONAL_LOCK();
7462 :
7463 56 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7464 : {
7465 : // Not sure this is correct, but this is how it has been used
7466 : // historically
7467 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7468 0 : dfFalseNorthing);
7469 : }
7470 56 : return d->replaceConversionAndUnref(
7471 : proj_create_conversion_mercator_variant_a(
7472 : d->getPROJContext(),
7473 : dfCenterLat, // should be zero
7474 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7475 56 : nullptr, 0));
7476 : }
7477 :
7478 : /************************************************************************/
7479 : /* OSRSetMercator() */
7480 : /************************************************************************/
7481 :
7482 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7483 : double dfCenterLong, double dfScale,
7484 : double dfFalseEasting, double dfFalseNorthing)
7485 :
7486 : {
7487 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7488 :
7489 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7490 2 : dfFalseEasting, dfFalseNorthing);
7491 : }
7492 :
7493 : /************************************************************************/
7494 : /* SetMercator2SP() */
7495 : /************************************************************************/
7496 :
7497 30 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7498 : double dfCenterLong,
7499 : double dfFalseEasting,
7500 : double dfFalseNorthing)
7501 :
7502 : {
7503 30 : if (dfCenterLat == 0.0)
7504 : {
7505 29 : return d->replaceConversionAndUnref(
7506 : proj_create_conversion_mercator_variant_b(
7507 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7508 29 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7509 : }
7510 :
7511 1 : TAKE_OPTIONAL_LOCK();
7512 :
7513 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7514 :
7515 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7516 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7517 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7518 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7519 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7520 :
7521 1 : return OGRERR_NONE;
7522 : }
7523 :
7524 : /************************************************************************/
7525 : /* OSRSetMercator2SP() */
7526 : /************************************************************************/
7527 :
7528 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7529 : double dfCenterLat, double dfCenterLong,
7530 : double dfFalseEasting, double dfFalseNorthing)
7531 :
7532 : {
7533 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7534 :
7535 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7536 1 : dfFalseEasting, dfFalseNorthing);
7537 : }
7538 :
7539 : /************************************************************************/
7540 : /* SetMollweide() */
7541 : /************************************************************************/
7542 :
7543 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7544 : double dfFalseEasting,
7545 : double dfFalseNorthing)
7546 :
7547 : {
7548 6 : TAKE_OPTIONAL_LOCK();
7549 :
7550 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7551 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7552 6 : nullptr, 0, nullptr, 0));
7553 : }
7554 :
7555 : /************************************************************************/
7556 : /* OSRSetMollweide() */
7557 : /************************************************************************/
7558 :
7559 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7560 : double dfFalseEasting, double dfFalseNorthing)
7561 :
7562 : {
7563 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7564 :
7565 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7566 0 : dfFalseNorthing);
7567 : }
7568 :
7569 : /************************************************************************/
7570 : /* SetNZMG() */
7571 : /************************************************************************/
7572 :
7573 6 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7574 : double dfFalseEasting,
7575 : double dfFalseNorthing)
7576 :
7577 : {
7578 12 : TAKE_OPTIONAL_LOCK();
7579 :
7580 6 : return d->replaceConversionAndUnref(
7581 : proj_create_conversion_new_zealand_mapping_grid(
7582 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7583 12 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7584 : }
7585 :
7586 : /************************************************************************/
7587 : /* OSRSetNZMG() */
7588 : /************************************************************************/
7589 :
7590 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7591 : double dfCenterLong, double dfFalseEasting,
7592 : double dfFalseNorthing)
7593 :
7594 : {
7595 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7596 :
7597 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7598 0 : dfFalseNorthing);
7599 : }
7600 :
7601 : /************************************************************************/
7602 : /* SetOS() */
7603 : /************************************************************************/
7604 :
7605 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7606 : double dfScale, double dfFalseEasting,
7607 : double dfFalseNorthing)
7608 :
7609 : {
7610 12 : TAKE_OPTIONAL_LOCK();
7611 :
7612 6 : return d->replaceConversionAndUnref(
7613 : proj_create_conversion_oblique_stereographic(
7614 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7615 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7616 : }
7617 :
7618 : /************************************************************************/
7619 : /* OSRSetOS() */
7620 : /************************************************************************/
7621 :
7622 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7623 : double dfCMeridian, double dfScale, double dfFalseEasting,
7624 : double dfFalseNorthing)
7625 :
7626 : {
7627 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7628 :
7629 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7630 0 : dfFalseEasting, dfFalseNorthing);
7631 : }
7632 :
7633 : /************************************************************************/
7634 : /* SetOrthographic() */
7635 : /************************************************************************/
7636 :
7637 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7638 : double dfCenterLong,
7639 : double dfFalseEasting,
7640 : double dfFalseNorthing)
7641 :
7642 : {
7643 14 : TAKE_OPTIONAL_LOCK();
7644 :
7645 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7646 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7647 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7648 : }
7649 :
7650 : /************************************************************************/
7651 : /* OSRSetOrthographic() */
7652 : /************************************************************************/
7653 :
7654 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7655 : double dfCenterLong, double dfFalseEasting,
7656 : double dfFalseNorthing)
7657 :
7658 : {
7659 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7660 :
7661 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7662 1 : dfFalseEasting, dfFalseNorthing);
7663 : }
7664 :
7665 : /************************************************************************/
7666 : /* SetPolyconic() */
7667 : /************************************************************************/
7668 :
7669 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7670 : double dfCenterLong,
7671 : double dfFalseEasting,
7672 : double dfFalseNorthing)
7673 :
7674 : {
7675 14 : TAKE_OPTIONAL_LOCK();
7676 :
7677 : // note: it seems that by some definitions this should include a
7678 : // scale_factor parameter.
7679 7 : return d->replaceConversionAndUnref(
7680 : proj_create_conversion_american_polyconic(
7681 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7682 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7683 : }
7684 :
7685 : /************************************************************************/
7686 : /* OSRSetPolyconic() */
7687 : /************************************************************************/
7688 :
7689 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7690 : double dfCenterLong, double dfFalseEasting,
7691 : double dfFalseNorthing)
7692 :
7693 : {
7694 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7695 :
7696 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7697 0 : dfFalseEasting, dfFalseNorthing);
7698 : }
7699 :
7700 : /************************************************************************/
7701 : /* SetPS() */
7702 : /************************************************************************/
7703 :
7704 : /** Sets a Polar Stereographic projection.
7705 : *
7706 : * Two variants are possible:
7707 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7708 : * interpreted as the latitude of origin, combined with the scale factor
7709 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7710 : * is interpreted as the latitude of true scale. In that situation, dfScale
7711 : * must be set to 1 (it is ignored in the projection parameters)
7712 : */
7713 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7714 : double dfScale, double dfFalseEasting,
7715 : double dfFalseNorthing)
7716 :
7717 : {
7718 60 : TAKE_OPTIONAL_LOCK();
7719 :
7720 : PJ *conv;
7721 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7722 : {
7723 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7724 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7725 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7726 : }
7727 : else
7728 : {
7729 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7730 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7731 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7732 : }
7733 :
7734 30 : const char *pszName = nullptr;
7735 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7736 30 : CPLString osName = pszName ? pszName : "";
7737 :
7738 30 : d->refreshProjObj();
7739 :
7740 30 : d->demoteFromBoundCRS();
7741 :
7742 30 : auto cs = proj_create_cartesian_2D_cs(
7743 : d->getPROJContext(),
7744 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7745 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7746 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7747 : auto projCRS =
7748 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7749 30 : d->getGeodBaseCRS(), conv, cs);
7750 30 : proj_destroy(conv);
7751 30 : proj_destroy(cs);
7752 :
7753 30 : d->setPjCRS(projCRS);
7754 :
7755 30 : d->undoDemoteFromBoundCRS();
7756 :
7757 60 : return OGRERR_NONE;
7758 : }
7759 :
7760 : /************************************************************************/
7761 : /* OSRSetPS() */
7762 : /************************************************************************/
7763 :
7764 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7765 : double dfCenterLong, double dfScale, double dfFalseEasting,
7766 : double dfFalseNorthing)
7767 :
7768 : {
7769 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7770 :
7771 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7772 1 : dfFalseEasting, dfFalseNorthing);
7773 : }
7774 :
7775 : /************************************************************************/
7776 : /* SetRobinson() */
7777 : /************************************************************************/
7778 :
7779 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7780 : double dfFalseEasting,
7781 : double dfFalseNorthing)
7782 :
7783 : {
7784 8 : TAKE_OPTIONAL_LOCK();
7785 :
7786 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7787 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7788 8 : nullptr, 0, nullptr, 0));
7789 : }
7790 :
7791 : /************************************************************************/
7792 : /* OSRSetRobinson() */
7793 : /************************************************************************/
7794 :
7795 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7796 : double dfFalseEasting, double dfFalseNorthing)
7797 :
7798 : {
7799 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7800 :
7801 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7802 0 : dfFalseNorthing);
7803 : }
7804 :
7805 : /************************************************************************/
7806 : /* SetSinusoidal() */
7807 : /************************************************************************/
7808 :
7809 33 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7810 : double dfFalseEasting,
7811 : double dfFalseNorthing)
7812 :
7813 : {
7814 66 : TAKE_OPTIONAL_LOCK();
7815 :
7816 33 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7817 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7818 66 : nullptr, 0, nullptr, 0));
7819 : }
7820 :
7821 : /************************************************************************/
7822 : /* OSRSetSinusoidal() */
7823 : /************************************************************************/
7824 :
7825 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7826 : double dfFalseEasting, double dfFalseNorthing)
7827 :
7828 : {
7829 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7830 :
7831 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7832 1 : dfFalseNorthing);
7833 : }
7834 :
7835 : /************************************************************************/
7836 : /* SetStereographic() */
7837 : /************************************************************************/
7838 :
7839 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7840 : double dfCMeridian, double dfScale,
7841 : double dfFalseEasting,
7842 : double dfFalseNorthing)
7843 :
7844 : {
7845 4 : TAKE_OPTIONAL_LOCK();
7846 :
7847 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7848 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7849 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7850 : }
7851 :
7852 : /************************************************************************/
7853 : /* OSRSetStereographic() */
7854 : /************************************************************************/
7855 :
7856 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7857 : double dfCMeridian, double dfScale,
7858 : double dfFalseEasting, double dfFalseNorthing)
7859 :
7860 : {
7861 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7862 :
7863 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7864 0 : dfFalseEasting, dfFalseNorthing);
7865 : }
7866 :
7867 : /************************************************************************/
7868 : /* SetSOC() */
7869 : /* */
7870 : /* NOTE: This definition isn't really used in practice any more */
7871 : /* and should be considered deprecated. It seems that swiss */
7872 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
7873 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7874 : /* EPSG:2056 and Bug 423. */
7875 : /************************************************************************/
7876 :
7877 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7878 : double dfCentralMeridian,
7879 : double dfFalseEasting,
7880 : double dfFalseNorthing)
7881 :
7882 : {
7883 4 : TAKE_OPTIONAL_LOCK();
7884 :
7885 2 : return d->replaceConversionAndUnref(
7886 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7887 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7888 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7889 4 : 0.0));
7890 : #if 0
7891 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7892 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7893 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7894 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7895 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7896 :
7897 : return OGRERR_NONE;
7898 : #endif
7899 : }
7900 :
7901 : /************************************************************************/
7902 : /* OSRSetSOC() */
7903 : /************************************************************************/
7904 :
7905 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7906 : double dfCentralMeridian, double dfFalseEasting,
7907 : double dfFalseNorthing)
7908 :
7909 : {
7910 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7911 :
7912 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7913 0 : dfFalseEasting, dfFalseNorthing);
7914 : }
7915 :
7916 : /************************************************************************/
7917 : /* SetVDG() */
7918 : /************************************************************************/
7919 :
7920 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7921 : double dfFalseNorthing)
7922 :
7923 : {
7924 4 : TAKE_OPTIONAL_LOCK();
7925 :
7926 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7927 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7928 4 : nullptr, 0, nullptr, 0));
7929 : }
7930 :
7931 : /************************************************************************/
7932 : /* OSRSetVDG() */
7933 : /************************************************************************/
7934 :
7935 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7936 : double dfFalseEasting, double dfFalseNorthing)
7937 :
7938 : {
7939 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7940 :
7941 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7942 0 : dfFalseNorthing);
7943 : }
7944 :
7945 : /************************************************************************/
7946 : /* SetUTM() */
7947 : /************************************************************************/
7948 :
7949 : /**
7950 : * \brief Set UTM projection definition.
7951 : *
7952 : * This will generate a projection definition with the full set of
7953 : * transverse mercator projection parameters for the given UTM zone.
7954 : * If no PROJCS[] description is set yet, one will be set to look
7955 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7956 : *
7957 : * This method is the same as the C function OSRSetUTM().
7958 : *
7959 : * @param nZone UTM zone.
7960 : *
7961 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7962 : * hemisphere.
7963 : *
7964 : * @return OGRERR_NONE on success.
7965 : */
7966 :
7967 342 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7968 :
7969 : {
7970 684 : TAKE_OPTIONAL_LOCK();
7971 :
7972 342 : if (nZone < 0 || nZone > 60)
7973 : {
7974 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7975 0 : return OGRERR_FAILURE;
7976 : }
7977 :
7978 342 : return d->replaceConversionAndUnref(
7979 342 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7980 : }
7981 :
7982 : /************************************************************************/
7983 : /* OSRSetUTM() */
7984 : /************************************************************************/
7985 :
7986 : /**
7987 : * \brief Set UTM projection definition.
7988 : *
7989 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
7990 : */
7991 20 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
7992 :
7993 : {
7994 20 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
7995 :
7996 20 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
7997 : }
7998 :
7999 : /************************************************************************/
8000 : /* GetUTMZone() */
8001 : /* */
8002 : /* Returns zero if it isn't UTM. */
8003 : /************************************************************************/
8004 :
8005 : /**
8006 : * \brief Get utm zone information.
8007 : *
8008 : * This is the same as the C function OSRGetUTMZone().
8009 : *
8010 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8011 : * zone which is negative in the southern hemisphere instead of having the
8012 : * pbNorth flag used in the C and C++ interface.
8013 : *
8014 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8015 : * FALSE if southern.
8016 : *
8017 : * @return UTM zone number or zero if this isn't a UTM definition.
8018 : */
8019 :
8020 632 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8021 :
8022 : {
8023 1264 : TAKE_OPTIONAL_LOCK();
8024 :
8025 632 : if (IsProjected() && GetAxesCount() == 3)
8026 : {
8027 1 : OGRSpatialReference *poSRSTmp = Clone();
8028 1 : poSRSTmp->DemoteTo2D(nullptr);
8029 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8030 1 : delete poSRSTmp;
8031 1 : return nZone;
8032 : }
8033 :
8034 631 : const char *pszProjection = GetAttrValue("PROJECTION");
8035 :
8036 631 : if (pszProjection == nullptr ||
8037 527 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8038 297 : return 0;
8039 :
8040 334 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8041 5 : return 0;
8042 :
8043 329 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8044 15 : return 0;
8045 :
8046 314 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8047 3 : return 0;
8048 :
8049 311 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8050 :
8051 311 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8052 0 : return 0;
8053 :
8054 311 : if (pbNorth != nullptr)
8055 248 : *pbNorth = (dfFalseNorthing == 0);
8056 :
8057 : const double dfCentralMeridian =
8058 311 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8059 311 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8060 :
8061 622 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8062 933 : std::isnan(dfZone) ||
8063 311 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8064 0 : return 0;
8065 :
8066 311 : return static_cast<int>(dfZone);
8067 : }
8068 :
8069 : /************************************************************************/
8070 : /* OSRGetUTMZone() */
8071 : /************************************************************************/
8072 :
8073 : /**
8074 : * \brief Get utm zone information.
8075 : *
8076 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8077 : */
8078 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8079 :
8080 : {
8081 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8082 :
8083 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8084 : }
8085 :
8086 : /************************************************************************/
8087 : /* SetWagner() */
8088 : /************************************************************************/
8089 :
8090 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8091 : double dfCenterLat, double dfFalseEasting,
8092 : double dfFalseNorthing)
8093 :
8094 : {
8095 0 : TAKE_OPTIONAL_LOCK();
8096 :
8097 : PJ *conv;
8098 0 : if (nVariation == 1)
8099 : {
8100 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8101 : dfFalseEasting, dfFalseNorthing,
8102 : nullptr, 0.0, nullptr, 0.0);
8103 : }
8104 0 : else if (nVariation == 2)
8105 : {
8106 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8107 : dfFalseEasting, dfFalseNorthing,
8108 : nullptr, 0.0, nullptr, 0.0);
8109 : }
8110 0 : else if (nVariation == 3)
8111 : {
8112 0 : conv = proj_create_conversion_wagner_iii(
8113 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8114 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8115 : }
8116 0 : else if (nVariation == 4)
8117 : {
8118 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8119 : dfFalseEasting, dfFalseNorthing,
8120 : nullptr, 0.0, nullptr, 0.0);
8121 : }
8122 0 : else if (nVariation == 5)
8123 : {
8124 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8125 : dfFalseEasting, dfFalseNorthing,
8126 : nullptr, 0.0, nullptr, 0.0);
8127 : }
8128 0 : else if (nVariation == 6)
8129 : {
8130 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8131 : dfFalseEasting, dfFalseNorthing,
8132 : nullptr, 0.0, nullptr, 0.0);
8133 : }
8134 0 : else if (nVariation == 7)
8135 : {
8136 0 : conv = proj_create_conversion_wagner_vii(
8137 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8138 : 0.0, nullptr, 0.0);
8139 : }
8140 : else
8141 : {
8142 0 : CPLError(CE_Failure, CPLE_AppDefined,
8143 : "Unsupported Wagner variation (%d).", nVariation);
8144 0 : return OGRERR_UNSUPPORTED_SRS;
8145 : }
8146 :
8147 0 : return d->replaceConversionAndUnref(conv);
8148 : }
8149 :
8150 : /************************************************************************/
8151 : /* OSRSetWagner() */
8152 : /************************************************************************/
8153 :
8154 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8155 : double dfCenterLat, double dfFalseEasting,
8156 : double dfFalseNorthing)
8157 :
8158 : {
8159 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8160 :
8161 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8162 0 : dfFalseNorthing);
8163 : }
8164 :
8165 : /************************************************************************/
8166 : /* SetQSC() */
8167 : /************************************************************************/
8168 :
8169 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8170 : {
8171 0 : TAKE_OPTIONAL_LOCK();
8172 :
8173 0 : return d->replaceConversionAndUnref(
8174 : proj_create_conversion_quadrilateralized_spherical_cube(
8175 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8176 0 : 0, nullptr, 0));
8177 : }
8178 :
8179 : /************************************************************************/
8180 : /* OSRSetQSC() */
8181 : /************************************************************************/
8182 :
8183 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8184 : double dfCenterLong)
8185 :
8186 : {
8187 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8188 :
8189 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8190 : }
8191 :
8192 : /************************************************************************/
8193 : /* SetSCH() */
8194 : /************************************************************************/
8195 :
8196 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8197 : double dfPegHeading, double dfPegHgt)
8198 :
8199 : {
8200 0 : TAKE_OPTIONAL_LOCK();
8201 :
8202 0 : return d->replaceConversionAndUnref(
8203 : proj_create_conversion_spherical_cross_track_height(
8204 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8205 0 : nullptr, 0, nullptr, 0));
8206 : }
8207 :
8208 : /************************************************************************/
8209 : /* OSRSetSCH() */
8210 : /************************************************************************/
8211 :
8212 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8213 : double dfPegHeading, double dfPegHgt)
8214 :
8215 : {
8216 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8217 :
8218 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8219 : }
8220 :
8221 : /************************************************************************/
8222 : /* SetVerticalPerspective() */
8223 : /************************************************************************/
8224 :
8225 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8226 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8227 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8228 : {
8229 6 : TAKE_OPTIONAL_LOCK();
8230 :
8231 3 : return d->replaceConversionAndUnref(
8232 : proj_create_conversion_vertical_perspective(
8233 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8234 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8235 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8236 : }
8237 :
8238 : /************************************************************************/
8239 : /* OSRSetVerticalPerspective() */
8240 : /************************************************************************/
8241 :
8242 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8243 : double dfTopoOriginLat, double dfTopoOriginLon,
8244 : double dfTopoOriginHeight,
8245 : double dfViewPointHeight,
8246 : double dfFalseEasting, double dfFalseNorthing)
8247 :
8248 : {
8249 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8250 :
8251 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8252 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8253 1 : dfFalseEasting, dfFalseNorthing);
8254 : }
8255 :
8256 : /************************************************************************/
8257 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8258 : /************************************************************************/
8259 :
8260 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8261 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8262 : double dfAxisRotation)
8263 : {
8264 4 : TAKE_OPTIONAL_LOCK();
8265 :
8266 2 : d->refreshProjObj();
8267 2 : if (!d->m_pj_crs)
8268 0 : return OGRERR_FAILURE;
8269 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8270 0 : return OGRERR_FAILURE;
8271 2 : auto ctxt = d->getPROJContext();
8272 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8273 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8274 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8275 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8276 2 : d->m_pj_crs, conv, cs));
8277 2 : proj_destroy(conv);
8278 2 : proj_destroy(cs);
8279 2 : return OGRERR_NONE;
8280 : }
8281 :
8282 : /************************************************************************/
8283 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8284 : /************************************************************************/
8285 :
8286 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8287 : const char *pszCRSName, double dfGridNorthPoleLat,
8288 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8289 : {
8290 3 : TAKE_OPTIONAL_LOCK();
8291 :
8292 : #if PROJ_VERSION_MAJOR > 8 || \
8293 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8294 : d->refreshProjObj();
8295 : if (!d->m_pj_crs)
8296 : return OGRERR_FAILURE;
8297 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8298 : return OGRERR_FAILURE;
8299 : auto ctxt = d->getPROJContext();
8300 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8301 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8302 : nullptr, 0);
8303 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8304 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8305 : d->m_pj_crs, conv, cs));
8306 : proj_destroy(conv);
8307 : proj_destroy(cs);
8308 : return OGRERR_NONE;
8309 : #else
8310 : (void)pszCRSName;
8311 3 : SetProjection("Rotated_pole");
8312 3 : SetExtension(
8313 : "PROJCS", "PROJ4",
8314 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8315 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8316 : "+to_meter=0.0174532925199433 "
8317 : "+wktext",
8318 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8319 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8320 : GetSemiMinor(nullptr)));
8321 6 : return OGRERR_NONE;
8322 : #endif
8323 : }
8324 :
8325 : /************************************************************************/
8326 : /* SetAuthority() */
8327 : /************************************************************************/
8328 :
8329 : /**
8330 : * \brief Set the authority for a node.
8331 : *
8332 : * This method is the same as the C function OSRSetAuthority().
8333 : *
8334 : * @param pszTargetKey the partial or complete path to the node to
8335 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8336 : *
8337 : * @param pszAuthority authority name, such as "EPSG".
8338 : *
8339 : * @param nCode code for value with this authority.
8340 : *
8341 : * @return OGRERR_NONE on success.
8342 : */
8343 :
8344 9805 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8345 : const char *pszAuthority, int nCode)
8346 :
8347 : {
8348 19610 : TAKE_OPTIONAL_LOCK();
8349 :
8350 9805 : d->refreshProjObj();
8351 9805 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8352 :
8353 9805 : if (pszTargetKey == nullptr)
8354 : {
8355 241 : if (!d->m_pj_crs)
8356 0 : return OGRERR_FAILURE;
8357 241 : CPLString osCode;
8358 241 : osCode.Printf("%d", nCode);
8359 241 : d->demoteFromBoundCRS();
8360 241 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8361 : pszAuthority, osCode.c_str()));
8362 241 : d->undoDemoteFromBoundCRS();
8363 241 : return OGRERR_NONE;
8364 : }
8365 :
8366 9564 : d->demoteFromBoundCRS();
8367 9564 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8368 : {
8369 3161 : CPLString osCode;
8370 3161 : osCode.Printf("%d", nCode);
8371 : auto newGeogCRS =
8372 3161 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8373 : pszAuthority, osCode.c_str());
8374 :
8375 : auto conv =
8376 3161 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8377 :
8378 3161 : auto projCRS = proj_create_projected_crs(
8379 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8380 3161 : d->getProjCRSCoordSys());
8381 :
8382 : // Preserve existing id on the PROJCRS
8383 3161 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8384 3161 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8385 3161 : if (pszProjCRSAuthName && pszProjCRSCode)
8386 : {
8387 : auto projCRSWithId =
8388 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8389 : pszProjCRSCode);
8390 0 : proj_destroy(projCRS);
8391 0 : projCRS = projCRSWithId;
8392 : }
8393 :
8394 3161 : proj_destroy(newGeogCRS);
8395 3161 : proj_destroy(conv);
8396 :
8397 3161 : d->setPjCRS(projCRS);
8398 3161 : d->undoDemoteFromBoundCRS();
8399 3161 : return OGRERR_NONE;
8400 : }
8401 6403 : d->undoDemoteFromBoundCRS();
8402 :
8403 : /* -------------------------------------------------------------------- */
8404 : /* Find the node below which the authority should be put. */
8405 : /* -------------------------------------------------------------------- */
8406 6403 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8407 :
8408 6403 : if (poNode == nullptr)
8409 0 : return OGRERR_FAILURE;
8410 :
8411 : /* -------------------------------------------------------------------- */
8412 : /* If there is an existing AUTHORITY child blow it away before */
8413 : /* trying to set a new one. */
8414 : /* -------------------------------------------------------------------- */
8415 6403 : int iOldChild = poNode->FindChild("AUTHORITY");
8416 6403 : if (iOldChild != -1)
8417 5 : poNode->DestroyChild(iOldChild);
8418 :
8419 : /* -------------------------------------------------------------------- */
8420 : /* Create a new authority node. */
8421 : /* -------------------------------------------------------------------- */
8422 6403 : char szCode[32] = {};
8423 :
8424 6403 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8425 :
8426 6403 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8427 6403 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8428 6403 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8429 :
8430 6403 : poNode->AddChild(poAuthNode);
8431 :
8432 6403 : return OGRERR_NONE;
8433 : }
8434 :
8435 : /************************************************************************/
8436 : /* OSRSetAuthority() */
8437 : /************************************************************************/
8438 :
8439 : /**
8440 : * \brief Set the authority for a node.
8441 : *
8442 : * This function is the same as OGRSpatialReference::SetAuthority().
8443 : */
8444 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8445 : const char *pszAuthority, int nCode)
8446 :
8447 : {
8448 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8449 :
8450 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8451 : }
8452 :
8453 : /************************************************************************/
8454 : /* GetAuthorityCode() */
8455 : /************************************************************************/
8456 :
8457 : /**
8458 : * \brief Get the authority code for a node.
8459 : *
8460 : * This method is used to query an AUTHORITY[] node from within the
8461 : * WKT tree, and fetch the code value.
8462 : *
8463 : * While in theory values may be non-numeric, for the EPSG authority all
8464 : * code values should be integral.
8465 : *
8466 : * This method is the same as the C function OSRGetAuthorityCode().
8467 : *
8468 : * @param pszTargetKey the partial or complete path to the node to
8469 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8470 : * search for an authority node on the root element.
8471 : *
8472 : * @return value code from authority node, or NULL on failure. The value
8473 : * returned is internal and should not be freed or modified.
8474 : */
8475 :
8476 : const char *
8477 24591 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8478 :
8479 : {
8480 49182 : TAKE_OPTIONAL_LOCK();
8481 :
8482 24591 : d->refreshProjObj();
8483 24591 : const char *pszInputTargetKey = pszTargetKey;
8484 24591 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8485 24591 : if (pszTargetKey == nullptr)
8486 : {
8487 17581 : if (!d->m_pj_crs)
8488 : {
8489 13 : return nullptr;
8490 : }
8491 17568 : d->demoteFromBoundCRS();
8492 17568 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8493 17568 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8494 : {
8495 1071 : auto ctxt = d->getPROJContext();
8496 1071 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8497 1071 : if (cs)
8498 : {
8499 1071 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8500 1071 : proj_destroy(cs);
8501 1071 : if (axisCount == 3)
8502 : {
8503 : // This might come from a COMPD_CS with a VERT_DATUM type =
8504 : // 2002 in which case, using the WKT1 representation will
8505 : // enable us to recover the EPSG code.
8506 14 : pszTargetKey = pszInputTargetKey;
8507 : }
8508 : }
8509 : }
8510 17568 : d->undoDemoteFromBoundCRS();
8511 17568 : if (ret != nullptr || pszTargetKey == nullptr)
8512 : {
8513 17568 : return ret;
8514 : }
8515 : }
8516 :
8517 : // Special key for that context
8518 7014 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8519 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8520 : {
8521 4 : auto ctxt = d->getPROJContext();
8522 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8523 4 : if (crs)
8524 : {
8525 4 : const char *ret = proj_get_id_code(crs, 0);
8526 4 : if (ret)
8527 4 : ret = CPLSPrintf("%s", ret);
8528 4 : proj_destroy(crs);
8529 4 : return ret;
8530 : }
8531 : }
8532 7010 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8533 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8534 : {
8535 4 : auto ctxt = d->getPROJContext();
8536 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8537 4 : if (crs)
8538 : {
8539 4 : const char *ret = proj_get_id_code(crs, 0);
8540 4 : if (ret)
8541 4 : ret = CPLSPrintf("%s", ret);
8542 4 : proj_destroy(crs);
8543 4 : return ret;
8544 : }
8545 : }
8546 :
8547 : /* -------------------------------------------------------------------- */
8548 : /* Find the node below which the authority should be put. */
8549 : /* -------------------------------------------------------------------- */
8550 7002 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8551 :
8552 7002 : if (poNode == nullptr)
8553 84 : return nullptr;
8554 :
8555 : /* -------------------------------------------------------------------- */
8556 : /* Fetch AUTHORITY child if there is one. */
8557 : /* -------------------------------------------------------------------- */
8558 6918 : if (poNode->FindChild("AUTHORITY") == -1)
8559 182 : return nullptr;
8560 :
8561 6736 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8562 :
8563 : /* -------------------------------------------------------------------- */
8564 : /* Create a new authority node. */
8565 : /* -------------------------------------------------------------------- */
8566 6736 : if (poNode->GetChildCount() < 2)
8567 0 : return nullptr;
8568 :
8569 6736 : return poNode->GetChild(1)->GetValue();
8570 : }
8571 :
8572 : /************************************************************************/
8573 : /* OSRGetAuthorityCode() */
8574 : /************************************************************************/
8575 :
8576 : /**
8577 : * \brief Get the authority code for a node.
8578 : *
8579 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8580 : */
8581 664 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8582 : const char *pszTargetKey)
8583 :
8584 : {
8585 664 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8586 :
8587 664 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8588 : }
8589 :
8590 : /************************************************************************/
8591 : /* GetAuthorityName() */
8592 : /************************************************************************/
8593 :
8594 : /**
8595 : * \brief Get the authority name for a node.
8596 : *
8597 : * This method is used to query an AUTHORITY[] node from within the
8598 : * WKT tree, and fetch the authority name value.
8599 : *
8600 : * The most common authority is "EPSG".
8601 : *
8602 : * This method is the same as the C function OSRGetAuthorityName().
8603 : *
8604 : * @param pszTargetKey the partial or complete path to the node to
8605 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8606 : * search for an authority node on the root element.
8607 : *
8608 : * @return value code from authority node, or NULL on failure. The value
8609 : * returned is internal and should not be freed or modified.
8610 : */
8611 :
8612 : const char *
8613 44465 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8614 :
8615 : {
8616 88930 : TAKE_OPTIONAL_LOCK();
8617 :
8618 44465 : d->refreshProjObj();
8619 44465 : const char *pszInputTargetKey = pszTargetKey;
8620 44465 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8621 44465 : if (pszTargetKey == nullptr)
8622 : {
8623 19020 : if (!d->m_pj_crs)
8624 : {
8625 14 : return nullptr;
8626 : }
8627 19006 : d->demoteFromBoundCRS();
8628 19006 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8629 19006 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8630 : {
8631 794 : auto ctxt = d->getPROJContext();
8632 794 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8633 794 : if (cs)
8634 : {
8635 794 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8636 794 : proj_destroy(cs);
8637 794 : if (axisCount == 3)
8638 : {
8639 : // This might come from a COMPD_CS with a VERT_DATUM type =
8640 : // 2002 in which case, using the WKT1 representation will
8641 : // enable us to recover the EPSG code.
8642 14 : pszTargetKey = pszInputTargetKey;
8643 : }
8644 : }
8645 : }
8646 19006 : d->undoDemoteFromBoundCRS();
8647 19006 : if (ret != nullptr || pszTargetKey == nullptr)
8648 : {
8649 19006 : return ret;
8650 : }
8651 : }
8652 :
8653 : // Special key for that context
8654 25449 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8655 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8656 : {
8657 4 : auto ctxt = d->getPROJContext();
8658 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8659 4 : if (crs)
8660 : {
8661 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8662 4 : if (ret)
8663 4 : ret = CPLSPrintf("%s", ret);
8664 4 : proj_destroy(crs);
8665 4 : return ret;
8666 : }
8667 : }
8668 25445 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8669 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8670 : {
8671 4 : auto ctxt = d->getPROJContext();
8672 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8673 4 : if (crs)
8674 : {
8675 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8676 4 : if (ret)
8677 4 : ret = CPLSPrintf("%s", ret);
8678 4 : proj_destroy(crs);
8679 4 : return ret;
8680 : }
8681 : }
8682 :
8683 : /* -------------------------------------------------------------------- */
8684 : /* Find the node below which the authority should be put. */
8685 : /* -------------------------------------------------------------------- */
8686 25437 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8687 :
8688 25437 : if (poNode == nullptr)
8689 11058 : return nullptr;
8690 :
8691 : /* -------------------------------------------------------------------- */
8692 : /* Fetch AUTHORITY child if there is one. */
8693 : /* -------------------------------------------------------------------- */
8694 14379 : if (poNode->FindChild("AUTHORITY") == -1)
8695 1484 : return nullptr;
8696 :
8697 12895 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8698 :
8699 : /* -------------------------------------------------------------------- */
8700 : /* Create a new authority node. */
8701 : /* -------------------------------------------------------------------- */
8702 12895 : if (poNode->GetChildCount() < 2)
8703 0 : return nullptr;
8704 :
8705 12895 : return poNode->GetChild(0)->GetValue();
8706 : }
8707 :
8708 : /************************************************************************/
8709 : /* OSRGetAuthorityName() */
8710 : /************************************************************************/
8711 :
8712 : /**
8713 : * \brief Get the authority name for a node.
8714 : *
8715 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8716 : */
8717 226 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8718 : const char *pszTargetKey)
8719 :
8720 : {
8721 226 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8722 :
8723 226 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8724 : }
8725 :
8726 : /************************************************************************/
8727 : /* GetOGCURN() */
8728 : /************************************************************************/
8729 :
8730 : /**
8731 : * \brief Get a OGC URN string describing the CRS, when possible
8732 : *
8733 : * This method assumes that the CRS has a top-level identifier, or is
8734 : * a compound CRS whose horizontal and vertical parts have a top-level
8735 : * identifier.
8736 : *
8737 : * @return a string to free with CPLFree(), or nullptr when no result can be
8738 : * generated
8739 : *
8740 : * @since GDAL 3.5
8741 : */
8742 :
8743 59 : char *OGRSpatialReference::GetOGCURN() const
8744 :
8745 : {
8746 118 : TAKE_OPTIONAL_LOCK();
8747 :
8748 59 : const char *pszAuthName = GetAuthorityName(nullptr);
8749 59 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8750 59 : if (pszAuthName && pszAuthCode)
8751 56 : return CPLStrdup(
8752 56 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8753 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8754 2 : return nullptr;
8755 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8756 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8757 1 : char *pszRet = nullptr;
8758 1 : if (horizCRS && vertCRS)
8759 : {
8760 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8761 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8762 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8763 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8764 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8765 : {
8766 1 : pszRet = CPLStrdup(CPLSPrintf(
8767 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8768 : horizAuthCode, vertAuthName, vertAuthCode));
8769 : }
8770 : }
8771 1 : proj_destroy(horizCRS);
8772 1 : proj_destroy(vertCRS);
8773 1 : return pszRet;
8774 : }
8775 :
8776 : /************************************************************************/
8777 : /* StripVertical() */
8778 : /************************************************************************/
8779 :
8780 : /**
8781 : * \brief Convert a compound cs into a horizontal CS.
8782 : *
8783 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8784 : * nodes are stripped resulting and only the horizontal coordinate system
8785 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8786 : *
8787 : * If this is not a compound coordinate system then nothing is changed.
8788 : *
8789 : * This method is the same as the C function OSRStripVertical().
8790 : *
8791 : * @since OGR 1.8.0
8792 : */
8793 :
8794 42 : OGRErr OGRSpatialReference::StripVertical()
8795 :
8796 : {
8797 84 : TAKE_OPTIONAL_LOCK();
8798 :
8799 42 : d->refreshProjObj();
8800 42 : d->demoteFromBoundCRS();
8801 42 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8802 : {
8803 0 : d->undoDemoteFromBoundCRS();
8804 0 : return OGRERR_NONE;
8805 : }
8806 42 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8807 42 : if (!horizCRS)
8808 : {
8809 0 : d->undoDemoteFromBoundCRS();
8810 0 : return OGRERR_FAILURE;
8811 : }
8812 :
8813 42 : bool reuseExistingBoundCRS = false;
8814 42 : if (d->m_pj_bound_crs_target)
8815 : {
8816 3 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8817 6 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8818 6 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8819 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8820 : }
8821 :
8822 42 : if (reuseExistingBoundCRS)
8823 : {
8824 3 : auto newBoundCRS = proj_crs_create_bound_crs(
8825 3 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8826 3 : d->m_pj_bound_crs_co);
8827 3 : proj_destroy(horizCRS);
8828 3 : d->undoDemoteFromBoundCRS();
8829 3 : d->setPjCRS(newBoundCRS);
8830 : }
8831 : else
8832 : {
8833 39 : d->undoDemoteFromBoundCRS();
8834 39 : d->setPjCRS(horizCRS);
8835 : }
8836 :
8837 42 : return OGRERR_NONE;
8838 : }
8839 :
8840 : /************************************************************************/
8841 : /* OSRStripVertical() */
8842 : /************************************************************************/
8843 : /**
8844 : * \brief Convert a compound cs into a horizontal CS.
8845 : *
8846 : * This function is the same as the C++ method
8847 : * OGRSpatialReference::StripVertical().
8848 : */
8849 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8850 :
8851 : {
8852 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8853 :
8854 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8855 : }
8856 :
8857 : /************************************************************************/
8858 : /* StripTOWGS84IfKnownDatumAndAllowed() */
8859 : /************************************************************************/
8860 :
8861 : /**
8862 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8863 : * and this is allowed by the user.
8864 : *
8865 : * The default behavior is to remove TOWGS84 information if the CRS has a
8866 : * known horizontal datum. This can be disabled by setting the
8867 : * OSR_STRIP_TOWGS84 configuration option to NO.
8868 : *
8869 : * @return true if TOWGS84 has been removed.
8870 : * @since OGR 3.1.0
8871 : */
8872 :
8873 7783 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8874 : {
8875 7783 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8876 : {
8877 7780 : if (StripTOWGS84IfKnownDatum())
8878 : {
8879 70 : CPLDebug("OSR", "TOWGS84 information has been removed. "
8880 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
8881 : "configuration option to NO");
8882 70 : return true;
8883 : }
8884 : }
8885 7713 : return false;
8886 : }
8887 :
8888 : /************************************************************************/
8889 : /* StripTOWGS84IfKnownDatum() */
8890 : /************************************************************************/
8891 :
8892 : /**
8893 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8894 : *
8895 : * @return true if TOWGS84 has been removed.
8896 : * @since OGR 3.1.0
8897 : */
8898 :
8899 7786 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8900 :
8901 : {
8902 15572 : TAKE_OPTIONAL_LOCK();
8903 :
8904 7786 : d->refreshProjObj();
8905 7786 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8906 : {
8907 7698 : return false;
8908 : }
8909 88 : auto ctxt = d->getPROJContext();
8910 88 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8911 88 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8912 : {
8913 2 : proj_destroy(baseCRS);
8914 2 : return false;
8915 : }
8916 :
8917 : // Known base CRS code ? Return base CRS
8918 86 : const char *pszCode = proj_get_id_code(baseCRS, 0);
8919 86 : if (pszCode)
8920 : {
8921 2 : d->setPjCRS(baseCRS);
8922 2 : return true;
8923 : }
8924 :
8925 84 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
8926 : #if PROJ_VERSION_MAJOR > 7 || \
8927 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8928 : if (datum == nullptr)
8929 : {
8930 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8931 : }
8932 : #endif
8933 84 : if (!datum)
8934 : {
8935 0 : proj_destroy(baseCRS);
8936 0 : return false;
8937 : }
8938 :
8939 : // Known datum code ? Return base CRS
8940 84 : pszCode = proj_get_id_code(datum, 0);
8941 84 : if (pszCode)
8942 : {
8943 2 : proj_destroy(datum);
8944 2 : d->setPjCRS(baseCRS);
8945 2 : return true;
8946 : }
8947 :
8948 82 : const char *name = proj_get_name(datum);
8949 82 : if (EQUAL(name, "unknown"))
8950 : {
8951 1 : proj_destroy(datum);
8952 1 : proj_destroy(baseCRS);
8953 1 : return false;
8954 : }
8955 81 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8956 : PJ_OBJ_LIST *list =
8957 81 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8958 :
8959 81 : bool knownDatumName = false;
8960 81 : if (list)
8961 : {
8962 81 : if (proj_list_get_count(list) == 1)
8963 : {
8964 69 : knownDatumName = true;
8965 : }
8966 81 : proj_list_destroy(list);
8967 : }
8968 :
8969 81 : proj_destroy(datum);
8970 81 : if (knownDatumName)
8971 : {
8972 69 : d->setPjCRS(baseCRS);
8973 69 : return true;
8974 : }
8975 12 : proj_destroy(baseCRS);
8976 12 : return false;
8977 : }
8978 :
8979 : /************************************************************************/
8980 : /* IsCompound() */
8981 : /************************************************************************/
8982 :
8983 : /**
8984 : * \brief Check if coordinate system is compound.
8985 : *
8986 : * This method is the same as the C function OSRIsCompound().
8987 : *
8988 : * @return TRUE if this is rooted with a COMPD_CS node.
8989 : */
8990 :
8991 37131 : int OGRSpatialReference::IsCompound() const
8992 :
8993 : {
8994 37131 : TAKE_OPTIONAL_LOCK();
8995 :
8996 37131 : d->refreshProjObj();
8997 37131 : d->demoteFromBoundCRS();
8998 37131 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
8999 37131 : d->undoDemoteFromBoundCRS();
9000 74261 : return isCompound;
9001 : }
9002 :
9003 : /************************************************************************/
9004 : /* OSRIsCompound() */
9005 : /************************************************************************/
9006 :
9007 : /**
9008 : * \brief Check if the coordinate system is compound.
9009 : *
9010 : * This function is the same as OGRSpatialReference::IsCompound().
9011 : */
9012 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9013 :
9014 : {
9015 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9016 :
9017 5 : return ToPointer(hSRS)->IsCompound();
9018 : }
9019 :
9020 : /************************************************************************/
9021 : /* IsProjected() */
9022 : /************************************************************************/
9023 :
9024 : /**
9025 : * \brief Check if projected coordinate system.
9026 : *
9027 : * This method is the same as the C function OSRIsProjected().
9028 : *
9029 : * @return TRUE if this contains a PROJCS node indicating a it is a
9030 : * projected coordinate system. Also if it is a CompoundCRS made of a
9031 : * ProjectedCRS
9032 : */
9033 :
9034 35391 : int OGRSpatialReference::IsProjected() const
9035 :
9036 : {
9037 35391 : TAKE_OPTIONAL_LOCK();
9038 :
9039 35390 : d->refreshProjObj();
9040 35391 : d->demoteFromBoundCRS();
9041 35390 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9042 35391 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9043 : {
9044 : auto horizCRS =
9045 142 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9046 142 : if (horizCRS)
9047 : {
9048 142 : auto horizCRSType = proj_get_type(horizCRS);
9049 142 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9050 142 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9051 : {
9052 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9053 6 : if (base)
9054 : {
9055 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9056 6 : proj_destroy(base);
9057 : }
9058 : }
9059 142 : proj_destroy(horizCRS);
9060 : }
9061 : }
9062 35391 : d->undoDemoteFromBoundCRS();
9063 70781 : return isProjected;
9064 : }
9065 :
9066 : /************************************************************************/
9067 : /* OSRIsProjected() */
9068 : /************************************************************************/
9069 : /**
9070 : * \brief Check if projected coordinate system.
9071 : *
9072 : * This function is the same as OGRSpatialReference::IsProjected().
9073 : */
9074 410 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9075 :
9076 : {
9077 410 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9078 :
9079 410 : return ToPointer(hSRS)->IsProjected();
9080 : }
9081 :
9082 : /************************************************************************/
9083 : /* IsGeocentric() */
9084 : /************************************************************************/
9085 :
9086 : /**
9087 : * \brief Check if geocentric coordinate system.
9088 : *
9089 : * This method is the same as the C function OSRIsGeocentric().
9090 : *
9091 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9092 : * geocentric coordinate system.
9093 : *
9094 : * @since OGR 1.9.0
9095 : */
9096 :
9097 15350 : int OGRSpatialReference::IsGeocentric() const
9098 :
9099 : {
9100 15350 : TAKE_OPTIONAL_LOCK();
9101 :
9102 15350 : d->refreshProjObj();
9103 15351 : d->demoteFromBoundCRS();
9104 15350 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9105 15351 : d->undoDemoteFromBoundCRS();
9106 30700 : return isGeocentric;
9107 : }
9108 :
9109 : /************************************************************************/
9110 : /* OSRIsGeocentric() */
9111 : /************************************************************************/
9112 : /**
9113 : * \brief Check if geocentric coordinate system.
9114 : *
9115 : * This function is the same as OGRSpatialReference::IsGeocentric().
9116 : *
9117 : * @since OGR 1.9.0
9118 : */
9119 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9120 :
9121 : {
9122 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9123 :
9124 2 : return ToPointer(hSRS)->IsGeocentric();
9125 : }
9126 :
9127 : /************************************************************************/
9128 : /* IsEmpty() */
9129 : /************************************************************************/
9130 :
9131 : /**
9132 : * \brief Return if the SRS is not set.
9133 : */
9134 :
9135 99189 : bool OGRSpatialReference::IsEmpty() const
9136 : {
9137 99189 : TAKE_OPTIONAL_LOCK();
9138 :
9139 99188 : d->refreshProjObj();
9140 198377 : return d->m_pj_crs == nullptr;
9141 : }
9142 :
9143 : /************************************************************************/
9144 : /* IsGeographic() */
9145 : /************************************************************************/
9146 :
9147 : /**
9148 : * \brief Check if geographic coordinate system.
9149 : *
9150 : * This method is the same as the C function OSRIsGeographic().
9151 : *
9152 : * @return TRUE if this spatial reference is geographic ... that is the
9153 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9154 : * GeographicCRS
9155 : */
9156 :
9157 48481 : int OGRSpatialReference::IsGeographic() const
9158 :
9159 : {
9160 48481 : TAKE_OPTIONAL_LOCK();
9161 :
9162 48481 : d->refreshProjObj();
9163 48481 : d->demoteFromBoundCRS();
9164 69417 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9165 20936 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9166 48481 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9167 : {
9168 : auto horizCRS =
9169 292 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9170 292 : if (horizCRS)
9171 : {
9172 292 : auto horizCRSType = proj_get_type(horizCRS);
9173 292 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9174 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9175 292 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9176 : {
9177 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9178 13 : if (base)
9179 : {
9180 13 : horizCRSType = proj_get_type(base);
9181 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9182 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9183 13 : proj_destroy(base);
9184 : }
9185 : }
9186 292 : proj_destroy(horizCRS);
9187 : }
9188 : }
9189 48481 : d->undoDemoteFromBoundCRS();
9190 96962 : return isGeog;
9191 : }
9192 :
9193 : /************************************************************************/
9194 : /* OSRIsGeographic() */
9195 : /************************************************************************/
9196 : /**
9197 : * \brief Check if geographic coordinate system.
9198 : *
9199 : * This function is the same as OGRSpatialReference::IsGeographic().
9200 : */
9201 334 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9202 :
9203 : {
9204 334 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9205 :
9206 334 : return ToPointer(hSRS)->IsGeographic();
9207 : }
9208 :
9209 : /************************************************************************/
9210 : /* IsDerivedGeographic() */
9211 : /************************************************************************/
9212 :
9213 : /**
9214 : * \brief Check if the CRS is a derived geographic coordinate system.
9215 : * (for example a rotated long/lat grid)
9216 : *
9217 : * This method is the same as the C function OSRIsDerivedGeographic().
9218 : *
9219 : * @since GDAL 3.1.0 and PROJ 6.3.0
9220 : */
9221 :
9222 15210 : int OGRSpatialReference::IsDerivedGeographic() const
9223 :
9224 : {
9225 15210 : TAKE_OPTIONAL_LOCK();
9226 :
9227 15210 : d->refreshProjObj();
9228 15210 : d->demoteFromBoundCRS();
9229 24480 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9230 9270 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9231 : const bool isDerivedGeographic =
9232 15210 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9233 15210 : d->undoDemoteFromBoundCRS();
9234 30420 : return isDerivedGeographic ? TRUE : FALSE;
9235 : }
9236 :
9237 : /************************************************************************/
9238 : /* OSRIsDerivedGeographic() */
9239 : /************************************************************************/
9240 : /**
9241 : * \brief Check if the CRS is a derived geographic coordinate system.
9242 : * (for example a rotated long/lat grid)
9243 : *
9244 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9245 : */
9246 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9247 :
9248 : {
9249 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9250 :
9251 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9252 : }
9253 :
9254 : /************************************************************************/
9255 : /* IsDerivedProjected() */
9256 : /************************************************************************/
9257 :
9258 : /**
9259 : * \brief Check if the CRS is a derived projected coordinate system.
9260 : *
9261 : * This method is the same as the C function OSRIsDerivedGeographic().
9262 : *
9263 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9264 : */
9265 :
9266 0 : int OGRSpatialReference::IsDerivedProjected() const
9267 :
9268 : {
9269 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9270 : TAKE_OPTIONAL_LOCK();
9271 : d->refreshProjObj();
9272 : d->demoteFromBoundCRS();
9273 : const bool isDerivedProjected =
9274 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9275 : d->undoDemoteFromBoundCRS();
9276 : return isDerivedProjected ? TRUE : FALSE;
9277 : #else
9278 0 : return FALSE;
9279 : #endif
9280 : }
9281 :
9282 : /************************************************************************/
9283 : /* OSRIsDerivedProjected() */
9284 : /************************************************************************/
9285 : /**
9286 : * \brief Check if the CRS is a derived projected coordinate system.
9287 : *
9288 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9289 : *
9290 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9291 : */
9292 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9293 :
9294 : {
9295 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9296 :
9297 0 : return ToPointer(hSRS)->IsDerivedProjected();
9298 : }
9299 :
9300 : /************************************************************************/
9301 : /* IsLocal() */
9302 : /************************************************************************/
9303 :
9304 : /**
9305 : * \brief Check if local coordinate system.
9306 : *
9307 : * This method is the same as the C function OSRIsLocal().
9308 : *
9309 : * @return TRUE if this spatial reference is local ... that is the
9310 : * root is a LOCAL_CS node.
9311 : */
9312 :
9313 7250 : int OGRSpatialReference::IsLocal() const
9314 :
9315 : {
9316 7250 : TAKE_OPTIONAL_LOCK();
9317 7250 : d->refreshProjObj();
9318 14500 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9319 : }
9320 :
9321 : /************************************************************************/
9322 : /* OSRIsLocal() */
9323 : /************************************************************************/
9324 : /**
9325 : * \brief Check if local coordinate system.
9326 : *
9327 : * This function is the same as OGRSpatialReference::IsLocal().
9328 : */
9329 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9330 :
9331 : {
9332 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9333 :
9334 8 : return ToPointer(hSRS)->IsLocal();
9335 : }
9336 :
9337 : /************************************************************************/
9338 : /* IsVertical() */
9339 : /************************************************************************/
9340 :
9341 : /**
9342 : * \brief Check if vertical coordinate system.
9343 : *
9344 : * This method is the same as the C function OSRIsVertical().
9345 : *
9346 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9347 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9348 : * VerticalCRS
9349 : *
9350 : * @since OGR 1.8.0
9351 : */
9352 :
9353 7886 : int OGRSpatialReference::IsVertical() const
9354 :
9355 : {
9356 7886 : TAKE_OPTIONAL_LOCK();
9357 7886 : d->refreshProjObj();
9358 7886 : d->demoteFromBoundCRS();
9359 7886 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9360 7886 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9361 : {
9362 : auto vertCRS =
9363 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9364 33 : if (vertCRS)
9365 : {
9366 33 : const auto vertCRSType = proj_get_type(vertCRS);
9367 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9368 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9369 : {
9370 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9371 0 : if (base)
9372 : {
9373 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9374 0 : proj_destroy(base);
9375 : }
9376 : }
9377 33 : proj_destroy(vertCRS);
9378 : }
9379 : }
9380 7886 : d->undoDemoteFromBoundCRS();
9381 15772 : return isVertical;
9382 : }
9383 :
9384 : /************************************************************************/
9385 : /* OSRIsVertical() */
9386 : /************************************************************************/
9387 : /**
9388 : * \brief Check if vertical coordinate system.
9389 : *
9390 : * This function is the same as OGRSpatialReference::IsVertical().
9391 : *
9392 : * @since OGR 1.8.0
9393 : */
9394 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9395 :
9396 : {
9397 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9398 :
9399 0 : return ToPointer(hSRS)->IsVertical();
9400 : }
9401 :
9402 : /************************************************************************/
9403 : /* IsDynamic() */
9404 : /************************************************************************/
9405 :
9406 : /**
9407 : * \brief Check if a CRS is a dynamic CRS.
9408 : *
9409 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9410 : * plate-fixed.
9411 : *
9412 : * This method is the same as the C function OSRIsDynamic().
9413 : *
9414 : * @return true if the CRS is dynamic
9415 : *
9416 : * @since OGR 3.4.0
9417 : *
9418 : * @see HasPointMotionOperation()
9419 : */
9420 :
9421 11879 : bool OGRSpatialReference::IsDynamic() const
9422 :
9423 : {
9424 11879 : TAKE_OPTIONAL_LOCK();
9425 11879 : bool isDynamic = false;
9426 11879 : d->refreshProjObj();
9427 11878 : d->demoteFromBoundCRS();
9428 11878 : auto ctxt = d->getPROJContext();
9429 11879 : PJ *horiz = nullptr;
9430 11879 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9431 : {
9432 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9433 : }
9434 11783 : else if (d->m_pj_crs)
9435 : {
9436 11740 : horiz = proj_clone(ctxt, d->m_pj_crs);
9437 : }
9438 11879 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9439 : {
9440 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9441 6 : if (baseCRS)
9442 : {
9443 6 : proj_destroy(horiz);
9444 6 : horiz = baseCRS;
9445 : }
9446 : }
9447 11879 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9448 11879 : if (datum)
9449 : {
9450 11823 : const auto type = proj_get_type(datum);
9451 11823 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9452 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9453 11823 : if (!isDynamic)
9454 : {
9455 11823 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9456 11823 : const char *code = proj_get_id_code(datum, 0);
9457 11823 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9458 11422 : EQUAL(code, "6326"))
9459 : {
9460 7053 : isDynamic = true;
9461 : }
9462 : }
9463 11823 : proj_destroy(datum);
9464 : }
9465 : #if PROJ_VERSION_MAJOR > 7 || \
9466 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9467 : else
9468 : {
9469 : auto ensemble =
9470 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9471 : if (ensemble)
9472 : {
9473 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9474 : if (member)
9475 : {
9476 : const auto type = proj_get_type(member);
9477 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9478 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9479 : proj_destroy(member);
9480 : }
9481 : proj_destroy(ensemble);
9482 : }
9483 : }
9484 : #endif
9485 11879 : proj_destroy(horiz);
9486 11879 : d->undoDemoteFromBoundCRS();
9487 23758 : return isDynamic;
9488 : }
9489 :
9490 : /************************************************************************/
9491 : /* OSRIsDynamic() */
9492 : /************************************************************************/
9493 : /**
9494 : * \brief Check if a CRS is a dynamic CRS.
9495 : *
9496 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9497 : * plate-fixed.
9498 : *
9499 : * This function is the same as OGRSpatialReference::IsDynamic().
9500 : *
9501 : * @since OGR 3.4.0
9502 : */
9503 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9504 :
9505 : {
9506 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9507 :
9508 0 : return ToPointer(hSRS)->IsDynamic();
9509 : }
9510 :
9511 : /************************************************************************/
9512 : /* HasPointMotionOperation() */
9513 : /************************************************************************/
9514 :
9515 : /**
9516 : * \brief Check if a CRS has at least an associated point motion operation.
9517 : *
9518 : * Some CRS are not formally declared as dynamic, but may behave as such
9519 : * in practice due to the presence of point motion operation, to perform
9520 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9521 : *
9522 : * @return true if the CRS has at least an associated point motion operation.
9523 : *
9524 : * @since OGR 3.8.0 and PROJ 9.4.0
9525 : *
9526 : * @see IsDynamic()
9527 : */
9528 :
9529 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9530 :
9531 : {
9532 : #if PROJ_VERSION_MAJOR > 9 || \
9533 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9534 : TAKE_OPTIONAL_LOCK();
9535 : d->refreshProjObj();
9536 : d->demoteFromBoundCRS();
9537 : auto ctxt = d->getPROJContext();
9538 : auto res =
9539 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9540 : d->undoDemoteFromBoundCRS();
9541 : return res;
9542 : #else
9543 5 : return false;
9544 : #endif
9545 : }
9546 :
9547 : /************************************************************************/
9548 : /* OSRHasPointMotionOperation() */
9549 : /************************************************************************/
9550 :
9551 : /**
9552 : * \brief Check if a CRS has at least an associated point motion operation.
9553 : *
9554 : * Some CRS are not formally declared as dynamic, but may behave as such
9555 : * in practice due to the presence of point motion operation, to perform
9556 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9557 : *
9558 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9559 : *
9560 : * @since OGR 3.8.0 and PROJ 9.4.0
9561 : */
9562 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9563 :
9564 : {
9565 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9566 :
9567 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9568 : }
9569 :
9570 : /************************************************************************/
9571 : /* CloneGeogCS() */
9572 : /************************************************************************/
9573 :
9574 : /**
9575 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9576 : * object.
9577 : *
9578 : * @return a new SRS, which becomes the responsibility of the caller.
9579 : */
9580 3522 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9581 :
9582 : {
9583 7044 : TAKE_OPTIONAL_LOCK();
9584 3522 : d->refreshProjObj();
9585 3522 : if (d->m_pj_crs)
9586 : {
9587 3522 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9588 0 : return nullptr;
9589 :
9590 : auto geodCRS =
9591 3522 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9592 3522 : if (geodCRS)
9593 : {
9594 3522 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9595 3522 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9596 : {
9597 : PJ *hub_crs =
9598 14 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9599 14 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9600 14 : d->m_pj_crs);
9601 14 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9602 : geodCRS, hub_crs, co);
9603 14 : proj_destroy(geodCRS);
9604 14 : geodCRS = temp;
9605 14 : proj_destroy(hub_crs);
9606 14 : proj_destroy(co);
9607 : }
9608 :
9609 : /* --------------------------------------------------------------------
9610 : */
9611 : /* We have to reconstruct the GEOGCS node for geocentric */
9612 : /* coordinate systems. */
9613 : /* --------------------------------------------------------------------
9614 : */
9615 3522 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9616 : {
9617 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9618 : #if PROJ_VERSION_MAJOR > 7 || \
9619 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9620 : if (datum == nullptr)
9621 : {
9622 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9623 : geodCRS);
9624 : }
9625 : #endif
9626 0 : if (datum)
9627 : {
9628 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9629 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9630 : nullptr, 0);
9631 0 : auto temp = proj_create_geographic_crs_from_datum(
9632 : d->getPROJContext(), "unnamed", datum, cs);
9633 0 : proj_destroy(datum);
9634 0 : proj_destroy(cs);
9635 0 : proj_destroy(geodCRS);
9636 0 : geodCRS = temp;
9637 : }
9638 : }
9639 :
9640 3522 : poNewSRS->d->setPjCRS(geodCRS);
9641 3522 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9642 2349 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9643 3522 : return poNewSRS;
9644 : }
9645 : }
9646 0 : return nullptr;
9647 : }
9648 :
9649 : /************************************************************************/
9650 : /* OSRCloneGeogCS() */
9651 : /************************************************************************/
9652 : /**
9653 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9654 : * object.
9655 : *
9656 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9657 : */
9658 143 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9659 :
9660 : {
9661 143 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9662 :
9663 143 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9664 : }
9665 :
9666 : /************************************************************************/
9667 : /* IsSameGeogCS() */
9668 : /************************************************************************/
9669 :
9670 : /**
9671 : * \brief Do the GeogCS'es match?
9672 : *
9673 : * This method is the same as the C function OSRIsSameGeogCS().
9674 : *
9675 : * @param poOther the SRS being compared against.
9676 : *
9677 : * @return TRUE if they are the same or FALSE otherwise.
9678 : */
9679 :
9680 7226 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9681 :
9682 : {
9683 7226 : return IsSameGeogCS(poOther, nullptr);
9684 : }
9685 :
9686 : /**
9687 : * \brief Do the GeogCS'es match?
9688 : *
9689 : * This method is the same as the C function OSRIsSameGeogCS().
9690 : *
9691 : * @param poOther the SRS being compared against.
9692 : * @param papszOptions options. ignored
9693 : *
9694 : * @return TRUE if they are the same or FALSE otherwise.
9695 : */
9696 :
9697 7225 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9698 : const char *const *papszOptions) const
9699 :
9700 : {
9701 14450 : TAKE_OPTIONAL_LOCK();
9702 :
9703 7226 : CPL_IGNORE_RET_VAL(papszOptions);
9704 :
9705 7225 : d->refreshProjObj();
9706 7226 : poOther->d->refreshProjObj();
9707 7226 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9708 0 : return FALSE;
9709 7225 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9710 7226 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9711 21677 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9712 7225 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9713 : {
9714 0 : return FALSE;
9715 : }
9716 :
9717 7226 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9718 : auto otherGeodCRS =
9719 7226 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9720 7226 : if (!geodCRS || !otherGeodCRS)
9721 : {
9722 0 : proj_destroy(geodCRS);
9723 0 : proj_destroy(otherGeodCRS);
9724 0 : return FALSE;
9725 : }
9726 :
9727 7226 : int ret = proj_is_equivalent_to(
9728 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9729 :
9730 7226 : proj_destroy(geodCRS);
9731 7226 : proj_destroy(otherGeodCRS);
9732 7225 : return ret;
9733 : }
9734 :
9735 : /************************************************************************/
9736 : /* OSRIsSameGeogCS() */
9737 : /************************************************************************/
9738 :
9739 : /**
9740 : * \brief Do the GeogCS'es match?
9741 : *
9742 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9743 : */
9744 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9745 :
9746 : {
9747 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9748 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9749 :
9750 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9751 : }
9752 :
9753 : /************************************************************************/
9754 : /* IsSameVertCS() */
9755 : /************************************************************************/
9756 :
9757 : /**
9758 : * \brief Do the VertCS'es match?
9759 : *
9760 : * This method is the same as the C function OSRIsSameVertCS().
9761 : *
9762 : * @param poOther the SRS being compared against.
9763 : *
9764 : * @return TRUE if they are the same or FALSE otherwise.
9765 : */
9766 :
9767 2 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9768 :
9769 : {
9770 4 : TAKE_OPTIONAL_LOCK();
9771 :
9772 : /* -------------------------------------------------------------------- */
9773 : /* Does the datum name match? */
9774 : /* -------------------------------------------------------------------- */
9775 2 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9776 2 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9777 :
9778 2 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9779 2 : !EQUAL(pszThisValue, pszOtherValue))
9780 1 : return FALSE;
9781 :
9782 : /* -------------------------------------------------------------------- */
9783 : /* Do the units match? */
9784 : /* -------------------------------------------------------------------- */
9785 1 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9786 1 : if (pszThisValue == nullptr)
9787 0 : pszThisValue = "1.0";
9788 :
9789 1 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9790 1 : if (pszOtherValue == nullptr)
9791 0 : pszOtherValue = "1.0";
9792 :
9793 1 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9794 0 : return FALSE;
9795 :
9796 1 : return TRUE;
9797 : }
9798 :
9799 : /************************************************************************/
9800 : /* OSRIsSameVertCS() */
9801 : /************************************************************************/
9802 :
9803 : /**
9804 : * \brief Do the VertCS'es match?
9805 : *
9806 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9807 : */
9808 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9809 :
9810 : {
9811 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9812 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9813 :
9814 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9815 : }
9816 :
9817 : /************************************************************************/
9818 : /* IsSame() */
9819 : /************************************************************************/
9820 :
9821 : /**
9822 : * \brief Do these two spatial references describe the same system ?
9823 : *
9824 : * @param poOtherSRS the SRS being compared to.
9825 : *
9826 : * @return TRUE if equivalent or FALSE otherwise.
9827 : */
9828 :
9829 4376 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9830 :
9831 : {
9832 4376 : return IsSame(poOtherSRS, nullptr);
9833 : }
9834 :
9835 : /**
9836 : * \brief Do these two spatial references describe the same system ?
9837 : *
9838 : * This also takes into account the data axis to CRS axis mapping by default
9839 : *
9840 : * @param poOtherSRS the SRS being compared to.
9841 : * @param papszOptions options. NULL or NULL terminated list of options.
9842 : * Currently supported options are:
9843 : * <ul>
9844 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9845 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9846 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9847 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9848 : * </ul>
9849 : *
9850 : * @return TRUE if equivalent or FALSE otherwise.
9851 : */
9852 :
9853 12681 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9854 : const char *const *papszOptions) const
9855 :
9856 : {
9857 25362 : TAKE_OPTIONAL_LOCK();
9858 :
9859 12681 : d->refreshProjObj();
9860 12681 : poOtherSRS->d->refreshProjObj();
9861 12681 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9862 50 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9863 12631 : if (!CPLTestBool(CSLFetchNameValueDef(
9864 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9865 : {
9866 12107 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9867 2095 : return false;
9868 : }
9869 :
9870 10536 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9871 : "IGNORE_COORDINATE_EPOCH", "NO")))
9872 : {
9873 10229 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9874 27 : return false;
9875 : }
9876 :
9877 10509 : bool reboundSelf = false;
9878 10509 : bool reboundOther = false;
9879 10564 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9880 55 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9881 : {
9882 16 : d->demoteFromBoundCRS();
9883 16 : reboundSelf = true;
9884 : }
9885 20947 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9886 10454 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9887 : {
9888 28 : poOtherSRS->d->demoteFromBoundCRS();
9889 28 : reboundOther = true;
9890 : }
9891 :
9892 10509 : PJ_COMPARISON_CRITERION criterion =
9893 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9894 10509 : const char *pszCriterion = CSLFetchNameValueDef(
9895 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9896 10509 : if (EQUAL(pszCriterion, "STRICT"))
9897 0 : criterion = PJ_COMP_STRICT;
9898 10509 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
9899 5991 : criterion = PJ_COMP_EQUIVALENT;
9900 4518 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9901 : {
9902 0 : CPLError(CE_Warning, CPLE_NotSupported,
9903 : "Unsupported value for CRITERION: %s", pszCriterion);
9904 : }
9905 : int ret =
9906 10509 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9907 10509 : if (reboundSelf)
9908 16 : d->undoDemoteFromBoundCRS();
9909 10509 : if (reboundOther)
9910 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
9911 :
9912 10509 : return ret;
9913 : }
9914 :
9915 : /************************************************************************/
9916 : /* OSRIsSame() */
9917 : /************************************************************************/
9918 :
9919 : /**
9920 : * \brief Do these two spatial references describe the same system ?
9921 : *
9922 : * This function is the same as OGRSpatialReference::IsSame().
9923 : */
9924 29 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9925 :
9926 : {
9927 29 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9928 29 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9929 :
9930 29 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9931 : }
9932 :
9933 : /************************************************************************/
9934 : /* OSRIsSameEx() */
9935 : /************************************************************************/
9936 :
9937 : /**
9938 : * \brief Do these two spatial references describe the same system ?
9939 : *
9940 : * This function is the same as OGRSpatialReference::IsSame().
9941 : */
9942 600 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9943 : const char *const *papszOptions)
9944 : {
9945 600 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9946 600 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9947 :
9948 600 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9949 : }
9950 :
9951 : /************************************************************************/
9952 : /* convertToOtherProjection() */
9953 : /************************************************************************/
9954 :
9955 : /**
9956 : * \brief Convert to another equivalent projection
9957 : *
9958 : * Currently implemented:
9959 : * <ul>
9960 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9961 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9962 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9963 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9964 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9965 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9966 : * </ul>
9967 : *
9968 : * @param pszTargetProjection target projection.
9969 : * @param papszOptions lists of options. None supported currently.
9970 : * @return a new SRS, or NULL in case of error.
9971 : *
9972 : * @since GDAL 2.3
9973 : */
9974 89 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
9975 : const char *pszTargetProjection,
9976 : CPL_UNUSED const char *const *papszOptions) const
9977 : {
9978 178 : TAKE_OPTIONAL_LOCK();
9979 :
9980 89 : if (pszTargetProjection == nullptr)
9981 1 : return nullptr;
9982 : int new_code;
9983 88 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
9984 : {
9985 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
9986 : }
9987 82 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
9988 : {
9989 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
9990 : }
9991 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
9992 : {
9993 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
9994 : }
9995 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
9996 : {
9997 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
9998 : }
9999 : else
10000 : {
10001 1 : return nullptr;
10002 : }
10003 :
10004 87 : d->refreshProjObj();
10005 87 : d->demoteFromBoundCRS();
10006 87 : OGRSpatialReference *poNewSRS = nullptr;
10007 87 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10008 : {
10009 : auto conv =
10010 86 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10011 86 : auto new_conv = proj_convert_conversion_to_other_method(
10012 : d->getPROJContext(), conv, new_code, nullptr);
10013 86 : proj_destroy(conv);
10014 86 : if (new_conv)
10015 : {
10016 : auto geodCRS =
10017 72 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10018 72 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10019 72 : d->m_pj_crs);
10020 72 : if (geodCRS && cs)
10021 : {
10022 72 : auto new_proj_crs = proj_create_projected_crs(
10023 72 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10024 : new_conv, cs);
10025 72 : proj_destroy(new_conv);
10026 72 : if (new_proj_crs)
10027 : {
10028 72 : poNewSRS = new OGRSpatialReference();
10029 :
10030 72 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10031 : {
10032 9 : auto boundCRS = proj_crs_create_bound_crs(
10033 : d->getPROJContext(), new_proj_crs,
10034 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10035 9 : if (boundCRS)
10036 : {
10037 9 : proj_destroy(new_proj_crs);
10038 9 : new_proj_crs = boundCRS;
10039 : }
10040 : }
10041 :
10042 72 : poNewSRS->d->setPjCRS(new_proj_crs);
10043 : }
10044 : }
10045 72 : proj_destroy(geodCRS);
10046 72 : proj_destroy(cs);
10047 : }
10048 : }
10049 87 : d->undoDemoteFromBoundCRS();
10050 87 : return poNewSRS;
10051 : }
10052 :
10053 : /************************************************************************/
10054 : /* OSRConvertToOtherProjection() */
10055 : /************************************************************************/
10056 :
10057 : /**
10058 : * \brief Convert to another equivalent projection
10059 : *
10060 : * Currently implemented:
10061 : * <ul>
10062 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10063 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10064 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10065 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10066 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10067 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10068 : * </ul>
10069 : *
10070 : * @param hSRS source SRS
10071 : * @param pszTargetProjection target projection.
10072 : * @param papszOptions lists of options. None supported currently.
10073 : * @return a new SRS, or NULL in case of error.
10074 : *
10075 : * @since GDAL 2.3
10076 : */
10077 : OGRSpatialReferenceH
10078 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10079 : const char *pszTargetProjection,
10080 : const char *const *papszOptions)
10081 : {
10082 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10083 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10084 28 : pszTargetProjection, papszOptions));
10085 : }
10086 :
10087 : /************************************************************************/
10088 : /* OSRFindMatches() */
10089 : /************************************************************************/
10090 :
10091 : /**
10092 : * \brief Try to identify a match between the passed SRS and a related SRS
10093 : * in a catalog.
10094 : *
10095 : * Matching may be partial, or may fail.
10096 : * Returned entries will be sorted by decreasing match confidence (first
10097 : * entry has the highest match confidence).
10098 : *
10099 : * The exact way matching is done may change in future versions. Starting with
10100 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10101 : *
10102 : * This function is the same as OGRSpatialReference::FindMatches().
10103 : *
10104 : * @param hSRS SRS to match
10105 : * @param papszOptions NULL terminated list of options or NULL
10106 : * @param pnEntries Output parameter. Number of values in the returned array.
10107 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10108 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10109 : * indicate the confidence in the match. 100 is the highest confidence level.
10110 : * The array must be freed with CPLFree().
10111 : *
10112 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10113 : * with OSRFreeSRSArray()
10114 : *
10115 : * @since GDAL 2.3
10116 : */
10117 8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10118 : char **papszOptions, int *pnEntries,
10119 : int **ppanMatchConfidence)
10120 : {
10121 8 : if (pnEntries)
10122 8 : *pnEntries = 0;
10123 8 : if (ppanMatchConfidence)
10124 8 : *ppanMatchConfidence = nullptr;
10125 8 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10126 :
10127 8 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10128 8 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10129 : }
10130 :
10131 : /************************************************************************/
10132 : /* OSRFreeSRSArray() */
10133 : /************************************************************************/
10134 :
10135 : /**
10136 : * \brief Free return of OSRIdentifyMatches()
10137 : *
10138 : * @param pahSRS array of SRS (must be NULL terminated)
10139 : * @since GDAL 2.3
10140 : */
10141 195 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10142 : {
10143 195 : if (pahSRS != nullptr)
10144 : {
10145 2458 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10146 : {
10147 2281 : OSRRelease(pahSRS[i]);
10148 : }
10149 177 : CPLFree(pahSRS);
10150 : }
10151 195 : }
10152 :
10153 : /************************************************************************/
10154 : /* FindBestMatch() */
10155 : /************************************************************************/
10156 :
10157 : /**
10158 : * \brief Try to identify the best match between the passed SRS and a related
10159 : * SRS in a catalog.
10160 : *
10161 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10162 : * of filtering its output.
10163 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10164 : * will be considered. If there is a single match, it is returned.
10165 : * If there are several matches, only return the one under the
10166 : * pszPreferredAuthority, if there is a single one under that authority.
10167 : *
10168 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10169 : * 100). If set to 0, 90 is used.
10170 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10171 : * "EPSG" is used.
10172 : * @param papszOptions NULL terminated list of options or NULL. No option is
10173 : * defined at time of writing.
10174 : *
10175 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10176 : *
10177 : * @since GDAL 3.6
10178 : * @see OGRSpatialReference::FindMatches()
10179 : */
10180 : OGRSpatialReference *
10181 1209 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10182 : const char *pszPreferredAuthority,
10183 : CSLConstList papszOptions) const
10184 : {
10185 2418 : TAKE_OPTIONAL_LOCK();
10186 :
10187 1209 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10188 :
10189 1209 : if (nMinimumMatchConfidence == 0)
10190 0 : nMinimumMatchConfidence = 90;
10191 1209 : if (pszPreferredAuthority == nullptr)
10192 198 : pszPreferredAuthority = "EPSG";
10193 :
10194 : // Try to identify the CRS with the database
10195 1209 : int nEntries = 0;
10196 1209 : int *panConfidence = nullptr;
10197 : OGRSpatialReferenceH *pahSRS =
10198 1209 : FindMatches(nullptr, &nEntries, &panConfidence);
10199 1209 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10200 : {
10201 2122 : std::vector<double> adfTOWGS84(7);
10202 1061 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10203 : {
10204 1060 : adfTOWGS84.clear();
10205 : }
10206 :
10207 1061 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10208 :
10209 : auto poBaseGeogCRS =
10210 1061 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10211 :
10212 : // If the base geographic SRS of the SRS is EPSG:4326
10213 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10214 : // SRS code
10215 : // Same with EPSG:4258 (ETRS89), since it's the only known
10216 : // TOWGS84[] style transformation to WGS 84, and given the
10217 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10218 : // chance that a non-NULL TOWGS84[] will emerge.
10219 1061 : const char *pszAuthorityName = nullptr;
10220 1061 : const char *pszAuthorityCode = nullptr;
10221 1061 : const char *pszBaseAuthorityName = nullptr;
10222 1061 : const char *pszBaseAuthorityCode = nullptr;
10223 2122 : if (adfTOWGS84 == std::vector<double>(7) &&
10224 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10225 1 : EQUAL(pszAuthorityName, "EPSG") &&
10226 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10227 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10228 1 : nullptr &&
10229 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10230 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10231 2123 : nullptr &&
10232 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10233 1 : EQUAL(pszBaseAuthorityCode, "4258")))
10234 : {
10235 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10236 : }
10237 :
10238 1061 : CPLFree(pahSRS);
10239 1061 : CPLFree(panConfidence);
10240 :
10241 1061 : return poSRS;
10242 : }
10243 : else
10244 : {
10245 : // If there are several matches >= nMinimumMatchConfidence, take the
10246 : // only one that is under pszPreferredAuthority
10247 148 : int iBestEntry = -1;
10248 1648 : for (int i = 0; i < nEntries; i++)
10249 : {
10250 1500 : if (panConfidence[i] >= nMinimumMatchConfidence)
10251 : {
10252 : const char *pszAuthName =
10253 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10254 3 : ->GetAuthorityName(nullptr);
10255 3 : if (pszAuthName != nullptr &&
10256 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10257 : {
10258 3 : if (iBestEntry < 0)
10259 3 : iBestEntry = i;
10260 : else
10261 : {
10262 0 : iBestEntry = -1;
10263 0 : break;
10264 : }
10265 : }
10266 : }
10267 : }
10268 148 : if (iBestEntry >= 0)
10269 : {
10270 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10271 3 : OSRFreeSRSArray(pahSRS);
10272 3 : CPLFree(panConfidence);
10273 3 : return poRet;
10274 : }
10275 : }
10276 145 : OSRFreeSRSArray(pahSRS);
10277 145 : CPLFree(panConfidence);
10278 145 : return nullptr;
10279 : }
10280 :
10281 : /************************************************************************/
10282 : /* SetTOWGS84() */
10283 : /************************************************************************/
10284 :
10285 : /**
10286 : * \brief Set the Bursa-Wolf conversion to WGS84.
10287 : *
10288 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10289 : * if there is no existing DATUM node. It will replace
10290 : * an existing TOWGS84 node if there is one.
10291 : *
10292 : * The parameters have the same meaning as EPSG transformation 9606
10293 : * (Position Vector 7-param. transformation).
10294 : *
10295 : * This method is the same as the C function OSRSetTOWGS84().
10296 : *
10297 : * @param dfDX X child in meters.
10298 : * @param dfDY Y child in meters.
10299 : * @param dfDZ Z child in meters.
10300 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10301 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10302 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10303 : * @param dfPPM scaling factor (parts per million).
10304 : *
10305 : * @return OGRERR_NONE on success.
10306 : */
10307 :
10308 106 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10309 : double dfEX, double dfEY, double dfEZ,
10310 : double dfPPM)
10311 :
10312 : {
10313 212 : TAKE_OPTIONAL_LOCK();
10314 :
10315 106 : d->refreshProjObj();
10316 106 : if (d->m_pj_crs == nullptr)
10317 : {
10318 0 : return OGRERR_FAILURE;
10319 : }
10320 :
10321 : // Remove existing BoundCRS
10322 106 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10323 : {
10324 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10325 0 : if (!baseCRS)
10326 0 : return OGRERR_FAILURE;
10327 0 : d->setPjCRS(baseCRS);
10328 : }
10329 :
10330 : PJ_PARAM_DESCRIPTION params[7];
10331 :
10332 106 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10333 106 : params[0].auth_name = "EPSG";
10334 106 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10335 106 : params[0].value = dfDX;
10336 106 : params[0].unit_name = "metre";
10337 106 : params[0].unit_conv_factor = 1.0;
10338 106 : params[0].unit_type = PJ_UT_LINEAR;
10339 :
10340 106 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10341 106 : params[1].auth_name = "EPSG";
10342 106 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10343 106 : params[1].value = dfDY;
10344 106 : params[1].unit_name = "metre";
10345 106 : params[1].unit_conv_factor = 1.0;
10346 106 : params[1].unit_type = PJ_UT_LINEAR;
10347 :
10348 106 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10349 106 : params[2].auth_name = "EPSG";
10350 106 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10351 106 : params[2].value = dfDZ;
10352 106 : params[2].unit_name = "metre";
10353 106 : params[2].unit_conv_factor = 1.0;
10354 106 : params[2].unit_type = PJ_UT_LINEAR;
10355 :
10356 106 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10357 106 : params[3].auth_name = "EPSG";
10358 106 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10359 106 : params[3].value = dfEX;
10360 106 : params[3].unit_name = "arc-second";
10361 106 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10362 106 : params[3].unit_type = PJ_UT_ANGULAR;
10363 :
10364 106 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10365 106 : params[4].auth_name = "EPSG";
10366 106 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10367 106 : params[4].value = dfEY;
10368 106 : params[4].unit_name = "arc-second";
10369 106 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10370 106 : params[4].unit_type = PJ_UT_ANGULAR;
10371 :
10372 106 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10373 106 : params[5].auth_name = "EPSG";
10374 106 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10375 106 : params[5].value = dfEZ;
10376 106 : params[5].unit_name = "arc-second";
10377 106 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10378 106 : params[5].unit_type = PJ_UT_ANGULAR;
10379 :
10380 106 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10381 106 : params[6].auth_name = "EPSG";
10382 106 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10383 106 : params[6].value = dfPPM;
10384 106 : params[6].unit_name = "parts per million";
10385 106 : params[6].unit_conv_factor = 1e-6;
10386 106 : params[6].unit_type = PJ_UT_SCALE;
10387 :
10388 : auto sourceCRS =
10389 106 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10390 106 : if (!sourceCRS)
10391 : {
10392 0 : return OGRERR_FAILURE;
10393 : }
10394 :
10395 106 : const auto sourceType = proj_get_type(sourceCRS);
10396 :
10397 106 : auto targetCRS = proj_create_from_database(
10398 : d->getPROJContext(), "EPSG",
10399 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10400 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10401 : : "4978",
10402 : PJ_CATEGORY_CRS, false, nullptr);
10403 106 : if (!targetCRS)
10404 : {
10405 0 : proj_destroy(sourceCRS);
10406 0 : return OGRERR_FAILURE;
10407 : }
10408 :
10409 212 : CPLString osMethodCode;
10410 : osMethodCode.Printf("%d",
10411 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10412 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10413 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10414 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10415 106 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10416 :
10417 106 : auto transf = proj_create_transformation(
10418 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10419 : sourceCRS, targetCRS, nullptr,
10420 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10421 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10422 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10423 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10424 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10425 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10426 106 : proj_destroy(sourceCRS);
10427 106 : if (!transf)
10428 : {
10429 0 : proj_destroy(targetCRS);
10430 0 : return OGRERR_FAILURE;
10431 : }
10432 :
10433 106 : auto newBoundCRS = proj_crs_create_bound_crs(
10434 106 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10435 106 : proj_destroy(transf);
10436 106 : proj_destroy(targetCRS);
10437 106 : if (!newBoundCRS)
10438 : {
10439 0 : return OGRERR_FAILURE;
10440 : }
10441 :
10442 106 : d->setPjCRS(newBoundCRS);
10443 106 : return OGRERR_NONE;
10444 : }
10445 :
10446 : /************************************************************************/
10447 : /* OSRSetTOWGS84() */
10448 : /************************************************************************/
10449 :
10450 : /**
10451 : * \brief Set the Bursa-Wolf conversion to WGS84.
10452 : *
10453 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10454 : */
10455 10 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10456 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10457 : double dfPPM)
10458 :
10459 : {
10460 10 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10461 :
10462 10 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10463 10 : dfPPM);
10464 : }
10465 :
10466 : /************************************************************************/
10467 : /* GetTOWGS84() */
10468 : /************************************************************************/
10469 :
10470 : /**
10471 : * \brief Fetch TOWGS84 parameters, if available.
10472 : *
10473 : * The parameters have the same meaning as EPSG transformation 9606
10474 : * (Position Vector 7-param. transformation).
10475 : *
10476 : * @param padfCoeff array into which up to 7 coefficients are placed.
10477 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10478 : *
10479 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10480 : * TOWGS84 node available.
10481 : */
10482 :
10483 4213 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10484 :
10485 : {
10486 8426 : TAKE_OPTIONAL_LOCK();
10487 :
10488 4213 : d->refreshProjObj();
10489 4213 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10490 4163 : return OGRERR_FAILURE;
10491 :
10492 50 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10493 :
10494 50 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10495 50 : int success = proj_coordoperation_get_towgs84_values(
10496 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10497 50 : proj_destroy(transf);
10498 :
10499 50 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10500 : }
10501 :
10502 : /************************************************************************/
10503 : /* OSRGetTOWGS84() */
10504 : /************************************************************************/
10505 :
10506 : /**
10507 : * \brief Fetch TOWGS84 parameters, if available.
10508 : *
10509 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10510 : */
10511 11 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10512 : int nCoeffCount)
10513 :
10514 : {
10515 11 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10516 :
10517 11 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10518 : }
10519 :
10520 : /************************************************************************/
10521 : /* IsAngularParameter() */
10522 : /************************************************************************/
10523 :
10524 : /** Is the passed projection parameter an angular one?
10525 : *
10526 : * @return TRUE or FALSE
10527 : */
10528 :
10529 : /* static */
10530 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10531 :
10532 : {
10533 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10534 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10535 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10536 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10537 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10538 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10539 8 : return TRUE;
10540 :
10541 2 : return FALSE;
10542 : }
10543 :
10544 : /************************************************************************/
10545 : /* IsLongitudeParameter() */
10546 : /************************************************************************/
10547 :
10548 : /** Is the passed projection parameter an angular longitude
10549 : * (relative to a prime meridian)?
10550 : *
10551 : * @return TRUE or FALSE
10552 : */
10553 :
10554 : /* static */
10555 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10556 :
10557 : {
10558 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10559 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10560 0 : return TRUE;
10561 :
10562 0 : return FALSE;
10563 : }
10564 :
10565 : /************************************************************************/
10566 : /* IsLinearParameter() */
10567 : /************************************************************************/
10568 :
10569 : /** Is the passed projection parameter an linear one measured in meters or
10570 : * some similar linear measure.
10571 : *
10572 : * @return TRUE or FALSE
10573 : */
10574 :
10575 : /* static */
10576 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10577 :
10578 : {
10579 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10580 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10581 9 : return TRUE;
10582 :
10583 34 : return FALSE;
10584 : }
10585 :
10586 : /************************************************************************/
10587 : /* GetNormInfo() */
10588 : /************************************************************************/
10589 :
10590 : /**
10591 : * \brief Set the internal information for normalizing linear, and angular
10592 : * values.
10593 : */
10594 3551 : void OGRSpatialReference::GetNormInfo() const
10595 :
10596 : {
10597 3551 : TAKE_OPTIONAL_LOCK();
10598 :
10599 3551 : if (d->bNormInfoSet)
10600 2494 : return;
10601 :
10602 : /* -------------------------------------------------------------------- */
10603 : /* Initialize values. */
10604 : /* -------------------------------------------------------------------- */
10605 1057 : d->bNormInfoSet = TRUE;
10606 :
10607 1057 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10608 1057 : d->dfToMeter = GetLinearUnits(nullptr);
10609 1057 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10610 1057 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10611 1054 : d->dfToDegrees = 1.0;
10612 : }
10613 :
10614 : /************************************************************************/
10615 : /* GetExtension() */
10616 : /************************************************************************/
10617 :
10618 : /**
10619 : * \brief Fetch extension value.
10620 : *
10621 : * Fetch the value of the named EXTENSION item for the identified
10622 : * target node.
10623 : *
10624 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10625 : * @param pszName the name of the extension being fetched.
10626 : * @param pszDefault the value to return if the extension is not found.
10627 : *
10628 : * @return node value if successful or pszDefault on failure.
10629 : */
10630 :
10631 8967 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10632 : const char *pszName,
10633 : const char *pszDefault) const
10634 :
10635 : {
10636 17934 : TAKE_OPTIONAL_LOCK();
10637 :
10638 : /* -------------------------------------------------------------------- */
10639 : /* Find the target node. */
10640 : /* -------------------------------------------------------------------- */
10641 : const OGR_SRSNode *poNode =
10642 8967 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10643 :
10644 8967 : if (poNode == nullptr)
10645 2170 : return nullptr;
10646 :
10647 : /* -------------------------------------------------------------------- */
10648 : /* Fetch matching EXTENSION if there is one. */
10649 : /* -------------------------------------------------------------------- */
10650 50265 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10651 : {
10652 43490 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10653 :
10654 43514 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10655 24 : poChild->GetChildCount() >= 2)
10656 : {
10657 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10658 22 : return poChild->GetChild(1)->GetValue();
10659 : }
10660 : }
10661 :
10662 6775 : return pszDefault;
10663 : }
10664 :
10665 : /************************************************************************/
10666 : /* SetExtension() */
10667 : /************************************************************************/
10668 : /**
10669 : * \brief Set extension value.
10670 : *
10671 : * Set the value of the named EXTENSION item for the identified
10672 : * target node.
10673 : *
10674 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10675 : * @param pszName the name of the extension being fetched.
10676 : * @param pszValue the value to set
10677 : *
10678 : * @return OGRERR_NONE on success
10679 : */
10680 :
10681 18 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10682 : const char *pszName,
10683 : const char *pszValue)
10684 :
10685 : {
10686 36 : TAKE_OPTIONAL_LOCK();
10687 :
10688 : /* -------------------------------------------------------------------- */
10689 : /* Find the target node. */
10690 : /* -------------------------------------------------------------------- */
10691 18 : OGR_SRSNode *poNode = nullptr;
10692 :
10693 18 : if (pszTargetKey == nullptr)
10694 0 : poNode = GetRoot();
10695 : else
10696 18 : poNode = GetAttrNode(pszTargetKey);
10697 :
10698 18 : if (poNode == nullptr)
10699 0 : return OGRERR_FAILURE;
10700 :
10701 : /* -------------------------------------------------------------------- */
10702 : /* Fetch matching EXTENSION if there is one. */
10703 : /* -------------------------------------------------------------------- */
10704 139 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10705 : {
10706 126 : OGR_SRSNode *poChild = poNode->GetChild(i);
10707 :
10708 131 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10709 5 : poChild->GetChildCount() >= 2)
10710 : {
10711 5 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10712 : {
10713 5 : poChild->GetChild(1)->SetValue(pszValue);
10714 5 : return OGRERR_NONE;
10715 : }
10716 : }
10717 : }
10718 :
10719 : /* -------------------------------------------------------------------- */
10720 : /* Create a new EXTENSION node. */
10721 : /* -------------------------------------------------------------------- */
10722 13 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10723 13 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10724 13 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10725 :
10726 13 : poNode->AddChild(poAuthNode);
10727 :
10728 13 : return OGRERR_NONE;
10729 : }
10730 :
10731 : /************************************************************************/
10732 : /* OSRCleanup() */
10733 : /************************************************************************/
10734 :
10735 : static void CleanupSRSWGS84Mutex();
10736 :
10737 : /**
10738 : * \brief Cleanup cached SRS related memory.
10739 : *
10740 : * This function will attempt to cleanup any cache spatial reference
10741 : * related information, such as cached tables of coordinate systems.
10742 : *
10743 : * This function should not be called concurrently with any other GDAL/OGR
10744 : * function. It is meant at being called once before process termination
10745 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10746 : * thread-specific resources before thread termination.
10747 : */
10748 938 : void OSRCleanup(void)
10749 :
10750 : {
10751 938 : OGRCTDumpStatistics();
10752 938 : CSVDeaccess(nullptr);
10753 938 : CleanupSRSWGS84Mutex();
10754 938 : OSRCTCleanCache();
10755 938 : OSRCleanupTLSContext();
10756 938 : }
10757 :
10758 : /************************************************************************/
10759 : /* GetAxesCount() */
10760 : /************************************************************************/
10761 :
10762 : /**
10763 : * \brief Return the number of axis of the coordinate system of the CRS.
10764 : *
10765 : * @since GDAL 3.0
10766 : */
10767 34097 : int OGRSpatialReference::GetAxesCount() const
10768 : {
10769 68194 : TAKE_OPTIONAL_LOCK();
10770 :
10771 34096 : int axisCount = 0;
10772 34096 : d->refreshProjObj();
10773 34097 : if (d->m_pj_crs == nullptr)
10774 : {
10775 0 : return 0;
10776 : }
10777 34097 : d->demoteFromBoundCRS();
10778 34097 : auto ctxt = d->getPROJContext();
10779 34097 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10780 : {
10781 29 : for (int i = 0;; i++)
10782 : {
10783 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10784 87 : if (!subCRS)
10785 29 : break;
10786 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10787 : {
10788 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10789 17 : if (baseCRS)
10790 : {
10791 17 : proj_destroy(subCRS);
10792 17 : subCRS = baseCRS;
10793 : }
10794 : }
10795 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10796 58 : if (cs)
10797 : {
10798 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10799 58 : proj_destroy(cs);
10800 : }
10801 58 : proj_destroy(subCRS);
10802 58 : }
10803 : }
10804 : else
10805 : {
10806 34068 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10807 34068 : if (cs)
10808 : {
10809 34068 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10810 34068 : proj_destroy(cs);
10811 : }
10812 : }
10813 34097 : d->undoDemoteFromBoundCRS();
10814 34097 : return axisCount;
10815 : }
10816 :
10817 : /************************************************************************/
10818 : /* OSRGetAxesCount() */
10819 : /************************************************************************/
10820 :
10821 : /**
10822 : * \brief Return the number of axis of the coordinate system of the CRS.
10823 : *
10824 : * This method is the equivalent of the C++ method
10825 : * OGRSpatialReference::GetAxesCount()
10826 : *
10827 : * @since GDAL 3.1
10828 : */
10829 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10830 :
10831 : {
10832 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10833 :
10834 6 : return ToPointer(hSRS)->GetAxesCount();
10835 : }
10836 :
10837 : /************************************************************************/
10838 : /* GetAxis() */
10839 : /************************************************************************/
10840 :
10841 : /**
10842 : * \brief Fetch the orientation of one axis.
10843 : *
10844 : * Fetches the request axis (iAxis - zero based) from the
10845 : * indicated portion of the coordinate system (pszTargetKey) which
10846 : * should be either "GEOGCS" or "PROJCS".
10847 : *
10848 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10849 : *
10850 : * This method is equivalent to the C function OSRGetAxis().
10851 : *
10852 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10853 : * "GEOGCS").
10854 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10855 : * @param peOrientation location into which to place the fetch orientation, may
10856 : * be NULL.
10857 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10858 : * factor. May be NULL. Only set if pszTargetKey == NULL
10859 : *
10860 : * @return the name of the axis or NULL on failure.
10861 : */
10862 :
10863 5361 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10864 : OGRAxisOrientation *peOrientation,
10865 : double *pdfConvUnit) const
10866 :
10867 : {
10868 10722 : TAKE_OPTIONAL_LOCK();
10869 :
10870 5361 : if (peOrientation != nullptr)
10871 5269 : *peOrientation = OAO_Other;
10872 5361 : if (pdfConvUnit != nullptr)
10873 85 : *pdfConvUnit = 0;
10874 :
10875 5361 : d->refreshProjObj();
10876 5361 : if (d->m_pj_crs == nullptr)
10877 : {
10878 1 : return nullptr;
10879 : }
10880 :
10881 5360 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10882 5360 : if (pszTargetKey == nullptr && iAxis <= 2)
10883 : {
10884 5360 : auto ctxt = d->getPROJContext();
10885 :
10886 5360 : int iAxisModified = iAxis;
10887 :
10888 5360 : d->demoteFromBoundCRS();
10889 :
10890 5360 : PJ *cs = nullptr;
10891 5360 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10892 : {
10893 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10894 134 : if (horizCRS)
10895 : {
10896 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10897 : {
10898 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10899 6 : if (baseCRS)
10900 : {
10901 6 : proj_destroy(horizCRS);
10902 6 : horizCRS = baseCRS;
10903 : }
10904 : }
10905 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10906 134 : proj_destroy(horizCRS);
10907 134 : if (cs)
10908 : {
10909 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10910 : {
10911 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10912 44 : proj_destroy(cs);
10913 44 : cs = nullptr;
10914 : }
10915 : }
10916 : }
10917 :
10918 134 : if (cs == nullptr)
10919 : {
10920 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10921 44 : if (vertCRS)
10922 : {
10923 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10924 : {
10925 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10926 30 : if (baseCRS)
10927 : {
10928 30 : proj_destroy(vertCRS);
10929 30 : vertCRS = baseCRS;
10930 : }
10931 : }
10932 :
10933 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10934 44 : proj_destroy(vertCRS);
10935 : }
10936 : }
10937 : }
10938 : else
10939 : {
10940 5226 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10941 : }
10942 :
10943 5360 : if (cs)
10944 : {
10945 5360 : const char *pszName = nullptr;
10946 5360 : const char *pszOrientation = nullptr;
10947 5360 : double dfConvFactor = 0.0;
10948 5360 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10949 : &pszOrientation, &dfConvFactor, nullptr,
10950 : nullptr, nullptr);
10951 :
10952 5360 : if (pdfConvUnit != nullptr)
10953 : {
10954 85 : *pdfConvUnit = dfConvFactor;
10955 : }
10956 :
10957 5360 : if (pszName && pszOrientation)
10958 : {
10959 5360 : d->m_osAxisName[iAxis] = pszName;
10960 5360 : if (peOrientation)
10961 : {
10962 5268 : if (EQUAL(pszOrientation, "NORTH"))
10963 3327 : *peOrientation = OAO_North;
10964 1941 : else if (EQUAL(pszOrientation, "EAST"))
10965 1899 : *peOrientation = OAO_East;
10966 42 : else if (EQUAL(pszOrientation, "SOUTH"))
10967 31 : *peOrientation = OAO_South;
10968 11 : else if (EQUAL(pszOrientation, "WEST"))
10969 0 : *peOrientation = OAO_West;
10970 11 : else if (EQUAL(pszOrientation, "UP"))
10971 1 : *peOrientation = OAO_Up;
10972 10 : else if (EQUAL(pszOrientation, "DOWN"))
10973 0 : *peOrientation = OAO_Down;
10974 : }
10975 5360 : proj_destroy(cs);
10976 5360 : d->undoDemoteFromBoundCRS();
10977 5360 : return d->m_osAxisName[iAxis].c_str();
10978 : }
10979 0 : proj_destroy(cs);
10980 : }
10981 0 : d->undoDemoteFromBoundCRS();
10982 : }
10983 :
10984 : /* -------------------------------------------------------------------- */
10985 : /* Find the target node. */
10986 : /* -------------------------------------------------------------------- */
10987 0 : const OGR_SRSNode *poNode = nullptr;
10988 :
10989 0 : if (pszTargetKey == nullptr)
10990 0 : poNode = GetRoot();
10991 : else
10992 0 : poNode = GetAttrNode(pszTargetKey);
10993 :
10994 0 : if (poNode == nullptr)
10995 0 : return nullptr;
10996 :
10997 : /* -------------------------------------------------------------------- */
10998 : /* Find desired child AXIS. */
10999 : /* -------------------------------------------------------------------- */
11000 0 : const OGR_SRSNode *poAxis = nullptr;
11001 0 : const int nChildCount = poNode->GetChildCount();
11002 :
11003 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11004 : {
11005 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11006 :
11007 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11008 0 : continue;
11009 :
11010 0 : if (iAxis == 0)
11011 : {
11012 0 : poAxis = poChild;
11013 0 : break;
11014 : }
11015 0 : iAxis--;
11016 : }
11017 :
11018 0 : if (poAxis == nullptr)
11019 0 : return nullptr;
11020 :
11021 0 : if (poAxis->GetChildCount() < 2)
11022 0 : return nullptr;
11023 :
11024 : /* -------------------------------------------------------------------- */
11025 : /* Extract name and orientation if possible. */
11026 : /* -------------------------------------------------------------------- */
11027 0 : if (peOrientation != nullptr)
11028 : {
11029 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11030 :
11031 0 : if (EQUAL(pszOrientation, "NORTH"))
11032 0 : *peOrientation = OAO_North;
11033 0 : else if (EQUAL(pszOrientation, "EAST"))
11034 0 : *peOrientation = OAO_East;
11035 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11036 0 : *peOrientation = OAO_South;
11037 0 : else if (EQUAL(pszOrientation, "WEST"))
11038 0 : *peOrientation = OAO_West;
11039 0 : else if (EQUAL(pszOrientation, "UP"))
11040 0 : *peOrientation = OAO_Up;
11041 0 : else if (EQUAL(pszOrientation, "DOWN"))
11042 0 : *peOrientation = OAO_Down;
11043 0 : else if (EQUAL(pszOrientation, "OTHER"))
11044 0 : *peOrientation = OAO_Other;
11045 : else
11046 : {
11047 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11048 : pszOrientation);
11049 : }
11050 : }
11051 :
11052 0 : return poAxis->GetChild(0)->GetValue();
11053 : }
11054 :
11055 : /************************************************************************/
11056 : /* OSRGetAxis() */
11057 : /************************************************************************/
11058 :
11059 : /**
11060 : * \brief Fetch the orientation of one axis.
11061 : *
11062 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11063 : */
11064 11 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11065 : int iAxis, OGRAxisOrientation *peOrientation)
11066 :
11067 : {
11068 11 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11069 :
11070 11 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11071 : }
11072 :
11073 : /************************************************************************/
11074 : /* OSRAxisEnumToName() */
11075 : /************************************************************************/
11076 :
11077 : /**
11078 : * \brief Return the string representation for the OGRAxisOrientation
11079 : * enumeration.
11080 : *
11081 : * For example "NORTH" for OAO_North.
11082 : *
11083 : * @return an internal string
11084 : */
11085 312 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11086 :
11087 : {
11088 312 : if (eOrientation == OAO_North)
11089 156 : return "NORTH";
11090 156 : if (eOrientation == OAO_East)
11091 156 : return "EAST";
11092 0 : if (eOrientation == OAO_South)
11093 0 : return "SOUTH";
11094 0 : if (eOrientation == OAO_West)
11095 0 : return "WEST";
11096 0 : if (eOrientation == OAO_Up)
11097 0 : return "UP";
11098 0 : if (eOrientation == OAO_Down)
11099 0 : return "DOWN";
11100 0 : if (eOrientation == OAO_Other)
11101 0 : return "OTHER";
11102 :
11103 0 : return "UNKNOWN";
11104 : }
11105 :
11106 : /************************************************************************/
11107 : /* SetAxes() */
11108 : /************************************************************************/
11109 :
11110 : /**
11111 : * \brief Set the axes for a coordinate system.
11112 : *
11113 : * Set the names, and orientations of the axes for either a projected
11114 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11115 : *
11116 : * This method is equivalent to the C function OSRSetAxes().
11117 : *
11118 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11119 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11120 : * @param eXAxisOrientation normally OAO_East.
11121 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11122 : * @param eYAxisOrientation normally OAO_North.
11123 : *
11124 : * @return OGRERR_NONE on success or an error code.
11125 : */
11126 :
11127 156 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11128 : const char *pszXAxisName,
11129 : OGRAxisOrientation eXAxisOrientation,
11130 : const char *pszYAxisName,
11131 : OGRAxisOrientation eYAxisOrientation)
11132 :
11133 : {
11134 312 : TAKE_OPTIONAL_LOCK();
11135 :
11136 : /* -------------------------------------------------------------------- */
11137 : /* Find the target node. */
11138 : /* -------------------------------------------------------------------- */
11139 156 : OGR_SRSNode *poNode = nullptr;
11140 :
11141 156 : if (pszTargetKey == nullptr)
11142 156 : poNode = GetRoot();
11143 : else
11144 0 : poNode = GetAttrNode(pszTargetKey);
11145 :
11146 156 : if (poNode == nullptr)
11147 0 : return OGRERR_FAILURE;
11148 :
11149 : /* -------------------------------------------------------------------- */
11150 : /* Strip any existing AXIS children. */
11151 : /* -------------------------------------------------------------------- */
11152 468 : while (poNode->FindChild("AXIS") >= 0)
11153 312 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11154 :
11155 : /* -------------------------------------------------------------------- */
11156 : /* Insert desired axes */
11157 : /* -------------------------------------------------------------------- */
11158 156 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11159 :
11160 156 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11161 156 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11162 :
11163 156 : poNode->AddChild(poAxis);
11164 :
11165 156 : poAxis = new OGR_SRSNode("AXIS");
11166 :
11167 156 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11168 156 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11169 :
11170 156 : poNode->AddChild(poAxis);
11171 :
11172 156 : return OGRERR_NONE;
11173 : }
11174 :
11175 : /************************************************************************/
11176 : /* OSRSetAxes() */
11177 : /************************************************************************/
11178 : /**
11179 : * \brief Set the axes for a coordinate system.
11180 : *
11181 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11182 : */
11183 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11184 : const char *pszXAxisName,
11185 : OGRAxisOrientation eXAxisOrientation,
11186 : const char *pszYAxisName,
11187 : OGRAxisOrientation eYAxisOrientation)
11188 : {
11189 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11190 :
11191 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11192 : eXAxisOrientation, pszYAxisName,
11193 0 : eYAxisOrientation);
11194 : }
11195 :
11196 : /************************************************************************/
11197 : /* OSRExportToMICoordSys() */
11198 : /************************************************************************/
11199 : /**
11200 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11201 : *
11202 : * This method is the equivalent of the C++ method
11203 : * OGRSpatialReference::exportToMICoordSys
11204 : */
11205 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11206 :
11207 : {
11208 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11209 :
11210 5 : *ppszReturn = nullptr;
11211 :
11212 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11213 : }
11214 :
11215 : /************************************************************************/
11216 : /* exportToMICoordSys() */
11217 : /************************************************************************/
11218 :
11219 : /**
11220 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11221 : *
11222 : * Note that the returned WKT string should be freed with
11223 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11224 : *
11225 : * This method is the same as the C function OSRExportToMICoordSys().
11226 : *
11227 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11228 : * definition will be assigned.
11229 : *
11230 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11231 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11232 : */
11233 :
11234 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11235 :
11236 : {
11237 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11238 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11239 7 : return OGRERR_NONE;
11240 :
11241 0 : return OGRERR_FAILURE;
11242 : }
11243 :
11244 : /************************************************************************/
11245 : /* OSRImportFromMICoordSys() */
11246 : /************************************************************************/
11247 : /**
11248 : * \brief Import Mapinfo style CoordSys definition.
11249 : *
11250 : * This method is the equivalent of the C++ method
11251 : * OGRSpatialReference::importFromMICoordSys
11252 : */
11253 :
11254 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11255 : const char *pszCoordSys)
11256 :
11257 : {
11258 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11259 :
11260 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11261 : }
11262 :
11263 : /************************************************************************/
11264 : /* importFromMICoordSys() */
11265 : /************************************************************************/
11266 :
11267 : /**
11268 : * \brief Import Mapinfo style CoordSys definition.
11269 : *
11270 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11271 : * definition string.
11272 : *
11273 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11274 : *
11275 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11276 : *
11277 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11278 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11279 : */
11280 :
11281 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11282 :
11283 : {
11284 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11285 :
11286 17 : if (poResult == nullptr)
11287 0 : return OGRERR_FAILURE;
11288 :
11289 17 : *this = *poResult;
11290 17 : delete poResult;
11291 :
11292 17 : return OGRERR_NONE;
11293 : }
11294 :
11295 : /************************************************************************/
11296 : /* OSRCalcInvFlattening() */
11297 : /************************************************************************/
11298 :
11299 : /**
11300 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11301 : *
11302 : * @param dfSemiMajor Semi-major axis length.
11303 : * @param dfSemiMinor Semi-minor axis length.
11304 : *
11305 : * @return inverse flattening, or 0 if both axis are equal.
11306 : * @since GDAL 2.0
11307 : */
11308 :
11309 7242 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11310 : {
11311 7242 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11312 27 : return 0;
11313 7215 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11314 : {
11315 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11316 : "OSRCalcInvFlattening(): Wrong input values");
11317 0 : return 0;
11318 : }
11319 :
11320 7215 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11321 : }
11322 :
11323 : /************************************************************************/
11324 : /* OSRCalcInvFlattening() */
11325 : /************************************************************************/
11326 :
11327 : /**
11328 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11329 : *
11330 : * @param dfSemiMajor Semi-major axis length.
11331 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11332 : *
11333 : * @return semi-minor axis
11334 : * @since GDAL 2.0
11335 : */
11336 :
11337 635 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11338 : double dfInvFlattening)
11339 : {
11340 635 : if (fabs(dfInvFlattening) < 0.000000000001)
11341 101 : return dfSemiMajor;
11342 534 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11343 : {
11344 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11345 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11346 0 : return dfSemiMajor;
11347 : }
11348 :
11349 534 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11350 : }
11351 :
11352 : /************************************************************************/
11353 : /* GetWGS84SRS() */
11354 : /************************************************************************/
11355 :
11356 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11357 : static CPLMutex *hMutex = nullptr;
11358 :
11359 : /**
11360 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11361 : *
11362 : * Note: the instance will have
11363 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11364 : *
11365 : * The reference counter of the returned object is not increased by this
11366 : * operation.
11367 : *
11368 : * @return instance.
11369 : * @since GDAL 2.0
11370 : */
11371 :
11372 821 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11373 : {
11374 821 : CPLMutexHolderD(&hMutex);
11375 821 : if (poSRSWGS84 == nullptr)
11376 : {
11377 4 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11378 4 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11379 : }
11380 1642 : return poSRSWGS84;
11381 : }
11382 :
11383 : /************************************************************************/
11384 : /* CleanupSRSWGS84Mutex() */
11385 : /************************************************************************/
11386 :
11387 938 : static void CleanupSRSWGS84Mutex()
11388 : {
11389 938 : if (hMutex != nullptr)
11390 : {
11391 2 : poSRSWGS84->Release();
11392 2 : poSRSWGS84 = nullptr;
11393 2 : CPLDestroyMutex(hMutex);
11394 2 : hMutex = nullptr;
11395 : }
11396 938 : }
11397 :
11398 : /************************************************************************/
11399 : /* OSRImportFromProj4() */
11400 : /************************************************************************/
11401 : /**
11402 : * \brief Import PROJ coordinate string.
11403 : *
11404 : * This function is the same as OGRSpatialReference::importFromProj4().
11405 : */
11406 182 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11407 :
11408 : {
11409 182 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11410 :
11411 182 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11412 : }
11413 :
11414 : /************************************************************************/
11415 : /* importFromProj4() */
11416 : /************************************************************************/
11417 :
11418 : /**
11419 : * \brief Import PROJ coordinate string.
11420 : *
11421 : * The OGRSpatialReference is initialized from the passed PROJs style
11422 : * coordinate system string.
11423 : *
11424 : * Example:
11425 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11426 : *
11427 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11428 : * a legacy syntax that should be avoided in the future. In particular they will
11429 : * result in CRS objects whose axis order might not correspond to the official
11430 : * EPSG axis order.
11431 : *
11432 : * This method is the equivalent of the C function OSRImportFromProj4().
11433 : *
11434 : * @param pszProj4 the PROJ style string.
11435 : *
11436 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11437 : */
11438 :
11439 611 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11440 :
11441 : {
11442 1222 : TAKE_OPTIONAL_LOCK();
11443 :
11444 611 : if (strlen(pszProj4) >= 10000)
11445 : {
11446 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11447 1 : return OGRERR_CORRUPT_DATA;
11448 : }
11449 :
11450 : /* -------------------------------------------------------------------- */
11451 : /* Clear any existing definition. */
11452 : /* -------------------------------------------------------------------- */
11453 610 : Clear();
11454 :
11455 610 : CPLString osProj4(pszProj4);
11456 610 : if (osProj4.find("type=crs") == std::string::npos)
11457 : {
11458 601 : osProj4 += " +type=crs";
11459 : }
11460 :
11461 612 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11462 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11463 : {
11464 : static bool bHasWarned = false;
11465 2 : if (!bHasWarned)
11466 : {
11467 1 : CPLError(CE_Warning, CPLE_AppDefined,
11468 : "+init=epsg:XXXX syntax is deprecated. It might return "
11469 : "a CRS with a non-EPSG compliant axis order.");
11470 1 : bHasWarned = true;
11471 : }
11472 : }
11473 610 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11474 610 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11475 610 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11476 610 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11477 : }
11478 :
11479 : /************************************************************************/
11480 : /* OSRExportToProj4() */
11481 : /************************************************************************/
11482 : /**
11483 : * \brief Export coordinate system in PROJ.4 legacy format.
11484 : *
11485 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11486 : * PROJ >= 6 is significantly different from earlier versions. In particular
11487 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11488 : * will be missing most of the time. PROJ strings to encode CRS should be
11489 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11490 : * is the recommended way.
11491 : *
11492 : * This function is the same as OGRSpatialReference::exportToProj4().
11493 : */
11494 455 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11495 : char **ppszReturn)
11496 :
11497 : {
11498 455 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11499 :
11500 455 : *ppszReturn = nullptr;
11501 :
11502 455 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11503 : }
11504 :
11505 : /************************************************************************/
11506 : /* exportToProj4() */
11507 : /************************************************************************/
11508 :
11509 : /**
11510 : * \brief Export coordinate system in PROJ.4 legacy format.
11511 : *
11512 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11513 : * PROJ >= 6 is significantly different from earlier versions. In particular
11514 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11515 : * will be missing most of the time. PROJ strings to encode CRS should be
11516 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11517 : * representation is the recommended way.
11518 : *
11519 : * Converts the loaded coordinate reference system into PROJ format
11520 : * to the extent possible. The string returned in ppszProj4 should be
11521 : * deallocated by the caller with CPLFree() when no longer needed.
11522 : *
11523 : * LOCAL_CS coordinate systems are not translatable. An empty string
11524 : * will be returned along with OGRERR_NONE.
11525 : *
11526 : * Special processing for Transverse Mercator:
11527 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11528 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11529 : * for the tmerc and utm projection methods, rather than the more accurate
11530 : * method.
11531 : *
11532 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11533 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11534 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11535 : * added. This automatic addition may be disabled by setting the
11536 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11537 : *
11538 : * This method is the equivalent of the C function OSRExportToProj4().
11539 : *
11540 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11541 : * will be assigned.
11542 : *
11543 : * @return OGRERR_NONE on success or an error code on failure.
11544 : */
11545 :
11546 1421 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11547 :
11548 : {
11549 : // In the past calling this method was thread-safe, even if we never
11550 : // guaranteed it. Now proj_as_proj_string() will cache the result
11551 : // internally, so this is no longer thread-safe.
11552 2842 : std::lock_guard oLock(d->m_mutex);
11553 :
11554 1421 : d->refreshProjObj();
11555 1421 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11556 : {
11557 24 : *ppszProj4 = CPLStrdup("");
11558 24 : return OGRERR_FAILURE;
11559 : }
11560 :
11561 : // OSR_USE_ETMERC is here just for legacy
11562 1397 : bool bForceApproxTMerc = false;
11563 1397 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11564 1397 : if (pszUseETMERC && pszUseETMERC[0])
11565 : {
11566 : static bool bHasWarned = false;
11567 0 : if (!bHasWarned)
11568 : {
11569 0 : CPLError(CE_Warning, CPLE_AppDefined,
11570 : "OSR_USE_ETMERC is a legacy configuration option, which "
11571 : "now has only effect when set to NO (YES is the default). "
11572 : "Use OSR_USE_APPROX_TMERC=YES instead");
11573 0 : bHasWarned = true;
11574 : }
11575 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11576 : }
11577 : else
11578 : {
11579 : const char *pszUseApproxTMERC =
11580 1397 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11581 1397 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11582 : {
11583 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11584 : }
11585 : }
11586 1397 : const char *options[] = {
11587 1397 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11588 :
11589 1397 : const char *projString = proj_as_proj_string(
11590 1397 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11591 :
11592 1397 : PJ *boundCRS = nullptr;
11593 2790 : if (projString &&
11594 1393 : (strstr(projString, "+datum=") == nullptr ||
11595 2800 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11596 541 : CPLTestBool(
11597 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11598 : {
11599 541 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11600 541 : d->getPROJContext(), d->m_pj_crs, true,
11601 541 : strstr(projString, "+datum=") == nullptr);
11602 541 : if (boundCRS)
11603 : {
11604 216 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11605 : PJ_PROJ_4, options);
11606 : }
11607 : }
11608 :
11609 1397 : if (projString == nullptr)
11610 : {
11611 4 : *ppszProj4 = CPLStrdup("");
11612 4 : proj_destroy(boundCRS);
11613 4 : return OGRERR_FAILURE;
11614 : }
11615 1393 : *ppszProj4 = CPLStrdup(projString);
11616 1393 : proj_destroy(boundCRS);
11617 1393 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11618 1393 : if (pszTypeCrs)
11619 1393 : *pszTypeCrs = '\0';
11620 1393 : return OGRERR_NONE;
11621 : }
11622 :
11623 : /************************************************************************/
11624 : /* morphToESRI() */
11625 : /************************************************************************/
11626 : /**
11627 : * \brief Convert in place to ESRI WKT format.
11628 : *
11629 : * The value nodes of this coordinate system are modified in various manners
11630 : * more closely map onto the ESRI concept of WKT format. This includes
11631 : * renaming a variety of projections and arguments, and stripping out
11632 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11633 : *
11634 : * \note Since GDAL 3.0, this function has only user-visible effects at
11635 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11636 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11637 : *
11638 : * This does the same as the C function OSRMorphToESRI().
11639 : *
11640 : * @return OGRERR_NONE unless something goes badly wrong.
11641 : * @deprecated
11642 : */
11643 :
11644 272 : OGRErr OGRSpatialReference::morphToESRI()
11645 :
11646 : {
11647 272 : TAKE_OPTIONAL_LOCK();
11648 :
11649 272 : d->refreshProjObj();
11650 272 : d->setMorphToESRI(true);
11651 :
11652 544 : return OGRERR_NONE;
11653 : }
11654 :
11655 : /************************************************************************/
11656 : /* OSRMorphToESRI() */
11657 : /************************************************************************/
11658 :
11659 : /**
11660 : * \brief Convert in place to ESRI WKT format.
11661 : *
11662 : * This function is the same as the C++ method
11663 : * OGRSpatialReference::morphToESRI().
11664 : */
11665 107 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11666 :
11667 : {
11668 107 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11669 :
11670 107 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11671 : }
11672 :
11673 : /************************************************************************/
11674 : /* morphFromESRI() */
11675 : /************************************************************************/
11676 :
11677 : /**
11678 : * \brief Convert in place from ESRI WKT format.
11679 : *
11680 : * The value notes of this coordinate system are modified in various manners
11681 : * to adhere more closely to the WKT standard. This mostly involves
11682 : * translating a variety of ESRI names for projections, arguments and
11683 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11684 : * translation of EPSG to WKT for the CT specification.
11685 : *
11686 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11687 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11688 : * effect is to undo the effect of a potential prior call to morphToESRI().
11689 : *
11690 : * This does the same as the C function OSRMorphFromESRI().
11691 : *
11692 : * @return OGRERR_NONE unless something goes badly wrong.
11693 : * @deprecated
11694 : */
11695 :
11696 27 : OGRErr OGRSpatialReference::morphFromESRI()
11697 :
11698 : {
11699 27 : TAKE_OPTIONAL_LOCK();
11700 :
11701 27 : d->refreshProjObj();
11702 27 : d->setMorphToESRI(false);
11703 :
11704 54 : return OGRERR_NONE;
11705 : }
11706 :
11707 : /************************************************************************/
11708 : /* OSRMorphFromESRI() */
11709 : /************************************************************************/
11710 :
11711 : /**
11712 : * \brief Convert in place from ESRI WKT format.
11713 : *
11714 : * This function is the same as the C++ method
11715 : * OGRSpatialReference::morphFromESRI().
11716 : */
11717 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11718 :
11719 : {
11720 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11721 :
11722 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11723 : }
11724 :
11725 : /************************************************************************/
11726 : /* FindMatches() */
11727 : /************************************************************************/
11728 :
11729 : /**
11730 : * \brief Try to identify a match between the passed SRS and a related SRS
11731 : * in a catalog.
11732 : *
11733 : * Matching may be partial, or may fail.
11734 : * Returned entries will be sorted by decreasing match confidence (first
11735 : * entry has the highest match confidence).
11736 : *
11737 : * The exact way matching is done may change in future versions. Starting with
11738 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11739 : *
11740 : * This method is the same as OSRFindMatches().
11741 : *
11742 : * @param papszOptions NULL terminated list of options or NULL
11743 : * @param pnEntries Output parameter. Number of values in the returned array.
11744 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11745 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11746 : * indicate the confidence in the match. 100 is the highest confidence level.
11747 : * The array must be freed with CPLFree().
11748 : *
11749 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11750 : * with OSRFreeSRSArray()
11751 : *
11752 : * @since GDAL 2.3
11753 : *
11754 : * @see OGRSpatialReference::FindBestMatch()
11755 : */
11756 : OGRSpatialReferenceH *
11757 1236 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11758 : int **ppanMatchConfidence) const
11759 : {
11760 2472 : TAKE_OPTIONAL_LOCK();
11761 :
11762 1236 : CPL_IGNORE_RET_VAL(papszOptions);
11763 :
11764 1236 : if (pnEntries)
11765 1236 : *pnEntries = 0;
11766 1236 : if (ppanMatchConfidence)
11767 1236 : *ppanMatchConfidence = nullptr;
11768 :
11769 1236 : d->refreshProjObj();
11770 1236 : if (!d->m_pj_crs)
11771 0 : return nullptr;
11772 :
11773 1236 : int *panConfidence = nullptr;
11774 1236 : auto ctxt = d->getPROJContext();
11775 : auto list =
11776 1236 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11777 1236 : if (!list)
11778 0 : return nullptr;
11779 :
11780 1236 : const int nMatches = proj_list_get_count(list);
11781 :
11782 1236 : if (pnEntries)
11783 1236 : *pnEntries = static_cast<int>(nMatches);
11784 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11785 1236 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11786 1236 : if (ppanMatchConfidence)
11787 : {
11788 1236 : *ppanMatchConfidence =
11789 1236 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11790 : }
11791 :
11792 1236 : bool bSortAgain = false;
11793 :
11794 4573 : for (int i = 0; i < nMatches; i++)
11795 : {
11796 3337 : PJ *obj = proj_list_get(ctxt, list, i);
11797 3337 : CPLAssert(obj);
11798 3337 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11799 3337 : poSRS->d->setPjCRS(obj);
11800 3337 : pahRet[i] = ToHandle(poSRS);
11801 :
11802 : // Identify matches that only differ by axis order
11803 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11804 3355 : poSRS->GetAxesCount() == 2 &&
11805 3346 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11806 : {
11807 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11808 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11809 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11810 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11811 9 : GetAxis(nullptr, 0, &eThisAxis0);
11812 9 : GetAxis(nullptr, 1, &eThisAxis1);
11813 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11814 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11815 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11816 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11817 : {
11818 : auto pj_crs_normalized =
11819 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11820 9 : if (pj_crs_normalized)
11821 : {
11822 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11823 9 : PJ_COMP_EQUIVALENT))
11824 : {
11825 3 : bSortAgain = true;
11826 3 : panConfidence[i] = 90;
11827 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11828 : }
11829 9 : proj_destroy(pj_crs_normalized);
11830 : }
11831 : }
11832 : }
11833 :
11834 3337 : if (ppanMatchConfidence)
11835 3337 : (*ppanMatchConfidence)[i] = panConfidence[i];
11836 : }
11837 :
11838 1236 : if (bSortAgain)
11839 : {
11840 3 : std::vector<int> anIndices;
11841 12 : for (int i = 0; i < nMatches; ++i)
11842 9 : anIndices.push_back(i);
11843 :
11844 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11845 9 : [&panConfidence](int i, int j)
11846 9 : { return panConfidence[i] > panConfidence[j]; });
11847 :
11848 : OGRSpatialReferenceH *pahRetSorted =
11849 : static_cast<OGRSpatialReferenceH *>(
11850 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11851 12 : for (int i = 0; i < nMatches; ++i)
11852 : {
11853 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11854 9 : if (ppanMatchConfidence)
11855 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11856 : }
11857 3 : CPLFree(pahRet);
11858 3 : pahRet = pahRetSorted;
11859 : }
11860 :
11861 1236 : pahRet[nMatches] = nullptr;
11862 1236 : proj_list_destroy(list);
11863 1236 : proj_int_list_destroy(panConfidence);
11864 :
11865 1236 : return pahRet;
11866 : }
11867 :
11868 : /************************************************************************/
11869 : /* importFromEPSGA() */
11870 : /************************************************************************/
11871 :
11872 : /**
11873 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11874 : * code.
11875 : *
11876 : * This method will initialize the spatial reference based on the
11877 : * passed in EPSG CRS code found in the PROJ database.
11878 : *
11879 : * Since GDAL 3.0, this method is identical to importFromEPSG().
11880 : *
11881 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11882 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11883 : * such method available for the CRS. This behavior might not always be
11884 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11885 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11886 : * The AddGuessedTOWGS84() method can also be used for that purpose.
11887 : *
11888 : * The method will also by default substitute a deprecated EPSG code by its
11889 : * non-deprecated replacement. If this behavior is not desired, the
11890 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11891 : *
11892 : * This method is the same as the C function OSRImportFromEPSGA().
11893 : *
11894 : * @param nCode a CRS code.
11895 : *
11896 : * @return OGRERR_NONE on success, or an error code on failure.
11897 : */
11898 :
11899 34380 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11900 :
11901 : {
11902 68759 : TAKE_OPTIONAL_LOCK();
11903 :
11904 34379 : Clear();
11905 :
11906 : const char *pszUseNonDeprecated =
11907 34380 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11908 : const bool bUseNonDeprecated =
11909 34380 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11910 34380 : const bool bAddTOWGS84 = CPLTestBool(
11911 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11912 34380 : auto tlsCache = OSRGetProjTLSCache();
11913 34380 : if (tlsCache)
11914 : {
11915 : auto cachedObj =
11916 34380 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11917 34380 : if (cachedObj)
11918 : {
11919 26441 : d->setPjCRS(cachedObj);
11920 26442 : return OGRERR_NONE;
11921 : }
11922 : }
11923 :
11924 15876 : CPLString osCode;
11925 7938 : osCode.Printf("%d", nCode);
11926 : auto obj =
11927 7937 : proj_create_from_database(d->getPROJContext(), "EPSG", osCode.c_str(),
11928 : PJ_CATEGORY_CRS, true, nullptr);
11929 7937 : if (!obj)
11930 : {
11931 21 : return OGRERR_FAILURE;
11932 : }
11933 :
11934 7916 : if (bUseNonDeprecated && proj_is_deprecated(obj))
11935 : {
11936 411 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11937 411 : if (list)
11938 : {
11939 411 : const auto count = proj_list_get_count(list);
11940 411 : if (count == 1)
11941 : {
11942 : auto nonDeprecated =
11943 360 : proj_list_get(d->getPROJContext(), list, 0);
11944 360 : if (nonDeprecated)
11945 : {
11946 360 : if (pszUseNonDeprecated == nullptr)
11947 : {
11948 : const char *pszNewAuth =
11949 360 : proj_get_id_auth_name(nonDeprecated, 0);
11950 : const char *pszNewCode =
11951 360 : proj_get_id_code(nonDeprecated, 0);
11952 360 : CPLError(CE_Warning, CPLE_AppDefined,
11953 : "CRS EPSG:%d is deprecated. "
11954 : "Its non-deprecated replacement %s:%s "
11955 : "will be used instead. "
11956 : "To use the original CRS, set the "
11957 : "OSR_USE_NON_DEPRECATED "
11958 : "configuration option to NO.",
11959 : nCode, pszNewAuth ? pszNewAuth : "(null)",
11960 : pszNewCode ? pszNewCode : "(null)");
11961 : }
11962 360 : proj_destroy(obj);
11963 360 : obj = nonDeprecated;
11964 : }
11965 : }
11966 : }
11967 411 : proj_list_destroy(list);
11968 : }
11969 :
11970 7915 : if (bAddTOWGS84)
11971 : {
11972 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
11973 : obj, nullptr);
11974 1 : if (boundCRS)
11975 : {
11976 1 : proj_destroy(obj);
11977 1 : obj = boundCRS;
11978 : }
11979 : }
11980 :
11981 7915 : d->setPjCRS(obj);
11982 :
11983 7917 : if (tlsCache)
11984 : {
11985 7917 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
11986 : obj);
11987 : }
11988 :
11989 7916 : return OGRERR_NONE;
11990 : }
11991 :
11992 : /************************************************************************/
11993 : /* AddGuessedTOWGS84() */
11994 : /************************************************************************/
11995 :
11996 : /**
11997 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
11998 : * to WGS84.
11999 : *
12000 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12001 : * to WGS84 when there is one and only one such method available for the CRS.
12002 : * Note: this is more restrictive to how GDAL < 3 worked.
12003 : *
12004 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12005 : *
12006 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12007 : * already a transformation to WGS84 or none matching could be found).
12008 : *
12009 : * @since GDAL 3.0.3
12010 : */
12011 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12012 : {
12013 36 : TAKE_OPTIONAL_LOCK();
12014 :
12015 18 : d->refreshProjObj();
12016 18 : if (!d->m_pj_crs)
12017 0 : return OGRERR_FAILURE;
12018 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12019 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12020 18 : if (!boundCRS)
12021 : {
12022 0 : return OGRERR_FAILURE;
12023 : }
12024 18 : d->setPjCRS(boundCRS);
12025 18 : return OGRERR_NONE;
12026 : }
12027 :
12028 : /************************************************************************/
12029 : /* OSRImportFromEPSGA() */
12030 : /************************************************************************/
12031 :
12032 : /**
12033 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12034 : * to WGS84.
12035 : *
12036 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12037 : *
12038 : * @since GDAL 3.0.3
12039 : */
12040 :
12041 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12042 :
12043 : {
12044 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12045 :
12046 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12047 : }
12048 :
12049 : /************************************************************************/
12050 : /* OSRImportFromEPSGA() */
12051 : /************************************************************************/
12052 :
12053 : /**
12054 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12055 : * code.
12056 : *
12057 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12058 : */
12059 :
12060 2 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12061 :
12062 : {
12063 2 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12064 :
12065 2 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12066 : }
12067 :
12068 : /************************************************************************/
12069 : /* importFromEPSG() */
12070 : /************************************************************************/
12071 :
12072 : /**
12073 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12074 : * code.
12075 : *
12076 : * This method will initialize the spatial reference based on the
12077 : * passed in EPSG CRS code found in the PROJ database.
12078 : *
12079 : * This method is the same as the C function OSRImportFromEPSG().
12080 : *
12081 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12082 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12083 : * such method available for the CRS. This behavior might not always be
12084 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12085 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12086 : *
12087 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12088 : *
12089 : * @return OGRERR_NONE on success, or an error code on failure.
12090 : */
12091 :
12092 32904 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12093 :
12094 : {
12095 32904 : return importFromEPSGA(nCode);
12096 : }
12097 :
12098 : /************************************************************************/
12099 : /* OSRImportFromEPSG() */
12100 : /************************************************************************/
12101 :
12102 : /**
12103 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12104 : * code.
12105 : *
12106 : * This function is the same as OGRSpatialReference::importFromEPSG().
12107 : */
12108 :
12109 1406 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12110 :
12111 : {
12112 1406 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12113 :
12114 1406 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12115 : }
12116 :
12117 : /************************************************************************/
12118 : /* EPSGTreatsAsLatLong() */
12119 : /************************************************************************/
12120 :
12121 : /**
12122 : * \brief This method returns TRUE if this geographic coordinate
12123 : * system should be treated as having lat/long coordinate ordering.
12124 : *
12125 : * Currently this returns TRUE for all geographic coordinate systems
12126 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12127 : * also checked that the CRS had belonged to EPSG authority, but this check
12128 : * has now been removed).
12129 : *
12130 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12131 : * geographic CRS imported with importFromEPSG() would cause this method to
12132 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12133 : * is now equivalent to importFromEPSGA().
12134 : *
12135 : * FALSE will be returned for all coordinate systems that are not geographic,
12136 : * or whose axes ordering is not latitude, longitude.
12137 : *
12138 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12139 : *
12140 : * @return TRUE or FALSE.
12141 : */
12142 :
12143 708 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12144 :
12145 : {
12146 1416 : TAKE_OPTIONAL_LOCK();
12147 :
12148 708 : if (!IsGeographic())
12149 565 : return FALSE;
12150 :
12151 143 : d->demoteFromBoundCRS();
12152 :
12153 143 : bool ret = false;
12154 143 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12155 : {
12156 : auto horizCRS =
12157 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12158 3 : if (horizCRS)
12159 : {
12160 : auto cs =
12161 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12162 3 : if (cs)
12163 : {
12164 3 : const char *pszDirection = nullptr;
12165 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12166 : nullptr, &pszDirection, nullptr,
12167 3 : nullptr, nullptr, nullptr))
12168 : {
12169 3 : if (EQUAL(pszDirection, "north"))
12170 : {
12171 3 : ret = true;
12172 : }
12173 : }
12174 :
12175 3 : proj_destroy(cs);
12176 : }
12177 :
12178 3 : proj_destroy(horizCRS);
12179 : }
12180 : }
12181 : else
12182 : {
12183 : auto cs =
12184 140 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12185 140 : if (cs)
12186 : {
12187 140 : const char *pszDirection = nullptr;
12188 140 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12189 : nullptr, &pszDirection, nullptr, nullptr,
12190 140 : nullptr, nullptr))
12191 : {
12192 140 : if (EQUAL(pszDirection, "north"))
12193 : {
12194 113 : ret = true;
12195 : }
12196 : }
12197 :
12198 140 : proj_destroy(cs);
12199 : }
12200 : }
12201 143 : d->undoDemoteFromBoundCRS();
12202 :
12203 143 : return ret;
12204 : }
12205 :
12206 : /************************************************************************/
12207 : /* OSREPSGTreatsAsLatLong() */
12208 : /************************************************************************/
12209 :
12210 : /**
12211 : * \brief This function returns TRUE if this geographic coordinate
12212 : * system should be treated as having lat/long coordinate ordering.
12213 : *
12214 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12215 : */
12216 :
12217 179 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12218 :
12219 : {
12220 179 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12221 :
12222 179 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12223 : }
12224 :
12225 : /************************************************************************/
12226 : /* EPSGTreatsAsNorthingEasting() */
12227 : /************************************************************************/
12228 :
12229 : /**
12230 : * \brief This method returns TRUE if this projected coordinate
12231 : * system should be treated as having northing/easting coordinate ordering.
12232 : *
12233 : * Currently this returns TRUE for all projected coordinate systems
12234 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12235 : * also checked that the CRS had belonged to EPSG authority, but this check
12236 : * has now been removed).
12237 : *
12238 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12239 : * projected CRS with northing, easting axis order imported with
12240 : * importFromEPSG() would cause this method to
12241 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12242 : * is now equivalent to importFromEPSGA().
12243 : *
12244 : * FALSE will be returned for all coordinate systems that are not projected,
12245 : * or whose axes ordering is not northing, easting.
12246 : *
12247 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12248 : *
12249 : * @return TRUE or FALSE.
12250 : *
12251 : * @since OGR 1.10.0
12252 : */
12253 :
12254 598 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12255 :
12256 : {
12257 1196 : TAKE_OPTIONAL_LOCK();
12258 :
12259 598 : if (!IsProjected())
12260 23 : return FALSE;
12261 :
12262 575 : d->demoteFromBoundCRS();
12263 : PJ *projCRS;
12264 575 : const auto ctxt = d->getPROJContext();
12265 575 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12266 : {
12267 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12268 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12269 : {
12270 0 : d->undoDemoteFromBoundCRS();
12271 0 : proj_destroy(projCRS);
12272 0 : return FALSE;
12273 : }
12274 : }
12275 : else
12276 : {
12277 571 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12278 : }
12279 :
12280 575 : bool ret = false;
12281 575 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12282 575 : proj_destroy(projCRS);
12283 575 : d->undoDemoteFromBoundCRS();
12284 :
12285 575 : if (cs)
12286 : {
12287 575 : ret = isNorthEastAxisOrder(ctxt, cs);
12288 575 : proj_destroy(cs);
12289 : }
12290 :
12291 575 : return ret;
12292 : }
12293 :
12294 : /************************************************************************/
12295 : /* OSREPSGTreatsAsNorthingEasting() */
12296 : /************************************************************************/
12297 :
12298 : /**
12299 : * \brief This function returns TRUE if this projected coordinate
12300 : * system should be treated as having northing/easting coordinate ordering.
12301 : *
12302 : * This function is the same as
12303 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12304 : *
12305 : * @since OGR 1.10.0
12306 : */
12307 :
12308 186 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12309 :
12310 : {
12311 186 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12312 :
12313 186 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12314 : }
12315 :
12316 : /************************************************************************/
12317 : /* ImportFromESRIWisconsinWKT() */
12318 : /* */
12319 : /* Search a ESRI State Plane WKT and import it. */
12320 : /************************************************************************/
12321 :
12322 : // This is only used by the HFA driver and somewhat dubious we really need that
12323 : // Coming from an old ESRI merge
12324 :
12325 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12326 : double centralMeridian,
12327 : double latOfOrigin,
12328 : const char *unitsName,
12329 : const char *crsName)
12330 : {
12331 2 : TAKE_OPTIONAL_LOCK();
12332 :
12333 1 : if (centralMeridian < -93 || centralMeridian > -87)
12334 0 : return OGRERR_FAILURE;
12335 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12336 0 : return OGRERR_FAILURE;
12337 :
12338 : // If the CS name is known.
12339 1 : if (!prjName && !unitsName && crsName)
12340 : {
12341 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12342 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12343 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12344 0 : if (list)
12345 : {
12346 0 : if (proj_list_get_count(list) == 1)
12347 : {
12348 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12349 0 : if (crs)
12350 : {
12351 0 : Clear();
12352 0 : d->setPjCRS(crs);
12353 0 : proj_list_destroy(list);
12354 0 : return OGRERR_NONE;
12355 : }
12356 : }
12357 0 : proj_list_destroy(list);
12358 : }
12359 0 : return OGRERR_FAILURE;
12360 : }
12361 :
12362 1 : if (prjName == nullptr || unitsName == nullptr)
12363 : {
12364 0 : return OGRERR_FAILURE;
12365 : }
12366 :
12367 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12368 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12369 : "NAD_1983_HARN_WISCRS_", &type, 1,
12370 : true, 0, nullptr);
12371 1 : if (list)
12372 : {
12373 1 : const auto listSize = proj_list_get_count(list);
12374 8 : for (int i = 0; i < listSize; i++)
12375 : {
12376 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12377 8 : if (!crs)
12378 : {
12379 7 : continue;
12380 : }
12381 :
12382 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12383 8 : if (!conv)
12384 : {
12385 0 : proj_destroy(crs);
12386 0 : continue;
12387 : }
12388 8 : const char *pszMethodCode = nullptr;
12389 8 : proj_coordoperation_get_method_info(
12390 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12391 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12392 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12393 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12394 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12395 : nMethodCode ==
12396 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12397 : {
12398 3 : proj_destroy(crs);
12399 3 : proj_destroy(conv);
12400 3 : continue;
12401 : }
12402 :
12403 : auto coordSys =
12404 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12405 5 : if (!coordSys)
12406 : {
12407 0 : proj_destroy(crs);
12408 0 : proj_destroy(conv);
12409 0 : continue;
12410 : }
12411 :
12412 5 : double dfConvFactor = 0.0;
12413 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12414 : nullptr, nullptr, &dfConvFactor, nullptr,
12415 : nullptr, nullptr);
12416 5 : proj_destroy(coordSys);
12417 :
12418 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12419 1 : (!EQUAL(unitsName, "meters") &&
12420 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12421 : 1e-10))
12422 : {
12423 4 : proj_destroy(crs);
12424 4 : proj_destroy(conv);
12425 4 : continue;
12426 : }
12427 :
12428 1 : int idx_lat = proj_coordoperation_get_param_index(
12429 : d->getPROJContext(), conv,
12430 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12431 1 : double valueLat = -1000;
12432 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12433 : nullptr, nullptr, nullptr, &valueLat,
12434 : nullptr, nullptr, nullptr, nullptr,
12435 : nullptr, nullptr);
12436 1 : int idx_lon = proj_coordoperation_get_param_index(
12437 : d->getPROJContext(), conv,
12438 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12439 1 : double valueLong = -1000;
12440 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12441 : nullptr, nullptr, nullptr, &valueLong,
12442 : nullptr, nullptr, nullptr, nullptr,
12443 : nullptr, nullptr);
12444 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12445 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12446 : {
12447 1 : Clear();
12448 1 : d->setPjCRS(crs);
12449 1 : proj_list_destroy(list);
12450 1 : proj_destroy(conv);
12451 1 : return OGRERR_NONE;
12452 : }
12453 :
12454 0 : proj_destroy(crs);
12455 0 : proj_destroy(conv);
12456 : }
12457 0 : proj_list_destroy(list);
12458 : }
12459 :
12460 0 : return OGRERR_FAILURE;
12461 : }
12462 :
12463 : /************************************************************************/
12464 : /* GetAxisMappingStrategy() */
12465 : /************************************************************************/
12466 :
12467 : /** \brief Return the data axis to CRS axis mapping strategy.
12468 : *
12469 : * <ul>
12470 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12471 : * lat/long order, the data will still be long/lat ordered. Similarly for
12472 : * a projected CRS with northing/easting order, the data will still be
12473 : * easting/northing ordered.
12474 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12475 : * the CRS axis.
12476 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12477 : * SetDataAxisToSRSAxisMapping()
12478 : * </ul>
12479 : * @return the data axis to CRS axis mapping strategy.
12480 : * @since GDAL 3.0
12481 : */
12482 68 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12483 : {
12484 68 : TAKE_OPTIONAL_LOCK();
12485 :
12486 136 : return d->m_axisMappingStrategy;
12487 : }
12488 :
12489 : /************************************************************************/
12490 : /* OSRGetAxisMappingStrategy() */
12491 : /************************************************************************/
12492 :
12493 : /** \brief Return the data axis to CRS axis mapping strategy.
12494 : *
12495 : * See OGRSpatialReference::GetAxisMappingStrategy()
12496 : * @since GDAL 3.0
12497 : */
12498 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12499 : {
12500 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12501 :
12502 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12503 : }
12504 :
12505 : /************************************************************************/
12506 : /* SetAxisMappingStrategy() */
12507 : /************************************************************************/
12508 :
12509 : /** \brief Set the data axis to CRS axis mapping strategy.
12510 : *
12511 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12512 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12513 : * later being the default value when the option is not set) to control the
12514 : * value of the data axis to CRS axis mapping strategy when a
12515 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12516 : * override this default value.
12517 : *
12518 : * See OGRSpatialReference::GetAxisMappingStrategy()
12519 : * @since GDAL 3.0
12520 : */
12521 76333 : void OGRSpatialReference::SetAxisMappingStrategy(
12522 : OSRAxisMappingStrategy strategy)
12523 : {
12524 152594 : TAKE_OPTIONAL_LOCK();
12525 :
12526 76293 : d->m_axisMappingStrategy = strategy;
12527 76275 : d->refreshAxisMapping();
12528 76218 : }
12529 :
12530 : /************************************************************************/
12531 : /* OSRSetAxisMappingStrategy() */
12532 : /************************************************************************/
12533 :
12534 : /** \brief Set the data axis to CRS axis mapping strategy.
12535 : *
12536 : * See OGRSpatialReference::SetAxisMappingStrategy()
12537 : * @since GDAL 3.0
12538 : */
12539 851 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12540 : OSRAxisMappingStrategy strategy)
12541 : {
12542 851 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12543 :
12544 851 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12545 : }
12546 :
12547 : /************************************************************************/
12548 : /* GetDataAxisToSRSAxisMapping() */
12549 : /************************************************************************/
12550 :
12551 : /** \brief Return the data axis to SRS axis mapping.
12552 : *
12553 : * The number of elements of the vector will be the number of axis of the CRS.
12554 : * Values start at 1.
12555 : *
12556 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12557 : * for the first axis of the CRS.
12558 : *
12559 : * @since GDAL 3.0
12560 : */
12561 3645490 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12562 : {
12563 3645490 : TAKE_OPTIONAL_LOCK();
12564 :
12565 7290900 : return d->m_axisMapping;
12566 : }
12567 :
12568 : /************************************************************************/
12569 : /* OSRGetDataAxisToSRSAxisMapping() */
12570 : /************************************************************************/
12571 :
12572 : /** \brief Return the data axis to SRS axis mapping.
12573 : *
12574 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12575 : *
12576 : * @since GDAL 3.0
12577 : */
12578 178 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12579 : int *pnCount)
12580 : {
12581 178 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12582 178 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12583 :
12584 : const auto &v =
12585 178 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12586 178 : *pnCount = static_cast<int>(v.size());
12587 178 : return v.data();
12588 : }
12589 :
12590 : /************************************************************************/
12591 : /* SetDataAxisToSRSAxisMapping() */
12592 : /************************************************************************/
12593 :
12594 : /** \brief Set a custom data axis to CRS axis mapping.
12595 : *
12596 : * The number of elements of the mapping vector should be the number of axis
12597 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12598 : * check that, beyond checking there are at least 2 elements, so that this
12599 : * method and setting the CRS can be done in any order).
12600 : * This is taken into account by OGRCoordinateTransformation to transform the
12601 : * order of coordinates to the order expected by the CRS before
12602 : * transformation, and back to the data order after transformation.
12603 : *
12604 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12605 : * axis of the CRS. A negative value can also be used to ask for a sign
12606 : * reversal during coordinate transformation (to deal with northing vs southing,
12607 : * easting vs westing, heights vs depths).
12608 : *
12609 : * When used with OGRCoordinateTransformation,
12610 : * - the only valid values for mapping[0] (data axis number for the first axis
12611 : * of the CRS) are 1, 2, -1, -2.
12612 : * - the only valid values for mapping[1] (data axis number for the second axis
12613 : * of the CRS) are 1, 2, -1, -2.
12614 : * - the only valid values mapping[2] are 3 or -3.
12615 : * Note: this method does not validate the values of mapping[].
12616 : *
12617 : * mapping=[2,1] typically expresses the inversion of axis between the data
12618 : * axis and the CRS axis for a 2D CRS.
12619 : *
12620 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12621 : *
12622 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12623 : *
12624 : * @param mapping The new data axis to CRS axis mapping.
12625 : *
12626 : * @since GDAL 3.0
12627 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12628 : */
12629 6506 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12630 : const std::vector<int> &mapping)
12631 : {
12632 13012 : TAKE_OPTIONAL_LOCK();
12633 :
12634 6506 : if (mapping.size() < 2)
12635 0 : return OGRERR_FAILURE;
12636 6506 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12637 6506 : d->m_axisMapping = mapping;
12638 6506 : return OGRERR_NONE;
12639 : }
12640 :
12641 : /************************************************************************/
12642 : /* OSRSetDataAxisToSRSAxisMapping() */
12643 : /************************************************************************/
12644 :
12645 : /** \brief Set a custom data axis to CRS axis mapping.
12646 : *
12647 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12648 : *
12649 : * This is the same as the C++ method
12650 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12651 : *
12652 : * @since GDAL 3.1
12653 : */
12654 12 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12655 : int nMappingSize, const int *panMapping)
12656 : {
12657 12 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12658 12 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12659 : OGRERR_FAILURE);
12660 :
12661 12 : if (nMappingSize < 0)
12662 0 : return OGRERR_FAILURE;
12663 :
12664 24 : std::vector<int> mapping(nMappingSize);
12665 12 : if (nMappingSize)
12666 12 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12667 12 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12668 12 : mapping);
12669 : }
12670 :
12671 : /************************************************************************/
12672 : /* GetAreaOfUse() */
12673 : /************************************************************************/
12674 :
12675 : /** \brief Return the area of use of the CRS.
12676 : *
12677 : * This method is the same as the OSRGetAreaOfUse() function.
12678 : *
12679 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12680 : * longitude, expressed in degree. Might be NULL. If the returned value is
12681 : * -1000, the bounding box is unknown.
12682 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12683 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12684 : * the bounding box is unknown.
12685 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12686 : * longitude, expressed in degree. Might be NULL. If the returned value is
12687 : * -1000, the bounding box is unknown.
12688 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12689 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12690 : * the bounding box is unknown.
12691 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12692 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12693 : * invalidated by further calls.
12694 : * @return true in case of success
12695 : * @since GDAL 3.0
12696 : */
12697 32 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12698 : double *pdfSouthLatitudeDeg,
12699 : double *pdfEastLongitudeDeg,
12700 : double *pdfNorthLatitudeDeg,
12701 : const char **ppszAreaName) const
12702 : {
12703 64 : TAKE_OPTIONAL_LOCK();
12704 :
12705 32 : d->refreshProjObj();
12706 32 : if (!d->m_pj_crs)
12707 : {
12708 0 : return false;
12709 : }
12710 32 : d->demoteFromBoundCRS();
12711 32 : const char *pszAreaName = nullptr;
12712 32 : int bSuccess = proj_get_area_of_use(
12713 32 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12714 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12715 : &pszAreaName);
12716 32 : d->undoDemoteFromBoundCRS();
12717 32 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12718 32 : if (ppszAreaName)
12719 1 : *ppszAreaName = d->m_osAreaName.c_str();
12720 32 : return CPL_TO_BOOL(bSuccess);
12721 : }
12722 :
12723 : /************************************************************************/
12724 : /* GetAreaOfUse() */
12725 : /************************************************************************/
12726 :
12727 : /** \brief Return the area of use of the CRS.
12728 : *
12729 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12730 : *
12731 : * @since GDAL 3.0
12732 : */
12733 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12734 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12735 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12736 : {
12737 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12738 :
12739 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12740 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12741 1 : pdfNorthLatitudeDeg, ppszAreaName);
12742 : }
12743 :
12744 : /************************************************************************/
12745 : /* OSRGetCRSInfoListFromDatabase() */
12746 : /************************************************************************/
12747 :
12748 : /** \brief Enumerate CRS objects from the database.
12749 : *
12750 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12751 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12752 : *
12753 : * @param pszAuthName Authority name, used to restrict the search.
12754 : * Or NULL for all authorities.
12755 : * @param params Additional criteria. Must be set to NULL for now.
12756 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12757 : * the size of the result list. Might be NULL
12758 : * @return an array of OSRCRSInfo* pointers to be freed with
12759 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12760 : *
12761 : * @since GDAL 3.0
12762 : */
12763 : OSRCRSInfo **
12764 1 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12765 : CPL_UNUSED const OSRCRSListParameters *params,
12766 : int *pnOutResultCount)
12767 : {
12768 1 : int nResultCount = 0;
12769 1 : auto projList = proj_get_crs_info_list_from_database(
12770 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12771 1 : if (pnOutResultCount)
12772 1 : *pnOutResultCount = nResultCount;
12773 1 : if (!projList)
12774 : {
12775 0 : return nullptr;
12776 : }
12777 1 : auto res = new OSRCRSInfo *[nResultCount + 1];
12778 6610 : for (int i = 0; i < nResultCount; i++)
12779 : {
12780 6609 : res[i] = new OSRCRSInfo;
12781 13218 : res[i]->pszAuthName = projList[i]->auth_name
12782 6609 : ? CPLStrdup(projList[i]->auth_name)
12783 : : nullptr;
12784 6609 : res[i]->pszCode =
12785 6609 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12786 6609 : res[i]->pszName =
12787 6609 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12788 6609 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12789 6609 : switch (projList[i]->type)
12790 : {
12791 591 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12792 591 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12793 591 : break;
12794 221 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12795 221 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12796 221 : break;
12797 222 : case PJ_TYPE_GEOCENTRIC_CRS:
12798 222 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12799 222 : break;
12800 5077 : case PJ_TYPE_PROJECTED_CRS:
12801 5077 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12802 5077 : break;
12803 221 : case PJ_TYPE_VERTICAL_CRS:
12804 221 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12805 221 : break;
12806 277 : case PJ_TYPE_COMPOUND_CRS:
12807 277 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12808 277 : break;
12809 0 : default:
12810 0 : break;
12811 : }
12812 6609 : res[i]->bDeprecated = projList[i]->deprecated;
12813 6609 : res[i]->bBboxValid = projList[i]->bbox_valid;
12814 6609 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12815 6609 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12816 6609 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12817 6609 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12818 13218 : res[i]->pszAreaName = projList[i]->area_name
12819 6609 : ? CPLStrdup(projList[i]->area_name)
12820 : : nullptr;
12821 6609 : res[i]->pszProjectionMethod =
12822 6609 : projList[i]->projection_method_name
12823 6609 : ? CPLStrdup(projList[i]->projection_method_name)
12824 : : nullptr;
12825 : }
12826 1 : res[nResultCount] = nullptr;
12827 1 : proj_crs_info_list_destroy(projList);
12828 1 : return res;
12829 : }
12830 :
12831 : /************************************************************************/
12832 : /* OSRDestroyCRSInfoList() */
12833 : /************************************************************************/
12834 :
12835 : /** \brief Destroy the result returned by
12836 : * OSRGetCRSInfoListFromDatabase().
12837 : *
12838 : * @since GDAL 3.0
12839 : */
12840 1 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12841 : {
12842 1 : if (list)
12843 : {
12844 6610 : for (int i = 0; list[i] != nullptr; i++)
12845 : {
12846 6609 : CPLFree(list[i]->pszAuthName);
12847 6609 : CPLFree(list[i]->pszCode);
12848 6609 : CPLFree(list[i]->pszName);
12849 6609 : CPLFree(list[i]->pszAreaName);
12850 6609 : CPLFree(list[i]->pszProjectionMethod);
12851 6609 : delete list[i];
12852 : }
12853 1 : delete[] list;
12854 : }
12855 1 : }
12856 :
12857 : /************************************************************************/
12858 : /* OSRGetAuthorityListFromDatabase() */
12859 : /************************************************************************/
12860 :
12861 : /** \brief Return the list of CRS authorities used in the PROJ database.
12862 : *
12863 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
12864 : *
12865 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
12866 : *
12867 : * @return nullptr in case of error, or a NULL terminated list of strings to
12868 : * free with CSLDestroy()
12869 : * @since GDAL 3.10
12870 : */
12871 1 : char **OSRGetAuthorityListFromDatabase()
12872 : {
12873 : PROJ_STRING_LIST list =
12874 1 : proj_get_authorities_from_database(OSRGetProjTLSContext());
12875 1 : if (!list)
12876 : {
12877 0 : return nullptr;
12878 : }
12879 1 : int count = 0;
12880 6 : while (list[count])
12881 5 : ++count;
12882 1 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
12883 6 : for (int i = 0; i < count; ++i)
12884 5 : res[i] = CPLStrdup(list[i]);
12885 1 : proj_string_list_destroy(list);
12886 1 : return res;
12887 : }
12888 :
12889 : /************************************************************************/
12890 : /* UpdateCoordinateSystemFromGeogCRS() */
12891 : /************************************************************************/
12892 :
12893 : /*! @cond Doxygen_Suppress */
12894 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12895 : *
12896 : * @since GDAL 3.1
12897 : */
12898 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12899 : {
12900 1 : TAKE_OPTIONAL_LOCK();
12901 :
12902 1 : d->refreshProjObj();
12903 1 : if (!d->m_pj_crs)
12904 0 : return;
12905 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12906 0 : return;
12907 1 : if (GetAxesCount() == 3)
12908 0 : return;
12909 1 : auto ctxt = d->getPROJContext();
12910 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12911 1 : if (!baseCRS)
12912 0 : return;
12913 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12914 1 : if (!baseCRSCS)
12915 : {
12916 0 : proj_destroy(baseCRS);
12917 0 : return;
12918 : }
12919 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12920 : {
12921 0 : proj_destroy(baseCRSCS);
12922 0 : proj_destroy(baseCRS);
12923 0 : return;
12924 : }
12925 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12926 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12927 : {
12928 0 : proj_destroy(baseCRSCS);
12929 0 : proj_destroy(baseCRS);
12930 0 : proj_destroy(projCS);
12931 0 : return;
12932 : }
12933 :
12934 : PJ_AXIS_DESCRIPTION axis[3];
12935 4 : for (int i = 0; i < 3; i++)
12936 : {
12937 3 : const char *name = nullptr;
12938 3 : const char *abbreviation = nullptr;
12939 3 : const char *direction = nullptr;
12940 3 : double unit_conv_factor = 0;
12941 3 : const char *unit_name = nullptr;
12942 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
12943 : &abbreviation, &direction, &unit_conv_factor,
12944 : &unit_name, nullptr, nullptr);
12945 3 : axis[i].name = CPLStrdup(name);
12946 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
12947 3 : axis[i].direction = CPLStrdup(direction);
12948 3 : axis[i].unit_name = CPLStrdup(unit_name);
12949 3 : axis[i].unit_conv_factor = unit_conv_factor;
12950 3 : axis[i].unit_type = PJ_UT_LINEAR;
12951 : }
12952 1 : proj_destroy(baseCRSCS);
12953 1 : proj_destroy(projCS);
12954 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
12955 4 : for (int i = 0; i < 3; i++)
12956 : {
12957 3 : CPLFree(axis[i].name);
12958 3 : CPLFree(axis[i].abbreviation);
12959 3 : CPLFree(axis[i].direction);
12960 3 : CPLFree(axis[i].unit_name);
12961 : }
12962 1 : if (!cs)
12963 : {
12964 0 : proj_destroy(baseCRS);
12965 0 : return;
12966 : }
12967 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
12968 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
12969 : conversion, cs);
12970 1 : proj_destroy(baseCRS);
12971 1 : proj_destroy(conversion);
12972 1 : proj_destroy(cs);
12973 1 : d->setPjCRS(crs);
12974 : }
12975 :
12976 : /*! @endcond */
12977 :
12978 : /************************************************************************/
12979 : /* PromoteTo3D() */
12980 : /************************************************************************/
12981 :
12982 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
12983 : *
12984 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
12985 : * units.
12986 : *
12987 : * @param pszName New name for the CRS. If set to NULL, the previous name will
12988 : * be used.
12989 : * @return OGRERR_NONE if no error occurred.
12990 : * @since GDAL 3.1 and PROJ 6.3
12991 : */
12992 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
12993 : {
12994 84 : TAKE_OPTIONAL_LOCK();
12995 :
12996 42 : d->refreshProjObj();
12997 42 : if (!d->m_pj_crs)
12998 0 : return OGRERR_FAILURE;
12999 : auto newPj =
13000 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13001 42 : if (!newPj)
13002 0 : return OGRERR_FAILURE;
13003 42 : d->setPjCRS(newPj);
13004 42 : return OGRERR_NONE;
13005 : }
13006 :
13007 : /************************************************************************/
13008 : /* OSRPromoteTo3D() */
13009 : /************************************************************************/
13010 :
13011 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13012 : *
13013 : * See OGRSpatialReference::PromoteTo3D()
13014 : *
13015 : * @since GDAL 3.1 and PROJ 6.3
13016 : */
13017 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13018 : {
13019 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13020 :
13021 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13022 : }
13023 :
13024 : /************************************************************************/
13025 : /* DemoteTo2D() */
13026 : /************************************************************************/
13027 :
13028 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13029 : *
13030 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13031 : * be used.
13032 : * @return OGRERR_NONE if no error occurred.
13033 : * @since GDAL 3.2 and PROJ 6.3
13034 : */
13035 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13036 : {
13037 90 : TAKE_OPTIONAL_LOCK();
13038 :
13039 45 : d->refreshProjObj();
13040 45 : if (!d->m_pj_crs)
13041 0 : return OGRERR_FAILURE;
13042 : auto newPj =
13043 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13044 45 : if (!newPj)
13045 0 : return OGRERR_FAILURE;
13046 45 : d->setPjCRS(newPj);
13047 45 : return OGRERR_NONE;
13048 : }
13049 :
13050 : /************************************************************************/
13051 : /* OSRDemoteTo2D() */
13052 : /************************************************************************/
13053 :
13054 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13055 : *
13056 : * See OGRSpatialReference::DemoteTo2D()
13057 : *
13058 : * @since GDAL 3.2 and PROJ 6.3
13059 : */
13060 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13061 : {
13062 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13063 :
13064 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13065 : }
13066 :
13067 : /************************************************************************/
13068 : /* GetEPSGGeogCS() */
13069 : /************************************************************************/
13070 :
13071 : /** Try to establish what the EPSG code for this coordinate systems
13072 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13073 : *
13074 : * @return EPSG code
13075 : */
13076 :
13077 331 : int OGRSpatialReference::GetEPSGGeogCS() const
13078 :
13079 : {
13080 662 : TAKE_OPTIONAL_LOCK();
13081 :
13082 : /* -------------------------------------------------------------------- */
13083 : /* Check axis order. */
13084 : /* -------------------------------------------------------------------- */
13085 662 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13086 331 : if (!poGeogCRS)
13087 0 : return -1;
13088 :
13089 331 : bool ret = false;
13090 331 : poGeogCRS->d->demoteFromBoundCRS();
13091 331 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13092 331 : poGeogCRS->d->m_pj_crs);
13093 331 : poGeogCRS->d->undoDemoteFromBoundCRS();
13094 331 : if (cs)
13095 : {
13096 331 : const char *pszDirection = nullptr;
13097 331 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13098 : &pszDirection, nullptr, nullptr, nullptr,
13099 331 : nullptr))
13100 : {
13101 331 : if (EQUAL(pszDirection, "north"))
13102 : {
13103 133 : ret = true;
13104 : }
13105 : }
13106 :
13107 331 : proj_destroy(cs);
13108 : }
13109 331 : if (!ret)
13110 198 : return -1;
13111 :
13112 : /* -------------------------------------------------------------------- */
13113 : /* Do we already have it? */
13114 : /* -------------------------------------------------------------------- */
13115 133 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13116 133 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13117 59 : return atoi(GetAuthorityCode("GEOGCS"));
13118 :
13119 : /* -------------------------------------------------------------------- */
13120 : /* Get the datum and geogcs names. */
13121 : /* -------------------------------------------------------------------- */
13122 :
13123 74 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13124 74 : const char *pszDatum = GetAttrValue("DATUM");
13125 :
13126 : // We can only operate on coordinate systems with a geogcs.
13127 148 : OGRSpatialReference oSRSTmp;
13128 74 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13129 : {
13130 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13131 : // export to WKT1, so try to extract the geographic CRS through PROJ
13132 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13133 1 : oSRSTmp.CopyGeogCSFrom(this);
13134 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13135 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13136 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13137 : {
13138 0 : return -1;
13139 : }
13140 : }
13141 :
13142 : // Lookup geographic CRS name
13143 74 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13144 74 : PJ_OBJ_LIST *list = proj_create_from_name(
13145 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13146 74 : if (list)
13147 : {
13148 74 : const auto listSize = proj_list_get_count(list);
13149 74 : if (listSize == 1)
13150 : {
13151 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13152 49 : if (crs)
13153 : {
13154 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13155 49 : const char *pszCode = proj_get_id_code(crs, 0);
13156 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13157 : {
13158 47 : const int nCode = atoi(pszCode);
13159 47 : proj_destroy(crs);
13160 47 : proj_list_destroy(list);
13161 47 : return nCode;
13162 : }
13163 2 : proj_destroy(crs);
13164 : }
13165 : }
13166 27 : proj_list_destroy(list);
13167 : }
13168 :
13169 : /* -------------------------------------------------------------------- */
13170 : /* Is this a "well known" geographic coordinate system? */
13171 : /* -------------------------------------------------------------------- */
13172 81 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13173 27 : strstr(pszDatum, "WGS") ||
13174 27 : strstr(pszGEOGCS, "World Geodetic System") ||
13175 27 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13176 81 : strstr(pszDatum, "World Geodetic System") ||
13177 27 : strstr(pszDatum, "World_Geodetic_System");
13178 :
13179 81 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13180 27 : strstr(pszDatum, "NAD") ||
13181 27 : strstr(pszGEOGCS, "North American") ||
13182 27 : strstr(pszGEOGCS, "North_American") ||
13183 81 : strstr(pszDatum, "North American") ||
13184 27 : strstr(pszDatum, "North_American");
13185 :
13186 27 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13187 0 : return 4326;
13188 :
13189 27 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13190 0 : return 4322;
13191 :
13192 : // This is questionable as there are several 'flavors' of NAD83 that
13193 : // are not the same as 4269
13194 27 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13195 0 : return 4269;
13196 :
13197 27 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13198 0 : return 4267;
13199 :
13200 : /* -------------------------------------------------------------------- */
13201 : /* If we know the datum, associate the most likely GCS with */
13202 : /* it. */
13203 : /* -------------------------------------------------------------------- */
13204 27 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13205 27 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13206 27 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13207 0 : GetPrimeMeridian() == 0.0)
13208 : {
13209 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13210 :
13211 0 : if (nDatum >= 6000 && nDatum <= 6999)
13212 0 : return nDatum - 2000;
13213 : }
13214 :
13215 27 : return -1;
13216 : }
13217 :
13218 : /************************************************************************/
13219 : /* SetCoordinateEpoch() */
13220 : /************************************************************************/
13221 :
13222 : /** Set the coordinate epoch, as decimal year.
13223 : *
13224 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13225 : * change with time. To be unambiguous the coordinates must always be qualified
13226 : * with the epoch at which they are valid. The coordinate epoch is not
13227 : * necessarily the epoch at which the observation was collected.
13228 : *
13229 : * Pedantically the coordinate epoch of an observation belongs to the
13230 : * observation, and not to the CRS, however it is often more practical to
13231 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13232 : * CRS (see IsDynamic())
13233 : *
13234 : * This method is the same as the OSRSetCoordinateEpoch() function.
13235 : *
13236 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13237 : * @since OGR 3.4
13238 : */
13239 :
13240 759 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13241 : {
13242 759 : d->m_coordinateEpoch = dfCoordinateEpoch;
13243 759 : }
13244 :
13245 : /************************************************************************/
13246 : /* OSRSetCoordinateEpoch() */
13247 : /************************************************************************/
13248 :
13249 : /** \brief Set the coordinate epoch, as decimal year.
13250 : *
13251 : * See OGRSpatialReference::SetCoordinateEpoch()
13252 : *
13253 : * @since OGR 3.4
13254 : */
13255 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13256 : {
13257 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13258 :
13259 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13260 31 : dfCoordinateEpoch);
13261 : }
13262 :
13263 : /************************************************************************/
13264 : /* GetCoordinateEpoch() */
13265 : /************************************************************************/
13266 :
13267 : /** Return the coordinate epoch, as decimal year.
13268 : *
13269 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13270 : * change with time. To be unambiguous the coordinates must always be qualified
13271 : * with the epoch at which they are valid. The coordinate epoch is not
13272 : * necessarily the epoch at which the observation was collected.
13273 : *
13274 : * Pedantically the coordinate epoch of an observation belongs to the
13275 : * observation, and not to the CRS, however it is often more practical to
13276 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13277 : * CRS (see IsDynamic())
13278 : *
13279 : * This method is the same as the OSRGetCoordinateEpoch() function.
13280 : *
13281 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13282 : * if not set, or relevant.
13283 : * @since OGR 3.4
13284 : */
13285 :
13286 10439 : double OGRSpatialReference::GetCoordinateEpoch() const
13287 : {
13288 10439 : return d->m_coordinateEpoch;
13289 : }
13290 :
13291 : /************************************************************************/
13292 : /* OSRGetCoordinateEpoch() */
13293 : /************************************************************************/
13294 :
13295 : /** \brief Get the coordinate epoch, as decimal year.
13296 : *
13297 : * See OGRSpatialReference::GetCoordinateEpoch()
13298 : *
13299 : * @since OGR 3.4
13300 : */
13301 599 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13302 : {
13303 599 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13304 :
13305 599 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13306 : }
|