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 199278 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
69 : {
70 199165 : }
71 :
72 : Listener(const Listener &) = delete;
73 : Listener &operator=(const Listener &) = delete;
74 :
75 1814190 : void notifyChange(OGR_SRSNode *) override
76 : {
77 1814190 : m_poObj->nodesChanged();
78 1814190 : }
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 1004590 : PJ_CONTEXT *getPROJContext()
159 : {
160 1004590 : 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 4470900 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 4470900 : if (m_private.m_bIsThreadSafe)
182 3797 : m_private.m_mutex.lock();
183 4470900 : }
184 :
185 4470980 : ~OptionalLockGuard()
186 4470980 : {
187 4470980 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 4470980 : }
190 : };
191 :
192 4470940 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 4470940 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 : #define TAKE_OPTIONAL_LOCK() \
199 : auto lock = d->GetOptionalLockGuard(); \
200 : CPL_IGNORE_RET_VAL(lock)
201 :
202 199269 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
203 : {
204 : const char *pszDefaultAMS =
205 199269 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
206 199306 : 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 199305 : return OAMS_AUTHORITY_COMPLIANT;
220 : }
221 :
222 199270 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
223 : : m_poSelf(poSelf),
224 199270 : 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 199190 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
229 199306 : }
230 :
231 793828 : 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 198483 : auto ctxt = getPROJContext();
237 :
238 198487 : proj_assign_context(m_pj_crs, ctxt);
239 198487 : proj_destroy(m_pj_crs);
240 :
241 198482 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
242 198483 : proj_destroy(m_pj_geod_base_crs_temp);
243 :
244 198484 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
245 198481 : proj_destroy(m_pj_proj_crs_cs_temp);
246 :
247 198482 : proj_assign_context(m_pj_bound_crs_target, ctxt);
248 198481 : proj_destroy(m_pj_bound_crs_target);
249 :
250 198480 : proj_assign_context(m_pj_bound_crs_co, ctxt);
251 198481 : proj_destroy(m_pj_bound_crs_co);
252 :
253 198481 : proj_assign_context(m_pj_crs_backup, ctxt);
254 198479 : proj_destroy(m_pj_crs_backup);
255 :
256 198480 : delete m_poRootBackup;
257 198482 : delete m_poRoot;
258 198474 : }
259 :
260 99555 : void OGRSpatialReference::Private::clear()
261 : {
262 99555 : proj_assign_context(m_pj_crs, getPROJContext());
263 99556 : proj_destroy(m_pj_crs);
264 99556 : m_pj_crs = nullptr;
265 :
266 99556 : delete m_poRoot;
267 99556 : m_poRoot = nullptr;
268 99556 : m_bNodesChanged = false;
269 :
270 99556 : m_wktImportWarnings.clear();
271 99556 : m_wktImportErrors.clear();
272 :
273 99556 : m_pj_crs_modified_during_demote = false;
274 99556 : m_pjType = PJ_TYPE_UNKNOWN;
275 99556 : m_osPrimeMeridianName.clear();
276 99556 : m_osAngularUnits.clear();
277 99556 : m_osLinearUnits.clear();
278 :
279 99556 : bNormInfoSet = FALSE;
280 99556 : dfFromGreenwich = 1.0;
281 99556 : dfToMeter = 1.0;
282 99556 : dfToDegrees = 1.0;
283 99556 : m_dfAngularUnitToRadian = 0.0;
284 :
285 99556 : m_bMorphToESRI = false;
286 99556 : m_bHasCenterLong = false;
287 :
288 99556 : m_coordinateEpoch = 0.0;
289 99556 : }
290 :
291 23149 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
292 : {
293 23149 : m_poRoot = poRoot;
294 23149 : if (m_poRoot)
295 : {
296 23149 : m_poRoot->RegisterListener(m_poListener);
297 : }
298 23149 : nodesChanged();
299 23149 : }
300 :
301 160137 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
302 : bool doRefreshAxisMapping)
303 : {
304 160137 : 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 160137 : proj_assign_context(m_pj_crs, ctxt);
322 160137 : proj_destroy(m_pj_crs);
323 160137 : m_pj_crs = pj_crsIn;
324 160137 : if (m_pj_crs)
325 : {
326 160083 : m_pjType = proj_get_type(m_pj_crs);
327 : }
328 160137 : if (m_pj_crs_backup)
329 : {
330 21 : m_pj_crs_modified_during_demote = true;
331 : }
332 160137 : invalidateNodes();
333 160137 : if (doRefreshAxisMapping)
334 : {
335 160117 : refreshAxisMapping();
336 : }
337 160135 : }
338 :
339 596974 : void OGRSpatialReference::Private::refreshProjObj()
340 : {
341 596974 : if (m_bNodesChanged && m_poRoot)
342 : {
343 7186 : char *pszWKT = nullptr;
344 7186 : m_poRoot->exportToWkt(&pszWKT);
345 7186 : auto poRootBackup = m_poRoot;
346 7186 : m_poRoot = nullptr;
347 7186 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
348 7186 : clear();
349 7186 : m_coordinateEpoch = dfCoordinateEpochBackup;
350 7186 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
351 :
352 7186 : 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 7186 : PROJ_STRING_LIST warnings = nullptr;
360 7186 : PROJ_STRING_LIST errors = nullptr;
361 7186 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
362 : &warnings, &errors));
363 14048 : for (auto iter = warnings; iter && *iter; ++iter)
364 : {
365 6862 : m_wktImportWarnings.push_back(*iter);
366 : }
367 7397 : for (auto iter = errors; iter && *iter; ++iter)
368 : {
369 211 : m_wktImportErrors.push_back(*iter);
370 : }
371 7186 : proj_string_list_destroy(warnings);
372 7186 : proj_string_list_destroy(errors);
373 :
374 7186 : CPLFree(pszWKT);
375 :
376 7186 : m_poRoot = poRootBackup;
377 7186 : m_bNodesChanged = false;
378 : }
379 596974 : }
380 :
381 25261 : void OGRSpatialReference::Private::refreshRootFromProjObj()
382 : {
383 25261 : CPLAssert(m_poRoot == nullptr);
384 :
385 25261 : if (m_pj_crs)
386 : {
387 46210 : CPLStringList aosOptions;
388 23105 : if (!m_bMorphToESRI)
389 : {
390 23101 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
391 23101 : aosOptions.SetNameValue("MULTILINE", "NO");
392 : }
393 23105 : aosOptions.SetNameValue("STRICT", "NO");
394 :
395 : const char *pszWKT;
396 : {
397 23105 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
398 23105 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
399 23105 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
400 23105 : aosOptions.List());
401 23105 : m_bNodesWKT2 = false;
402 : }
403 23105 : if (!m_bMorphToESRI && pszWKT == nullptr)
404 : {
405 70 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
406 70 : aosOptions.List());
407 70 : m_bNodesWKT2 = true;
408 : }
409 23105 : if (pszWKT)
410 : {
411 23105 : auto root = new OGR_SRSNode();
412 23105 : setRoot(root);
413 23105 : root->importFromWkt(&pszWKT);
414 23105 : m_bNodesChanged = false;
415 : }
416 : }
417 25261 : }
418 :
419 185999 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
420 : {
421 185999 : const char *pszName1 = nullptr;
422 185999 : const char *pszDirection1 = nullptr;
423 185999 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
424 : nullptr, nullptr, nullptr, nullptr);
425 186000 : const char *pszName2 = nullptr;
426 186000 : const char *pszDirection2 = nullptr;
427 186000 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
428 : nullptr, nullptr, nullptr, nullptr);
429 186000 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
430 82891 : EQUAL(pszDirection2, "east"))
431 : {
432 82312 : return true;
433 : }
434 103688 : if (pszDirection1 && pszDirection2 &&
435 103688 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
436 103125 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
437 1106 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
438 258 : STARTS_WITH_CI(pszName2, "easting"))
439 : {
440 258 : return true;
441 : }
442 103430 : return false;
443 : }
444 :
445 237660 : void OGRSpatialReference::Private::refreshAxisMapping()
446 : {
447 237660 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
448 52005 : return;
449 :
450 185655 : bool doUndoDemote = false;
451 185655 : if (m_pj_crs_backup == nullptr)
452 : {
453 185634 : doUndoDemote = true;
454 185634 : demoteFromBoundCRS();
455 : }
456 185655 : const auto ctxt = getPROJContext();
457 185655 : PJ *horizCRS = nullptr;
458 185655 : int axisCount = 0;
459 185655 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
460 : {
461 221 : axisCount = 1;
462 : }
463 185434 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
464 : {
465 1104 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
466 1104 : 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 1104 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
477 1104 : if (vertCRS)
478 : {
479 1101 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
480 : {
481 391 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
482 391 : if (baseCRS)
483 : {
484 391 : proj_destroy(vertCRS);
485 391 : vertCRS = baseCRS;
486 : }
487 : }
488 :
489 1101 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
490 1101 : if (cs)
491 : {
492 1101 : axisCount += proj_cs_get_axis_count(ctxt, cs);
493 1101 : proj_destroy(cs);
494 : }
495 1101 : proj_destroy(vertCRS);
496 : }
497 : }
498 : else
499 : {
500 184330 : horizCRS = m_pj_crs;
501 : }
502 :
503 185655 : bool bSwitchForGisFriendlyOrder = false;
504 185655 : if (horizCRS)
505 : {
506 185431 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
507 185431 : if (cs)
508 : {
509 185431 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
510 185431 : axisCount += nHorizCSAxisCount;
511 185431 : if (nHorizCSAxisCount >= 2)
512 : {
513 185421 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
514 : }
515 185430 : proj_destroy(cs);
516 : }
517 : }
518 185655 : if (horizCRS != m_pj_crs)
519 : {
520 1325 : proj_destroy(horizCRS);
521 : }
522 185655 : if (doUndoDemote)
523 : {
524 185634 : undoDemoteFromBoundCRS();
525 : }
526 :
527 185655 : m_axisMapping.resize(axisCount);
528 185653 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
529 58154 : !bSwitchForGisFriendlyOrder)
530 : {
531 480706 : for (int i = 0; i < axisCount; i++)
532 : {
533 320932 : m_axisMapping[i] = i + 1;
534 159774 : }
535 : }
536 : else
537 : {
538 25879 : m_axisMapping[0] = 2;
539 25879 : m_axisMapping[1] = 1;
540 25879 : if (axisCount == 3)
541 : {
542 331 : m_axisMapping[2] = 3;
543 : }
544 : }
545 : }
546 :
547 1837340 : void OGRSpatialReference::Private::nodesChanged()
548 : {
549 1837340 : m_bNodesChanged = true;
550 1837340 : }
551 :
552 160432 : void OGRSpatialReference::Private::invalidateNodes()
553 : {
554 160432 : delete m_poRoot;
555 160432 : m_poRoot = nullptr;
556 160432 : m_bNodesChanged = false;
557 160432 : }
558 :
559 295 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
560 : {
561 295 : invalidateNodes();
562 295 : m_bMorphToESRI = b;
563 295 : }
564 :
565 524185 : void OGRSpatialReference::Private::demoteFromBoundCRS()
566 : {
567 524185 : CPLAssert(m_pj_bound_crs_target == nullptr);
568 524185 : CPLAssert(m_pj_bound_crs_co == nullptr);
569 524185 : CPLAssert(m_poRootBackup == nullptr);
570 524185 : CPLAssert(m_pj_crs_backup == nullptr);
571 :
572 524185 : m_pj_crs_modified_during_demote = false;
573 :
574 524185 : if (m_pjType == PJ_TYPE_BOUND_CRS)
575 : {
576 2755 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
577 2755 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
578 2755 : m_pj_bound_crs_co =
579 2755 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
580 :
581 2755 : m_poRootBackup = m_poRoot;
582 2755 : m_poRoot = nullptr;
583 2755 : m_pj_crs_backup = m_pj_crs;
584 2755 : m_pj_crs = baseCRS;
585 2755 : m_pjType = proj_get_type(m_pj_crs);
586 : }
587 524185 : }
588 :
589 524187 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
590 : {
591 524187 : if (m_pj_bound_crs_target)
592 : {
593 2755 : CPLAssert(m_poRoot == nullptr);
594 2755 : CPLAssert(m_pj_crs);
595 2755 : if (!m_pj_crs_modified_during_demote)
596 : {
597 2735 : proj_destroy(m_pj_crs);
598 2735 : m_pj_crs = m_pj_crs_backup;
599 2735 : m_pjType = proj_get_type(m_pj_crs);
600 2735 : m_poRoot = m_poRootBackup;
601 : }
602 : else
603 : {
604 20 : delete m_poRootBackup;
605 20 : m_poRootBackup = nullptr;
606 20 : proj_destroy(m_pj_crs_backup);
607 20 : m_pj_crs_backup = nullptr;
608 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
609 20 : m_pj_bound_crs_target,
610 20 : m_pj_bound_crs_co),
611 : false);
612 : }
613 : }
614 :
615 524187 : m_poRootBackup = nullptr;
616 524187 : m_pj_crs_backup = nullptr;
617 524187 : proj_destroy(m_pj_bound_crs_target);
618 524187 : m_pj_bound_crs_target = nullptr;
619 524187 : proj_destroy(m_pj_bound_crs_co);
620 524187 : m_pj_bound_crs_co = nullptr;
621 524187 : m_pj_crs_modified_during_demote = false;
622 524187 : }
623 :
624 111363 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
625 : const char *pszTargetKey)
626 : {
627 111363 : if (pszTargetKey)
628 : {
629 52397 : demoteFromBoundCRS();
630 52397 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
631 26102 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
632 26369 : EQUAL(pszTargetKey, "GEOGCS"))
633 : {
634 5821 : pszTargetKey = nullptr;
635 : }
636 46576 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
637 20 : EQUAL(pszTargetKey, "GEOCCS"))
638 : {
639 0 : pszTargetKey = nullptr;
640 : }
641 46576 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
642 24719 : EQUAL(pszTargetKey, "PROJCS"))
643 : {
644 3515 : pszTargetKey = nullptr;
645 : }
646 43061 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
647 4 : EQUAL(pszTargetKey, "VERT_CS"))
648 : {
649 2 : pszTargetKey = nullptr;
650 : }
651 52397 : undoDemoteFromBoundCRS();
652 : }
653 111363 : return pszTargetKey;
654 : }
655 :
656 8220 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
657 : {
658 8220 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
659 8161 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
660 : {
661 59 : return m_pj_crs;
662 : }
663 :
664 8161 : auto ctxt = getPROJContext();
665 8161 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
666 : {
667 3703 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
668 3703 : proj_destroy(m_pj_geod_base_crs_temp);
669 3703 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
670 3703 : return m_pj_geod_base_crs_temp;
671 : }
672 :
673 4458 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
674 4458 : proj_destroy(m_pj_geod_base_crs_temp);
675 4458 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
676 : nullptr, 0);
677 4458 : 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 4458 : proj_destroy(cs);
682 :
683 4458 : return m_pj_geod_base_crs_temp;
684 : }
685 :
686 4665 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
687 : {
688 4665 : auto ctxt = getPROJContext();
689 4665 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
690 : {
691 3690 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
692 3690 : proj_destroy(m_pj_proj_crs_cs_temp);
693 3690 : m_pj_proj_crs_cs_temp =
694 3690 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
695 3690 : return m_pj_proj_crs_cs_temp;
696 : }
697 :
698 975 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
699 975 : proj_destroy(m_pj_proj_crs_cs_temp);
700 975 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
701 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
702 975 : return m_pj_proj_crs_cs_temp;
703 : }
704 :
705 4715 : const char *OGRSpatialReference::Private::getProjCRSName()
706 : {
707 4715 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
708 : {
709 3704 : return proj_get_name(m_pj_crs);
710 : }
711 :
712 1011 : return "unnamed";
713 : }
714 :
715 1379 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
716 : {
717 1379 : refreshProjObj();
718 :
719 1379 : demoteFromBoundCRS();
720 :
721 : auto projCRS =
722 1379 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
723 1379 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
724 1379 : proj_destroy(conv);
725 :
726 1379 : setPjCRS(projCRS);
727 :
728 1379 : undoDemoteFromBoundCRS();
729 1379 : return OGRERR_NONE;
730 : }
731 :
732 : /************************************************************************/
733 : /* ToPointer() */
734 : /************************************************************************/
735 :
736 24328 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
737 : {
738 24328 : return OGRSpatialReference::FromHandle(hSRS);
739 : }
740 :
741 : /************************************************************************/
742 : /* ToHandle() */
743 : /************************************************************************/
744 :
745 4046 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
746 : {
747 4046 : 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 197054 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
816 197054 : : d(new Private(this))
817 : {
818 196980 : if (pszWKT != nullptr)
819 1070 : importFromWkt(pszWKT);
820 196980 : }
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 2833 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
845 :
846 : {
847 2833 : OGRSpatialReference *poSRS = new OGRSpatialReference();
848 :
849 2833 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
850 : {
851 292 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
852 : {
853 1 : delete poSRS;
854 1 : poSRS = nullptr;
855 : }
856 : }
857 :
858 2833 : return ToHandle(poSRS);
859 : }
860 :
861 : /************************************************************************/
862 : /* OGRSpatialReference() */
863 : /************************************************************************/
864 :
865 : /** Copy constructor. See also Clone().
866 : * @param oOther other spatial reference
867 : */
868 2238 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
869 2238 : : d(new Private(this))
870 : {
871 2238 : *this = oOther;
872 2238 : }
873 :
874 : /************************************************************************/
875 : /* OGRSpatialReference() */
876 : /************************************************************************/
877 :
878 : /** Move constructor.
879 : * @param oOther other spatial reference
880 : */
881 27 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
882 27 : : d(std::move(oOther.d))
883 : {
884 27 : }
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 251565 : OGRSpatialReference::~OGRSpatialReference()
900 :
901 : {
902 251553 : }
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 8190 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
941 :
942 : {
943 8190 : delete ToPointer(hSRS);
944 8190 : }
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 92369 : void OGRSpatialReference::Clear()
958 :
959 : {
960 92369 : d->clear();
961 92370 : }
962 :
963 : /************************************************************************/
964 : /* operator=() */
965 : /************************************************************************/
966 :
967 : /** Assignment operator.
968 : * @param oSource SRS to assign to *this
969 : * @return *this
970 : */
971 : OGRSpatialReference &
972 22993 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
973 :
974 : {
975 22993 : if (&oSource != this)
976 : {
977 22992 : 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 22992 : oSource.d->refreshProjObj();
984 22992 : if (oSource.d->m_pj_crs)
985 22690 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
986 22992 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
987 10523 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
988 12469 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
989 110 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
990 :
991 22992 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
992 : }
993 :
994 22993 : 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 4027 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1007 :
1008 : {
1009 4027 : if (&oSource != this)
1010 : {
1011 4026 : d = std::move(oSource.d);
1012 : }
1013 :
1014 4027 : 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 4025980 : int OGRSpatialReference::Reference()
1055 :
1056 : {
1057 4025980 : 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 924 : int OSRReference(OGRSpatialReferenceH hSRS)
1070 :
1071 : {
1072 924 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1073 :
1074 924 : 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 4064320 : int OGRSpatialReference::Dereference()
1090 :
1091 : {
1092 4064320 : 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 4064320 : 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 4061550 : void OGRSpatialReference::Release()
1142 :
1143 : {
1144 4061550 : if (Dereference() <= 0)
1145 38278 : delete this;
1146 4061550 : }
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 6656 : void OSRRelease(OGRSpatialReferenceH hSRS)
1158 :
1159 : {
1160 6656 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1161 :
1162 6656 : ToPointer(hSRS)->Release();
1163 : }
1164 :
1165 80329 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1166 : {
1167 80329 : TAKE_OPTIONAL_LOCK();
1168 :
1169 80329 : if (!d->m_poRoot)
1170 : {
1171 22771 : d->refreshRootFromProjObj();
1172 : }
1173 160658 : return d->m_poRoot;
1174 : }
1175 :
1176 7113 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1177 : {
1178 7113 : TAKE_OPTIONAL_LOCK();
1179 :
1180 7113 : if (!d->m_poRoot)
1181 : {
1182 2490 : d->refreshRootFromProjObj();
1183 : }
1184 14226 : 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 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1202 :
1203 : {
1204 44 : if (d->m_poRoot != poNewRoot)
1205 : {
1206 44 : delete d->m_poRoot;
1207 44 : d->setRoot(poNewRoot);
1208 : }
1209 44 : }
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 77212 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1234 :
1235 : {
1236 77212 : if (strchr(pszNodePath, '|') == nullptr)
1237 : {
1238 : // Fast path
1239 41820 : OGR_SRSNode *poNode = GetRoot();
1240 41820 : if (poNode)
1241 40625 : poNode = poNode->GetNode(pszNodePath);
1242 41820 : return poNode;
1243 : }
1244 :
1245 : char **papszPathTokens =
1246 35392 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1247 :
1248 35392 : if (CSLCount(papszPathTokens) < 1)
1249 : {
1250 0 : CSLDestroy(papszPathTokens);
1251 0 : return nullptr;
1252 : }
1253 :
1254 35392 : OGR_SRSNode *poNode = GetRoot();
1255 106415 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1256 : {
1257 71023 : poNode = poNode->GetNode(papszPathTokens[i]);
1258 : }
1259 :
1260 35392 : CSLDestroy(papszPathTokens);
1261 :
1262 35392 : 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 70279 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1285 :
1286 : {
1287 : OGR_SRSNode *poNode =
1288 70279 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1289 :
1290 70279 : 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 21496 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1314 : int iAttr) const
1315 :
1316 : {
1317 21496 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1318 21496 : if (poNode == nullptr)
1319 : {
1320 9678 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1321 : {
1322 14 : return GetAttrValue("METHOD", iAttr);
1323 : }
1324 9664 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1325 : {
1326 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1327 : }
1328 9664 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1329 : {
1330 1 : return GetAttrValue("PROJCRS", iAttr);
1331 : }
1332 9663 : return nullptr;
1333 : }
1334 :
1335 11818 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1336 0 : return nullptr;
1337 :
1338 11818 : 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 5077 : const char *OGRSpatialReference::GetName() const
1373 : {
1374 10154 : TAKE_OPTIONAL_LOCK();
1375 :
1376 5077 : d->refreshProjObj();
1377 5077 : if (!d->m_pj_crs)
1378 113 : return nullptr;
1379 4964 : 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 4964 : 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 40 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1410 :
1411 : {
1412 40 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1413 :
1414 40 : 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 27345 : OGRSpatialReference *OGRSpatialReference::Clone() const
1430 :
1431 : {
1432 27345 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1433 :
1434 27345 : TAKE_OPTIONAL_LOCK();
1435 :
1436 27343 : d->refreshProjObj();
1437 27345 : if (d->m_pj_crs != nullptr)
1438 27293 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1439 27344 : if (d->m_bHasCenterLong && d->m_poRoot)
1440 : {
1441 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1442 : }
1443 27345 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1444 27343 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1445 27344 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1446 54689 : 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 1037 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1459 :
1460 : {
1461 1037 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1462 :
1463 1037 : 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 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1511 : int bSimplify) const
1512 :
1513 : {
1514 116 : CPLStringList aosOptions;
1515 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1516 58 : if (bSimplify)
1517 : {
1518 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1519 : }
1520 116 : 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 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1539 : char **ppszReturn, int bSimplify)
1540 :
1541 : {
1542 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1543 :
1544 56 : *ppszReturn = nullptr;
1545 :
1546 56 : 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 12972 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1574 :
1575 : {
1576 12972 : return exportToWkt(ppszResult, nullptr);
1577 : }
1578 :
1579 : /************************************************************************/
1580 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1581 : /************************************************************************/
1582 :
1583 557 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1584 : bool onlyIfEPSGCode,
1585 : bool canModifyHorizPart)
1586 : {
1587 557 : PJ *ret = nullptr;
1588 557 : 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 1048 : 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 557 : 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 17026 : 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 34052 : std::lock_guard oLock(d->m_mutex);
1672 :
1673 17026 : d->refreshProjObj();
1674 17026 : if (!d->m_pj_crs)
1675 : {
1676 21 : *ppszResult = CPLStrdup("");
1677 21 : return OGRERR_FAILURE;
1678 : }
1679 :
1680 17005 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1681 : {
1682 0 : return d->m_poRoot->exportToWkt(ppszResult);
1683 : }
1684 :
1685 17005 : auto ctxt = d->getPROJContext();
1686 17005 : auto wktFormat = PJ_WKT1_GDAL;
1687 : const char *pszFormat =
1688 17005 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1689 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1690 17005 : if (EQUAL(pszFormat, "DEFAULT"))
1691 14731 : pszFormat = "";
1692 :
1693 17005 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1694 : {
1695 574 : wktFormat = PJ_WKT1_ESRI;
1696 : }
1697 16431 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1698 15804 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1699 : {
1700 632 : wktFormat = PJ_WKT1_GDAL;
1701 : }
1702 15799 : else if (EQUAL(pszFormat, "WKT2_2015"))
1703 : {
1704 259 : wktFormat = PJ_WKT2_2015;
1705 : }
1706 15540 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1707 15221 : EQUAL(pszFormat, "WKT2_2019"))
1708 : {
1709 1071 : wktFormat = PJ_WKT2_2018;
1710 : }
1711 14469 : else if (pszFormat[0] == '\0')
1712 : {
1713 : // cppcheck-suppress knownConditionTrueFalse
1714 14469 : if (IsDerivedGeographic())
1715 : {
1716 2 : wktFormat = PJ_WKT2_2018;
1717 : }
1718 28305 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1719 13838 : 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 34010 : CPLStringList aosOptions;
1732 17005 : if (wktFormat != PJ_WKT1_ESRI)
1733 : {
1734 16431 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1735 : }
1736 : aosOptions.SetNameValue(
1737 17005 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1738 :
1739 17004 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1740 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1741 17005 : if (pszAllowEllpsHeightAsVertCS)
1742 : {
1743 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1744 0 : pszAllowEllpsHeightAsVertCS);
1745 : }
1746 :
1747 17004 : PJ *boundCRS = nullptr;
1748 32047 : 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 34010 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
1758 17005 : CPLInstallErrorHandlerAccumulator(aoErrors);
1759 17005 : const char *pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1760 17005 : wktFormat, aosOptions.List());
1761 17005 : CPLUninstallErrorHandlerAccumulator();
1762 17007 : 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 17005 : if (!pszWKT)
1782 : {
1783 2 : *ppszResult = CPLStrdup("");
1784 2 : proj_destroy(boundCRS);
1785 2 : return OGRERR_FAILURE;
1786 : }
1787 :
1788 17003 : 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 16998 : *ppszResult = CPLStrdup(pszWKT);
1809 :
1810 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1811 16998 : 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 1129 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1816 1129 : if (pszPtr)
1817 : {
1818 266 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1819 266 : const int nZone = atoi(pszPtr);
1820 797 : while (*pszPtr >= '0' && *pszPtr <= '9')
1821 531 : ++pszPtr;
1822 266 : 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 16998 : proj_destroy(boundCRS);
1875 16998 : 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 97 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1920 : {
1921 97 : std::string osWKT;
1922 97 : char *pszWKT = nullptr;
1923 97 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1924 97 : osWKT = pszWKT;
1925 97 : CPLFree(pszWKT);
1926 194 : 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 872 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1947 :
1948 : {
1949 872 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1950 :
1951 872 : *ppszReturn = nullptr;
1952 :
1953 872 : 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 1304 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1970 : const char *const *papszOptions)
1971 : {
1972 1304 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1973 :
1974 1304 : *ppszReturn = nullptr;
1975 :
1976 1304 : 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 2182 : OGRErr OGRSpatialReference::exportToPROJJSON(
2005 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2006 : {
2007 4364 : TAKE_OPTIONAL_LOCK();
2008 :
2009 2182 : d->refreshProjObj();
2010 2182 : if (!d->m_pj_crs)
2011 : {
2012 0 : *ppszResult = nullptr;
2013 0 : return OGRERR_FAILURE;
2014 : }
2015 :
2016 : const char *pszPROJJSON =
2017 2182 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2018 :
2019 2182 : if (!pszPROJJSON)
2020 : {
2021 0 : *ppszResult = CPLStrdup("");
2022 0 : return OGRERR_FAILURE;
2023 : }
2024 :
2025 2182 : *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 2182 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2032 2182 : if (pszPtr)
2033 : {
2034 234 : pszPtr += strlen("\"name\": \"UTM zone ");
2035 234 : const int nZone = atoi(pszPtr);
2036 701 : while (*pszPtr >= '0' && *pszPtr <= '9')
2037 467 : ++pszPtr;
2038 234 : 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 2182 : 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 62 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2111 : const char *const *papszOptions)
2112 : {
2113 62 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2114 :
2115 62 : *ppszReturn = nullptr;
2116 :
2117 62 : 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 17709 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2152 :
2153 : {
2154 17709 : 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 17730 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2171 : CSLConstList papszOptions)
2172 :
2173 : {
2174 35460 : TAKE_OPTIONAL_LOCK();
2175 :
2176 17730 : if (!ppszInput || !*ppszInput)
2177 0 : return OGRERR_FAILURE;
2178 :
2179 17730 : 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 17730 : Clear();
2190 :
2191 17730 : bool canCache = false;
2192 17730 : auto tlsCache = OSRGetProjTLSCache();
2193 35460 : std::string osWkt;
2194 17730 : if (**ppszInput)
2195 : {
2196 17175 : osWkt = *ppszInput;
2197 17175 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2198 17175 : if (cachedObj)
2199 : {
2200 15483 : d->setPjCRS(cachedObj);
2201 : }
2202 : else
2203 : {
2204 3384 : CPLStringList aosOptions(papszOptions);
2205 1692 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2206 1692 : aosOptions.SetNameValue("STRICT", "NO");
2207 1692 : PROJ_STRING_LIST warnings = nullptr;
2208 1692 : PROJ_STRING_LIST errors = nullptr;
2209 1692 : auto ctxt = d->getPROJContext();
2210 1692 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2211 : &warnings, &errors);
2212 1692 : d->setPjCRS(pj);
2213 :
2214 1742 : for (auto iter = warnings; iter && *iter; ++iter)
2215 : {
2216 50 : d->m_wktImportWarnings.push_back(*iter);
2217 : }
2218 1931 : for (auto iter = errors; iter && *iter; ++iter)
2219 : {
2220 239 : d->m_wktImportErrors.push_back(*iter);
2221 239 : if (!d->m_pj_crs)
2222 : {
2223 37 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2224 : }
2225 : }
2226 1692 : if (warnings == nullptr && errors == nullptr)
2227 : {
2228 1410 : canCache = true;
2229 : }
2230 1692 : proj_string_list_destroy(warnings);
2231 1692 : proj_string_list_destroy(errors);
2232 : }
2233 : }
2234 17730 : if (!d->m_pj_crs)
2235 592 : return OGRERR_CORRUPT_DATA;
2236 :
2237 : // Only accept CRS objects
2238 17138 : if (!proj_is_crs(d->m_pj_crs))
2239 : {
2240 0 : Clear();
2241 0 : return OGRERR_CORRUPT_DATA;
2242 : }
2243 :
2244 17138 : if (canCache)
2245 : {
2246 1410 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2247 : }
2248 :
2249 17138 : 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 17138 : *ppszInput += strlen(*ppszInput);
2261 17138 : 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 17416 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2333 : {
2334 17416 : 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 293 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2410 :
2411 : {
2412 293 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2413 :
2414 293 : 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 580 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2441 : const char *pszNewNodeValue)
2442 :
2443 : {
2444 1160 : TAKE_OPTIONAL_LOCK();
2445 :
2446 : char **papszPathTokens =
2447 580 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2448 :
2449 580 : if (CSLCount(papszPathTokens) < 1)
2450 : {
2451 0 : CSLDestroy(papszPathTokens);
2452 0 : return OGRERR_FAILURE;
2453 : }
2454 :
2455 1013 : if (GetRoot() == nullptr ||
2456 433 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2457 : {
2458 266 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2459 115 : CSLCount(papszPathTokens) == 1)
2460 : {
2461 115 : CSLDestroy(papszPathTokens);
2462 115 : return SetProjCS(pszNewNodeValue);
2463 : }
2464 : else
2465 : {
2466 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2467 : }
2468 : }
2469 :
2470 465 : OGR_SRSNode *poNode = GetRoot();
2471 723 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2472 : {
2473 258 : int j = 0; // Used after for.
2474 :
2475 645 : for (; j < poNode->GetChildCount(); j++)
2476 : {
2477 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2478 : {
2479 198 : poNode = poNode->GetChild(j);
2480 198 : j = -1;
2481 198 : break;
2482 : }
2483 : }
2484 :
2485 258 : if (j != -1)
2486 : {
2487 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2488 60 : poNode->AddChild(poNewNode);
2489 60 : poNode = poNewNode;
2490 : }
2491 : }
2492 :
2493 465 : CSLDestroy(papszPathTokens);
2494 :
2495 465 : if (pszNewNodeValue != nullptr)
2496 : {
2497 465 : if (poNode->GetChildCount() > 0)
2498 369 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2499 : else
2500 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2501 : };
2502 465 : 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 1050 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2580 : double dfInRadians)
2581 :
2582 : {
2583 2100 : TAKE_OPTIONAL_LOCK();
2584 :
2585 1050 : d->bNormInfoSet = FALSE;
2586 :
2587 1050 : d->refreshProjObj();
2588 1050 : if (!d->m_pj_crs)
2589 0 : return OGRERR_FAILURE;
2590 1050 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2591 1050 : if (!geodCRS)
2592 0 : return OGRERR_FAILURE;
2593 1050 : proj_destroy(geodCRS);
2594 1050 : d->demoteFromBoundCRS();
2595 1050 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2596 : pszUnitsName, dfInRadians,
2597 : nullptr, nullptr));
2598 1050 : d->undoDemoteFromBoundCRS();
2599 :
2600 1050 : d->m_osAngularUnits = pszUnitsName;
2601 1050 : d->m_dfAngularUnitToRadian = dfInRadians;
2602 :
2603 1050 : 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 41 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2616 : double dfInRadians)
2617 :
2618 : {
2619 41 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2620 :
2621 41 : 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 6669 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2648 :
2649 : {
2650 13338 : TAKE_OPTIONAL_LOCK();
2651 :
2652 6669 : d->refreshProjObj();
2653 :
2654 6669 : if (!d->m_osAngularUnits.empty())
2655 : {
2656 1888 : if (ppszName != nullptr)
2657 141 : *ppszName = d->m_osAngularUnits.c_str();
2658 1888 : return d->m_dfAngularUnitToRadian;
2659 : }
2660 :
2661 : do
2662 : {
2663 4781 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2664 : {
2665 113 : break;
2666 : }
2667 :
2668 : auto geodCRS =
2669 4670 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2670 4670 : if (!geodCRS)
2671 : {
2672 0 : break;
2673 : }
2674 : auto coordSys =
2675 4670 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2676 4670 : proj_destroy(geodCRS);
2677 4670 : if (!coordSys)
2678 : {
2679 0 : break;
2680 : }
2681 4670 : 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 4668 : double dfConvFactor = 0.0;
2689 4668 : const char *pszUnitName = nullptr;
2690 4668 : 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 4668 : d->m_osAngularUnits = pszUnitName;
2699 :
2700 4668 : proj_destroy(coordSys);
2701 4668 : d->m_dfAngularUnitToRadian = dfConvFactor;
2702 : } while (false);
2703 :
2704 4781 : 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 4781 : if (ppszName != nullptr)
2711 2883 : *ppszName = d->m_osAngularUnits.c_str();
2712 4781 : 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 6557 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2864 : double dfInMeters)
2865 :
2866 : {
2867 6557 : 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 10182 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2921 : const char *pszUnitsName,
2922 : double dfInMeters,
2923 : const char *pszUnitAuthority,
2924 : const char *pszUnitCode)
2925 :
2926 : {
2927 20364 : TAKE_OPTIONAL_LOCK();
2928 :
2929 10182 : if (dfInMeters <= 0.0)
2930 0 : return OGRERR_FAILURE;
2931 :
2932 10182 : d->refreshProjObj();
2933 10182 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2934 10182 : if (pszTargetKey == nullptr)
2935 : {
2936 10182 : if (!d->m_pj_crs)
2937 0 : return OGRERR_FAILURE;
2938 :
2939 10182 : d->demoteFromBoundCRS();
2940 10182 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2941 : {
2942 14576 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2943 7288 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2944 : pszUnitAuthority, pszUnitCode, false));
2945 : }
2946 20364 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
2947 10182 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2948 : pszUnitAuthority, pszUnitCode));
2949 10182 : d->undoDemoteFromBoundCRS();
2950 :
2951 10182 : d->m_osLinearUnits = pszUnitsName;
2952 10182 : d->dfToMeter = dfInMeters;
2953 :
2954 10182 : 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 15217 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3065 :
3066 : {
3067 15217 : 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 15466 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3115 : const char **ppszName) const
3116 :
3117 : {
3118 30932 : TAKE_OPTIONAL_LOCK();
3119 :
3120 15466 : d->refreshProjObj();
3121 :
3122 15466 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3123 15466 : if (pszTargetKey == nullptr)
3124 : {
3125 : // Use cached result if available
3126 15280 : if (!d->m_osLinearUnits.empty())
3127 : {
3128 7257 : if (ppszName)
3129 6562 : *ppszName = d->m_osLinearUnits.c_str();
3130 7257 : return d->dfToMeter;
3131 : }
3132 :
3133 : while (true)
3134 : {
3135 8023 : if (d->m_pj_crs == nullptr)
3136 : {
3137 241 : break;
3138 : }
3139 :
3140 7782 : d->demoteFromBoundCRS();
3141 7782 : PJ *coordSys = nullptr;
3142 7782 : 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 7758 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3179 7758 : d->m_pj_crs);
3180 : }
3181 :
3182 7782 : d->undoDemoteFromBoundCRS();
3183 7782 : if (!coordSys)
3184 : {
3185 0 : break;
3186 : }
3187 7782 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3188 :
3189 7782 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3190 2119 : 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 7782 : int axis = 0;
3199 :
3200 7782 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3201 : csType == PJ_CS_TYPE_SPHERICAL)
3202 : {
3203 : const int axisCount =
3204 2119 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3205 :
3206 2119 : if (axisCount == 3)
3207 : {
3208 4 : axis = 2;
3209 : }
3210 : else
3211 : {
3212 2115 : proj_destroy(coordSys);
3213 2115 : break;
3214 : }
3215 : }
3216 :
3217 5667 : double dfConvFactor = 0.0;
3218 5667 : const char *pszUnitName = nullptr;
3219 5667 : 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 5667 : d->m_osLinearUnits = pszUnitName;
3228 5667 : d->dfToMeter = dfConvFactor;
3229 5667 : if (ppszName)
3230 1030 : *ppszName = d->m_osLinearUnits.c_str();
3231 :
3232 5667 : proj_destroy(coordSys);
3233 5667 : return dfConvFactor;
3234 : }
3235 :
3236 2356 : d->m_osLinearUnits = "unknown";
3237 2356 : d->dfToMeter = 1.0;
3238 :
3239 2356 : if (ppszName != nullptr)
3240 2177 : *ppszName = d->m_osLinearUnits.c_str();
3241 2356 : return 1.0;
3242 : }
3243 :
3244 186 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3245 :
3246 186 : if (ppszName != nullptr)
3247 37 : *ppszName = "unknown";
3248 :
3249 186 : if (poCS == nullptr)
3250 148 : return 1.0;
3251 :
3252 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3253 : {
3254 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3255 :
3256 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3257 : {
3258 38 : if (ppszName != nullptr)
3259 37 : *ppszName = poChild->GetChild(0)->GetValue();
3260 :
3261 38 : 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 1362 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3345 :
3346 : {
3347 2724 : TAKE_OPTIONAL_LOCK();
3348 :
3349 1362 : d->refreshProjObj();
3350 :
3351 1362 : if (!d->m_osPrimeMeridianName.empty())
3352 : {
3353 59 : if (ppszName != nullptr)
3354 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3355 59 : return d->dfFromGreenwich;
3356 : }
3357 :
3358 : while (true)
3359 : {
3360 1303 : if (!d->m_pj_crs)
3361 0 : break;
3362 :
3363 1303 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3364 1303 : if (!pm)
3365 0 : break;
3366 :
3367 1303 : d->m_osPrimeMeridianName = proj_get_name(pm);
3368 1303 : if (ppszName)
3369 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3370 1303 : double dfLongitude = 0.0;
3371 1303 : double dfConvFactor = 0.0;
3372 1303 : proj_prime_meridian_get_parameters(
3373 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3374 1303 : proj_destroy(pm);
3375 2606 : d->dfFromGreenwich =
3376 1303 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3377 1303 : 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 8269 : 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 16538 : TAKE_OPTIONAL_LOCK();
3487 :
3488 8269 : d->bNormInfoSet = FALSE;
3489 8269 : d->m_osAngularUnits.clear();
3490 8269 : d->m_dfAngularUnitToRadian = 0.0;
3491 8269 : d->m_osPrimeMeridianName.clear();
3492 8269 : 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 8269 : 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 8267 : auto cs = proj_create_ellipsoidal_2D_cs(
3511 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3512 : dfConvertToRadians);
3513 : // Prime meridian expressed in Degree
3514 8267 : auto obj = proj_create_geographic_crs(
3515 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3516 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3517 8267 : proj_destroy(cs);
3518 :
3519 12422 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3520 4155 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3521 : {
3522 4112 : d->setPjCRS(obj);
3523 : }
3524 4155 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3525 : {
3526 8310 : d->setPjCRS(
3527 4155 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3528 4155 : proj_destroy(obj);
3529 : }
3530 : else
3531 : {
3532 0 : proj_destroy(obj);
3533 : }
3534 :
3535 8267 : 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 2944 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3594 :
3595 : {
3596 5888 : TAKE_OPTIONAL_LOCK();
3597 :
3598 : /* -------------------------------------------------------------------- */
3599 : /* Check for EPSG authority numbers. */
3600 : /* -------------------------------------------------------------------- */
3601 2944 : 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 2902 : const char *pszWKT = nullptr;
3618 :
3619 2902 : if (EQUAL(pszName, "WGS84"))
3620 : {
3621 2041 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3622 : }
3623 861 : 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 757 : 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 707 : else if (EQUAL(pszName, "NAD27"))
3648 165 : 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 542 : 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 542 : else if (EQUAL(pszName, "NAD83"))
3667 538 : 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 5796 : OGRSpatialReference oSRS2;
3694 2898 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3695 2898 : if (eErr != OGRERR_NONE)
3696 0 : return eErr;
3697 :
3698 : /* -------------------------------------------------------------------- */
3699 : /* Copy over. */
3700 : /* -------------------------------------------------------------------- */
3701 2898 : 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 128 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3714 :
3715 : {
3716 128 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3717 :
3718 128 : 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 2437 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3856 : {
3857 2437 : return SET_FROM_USER_INPUT_LIMITATIONS;
3858 : }
3859 :
3860 : /************************************************************************/
3861 : /* RemoveIDFromMemberOfEnsembles() */
3862 : /************************************************************************/
3863 :
3864 : // cppcheck-suppress constParameterReference
3865 198 : 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 198 : if (obj.GetType() == CPLJSONObject::Type::Object)
3872 : {
3873 243 : for (auto &subObj : obj.GetChildren())
3874 : {
3875 191 : RemoveIDFromMemberOfEnsembles(subObj);
3876 : }
3877 : }
3878 162 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3879 162 : obj.GetName() == "members")
3880 : {
3881 51 : for (auto &subObj : obj.ToArray())
3882 : {
3883 44 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3884 : {
3885 43 : subObj.Delete("id");
3886 : }
3887 : }
3888 : }
3889 198 : }
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 13867 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3935 : {
3936 13867 : 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 17813 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
3989 : CSLConstList papszOptions)
3990 : {
3991 35626 : TAKE_OPTIONAL_LOCK();
3992 :
3993 : // Skip leading white space
3994 17815 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
3995 2 : pszDefinition++;
3996 :
3997 17813 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
3998 : {
3999 1 : pszDefinition += 6;
4000 : }
4001 :
4002 : /* -------------------------------------------------------------------- */
4003 : /* Is it a recognised syntax? */
4004 : /* -------------------------------------------------------------------- */
4005 17813 : 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 222978 : for (const char *keyword : wktKeywords)
4013 : {
4014 213168 : if (STARTS_WITH_CI(pszDefinition, keyword))
4015 : {
4016 8003 : return importFromWkt(pszDefinition);
4017 : }
4018 : }
4019 :
4020 9810 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4021 9810 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4022 : {
4023 7524 : OGRErr eStatus = OGRERR_NONE;
4024 :
4025 7524 : 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 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4031 62 : if (!pj)
4032 : {
4033 1 : return OGRERR_FAILURE;
4034 : }
4035 61 : Clear();
4036 61 : d->setPjCRS(pj);
4037 61 : return OGRERR_NONE;
4038 : }
4039 : else
4040 : {
4041 : eStatus =
4042 7462 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4043 : }
4044 :
4045 7462 : return eStatus;
4046 : }
4047 :
4048 2286 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4049 1622 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4050 1621 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4051 1563 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4052 1563 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4053 1563 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4054 723 : return importFromURN(pszDefinition);
4055 :
4056 1563 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4057 1561 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4058 1560 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4059 885 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4060 884 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4061 679 : return importFromCRSURL(pszDefinition);
4062 :
4063 884 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4064 1 : return importFromWMSAUTO(pszDefinition);
4065 :
4066 : // WMS/WCS OGC codes like OGC:CRS84.
4067 883 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4068 27 : return SetWellKnownGeogCS(pszDefinition + 4);
4069 :
4070 856 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4071 1 : return SetWellKnownGeogCS(pszDefinition);
4072 :
4073 855 : 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 855 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4087 851 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4088 : {
4089 256 : Clear();
4090 256 : return SetWellKnownGeogCS(pszDefinition);
4091 : }
4092 :
4093 : // PROJJSON
4094 599 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4095 53 : (strstr(pszDefinition, "GeodeticCRS") ||
4096 53 : 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 53 : 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 7 : CPLJSONDocument oCRSDoc;
4118 7 : if (!oCRSDoc.LoadMemory(pszDefinition))
4119 0 : return OGRERR_CORRUPT_DATA;
4120 7 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4121 7 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4122 7 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4123 : }
4124 : else
4125 : {
4126 46 : pj = proj_create(d->getPROJContext(), pszDefinition);
4127 : }
4128 53 : if (!pj)
4129 : {
4130 2 : return OGRERR_FAILURE;
4131 : }
4132 51 : Clear();
4133 51 : d->setPjCRS(pj);
4134 51 : return OGRERR_NONE;
4135 : }
4136 :
4137 546 : if (strstr(pszDefinition, "+proj") != nullptr ||
4138 215 : strstr(pszDefinition, "+init") != nullptr)
4139 331 : return importFromProj4(pszDefinition);
4140 :
4141 215 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4142 215 : 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 214 : if (EQUAL(pszDefinition, "osgb:BNG"))
4155 : {
4156 13 : return importFromEPSG(27700);
4157 : }
4158 :
4159 : // Used by German CityGML files
4160 201 : 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 201 : 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 197 : const char *pszDot = strrchr(pszDefinition, ':');
4173 197 : if (pszDot)
4174 : {
4175 109 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4176 : auto authorities =
4177 109 : proj_get_authorities_from_database(d->getPROJContext());
4178 109 : if (authorities)
4179 : {
4180 109 : std::set<std::string> aosCandidateAuthorities;
4181 236 : for (auto iter = authorities; *iter; ++iter)
4182 : {
4183 233 : if (*iter == osPrefix)
4184 : {
4185 106 : aosCandidateAuthorities.clear();
4186 106 : aosCandidateAuthorities.insert(*iter);
4187 106 : break;
4188 : }
4189 : // Deal with "IAU_2015" as authority in the list and input
4190 : // "IAU:code"
4191 127 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4192 127 : 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 254 : else if (osPrefix.find(':') != std::string::npos &&
4200 127 : osPrefix.size() == strlen(*iter) &&
4201 127 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4202 : {
4203 0 : aosCandidateAuthorities.clear();
4204 0 : aosCandidateAuthorities.insert(*iter);
4205 0 : break;
4206 : }
4207 : }
4208 :
4209 109 : proj_string_list_destroy(authorities);
4210 :
4211 109 : if (!aosCandidateAuthorities.empty())
4212 : {
4213 106 : auto obj = proj_create_from_database(
4214 : d->getPROJContext(),
4215 106 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4216 : PJ_CATEGORY_CRS, false, nullptr);
4217 106 : if (!obj)
4218 : {
4219 3 : return OGRERR_FAILURE;
4220 : }
4221 103 : Clear();
4222 103 : d->setPjCRS(obj);
4223 103 : return OGRERR_NONE;
4224 : }
4225 : }
4226 : }
4227 :
4228 : /* -------------------------------------------------------------------- */
4229 : /* Try to open it as a file. */
4230 : /* -------------------------------------------------------------------- */
4231 91 : 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 158 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4249 79 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4250 79 : if (fp == nullptr)
4251 74 : 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 260 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4311 : const char *pszDef)
4312 :
4313 : {
4314 260 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4315 :
4316 260 : 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 713 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4331 : CSLConstList papszOptions)
4332 :
4333 : {
4334 713 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4335 :
4336 713 : 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 866 : 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 866 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4489 787 : 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 784 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4570 :
4571 : {
4572 784 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4573 1484 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4574 700 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4575 : CPL_VALUE_INTEGER)
4576 : {
4577 698 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4578 : }
4579 :
4580 172 : TAKE_OPTIONAL_LOCK();
4581 :
4582 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4583 :
4584 : // PROJ 8.2.0 has support for IAU codes now.
4585 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4586 : /* -------------------------------------------------------------------- */
4587 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4588 : /* -------------------------------------------------------------------- */
4589 : const char *pszIAU = strstr(pszURN, "IAU");
4590 : if (pszIAU)
4591 : {
4592 : const char *pszCode = strchr(pszIAU, ':');
4593 : if (pszCode)
4594 : {
4595 : ++pszCode;
4596 : if (*pszCode == ':')
4597 : ++pszCode;
4598 : return importFromDict("IAU2000.wkt", pszCode);
4599 : }
4600 : }
4601 : #endif
4602 :
4603 : if (strlen(pszURN) >= 1000)
4604 : {
4605 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4606 : return OGRERR_CORRUPT_DATA;
4607 : }
4608 : auto obj = proj_create(d->getPROJContext(), pszURN);
4609 : if (!obj)
4610 : {
4611 : return OGRERR_FAILURE;
4612 : }
4613 : Clear();
4614 : d->setPjCRS(obj);
4615 : return OGRERR_NONE;
4616 : #else
4617 86 : const char *pszCur = nullptr;
4618 :
4619 86 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4620 21 : pszCur = pszURN + 16;
4621 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4622 1 : pszCur = pszURN + 20;
4623 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4624 62 : pszCur = pszURN + 18;
4625 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4626 0 : pszCur = pszURN + 16;
4627 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4628 0 : pszCur = pszURN + 20;
4629 : else
4630 : {
4631 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4632 : pszURN);
4633 2 : return OGRERR_FAILURE;
4634 : }
4635 :
4636 : /* -------------------------------------------------------------------- */
4637 : /* Clear any existing definition. */
4638 : /* -------------------------------------------------------------------- */
4639 84 : Clear();
4640 :
4641 : /* -------------------------------------------------------------------- */
4642 : /* Find code (ignoring version) out of string like: */
4643 : /* */
4644 : /* authority:[version]:code */
4645 : /* -------------------------------------------------------------------- */
4646 84 : const char *pszAuthority = pszCur;
4647 :
4648 : // skip authority
4649 403 : while (*pszCur != ':' && *pszCur)
4650 319 : pszCur++;
4651 84 : if (*pszCur == ':')
4652 84 : pszCur++;
4653 :
4654 : // skip version
4655 84 : const char *pszBeforeVersion = pszCur;
4656 380 : while (*pszCur != ':' && *pszCur)
4657 296 : pszCur++;
4658 84 : if (*pszCur == ':')
4659 56 : pszCur++;
4660 : else
4661 : // We come here in the case, the content to parse is authority:code
4662 : // (instead of authority::code) which is probably illegal according to
4663 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4664 : // for example in what is returned by GeoServer.
4665 28 : pszCur = pszBeforeVersion;
4666 :
4667 84 : const char *pszCode = pszCur;
4668 :
4669 84 : const char *pszComma = strchr(pszCur, ',');
4670 84 : if (pszComma == nullptr)
4671 83 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4672 :
4673 : // There's a second part with the vertical SRS.
4674 1 : pszCur = pszComma + 1;
4675 1 : if (!STARTS_WITH(pszCur, "crs:"))
4676 : {
4677 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4678 : pszURN);
4679 0 : return OGRERR_FAILURE;
4680 : }
4681 :
4682 1 : pszCur += 4;
4683 :
4684 1 : char *pszFirstCode = CPLStrdup(pszCode);
4685 1 : pszFirstCode[pszComma - pszCode] = '\0';
4686 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4687 1 : CPLFree(pszFirstCode);
4688 :
4689 : // Do we want to turn this into a compound definition
4690 : // with a vertical datum?
4691 1 : if (eStatus != OGRERR_NONE)
4692 0 : return eStatus;
4693 :
4694 : /* -------------------------------------------------------------------- */
4695 : /* Find code (ignoring version) out of string like: */
4696 : /* */
4697 : /* authority:[version]:code */
4698 : /* -------------------------------------------------------------------- */
4699 1 : pszAuthority = pszCur;
4700 :
4701 : // skip authority
4702 5 : while (*pszCur != ':' && *pszCur)
4703 4 : pszCur++;
4704 1 : if (*pszCur == ':')
4705 1 : pszCur++;
4706 :
4707 : // skip version
4708 1 : pszBeforeVersion = pszCur;
4709 1 : while (*pszCur != ':' && *pszCur)
4710 0 : pszCur++;
4711 1 : if (*pszCur == ':')
4712 1 : pszCur++;
4713 : else
4714 0 : pszCur = pszBeforeVersion;
4715 :
4716 1 : pszCode = pszCur;
4717 :
4718 2 : OGRSpatialReference oVertSRS;
4719 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4720 1 : if (eStatus == OGRERR_NONE)
4721 : {
4722 1 : OGRSpatialReference oHorizSRS(*this);
4723 :
4724 1 : Clear();
4725 :
4726 1 : oHorizSRS.d->refreshProjObj();
4727 1 : oVertSRS.d->refreshProjObj();
4728 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4729 0 : return OGRERR_FAILURE;
4730 :
4731 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4732 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4733 :
4734 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4735 1 : osName += " + ";
4736 1 : osName += pszVertName ? pszVertName : "";
4737 :
4738 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4739 : }
4740 :
4741 1 : return eStatus;
4742 : #endif
4743 : }
4744 :
4745 : /************************************************************************/
4746 : /* importFromCRSURL() */
4747 : /* */
4748 : /* See OGC Best Practice document 11-135 for details. */
4749 : /************************************************************************/
4750 :
4751 : /**
4752 : * \brief Initialize from OGC URL.
4753 : *
4754 : * Initializes this spatial reference from a coordinate system defined
4755 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4756 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4757 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4758 : *
4759 : * This method is also supported through SetFromUserInput() which can
4760 : * normally be used for URLs.
4761 : *
4762 : * @param pszURL the URL string.
4763 : *
4764 : * @return OGRERR_NONE on success or an error code.
4765 : */
4766 :
4767 782 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4768 :
4769 : {
4770 1564 : TAKE_OPTIONAL_LOCK();
4771 :
4772 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4773 : if (strlen(pszURL) >= 10000)
4774 : {
4775 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4776 : return OGRERR_CORRUPT_DATA;
4777 : }
4778 :
4779 : PJ *obj;
4780 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4781 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4782 : {
4783 : obj = proj_create(
4784 : d->getPROJContext(),
4785 : CPLSPrintf("IAU:%s",
4786 : pszURL +
4787 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4788 : }
4789 : else
4790 : #endif
4791 : {
4792 : obj = proj_create(d->getPROJContext(), pszURL);
4793 : }
4794 : if (!obj)
4795 : {
4796 : return OGRERR_FAILURE;
4797 : }
4798 : Clear();
4799 : d->setPjCRS(obj);
4800 : return OGRERR_NONE;
4801 : #else
4802 782 : const char *pszCur = nullptr;
4803 :
4804 782 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4805 2 : pszCur = pszURL + 26;
4806 780 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4807 1 : pszCur = pszURL + 27;
4808 779 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4809 778 : pszCur = pszURL + 30;
4810 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4811 1 : pszCur = pszURL + 31;
4812 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4813 0 : pszCur = pszURL + 23;
4814 : else
4815 : {
4816 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4817 : pszURL);
4818 0 : return OGRERR_FAILURE;
4819 : }
4820 :
4821 782 : if (*pszCur == '\0')
4822 : {
4823 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4824 0 : return OGRERR_FAILURE;
4825 : }
4826 :
4827 : /* -------------------------------------------------------------------- */
4828 : /* Clear any existing definition. */
4829 : /* -------------------------------------------------------------------- */
4830 782 : Clear();
4831 :
4832 782 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4833 : {
4834 : /* --------------------------------------------------------------------
4835 : */
4836 : /* It's a compound CRS, of the form: */
4837 : /* */
4838 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4839 : /* --------------------------------------------------------------------
4840 : */
4841 1 : pszCur += 12;
4842 :
4843 : // Extract each component CRS URL.
4844 1 : int iComponentUrl = 2;
4845 :
4846 2 : CPLString osName = "";
4847 1 : Clear();
4848 :
4849 3 : while (iComponentUrl != -1)
4850 : {
4851 2 : char searchStr[15] = {};
4852 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4853 :
4854 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
4855 :
4856 : // Figure out the next component URL.
4857 2 : char *pszComponentUrl = nullptr;
4858 :
4859 2 : if (pszUrlEnd)
4860 : {
4861 1 : size_t nLen = pszUrlEnd - pszCur;
4862 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4863 1 : strncpy(pszComponentUrl, pszCur, nLen);
4864 1 : pszComponentUrl[nLen] = '\0';
4865 :
4866 1 : ++iComponentUrl;
4867 1 : pszCur += nLen + strlen(searchStr);
4868 : }
4869 : else
4870 : {
4871 1 : if (iComponentUrl == 2)
4872 : {
4873 0 : CPLError(CE_Failure, CPLE_AppDefined,
4874 : "Compound CRS URLs must have at least two "
4875 : "component CRSs.");
4876 0 : return OGRERR_FAILURE;
4877 : }
4878 : else
4879 : {
4880 1 : pszComponentUrl = CPLStrdup(pszCur);
4881 : // no more components
4882 1 : iComponentUrl = -1;
4883 : }
4884 : }
4885 :
4886 2 : OGRSpatialReference oComponentSRS;
4887 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4888 :
4889 2 : CPLFree(pszComponentUrl);
4890 2 : pszComponentUrl = nullptr;
4891 :
4892 2 : if (eStatus == OGRERR_NONE)
4893 : {
4894 2 : if (osName.length() != 0)
4895 : {
4896 1 : osName += " + ";
4897 : }
4898 2 : osName += oComponentSRS.GetRoot()->GetValue();
4899 2 : SetNode("COMPD_CS", osName);
4900 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4901 : }
4902 : else
4903 0 : return eStatus;
4904 : }
4905 :
4906 1 : return OGRERR_NONE;
4907 : }
4908 :
4909 : /* -------------------------------------------------------------------- */
4910 : /* It's a normal CRS URL, of the form: */
4911 : /* */
4912 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
4913 : /* -------------------------------------------------------------------- */
4914 781 : ++pszCur;
4915 781 : const char *pszAuthority = pszCur;
4916 :
4917 : // skip authority
4918 103840 : while (*pszCur != '/' && *pszCur)
4919 103059 : pszCur++;
4920 781 : if (*pszCur == '/')
4921 780 : pszCur++;
4922 :
4923 : // skip version
4924 1683 : while (*pszCur != '/' && *pszCur)
4925 902 : pszCur++;
4926 781 : if (*pszCur == '/')
4927 780 : pszCur++;
4928 :
4929 781 : const char *pszCode = pszCur;
4930 :
4931 781 : return importFromURNPart(pszAuthority, pszCode, pszURL);
4932 : #endif
4933 : }
4934 :
4935 : /************************************************************************/
4936 : /* importFromWMSAUTO() */
4937 : /************************************************************************/
4938 :
4939 : /**
4940 : * \brief Initialize from WMSAUTO string.
4941 : *
4942 : * Note that the WMS 1.3 specification does not include the
4943 : * units code, while apparently earlier specs do. We try to
4944 : * guess around this.
4945 : *
4946 : * @param pszDefinition the WMSAUTO string
4947 : *
4948 : * @return OGRERR_NONE on success or an error code.
4949 : */
4950 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4951 :
4952 : {
4953 6 : TAKE_OPTIONAL_LOCK();
4954 :
4955 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4956 : if (strlen(pszDefinition) >= 10000)
4957 : {
4958 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4959 : return OGRERR_CORRUPT_DATA;
4960 : }
4961 :
4962 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
4963 : if (!obj)
4964 : {
4965 : return OGRERR_FAILURE;
4966 : }
4967 : Clear();
4968 : d->setPjCRS(obj);
4969 : return OGRERR_NONE;
4970 : #else
4971 : int nProjId, nUnitsId;
4972 3 : double dfRefLong, dfRefLat = 0.0;
4973 :
4974 : /* -------------------------------------------------------------------- */
4975 : /* Tokenize */
4976 : /* -------------------------------------------------------------------- */
4977 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4978 3 : pszDefinition += 5;
4979 :
4980 : char **papszTokens =
4981 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
4982 :
4983 3 : if (CSLCount(papszTokens) == 4)
4984 : {
4985 0 : nProjId = atoi(papszTokens[0]);
4986 0 : nUnitsId = atoi(papszTokens[1]);
4987 0 : dfRefLong = CPLAtof(papszTokens[2]);
4988 0 : dfRefLat = CPLAtof(papszTokens[3]);
4989 : }
4990 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
4991 : {
4992 0 : nProjId = atoi(papszTokens[0]);
4993 0 : nUnitsId = atoi(papszTokens[1]);
4994 0 : dfRefLong = CPLAtof(papszTokens[2]);
4995 0 : dfRefLat = 0.0;
4996 : }
4997 3 : else if (CSLCount(papszTokens) == 3)
4998 : {
4999 2 : nProjId = atoi(papszTokens[0]);
5000 2 : nUnitsId = 9001;
5001 2 : dfRefLong = CPLAtof(papszTokens[1]);
5002 2 : dfRefLat = CPLAtof(papszTokens[2]);
5003 : }
5004 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5005 : {
5006 0 : nProjId = atoi(papszTokens[0]);
5007 0 : nUnitsId = 9001;
5008 0 : dfRefLong = CPLAtof(papszTokens[1]);
5009 : }
5010 : else
5011 : {
5012 1 : CSLDestroy(papszTokens);
5013 1 : CPLError(CE_Failure, CPLE_AppDefined,
5014 : "AUTO projection has wrong number of arguments, expected\n"
5015 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5016 : "AUTO:proj_id,ref_long,ref_lat");
5017 1 : return OGRERR_FAILURE;
5018 : }
5019 :
5020 2 : CSLDestroy(papszTokens);
5021 2 : papszTokens = nullptr;
5022 :
5023 : /* -------------------------------------------------------------------- */
5024 : /* Build coordsys. */
5025 : /* -------------------------------------------------------------------- */
5026 2 : Clear();
5027 :
5028 : /* -------------------------------------------------------------------- */
5029 : /* Set WGS84. */
5030 : /* -------------------------------------------------------------------- */
5031 2 : SetWellKnownGeogCS("WGS84");
5032 :
5033 2 : switch (nProjId)
5034 : {
5035 2 : case 42001: // Auto UTM
5036 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5037 : dfRefLat >= 0.0);
5038 2 : break;
5039 :
5040 0 : case 42002: // Auto TM (strangely very UTM-like).
5041 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5042 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5043 0 : break;
5044 :
5045 0 : case 42003: // Auto Orthographic.
5046 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5047 0 : break;
5048 :
5049 0 : case 42004: // Auto Equirectangular
5050 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5051 0 : break;
5052 :
5053 0 : case 42005:
5054 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5055 0 : break;
5056 :
5057 0 : default:
5058 0 : CPLError(CE_Failure, CPLE_AppDefined,
5059 : "Unsupported projection id in importFromWMSAUTO(): %d",
5060 : nProjId);
5061 0 : return OGRERR_FAILURE;
5062 : }
5063 :
5064 : /* -------------------------------------------------------------------- */
5065 : /* Set units. */
5066 : /* -------------------------------------------------------------------- */
5067 :
5068 2 : switch (nUnitsId)
5069 : {
5070 2 : case 9001:
5071 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5072 2 : break;
5073 :
5074 0 : case 9002:
5075 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5076 0 : break;
5077 :
5078 0 : case 9003:
5079 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5080 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5081 0 : break;
5082 :
5083 0 : default:
5084 0 : CPLError(CE_Failure, CPLE_AppDefined,
5085 : "Unsupported units code (%d).", nUnitsId);
5086 0 : return OGRERR_FAILURE;
5087 : break;
5088 : }
5089 :
5090 2 : return OGRERR_NONE;
5091 : #endif
5092 : }
5093 :
5094 : /************************************************************************/
5095 : /* GetSemiMajor() */
5096 : /************************************************************************/
5097 :
5098 : /**
5099 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5100 : *
5101 : * This method does the same thing as the C function OSRGetSemiMajor().
5102 : *
5103 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5104 : * can be found.
5105 : *
5106 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5107 : */
5108 :
5109 5863 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5110 :
5111 : {
5112 11726 : TAKE_OPTIONAL_LOCK();
5113 :
5114 5863 : if (pnErr != nullptr)
5115 3180 : *pnErr = OGRERR_FAILURE;
5116 :
5117 5863 : d->refreshProjObj();
5118 5863 : if (!d->m_pj_crs)
5119 111 : return SRS_WGS84_SEMIMAJOR;
5120 :
5121 5752 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5122 5752 : if (!ellps)
5123 5 : return SRS_WGS84_SEMIMAJOR;
5124 :
5125 5747 : double dfSemiMajor = 0.0;
5126 5747 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5127 : nullptr, nullptr, nullptr);
5128 5747 : proj_destroy(ellps);
5129 :
5130 5747 : if (dfSemiMajor > 0)
5131 : {
5132 5747 : if (pnErr != nullptr)
5133 3066 : *pnErr = OGRERR_NONE;
5134 5747 : return dfSemiMajor;
5135 : }
5136 :
5137 0 : return SRS_WGS84_SEMIMAJOR;
5138 : }
5139 :
5140 : /************************************************************************/
5141 : /* OSRGetSemiMajor() */
5142 : /************************************************************************/
5143 :
5144 : /**
5145 : * \brief Get spheroid semi major axis.
5146 : *
5147 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5148 : */
5149 68 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5150 :
5151 : {
5152 68 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5153 :
5154 68 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5155 : }
5156 :
5157 : /************************************************************************/
5158 : /* GetInvFlattening() */
5159 : /************************************************************************/
5160 :
5161 : /**
5162 : * \brief Get spheroid inverse flattening.
5163 : *
5164 : * This method does the same thing as the C function OSRGetInvFlattening().
5165 : *
5166 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5167 : * can be found.
5168 : *
5169 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5170 : */
5171 :
5172 4141 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5173 :
5174 : {
5175 8282 : TAKE_OPTIONAL_LOCK();
5176 :
5177 4141 : if (pnErr != nullptr)
5178 3098 : *pnErr = OGRERR_FAILURE;
5179 :
5180 4141 : d->refreshProjObj();
5181 4141 : if (!d->m_pj_crs)
5182 111 : return SRS_WGS84_INVFLATTENING;
5183 :
5184 4030 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5185 4030 : if (!ellps)
5186 2 : return SRS_WGS84_INVFLATTENING;
5187 :
5188 4028 : double dfInvFlattening = -1.0;
5189 4028 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5190 : nullptr, &dfInvFlattening);
5191 4028 : proj_destroy(ellps);
5192 :
5193 4028 : if (dfInvFlattening >= 0.0)
5194 : {
5195 4028 : if (pnErr != nullptr)
5196 2987 : *pnErr = OGRERR_NONE;
5197 4028 : return dfInvFlattening;
5198 : }
5199 :
5200 0 : return SRS_WGS84_INVFLATTENING;
5201 : }
5202 :
5203 : /************************************************************************/
5204 : /* OSRGetInvFlattening() */
5205 : /************************************************************************/
5206 :
5207 : /**
5208 : * \brief Get spheroid inverse flattening.
5209 : *
5210 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5211 : */
5212 11 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5213 :
5214 : {
5215 11 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5216 :
5217 11 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5218 : }
5219 :
5220 : /************************************************************************/
5221 : /* GetEccentricity() */
5222 : /************************************************************************/
5223 :
5224 : /**
5225 : * \brief Get spheroid eccentricity
5226 : *
5227 : * @return eccentricity (or -1 in case of error)
5228 : * @since GDAL 2.3
5229 : */
5230 :
5231 0 : double OGRSpatialReference::GetEccentricity() const
5232 :
5233 : {
5234 0 : OGRErr eErr = OGRERR_NONE;
5235 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5236 0 : if (eErr != OGRERR_NONE)
5237 : {
5238 0 : return -1.0;
5239 : }
5240 0 : if (dfInvFlattening == 0.0)
5241 0 : return 0.0;
5242 0 : if (dfInvFlattening < 0.5)
5243 0 : return -1.0;
5244 0 : return sqrt(2.0 / dfInvFlattening -
5245 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5246 : }
5247 :
5248 : /************************************************************************/
5249 : /* GetSquaredEccentricity() */
5250 : /************************************************************************/
5251 :
5252 : /**
5253 : * \brief Get spheroid squared eccentricity
5254 : *
5255 : * @return squared eccentricity (or -1 in case of error)
5256 : * @since GDAL 2.3
5257 : */
5258 :
5259 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5260 :
5261 : {
5262 0 : OGRErr eErr = OGRERR_NONE;
5263 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5264 0 : if (eErr != OGRERR_NONE)
5265 : {
5266 0 : return -1.0;
5267 : }
5268 0 : if (dfInvFlattening == 0.0)
5269 0 : return 0.0;
5270 0 : if (dfInvFlattening < 0.5)
5271 0 : return -1.0;
5272 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5273 : }
5274 :
5275 : /************************************************************************/
5276 : /* GetSemiMinor() */
5277 : /************************************************************************/
5278 :
5279 : /**
5280 : * \brief Get spheroid semi minor axis.
5281 : *
5282 : * This method does the same thing as the C function OSRGetSemiMinor().
5283 : *
5284 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5285 : * can be found.
5286 : *
5287 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5288 : */
5289 :
5290 631 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5291 :
5292 : {
5293 631 : const double dfSemiMajor = GetSemiMajor(pnErr);
5294 631 : const double dfInvFlattening = GetInvFlattening(pnErr);
5295 :
5296 631 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5297 : }
5298 :
5299 : /************************************************************************/
5300 : /* OSRGetSemiMinor() */
5301 : /************************************************************************/
5302 :
5303 : /**
5304 : * \brief Get spheroid semi minor axis.
5305 : *
5306 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5307 : */
5308 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5309 :
5310 : {
5311 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5312 :
5313 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5314 : }
5315 :
5316 : /************************************************************************/
5317 : /* SetLocalCS() */
5318 : /************************************************************************/
5319 :
5320 : /**
5321 : * \brief Set the user visible LOCAL_CS name.
5322 : *
5323 : * This method is the same as the C function OSRSetLocalCS().
5324 : *
5325 : * This method will ensure a LOCAL_CS node is created as the root,
5326 : * and set the provided name on it. It must be used before SetLinearUnits().
5327 : *
5328 : * @param pszName the user visible name to assign. Not used as a key.
5329 : *
5330 : * @return OGRERR_NONE on success.
5331 : */
5332 :
5333 2893 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5334 :
5335 : {
5336 5786 : TAKE_OPTIONAL_LOCK();
5337 :
5338 2893 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5339 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5340 : {
5341 2893 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5342 : }
5343 : else
5344 : {
5345 0 : CPLDebug("OGR",
5346 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5347 : "It appears an incompatible object already exists.",
5348 : pszName);
5349 0 : return OGRERR_FAILURE;
5350 : }
5351 :
5352 2893 : return OGRERR_NONE;
5353 : }
5354 :
5355 : /************************************************************************/
5356 : /* OSRSetLocalCS() */
5357 : /************************************************************************/
5358 :
5359 : /**
5360 : * \brief Set the user visible LOCAL_CS name.
5361 : *
5362 : * This function is the same as OGRSpatialReference::SetLocalCS()
5363 : */
5364 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5365 :
5366 : {
5367 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5368 :
5369 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5370 : }
5371 :
5372 : /************************************************************************/
5373 : /* SetGeocCS() */
5374 : /************************************************************************/
5375 :
5376 : /**
5377 : * \brief Set the user visible GEOCCS name.
5378 : *
5379 : * This method is the same as the C function OSRSetGeocCS().
5380 :
5381 : * This method will ensure a GEOCCS node is created as the root,
5382 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5383 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5384 : * the GEOGCS.
5385 : *
5386 : * @param pszName the user visible name to assign. Not used as a key.
5387 : *
5388 : * @return OGRERR_NONE on success.
5389 : *
5390 : * @since OGR 1.9.0
5391 : */
5392 :
5393 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5394 :
5395 : {
5396 12 : TAKE_OPTIONAL_LOCK();
5397 :
5398 6 : OGRErr eErr = OGRERR_NONE;
5399 6 : d->refreshProjObj();
5400 6 : d->demoteFromBoundCRS();
5401 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5402 : {
5403 3 : d->setPjCRS(proj_create_geocentric_crs(
5404 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5405 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5406 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5407 : "Metre", 1.0));
5408 : }
5409 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5410 : {
5411 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5412 : }
5413 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5414 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5415 : {
5416 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5417 : #if PROJ_VERSION_MAJOR > 7 || \
5418 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5419 : if (datum == nullptr)
5420 : {
5421 : datum =
5422 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5423 : }
5424 : #endif
5425 1 : if (datum == nullptr)
5426 : {
5427 0 : d->undoDemoteFromBoundCRS();
5428 0 : return OGRERR_FAILURE;
5429 : }
5430 :
5431 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5432 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5433 : 0.0);
5434 1 : d->setPjCRS(pj_crs);
5435 :
5436 1 : proj_destroy(datum);
5437 : }
5438 : else
5439 : {
5440 1 : CPLDebug("OGR",
5441 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5442 : "It appears an incompatible object already exists.",
5443 : pszName);
5444 1 : eErr = OGRERR_FAILURE;
5445 : }
5446 6 : d->undoDemoteFromBoundCRS();
5447 :
5448 6 : return eErr;
5449 : }
5450 :
5451 : /************************************************************************/
5452 : /* OSRSetGeocCS() */
5453 : /************************************************************************/
5454 :
5455 : /**
5456 : * \brief Set the user visible PROJCS name.
5457 : *
5458 : * This function is the same as OGRSpatialReference::SetGeocCS()
5459 : *
5460 : * @since OGR 1.9.0
5461 : */
5462 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5463 :
5464 : {
5465 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5466 :
5467 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5468 : }
5469 :
5470 : /************************************************************************/
5471 : /* SetVertCS() */
5472 : /************************************************************************/
5473 :
5474 : /**
5475 : * \brief Set the user visible VERT_CS name.
5476 : *
5477 : * This method is the same as the C function OSRSetVertCS().
5478 :
5479 : * This method will ensure a VERT_CS node is created if needed. If the
5480 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5481 : * turned into a COMPD_CS.
5482 : *
5483 : * @param pszVertCSName the user visible name of the vertical coordinate
5484 : * system. Not used as a key.
5485 : *
5486 : * @param pszVertDatumName the user visible name of the vertical datum. It
5487 : * is helpful if this matches the EPSG name.
5488 : *
5489 : * @param nVertDatumType the OGC vertical datum type. Ignored
5490 : *
5491 : * @return OGRERR_NONE on success.
5492 : *
5493 : * @since OGR 1.9.0
5494 : */
5495 :
5496 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5497 : const char *pszVertDatumName,
5498 : int nVertDatumType)
5499 :
5500 : {
5501 1 : TAKE_OPTIONAL_LOCK();
5502 :
5503 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5504 :
5505 1 : d->refreshProjObj();
5506 :
5507 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5508 : pszVertDatumName, nullptr, 0.0);
5509 :
5510 : /* -------------------------------------------------------------------- */
5511 : /* Handle the case where we want to make a compound coordinate */
5512 : /* system. */
5513 : /* -------------------------------------------------------------------- */
5514 1 : if (IsProjected() || IsGeographic())
5515 : {
5516 1 : auto compoundCRS = proj_create_compound_crs(
5517 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5518 1 : proj_destroy(vertCRS);
5519 1 : d->setPjCRS(compoundCRS);
5520 : }
5521 : else
5522 : {
5523 0 : d->setPjCRS(vertCRS);
5524 : }
5525 2 : return OGRERR_NONE;
5526 : }
5527 :
5528 : /************************************************************************/
5529 : /* OSRSetVertCS() */
5530 : /************************************************************************/
5531 :
5532 : /**
5533 : * \brief Setup the vertical coordinate system.
5534 : *
5535 : * This function is the same as OGRSpatialReference::SetVertCS()
5536 : *
5537 : * @since OGR 1.9.0
5538 : */
5539 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5540 : const char *pszVertDatumName, int nVertDatumType)
5541 :
5542 : {
5543 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5544 :
5545 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5546 0 : nVertDatumType);
5547 : }
5548 :
5549 : /************************************************************************/
5550 : /* SetCompoundCS() */
5551 : /************************************************************************/
5552 :
5553 : /**
5554 : * \brief Setup a compound coordinate system.
5555 : *
5556 : * This method is the same as the C function OSRSetCompoundCS().
5557 :
5558 : * This method is replace the current SRS with a COMPD_CS coordinate system
5559 : * consisting of the passed in horizontal and vertical coordinate systems.
5560 : *
5561 : * @param pszName the name of the compound coordinate system.
5562 : *
5563 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5564 : *
5565 : * @param poVertSRS the vertical SRS (VERT_CS).
5566 : *
5567 : * @return OGRERR_NONE on success.
5568 : */
5569 :
5570 93 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5571 : const OGRSpatialReference *poHorizSRS,
5572 : const OGRSpatialReference *poVertSRS)
5573 :
5574 : {
5575 186 : TAKE_OPTIONAL_LOCK();
5576 :
5577 : /* -------------------------------------------------------------------- */
5578 : /* Verify these are legal horizontal and vertical coordinate */
5579 : /* systems. */
5580 : /* -------------------------------------------------------------------- */
5581 93 : if (!poVertSRS->IsVertical())
5582 : {
5583 0 : CPLError(CE_Failure, CPLE_AppDefined,
5584 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5585 0 : return OGRERR_FAILURE;
5586 : }
5587 93 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5588 : {
5589 0 : CPLError(CE_Failure, CPLE_AppDefined,
5590 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5591 : "GEOGCS.");
5592 0 : return OGRERR_FAILURE;
5593 : }
5594 :
5595 : /* -------------------------------------------------------------------- */
5596 : /* Replace with compound srs. */
5597 : /* -------------------------------------------------------------------- */
5598 93 : Clear();
5599 :
5600 93 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5601 93 : poHorizSRS->d->m_pj_crs,
5602 93 : poVertSRS->d->m_pj_crs);
5603 93 : d->setPjCRS(compoundCRS);
5604 :
5605 93 : return OGRERR_NONE;
5606 : }
5607 :
5608 : /************************************************************************/
5609 : /* OSRSetCompoundCS() */
5610 : /************************************************************************/
5611 :
5612 : /**
5613 : * \brief Setup a compound coordinate system.
5614 : *
5615 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5616 : */
5617 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5618 : OGRSpatialReferenceH hHorizSRS,
5619 : OGRSpatialReferenceH hVertSRS)
5620 :
5621 : {
5622 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5623 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5624 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5625 :
5626 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5627 16 : ToPointer(hVertSRS));
5628 : }
5629 :
5630 : /************************************************************************/
5631 : /* SetProjCS() */
5632 : /************************************************************************/
5633 :
5634 : /**
5635 : * \brief Set the user visible PROJCS name.
5636 : *
5637 : * This method is the same as the C function OSRSetProjCS().
5638 : *
5639 : * This method will ensure a PROJCS node is created as the root,
5640 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5641 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5642 : *
5643 : * @param pszName the user visible name to assign. Not used as a key.
5644 : *
5645 : * @return OGRERR_NONE on success.
5646 : */
5647 :
5648 3987 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5649 :
5650 : {
5651 3987 : TAKE_OPTIONAL_LOCK();
5652 :
5653 3987 : d->refreshProjObj();
5654 3987 : d->demoteFromBoundCRS();
5655 3987 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5656 : {
5657 481 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5658 : }
5659 : else
5660 : {
5661 3506 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5662 : nullptr, nullptr, nullptr,
5663 : nullptr, nullptr, 0, nullptr);
5664 3506 : auto cs = proj_create_cartesian_2D_cs(
5665 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5666 :
5667 3506 : auto projCRS = proj_create_projected_crs(
5668 3506 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5669 3506 : proj_destroy(dummyConv);
5670 3506 : proj_destroy(cs);
5671 :
5672 3506 : d->setPjCRS(projCRS);
5673 : }
5674 3987 : d->undoDemoteFromBoundCRS();
5675 7974 : return OGRERR_NONE;
5676 : }
5677 :
5678 : /************************************************************************/
5679 : /* OSRSetProjCS() */
5680 : /************************************************************************/
5681 :
5682 : /**
5683 : * \brief Set the user visible PROJCS name.
5684 : *
5685 : * This function is the same as OGRSpatialReference::SetProjCS()
5686 : */
5687 7 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5688 :
5689 : {
5690 7 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5691 :
5692 7 : return ToPointer(hSRS)->SetProjCS(pszName);
5693 : }
5694 :
5695 : /************************************************************************/
5696 : /* SetProjection() */
5697 : /************************************************************************/
5698 :
5699 : /**
5700 : * \brief Set a projection name.
5701 : *
5702 : * This method is the same as the C function OSRSetProjection().
5703 : *
5704 : * @param pszProjection the projection name, which should be selected from
5705 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5706 : *
5707 : * @return OGRERR_NONE on success.
5708 : */
5709 :
5710 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5711 :
5712 : {
5713 46 : TAKE_OPTIONAL_LOCK();
5714 :
5715 23 : OGR_SRSNode *poGeogCS = nullptr;
5716 :
5717 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5718 : {
5719 4 : poGeogCS = d->m_poRoot;
5720 4 : d->m_poRoot = nullptr;
5721 : }
5722 :
5723 23 : if (!GetAttrNode("PROJCS"))
5724 : {
5725 11 : SetNode("PROJCS", "unnamed");
5726 : }
5727 :
5728 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5729 23 : if (eErr != OGRERR_NONE)
5730 0 : return eErr;
5731 :
5732 23 : if (poGeogCS != nullptr)
5733 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5734 :
5735 23 : return OGRERR_NONE;
5736 : }
5737 :
5738 : /************************************************************************/
5739 : /* OSRSetProjection() */
5740 : /************************************************************************/
5741 :
5742 : /**
5743 : * \brief Set a projection name.
5744 : *
5745 : * This function is the same as OGRSpatialReference::SetProjection()
5746 : */
5747 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5748 :
5749 : {
5750 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5751 :
5752 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5753 : }
5754 :
5755 : /************************************************************************/
5756 : /* GetWKT2ProjectionMethod() */
5757 : /************************************************************************/
5758 :
5759 : /**
5760 : * \brief Returns info on the projection method, based on WKT2 naming
5761 : * conventions.
5762 : *
5763 : * The returned strings are short lived and should be considered to be
5764 : * invalidated by any further call to the GDAL API.
5765 : *
5766 : * @param[out] ppszMethodName Pointer to a string that will receive the
5767 : * projection method name.
5768 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5769 : * receive the name of the authority that defines the projection method.
5770 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5771 : * an authority.
5772 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5773 : * receive the code that defines the projection method.
5774 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5775 : * an authority.
5776 : *
5777 : * @return OGRERR_NONE on success.
5778 : */
5779 : OGRErr
5780 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5781 : const char **ppszMethodAuthName,
5782 : const char **ppszMethodCode) const
5783 : {
5784 2 : TAKE_OPTIONAL_LOCK();
5785 :
5786 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5787 1 : if (!conv)
5788 0 : return OGRERR_FAILURE;
5789 1 : const char *pszTmpMethodName = "";
5790 1 : const char *pszTmpMethodAuthName = "";
5791 1 : const char *pszTmpMethodCode = "";
5792 1 : int ret = proj_coordoperation_get_method_info(
5793 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5794 : &pszTmpMethodCode);
5795 : // "Internalize" temporary strings returned by PROJ
5796 1 : CPLAssert(pszTmpMethodName);
5797 1 : if (ppszMethodName)
5798 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5799 1 : if (ppszMethodAuthName)
5800 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5801 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5802 0 : : nullptr;
5803 1 : if (ppszMethodCode)
5804 0 : *ppszMethodCode =
5805 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5806 1 : proj_destroy(conv);
5807 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5808 : }
5809 :
5810 : /************************************************************************/
5811 : /* SetProjParm() */
5812 : /************************************************************************/
5813 :
5814 : /**
5815 : * \brief Set a projection parameter value.
5816 : *
5817 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5818 : *
5819 : * This method is the same as the C function OSRSetProjParm().
5820 : *
5821 : * Please check https://gdal.org/proj_list pages for
5822 : * legal parameter names for specific projections.
5823 : *
5824 : *
5825 : * @param pszParamName the parameter name, which should be selected from
5826 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5827 : *
5828 : * @param dfValue value to assign.
5829 : *
5830 : * @return OGRERR_NONE on success.
5831 : */
5832 :
5833 133 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5834 : double dfValue)
5835 :
5836 : {
5837 266 : TAKE_OPTIONAL_LOCK();
5838 :
5839 133 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5840 :
5841 133 : if (poPROJCS == nullptr)
5842 5 : return OGRERR_FAILURE;
5843 :
5844 128 : char szValue[64] = {'\0'};
5845 128 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5846 :
5847 : /* -------------------------------------------------------------------- */
5848 : /* Try to find existing parameter with this name. */
5849 : /* -------------------------------------------------------------------- */
5850 1040 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5851 : {
5852 953 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5853 :
5854 1256 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5855 1256 : poParam->GetChildCount() == 2 &&
5856 303 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5857 : {
5858 41 : poParam->GetChild(1)->SetValue(szValue);
5859 41 : return OGRERR_NONE;
5860 : }
5861 : }
5862 :
5863 : /* -------------------------------------------------------------------- */
5864 : /* Otherwise create a new parameter and append. */
5865 : /* -------------------------------------------------------------------- */
5866 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5867 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
5868 87 : poParam->AddChild(new OGR_SRSNode(szValue));
5869 :
5870 87 : poPROJCS->AddChild(poParam);
5871 :
5872 87 : return OGRERR_NONE;
5873 : }
5874 :
5875 : /************************************************************************/
5876 : /* OSRSetProjParm() */
5877 : /************************************************************************/
5878 :
5879 : /**
5880 : * \brief Set a projection parameter value.
5881 : *
5882 : * This function is the same as OGRSpatialReference::SetProjParm()
5883 : */
5884 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5885 : double dfValue)
5886 :
5887 : {
5888 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5889 :
5890 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5891 : }
5892 :
5893 : /************************************************************************/
5894 : /* FindProjParm() */
5895 : /************************************************************************/
5896 :
5897 : /**
5898 : * \brief Return the child index of the named projection parameter on
5899 : * its parent PROJCS node.
5900 : *
5901 : * @param pszParameter projection parameter to look for
5902 : * @param poPROJCS projection CS node to look in. If NULL is passed,
5903 : * the PROJCS node of the SpatialReference object will be searched.
5904 : *
5905 : * @return the child index of the named projection parameter. -1 on failure
5906 : */
5907 4393 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
5908 : const OGR_SRSNode *poPROJCS) const
5909 :
5910 : {
5911 8786 : TAKE_OPTIONAL_LOCK();
5912 :
5913 4393 : if (poPROJCS == nullptr)
5914 0 : poPROJCS = GetAttrNode("PROJCS");
5915 :
5916 4393 : if (poPROJCS == nullptr)
5917 0 : return -1;
5918 :
5919 : /* -------------------------------------------------------------------- */
5920 : /* Search for requested parameter. */
5921 : /* -------------------------------------------------------------------- */
5922 4393 : bool bIsWKT2 = false;
5923 28028 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5924 : {
5925 27607 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5926 :
5927 27607 : if (poParameter->GetChildCount() >= 2)
5928 : {
5929 18862 : const char *pszValue = poParameter->GetValue();
5930 31680 : if (EQUAL(pszValue, "PARAMETER") &&
5931 12818 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5932 : pszParameter))
5933 : {
5934 3972 : return iChild;
5935 : }
5936 14890 : else if (EQUAL(pszValue, "METHOD"))
5937 : {
5938 41 : bIsWKT2 = true;
5939 : }
5940 : }
5941 : }
5942 :
5943 : /* -------------------------------------------------------------------- */
5944 : /* Try similar names, for selected parameters. */
5945 : /* -------------------------------------------------------------------- */
5946 421 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5947 : {
5948 189 : if (bIsWKT2)
5949 : {
5950 8 : int iChild = FindProjParm(
5951 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5952 8 : if (iChild == -1)
5953 3 : iChild = FindProjParm(
5954 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5955 8 : return iChild;
5956 : }
5957 181 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5958 : }
5959 :
5960 232 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5961 : {
5962 31 : if (bIsWKT2)
5963 : {
5964 9 : int iChild = FindProjParm(
5965 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5966 9 : if (iChild == -1)
5967 0 : iChild = FindProjParm(
5968 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5969 9 : return iChild;
5970 : }
5971 22 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5972 22 : if (iChild == -1)
5973 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5974 22 : return iChild;
5975 : }
5976 :
5977 201 : return -1;
5978 : }
5979 :
5980 : /************************************************************************/
5981 : /* GetProjParm() */
5982 : /************************************************************************/
5983 :
5984 : /**
5985 : * \brief Fetch a projection parameter value.
5986 : *
5987 : * NOTE: This code should be modified to translate non degree angles into
5988 : * degrees based on the GEOGCS unit. This has not yet been done.
5989 : *
5990 : * This method is the same as the C function OSRGetProjParm().
5991 : *
5992 : * @param pszName the name of the parameter to fetch, from the set of
5993 : * SRS_PP codes in ogr_srs_api.h.
5994 : *
5995 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
5996 : *
5997 : * @param pnErr place to put error code on failure. Ignored if NULL.
5998 : *
5999 : * @return value of parameter.
6000 : */
6001 :
6002 4419 : double OGRSpatialReference::GetProjParm(const char *pszName,
6003 : double dfDefaultValue,
6004 : OGRErr *pnErr) const
6005 :
6006 : {
6007 8838 : TAKE_OPTIONAL_LOCK();
6008 :
6009 4419 : d->refreshProjObj();
6010 4419 : GetRoot(); // force update of d->m_bNodesWKT2
6011 :
6012 4419 : if (pnErr != nullptr)
6013 3452 : *pnErr = OGRERR_NONE;
6014 :
6015 : /* -------------------------------------------------------------------- */
6016 : /* Find the desired parameter. */
6017 : /* -------------------------------------------------------------------- */
6018 : const OGR_SRSNode *poPROJCS =
6019 4419 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6020 4419 : if (poPROJCS == nullptr)
6021 : {
6022 249 : if (pnErr != nullptr)
6023 248 : *pnErr = OGRERR_FAILURE;
6024 249 : return dfDefaultValue;
6025 : }
6026 :
6027 4170 : const int iChild = FindProjParm(pszName, poPROJCS);
6028 4170 : if (iChild == -1)
6029 : {
6030 198 : if (IsProjected() && GetAxesCount() == 3)
6031 : {
6032 3 : OGRSpatialReference *poSRSTmp = Clone();
6033 3 : poSRSTmp->DemoteTo2D(nullptr);
6034 : const double dfRet =
6035 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6036 3 : delete poSRSTmp;
6037 3 : return dfRet;
6038 : }
6039 :
6040 195 : if (pnErr != nullptr)
6041 173 : *pnErr = OGRERR_FAILURE;
6042 195 : return dfDefaultValue;
6043 : }
6044 :
6045 3972 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6046 3972 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6047 : }
6048 :
6049 : /************************************************************************/
6050 : /* OSRGetProjParm() */
6051 : /************************************************************************/
6052 :
6053 : /**
6054 : * \brief Fetch a projection parameter value.
6055 : *
6056 : * This function is the same as OGRSpatialReference::GetProjParm()
6057 : */
6058 91 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6059 : double dfDefaultValue, OGRErr *pnErr)
6060 :
6061 : {
6062 91 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6063 :
6064 91 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6065 : }
6066 :
6067 : /************************************************************************/
6068 : /* GetNormProjParm() */
6069 : /************************************************************************/
6070 :
6071 : /**
6072 : * \brief Fetch a normalized projection parameter value.
6073 : *
6074 : * This method is the same as GetProjParm() except that the value of
6075 : * the parameter is "normalized" into degrees or meters depending on
6076 : * whether it is linear or angular.
6077 : *
6078 : * This method is the same as the C function OSRGetNormProjParm().
6079 : *
6080 : * @param pszName the name of the parameter to fetch, from the set of
6081 : * SRS_PP codes in ogr_srs_api.h.
6082 : *
6083 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6084 : *
6085 : * @param pnErr place to put error code on failure. Ignored if NULL.
6086 : *
6087 : * @return value of parameter.
6088 : */
6089 :
6090 3426 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6091 : double dfDefaultValue,
6092 : OGRErr *pnErr) const
6093 :
6094 : {
6095 6852 : TAKE_OPTIONAL_LOCK();
6096 :
6097 3426 : GetNormInfo();
6098 :
6099 3426 : OGRErr nError = OGRERR_NONE;
6100 3426 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6101 3426 : if (pnErr != nullptr)
6102 0 : *pnErr = nError;
6103 :
6104 : // If we got the default just return it unadjusted.
6105 3426 : if (nError != OGRERR_NONE)
6106 421 : return dfRawResult;
6107 :
6108 3005 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6109 8 : dfRawResult *= d->dfToDegrees;
6110 :
6111 3005 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6112 5 : return dfRawResult * d->dfToMeter;
6113 :
6114 3000 : return dfRawResult;
6115 : }
6116 :
6117 : /************************************************************************/
6118 : /* OSRGetNormProjParm() */
6119 : /************************************************************************/
6120 :
6121 : /**
6122 : * \brief This function is the same as OGRSpatialReference::
6123 : *
6124 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6125 : */
6126 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6127 : double dfDefaultValue, OGRErr *pnErr)
6128 :
6129 : {
6130 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6131 :
6132 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6133 : }
6134 :
6135 : /************************************************************************/
6136 : /* SetNormProjParm() */
6137 : /************************************************************************/
6138 :
6139 : /**
6140 : * \brief Set a projection parameter with a normalized value.
6141 : *
6142 : * This method is the same as SetProjParm() except that the value of
6143 : * the parameter passed in is assumed to be in "normalized" form (decimal
6144 : * degrees for angular values, meters for linear values. The values are
6145 : * converted in a form suitable for the GEOGCS and linear units in effect.
6146 : *
6147 : * This method is the same as the C function OSRSetNormProjParm().
6148 : *
6149 : * @param pszName the parameter name, which should be selected from
6150 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6151 : *
6152 : * @param dfValue value to assign.
6153 : *
6154 : * @return OGRERR_NONE on success.
6155 : */
6156 :
6157 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6158 :
6159 : {
6160 182 : TAKE_OPTIONAL_LOCK();
6161 :
6162 91 : GetNormInfo();
6163 :
6164 91 : if (d->dfToDegrees != 0.0 &&
6165 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6166 0 : IsAngularParameter(pszName))
6167 : {
6168 0 : dfValue /= d->dfToDegrees;
6169 : }
6170 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6171 4 : IsLinearParameter(pszName))
6172 4 : dfValue /= d->dfToMeter;
6173 :
6174 182 : return SetProjParm(pszName, dfValue);
6175 : }
6176 :
6177 : /************************************************************************/
6178 : /* OSRSetNormProjParm() */
6179 : /************************************************************************/
6180 :
6181 : /**
6182 : * \brief Set a projection parameter with a normalized value.
6183 : *
6184 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6185 : */
6186 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6187 : double dfValue)
6188 :
6189 : {
6190 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6191 :
6192 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6193 : }
6194 :
6195 : /************************************************************************/
6196 : /* SetTM() */
6197 : /************************************************************************/
6198 :
6199 425 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6200 : double dfScale, double dfFalseEasting,
6201 : double dfFalseNorthing)
6202 :
6203 : {
6204 850 : TAKE_OPTIONAL_LOCK();
6205 :
6206 425 : return d->replaceConversionAndUnref(
6207 : proj_create_conversion_transverse_mercator(
6208 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6209 850 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6210 : }
6211 :
6212 : /************************************************************************/
6213 : /* OSRSetTM() */
6214 : /************************************************************************/
6215 :
6216 5 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6217 : double dfCenterLong, double dfScale, double dfFalseEasting,
6218 : double dfFalseNorthing)
6219 :
6220 : {
6221 5 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6222 :
6223 5 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6224 5 : dfFalseEasting, dfFalseNorthing);
6225 : }
6226 :
6227 : /************************************************************************/
6228 : /* SetTMVariant() */
6229 : /************************************************************************/
6230 :
6231 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6232 : double dfCenterLat,
6233 : double dfCenterLong, double dfScale,
6234 : double dfFalseEasting,
6235 : double dfFalseNorthing)
6236 :
6237 : {
6238 0 : TAKE_OPTIONAL_LOCK();
6239 :
6240 0 : SetProjection(pszVariantName);
6241 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6242 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6243 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6244 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6245 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6246 :
6247 0 : return OGRERR_NONE;
6248 : }
6249 :
6250 : /************************************************************************/
6251 : /* OSRSetTMVariant() */
6252 : /************************************************************************/
6253 :
6254 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6255 : double dfCenterLat, double dfCenterLong, double dfScale,
6256 : double dfFalseEasting, double dfFalseNorthing)
6257 :
6258 : {
6259 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6260 :
6261 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6262 : dfCenterLong, dfScale, dfFalseEasting,
6263 0 : dfFalseNorthing);
6264 : }
6265 :
6266 : /************************************************************************/
6267 : /* SetTMSO() */
6268 : /************************************************************************/
6269 :
6270 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6271 : double dfScale, double dfFalseEasting,
6272 : double dfFalseNorthing)
6273 :
6274 : {
6275 6 : TAKE_OPTIONAL_LOCK();
6276 :
6277 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6278 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6279 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6280 :
6281 3 : const char *pszName = nullptr;
6282 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6283 3 : CPLString osName = pszName ? pszName : "";
6284 :
6285 3 : d->refreshProjObj();
6286 :
6287 3 : d->demoteFromBoundCRS();
6288 :
6289 3 : auto cs = proj_create_cartesian_2D_cs(
6290 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6291 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6292 : auto projCRS =
6293 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6294 3 : d->getGeodBaseCRS(), conv, cs);
6295 3 : proj_destroy(conv);
6296 3 : proj_destroy(cs);
6297 :
6298 3 : d->setPjCRS(projCRS);
6299 :
6300 3 : d->undoDemoteFromBoundCRS();
6301 :
6302 6 : return OGRERR_NONE;
6303 : }
6304 :
6305 : /************************************************************************/
6306 : /* OSRSetTMSO() */
6307 : /************************************************************************/
6308 :
6309 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6310 : double dfCenterLong, double dfScale, double dfFalseEasting,
6311 : double dfFalseNorthing)
6312 :
6313 : {
6314 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6315 :
6316 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6317 0 : dfFalseEasting, dfFalseNorthing);
6318 : }
6319 :
6320 : /************************************************************************/
6321 : /* SetTPED() */
6322 : /************************************************************************/
6323 :
6324 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6325 : double dfLat2, double dfLong2,
6326 : double dfFalseEasting,
6327 : double dfFalseNorthing)
6328 :
6329 : {
6330 2 : TAKE_OPTIONAL_LOCK();
6331 :
6332 1 : return d->replaceConversionAndUnref(
6333 : proj_create_conversion_two_point_equidistant(
6334 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6335 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6336 : }
6337 :
6338 : /************************************************************************/
6339 : /* OSRSetTPED() */
6340 : /************************************************************************/
6341 :
6342 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6343 : double dfLat2, double dfLong2, double dfFalseEasting,
6344 : double dfFalseNorthing)
6345 :
6346 : {
6347 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6348 :
6349 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6350 0 : dfFalseEasting, dfFalseNorthing);
6351 : }
6352 :
6353 : /************************************************************************/
6354 : /* SetTMG() */
6355 : /************************************************************************/
6356 :
6357 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6358 : double dfFalseEasting,
6359 : double dfFalseNorthing)
6360 :
6361 : {
6362 0 : TAKE_OPTIONAL_LOCK();
6363 :
6364 0 : return d->replaceConversionAndUnref(
6365 : proj_create_conversion_tunisia_mapping_grid(
6366 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6367 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6368 : }
6369 :
6370 : /************************************************************************/
6371 : /* OSRSetTMG() */
6372 : /************************************************************************/
6373 :
6374 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6375 : double dfCenterLong, double dfFalseEasting,
6376 : double dfFalseNorthing)
6377 :
6378 : {
6379 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6380 :
6381 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6382 0 : dfFalseNorthing);
6383 : }
6384 :
6385 : /************************************************************************/
6386 : /* SetACEA() */
6387 : /************************************************************************/
6388 :
6389 40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6390 : double dfCenterLat, double dfCenterLong,
6391 : double dfFalseEasting,
6392 : double dfFalseNorthing)
6393 :
6394 : {
6395 80 : TAKE_OPTIONAL_LOCK();
6396 :
6397 : // Note different order of parameters. The one in PROJ is conformant with
6398 : // EPSG
6399 40 : return d->replaceConversionAndUnref(
6400 : proj_create_conversion_albers_equal_area(
6401 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6402 80 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6403 : }
6404 :
6405 : /************************************************************************/
6406 : /* OSRSetACEA() */
6407 : /************************************************************************/
6408 :
6409 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6410 : double dfCenterLat, double dfCenterLong,
6411 : double dfFalseEasting, double dfFalseNorthing)
6412 :
6413 : {
6414 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6415 :
6416 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6417 0 : dfFalseEasting, dfFalseNorthing);
6418 : }
6419 :
6420 : /************************************************************************/
6421 : /* SetAE() */
6422 : /************************************************************************/
6423 :
6424 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6425 : double dfFalseEasting, double dfFalseNorthing)
6426 :
6427 : {
6428 42 : TAKE_OPTIONAL_LOCK();
6429 :
6430 21 : return d->replaceConversionAndUnref(
6431 : proj_create_conversion_azimuthal_equidistant(
6432 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6433 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6434 : }
6435 :
6436 : /************************************************************************/
6437 : /* OSRSetAE() */
6438 : /************************************************************************/
6439 :
6440 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6441 : double dfCenterLong, double dfFalseEasting,
6442 : double dfFalseNorthing)
6443 :
6444 : {
6445 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6446 :
6447 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6448 0 : dfFalseNorthing);
6449 : }
6450 :
6451 : /************************************************************************/
6452 : /* SetBonne() */
6453 : /************************************************************************/
6454 :
6455 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6456 : double dfFalseEasting,
6457 : double dfFalseNorthing)
6458 :
6459 : {
6460 2 : TAKE_OPTIONAL_LOCK();
6461 :
6462 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6463 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6464 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6465 : }
6466 :
6467 : /************************************************************************/
6468 : /* OSRSetBonne() */
6469 : /************************************************************************/
6470 :
6471 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6472 : double dfCentralMeridian, double dfFalseEasting,
6473 : double dfFalseNorthing)
6474 :
6475 : {
6476 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6477 :
6478 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6479 0 : dfFalseNorthing);
6480 : }
6481 :
6482 : /************************************************************************/
6483 : /* SetCEA() */
6484 : /************************************************************************/
6485 :
6486 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6487 : double dfFalseEasting,
6488 : double dfFalseNorthing)
6489 :
6490 : {
6491 8 : TAKE_OPTIONAL_LOCK();
6492 :
6493 4 : return d->replaceConversionAndUnref(
6494 : proj_create_conversion_lambert_cylindrical_equal_area(
6495 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6496 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6497 : }
6498 :
6499 : /************************************************************************/
6500 : /* OSRSetCEA() */
6501 : /************************************************************************/
6502 :
6503 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6504 : double dfCentralMeridian, double dfFalseEasting,
6505 : double dfFalseNorthing)
6506 :
6507 : {
6508 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6509 :
6510 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6511 0 : dfFalseNorthing);
6512 : }
6513 :
6514 : /************************************************************************/
6515 : /* SetCS() */
6516 : /************************************************************************/
6517 :
6518 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6519 : double dfFalseEasting, double dfFalseNorthing)
6520 :
6521 : {
6522 10 : TAKE_OPTIONAL_LOCK();
6523 :
6524 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6525 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6526 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6527 : }
6528 :
6529 : /************************************************************************/
6530 : /* OSRSetCS() */
6531 : /************************************************************************/
6532 :
6533 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6534 : double dfCenterLong, double dfFalseEasting,
6535 : double dfFalseNorthing)
6536 :
6537 : {
6538 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6539 :
6540 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6541 0 : dfFalseNorthing);
6542 : }
6543 :
6544 : /************************************************************************/
6545 : /* SetEC() */
6546 : /************************************************************************/
6547 :
6548 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6549 : double dfCenterLat, double dfCenterLong,
6550 : double dfFalseEasting, double dfFalseNorthing)
6551 :
6552 : {
6553 14 : TAKE_OPTIONAL_LOCK();
6554 :
6555 : // Note: different order of arguments
6556 7 : return d->replaceConversionAndUnref(
6557 : proj_create_conversion_equidistant_conic(
6558 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6559 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6560 : }
6561 :
6562 : /************************************************************************/
6563 : /* OSRSetEC() */
6564 : /************************************************************************/
6565 :
6566 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6567 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6568 : double dfFalseNorthing)
6569 :
6570 : {
6571 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6572 :
6573 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6574 0 : dfFalseEasting, dfFalseNorthing);
6575 : }
6576 :
6577 : /************************************************************************/
6578 : /* SetEckert() */
6579 : /************************************************************************/
6580 :
6581 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6582 : double dfCentralMeridian,
6583 : double dfFalseEasting,
6584 : double dfFalseNorthing)
6585 :
6586 : {
6587 20 : TAKE_OPTIONAL_LOCK();
6588 :
6589 : PJ *conv;
6590 10 : if (nVariation == 1)
6591 : {
6592 1 : conv = proj_create_conversion_eckert_i(
6593 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6594 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6595 : }
6596 9 : else if (nVariation == 2)
6597 : {
6598 1 : conv = proj_create_conversion_eckert_ii(
6599 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6600 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6601 : }
6602 8 : else if (nVariation == 3)
6603 : {
6604 1 : conv = proj_create_conversion_eckert_iii(
6605 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6606 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6607 : }
6608 7 : else if (nVariation == 4)
6609 : {
6610 3 : conv = proj_create_conversion_eckert_iv(
6611 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6612 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6613 : }
6614 4 : else if (nVariation == 5)
6615 : {
6616 1 : conv = proj_create_conversion_eckert_v(
6617 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6618 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6619 : }
6620 3 : else if (nVariation == 6)
6621 : {
6622 3 : conv = proj_create_conversion_eckert_vi(
6623 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6624 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6625 : }
6626 : else
6627 : {
6628 0 : CPLError(CE_Failure, CPLE_AppDefined,
6629 : "Unsupported Eckert variation (%d).", nVariation);
6630 0 : return OGRERR_UNSUPPORTED_SRS;
6631 : }
6632 :
6633 10 : return d->replaceConversionAndUnref(conv);
6634 : }
6635 :
6636 : /************************************************************************/
6637 : /* OSRSetEckert() */
6638 : /************************************************************************/
6639 :
6640 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6641 : double dfCentralMeridian, double dfFalseEasting,
6642 : double dfFalseNorthing)
6643 :
6644 : {
6645 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6646 :
6647 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6648 0 : dfFalseEasting, dfFalseNorthing);
6649 : }
6650 :
6651 : /************************************************************************/
6652 : /* SetEckertIV() */
6653 : /* */
6654 : /* Deprecated */
6655 : /************************************************************************/
6656 :
6657 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6658 : double dfFalseEasting,
6659 : double dfFalseNorthing)
6660 :
6661 : {
6662 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6663 : }
6664 :
6665 : /************************************************************************/
6666 : /* OSRSetEckertIV() */
6667 : /************************************************************************/
6668 :
6669 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6670 : double dfFalseEasting, double dfFalseNorthing)
6671 :
6672 : {
6673 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6674 :
6675 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6676 0 : dfFalseNorthing);
6677 : }
6678 :
6679 : /************************************************************************/
6680 : /* SetEckertVI() */
6681 : /* */
6682 : /* Deprecated */
6683 : /************************************************************************/
6684 :
6685 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6686 : double dfFalseEasting,
6687 : double dfFalseNorthing)
6688 :
6689 : {
6690 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6691 : }
6692 :
6693 : /************************************************************************/
6694 : /* OSRSetEckertVI() */
6695 : /************************************************************************/
6696 :
6697 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6698 : double dfFalseEasting, double dfFalseNorthing)
6699 :
6700 : {
6701 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6702 :
6703 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6704 0 : dfFalseNorthing);
6705 : }
6706 :
6707 : /************************************************************************/
6708 : /* SetEquirectangular() */
6709 : /************************************************************************/
6710 :
6711 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6712 : double dfCenterLong,
6713 : double dfFalseEasting,
6714 : double dfFalseNorthing)
6715 :
6716 : {
6717 4 : TAKE_OPTIONAL_LOCK();
6718 :
6719 2 : if (dfCenterLat == 0.0)
6720 : {
6721 0 : return d->replaceConversionAndUnref(
6722 : proj_create_conversion_equidistant_cylindrical(
6723 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6724 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6725 : }
6726 :
6727 : // Non-standard extension with non-zero latitude of origin
6728 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6729 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6730 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6731 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6732 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6733 :
6734 2 : return OGRERR_NONE;
6735 : }
6736 :
6737 : /************************************************************************/
6738 : /* OSRSetEquirectangular() */
6739 : /************************************************************************/
6740 :
6741 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6742 : double dfCenterLong, double dfFalseEasting,
6743 : double dfFalseNorthing)
6744 :
6745 : {
6746 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6747 :
6748 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6749 0 : dfFalseEasting, dfFalseNorthing);
6750 : }
6751 :
6752 : /************************************************************************/
6753 : /* SetEquirectangular2() */
6754 : /* Generalized form */
6755 : /************************************************************************/
6756 :
6757 174 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6758 : double dfCenterLong,
6759 : double dfStdParallel1,
6760 : double dfFalseEasting,
6761 : double dfFalseNorthing)
6762 :
6763 : {
6764 348 : TAKE_OPTIONAL_LOCK();
6765 :
6766 174 : if (dfCenterLat == 0.0)
6767 : {
6768 169 : return d->replaceConversionAndUnref(
6769 : proj_create_conversion_equidistant_cylindrical(
6770 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6771 169 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6772 : }
6773 :
6774 : // Non-standard extension with non-zero latitude of origin
6775 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6776 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6777 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6778 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6779 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6780 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6781 :
6782 5 : return OGRERR_NONE;
6783 : }
6784 :
6785 : /************************************************************************/
6786 : /* OSRSetEquirectangular2() */
6787 : /************************************************************************/
6788 :
6789 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6790 : double dfCenterLong, double dfStdParallel1,
6791 : double dfFalseEasting, double dfFalseNorthing)
6792 :
6793 : {
6794 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6795 :
6796 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6797 : dfStdParallel1, dfFalseEasting,
6798 3 : dfFalseNorthing);
6799 : }
6800 :
6801 : /************************************************************************/
6802 : /* SetGS() */
6803 : /************************************************************************/
6804 :
6805 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6806 : double dfFalseEasting, double dfFalseNorthing)
6807 :
6808 : {
6809 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6810 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6811 5 : nullptr, 0.0, nullptr, 0.0));
6812 : }
6813 :
6814 : /************************************************************************/
6815 : /* OSRSetGS() */
6816 : /************************************************************************/
6817 :
6818 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6819 : double dfFalseEasting, double dfFalseNorthing)
6820 :
6821 : {
6822 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6823 :
6824 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6825 2 : dfFalseNorthing);
6826 : }
6827 :
6828 : /************************************************************************/
6829 : /* SetGH() */
6830 : /************************************************************************/
6831 :
6832 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6833 : double dfFalseEasting, double dfFalseNorthing)
6834 :
6835 : {
6836 0 : TAKE_OPTIONAL_LOCK();
6837 :
6838 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6839 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6840 0 : nullptr, 0.0, nullptr, 0.0));
6841 : }
6842 :
6843 : /************************************************************************/
6844 : /* OSRSetGH() */
6845 : /************************************************************************/
6846 :
6847 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6848 : double dfFalseEasting, double dfFalseNorthing)
6849 :
6850 : {
6851 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6852 :
6853 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6854 0 : dfFalseNorthing);
6855 : }
6856 :
6857 : /************************************************************************/
6858 : /* SetIGH() */
6859 : /************************************************************************/
6860 :
6861 0 : OGRErr OGRSpatialReference::SetIGH()
6862 :
6863 : {
6864 0 : TAKE_OPTIONAL_LOCK();
6865 :
6866 0 : return d->replaceConversionAndUnref(
6867 : proj_create_conversion_interrupted_goode_homolosine(
6868 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6869 : }
6870 :
6871 : /************************************************************************/
6872 : /* OSRSetIGH() */
6873 : /************************************************************************/
6874 :
6875 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6876 :
6877 : {
6878 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6879 :
6880 0 : return ToPointer(hSRS)->SetIGH();
6881 : }
6882 :
6883 : /************************************************************************/
6884 : /* SetGEOS() */
6885 : /************************************************************************/
6886 :
6887 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6888 : double dfSatelliteHeight,
6889 : double dfFalseEasting,
6890 : double dfFalseNorthing)
6891 :
6892 : {
6893 6 : TAKE_OPTIONAL_LOCK();
6894 :
6895 3 : return d->replaceConversionAndUnref(
6896 : proj_create_conversion_geostationary_satellite_sweep_y(
6897 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6898 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6899 : }
6900 :
6901 : /************************************************************************/
6902 : /* OSRSetGEOS() */
6903 : /************************************************************************/
6904 :
6905 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6906 : double dfSatelliteHeight, double dfFalseEasting,
6907 : double dfFalseNorthing)
6908 :
6909 : {
6910 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6911 :
6912 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6913 0 : dfFalseEasting, dfFalseNorthing);
6914 : }
6915 :
6916 : /************************************************************************/
6917 : /* SetGaussSchreiberTMercator() */
6918 : /************************************************************************/
6919 :
6920 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6921 : double dfCenterLong,
6922 : double dfScale,
6923 : double dfFalseEasting,
6924 : double dfFalseNorthing)
6925 :
6926 : {
6927 0 : TAKE_OPTIONAL_LOCK();
6928 :
6929 0 : return d->replaceConversionAndUnref(
6930 : proj_create_conversion_gauss_schreiber_transverse_mercator(
6931 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6932 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6933 : }
6934 :
6935 : /************************************************************************/
6936 : /* OSRSetGaussSchreiberTMercator() */
6937 : /************************************************************************/
6938 :
6939 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6940 : double dfCenterLat, double dfCenterLong,
6941 : double dfScale, double dfFalseEasting,
6942 : double dfFalseNorthing)
6943 :
6944 : {
6945 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6946 :
6947 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6948 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6949 : }
6950 :
6951 : /************************************************************************/
6952 : /* SetGnomonic() */
6953 : /************************************************************************/
6954 :
6955 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6956 : double dfFalseEasting,
6957 : double dfFalseNorthing)
6958 :
6959 : {
6960 4 : TAKE_OPTIONAL_LOCK();
6961 :
6962 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6963 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6964 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6965 : }
6966 :
6967 : /************************************************************************/
6968 : /* OSRSetGnomonic() */
6969 : /************************************************************************/
6970 :
6971 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6972 : double dfCenterLong, double dfFalseEasting,
6973 : double dfFalseNorthing)
6974 :
6975 : {
6976 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6977 :
6978 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6979 0 : dfFalseEasting, dfFalseNorthing);
6980 : }
6981 :
6982 : /************************************************************************/
6983 : /* SetHOMAC() */
6984 : /************************************************************************/
6985 :
6986 : /**
6987 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6988 : * azimuth angle.
6989 : *
6990 : * This projection corresponds to EPSG projection method 9815, also
6991 : * sometimes known as hotine oblique mercator (variant B).
6992 : *
6993 : * This method does the same thing as the C function OSRSetHOMAC().
6994 : *
6995 : * @param dfCenterLat Latitude of the projection origin.
6996 : * @param dfCenterLong Longitude of the projection origin.
6997 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6998 : * centerline.
6999 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7000 : * @param dfScale Scale factor applies to the projection origin.
7001 : * @param dfFalseEasting False easting.
7002 : * @param dfFalseNorthing False northing.
7003 : *
7004 : * @return OGRERR_NONE on success.
7005 : */
7006 :
7007 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7008 : double dfAzimuth, double dfRectToSkew,
7009 : double dfScale, double dfFalseEasting,
7010 : double dfFalseNorthing)
7011 :
7012 : {
7013 8 : TAKE_OPTIONAL_LOCK();
7014 :
7015 4 : return d->replaceConversionAndUnref(
7016 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7017 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7018 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7019 8 : 0.0, nullptr, 0.0));
7020 : }
7021 :
7022 : /************************************************************************/
7023 : /* OSRSetHOMAC() */
7024 : /************************************************************************/
7025 :
7026 : /**
7027 : * \brief Set an Oblique Mercator projection using azimuth angle.
7028 : *
7029 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7030 : */
7031 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7032 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7033 : double dfScale, double dfFalseEasting,
7034 : double dfFalseNorthing)
7035 :
7036 : {
7037 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7038 :
7039 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7040 : dfRectToSkew, dfScale, dfFalseEasting,
7041 0 : dfFalseNorthing);
7042 : }
7043 :
7044 : /************************************************************************/
7045 : /* SetHOM() */
7046 : /************************************************************************/
7047 :
7048 : /**
7049 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7050 : *
7051 : * This projection corresponds to EPSG projection method 9812, also
7052 : * sometimes known as hotine oblique mercator (variant A)..
7053 : *
7054 : * This method does the same thing as the C function OSRSetHOM().
7055 : *
7056 : * @param dfCenterLat Latitude of the projection origin.
7057 : * @param dfCenterLong Longitude of the projection origin.
7058 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7059 : * centerline.
7060 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7061 : * @param dfScale Scale factor applies to the projection origin.
7062 : * @param dfFalseEasting False easting.
7063 : * @param dfFalseNorthing False northing.
7064 : *
7065 : * @return OGRERR_NONE on success.
7066 : */
7067 :
7068 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7069 : double dfAzimuth, double dfRectToSkew,
7070 : double dfScale, double dfFalseEasting,
7071 : double dfFalseNorthing)
7072 :
7073 : {
7074 26 : TAKE_OPTIONAL_LOCK();
7075 :
7076 13 : return d->replaceConversionAndUnref(
7077 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7078 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7079 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7080 26 : 0.0, nullptr, 0.0));
7081 : }
7082 :
7083 : /************************************************************************/
7084 : /* OSRSetHOM() */
7085 : /************************************************************************/
7086 : /**
7087 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7088 : *
7089 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7090 : */
7091 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7092 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7093 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7094 :
7095 : {
7096 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7097 :
7098 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7099 : dfRectToSkew, dfScale, dfFalseEasting,
7100 0 : dfFalseNorthing);
7101 : }
7102 :
7103 : /************************************************************************/
7104 : /* SetHOM2PNO() */
7105 : /************************************************************************/
7106 :
7107 : /**
7108 : * \brief Set a Hotine Oblique Mercator projection using two points on
7109 : * projection centerline.
7110 : *
7111 : * This method does the same thing as the C function OSRSetHOM2PNO().
7112 : *
7113 : * @param dfCenterLat Latitude of the projection origin.
7114 : * @param dfLat1 Latitude of the first point on center line.
7115 : * @param dfLong1 Longitude of the first point on center line.
7116 : * @param dfLat2 Latitude of the second point on center line.
7117 : * @param dfLong2 Longitude of the second point on center line.
7118 : * @param dfScale Scale factor applies to the projection origin.
7119 : * @param dfFalseEasting False easting.
7120 : * @param dfFalseNorthing False northing.
7121 : *
7122 : * @return OGRERR_NONE on success.
7123 : */
7124 :
7125 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7126 : double dfLong1, double dfLat2,
7127 : double dfLong2, double dfScale,
7128 : double dfFalseEasting,
7129 : double dfFalseNorthing)
7130 :
7131 : {
7132 6 : TAKE_OPTIONAL_LOCK();
7133 :
7134 3 : return d->replaceConversionAndUnref(
7135 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7136 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7137 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7138 6 : 0.0));
7139 : }
7140 :
7141 : /************************************************************************/
7142 : /* OSRSetHOM2PNO() */
7143 : /************************************************************************/
7144 : /**
7145 : * \brief Set a Hotine Oblique Mercator projection using two points on
7146 : * projection centerline.
7147 : *
7148 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7149 : */
7150 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7151 : double dfLat1, double dfLong1, double dfLat2,
7152 : double dfLong2, double dfScale, double dfFalseEasting,
7153 : double dfFalseNorthing)
7154 :
7155 : {
7156 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7157 :
7158 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7159 : dfLong2, dfScale, dfFalseEasting,
7160 0 : dfFalseNorthing);
7161 : }
7162 :
7163 : /************************************************************************/
7164 : /* SetLOM() */
7165 : /************************************************************************/
7166 :
7167 : /**
7168 : * \brief Set a Laborde Oblique Mercator projection.
7169 : *
7170 : * @param dfCenterLat Latitude of the projection origin.
7171 : * @param dfCenterLong Longitude of the projection origin.
7172 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7173 : * centerline.
7174 : * @param dfScale Scale factor on the initiali line
7175 : * @param dfFalseEasting False easting.
7176 : * @param dfFalseNorthing False northing.
7177 : *
7178 : * @return OGRERR_NONE on success.
7179 : */
7180 :
7181 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7182 : double dfAzimuth, double dfScale,
7183 : double dfFalseEasting,
7184 : double dfFalseNorthing)
7185 :
7186 : {
7187 0 : TAKE_OPTIONAL_LOCK();
7188 :
7189 0 : return d->replaceConversionAndUnref(
7190 : proj_create_conversion_laborde_oblique_mercator(
7191 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7192 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7193 : }
7194 :
7195 : /************************************************************************/
7196 : /* SetIWMPolyconic() */
7197 : /************************************************************************/
7198 :
7199 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7200 : double dfCenterLong,
7201 : double dfFalseEasting,
7202 : double dfFalseNorthing)
7203 :
7204 : {
7205 0 : TAKE_OPTIONAL_LOCK();
7206 :
7207 0 : return d->replaceConversionAndUnref(
7208 : proj_create_conversion_international_map_world_polyconic(
7209 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7210 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7211 : }
7212 :
7213 : /************************************************************************/
7214 : /* OSRSetIWMPolyconic() */
7215 : /************************************************************************/
7216 :
7217 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7218 : double dfLat2, double dfCenterLong,
7219 : double dfFalseEasting, double dfFalseNorthing)
7220 :
7221 : {
7222 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7223 :
7224 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7225 0 : dfFalseEasting, dfFalseNorthing);
7226 : }
7227 :
7228 : /************************************************************************/
7229 : /* SetKrovak() */
7230 : /************************************************************************/
7231 :
7232 : /** Krovak east-north projection.
7233 : *
7234 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7235 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7236 : */
7237 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7238 : double dfAzimuth,
7239 : double dfPseudoStdParallel1,
7240 : double dfScale, double dfFalseEasting,
7241 : double dfFalseNorthing)
7242 :
7243 : {
7244 6 : TAKE_OPTIONAL_LOCK();
7245 :
7246 3 : return d->replaceConversionAndUnref(
7247 : proj_create_conversion_krovak_north_oriented(
7248 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7249 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7250 6 : nullptr, 0.0, nullptr, 0.0));
7251 : }
7252 :
7253 : /************************************************************************/
7254 : /* OSRSetKrovak() */
7255 : /************************************************************************/
7256 :
7257 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7258 : double dfCenterLong, double dfAzimuth,
7259 : double dfPseudoStdParallel1, double dfScale,
7260 : double dfFalseEasting, double dfFalseNorthing)
7261 :
7262 : {
7263 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7264 :
7265 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7266 : dfPseudoStdParallel1, dfScale,
7267 0 : dfFalseEasting, dfFalseNorthing);
7268 : }
7269 :
7270 : /************************************************************************/
7271 : /* SetLAEA() */
7272 : /************************************************************************/
7273 :
7274 16 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7275 : double dfFalseEasting,
7276 : double dfFalseNorthing)
7277 :
7278 : {
7279 32 : TAKE_OPTIONAL_LOCK();
7280 :
7281 16 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7282 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7283 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7284 :
7285 16 : const char *pszName = nullptr;
7286 16 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7287 16 : CPLString osName = pszName ? pszName : "";
7288 :
7289 16 : d->refreshProjObj();
7290 :
7291 16 : d->demoteFromBoundCRS();
7292 :
7293 16 : auto cs = proj_create_cartesian_2D_cs(
7294 : d->getPROJContext(),
7295 16 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7296 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7297 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7298 13 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7299 : : PJ_CART2D_EASTING_NORTHING,
7300 16 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7301 : auto projCRS =
7302 16 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7303 16 : d->getGeodBaseCRS(), conv, cs);
7304 16 : proj_destroy(conv);
7305 16 : proj_destroy(cs);
7306 :
7307 16 : d->setPjCRS(projCRS);
7308 :
7309 16 : d->undoDemoteFromBoundCRS();
7310 :
7311 32 : return OGRERR_NONE;
7312 : }
7313 :
7314 : /************************************************************************/
7315 : /* OSRSetLAEA() */
7316 : /************************************************************************/
7317 :
7318 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7319 : double dfCenterLong, double dfFalseEasting,
7320 : double dfFalseNorthing)
7321 :
7322 : {
7323 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7324 :
7325 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7326 0 : dfFalseNorthing);
7327 : }
7328 :
7329 : /************************************************************************/
7330 : /* SetLCC() */
7331 : /************************************************************************/
7332 :
7333 153 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7334 : double dfCenterLat, double dfCenterLong,
7335 : double dfFalseEasting,
7336 : double dfFalseNorthing)
7337 :
7338 : {
7339 306 : TAKE_OPTIONAL_LOCK();
7340 :
7341 153 : return d->replaceConversionAndUnref(
7342 : proj_create_conversion_lambert_conic_conformal_2sp(
7343 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7344 306 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7345 : }
7346 :
7347 : /************************************************************************/
7348 : /* OSRSetLCC() */
7349 : /************************************************************************/
7350 :
7351 3 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7352 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7353 : double dfFalseNorthing)
7354 :
7355 : {
7356 3 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7357 :
7358 3 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7359 3 : dfFalseEasting, dfFalseNorthing);
7360 : }
7361 :
7362 : /************************************************************************/
7363 : /* SetLCC1SP() */
7364 : /************************************************************************/
7365 :
7366 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7367 : double dfScale, double dfFalseEasting,
7368 : double dfFalseNorthing)
7369 :
7370 : {
7371 20 : TAKE_OPTIONAL_LOCK();
7372 :
7373 10 : return d->replaceConversionAndUnref(
7374 : proj_create_conversion_lambert_conic_conformal_1sp(
7375 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7376 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7377 : }
7378 :
7379 : /************************************************************************/
7380 : /* OSRSetLCC1SP() */
7381 : /************************************************************************/
7382 :
7383 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7384 : double dfCenterLong, double dfScale, double dfFalseEasting,
7385 : double dfFalseNorthing)
7386 :
7387 : {
7388 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7389 :
7390 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7391 0 : dfFalseEasting, dfFalseNorthing);
7392 : }
7393 :
7394 : /************************************************************************/
7395 : /* SetLCCB() */
7396 : /************************************************************************/
7397 :
7398 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7399 : double dfCenterLat, double dfCenterLong,
7400 : double dfFalseEasting,
7401 : double dfFalseNorthing)
7402 :
7403 : {
7404 4 : TAKE_OPTIONAL_LOCK();
7405 :
7406 2 : return d->replaceConversionAndUnref(
7407 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7408 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7409 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7410 : }
7411 :
7412 : /************************************************************************/
7413 : /* OSRSetLCCB() */
7414 : /************************************************************************/
7415 :
7416 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7417 : double dfCenterLat, double dfCenterLong,
7418 : double dfFalseEasting, double dfFalseNorthing)
7419 :
7420 : {
7421 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7422 :
7423 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7424 0 : dfFalseEasting, dfFalseNorthing);
7425 : }
7426 :
7427 : /************************************************************************/
7428 : /* SetMC() */
7429 : /************************************************************************/
7430 :
7431 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7432 : double dfFalseEasting, double dfFalseNorthing)
7433 :
7434 : {
7435 8 : TAKE_OPTIONAL_LOCK();
7436 :
7437 : (void)dfCenterLat; // ignored
7438 :
7439 4 : return d->replaceConversionAndUnref(
7440 : proj_create_conversion_miller_cylindrical(
7441 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7442 8 : nullptr, 0, nullptr, 0));
7443 : }
7444 :
7445 : /************************************************************************/
7446 : /* OSRSetMC() */
7447 : /************************************************************************/
7448 :
7449 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7450 : double dfCenterLong, double dfFalseEasting,
7451 : double dfFalseNorthing)
7452 :
7453 : {
7454 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7455 :
7456 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7457 0 : dfFalseNorthing);
7458 : }
7459 :
7460 : /************************************************************************/
7461 : /* SetMercator() */
7462 : /************************************************************************/
7463 :
7464 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7465 : double dfScale, double dfFalseEasting,
7466 : double dfFalseNorthing)
7467 :
7468 : {
7469 118 : TAKE_OPTIONAL_LOCK();
7470 :
7471 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7472 : {
7473 : // Not sure this is correct, but this is how it has been used
7474 : // historically
7475 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7476 0 : dfFalseNorthing);
7477 : }
7478 59 : return d->replaceConversionAndUnref(
7479 : proj_create_conversion_mercator_variant_a(
7480 : d->getPROJContext(),
7481 : dfCenterLat, // should be zero
7482 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7483 59 : nullptr, 0));
7484 : }
7485 :
7486 : /************************************************************************/
7487 : /* OSRSetMercator() */
7488 : /************************************************************************/
7489 :
7490 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7491 : double dfCenterLong, double dfScale,
7492 : double dfFalseEasting, double dfFalseNorthing)
7493 :
7494 : {
7495 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7496 :
7497 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7498 2 : dfFalseEasting, dfFalseNorthing);
7499 : }
7500 :
7501 : /************************************************************************/
7502 : /* SetMercator2SP() */
7503 : /************************************************************************/
7504 :
7505 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7506 : double dfCenterLong,
7507 : double dfFalseEasting,
7508 : double dfFalseNorthing)
7509 :
7510 : {
7511 31 : if (dfCenterLat == 0.0)
7512 : {
7513 30 : return d->replaceConversionAndUnref(
7514 : proj_create_conversion_mercator_variant_b(
7515 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7516 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7517 : }
7518 :
7519 1 : TAKE_OPTIONAL_LOCK();
7520 :
7521 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7522 :
7523 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7524 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7525 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7526 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7527 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7528 :
7529 1 : return OGRERR_NONE;
7530 : }
7531 :
7532 : /************************************************************************/
7533 : /* OSRSetMercator2SP() */
7534 : /************************************************************************/
7535 :
7536 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7537 : double dfCenterLat, double dfCenterLong,
7538 : double dfFalseEasting, double dfFalseNorthing)
7539 :
7540 : {
7541 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7542 :
7543 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7544 1 : dfFalseEasting, dfFalseNorthing);
7545 : }
7546 :
7547 : /************************************************************************/
7548 : /* SetMollweide() */
7549 : /************************************************************************/
7550 :
7551 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7552 : double dfFalseEasting,
7553 : double dfFalseNorthing)
7554 :
7555 : {
7556 6 : TAKE_OPTIONAL_LOCK();
7557 :
7558 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7559 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7560 6 : nullptr, 0, nullptr, 0));
7561 : }
7562 :
7563 : /************************************************************************/
7564 : /* OSRSetMollweide() */
7565 : /************************************************************************/
7566 :
7567 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7568 : double dfFalseEasting, double dfFalseNorthing)
7569 :
7570 : {
7571 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7572 :
7573 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7574 0 : dfFalseNorthing);
7575 : }
7576 :
7577 : /************************************************************************/
7578 : /* SetNZMG() */
7579 : /************************************************************************/
7580 :
7581 6 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7582 : double dfFalseEasting,
7583 : double dfFalseNorthing)
7584 :
7585 : {
7586 12 : TAKE_OPTIONAL_LOCK();
7587 :
7588 6 : return d->replaceConversionAndUnref(
7589 : proj_create_conversion_new_zealand_mapping_grid(
7590 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7591 12 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7592 : }
7593 :
7594 : /************************************************************************/
7595 : /* OSRSetNZMG() */
7596 : /************************************************************************/
7597 :
7598 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7599 : double dfCenterLong, double dfFalseEasting,
7600 : double dfFalseNorthing)
7601 :
7602 : {
7603 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7604 :
7605 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7606 0 : dfFalseNorthing);
7607 : }
7608 :
7609 : /************************************************************************/
7610 : /* SetOS() */
7611 : /************************************************************************/
7612 :
7613 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7614 : double dfScale, double dfFalseEasting,
7615 : double dfFalseNorthing)
7616 :
7617 : {
7618 12 : TAKE_OPTIONAL_LOCK();
7619 :
7620 6 : return d->replaceConversionAndUnref(
7621 : proj_create_conversion_oblique_stereographic(
7622 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7623 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7624 : }
7625 :
7626 : /************************************************************************/
7627 : /* OSRSetOS() */
7628 : /************************************************************************/
7629 :
7630 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7631 : double dfCMeridian, double dfScale, double dfFalseEasting,
7632 : double dfFalseNorthing)
7633 :
7634 : {
7635 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7636 :
7637 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7638 0 : dfFalseEasting, dfFalseNorthing);
7639 : }
7640 :
7641 : /************************************************************************/
7642 : /* SetOrthographic() */
7643 : /************************************************************************/
7644 :
7645 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7646 : double dfCenterLong,
7647 : double dfFalseEasting,
7648 : double dfFalseNorthing)
7649 :
7650 : {
7651 14 : TAKE_OPTIONAL_LOCK();
7652 :
7653 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7654 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7655 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7656 : }
7657 :
7658 : /************************************************************************/
7659 : /* OSRSetOrthographic() */
7660 : /************************************************************************/
7661 :
7662 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7663 : double dfCenterLong, double dfFalseEasting,
7664 : double dfFalseNorthing)
7665 :
7666 : {
7667 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7668 :
7669 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7670 1 : dfFalseEasting, dfFalseNorthing);
7671 : }
7672 :
7673 : /************************************************************************/
7674 : /* SetPolyconic() */
7675 : /************************************************************************/
7676 :
7677 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7678 : double dfCenterLong,
7679 : double dfFalseEasting,
7680 : double dfFalseNorthing)
7681 :
7682 : {
7683 14 : TAKE_OPTIONAL_LOCK();
7684 :
7685 : // note: it seems that by some definitions this should include a
7686 : // scale_factor parameter.
7687 7 : return d->replaceConversionAndUnref(
7688 : proj_create_conversion_american_polyconic(
7689 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7690 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7691 : }
7692 :
7693 : /************************************************************************/
7694 : /* OSRSetPolyconic() */
7695 : /************************************************************************/
7696 :
7697 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7698 : double dfCenterLong, double dfFalseEasting,
7699 : double dfFalseNorthing)
7700 :
7701 : {
7702 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7703 :
7704 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7705 0 : dfFalseEasting, dfFalseNorthing);
7706 : }
7707 :
7708 : /************************************************************************/
7709 : /* SetPS() */
7710 : /************************************************************************/
7711 :
7712 : /** Sets a Polar Stereographic projection.
7713 : *
7714 : * Two variants are possible:
7715 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7716 : * interpreted as the latitude of origin, combined with the scale factor
7717 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7718 : * is interpreted as the latitude of true scale. In that situation, dfScale
7719 : * must be set to 1 (it is ignored in the projection parameters)
7720 : */
7721 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7722 : double dfScale, double dfFalseEasting,
7723 : double dfFalseNorthing)
7724 :
7725 : {
7726 60 : TAKE_OPTIONAL_LOCK();
7727 :
7728 : PJ *conv;
7729 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7730 : {
7731 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7732 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7733 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7734 : }
7735 : else
7736 : {
7737 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7738 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7739 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7740 : }
7741 :
7742 30 : const char *pszName = nullptr;
7743 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7744 30 : CPLString osName = pszName ? pszName : "";
7745 :
7746 30 : d->refreshProjObj();
7747 :
7748 30 : d->demoteFromBoundCRS();
7749 :
7750 30 : auto cs = proj_create_cartesian_2D_cs(
7751 : d->getPROJContext(),
7752 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7753 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7754 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7755 : auto projCRS =
7756 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7757 30 : d->getGeodBaseCRS(), conv, cs);
7758 30 : proj_destroy(conv);
7759 30 : proj_destroy(cs);
7760 :
7761 30 : d->setPjCRS(projCRS);
7762 :
7763 30 : d->undoDemoteFromBoundCRS();
7764 :
7765 60 : return OGRERR_NONE;
7766 : }
7767 :
7768 : /************************************************************************/
7769 : /* OSRSetPS() */
7770 : /************************************************************************/
7771 :
7772 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7773 : double dfCenterLong, double dfScale, double dfFalseEasting,
7774 : double dfFalseNorthing)
7775 :
7776 : {
7777 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7778 :
7779 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7780 1 : dfFalseEasting, dfFalseNorthing);
7781 : }
7782 :
7783 : /************************************************************************/
7784 : /* SetRobinson() */
7785 : /************************************************************************/
7786 :
7787 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7788 : double dfFalseEasting,
7789 : double dfFalseNorthing)
7790 :
7791 : {
7792 8 : TAKE_OPTIONAL_LOCK();
7793 :
7794 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7795 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7796 8 : nullptr, 0, nullptr, 0));
7797 : }
7798 :
7799 : /************************************************************************/
7800 : /* OSRSetRobinson() */
7801 : /************************************************************************/
7802 :
7803 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7804 : double dfFalseEasting, double dfFalseNorthing)
7805 :
7806 : {
7807 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7808 :
7809 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7810 0 : dfFalseNorthing);
7811 : }
7812 :
7813 : /************************************************************************/
7814 : /* SetSinusoidal() */
7815 : /************************************************************************/
7816 :
7817 33 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7818 : double dfFalseEasting,
7819 : double dfFalseNorthing)
7820 :
7821 : {
7822 66 : TAKE_OPTIONAL_LOCK();
7823 :
7824 33 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7825 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7826 66 : nullptr, 0, nullptr, 0));
7827 : }
7828 :
7829 : /************************************************************************/
7830 : /* OSRSetSinusoidal() */
7831 : /************************************************************************/
7832 :
7833 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7834 : double dfFalseEasting, double dfFalseNorthing)
7835 :
7836 : {
7837 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7838 :
7839 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7840 1 : dfFalseNorthing);
7841 : }
7842 :
7843 : /************************************************************************/
7844 : /* SetStereographic() */
7845 : /************************************************************************/
7846 :
7847 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7848 : double dfCMeridian, double dfScale,
7849 : double dfFalseEasting,
7850 : double dfFalseNorthing)
7851 :
7852 : {
7853 4 : TAKE_OPTIONAL_LOCK();
7854 :
7855 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7856 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7857 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7858 : }
7859 :
7860 : /************************************************************************/
7861 : /* OSRSetStereographic() */
7862 : /************************************************************************/
7863 :
7864 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7865 : double dfCMeridian, double dfScale,
7866 : double dfFalseEasting, double dfFalseNorthing)
7867 :
7868 : {
7869 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7870 :
7871 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7872 0 : dfFalseEasting, dfFalseNorthing);
7873 : }
7874 :
7875 : /************************************************************************/
7876 : /* SetSOC() */
7877 : /* */
7878 : /* NOTE: This definition isn't really used in practice any more */
7879 : /* and should be considered deprecated. It seems that swiss */
7880 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
7881 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7882 : /* EPSG:2056 and Bug 423. */
7883 : /************************************************************************/
7884 :
7885 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7886 : double dfCentralMeridian,
7887 : double dfFalseEasting,
7888 : double dfFalseNorthing)
7889 :
7890 : {
7891 4 : TAKE_OPTIONAL_LOCK();
7892 :
7893 2 : return d->replaceConversionAndUnref(
7894 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7895 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7896 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7897 4 : 0.0));
7898 : #if 0
7899 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7900 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7901 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7902 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7903 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7904 :
7905 : return OGRERR_NONE;
7906 : #endif
7907 : }
7908 :
7909 : /************************************************************************/
7910 : /* OSRSetSOC() */
7911 : /************************************************************************/
7912 :
7913 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7914 : double dfCentralMeridian, double dfFalseEasting,
7915 : double dfFalseNorthing)
7916 :
7917 : {
7918 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7919 :
7920 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7921 0 : dfFalseEasting, dfFalseNorthing);
7922 : }
7923 :
7924 : /************************************************************************/
7925 : /* SetVDG() */
7926 : /************************************************************************/
7927 :
7928 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7929 : double dfFalseNorthing)
7930 :
7931 : {
7932 4 : TAKE_OPTIONAL_LOCK();
7933 :
7934 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7935 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7936 4 : nullptr, 0, nullptr, 0));
7937 : }
7938 :
7939 : /************************************************************************/
7940 : /* OSRSetVDG() */
7941 : /************************************************************************/
7942 :
7943 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7944 : double dfFalseEasting, double dfFalseNorthing)
7945 :
7946 : {
7947 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7948 :
7949 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7950 0 : dfFalseNorthing);
7951 : }
7952 :
7953 : /************************************************************************/
7954 : /* SetUTM() */
7955 : /************************************************************************/
7956 :
7957 : /**
7958 : * \brief Set UTM projection definition.
7959 : *
7960 : * This will generate a projection definition with the full set of
7961 : * transverse mercator projection parameters for the given UTM zone.
7962 : * If no PROJCS[] description is set yet, one will be set to look
7963 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7964 : *
7965 : * This method is the same as the C function OSRSetUTM().
7966 : *
7967 : * @param nZone UTM zone.
7968 : *
7969 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7970 : * hemisphere.
7971 : *
7972 : * @return OGRERR_NONE on success.
7973 : */
7974 :
7975 330 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7976 :
7977 : {
7978 660 : TAKE_OPTIONAL_LOCK();
7979 :
7980 330 : if (nZone < 0 || nZone > 60)
7981 : {
7982 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7983 0 : return OGRERR_FAILURE;
7984 : }
7985 :
7986 330 : return d->replaceConversionAndUnref(
7987 330 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7988 : }
7989 :
7990 : /************************************************************************/
7991 : /* OSRSetUTM() */
7992 : /************************************************************************/
7993 :
7994 : /**
7995 : * \brief Set UTM projection definition.
7996 : *
7997 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
7998 : */
7999 20 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8000 :
8001 : {
8002 20 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8003 :
8004 20 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8005 : }
8006 :
8007 : /************************************************************************/
8008 : /* GetUTMZone() */
8009 : /* */
8010 : /* Returns zero if it isn't UTM. */
8011 : /************************************************************************/
8012 :
8013 : /**
8014 : * \brief Get utm zone information.
8015 : *
8016 : * This is the same as the C function OSRGetUTMZone().
8017 : *
8018 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8019 : * zone which is negative in the southern hemisphere instead of having the
8020 : * pbNorth flag used in the C and C++ interface.
8021 : *
8022 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8023 : * FALSE if southern.
8024 : *
8025 : * @return UTM zone number or zero if this isn't a UTM definition.
8026 : */
8027 :
8028 621 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8029 :
8030 : {
8031 1242 : TAKE_OPTIONAL_LOCK();
8032 :
8033 621 : if (IsProjected() && GetAxesCount() == 3)
8034 : {
8035 1 : OGRSpatialReference *poSRSTmp = Clone();
8036 1 : poSRSTmp->DemoteTo2D(nullptr);
8037 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8038 1 : delete poSRSTmp;
8039 1 : return nZone;
8040 : }
8041 :
8042 620 : const char *pszProjection = GetAttrValue("PROJECTION");
8043 :
8044 620 : if (pszProjection == nullptr ||
8045 516 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8046 296 : return 0;
8047 :
8048 324 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8049 5 : return 0;
8050 :
8051 319 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8052 15 : return 0;
8053 :
8054 304 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8055 3 : return 0;
8056 :
8057 301 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8058 :
8059 301 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8060 0 : return 0;
8061 :
8062 301 : if (pbNorth != nullptr)
8063 238 : *pbNorth = (dfFalseNorthing == 0);
8064 :
8065 : const double dfCentralMeridian =
8066 301 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8067 301 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8068 :
8069 602 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8070 903 : std::isnan(dfZone) ||
8071 301 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8072 0 : return 0;
8073 :
8074 301 : return static_cast<int>(dfZone);
8075 : }
8076 :
8077 : /************************************************************************/
8078 : /* OSRGetUTMZone() */
8079 : /************************************************************************/
8080 :
8081 : /**
8082 : * \brief Get utm zone information.
8083 : *
8084 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8085 : */
8086 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8087 :
8088 : {
8089 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8090 :
8091 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8092 : }
8093 :
8094 : /************************************************************************/
8095 : /* SetWagner() */
8096 : /************************************************************************/
8097 :
8098 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8099 : double dfCenterLat, double dfFalseEasting,
8100 : double dfFalseNorthing)
8101 :
8102 : {
8103 0 : TAKE_OPTIONAL_LOCK();
8104 :
8105 : PJ *conv;
8106 0 : if (nVariation == 1)
8107 : {
8108 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8109 : dfFalseEasting, dfFalseNorthing,
8110 : nullptr, 0.0, nullptr, 0.0);
8111 : }
8112 0 : else if (nVariation == 2)
8113 : {
8114 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8115 : dfFalseEasting, dfFalseNorthing,
8116 : nullptr, 0.0, nullptr, 0.0);
8117 : }
8118 0 : else if (nVariation == 3)
8119 : {
8120 0 : conv = proj_create_conversion_wagner_iii(
8121 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8122 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8123 : }
8124 0 : else if (nVariation == 4)
8125 : {
8126 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8127 : dfFalseEasting, dfFalseNorthing,
8128 : nullptr, 0.0, nullptr, 0.0);
8129 : }
8130 0 : else if (nVariation == 5)
8131 : {
8132 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8133 : dfFalseEasting, dfFalseNorthing,
8134 : nullptr, 0.0, nullptr, 0.0);
8135 : }
8136 0 : else if (nVariation == 6)
8137 : {
8138 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8139 : dfFalseEasting, dfFalseNorthing,
8140 : nullptr, 0.0, nullptr, 0.0);
8141 : }
8142 0 : else if (nVariation == 7)
8143 : {
8144 0 : conv = proj_create_conversion_wagner_vii(
8145 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8146 : 0.0, nullptr, 0.0);
8147 : }
8148 : else
8149 : {
8150 0 : CPLError(CE_Failure, CPLE_AppDefined,
8151 : "Unsupported Wagner variation (%d).", nVariation);
8152 0 : return OGRERR_UNSUPPORTED_SRS;
8153 : }
8154 :
8155 0 : return d->replaceConversionAndUnref(conv);
8156 : }
8157 :
8158 : /************************************************************************/
8159 : /* OSRSetWagner() */
8160 : /************************************************************************/
8161 :
8162 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8163 : double dfCenterLat, double dfFalseEasting,
8164 : double dfFalseNorthing)
8165 :
8166 : {
8167 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8168 :
8169 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8170 0 : dfFalseNorthing);
8171 : }
8172 :
8173 : /************************************************************************/
8174 : /* SetQSC() */
8175 : /************************************************************************/
8176 :
8177 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8178 : {
8179 0 : TAKE_OPTIONAL_LOCK();
8180 :
8181 0 : return d->replaceConversionAndUnref(
8182 : proj_create_conversion_quadrilateralized_spherical_cube(
8183 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8184 0 : 0, nullptr, 0));
8185 : }
8186 :
8187 : /************************************************************************/
8188 : /* OSRSetQSC() */
8189 : /************************************************************************/
8190 :
8191 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8192 : double dfCenterLong)
8193 :
8194 : {
8195 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8196 :
8197 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8198 : }
8199 :
8200 : /************************************************************************/
8201 : /* SetSCH() */
8202 : /************************************************************************/
8203 :
8204 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8205 : double dfPegHeading, double dfPegHgt)
8206 :
8207 : {
8208 0 : TAKE_OPTIONAL_LOCK();
8209 :
8210 0 : return d->replaceConversionAndUnref(
8211 : proj_create_conversion_spherical_cross_track_height(
8212 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8213 0 : nullptr, 0, nullptr, 0));
8214 : }
8215 :
8216 : /************************************************************************/
8217 : /* OSRSetSCH() */
8218 : /************************************************************************/
8219 :
8220 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8221 : double dfPegHeading, double dfPegHgt)
8222 :
8223 : {
8224 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8225 :
8226 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8227 : }
8228 :
8229 : /************************************************************************/
8230 : /* SetVerticalPerspective() */
8231 : /************************************************************************/
8232 :
8233 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8234 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8235 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8236 : {
8237 6 : TAKE_OPTIONAL_LOCK();
8238 :
8239 3 : return d->replaceConversionAndUnref(
8240 : proj_create_conversion_vertical_perspective(
8241 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8242 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8243 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8244 : }
8245 :
8246 : /************************************************************************/
8247 : /* OSRSetVerticalPerspective() */
8248 : /************************************************************************/
8249 :
8250 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8251 : double dfTopoOriginLat, double dfTopoOriginLon,
8252 : double dfTopoOriginHeight,
8253 : double dfViewPointHeight,
8254 : double dfFalseEasting, double dfFalseNorthing)
8255 :
8256 : {
8257 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8258 :
8259 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8260 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8261 1 : dfFalseEasting, dfFalseNorthing);
8262 : }
8263 :
8264 : /************************************************************************/
8265 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8266 : /************************************************************************/
8267 :
8268 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8269 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8270 : double dfAxisRotation)
8271 : {
8272 4 : TAKE_OPTIONAL_LOCK();
8273 :
8274 2 : d->refreshProjObj();
8275 2 : if (!d->m_pj_crs)
8276 0 : return OGRERR_FAILURE;
8277 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8278 0 : return OGRERR_FAILURE;
8279 2 : auto ctxt = d->getPROJContext();
8280 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8281 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8282 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8283 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8284 2 : d->m_pj_crs, conv, cs));
8285 2 : proj_destroy(conv);
8286 2 : proj_destroy(cs);
8287 2 : return OGRERR_NONE;
8288 : }
8289 :
8290 : /************************************************************************/
8291 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8292 : /************************************************************************/
8293 :
8294 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8295 : const char *pszCRSName, double dfGridNorthPoleLat,
8296 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8297 : {
8298 3 : TAKE_OPTIONAL_LOCK();
8299 :
8300 : #if PROJ_VERSION_MAJOR > 8 || \
8301 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8302 : d->refreshProjObj();
8303 : if (!d->m_pj_crs)
8304 : return OGRERR_FAILURE;
8305 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8306 : return OGRERR_FAILURE;
8307 : auto ctxt = d->getPROJContext();
8308 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8309 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8310 : nullptr, 0);
8311 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8312 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8313 : d->m_pj_crs, conv, cs));
8314 : proj_destroy(conv);
8315 : proj_destroy(cs);
8316 : return OGRERR_NONE;
8317 : #else
8318 : (void)pszCRSName;
8319 3 : SetProjection("Rotated_pole");
8320 3 : SetExtension(
8321 : "PROJCS", "PROJ4",
8322 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8323 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8324 : "+to_meter=0.0174532925199433 "
8325 : "+wktext",
8326 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8327 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8328 : GetSemiMinor(nullptr)));
8329 6 : return OGRERR_NONE;
8330 : #endif
8331 : }
8332 :
8333 : /************************************************************************/
8334 : /* SetAuthority() */
8335 : /************************************************************************/
8336 :
8337 : /**
8338 : * \brief Set the authority for a node.
8339 : *
8340 : * This method is the same as the C function OSRSetAuthority().
8341 : *
8342 : * @param pszTargetKey the partial or complete path to the node to
8343 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8344 : *
8345 : * @param pszAuthority authority name, such as "EPSG".
8346 : *
8347 : * @param nCode code for value with this authority.
8348 : *
8349 : * @return OGRERR_NONE on success.
8350 : */
8351 :
8352 10199 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8353 : const char *pszAuthority, int nCode)
8354 :
8355 : {
8356 20398 : TAKE_OPTIONAL_LOCK();
8357 :
8358 10199 : d->refreshProjObj();
8359 10199 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8360 :
8361 10199 : if (pszTargetKey == nullptr)
8362 : {
8363 258 : if (!d->m_pj_crs)
8364 0 : return OGRERR_FAILURE;
8365 258 : CPLString osCode;
8366 258 : osCode.Printf("%d", nCode);
8367 258 : d->demoteFromBoundCRS();
8368 258 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8369 : pszAuthority, osCode.c_str()));
8370 258 : d->undoDemoteFromBoundCRS();
8371 258 : return OGRERR_NONE;
8372 : }
8373 :
8374 9941 : d->demoteFromBoundCRS();
8375 9941 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8376 : {
8377 3286 : CPLString osCode;
8378 3286 : osCode.Printf("%d", nCode);
8379 : auto newGeogCRS =
8380 3286 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8381 : pszAuthority, osCode.c_str());
8382 :
8383 : auto conv =
8384 3286 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8385 :
8386 3286 : auto projCRS = proj_create_projected_crs(
8387 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8388 3286 : d->getProjCRSCoordSys());
8389 :
8390 : // Preserve existing id on the PROJCRS
8391 3286 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8392 3286 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8393 3286 : if (pszProjCRSAuthName && pszProjCRSCode)
8394 : {
8395 : auto projCRSWithId =
8396 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8397 : pszProjCRSCode);
8398 0 : proj_destroy(projCRS);
8399 0 : projCRS = projCRSWithId;
8400 : }
8401 :
8402 3286 : proj_destroy(newGeogCRS);
8403 3286 : proj_destroy(conv);
8404 :
8405 3286 : d->setPjCRS(projCRS);
8406 3286 : d->undoDemoteFromBoundCRS();
8407 3286 : return OGRERR_NONE;
8408 : }
8409 6655 : d->undoDemoteFromBoundCRS();
8410 :
8411 : /* -------------------------------------------------------------------- */
8412 : /* Find the node below which the authority should be put. */
8413 : /* -------------------------------------------------------------------- */
8414 6655 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8415 :
8416 6655 : if (poNode == nullptr)
8417 0 : return OGRERR_FAILURE;
8418 :
8419 : /* -------------------------------------------------------------------- */
8420 : /* If there is an existing AUTHORITY child blow it away before */
8421 : /* trying to set a new one. */
8422 : /* -------------------------------------------------------------------- */
8423 6655 : int iOldChild = poNode->FindChild("AUTHORITY");
8424 6655 : if (iOldChild != -1)
8425 5 : poNode->DestroyChild(iOldChild);
8426 :
8427 : /* -------------------------------------------------------------------- */
8428 : /* Create a new authority node. */
8429 : /* -------------------------------------------------------------------- */
8430 6655 : char szCode[32] = {};
8431 :
8432 6655 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8433 :
8434 6655 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8435 6655 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8436 6655 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8437 :
8438 6655 : poNode->AddChild(poAuthNode);
8439 :
8440 6655 : return OGRERR_NONE;
8441 : }
8442 :
8443 : /************************************************************************/
8444 : /* OSRSetAuthority() */
8445 : /************************************************************************/
8446 :
8447 : /**
8448 : * \brief Set the authority for a node.
8449 : *
8450 : * This function is the same as OGRSpatialReference::SetAuthority().
8451 : */
8452 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8453 : const char *pszAuthority, int nCode)
8454 :
8455 : {
8456 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8457 :
8458 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8459 : }
8460 :
8461 : /************************************************************************/
8462 : /* GetAuthorityCode() */
8463 : /************************************************************************/
8464 :
8465 : /**
8466 : * \brief Get the authority code for a node.
8467 : *
8468 : * This method is used to query an AUTHORITY[] node from within the
8469 : * WKT tree, and fetch the code value.
8470 : *
8471 : * While in theory values may be non-numeric, for the EPSG authority all
8472 : * code values should be integral.
8473 : *
8474 : * This method is the same as the C function OSRGetAuthorityCode().
8475 : *
8476 : * @param pszTargetKey the partial or complete path to the node to
8477 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8478 : * search for an authority node on the root element.
8479 : *
8480 : * @return value code from authority node, or NULL on failure. The value
8481 : * returned is internal and should not be freed or modified.
8482 : */
8483 :
8484 : const char *
8485 24995 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8486 :
8487 : {
8488 49990 : TAKE_OPTIONAL_LOCK();
8489 :
8490 24995 : d->refreshProjObj();
8491 24995 : const char *pszInputTargetKey = pszTargetKey;
8492 24995 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8493 24995 : if (pszTargetKey == nullptr)
8494 : {
8495 17847 : if (!d->m_pj_crs)
8496 : {
8497 13 : return nullptr;
8498 : }
8499 17834 : d->demoteFromBoundCRS();
8500 17834 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8501 17834 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8502 : {
8503 1065 : auto ctxt = d->getPROJContext();
8504 1065 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8505 1065 : if (cs)
8506 : {
8507 1065 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8508 1065 : proj_destroy(cs);
8509 1065 : if (axisCount == 3)
8510 : {
8511 : // This might come from a COMPD_CS with a VERT_DATUM type =
8512 : // 2002 in which case, using the WKT1 representation will
8513 : // enable us to recover the EPSG code.
8514 14 : pszTargetKey = pszInputTargetKey;
8515 : }
8516 : }
8517 : }
8518 17834 : d->undoDemoteFromBoundCRS();
8519 17834 : if (ret != nullptr || pszTargetKey == nullptr)
8520 : {
8521 17834 : return ret;
8522 : }
8523 : }
8524 :
8525 : // Special key for that context
8526 7152 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8527 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8528 : {
8529 4 : auto ctxt = d->getPROJContext();
8530 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8531 4 : if (crs)
8532 : {
8533 4 : const char *ret = proj_get_id_code(crs, 0);
8534 4 : if (ret)
8535 4 : ret = CPLSPrintf("%s", ret);
8536 4 : proj_destroy(crs);
8537 4 : return ret;
8538 : }
8539 : }
8540 7148 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8541 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8542 : {
8543 4 : auto ctxt = d->getPROJContext();
8544 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8545 4 : if (crs)
8546 : {
8547 4 : const char *ret = proj_get_id_code(crs, 0);
8548 4 : if (ret)
8549 4 : ret = CPLSPrintf("%s", ret);
8550 4 : proj_destroy(crs);
8551 4 : return ret;
8552 : }
8553 : }
8554 :
8555 : /* -------------------------------------------------------------------- */
8556 : /* Find the node below which the authority should be put. */
8557 : /* -------------------------------------------------------------------- */
8558 7140 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8559 :
8560 7140 : if (poNode == nullptr)
8561 84 : return nullptr;
8562 :
8563 : /* -------------------------------------------------------------------- */
8564 : /* Fetch AUTHORITY child if there is one. */
8565 : /* -------------------------------------------------------------------- */
8566 7056 : if (poNode->FindChild("AUTHORITY") == -1)
8567 183 : return nullptr;
8568 :
8569 6873 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8570 :
8571 : /* -------------------------------------------------------------------- */
8572 : /* Create a new authority node. */
8573 : /* -------------------------------------------------------------------- */
8574 6873 : if (poNode->GetChildCount() < 2)
8575 0 : return nullptr;
8576 :
8577 6873 : return poNode->GetChild(1)->GetValue();
8578 : }
8579 :
8580 : /************************************************************************/
8581 : /* OSRGetAuthorityCode() */
8582 : /************************************************************************/
8583 :
8584 : /**
8585 : * \brief Get the authority code for a node.
8586 : *
8587 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8588 : */
8589 688 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8590 : const char *pszTargetKey)
8591 :
8592 : {
8593 688 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8594 :
8595 688 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8596 : }
8597 :
8598 : /************************************************************************/
8599 : /* GetAuthorityName() */
8600 : /************************************************************************/
8601 :
8602 : /**
8603 : * \brief Get the authority name for a node.
8604 : *
8605 : * This method is used to query an AUTHORITY[] node from within the
8606 : * WKT tree, and fetch the authority name value.
8607 : *
8608 : * The most common authority is "EPSG".
8609 : *
8610 : * This method is the same as the C function OSRGetAuthorityName().
8611 : *
8612 : * @param pszTargetKey the partial or complete path to the node to
8613 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8614 : * search for an authority node on the root element.
8615 : *
8616 : * @return value code from authority node, or NULL on failure. The value
8617 : * returned is internal and should not be freed or modified.
8618 : */
8619 :
8620 : const char *
8621 45090 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8622 :
8623 : {
8624 90180 : TAKE_OPTIONAL_LOCK();
8625 :
8626 45090 : d->refreshProjObj();
8627 45090 : const char *pszInputTargetKey = pszTargetKey;
8628 45090 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8629 45090 : if (pszTargetKey == nullptr)
8630 : {
8631 19306 : if (!d->m_pj_crs)
8632 : {
8633 14 : return nullptr;
8634 : }
8635 19292 : d->demoteFromBoundCRS();
8636 19292 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8637 19292 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8638 : {
8639 782 : auto ctxt = d->getPROJContext();
8640 782 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8641 782 : if (cs)
8642 : {
8643 782 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8644 782 : proj_destroy(cs);
8645 782 : if (axisCount == 3)
8646 : {
8647 : // This might come from a COMPD_CS with a VERT_DATUM type =
8648 : // 2002 in which case, using the WKT1 representation will
8649 : // enable us to recover the EPSG code.
8650 14 : pszTargetKey = pszInputTargetKey;
8651 : }
8652 : }
8653 : }
8654 19292 : d->undoDemoteFromBoundCRS();
8655 19292 : if (ret != nullptr || pszTargetKey == nullptr)
8656 : {
8657 19292 : return ret;
8658 : }
8659 : }
8660 :
8661 : // Special key for that context
8662 25788 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8663 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8664 : {
8665 4 : auto ctxt = d->getPROJContext();
8666 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8667 4 : if (crs)
8668 : {
8669 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8670 4 : if (ret)
8671 4 : ret = CPLSPrintf("%s", ret);
8672 4 : proj_destroy(crs);
8673 4 : return ret;
8674 : }
8675 : }
8676 25784 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8677 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8678 : {
8679 4 : auto ctxt = d->getPROJContext();
8680 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8681 4 : if (crs)
8682 : {
8683 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8684 4 : if (ret)
8685 4 : ret = CPLSPrintf("%s", ret);
8686 4 : proj_destroy(crs);
8687 4 : return ret;
8688 : }
8689 : }
8690 :
8691 : /* -------------------------------------------------------------------- */
8692 : /* Find the node below which the authority should be put. */
8693 : /* -------------------------------------------------------------------- */
8694 25776 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8695 :
8696 25776 : if (poNode == nullptr)
8697 11118 : return nullptr;
8698 :
8699 : /* -------------------------------------------------------------------- */
8700 : /* Fetch AUTHORITY child if there is one. */
8701 : /* -------------------------------------------------------------------- */
8702 14658 : if (poNode->FindChild("AUTHORITY") == -1)
8703 1480 : return nullptr;
8704 :
8705 13178 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8706 :
8707 : /* -------------------------------------------------------------------- */
8708 : /* Create a new authority node. */
8709 : /* -------------------------------------------------------------------- */
8710 13178 : if (poNode->GetChildCount() < 2)
8711 0 : return nullptr;
8712 :
8713 13178 : return poNode->GetChild(0)->GetValue();
8714 : }
8715 :
8716 : /************************************************************************/
8717 : /* OSRGetAuthorityName() */
8718 : /************************************************************************/
8719 :
8720 : /**
8721 : * \brief Get the authority name for a node.
8722 : *
8723 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8724 : */
8725 231 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8726 : const char *pszTargetKey)
8727 :
8728 : {
8729 231 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8730 :
8731 231 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8732 : }
8733 :
8734 : /************************************************************************/
8735 : /* GetOGCURN() */
8736 : /************************************************************************/
8737 :
8738 : /**
8739 : * \brief Get a OGC URN string describing the CRS, when possible
8740 : *
8741 : * This method assumes that the CRS has a top-level identifier, or is
8742 : * a compound CRS whose horizontal and vertical parts have a top-level
8743 : * identifier.
8744 : *
8745 : * @return a string to free with CPLFree(), or nullptr when no result can be
8746 : * generated
8747 : *
8748 : * @since GDAL 3.5
8749 : */
8750 :
8751 59 : char *OGRSpatialReference::GetOGCURN() const
8752 :
8753 : {
8754 118 : TAKE_OPTIONAL_LOCK();
8755 :
8756 59 : const char *pszAuthName = GetAuthorityName(nullptr);
8757 59 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8758 59 : if (pszAuthName && pszAuthCode)
8759 56 : return CPLStrdup(
8760 56 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8761 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8762 2 : return nullptr;
8763 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8764 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8765 1 : char *pszRet = nullptr;
8766 1 : if (horizCRS && vertCRS)
8767 : {
8768 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8769 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8770 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8771 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8772 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8773 : {
8774 1 : pszRet = CPLStrdup(CPLSPrintf(
8775 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8776 : horizAuthCode, vertAuthName, vertAuthCode));
8777 : }
8778 : }
8779 1 : proj_destroy(horizCRS);
8780 1 : proj_destroy(vertCRS);
8781 1 : return pszRet;
8782 : }
8783 :
8784 : /************************************************************************/
8785 : /* StripVertical() */
8786 : /************************************************************************/
8787 :
8788 : /**
8789 : * \brief Convert a compound cs into a horizontal CS.
8790 : *
8791 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8792 : * nodes are stripped resulting and only the horizontal coordinate system
8793 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8794 : *
8795 : * If this is not a compound coordinate system then nothing is changed.
8796 : *
8797 : * This method is the same as the C function OSRStripVertical().
8798 : *
8799 : * @since OGR 1.8.0
8800 : */
8801 :
8802 44 : OGRErr OGRSpatialReference::StripVertical()
8803 :
8804 : {
8805 88 : TAKE_OPTIONAL_LOCK();
8806 :
8807 44 : d->refreshProjObj();
8808 44 : d->demoteFromBoundCRS();
8809 44 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8810 : {
8811 0 : d->undoDemoteFromBoundCRS();
8812 0 : return OGRERR_NONE;
8813 : }
8814 44 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8815 44 : if (!horizCRS)
8816 : {
8817 0 : d->undoDemoteFromBoundCRS();
8818 0 : return OGRERR_FAILURE;
8819 : }
8820 :
8821 44 : bool reuseExistingBoundCRS = false;
8822 44 : if (d->m_pj_bound_crs_target)
8823 : {
8824 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8825 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8826 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8827 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8828 : }
8829 :
8830 44 : if (reuseExistingBoundCRS)
8831 : {
8832 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8833 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8834 4 : d->m_pj_bound_crs_co);
8835 4 : proj_destroy(horizCRS);
8836 4 : d->undoDemoteFromBoundCRS();
8837 4 : d->setPjCRS(newBoundCRS);
8838 : }
8839 : else
8840 : {
8841 40 : d->undoDemoteFromBoundCRS();
8842 40 : d->setPjCRS(horizCRS);
8843 : }
8844 :
8845 44 : return OGRERR_NONE;
8846 : }
8847 :
8848 : /************************************************************************/
8849 : /* OSRStripVertical() */
8850 : /************************************************************************/
8851 : /**
8852 : * \brief Convert a compound cs into a horizontal CS.
8853 : *
8854 : * This function is the same as the C++ method
8855 : * OGRSpatialReference::StripVertical().
8856 : */
8857 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8858 :
8859 : {
8860 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8861 :
8862 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8863 : }
8864 :
8865 : /************************************************************************/
8866 : /* StripTOWGS84IfKnownDatumAndAllowed() */
8867 : /************************************************************************/
8868 :
8869 : /**
8870 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8871 : * and this is allowed by the user.
8872 : *
8873 : * The default behavior is to remove TOWGS84 information if the CRS has a
8874 : * known horizontal datum. This can be disabled by setting the
8875 : * OSR_STRIP_TOWGS84 configuration option to NO.
8876 : *
8877 : * @return true if TOWGS84 has been removed.
8878 : * @since OGR 3.1.0
8879 : */
8880 :
8881 7952 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8882 : {
8883 7952 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8884 : {
8885 7950 : if (StripTOWGS84IfKnownDatum())
8886 : {
8887 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
8888 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
8889 : "configuration option to NO");
8890 71 : return true;
8891 : }
8892 : }
8893 7881 : return false;
8894 : }
8895 :
8896 : /************************************************************************/
8897 : /* StripTOWGS84IfKnownDatum() */
8898 : /************************************************************************/
8899 :
8900 : /**
8901 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8902 : *
8903 : * @return true if TOWGS84 has been removed.
8904 : * @since OGR 3.1.0
8905 : */
8906 :
8907 7956 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8908 :
8909 : {
8910 15912 : TAKE_OPTIONAL_LOCK();
8911 :
8912 7956 : d->refreshProjObj();
8913 7956 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8914 : {
8915 7865 : return false;
8916 : }
8917 91 : auto ctxt = d->getPROJContext();
8918 91 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8919 91 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8920 : {
8921 3 : proj_destroy(baseCRS);
8922 3 : return false;
8923 : }
8924 :
8925 : // Known base CRS code ? Return base CRS
8926 88 : const char *pszCode = proj_get_id_code(baseCRS, 0);
8927 88 : if (pszCode)
8928 : {
8929 2 : d->setPjCRS(baseCRS);
8930 2 : return true;
8931 : }
8932 :
8933 86 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
8934 : #if PROJ_VERSION_MAJOR > 7 || \
8935 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8936 : if (datum == nullptr)
8937 : {
8938 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8939 : }
8940 : #endif
8941 86 : if (!datum)
8942 : {
8943 0 : proj_destroy(baseCRS);
8944 0 : return false;
8945 : }
8946 :
8947 : // Known datum code ? Return base CRS
8948 86 : pszCode = proj_get_id_code(datum, 0);
8949 86 : if (pszCode)
8950 : {
8951 3 : proj_destroy(datum);
8952 3 : d->setPjCRS(baseCRS);
8953 3 : return true;
8954 : }
8955 :
8956 83 : const char *name = proj_get_name(datum);
8957 83 : if (EQUAL(name, "unknown"))
8958 : {
8959 1 : proj_destroy(datum);
8960 1 : proj_destroy(baseCRS);
8961 1 : return false;
8962 : }
8963 82 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8964 : PJ_OBJ_LIST *list =
8965 82 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8966 :
8967 82 : bool knownDatumName = false;
8968 82 : if (list)
8969 : {
8970 82 : if (proj_list_get_count(list) == 1)
8971 : {
8972 69 : knownDatumName = true;
8973 : }
8974 82 : proj_list_destroy(list);
8975 : }
8976 :
8977 82 : proj_destroy(datum);
8978 82 : if (knownDatumName)
8979 : {
8980 69 : d->setPjCRS(baseCRS);
8981 69 : return true;
8982 : }
8983 13 : proj_destroy(baseCRS);
8984 13 : return false;
8985 : }
8986 :
8987 : /************************************************************************/
8988 : /* IsCompound() */
8989 : /************************************************************************/
8990 :
8991 : /**
8992 : * \brief Check if coordinate system is compound.
8993 : *
8994 : * This method is the same as the C function OSRIsCompound().
8995 : *
8996 : * @return TRUE if this is rooted with a COMPD_CS node.
8997 : */
8998 :
8999 37522 : int OGRSpatialReference::IsCompound() const
9000 :
9001 : {
9002 37522 : TAKE_OPTIONAL_LOCK();
9003 :
9004 37522 : d->refreshProjObj();
9005 37520 : d->demoteFromBoundCRS();
9006 37521 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9007 37521 : d->undoDemoteFromBoundCRS();
9008 75044 : return isCompound;
9009 : }
9010 :
9011 : /************************************************************************/
9012 : /* OSRIsCompound() */
9013 : /************************************************************************/
9014 :
9015 : /**
9016 : * \brief Check if the coordinate system is compound.
9017 : *
9018 : * This function is the same as OGRSpatialReference::IsCompound().
9019 : */
9020 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9021 :
9022 : {
9023 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9024 :
9025 5 : return ToPointer(hSRS)->IsCompound();
9026 : }
9027 :
9028 : /************************************************************************/
9029 : /* IsProjected() */
9030 : /************************************************************************/
9031 :
9032 : /**
9033 : * \brief Check if projected coordinate system.
9034 : *
9035 : * This method is the same as the C function OSRIsProjected().
9036 : *
9037 : * @return TRUE if this contains a PROJCS node indicating a it is a
9038 : * projected coordinate system. Also if it is a CompoundCRS made of a
9039 : * ProjectedCRS
9040 : */
9041 :
9042 35797 : int OGRSpatialReference::IsProjected() const
9043 :
9044 : {
9045 35797 : TAKE_OPTIONAL_LOCK();
9046 :
9047 35797 : d->refreshProjObj();
9048 35796 : d->demoteFromBoundCRS();
9049 35796 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9050 35797 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9051 : {
9052 : auto horizCRS =
9053 142 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9054 142 : if (horizCRS)
9055 : {
9056 142 : auto horizCRSType = proj_get_type(horizCRS);
9057 142 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9058 142 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9059 : {
9060 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9061 6 : if (base)
9062 : {
9063 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9064 6 : proj_destroy(base);
9065 : }
9066 : }
9067 142 : proj_destroy(horizCRS);
9068 : }
9069 : }
9070 35797 : d->undoDemoteFromBoundCRS();
9071 71593 : return isProjected;
9072 : }
9073 :
9074 : /************************************************************************/
9075 : /* OSRIsProjected() */
9076 : /************************************************************************/
9077 : /**
9078 : * \brief Check if projected coordinate system.
9079 : *
9080 : * This function is the same as OGRSpatialReference::IsProjected().
9081 : */
9082 414 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9083 :
9084 : {
9085 414 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9086 :
9087 414 : return ToPointer(hSRS)->IsProjected();
9088 : }
9089 :
9090 : /************************************************************************/
9091 : /* IsGeocentric() */
9092 : /************************************************************************/
9093 :
9094 : /**
9095 : * \brief Check if geocentric coordinate system.
9096 : *
9097 : * This method is the same as the C function OSRIsGeocentric().
9098 : *
9099 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9100 : * geocentric coordinate system.
9101 : *
9102 : * @since OGR 1.9.0
9103 : */
9104 :
9105 15560 : int OGRSpatialReference::IsGeocentric() const
9106 :
9107 : {
9108 15560 : TAKE_OPTIONAL_LOCK();
9109 :
9110 15560 : d->refreshProjObj();
9111 15560 : d->demoteFromBoundCRS();
9112 15560 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9113 15560 : d->undoDemoteFromBoundCRS();
9114 31120 : return isGeocentric;
9115 : }
9116 :
9117 : /************************************************************************/
9118 : /* OSRIsGeocentric() */
9119 : /************************************************************************/
9120 : /**
9121 : * \brief Check if geocentric coordinate system.
9122 : *
9123 : * This function is the same as OGRSpatialReference::IsGeocentric().
9124 : *
9125 : * @since OGR 1.9.0
9126 : */
9127 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9128 :
9129 : {
9130 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9131 :
9132 2 : return ToPointer(hSRS)->IsGeocentric();
9133 : }
9134 :
9135 : /************************************************************************/
9136 : /* IsEmpty() */
9137 : /************************************************************************/
9138 :
9139 : /**
9140 : * \brief Return if the SRS is not set.
9141 : */
9142 :
9143 100231 : bool OGRSpatialReference::IsEmpty() const
9144 : {
9145 100231 : TAKE_OPTIONAL_LOCK();
9146 :
9147 100230 : d->refreshProjObj();
9148 200463 : return d->m_pj_crs == nullptr;
9149 : }
9150 :
9151 : /************************************************************************/
9152 : /* IsGeographic() */
9153 : /************************************************************************/
9154 :
9155 : /**
9156 : * \brief Check if geographic coordinate system.
9157 : *
9158 : * This method is the same as the C function OSRIsGeographic().
9159 : *
9160 : * @return TRUE if this spatial reference is geographic ... that is the
9161 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9162 : * GeographicCRS
9163 : */
9164 :
9165 48939 : int OGRSpatialReference::IsGeographic() const
9166 :
9167 : {
9168 48939 : TAKE_OPTIONAL_LOCK();
9169 :
9170 48939 : d->refreshProjObj();
9171 48939 : d->demoteFromBoundCRS();
9172 70162 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9173 21223 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9174 48939 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9175 : {
9176 : auto horizCRS =
9177 292 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9178 292 : if (horizCRS)
9179 : {
9180 292 : auto horizCRSType = proj_get_type(horizCRS);
9181 292 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9182 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9183 292 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9184 : {
9185 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9186 13 : if (base)
9187 : {
9188 13 : horizCRSType = proj_get_type(base);
9189 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9190 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9191 13 : proj_destroy(base);
9192 : }
9193 : }
9194 292 : proj_destroy(horizCRS);
9195 : }
9196 : }
9197 48939 : d->undoDemoteFromBoundCRS();
9198 97878 : return isGeog;
9199 : }
9200 :
9201 : /************************************************************************/
9202 : /* OSRIsGeographic() */
9203 : /************************************************************************/
9204 : /**
9205 : * \brief Check if geographic coordinate system.
9206 : *
9207 : * This function is the same as OGRSpatialReference::IsGeographic().
9208 : */
9209 334 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9210 :
9211 : {
9212 334 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9213 :
9214 334 : return ToPointer(hSRS)->IsGeographic();
9215 : }
9216 :
9217 : /************************************************************************/
9218 : /* IsDerivedGeographic() */
9219 : /************************************************************************/
9220 :
9221 : /**
9222 : * \brief Check if the CRS is a derived geographic coordinate system.
9223 : * (for example a rotated long/lat grid)
9224 : *
9225 : * This method is the same as the C function OSRIsDerivedGeographic().
9226 : *
9227 : * @since GDAL 3.1.0 and PROJ 6.3.0
9228 : */
9229 :
9230 15196 : int OGRSpatialReference::IsDerivedGeographic() const
9231 :
9232 : {
9233 15196 : TAKE_OPTIONAL_LOCK();
9234 :
9235 15196 : d->refreshProjObj();
9236 15196 : d->demoteFromBoundCRS();
9237 24457 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9238 9261 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9239 : const bool isDerivedGeographic =
9240 15196 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9241 15196 : d->undoDemoteFromBoundCRS();
9242 30392 : return isDerivedGeographic ? TRUE : FALSE;
9243 : }
9244 :
9245 : /************************************************************************/
9246 : /* OSRIsDerivedGeographic() */
9247 : /************************************************************************/
9248 : /**
9249 : * \brief Check if the CRS is a derived geographic coordinate system.
9250 : * (for example a rotated long/lat grid)
9251 : *
9252 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9253 : */
9254 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9255 :
9256 : {
9257 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9258 :
9259 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9260 : }
9261 :
9262 : /************************************************************************/
9263 : /* IsDerivedProjected() */
9264 : /************************************************************************/
9265 :
9266 : /**
9267 : * \brief Check if the CRS is a derived projected coordinate system.
9268 : *
9269 : * This method is the same as the C function OSRIsDerivedGeographic().
9270 : *
9271 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9272 : */
9273 :
9274 0 : int OGRSpatialReference::IsDerivedProjected() const
9275 :
9276 : {
9277 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9278 : TAKE_OPTIONAL_LOCK();
9279 : d->refreshProjObj();
9280 : d->demoteFromBoundCRS();
9281 : const bool isDerivedProjected =
9282 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9283 : d->undoDemoteFromBoundCRS();
9284 : return isDerivedProjected ? TRUE : FALSE;
9285 : #else
9286 0 : return FALSE;
9287 : #endif
9288 : }
9289 :
9290 : /************************************************************************/
9291 : /* OSRIsDerivedProjected() */
9292 : /************************************************************************/
9293 : /**
9294 : * \brief Check if the CRS is a derived projected coordinate system.
9295 : *
9296 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9297 : *
9298 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9299 : */
9300 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9301 :
9302 : {
9303 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9304 :
9305 0 : return ToPointer(hSRS)->IsDerivedProjected();
9306 : }
9307 :
9308 : /************************************************************************/
9309 : /* IsLocal() */
9310 : /************************************************************************/
9311 :
9312 : /**
9313 : * \brief Check if local coordinate system.
9314 : *
9315 : * This method is the same as the C function OSRIsLocal().
9316 : *
9317 : * @return TRUE if this spatial reference is local ... that is the
9318 : * root is a LOCAL_CS node.
9319 : */
9320 :
9321 7325 : int OGRSpatialReference::IsLocal() const
9322 :
9323 : {
9324 7325 : TAKE_OPTIONAL_LOCK();
9325 7325 : d->refreshProjObj();
9326 14650 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9327 : }
9328 :
9329 : /************************************************************************/
9330 : /* OSRIsLocal() */
9331 : /************************************************************************/
9332 : /**
9333 : * \brief Check if local coordinate system.
9334 : *
9335 : * This function is the same as OGRSpatialReference::IsLocal().
9336 : */
9337 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9338 :
9339 : {
9340 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9341 :
9342 8 : return ToPointer(hSRS)->IsLocal();
9343 : }
9344 :
9345 : /************************************************************************/
9346 : /* IsVertical() */
9347 : /************************************************************************/
9348 :
9349 : /**
9350 : * \brief Check if vertical coordinate system.
9351 : *
9352 : * This method is the same as the C function OSRIsVertical().
9353 : *
9354 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9355 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9356 : * VerticalCRS
9357 : *
9358 : * @since OGR 1.8.0
9359 : */
9360 :
9361 8002 : int OGRSpatialReference::IsVertical() const
9362 :
9363 : {
9364 8002 : TAKE_OPTIONAL_LOCK();
9365 8002 : d->refreshProjObj();
9366 8002 : d->demoteFromBoundCRS();
9367 8002 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9368 8002 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9369 : {
9370 : auto vertCRS =
9371 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9372 33 : if (vertCRS)
9373 : {
9374 33 : const auto vertCRSType = proj_get_type(vertCRS);
9375 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9376 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9377 : {
9378 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9379 0 : if (base)
9380 : {
9381 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9382 0 : proj_destroy(base);
9383 : }
9384 : }
9385 33 : proj_destroy(vertCRS);
9386 : }
9387 : }
9388 8002 : d->undoDemoteFromBoundCRS();
9389 16004 : return isVertical;
9390 : }
9391 :
9392 : /************************************************************************/
9393 : /* OSRIsVertical() */
9394 : /************************************************************************/
9395 : /**
9396 : * \brief Check if vertical coordinate system.
9397 : *
9398 : * This function is the same as OGRSpatialReference::IsVertical().
9399 : *
9400 : * @since OGR 1.8.0
9401 : */
9402 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9403 :
9404 : {
9405 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9406 :
9407 0 : return ToPointer(hSRS)->IsVertical();
9408 : }
9409 :
9410 : /************************************************************************/
9411 : /* IsDynamic() */
9412 : /************************************************************************/
9413 :
9414 : /**
9415 : * \brief Check if a CRS is a dynamic CRS.
9416 : *
9417 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9418 : * plate-fixed.
9419 : *
9420 : * This method is the same as the C function OSRIsDynamic().
9421 : *
9422 : * @return true if the CRS is dynamic
9423 : *
9424 : * @since OGR 3.4.0
9425 : *
9426 : * @see HasPointMotionOperation()
9427 : */
9428 :
9429 12105 : bool OGRSpatialReference::IsDynamic() const
9430 :
9431 : {
9432 12105 : TAKE_OPTIONAL_LOCK();
9433 12105 : bool isDynamic = false;
9434 12105 : d->refreshProjObj();
9435 12105 : d->demoteFromBoundCRS();
9436 12105 : auto ctxt = d->getPROJContext();
9437 12105 : PJ *horiz = nullptr;
9438 12105 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9439 : {
9440 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9441 : }
9442 12009 : else if (d->m_pj_crs)
9443 : {
9444 11952 : horiz = proj_clone(ctxt, d->m_pj_crs);
9445 : }
9446 12105 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9447 : {
9448 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9449 6 : if (baseCRS)
9450 : {
9451 6 : proj_destroy(horiz);
9452 6 : horiz = baseCRS;
9453 : }
9454 : }
9455 12105 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9456 12105 : if (datum)
9457 : {
9458 12026 : const auto type = proj_get_type(datum);
9459 12026 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9460 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9461 12026 : if (!isDynamic)
9462 : {
9463 12026 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9464 12026 : const char *code = proj_get_id_code(datum, 0);
9465 12026 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9466 11619 : EQUAL(code, "6326"))
9467 : {
9468 7139 : isDynamic = true;
9469 : }
9470 : }
9471 12026 : proj_destroy(datum);
9472 : }
9473 : #if PROJ_VERSION_MAJOR > 7 || \
9474 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9475 : else
9476 : {
9477 : auto ensemble =
9478 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9479 : if (ensemble)
9480 : {
9481 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9482 : if (member)
9483 : {
9484 : const auto type = proj_get_type(member);
9485 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9486 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9487 : proj_destroy(member);
9488 : }
9489 : proj_destroy(ensemble);
9490 : }
9491 : }
9492 : #endif
9493 12105 : proj_destroy(horiz);
9494 12105 : d->undoDemoteFromBoundCRS();
9495 24210 : return isDynamic;
9496 : }
9497 :
9498 : /************************************************************************/
9499 : /* OSRIsDynamic() */
9500 : /************************************************************************/
9501 : /**
9502 : * \brief Check if a CRS is a dynamic CRS.
9503 : *
9504 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9505 : * plate-fixed.
9506 : *
9507 : * This function is the same as OGRSpatialReference::IsDynamic().
9508 : *
9509 : * @since OGR 3.4.0
9510 : */
9511 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9512 :
9513 : {
9514 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9515 :
9516 0 : return ToPointer(hSRS)->IsDynamic();
9517 : }
9518 :
9519 : /************************************************************************/
9520 : /* HasPointMotionOperation() */
9521 : /************************************************************************/
9522 :
9523 : /**
9524 : * \brief Check if a CRS has at least an associated point motion operation.
9525 : *
9526 : * Some CRS are not formally declared as dynamic, but may behave as such
9527 : * in practice due to the presence of point motion operation, to perform
9528 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9529 : *
9530 : * @return true if the CRS has at least an associated point motion operation.
9531 : *
9532 : * @since OGR 3.8.0 and PROJ 9.4.0
9533 : *
9534 : * @see IsDynamic()
9535 : */
9536 :
9537 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9538 :
9539 : {
9540 : #if PROJ_VERSION_MAJOR > 9 || \
9541 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9542 : TAKE_OPTIONAL_LOCK();
9543 : d->refreshProjObj();
9544 : d->demoteFromBoundCRS();
9545 : auto ctxt = d->getPROJContext();
9546 : auto res =
9547 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9548 : d->undoDemoteFromBoundCRS();
9549 : return res;
9550 : #else
9551 5 : return false;
9552 : #endif
9553 : }
9554 :
9555 : /************************************************************************/
9556 : /* OSRHasPointMotionOperation() */
9557 : /************************************************************************/
9558 :
9559 : /**
9560 : * \brief Check if a CRS has at least an associated point motion operation.
9561 : *
9562 : * Some CRS are not formally declared as dynamic, but may behave as such
9563 : * in practice due to the presence of point motion operation, to perform
9564 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9565 : *
9566 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9567 : *
9568 : * @since OGR 3.8.0 and PROJ 9.4.0
9569 : */
9570 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9571 :
9572 : {
9573 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9574 :
9575 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9576 : }
9577 :
9578 : /************************************************************************/
9579 : /* CloneGeogCS() */
9580 : /************************************************************************/
9581 :
9582 : /**
9583 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9584 : * object.
9585 : *
9586 : * @return a new SRS, which becomes the responsibility of the caller.
9587 : */
9588 3591 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9589 :
9590 : {
9591 7182 : TAKE_OPTIONAL_LOCK();
9592 3591 : d->refreshProjObj();
9593 3591 : if (d->m_pj_crs)
9594 : {
9595 3591 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9596 0 : return nullptr;
9597 :
9598 : auto geodCRS =
9599 3591 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9600 3591 : if (geodCRS)
9601 : {
9602 3591 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9603 3591 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9604 : {
9605 : PJ *hub_crs =
9606 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9607 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9608 13 : d->m_pj_crs);
9609 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9610 : geodCRS, hub_crs, co);
9611 13 : proj_destroy(geodCRS);
9612 13 : geodCRS = temp;
9613 13 : proj_destroy(hub_crs);
9614 13 : proj_destroy(co);
9615 : }
9616 :
9617 : /* --------------------------------------------------------------------
9618 : */
9619 : /* We have to reconstruct the GEOGCS node for geocentric */
9620 : /* coordinate systems. */
9621 : /* --------------------------------------------------------------------
9622 : */
9623 3591 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9624 : {
9625 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9626 : #if PROJ_VERSION_MAJOR > 7 || \
9627 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9628 : if (datum == nullptr)
9629 : {
9630 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9631 : geodCRS);
9632 : }
9633 : #endif
9634 0 : if (datum)
9635 : {
9636 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9637 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9638 : nullptr, 0);
9639 0 : auto temp = proj_create_geographic_crs_from_datum(
9640 : d->getPROJContext(), "unnamed", datum, cs);
9641 0 : proj_destroy(datum);
9642 0 : proj_destroy(cs);
9643 0 : proj_destroy(geodCRS);
9644 0 : geodCRS = temp;
9645 : }
9646 : }
9647 :
9648 3591 : poNewSRS->d->setPjCRS(geodCRS);
9649 3591 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9650 2378 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9651 3591 : return poNewSRS;
9652 : }
9653 : }
9654 0 : return nullptr;
9655 : }
9656 :
9657 : /************************************************************************/
9658 : /* OSRCloneGeogCS() */
9659 : /************************************************************************/
9660 : /**
9661 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9662 : * object.
9663 : *
9664 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9665 : */
9666 148 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9667 :
9668 : {
9669 148 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9670 :
9671 148 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9672 : }
9673 :
9674 : /************************************************************************/
9675 : /* IsSameGeogCS() */
9676 : /************************************************************************/
9677 :
9678 : /**
9679 : * \brief Do the GeogCS'es match?
9680 : *
9681 : * This method is the same as the C function OSRIsSameGeogCS().
9682 : *
9683 : * @param poOther the SRS being compared against.
9684 : *
9685 : * @return TRUE if they are the same or FALSE otherwise.
9686 : */
9687 :
9688 7362 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9689 :
9690 : {
9691 7362 : return IsSameGeogCS(poOther, nullptr);
9692 : }
9693 :
9694 : /**
9695 : * \brief Do the GeogCS'es match?
9696 : *
9697 : * This method is the same as the C function OSRIsSameGeogCS().
9698 : *
9699 : * @param poOther the SRS being compared against.
9700 : * @param papszOptions options. ignored
9701 : *
9702 : * @return TRUE if they are the same or FALSE otherwise.
9703 : */
9704 :
9705 7362 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9706 : const char *const *papszOptions) const
9707 :
9708 : {
9709 14724 : TAKE_OPTIONAL_LOCK();
9710 :
9711 7362 : CPL_IGNORE_RET_VAL(papszOptions);
9712 :
9713 7362 : d->refreshProjObj();
9714 7362 : poOther->d->refreshProjObj();
9715 7362 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9716 0 : return FALSE;
9717 7362 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9718 7362 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9719 22086 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9720 7362 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9721 : {
9722 0 : return FALSE;
9723 : }
9724 :
9725 7362 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9726 : auto otherGeodCRS =
9727 7362 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9728 7362 : if (!geodCRS || !otherGeodCRS)
9729 : {
9730 0 : proj_destroy(geodCRS);
9731 0 : proj_destroy(otherGeodCRS);
9732 0 : return FALSE;
9733 : }
9734 :
9735 7362 : int ret = proj_is_equivalent_to(
9736 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9737 :
9738 7362 : proj_destroy(geodCRS);
9739 7362 : proj_destroy(otherGeodCRS);
9740 7362 : return ret;
9741 : }
9742 :
9743 : /************************************************************************/
9744 : /* OSRIsSameGeogCS() */
9745 : /************************************************************************/
9746 :
9747 : /**
9748 : * \brief Do the GeogCS'es match?
9749 : *
9750 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9751 : */
9752 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9753 :
9754 : {
9755 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9756 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9757 :
9758 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9759 : }
9760 :
9761 : /************************************************************************/
9762 : /* IsSameVertCS() */
9763 : /************************************************************************/
9764 :
9765 : /**
9766 : * \brief Do the VertCS'es match?
9767 : *
9768 : * This method is the same as the C function OSRIsSameVertCS().
9769 : *
9770 : * @param poOther the SRS being compared against.
9771 : *
9772 : * @return TRUE if they are the same or FALSE otherwise.
9773 : */
9774 :
9775 2 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9776 :
9777 : {
9778 4 : TAKE_OPTIONAL_LOCK();
9779 :
9780 : /* -------------------------------------------------------------------- */
9781 : /* Does the datum name match? */
9782 : /* -------------------------------------------------------------------- */
9783 2 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9784 2 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9785 :
9786 2 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9787 2 : !EQUAL(pszThisValue, pszOtherValue))
9788 1 : return FALSE;
9789 :
9790 : /* -------------------------------------------------------------------- */
9791 : /* Do the units match? */
9792 : /* -------------------------------------------------------------------- */
9793 1 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9794 1 : if (pszThisValue == nullptr)
9795 0 : pszThisValue = "1.0";
9796 :
9797 1 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9798 1 : if (pszOtherValue == nullptr)
9799 0 : pszOtherValue = "1.0";
9800 :
9801 1 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9802 0 : return FALSE;
9803 :
9804 1 : return TRUE;
9805 : }
9806 :
9807 : /************************************************************************/
9808 : /* OSRIsSameVertCS() */
9809 : /************************************************************************/
9810 :
9811 : /**
9812 : * \brief Do the VertCS'es match?
9813 : *
9814 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9815 : */
9816 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9817 :
9818 : {
9819 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9820 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9821 :
9822 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9823 : }
9824 :
9825 : /************************************************************************/
9826 : /* IsSame() */
9827 : /************************************************************************/
9828 :
9829 : /**
9830 : * \brief Do these two spatial references describe the same system ?
9831 : *
9832 : * @param poOtherSRS the SRS being compared to.
9833 : *
9834 : * @return TRUE if equivalent or FALSE otherwise.
9835 : */
9836 :
9837 4438 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9838 :
9839 : {
9840 4438 : return IsSame(poOtherSRS, nullptr);
9841 : }
9842 :
9843 : /**
9844 : * \brief Do these two spatial references describe the same system ?
9845 : *
9846 : * This also takes into account the data axis to CRS axis mapping by default
9847 : *
9848 : * @param poOtherSRS the SRS being compared to.
9849 : * @param papszOptions options. NULL or NULL terminated list of options.
9850 : * Currently supported options are:
9851 : * <ul>
9852 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9853 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9854 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9855 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9856 : * </ul>
9857 : *
9858 : * @return TRUE if equivalent or FALSE otherwise.
9859 : */
9860 :
9861 12919 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9862 : const char *const *papszOptions) const
9863 :
9864 : {
9865 25837 : TAKE_OPTIONAL_LOCK();
9866 :
9867 12919 : d->refreshProjObj();
9868 12919 : poOtherSRS->d->refreshProjObj();
9869 12919 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9870 50 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9871 12868 : if (!CPLTestBool(CSLFetchNameValueDef(
9872 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9873 : {
9874 12308 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9875 2122 : return false;
9876 : }
9877 :
9878 10747 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9879 : "IGNORE_COORDINATE_EPOCH", "NO")))
9880 : {
9881 10420 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9882 27 : return false;
9883 : }
9884 :
9885 10720 : bool reboundSelf = false;
9886 10720 : bool reboundOther = false;
9887 10772 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9888 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9889 : {
9890 14 : d->demoteFromBoundCRS();
9891 14 : reboundSelf = true;
9892 : }
9893 21374 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9894 10668 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9895 : {
9896 28 : poOtherSRS->d->demoteFromBoundCRS();
9897 28 : reboundOther = true;
9898 : }
9899 :
9900 10720 : PJ_COMPARISON_CRITERION criterion =
9901 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9902 10720 : const char *pszCriterion = CSLFetchNameValueDef(
9903 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9904 10720 : if (EQUAL(pszCriterion, "STRICT"))
9905 0 : criterion = PJ_COMP_STRICT;
9906 10720 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
9907 6114 : criterion = PJ_COMP_EQUIVALENT;
9908 4606 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9909 : {
9910 0 : CPLError(CE_Warning, CPLE_NotSupported,
9911 : "Unsupported value for CRITERION: %s", pszCriterion);
9912 : }
9913 : int ret =
9914 10720 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9915 10719 : if (reboundSelf)
9916 14 : d->undoDemoteFromBoundCRS();
9917 10719 : if (reboundOther)
9918 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
9919 :
9920 10719 : return ret;
9921 : }
9922 :
9923 : /************************************************************************/
9924 : /* OSRIsSame() */
9925 : /************************************************************************/
9926 :
9927 : /**
9928 : * \brief Do these two spatial references describe the same system ?
9929 : *
9930 : * This function is the same as OGRSpatialReference::IsSame().
9931 : */
9932 29 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9933 :
9934 : {
9935 29 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9936 29 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9937 :
9938 29 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9939 : }
9940 :
9941 : /************************************************************************/
9942 : /* OSRIsSameEx() */
9943 : /************************************************************************/
9944 :
9945 : /**
9946 : * \brief Do these two spatial references describe the same system ?
9947 : *
9948 : * This function is the same as OGRSpatialReference::IsSame().
9949 : */
9950 593 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9951 : const char *const *papszOptions)
9952 : {
9953 593 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9954 593 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9955 :
9956 593 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9957 : }
9958 :
9959 : /************************************************************************/
9960 : /* convertToOtherProjection() */
9961 : /************************************************************************/
9962 :
9963 : /**
9964 : * \brief Convert to another equivalent projection
9965 : *
9966 : * Currently implemented:
9967 : * <ul>
9968 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9969 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9970 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9971 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9972 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9973 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9974 : * </ul>
9975 : *
9976 : * @param pszTargetProjection target projection.
9977 : * @param papszOptions lists of options. None supported currently.
9978 : * @return a new SRS, or NULL in case of error.
9979 : *
9980 : * @since GDAL 2.3
9981 : */
9982 89 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
9983 : const char *pszTargetProjection,
9984 : CPL_UNUSED const char *const *papszOptions) const
9985 : {
9986 178 : TAKE_OPTIONAL_LOCK();
9987 :
9988 89 : if (pszTargetProjection == nullptr)
9989 1 : return nullptr;
9990 : int new_code;
9991 88 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
9992 : {
9993 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
9994 : }
9995 82 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
9996 : {
9997 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
9998 : }
9999 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10000 : {
10001 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10002 : }
10003 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10004 : {
10005 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10006 : }
10007 : else
10008 : {
10009 1 : return nullptr;
10010 : }
10011 :
10012 87 : d->refreshProjObj();
10013 87 : d->demoteFromBoundCRS();
10014 87 : OGRSpatialReference *poNewSRS = nullptr;
10015 87 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10016 : {
10017 : auto conv =
10018 86 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10019 86 : auto new_conv = proj_convert_conversion_to_other_method(
10020 : d->getPROJContext(), conv, new_code, nullptr);
10021 86 : proj_destroy(conv);
10022 86 : if (new_conv)
10023 : {
10024 : auto geodCRS =
10025 72 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10026 72 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10027 72 : d->m_pj_crs);
10028 72 : if (geodCRS && cs)
10029 : {
10030 72 : auto new_proj_crs = proj_create_projected_crs(
10031 72 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10032 : new_conv, cs);
10033 72 : proj_destroy(new_conv);
10034 72 : if (new_proj_crs)
10035 : {
10036 72 : poNewSRS = new OGRSpatialReference();
10037 :
10038 72 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10039 : {
10040 9 : auto boundCRS = proj_crs_create_bound_crs(
10041 : d->getPROJContext(), new_proj_crs,
10042 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10043 9 : if (boundCRS)
10044 : {
10045 9 : proj_destroy(new_proj_crs);
10046 9 : new_proj_crs = boundCRS;
10047 : }
10048 : }
10049 :
10050 72 : poNewSRS->d->setPjCRS(new_proj_crs);
10051 : }
10052 : }
10053 72 : proj_destroy(geodCRS);
10054 72 : proj_destroy(cs);
10055 : }
10056 : }
10057 87 : d->undoDemoteFromBoundCRS();
10058 87 : return poNewSRS;
10059 : }
10060 :
10061 : /************************************************************************/
10062 : /* OSRConvertToOtherProjection() */
10063 : /************************************************************************/
10064 :
10065 : /**
10066 : * \brief Convert to another equivalent projection
10067 : *
10068 : * Currently implemented:
10069 : * <ul>
10070 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10071 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10072 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10073 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10074 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10075 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10076 : * </ul>
10077 : *
10078 : * @param hSRS source SRS
10079 : * @param pszTargetProjection target projection.
10080 : * @param papszOptions lists of options. None supported currently.
10081 : * @return a new SRS, or NULL in case of error.
10082 : *
10083 : * @since GDAL 2.3
10084 : */
10085 : OGRSpatialReferenceH
10086 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10087 : const char *pszTargetProjection,
10088 : const char *const *papszOptions)
10089 : {
10090 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10091 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10092 28 : pszTargetProjection, papszOptions));
10093 : }
10094 :
10095 : /************************************************************************/
10096 : /* OSRFindMatches() */
10097 : /************************************************************************/
10098 :
10099 : /**
10100 : * \brief Try to identify a match between the passed SRS and a related SRS
10101 : * in a catalog.
10102 : *
10103 : * Matching may be partial, or may fail.
10104 : * Returned entries will be sorted by decreasing match confidence (first
10105 : * entry has the highest match confidence).
10106 : *
10107 : * The exact way matching is done may change in future versions. Starting with
10108 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10109 : *
10110 : * This function is the same as OGRSpatialReference::FindMatches().
10111 : *
10112 : * @param hSRS SRS to match
10113 : * @param papszOptions NULL terminated list of options or NULL
10114 : * @param pnEntries Output parameter. Number of values in the returned array.
10115 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10116 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10117 : * indicate the confidence in the match. 100 is the highest confidence level.
10118 : * The array must be freed with CPLFree().
10119 : *
10120 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10121 : * with OSRFreeSRSArray()
10122 : *
10123 : * @since GDAL 2.3
10124 : */
10125 8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10126 : char **papszOptions, int *pnEntries,
10127 : int **ppanMatchConfidence)
10128 : {
10129 8 : if (pnEntries)
10130 8 : *pnEntries = 0;
10131 8 : if (ppanMatchConfidence)
10132 8 : *ppanMatchConfidence = nullptr;
10133 8 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10134 :
10135 8 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10136 8 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10137 : }
10138 :
10139 : /************************************************************************/
10140 : /* OSRFreeSRSArray() */
10141 : /************************************************************************/
10142 :
10143 : /**
10144 : * \brief Free return of OSRIdentifyMatches()
10145 : *
10146 : * @param pahSRS array of SRS (must be NULL terminated)
10147 : * @since GDAL 2.3
10148 : */
10149 196 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10150 : {
10151 196 : if (pahSRS != nullptr)
10152 : {
10153 2460 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10154 : {
10155 2282 : OSRRelease(pahSRS[i]);
10156 : }
10157 178 : CPLFree(pahSRS);
10158 : }
10159 196 : }
10160 :
10161 : /************************************************************************/
10162 : /* FindBestMatch() */
10163 : /************************************************************************/
10164 :
10165 : /**
10166 : * \brief Try to identify the best match between the passed SRS and a related
10167 : * SRS in a catalog.
10168 : *
10169 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10170 : * of filtering its output.
10171 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10172 : * will be considered. If there is a single match, it is returned.
10173 : * If there are several matches, only return the one under the
10174 : * pszPreferredAuthority, if there is a single one under that authority.
10175 : *
10176 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10177 : * 100). If set to 0, 90 is used.
10178 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10179 : * "EPSG" is used.
10180 : * @param papszOptions NULL terminated list of options or NULL. No option is
10181 : * defined at time of writing.
10182 : *
10183 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10184 : *
10185 : * @since GDAL 3.6
10186 : * @see OGRSpatialReference::FindMatches()
10187 : */
10188 : OGRSpatialReference *
10189 1245 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10190 : const char *pszPreferredAuthority,
10191 : CSLConstList papszOptions) const
10192 : {
10193 2490 : TAKE_OPTIONAL_LOCK();
10194 :
10195 1245 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10196 :
10197 1245 : if (nMinimumMatchConfidence == 0)
10198 0 : nMinimumMatchConfidence = 90;
10199 1245 : if (pszPreferredAuthority == nullptr)
10200 199 : pszPreferredAuthority = "EPSG";
10201 :
10202 : // Try to identify the CRS with the database
10203 1245 : int nEntries = 0;
10204 1245 : int *panConfidence = nullptr;
10205 : OGRSpatialReferenceH *pahSRS =
10206 1245 : FindMatches(nullptr, &nEntries, &panConfidence);
10207 1245 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10208 : {
10209 2192 : std::vector<double> adfTOWGS84(7);
10210 1096 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10211 : {
10212 1095 : adfTOWGS84.clear();
10213 : }
10214 :
10215 1096 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10216 :
10217 : auto poBaseGeogCRS =
10218 1096 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10219 :
10220 : // If the base geographic SRS of the SRS is EPSG:4326
10221 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10222 : // SRS code
10223 : // Same with EPSG:4258 (ETRS89), since it's the only known
10224 : // TOWGS84[] style transformation to WGS 84, and given the
10225 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10226 : // chance that a non-NULL TOWGS84[] will emerge.
10227 1096 : const char *pszAuthorityName = nullptr;
10228 1096 : const char *pszAuthorityCode = nullptr;
10229 1096 : const char *pszBaseAuthorityName = nullptr;
10230 1096 : const char *pszBaseAuthorityCode = nullptr;
10231 2192 : if (adfTOWGS84 == std::vector<double>(7) &&
10232 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10233 1 : EQUAL(pszAuthorityName, "EPSG") &&
10234 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10235 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10236 1 : nullptr &&
10237 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10238 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10239 2193 : nullptr &&
10240 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10241 1 : EQUAL(pszBaseAuthorityCode, "4258")))
10242 : {
10243 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10244 : }
10245 :
10246 1096 : CPLFree(pahSRS);
10247 1096 : CPLFree(panConfidence);
10248 :
10249 1096 : return poSRS;
10250 : }
10251 : else
10252 : {
10253 : // If there are several matches >= nMinimumMatchConfidence, take the
10254 : // only one that is under pszPreferredAuthority
10255 149 : int iBestEntry = -1;
10256 1650 : for (int i = 0; i < nEntries; i++)
10257 : {
10258 1501 : if (panConfidence[i] >= nMinimumMatchConfidence)
10259 : {
10260 : const char *pszAuthName =
10261 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10262 3 : ->GetAuthorityName(nullptr);
10263 3 : if (pszAuthName != nullptr &&
10264 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10265 : {
10266 3 : if (iBestEntry < 0)
10267 3 : iBestEntry = i;
10268 : else
10269 : {
10270 0 : iBestEntry = -1;
10271 0 : break;
10272 : }
10273 : }
10274 : }
10275 : }
10276 149 : if (iBestEntry >= 0)
10277 : {
10278 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10279 3 : OSRFreeSRSArray(pahSRS);
10280 3 : CPLFree(panConfidence);
10281 3 : return poRet;
10282 : }
10283 : }
10284 146 : OSRFreeSRSArray(pahSRS);
10285 146 : CPLFree(panConfidence);
10286 146 : return nullptr;
10287 : }
10288 :
10289 : /************************************************************************/
10290 : /* SetTOWGS84() */
10291 : /************************************************************************/
10292 :
10293 : /**
10294 : * \brief Set the Bursa-Wolf conversion to WGS84.
10295 : *
10296 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10297 : * if there is no existing DATUM node. It will replace
10298 : * an existing TOWGS84 node if there is one.
10299 : *
10300 : * The parameters have the same meaning as EPSG transformation 9606
10301 : * (Position Vector 7-param. transformation).
10302 : *
10303 : * This method is the same as the C function OSRSetTOWGS84().
10304 : *
10305 : * @param dfDX X child in meters.
10306 : * @param dfDY Y child in meters.
10307 : * @param dfDZ Z child in meters.
10308 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10309 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10310 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10311 : * @param dfPPM scaling factor (parts per million).
10312 : *
10313 : * @return OGRERR_NONE on success.
10314 : */
10315 :
10316 109 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10317 : double dfEX, double dfEY, double dfEZ,
10318 : double dfPPM)
10319 :
10320 : {
10321 218 : TAKE_OPTIONAL_LOCK();
10322 :
10323 109 : d->refreshProjObj();
10324 109 : if (d->m_pj_crs == nullptr)
10325 : {
10326 0 : return OGRERR_FAILURE;
10327 : }
10328 :
10329 : // Remove existing BoundCRS
10330 109 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10331 : {
10332 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10333 0 : if (!baseCRS)
10334 0 : return OGRERR_FAILURE;
10335 0 : d->setPjCRS(baseCRS);
10336 : }
10337 :
10338 : PJ_PARAM_DESCRIPTION params[7];
10339 :
10340 109 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10341 109 : params[0].auth_name = "EPSG";
10342 109 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10343 109 : params[0].value = dfDX;
10344 109 : params[0].unit_name = "metre";
10345 109 : params[0].unit_conv_factor = 1.0;
10346 109 : params[0].unit_type = PJ_UT_LINEAR;
10347 :
10348 109 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10349 109 : params[1].auth_name = "EPSG";
10350 109 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10351 109 : params[1].value = dfDY;
10352 109 : params[1].unit_name = "metre";
10353 109 : params[1].unit_conv_factor = 1.0;
10354 109 : params[1].unit_type = PJ_UT_LINEAR;
10355 :
10356 109 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10357 109 : params[2].auth_name = "EPSG";
10358 109 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10359 109 : params[2].value = dfDZ;
10360 109 : params[2].unit_name = "metre";
10361 109 : params[2].unit_conv_factor = 1.0;
10362 109 : params[2].unit_type = PJ_UT_LINEAR;
10363 :
10364 109 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10365 109 : params[3].auth_name = "EPSG";
10366 109 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10367 109 : params[3].value = dfEX;
10368 109 : params[3].unit_name = "arc-second";
10369 109 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10370 109 : params[3].unit_type = PJ_UT_ANGULAR;
10371 :
10372 109 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10373 109 : params[4].auth_name = "EPSG";
10374 109 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10375 109 : params[4].value = dfEY;
10376 109 : params[4].unit_name = "arc-second";
10377 109 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10378 109 : params[4].unit_type = PJ_UT_ANGULAR;
10379 :
10380 109 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10381 109 : params[5].auth_name = "EPSG";
10382 109 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10383 109 : params[5].value = dfEZ;
10384 109 : params[5].unit_name = "arc-second";
10385 109 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10386 109 : params[5].unit_type = PJ_UT_ANGULAR;
10387 :
10388 109 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10389 109 : params[6].auth_name = "EPSG";
10390 109 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10391 109 : params[6].value = dfPPM;
10392 109 : params[6].unit_name = "parts per million";
10393 109 : params[6].unit_conv_factor = 1e-6;
10394 109 : params[6].unit_type = PJ_UT_SCALE;
10395 :
10396 : auto sourceCRS =
10397 109 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10398 109 : if (!sourceCRS)
10399 : {
10400 0 : return OGRERR_FAILURE;
10401 : }
10402 :
10403 109 : const auto sourceType = proj_get_type(sourceCRS);
10404 :
10405 109 : auto targetCRS = proj_create_from_database(
10406 : d->getPROJContext(), "EPSG",
10407 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10408 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10409 : : "4978",
10410 : PJ_CATEGORY_CRS, false, nullptr);
10411 109 : if (!targetCRS)
10412 : {
10413 0 : proj_destroy(sourceCRS);
10414 0 : return OGRERR_FAILURE;
10415 : }
10416 :
10417 218 : CPLString osMethodCode;
10418 : osMethodCode.Printf("%d",
10419 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10420 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10421 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10422 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10423 109 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10424 :
10425 109 : auto transf = proj_create_transformation(
10426 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10427 : sourceCRS, targetCRS, nullptr,
10428 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10429 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10430 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10431 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10432 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10433 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10434 109 : proj_destroy(sourceCRS);
10435 109 : if (!transf)
10436 : {
10437 0 : proj_destroy(targetCRS);
10438 0 : return OGRERR_FAILURE;
10439 : }
10440 :
10441 109 : auto newBoundCRS = proj_crs_create_bound_crs(
10442 109 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10443 109 : proj_destroy(transf);
10444 109 : proj_destroy(targetCRS);
10445 109 : if (!newBoundCRS)
10446 : {
10447 0 : return OGRERR_FAILURE;
10448 : }
10449 :
10450 109 : d->setPjCRS(newBoundCRS);
10451 109 : return OGRERR_NONE;
10452 : }
10453 :
10454 : /************************************************************************/
10455 : /* OSRSetTOWGS84() */
10456 : /************************************************************************/
10457 :
10458 : /**
10459 : * \brief Set the Bursa-Wolf conversion to WGS84.
10460 : *
10461 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10462 : */
10463 10 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10464 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10465 : double dfPPM)
10466 :
10467 : {
10468 10 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10469 :
10470 10 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10471 10 : dfPPM);
10472 : }
10473 :
10474 : /************************************************************************/
10475 : /* GetTOWGS84() */
10476 : /************************************************************************/
10477 :
10478 : /**
10479 : * \brief Fetch TOWGS84 parameters, if available.
10480 : *
10481 : * The parameters have the same meaning as EPSG transformation 9606
10482 : * (Position Vector 7-param. transformation).
10483 : *
10484 : * @param padfCoeff array into which up to 7 coefficients are placed.
10485 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10486 : *
10487 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10488 : * TOWGS84 node available.
10489 : */
10490 :
10491 4290 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10492 :
10493 : {
10494 8580 : TAKE_OPTIONAL_LOCK();
10495 :
10496 4290 : d->refreshProjObj();
10497 4290 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10498 4242 : return OGRERR_FAILURE;
10499 :
10500 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10501 :
10502 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10503 48 : int success = proj_coordoperation_get_towgs84_values(
10504 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10505 48 : proj_destroy(transf);
10506 :
10507 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10508 : }
10509 :
10510 : /************************************************************************/
10511 : /* OSRGetTOWGS84() */
10512 : /************************************************************************/
10513 :
10514 : /**
10515 : * \brief Fetch TOWGS84 parameters, if available.
10516 : *
10517 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10518 : */
10519 11 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10520 : int nCoeffCount)
10521 :
10522 : {
10523 11 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10524 :
10525 11 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10526 : }
10527 :
10528 : /************************************************************************/
10529 : /* IsAngularParameter() */
10530 : /************************************************************************/
10531 :
10532 : /** Is the passed projection parameter an angular one?
10533 : *
10534 : * @return TRUE or FALSE
10535 : */
10536 :
10537 : /* static */
10538 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10539 :
10540 : {
10541 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10542 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10543 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10544 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10545 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10546 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10547 8 : return TRUE;
10548 :
10549 2 : return FALSE;
10550 : }
10551 :
10552 : /************************************************************************/
10553 : /* IsLongitudeParameter() */
10554 : /************************************************************************/
10555 :
10556 : /** Is the passed projection parameter an angular longitude
10557 : * (relative to a prime meridian)?
10558 : *
10559 : * @return TRUE or FALSE
10560 : */
10561 :
10562 : /* static */
10563 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10564 :
10565 : {
10566 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10567 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10568 0 : return TRUE;
10569 :
10570 0 : return FALSE;
10571 : }
10572 :
10573 : /************************************************************************/
10574 : /* IsLinearParameter() */
10575 : /************************************************************************/
10576 :
10577 : /** Is the passed projection parameter an linear one measured in meters or
10578 : * some similar linear measure.
10579 : *
10580 : * @return TRUE or FALSE
10581 : */
10582 :
10583 : /* static */
10584 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10585 :
10586 : {
10587 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10588 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10589 9 : return TRUE;
10590 :
10591 34 : return FALSE;
10592 : }
10593 :
10594 : /************************************************************************/
10595 : /* GetNormInfo() */
10596 : /************************************************************************/
10597 :
10598 : /**
10599 : * \brief Set the internal information for normalizing linear, and angular
10600 : * values.
10601 : */
10602 3517 : void OGRSpatialReference::GetNormInfo() const
10603 :
10604 : {
10605 3517 : TAKE_OPTIONAL_LOCK();
10606 :
10607 3517 : if (d->bNormInfoSet)
10608 2466 : return;
10609 :
10610 : /* -------------------------------------------------------------------- */
10611 : /* Initialize values. */
10612 : /* -------------------------------------------------------------------- */
10613 1051 : d->bNormInfoSet = TRUE;
10614 :
10615 1051 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10616 1051 : d->dfToMeter = GetLinearUnits(nullptr);
10617 1051 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10618 1051 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10619 1048 : d->dfToDegrees = 1.0;
10620 : }
10621 :
10622 : /************************************************************************/
10623 : /* GetExtension() */
10624 : /************************************************************************/
10625 :
10626 : /**
10627 : * \brief Fetch extension value.
10628 : *
10629 : * Fetch the value of the named EXTENSION item for the identified
10630 : * target node.
10631 : *
10632 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10633 : * @param pszName the name of the extension being fetched.
10634 : * @param pszDefault the value to return if the extension is not found.
10635 : *
10636 : * @return node value if successful or pszDefault on failure.
10637 : */
10638 :
10639 9069 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10640 : const char *pszName,
10641 : const char *pszDefault) const
10642 :
10643 : {
10644 18138 : TAKE_OPTIONAL_LOCK();
10645 :
10646 : /* -------------------------------------------------------------------- */
10647 : /* Find the target node. */
10648 : /* -------------------------------------------------------------------- */
10649 : const OGR_SRSNode *poNode =
10650 9069 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10651 :
10652 9069 : if (poNode == nullptr)
10653 2189 : return nullptr;
10654 :
10655 : /* -------------------------------------------------------------------- */
10656 : /* Fetch matching EXTENSION if there is one. */
10657 : /* -------------------------------------------------------------------- */
10658 50844 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10659 : {
10660 43986 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10661 :
10662 44010 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10663 24 : poChild->GetChildCount() >= 2)
10664 : {
10665 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10666 22 : return poChild->GetChild(1)->GetValue();
10667 : }
10668 : }
10669 :
10670 6858 : return pszDefault;
10671 : }
10672 :
10673 : /************************************************************************/
10674 : /* SetExtension() */
10675 : /************************************************************************/
10676 : /**
10677 : * \brief Set extension value.
10678 : *
10679 : * Set the value of the named EXTENSION item for the identified
10680 : * target node.
10681 : *
10682 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10683 : * @param pszName the name of the extension being fetched.
10684 : * @param pszValue the value to set
10685 : *
10686 : * @return OGRERR_NONE on success
10687 : */
10688 :
10689 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10690 : const char *pszName,
10691 : const char *pszValue)
10692 :
10693 : {
10694 40 : TAKE_OPTIONAL_LOCK();
10695 :
10696 : /* -------------------------------------------------------------------- */
10697 : /* Find the target node. */
10698 : /* -------------------------------------------------------------------- */
10699 20 : OGR_SRSNode *poNode = nullptr;
10700 :
10701 20 : if (pszTargetKey == nullptr)
10702 0 : poNode = GetRoot();
10703 : else
10704 20 : poNode = GetAttrNode(pszTargetKey);
10705 :
10706 20 : if (poNode == nullptr)
10707 0 : return OGRERR_FAILURE;
10708 :
10709 : /* -------------------------------------------------------------------- */
10710 : /* Fetch matching EXTENSION if there is one. */
10711 : /* -------------------------------------------------------------------- */
10712 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10713 : {
10714 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10715 :
10716 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10717 6 : poChild->GetChildCount() >= 2)
10718 : {
10719 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10720 : {
10721 6 : poChild->GetChild(1)->SetValue(pszValue);
10722 6 : return OGRERR_NONE;
10723 : }
10724 : }
10725 : }
10726 :
10727 : /* -------------------------------------------------------------------- */
10728 : /* Create a new EXTENSION node. */
10729 : /* -------------------------------------------------------------------- */
10730 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10731 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10732 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10733 :
10734 14 : poNode->AddChild(poAuthNode);
10735 :
10736 14 : return OGRERR_NONE;
10737 : }
10738 :
10739 : /************************************************************************/
10740 : /* OSRCleanup() */
10741 : /************************************************************************/
10742 :
10743 : static void CleanupSRSWGS84Mutex();
10744 :
10745 : /**
10746 : * \brief Cleanup cached SRS related memory.
10747 : *
10748 : * This function will attempt to cleanup any cache spatial reference
10749 : * related information, such as cached tables of coordinate systems.
10750 : *
10751 : * This function should not be called concurrently with any other GDAL/OGR
10752 : * function. It is meant at being called once before process termination
10753 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10754 : * thread-specific resources before thread termination.
10755 : */
10756 946 : void OSRCleanup(void)
10757 :
10758 : {
10759 946 : OGRCTDumpStatistics();
10760 946 : CSVDeaccess(nullptr);
10761 946 : CleanupSRSWGS84Mutex();
10762 946 : OSRCTCleanCache();
10763 946 : OSRCleanupTLSContext();
10764 946 : }
10765 :
10766 : /************************************************************************/
10767 : /* GetAxesCount() */
10768 : /************************************************************************/
10769 :
10770 : /**
10771 : * \brief Return the number of axis of the coordinate system of the CRS.
10772 : *
10773 : * @since GDAL 3.0
10774 : */
10775 34542 : int OGRSpatialReference::GetAxesCount() const
10776 : {
10777 69084 : TAKE_OPTIONAL_LOCK();
10778 :
10779 34542 : int axisCount = 0;
10780 34542 : d->refreshProjObj();
10781 34542 : if (d->m_pj_crs == nullptr)
10782 : {
10783 0 : return 0;
10784 : }
10785 34542 : d->demoteFromBoundCRS();
10786 34542 : auto ctxt = d->getPROJContext();
10787 34542 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10788 : {
10789 29 : for (int i = 0;; i++)
10790 : {
10791 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10792 87 : if (!subCRS)
10793 29 : break;
10794 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10795 : {
10796 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10797 17 : if (baseCRS)
10798 : {
10799 17 : proj_destroy(subCRS);
10800 17 : subCRS = baseCRS;
10801 : }
10802 : }
10803 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10804 58 : if (cs)
10805 : {
10806 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10807 58 : proj_destroy(cs);
10808 : }
10809 58 : proj_destroy(subCRS);
10810 58 : }
10811 : }
10812 : else
10813 : {
10814 34513 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10815 34513 : if (cs)
10816 : {
10817 34513 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10818 34513 : proj_destroy(cs);
10819 : }
10820 : }
10821 34542 : d->undoDemoteFromBoundCRS();
10822 34542 : return axisCount;
10823 : }
10824 :
10825 : /************************************************************************/
10826 : /* OSRGetAxesCount() */
10827 : /************************************************************************/
10828 :
10829 : /**
10830 : * \brief Return the number of axis of the coordinate system of the CRS.
10831 : *
10832 : * This method is the equivalent of the C++ method
10833 : * OGRSpatialReference::GetAxesCount()
10834 : *
10835 : * @since GDAL 3.1
10836 : */
10837 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10838 :
10839 : {
10840 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10841 :
10842 6 : return ToPointer(hSRS)->GetAxesCount();
10843 : }
10844 :
10845 : /************************************************************************/
10846 : /* GetAxis() */
10847 : /************************************************************************/
10848 :
10849 : /**
10850 : * \brief Fetch the orientation of one axis.
10851 : *
10852 : * Fetches the request axis (iAxis - zero based) from the
10853 : * indicated portion of the coordinate system (pszTargetKey) which
10854 : * should be either "GEOGCS" or "PROJCS".
10855 : *
10856 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10857 : *
10858 : * This method is equivalent to the C function OSRGetAxis().
10859 : *
10860 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10861 : * "GEOGCS").
10862 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10863 : * @param peOrientation location into which to place the fetch orientation, may
10864 : * be NULL.
10865 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10866 : * factor. May be NULL. Only set if pszTargetKey == NULL
10867 : *
10868 : * @return the name of the axis or NULL on failure.
10869 : */
10870 :
10871 5432 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10872 : OGRAxisOrientation *peOrientation,
10873 : double *pdfConvUnit) const
10874 :
10875 : {
10876 10864 : TAKE_OPTIONAL_LOCK();
10877 :
10878 5432 : if (peOrientation != nullptr)
10879 5339 : *peOrientation = OAO_Other;
10880 5432 : if (pdfConvUnit != nullptr)
10881 85 : *pdfConvUnit = 0;
10882 :
10883 5432 : d->refreshProjObj();
10884 5432 : if (d->m_pj_crs == nullptr)
10885 : {
10886 1 : return nullptr;
10887 : }
10888 :
10889 5431 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10890 5431 : if (pszTargetKey == nullptr && iAxis <= 2)
10891 : {
10892 5431 : auto ctxt = d->getPROJContext();
10893 :
10894 5431 : int iAxisModified = iAxis;
10895 :
10896 5431 : d->demoteFromBoundCRS();
10897 :
10898 5431 : PJ *cs = nullptr;
10899 5431 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10900 : {
10901 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10902 134 : if (horizCRS)
10903 : {
10904 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10905 : {
10906 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10907 6 : if (baseCRS)
10908 : {
10909 6 : proj_destroy(horizCRS);
10910 6 : horizCRS = baseCRS;
10911 : }
10912 : }
10913 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10914 134 : proj_destroy(horizCRS);
10915 134 : if (cs)
10916 : {
10917 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10918 : {
10919 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10920 44 : proj_destroy(cs);
10921 44 : cs = nullptr;
10922 : }
10923 : }
10924 : }
10925 :
10926 134 : if (cs == nullptr)
10927 : {
10928 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10929 44 : if (vertCRS)
10930 : {
10931 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10932 : {
10933 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10934 30 : if (baseCRS)
10935 : {
10936 30 : proj_destroy(vertCRS);
10937 30 : vertCRS = baseCRS;
10938 : }
10939 : }
10940 :
10941 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10942 44 : proj_destroy(vertCRS);
10943 : }
10944 : }
10945 : }
10946 : else
10947 : {
10948 5297 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10949 : }
10950 :
10951 5431 : if (cs)
10952 : {
10953 5431 : const char *pszName = nullptr;
10954 5431 : const char *pszOrientation = nullptr;
10955 5431 : double dfConvFactor = 0.0;
10956 5431 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10957 : &pszOrientation, &dfConvFactor, nullptr,
10958 : nullptr, nullptr);
10959 :
10960 5431 : if (pdfConvUnit != nullptr)
10961 : {
10962 85 : *pdfConvUnit = dfConvFactor;
10963 : }
10964 :
10965 5431 : if (pszName && pszOrientation)
10966 : {
10967 5431 : d->m_osAxisName[iAxis] = pszName;
10968 5431 : if (peOrientation)
10969 : {
10970 5338 : if (EQUAL(pszOrientation, "NORTH"))
10971 3372 : *peOrientation = OAO_North;
10972 1966 : else if (EQUAL(pszOrientation, "EAST"))
10973 1924 : *peOrientation = OAO_East;
10974 42 : else if (EQUAL(pszOrientation, "SOUTH"))
10975 31 : *peOrientation = OAO_South;
10976 11 : else if (EQUAL(pszOrientation, "WEST"))
10977 0 : *peOrientation = OAO_West;
10978 11 : else if (EQUAL(pszOrientation, "UP"))
10979 1 : *peOrientation = OAO_Up;
10980 10 : else if (EQUAL(pszOrientation, "DOWN"))
10981 0 : *peOrientation = OAO_Down;
10982 : }
10983 5431 : proj_destroy(cs);
10984 5431 : d->undoDemoteFromBoundCRS();
10985 5431 : return d->m_osAxisName[iAxis].c_str();
10986 : }
10987 0 : proj_destroy(cs);
10988 : }
10989 0 : d->undoDemoteFromBoundCRS();
10990 : }
10991 :
10992 : /* -------------------------------------------------------------------- */
10993 : /* Find the target node. */
10994 : /* -------------------------------------------------------------------- */
10995 0 : const OGR_SRSNode *poNode = nullptr;
10996 :
10997 0 : if (pszTargetKey == nullptr)
10998 0 : poNode = GetRoot();
10999 : else
11000 0 : poNode = GetAttrNode(pszTargetKey);
11001 :
11002 0 : if (poNode == nullptr)
11003 0 : return nullptr;
11004 :
11005 : /* -------------------------------------------------------------------- */
11006 : /* Find desired child AXIS. */
11007 : /* -------------------------------------------------------------------- */
11008 0 : const OGR_SRSNode *poAxis = nullptr;
11009 0 : const int nChildCount = poNode->GetChildCount();
11010 :
11011 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11012 : {
11013 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11014 :
11015 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11016 0 : continue;
11017 :
11018 0 : if (iAxis == 0)
11019 : {
11020 0 : poAxis = poChild;
11021 0 : break;
11022 : }
11023 0 : iAxis--;
11024 : }
11025 :
11026 0 : if (poAxis == nullptr)
11027 0 : return nullptr;
11028 :
11029 0 : if (poAxis->GetChildCount() < 2)
11030 0 : return nullptr;
11031 :
11032 : /* -------------------------------------------------------------------- */
11033 : /* Extract name and orientation if possible. */
11034 : /* -------------------------------------------------------------------- */
11035 0 : if (peOrientation != nullptr)
11036 : {
11037 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11038 :
11039 0 : if (EQUAL(pszOrientation, "NORTH"))
11040 0 : *peOrientation = OAO_North;
11041 0 : else if (EQUAL(pszOrientation, "EAST"))
11042 0 : *peOrientation = OAO_East;
11043 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11044 0 : *peOrientation = OAO_South;
11045 0 : else if (EQUAL(pszOrientation, "WEST"))
11046 0 : *peOrientation = OAO_West;
11047 0 : else if (EQUAL(pszOrientation, "UP"))
11048 0 : *peOrientation = OAO_Up;
11049 0 : else if (EQUAL(pszOrientation, "DOWN"))
11050 0 : *peOrientation = OAO_Down;
11051 0 : else if (EQUAL(pszOrientation, "OTHER"))
11052 0 : *peOrientation = OAO_Other;
11053 : else
11054 : {
11055 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11056 : pszOrientation);
11057 : }
11058 : }
11059 :
11060 0 : return poAxis->GetChild(0)->GetValue();
11061 : }
11062 :
11063 : /************************************************************************/
11064 : /* OSRGetAxis() */
11065 : /************************************************************************/
11066 :
11067 : /**
11068 : * \brief Fetch the orientation of one axis.
11069 : *
11070 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11071 : */
11072 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11073 : int iAxis, OGRAxisOrientation *peOrientation)
11074 :
11075 : {
11076 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11077 :
11078 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11079 : }
11080 :
11081 : /************************************************************************/
11082 : /* OSRAxisEnumToName() */
11083 : /************************************************************************/
11084 :
11085 : /**
11086 : * \brief Return the string representation for the OGRAxisOrientation
11087 : * enumeration.
11088 : *
11089 : * For example "NORTH" for OAO_North.
11090 : *
11091 : * @return an internal string
11092 : */
11093 380 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11094 :
11095 : {
11096 380 : if (eOrientation == OAO_North)
11097 190 : return "NORTH";
11098 190 : if (eOrientation == OAO_East)
11099 190 : return "EAST";
11100 0 : if (eOrientation == OAO_South)
11101 0 : return "SOUTH";
11102 0 : if (eOrientation == OAO_West)
11103 0 : return "WEST";
11104 0 : if (eOrientation == OAO_Up)
11105 0 : return "UP";
11106 0 : if (eOrientation == OAO_Down)
11107 0 : return "DOWN";
11108 0 : if (eOrientation == OAO_Other)
11109 0 : return "OTHER";
11110 :
11111 0 : return "UNKNOWN";
11112 : }
11113 :
11114 : /************************************************************************/
11115 : /* SetAxes() */
11116 : /************************************************************************/
11117 :
11118 : /**
11119 : * \brief Set the axes for a coordinate system.
11120 : *
11121 : * Set the names, and orientations of the axes for either a projected
11122 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11123 : *
11124 : * This method is equivalent to the C function OSRSetAxes().
11125 : *
11126 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11127 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11128 : * @param eXAxisOrientation normally OAO_East.
11129 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11130 : * @param eYAxisOrientation normally OAO_North.
11131 : *
11132 : * @return OGRERR_NONE on success or an error code.
11133 : */
11134 :
11135 190 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11136 : const char *pszXAxisName,
11137 : OGRAxisOrientation eXAxisOrientation,
11138 : const char *pszYAxisName,
11139 : OGRAxisOrientation eYAxisOrientation)
11140 :
11141 : {
11142 380 : TAKE_OPTIONAL_LOCK();
11143 :
11144 : /* -------------------------------------------------------------------- */
11145 : /* Find the target node. */
11146 : /* -------------------------------------------------------------------- */
11147 190 : OGR_SRSNode *poNode = nullptr;
11148 :
11149 190 : if (pszTargetKey == nullptr)
11150 190 : poNode = GetRoot();
11151 : else
11152 0 : poNode = GetAttrNode(pszTargetKey);
11153 :
11154 190 : if (poNode == nullptr)
11155 0 : return OGRERR_FAILURE;
11156 :
11157 : /* -------------------------------------------------------------------- */
11158 : /* Strip any existing AXIS children. */
11159 : /* -------------------------------------------------------------------- */
11160 570 : while (poNode->FindChild("AXIS") >= 0)
11161 380 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11162 :
11163 : /* -------------------------------------------------------------------- */
11164 : /* Insert desired axes */
11165 : /* -------------------------------------------------------------------- */
11166 190 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11167 :
11168 190 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11169 190 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11170 :
11171 190 : poNode->AddChild(poAxis);
11172 :
11173 190 : poAxis = new OGR_SRSNode("AXIS");
11174 :
11175 190 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11176 190 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11177 :
11178 190 : poNode->AddChild(poAxis);
11179 :
11180 190 : return OGRERR_NONE;
11181 : }
11182 :
11183 : /************************************************************************/
11184 : /* OSRSetAxes() */
11185 : /************************************************************************/
11186 : /**
11187 : * \brief Set the axes for a coordinate system.
11188 : *
11189 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11190 : */
11191 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11192 : const char *pszXAxisName,
11193 : OGRAxisOrientation eXAxisOrientation,
11194 : const char *pszYAxisName,
11195 : OGRAxisOrientation eYAxisOrientation)
11196 : {
11197 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11198 :
11199 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11200 : eXAxisOrientation, pszYAxisName,
11201 0 : eYAxisOrientation);
11202 : }
11203 :
11204 : /************************************************************************/
11205 : /* OSRExportToMICoordSys() */
11206 : /************************************************************************/
11207 : /**
11208 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11209 : *
11210 : * This method is the equivalent of the C++ method
11211 : * OGRSpatialReference::exportToMICoordSys
11212 : */
11213 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11214 :
11215 : {
11216 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11217 :
11218 5 : *ppszReturn = nullptr;
11219 :
11220 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11221 : }
11222 :
11223 : /************************************************************************/
11224 : /* exportToMICoordSys() */
11225 : /************************************************************************/
11226 :
11227 : /**
11228 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11229 : *
11230 : * Note that the returned WKT string should be freed with
11231 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11232 : *
11233 : * This method is the same as the C function OSRExportToMICoordSys().
11234 : *
11235 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11236 : * definition will be assigned.
11237 : *
11238 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11239 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11240 : */
11241 :
11242 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11243 :
11244 : {
11245 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11246 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11247 7 : return OGRERR_NONE;
11248 :
11249 0 : return OGRERR_FAILURE;
11250 : }
11251 :
11252 : /************************************************************************/
11253 : /* OSRImportFromMICoordSys() */
11254 : /************************************************************************/
11255 : /**
11256 : * \brief Import Mapinfo style CoordSys definition.
11257 : *
11258 : * This method is the equivalent of the C++ method
11259 : * OGRSpatialReference::importFromMICoordSys
11260 : */
11261 :
11262 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11263 : const char *pszCoordSys)
11264 :
11265 : {
11266 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11267 :
11268 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11269 : }
11270 :
11271 : /************************************************************************/
11272 : /* importFromMICoordSys() */
11273 : /************************************************************************/
11274 :
11275 : /**
11276 : * \brief Import Mapinfo style CoordSys definition.
11277 : *
11278 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11279 : * definition string.
11280 : *
11281 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11282 : *
11283 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11284 : *
11285 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11286 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11287 : */
11288 :
11289 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11290 :
11291 : {
11292 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11293 :
11294 17 : if (poResult == nullptr)
11295 0 : return OGRERR_FAILURE;
11296 :
11297 17 : *this = *poResult;
11298 17 : delete poResult;
11299 :
11300 17 : return OGRERR_NONE;
11301 : }
11302 :
11303 : /************************************************************************/
11304 : /* OSRCalcInvFlattening() */
11305 : /************************************************************************/
11306 :
11307 : /**
11308 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11309 : *
11310 : * @param dfSemiMajor Semi-major axis length.
11311 : * @param dfSemiMinor Semi-minor axis length.
11312 : *
11313 : * @return inverse flattening, or 0 if both axis are equal.
11314 : * @since GDAL 2.0
11315 : */
11316 :
11317 7380 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11318 : {
11319 7380 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11320 27 : return 0;
11321 7353 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11322 : {
11323 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11324 : "OSRCalcInvFlattening(): Wrong input values");
11325 0 : return 0;
11326 : }
11327 :
11328 7353 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11329 : }
11330 :
11331 : /************************************************************************/
11332 : /* OSRCalcInvFlattening() */
11333 : /************************************************************************/
11334 :
11335 : /**
11336 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11337 : *
11338 : * @param dfSemiMajor Semi-major axis length.
11339 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11340 : *
11341 : * @return semi-minor axis
11342 : * @since GDAL 2.0
11343 : */
11344 :
11345 635 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11346 : double dfInvFlattening)
11347 : {
11348 635 : if (fabs(dfInvFlattening) < 0.000000000001)
11349 101 : return dfSemiMajor;
11350 534 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11351 : {
11352 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11353 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11354 0 : return dfSemiMajor;
11355 : }
11356 :
11357 534 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11358 : }
11359 :
11360 : /************************************************************************/
11361 : /* GetWGS84SRS() */
11362 : /************************************************************************/
11363 :
11364 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11365 : static CPLMutex *hMutex = nullptr;
11366 :
11367 : /**
11368 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11369 : *
11370 : * Note: the instance will have
11371 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11372 : *
11373 : * The reference counter of the returned object is not increased by this
11374 : * operation.
11375 : *
11376 : * @return instance.
11377 : * @since GDAL 2.0
11378 : */
11379 :
11380 821 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11381 : {
11382 821 : CPLMutexHolderD(&hMutex);
11383 821 : if (poSRSWGS84 == nullptr)
11384 : {
11385 4 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11386 4 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11387 : }
11388 1642 : return poSRSWGS84;
11389 : }
11390 :
11391 : /************************************************************************/
11392 : /* CleanupSRSWGS84Mutex() */
11393 : /************************************************************************/
11394 :
11395 946 : static void CleanupSRSWGS84Mutex()
11396 : {
11397 946 : if (hMutex != nullptr)
11398 : {
11399 2 : poSRSWGS84->Release();
11400 2 : poSRSWGS84 = nullptr;
11401 2 : CPLDestroyMutex(hMutex);
11402 2 : hMutex = nullptr;
11403 : }
11404 946 : }
11405 :
11406 : /************************************************************************/
11407 : /* OSRImportFromProj4() */
11408 : /************************************************************************/
11409 : /**
11410 : * \brief Import PROJ coordinate string.
11411 : *
11412 : * This function is the same as OGRSpatialReference::importFromProj4().
11413 : */
11414 189 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11415 :
11416 : {
11417 189 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11418 :
11419 189 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11420 : }
11421 :
11422 : /************************************************************************/
11423 : /* importFromProj4() */
11424 : /************************************************************************/
11425 :
11426 : /**
11427 : * \brief Import PROJ coordinate string.
11428 : *
11429 : * The OGRSpatialReference is initialized from the passed PROJs style
11430 : * coordinate system string.
11431 : *
11432 : * Example:
11433 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11434 : *
11435 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11436 : * a legacy syntax that should be avoided in the future. In particular they will
11437 : * result in CRS objects whose axis order might not correspond to the official
11438 : * EPSG axis order.
11439 : *
11440 : * This method is the equivalent of the C function OSRImportFromProj4().
11441 : *
11442 : * @param pszProj4 the PROJ style string.
11443 : *
11444 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11445 : */
11446 :
11447 618 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11448 :
11449 : {
11450 1236 : TAKE_OPTIONAL_LOCK();
11451 :
11452 618 : if (strlen(pszProj4) >= 10000)
11453 : {
11454 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11455 1 : return OGRERR_CORRUPT_DATA;
11456 : }
11457 :
11458 : /* -------------------------------------------------------------------- */
11459 : /* Clear any existing definition. */
11460 : /* -------------------------------------------------------------------- */
11461 617 : Clear();
11462 :
11463 617 : CPLString osProj4(pszProj4);
11464 617 : if (osProj4.find("type=crs") == std::string::npos)
11465 : {
11466 608 : osProj4 += " +type=crs";
11467 : }
11468 :
11469 619 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11470 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11471 : {
11472 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11473 : "+init=epsg:XXXX syntax is deprecated. It might return "
11474 : "a CRS with a non-EPSG compliant axis order.");
11475 : }
11476 617 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11477 617 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11478 617 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11479 617 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11480 : }
11481 :
11482 : /************************************************************************/
11483 : /* OSRExportToProj4() */
11484 : /************************************************************************/
11485 : /**
11486 : * \brief Export coordinate system in PROJ.4 legacy format.
11487 : *
11488 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11489 : * PROJ >= 6 is significantly different from earlier versions. In particular
11490 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11491 : * will be missing most of the time. PROJ strings to encode CRS should be
11492 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11493 : * is the recommended way.
11494 : *
11495 : * This function is the same as OGRSpatialReference::exportToProj4().
11496 : */
11497 455 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11498 : char **ppszReturn)
11499 :
11500 : {
11501 455 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11502 :
11503 455 : *ppszReturn = nullptr;
11504 :
11505 455 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11506 : }
11507 :
11508 : /************************************************************************/
11509 : /* exportToProj4() */
11510 : /************************************************************************/
11511 :
11512 : /**
11513 : * \brief Export coordinate system in PROJ.4 legacy format.
11514 : *
11515 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11516 : * PROJ >= 6 is significantly different from earlier versions. In particular
11517 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11518 : * will be missing most of the time. PROJ strings to encode CRS should be
11519 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11520 : * representation is the recommended way.
11521 : *
11522 : * Converts the loaded coordinate reference system into PROJ format
11523 : * to the extent possible. The string returned in ppszProj4 should be
11524 : * deallocated by the caller with CPLFree() when no longer needed.
11525 : *
11526 : * LOCAL_CS coordinate systems are not translatable. An empty string
11527 : * will be returned along with OGRERR_NONE.
11528 : *
11529 : * Special processing for Transverse Mercator:
11530 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11531 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11532 : * for the tmerc and utm projection methods, rather than the more accurate
11533 : * method.
11534 : *
11535 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11536 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11537 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11538 : * added. This automatic addition may be disabled by setting the
11539 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11540 : *
11541 : * This method is the equivalent of the C function OSRExportToProj4().
11542 : *
11543 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11544 : * will be assigned.
11545 : *
11546 : * @return OGRERR_NONE on success or an error code on failure.
11547 : */
11548 :
11549 1420 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11550 :
11551 : {
11552 : // In the past calling this method was thread-safe, even if we never
11553 : // guaranteed it. Now proj_as_proj_string() will cache the result
11554 : // internally, so this is no longer thread-safe.
11555 2840 : std::lock_guard oLock(d->m_mutex);
11556 :
11557 1420 : d->refreshProjObj();
11558 1420 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11559 : {
11560 24 : *ppszProj4 = CPLStrdup("");
11561 24 : return OGRERR_FAILURE;
11562 : }
11563 :
11564 : // OSR_USE_ETMERC is here just for legacy
11565 1396 : bool bForceApproxTMerc = false;
11566 1396 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11567 1396 : if (pszUseETMERC && pszUseETMERC[0])
11568 : {
11569 0 : CPLErrorOnce(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 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11574 : }
11575 : else
11576 : {
11577 : const char *pszUseApproxTMERC =
11578 1396 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11579 1396 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11580 : {
11581 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11582 : }
11583 : }
11584 1396 : const char *options[] = {
11585 1396 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11586 :
11587 1396 : const char *projString = proj_as_proj_string(
11588 1396 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11589 :
11590 1396 : PJ *boundCRS = nullptr;
11591 2788 : if (projString &&
11592 1392 : (strstr(projString, "+datum=") == nullptr ||
11593 2798 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11594 539 : CPLTestBool(
11595 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11596 : {
11597 539 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11598 539 : d->getPROJContext(), d->m_pj_crs, true,
11599 539 : strstr(projString, "+datum=") == nullptr);
11600 539 : if (boundCRS)
11601 : {
11602 216 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11603 : PJ_PROJ_4, options);
11604 : }
11605 : }
11606 :
11607 1396 : if (projString == nullptr)
11608 : {
11609 4 : *ppszProj4 = CPLStrdup("");
11610 4 : proj_destroy(boundCRS);
11611 4 : return OGRERR_FAILURE;
11612 : }
11613 1392 : *ppszProj4 = CPLStrdup(projString);
11614 1392 : proj_destroy(boundCRS);
11615 1392 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11616 1392 : if (pszTypeCrs)
11617 1392 : *pszTypeCrs = '\0';
11618 1392 : return OGRERR_NONE;
11619 : }
11620 :
11621 : /************************************************************************/
11622 : /* morphToESRI() */
11623 : /************************************************************************/
11624 : /**
11625 : * \brief Convert in place to ESRI WKT format.
11626 : *
11627 : * The value nodes of this coordinate system are modified in various manners
11628 : * more closely map onto the ESRI concept of WKT format. This includes
11629 : * renaming a variety of projections and arguments, and stripping out
11630 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11631 : *
11632 : * \note Since GDAL 3.0, this function has only user-visible effects at
11633 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11634 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11635 : *
11636 : * This does the same as the C function OSRMorphToESRI().
11637 : *
11638 : * @return OGRERR_NONE unless something goes badly wrong.
11639 : * @deprecated
11640 : */
11641 :
11642 268 : OGRErr OGRSpatialReference::morphToESRI()
11643 :
11644 : {
11645 268 : TAKE_OPTIONAL_LOCK();
11646 :
11647 268 : d->refreshProjObj();
11648 268 : d->setMorphToESRI(true);
11649 :
11650 536 : return OGRERR_NONE;
11651 : }
11652 :
11653 : /************************************************************************/
11654 : /* OSRMorphToESRI() */
11655 : /************************************************************************/
11656 :
11657 : /**
11658 : * \brief Convert in place to ESRI WKT format.
11659 : *
11660 : * This function is the same as the C++ method
11661 : * OGRSpatialReference::morphToESRI().
11662 : */
11663 103 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11664 :
11665 : {
11666 103 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11667 :
11668 103 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11669 : }
11670 :
11671 : /************************************************************************/
11672 : /* morphFromESRI() */
11673 : /************************************************************************/
11674 :
11675 : /**
11676 : * \brief Convert in place from ESRI WKT format.
11677 : *
11678 : * The value notes of this coordinate system are modified in various manners
11679 : * to adhere more closely to the WKT standard. This mostly involves
11680 : * translating a variety of ESRI names for projections, arguments and
11681 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11682 : * translation of EPSG to WKT for the CT specification.
11683 : *
11684 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11685 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11686 : * effect is to undo the effect of a potential prior call to morphToESRI().
11687 : *
11688 : * This does the same as the C function OSRMorphFromESRI().
11689 : *
11690 : * @return OGRERR_NONE unless something goes badly wrong.
11691 : * @deprecated
11692 : */
11693 :
11694 27 : OGRErr OGRSpatialReference::morphFromESRI()
11695 :
11696 : {
11697 27 : TAKE_OPTIONAL_LOCK();
11698 :
11699 27 : d->refreshProjObj();
11700 27 : d->setMorphToESRI(false);
11701 :
11702 54 : return OGRERR_NONE;
11703 : }
11704 :
11705 : /************************************************************************/
11706 : /* OSRMorphFromESRI() */
11707 : /************************************************************************/
11708 :
11709 : /**
11710 : * \brief Convert in place from ESRI WKT format.
11711 : *
11712 : * This function is the same as the C++ method
11713 : * OGRSpatialReference::morphFromESRI().
11714 : */
11715 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11716 :
11717 : {
11718 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11719 :
11720 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11721 : }
11722 :
11723 : /************************************************************************/
11724 : /* FindMatches() */
11725 : /************************************************************************/
11726 :
11727 : /**
11728 : * \brief Try to identify a match between the passed SRS and a related SRS
11729 : * in a catalog.
11730 : *
11731 : * Matching may be partial, or may fail.
11732 : * Returned entries will be sorted by decreasing match confidence (first
11733 : * entry has the highest match confidence).
11734 : *
11735 : * The exact way matching is done may change in future versions. Starting with
11736 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11737 : *
11738 : * This method is the same as OSRFindMatches().
11739 : *
11740 : * @param papszOptions NULL terminated list of options or NULL
11741 : * @param pnEntries Output parameter. Number of values in the returned array.
11742 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11743 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11744 : * indicate the confidence in the match. 100 is the highest confidence level.
11745 : * The array must be freed with CPLFree().
11746 : *
11747 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11748 : * with OSRFreeSRSArray()
11749 : *
11750 : * @since GDAL 2.3
11751 : *
11752 : * @see OGRSpatialReference::FindBestMatch()
11753 : */
11754 : OGRSpatialReferenceH *
11755 1272 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11756 : int **ppanMatchConfidence) const
11757 : {
11758 2544 : TAKE_OPTIONAL_LOCK();
11759 :
11760 1272 : CPL_IGNORE_RET_VAL(papszOptions);
11761 :
11762 1272 : if (pnEntries)
11763 1272 : *pnEntries = 0;
11764 1272 : if (ppanMatchConfidence)
11765 1272 : *ppanMatchConfidence = nullptr;
11766 :
11767 1272 : d->refreshProjObj();
11768 1272 : if (!d->m_pj_crs)
11769 0 : return nullptr;
11770 :
11771 1272 : int *panConfidence = nullptr;
11772 1272 : auto ctxt = d->getPROJContext();
11773 : auto list =
11774 1272 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11775 1272 : if (!list)
11776 0 : return nullptr;
11777 :
11778 1272 : const int nMatches = proj_list_get_count(list);
11779 :
11780 1272 : if (pnEntries)
11781 1272 : *pnEntries = static_cast<int>(nMatches);
11782 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11783 1272 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11784 1272 : if (ppanMatchConfidence)
11785 : {
11786 1272 : *ppanMatchConfidence =
11787 1272 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11788 : }
11789 :
11790 1272 : bool bSortAgain = false;
11791 :
11792 4645 : for (int i = 0; i < nMatches; i++)
11793 : {
11794 3373 : PJ *obj = proj_list_get(ctxt, list, i);
11795 3373 : CPLAssert(obj);
11796 3373 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11797 3373 : poSRS->d->setPjCRS(obj);
11798 3373 : pahRet[i] = ToHandle(poSRS);
11799 :
11800 : // Identify matches that only differ by axis order
11801 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11802 3391 : poSRS->GetAxesCount() == 2 &&
11803 3382 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11804 : {
11805 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11806 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11807 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11808 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11809 9 : GetAxis(nullptr, 0, &eThisAxis0);
11810 9 : GetAxis(nullptr, 1, &eThisAxis1);
11811 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11812 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11813 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11814 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11815 : {
11816 : auto pj_crs_normalized =
11817 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11818 9 : if (pj_crs_normalized)
11819 : {
11820 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11821 9 : PJ_COMP_EQUIVALENT))
11822 : {
11823 3 : bSortAgain = true;
11824 3 : panConfidence[i] = 90;
11825 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11826 : }
11827 9 : proj_destroy(pj_crs_normalized);
11828 : }
11829 : }
11830 : }
11831 :
11832 3373 : if (ppanMatchConfidence)
11833 3373 : (*ppanMatchConfidence)[i] = panConfidence[i];
11834 : }
11835 :
11836 1272 : if (bSortAgain)
11837 : {
11838 3 : std::vector<int> anIndices;
11839 12 : for (int i = 0; i < nMatches; ++i)
11840 9 : anIndices.push_back(i);
11841 :
11842 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11843 9 : [&panConfidence](int i, int j)
11844 9 : { return panConfidence[i] > panConfidence[j]; });
11845 :
11846 : OGRSpatialReferenceH *pahRetSorted =
11847 : static_cast<OGRSpatialReferenceH *>(
11848 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11849 12 : for (int i = 0; i < nMatches; ++i)
11850 : {
11851 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11852 9 : if (ppanMatchConfidence)
11853 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11854 : }
11855 3 : CPLFree(pahRet);
11856 3 : pahRet = pahRetSorted;
11857 : }
11858 :
11859 1272 : pahRet[nMatches] = nullptr;
11860 1272 : proj_list_destroy(list);
11861 1272 : proj_int_list_destroy(panConfidence);
11862 :
11863 1272 : return pahRet;
11864 : }
11865 :
11866 : /************************************************************************/
11867 : /* importFromEPSGA() */
11868 : /************************************************************************/
11869 :
11870 : /**
11871 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11872 : * code.
11873 : *
11874 : * This method will initialize the spatial reference based on the
11875 : * passed in EPSG CRS code found in the PROJ database.
11876 : *
11877 : * Since GDAL 3.0, this method is identical to importFromEPSG().
11878 : *
11879 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11880 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11881 : * such method available for the CRS. This behavior might not always be
11882 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11883 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11884 : * The AddGuessedTOWGS84() method can also be used for that purpose.
11885 : *
11886 : * The method will also by default substitute a deprecated EPSG code by its
11887 : * non-deprecated replacement. If this behavior is not desired, the
11888 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11889 : *
11890 : * This method is the same as the C function OSRImportFromEPSGA().
11891 : *
11892 : * @param nCode a CRS code.
11893 : *
11894 : * @return OGRERR_NONE on success, or an error code on failure.
11895 : */
11896 :
11897 35252 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11898 :
11899 : {
11900 70504 : TAKE_OPTIONAL_LOCK();
11901 :
11902 35252 : Clear();
11903 :
11904 : const char *pszUseNonDeprecated =
11905 35252 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11906 : const bool bUseNonDeprecated =
11907 35252 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11908 35252 : const bool bAddTOWGS84 = CPLTestBool(
11909 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11910 35252 : auto tlsCache = OSRGetProjTLSCache();
11911 35252 : if (tlsCache)
11912 : {
11913 : auto cachedObj =
11914 35252 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11915 35252 : if (cachedObj)
11916 : {
11917 27213 : d->setPjCRS(cachedObj);
11918 27213 : return OGRERR_NONE;
11919 : }
11920 : }
11921 :
11922 16078 : CPLString osCode;
11923 8039 : osCode.Printf("%d", nCode);
11924 : PJ *obj;
11925 8039 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
11926 8039 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
11927 : {
11928 8035 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11929 : osCode.c_str(), PJ_CATEGORY_CRS, true,
11930 : nullptr);
11931 8035 : if (!obj)
11932 : {
11933 22 : return OGRERR_FAILURE;
11934 : }
11935 : }
11936 : else
11937 : {
11938 : // Likely to be an ESRI CRS...
11939 4 : CPLErr eLastErrorType = CE_None;
11940 4 : CPLErrorNum eLastErrorNum = CPLE_None;
11941 4 : std::string osLastErrorMsg;
11942 4 : bool bIsESRI = false;
11943 : {
11944 8 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
11945 4 : CPLErrorReset();
11946 4 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11947 : osCode.c_str(), PJ_CATEGORY_CRS,
11948 : true, nullptr);
11949 4 : if (!obj)
11950 : {
11951 1 : eLastErrorType = CPLGetLastErrorType();
11952 1 : eLastErrorNum = CPLGetLastErrorNo();
11953 1 : osLastErrorMsg = CPLGetLastErrorMsg();
11954 1 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
11955 : osCode.c_str(), PJ_CATEGORY_CRS,
11956 : true, nullptr);
11957 1 : if (obj)
11958 0 : bIsESRI = true;
11959 : }
11960 : }
11961 4 : if (!obj)
11962 : {
11963 1 : if (eLastErrorType != CE_None)
11964 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
11965 : osLastErrorMsg.c_str());
11966 1 : return OGRERR_FAILURE;
11967 : }
11968 3 : if (bIsESRI)
11969 : {
11970 0 : CPLError(CE_Warning, CPLE_AppDefined,
11971 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
11972 : "Assuming ESRI:%d was meant",
11973 : nCode, nCode, nCode);
11974 : }
11975 : }
11976 :
11977 8016 : if (bUseNonDeprecated && proj_is_deprecated(obj))
11978 : {
11979 411 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11980 411 : if (list)
11981 : {
11982 411 : const auto count = proj_list_get_count(list);
11983 411 : if (count == 1)
11984 : {
11985 : auto nonDeprecated =
11986 360 : proj_list_get(d->getPROJContext(), list, 0);
11987 360 : if (nonDeprecated)
11988 : {
11989 360 : if (pszUseNonDeprecated == nullptr)
11990 : {
11991 : const char *pszNewAuth =
11992 360 : proj_get_id_auth_name(nonDeprecated, 0);
11993 : const char *pszNewCode =
11994 360 : proj_get_id_code(nonDeprecated, 0);
11995 360 : CPLError(CE_Warning, CPLE_AppDefined,
11996 : "CRS EPSG:%d is deprecated. "
11997 : "Its non-deprecated replacement %s:%s "
11998 : "will be used instead. "
11999 : "To use the original CRS, set the "
12000 : "OSR_USE_NON_DEPRECATED "
12001 : "configuration option to NO.",
12002 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12003 : pszNewCode ? pszNewCode : "(null)");
12004 : }
12005 360 : proj_destroy(obj);
12006 360 : obj = nonDeprecated;
12007 : }
12008 : }
12009 : }
12010 411 : proj_list_destroy(list);
12011 : }
12012 :
12013 8016 : if (bAddTOWGS84)
12014 : {
12015 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12016 : obj, nullptr);
12017 1 : if (boundCRS)
12018 : {
12019 1 : proj_destroy(obj);
12020 1 : obj = boundCRS;
12021 : }
12022 : }
12023 :
12024 8016 : d->setPjCRS(obj);
12025 :
12026 8016 : if (tlsCache)
12027 : {
12028 8016 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12029 : obj);
12030 : }
12031 :
12032 8016 : return OGRERR_NONE;
12033 : }
12034 :
12035 : /************************************************************************/
12036 : /* AddGuessedTOWGS84() */
12037 : /************************************************************************/
12038 :
12039 : /**
12040 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12041 : * to WGS84.
12042 : *
12043 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12044 : * to WGS84 when there is one and only one such method available for the CRS.
12045 : * Note: this is more restrictive to how GDAL < 3 worked.
12046 : *
12047 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12048 : *
12049 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12050 : * already a transformation to WGS84 or none matching could be found).
12051 : *
12052 : * @since GDAL 3.0.3
12053 : */
12054 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12055 : {
12056 36 : TAKE_OPTIONAL_LOCK();
12057 :
12058 18 : d->refreshProjObj();
12059 18 : if (!d->m_pj_crs)
12060 0 : return OGRERR_FAILURE;
12061 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12062 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12063 18 : if (!boundCRS)
12064 : {
12065 0 : return OGRERR_FAILURE;
12066 : }
12067 18 : d->setPjCRS(boundCRS);
12068 18 : return OGRERR_NONE;
12069 : }
12070 :
12071 : /************************************************************************/
12072 : /* OSRImportFromEPSGA() */
12073 : /************************************************************************/
12074 :
12075 : /**
12076 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12077 : * to WGS84.
12078 : *
12079 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12080 : *
12081 : * @since GDAL 3.0.3
12082 : */
12083 :
12084 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12085 :
12086 : {
12087 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12088 :
12089 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12090 : }
12091 :
12092 : /************************************************************************/
12093 : /* OSRImportFromEPSGA() */
12094 : /************************************************************************/
12095 :
12096 : /**
12097 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12098 : * code.
12099 : *
12100 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12101 : */
12102 :
12103 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12104 :
12105 : {
12106 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12107 :
12108 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12109 : }
12110 :
12111 : /************************************************************************/
12112 : /* importFromEPSG() */
12113 : /************************************************************************/
12114 :
12115 : /**
12116 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12117 : * code.
12118 : *
12119 : * This method will initialize the spatial reference based on the
12120 : * passed in EPSG CRS code found in the PROJ database.
12121 : *
12122 : * This method is the same as the C function OSRImportFromEPSG().
12123 : *
12124 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12125 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12126 : * such method available for the CRS. This behavior might not always be
12127 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12128 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12129 : *
12130 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12131 : *
12132 : * @return OGRERR_NONE on success, or an error code on failure.
12133 : */
12134 :
12135 34430 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12136 :
12137 : {
12138 34430 : return importFromEPSGA(nCode);
12139 : }
12140 :
12141 : /************************************************************************/
12142 : /* OSRImportFromEPSG() */
12143 : /************************************************************************/
12144 :
12145 : /**
12146 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12147 : * code.
12148 : *
12149 : * This function is the same as OGRSpatialReference::importFromEPSG().
12150 : */
12151 :
12152 1402 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12153 :
12154 : {
12155 1402 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12156 :
12157 1402 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12158 : }
12159 :
12160 : /************************************************************************/
12161 : /* EPSGTreatsAsLatLong() */
12162 : /************************************************************************/
12163 :
12164 : /**
12165 : * \brief This method returns TRUE if this geographic coordinate
12166 : * system should be treated as having lat/long coordinate ordering.
12167 : *
12168 : * Currently this returns TRUE for all geographic coordinate systems
12169 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12170 : * also checked that the CRS had belonged to EPSG authority, but this check
12171 : * has now been removed).
12172 : *
12173 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12174 : * geographic CRS imported with importFromEPSG() would cause this method to
12175 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12176 : * is now equivalent to importFromEPSGA().
12177 : *
12178 : * FALSE will be returned for all coordinate systems that are not geographic,
12179 : * or whose axes ordering is not latitude, longitude.
12180 : *
12181 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12182 : *
12183 : * @return TRUE or FALSE.
12184 : */
12185 :
12186 713 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12187 :
12188 : {
12189 1426 : TAKE_OPTIONAL_LOCK();
12190 :
12191 713 : if (!IsGeographic())
12192 566 : return FALSE;
12193 :
12194 147 : d->demoteFromBoundCRS();
12195 :
12196 147 : bool ret = false;
12197 147 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12198 : {
12199 : auto horizCRS =
12200 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12201 3 : if (horizCRS)
12202 : {
12203 : auto cs =
12204 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12205 3 : if (cs)
12206 : {
12207 3 : const char *pszDirection = nullptr;
12208 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12209 : nullptr, &pszDirection, nullptr,
12210 3 : nullptr, nullptr, nullptr))
12211 : {
12212 3 : if (EQUAL(pszDirection, "north"))
12213 : {
12214 3 : ret = true;
12215 : }
12216 : }
12217 :
12218 3 : proj_destroy(cs);
12219 : }
12220 :
12221 3 : proj_destroy(horizCRS);
12222 : }
12223 : }
12224 : else
12225 : {
12226 : auto cs =
12227 144 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12228 144 : if (cs)
12229 : {
12230 144 : const char *pszDirection = nullptr;
12231 144 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12232 : nullptr, &pszDirection, nullptr, nullptr,
12233 144 : nullptr, nullptr))
12234 : {
12235 144 : if (EQUAL(pszDirection, "north"))
12236 : {
12237 117 : ret = true;
12238 : }
12239 : }
12240 :
12241 144 : proj_destroy(cs);
12242 : }
12243 : }
12244 147 : d->undoDemoteFromBoundCRS();
12245 :
12246 147 : return ret;
12247 : }
12248 :
12249 : /************************************************************************/
12250 : /* OSREPSGTreatsAsLatLong() */
12251 : /************************************************************************/
12252 :
12253 : /**
12254 : * \brief This function returns TRUE if this geographic coordinate
12255 : * system should be treated as having lat/long coordinate ordering.
12256 : *
12257 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12258 : */
12259 :
12260 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12261 :
12262 : {
12263 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12264 :
12265 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12266 : }
12267 :
12268 : /************************************************************************/
12269 : /* EPSGTreatsAsNorthingEasting() */
12270 : /************************************************************************/
12271 :
12272 : /**
12273 : * \brief This method returns TRUE if this projected coordinate
12274 : * system should be treated as having northing/easting coordinate ordering.
12275 : *
12276 : * Currently this returns TRUE for all projected coordinate systems
12277 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12278 : * also checked that the CRS had belonged to EPSG authority, but this check
12279 : * has now been removed).
12280 : *
12281 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12282 : * projected CRS with northing, easting axis order imported with
12283 : * importFromEPSG() would cause this method to
12284 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12285 : * is now equivalent to importFromEPSGA().
12286 : *
12287 : * FALSE will be returned for all coordinate systems that are not projected,
12288 : * or whose axes ordering is not northing, easting.
12289 : *
12290 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12291 : *
12292 : * @return TRUE or FALSE.
12293 : *
12294 : * @since OGR 1.10.0
12295 : */
12296 :
12297 603 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12298 :
12299 : {
12300 1206 : TAKE_OPTIONAL_LOCK();
12301 :
12302 603 : if (!IsProjected())
12303 24 : return FALSE;
12304 :
12305 579 : d->demoteFromBoundCRS();
12306 : PJ *projCRS;
12307 579 : const auto ctxt = d->getPROJContext();
12308 579 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12309 : {
12310 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12311 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12312 : {
12313 0 : d->undoDemoteFromBoundCRS();
12314 0 : proj_destroy(projCRS);
12315 0 : return FALSE;
12316 : }
12317 : }
12318 : else
12319 : {
12320 575 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12321 : }
12322 :
12323 579 : bool ret = false;
12324 579 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12325 579 : proj_destroy(projCRS);
12326 579 : d->undoDemoteFromBoundCRS();
12327 :
12328 579 : if (cs)
12329 : {
12330 579 : ret = isNorthEastAxisOrder(ctxt, cs);
12331 579 : proj_destroy(cs);
12332 : }
12333 :
12334 579 : return ret;
12335 : }
12336 :
12337 : /************************************************************************/
12338 : /* OSREPSGTreatsAsNorthingEasting() */
12339 : /************************************************************************/
12340 :
12341 : /**
12342 : * \brief This function returns TRUE if this projected coordinate
12343 : * system should be treated as having northing/easting coordinate ordering.
12344 : *
12345 : * This function is the same as
12346 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12347 : *
12348 : * @since OGR 1.10.0
12349 : */
12350 :
12351 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12352 :
12353 : {
12354 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12355 :
12356 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12357 : }
12358 :
12359 : /************************************************************************/
12360 : /* ImportFromESRIWisconsinWKT() */
12361 : /* */
12362 : /* Search a ESRI State Plane WKT and import it. */
12363 : /************************************************************************/
12364 :
12365 : // This is only used by the HFA driver and somewhat dubious we really need that
12366 : // Coming from an old ESRI merge
12367 :
12368 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12369 : double centralMeridian,
12370 : double latOfOrigin,
12371 : const char *unitsName,
12372 : const char *crsName)
12373 : {
12374 2 : TAKE_OPTIONAL_LOCK();
12375 :
12376 1 : if (centralMeridian < -93 || centralMeridian > -87)
12377 0 : return OGRERR_FAILURE;
12378 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12379 0 : return OGRERR_FAILURE;
12380 :
12381 : // If the CS name is known.
12382 1 : if (!prjName && !unitsName && crsName)
12383 : {
12384 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12385 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12386 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12387 0 : if (list)
12388 : {
12389 0 : if (proj_list_get_count(list) == 1)
12390 : {
12391 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12392 0 : if (crs)
12393 : {
12394 0 : Clear();
12395 0 : d->setPjCRS(crs);
12396 0 : proj_list_destroy(list);
12397 0 : return OGRERR_NONE;
12398 : }
12399 : }
12400 0 : proj_list_destroy(list);
12401 : }
12402 0 : return OGRERR_FAILURE;
12403 : }
12404 :
12405 1 : if (prjName == nullptr || unitsName == nullptr)
12406 : {
12407 0 : return OGRERR_FAILURE;
12408 : }
12409 :
12410 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12411 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12412 : "NAD_1983_HARN_WISCRS_", &type, 1,
12413 : true, 0, nullptr);
12414 1 : if (list)
12415 : {
12416 1 : const auto listSize = proj_list_get_count(list);
12417 8 : for (int i = 0; i < listSize; i++)
12418 : {
12419 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12420 8 : if (!crs)
12421 : {
12422 7 : continue;
12423 : }
12424 :
12425 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12426 8 : if (!conv)
12427 : {
12428 0 : proj_destroy(crs);
12429 0 : continue;
12430 : }
12431 8 : const char *pszMethodCode = nullptr;
12432 8 : proj_coordoperation_get_method_info(
12433 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12434 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12435 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12436 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12437 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12438 : nMethodCode ==
12439 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12440 : {
12441 3 : proj_destroy(crs);
12442 3 : proj_destroy(conv);
12443 3 : continue;
12444 : }
12445 :
12446 : auto coordSys =
12447 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12448 5 : if (!coordSys)
12449 : {
12450 0 : proj_destroy(crs);
12451 0 : proj_destroy(conv);
12452 0 : continue;
12453 : }
12454 :
12455 5 : double dfConvFactor = 0.0;
12456 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12457 : nullptr, nullptr, &dfConvFactor, nullptr,
12458 : nullptr, nullptr);
12459 5 : proj_destroy(coordSys);
12460 :
12461 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12462 1 : (!EQUAL(unitsName, "meters") &&
12463 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12464 : 1e-10))
12465 : {
12466 4 : proj_destroy(crs);
12467 4 : proj_destroy(conv);
12468 4 : continue;
12469 : }
12470 :
12471 1 : int idx_lat = proj_coordoperation_get_param_index(
12472 : d->getPROJContext(), conv,
12473 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12474 1 : double valueLat = -1000;
12475 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12476 : nullptr, nullptr, nullptr, &valueLat,
12477 : nullptr, nullptr, nullptr, nullptr,
12478 : nullptr, nullptr);
12479 1 : int idx_lon = proj_coordoperation_get_param_index(
12480 : d->getPROJContext(), conv,
12481 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12482 1 : double valueLong = -1000;
12483 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12484 : nullptr, nullptr, nullptr, &valueLong,
12485 : nullptr, nullptr, nullptr, nullptr,
12486 : nullptr, nullptr);
12487 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12488 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12489 : {
12490 1 : Clear();
12491 1 : d->setPjCRS(crs);
12492 1 : proj_list_destroy(list);
12493 1 : proj_destroy(conv);
12494 1 : return OGRERR_NONE;
12495 : }
12496 :
12497 0 : proj_destroy(crs);
12498 0 : proj_destroy(conv);
12499 : }
12500 0 : proj_list_destroy(list);
12501 : }
12502 :
12503 0 : return OGRERR_FAILURE;
12504 : }
12505 :
12506 : /************************************************************************/
12507 : /* GetAxisMappingStrategy() */
12508 : /************************************************************************/
12509 :
12510 : /** \brief Return the data axis to CRS axis mapping strategy.
12511 : *
12512 : * <ul>
12513 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12514 : * lat/long order, the data will still be long/lat ordered. Similarly for
12515 : * a projected CRS with northing/easting order, the data will still be
12516 : * easting/northing ordered.
12517 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12518 : * the CRS axis.
12519 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12520 : * SetDataAxisToSRSAxisMapping()
12521 : * </ul>
12522 : * @return the data axis to CRS axis mapping strategy.
12523 : * @since GDAL 3.0
12524 : */
12525 72 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12526 : {
12527 72 : TAKE_OPTIONAL_LOCK();
12528 :
12529 144 : return d->m_axisMappingStrategy;
12530 : }
12531 :
12532 : /************************************************************************/
12533 : /* OSRGetAxisMappingStrategy() */
12534 : /************************************************************************/
12535 :
12536 : /** \brief Return the data axis to CRS axis mapping strategy.
12537 : *
12538 : * See OGRSpatialReference::GetAxisMappingStrategy()
12539 : * @since GDAL 3.0
12540 : */
12541 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12542 : {
12543 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12544 :
12545 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12546 : }
12547 :
12548 : /************************************************************************/
12549 : /* SetAxisMappingStrategy() */
12550 : /************************************************************************/
12551 :
12552 : /** \brief Set the data axis to CRS axis mapping strategy.
12553 : *
12554 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12555 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12556 : * later being the default value when the option is not set) to control the
12557 : * value of the data axis to CRS axis mapping strategy when a
12558 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12559 : * override this default value.
12560 : *
12561 : * See OGRSpatialReference::GetAxisMappingStrategy()
12562 : * @since GDAL 3.0
12563 : */
12564 77548 : void OGRSpatialReference::SetAxisMappingStrategy(
12565 : OSRAxisMappingStrategy strategy)
12566 : {
12567 154995 : TAKE_OPTIONAL_LOCK();
12568 :
12569 77518 : d->m_axisMappingStrategy = strategy;
12570 77476 : d->refreshAxisMapping();
12571 77408 : }
12572 :
12573 : /************************************************************************/
12574 : /* OSRSetAxisMappingStrategy() */
12575 : /************************************************************************/
12576 :
12577 : /** \brief Set the data axis to CRS axis mapping strategy.
12578 : *
12579 : * See OGRSpatialReference::SetAxisMappingStrategy()
12580 : * @since GDAL 3.0
12581 : */
12582 845 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12583 : OSRAxisMappingStrategy strategy)
12584 : {
12585 845 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12586 :
12587 845 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12588 : }
12589 :
12590 : /************************************************************************/
12591 : /* GetDataAxisToSRSAxisMapping() */
12592 : /************************************************************************/
12593 :
12594 : /** \brief Return the data axis to SRS axis mapping.
12595 : *
12596 : * The number of elements of the vector will be the number of axis of the CRS.
12597 : * Values start at 1.
12598 : *
12599 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12600 : * for the first axis of the CRS.
12601 : *
12602 : * @since GDAL 3.0
12603 : */
12604 3655990 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12605 : {
12606 3655990 : TAKE_OPTIONAL_LOCK();
12607 :
12608 7311940 : return d->m_axisMapping;
12609 : }
12610 :
12611 : /************************************************************************/
12612 : /* OSRGetDataAxisToSRSAxisMapping() */
12613 : /************************************************************************/
12614 :
12615 : /** \brief Return the data axis to SRS axis mapping.
12616 : *
12617 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12618 : *
12619 : * @since GDAL 3.0
12620 : */
12621 200 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12622 : int *pnCount)
12623 : {
12624 200 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12625 200 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12626 :
12627 : const auto &v =
12628 200 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12629 200 : *pnCount = static_cast<int>(v.size());
12630 200 : return v.data();
12631 : }
12632 :
12633 : /************************************************************************/
12634 : /* SetDataAxisToSRSAxisMapping() */
12635 : /************************************************************************/
12636 :
12637 : /** \brief Set a custom data axis to CRS axis mapping.
12638 : *
12639 : * The number of elements of the mapping vector should be the number of axis
12640 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12641 : * check that, beyond checking there are at least 2 elements, so that this
12642 : * method and setting the CRS can be done in any order).
12643 : * This is taken into account by OGRCoordinateTransformation to transform the
12644 : * order of coordinates to the order expected by the CRS before
12645 : * transformation, and back to the data order after transformation.
12646 : *
12647 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12648 : * axis of the CRS. A negative value can also be used to ask for a sign
12649 : * reversal during coordinate transformation (to deal with northing vs southing,
12650 : * easting vs westing, heights vs depths).
12651 : *
12652 : * When used with OGRCoordinateTransformation,
12653 : * - the only valid values for mapping[0] (data axis number for the first axis
12654 : * of the CRS) are 1, 2, -1, -2.
12655 : * - the only valid values for mapping[1] (data axis number for the second axis
12656 : * of the CRS) are 1, 2, -1, -2.
12657 : * - the only valid values mapping[2] are 3 or -3.
12658 : * Note: this method does not validate the values of mapping[].
12659 : *
12660 : * mapping=[2,1] typically expresses the inversion of axis between the data
12661 : * axis and the CRS axis for a 2D CRS.
12662 : *
12663 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12664 : *
12665 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12666 : *
12667 : * @param mapping The new data axis to CRS axis mapping.
12668 : *
12669 : * @since GDAL 3.0
12670 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12671 : */
12672 6629 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12673 : const std::vector<int> &mapping)
12674 : {
12675 13258 : TAKE_OPTIONAL_LOCK();
12676 :
12677 6629 : if (mapping.size() < 2)
12678 0 : return OGRERR_FAILURE;
12679 6629 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12680 6629 : d->m_axisMapping = mapping;
12681 6629 : return OGRERR_NONE;
12682 : }
12683 :
12684 : /************************************************************************/
12685 : /* OSRSetDataAxisToSRSAxisMapping() */
12686 : /************************************************************************/
12687 :
12688 : /** \brief Set a custom data axis to CRS axis mapping.
12689 : *
12690 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12691 : *
12692 : * This is the same as the C++ method
12693 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12694 : *
12695 : * @since GDAL 3.1
12696 : */
12697 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12698 : int nMappingSize, const int *panMapping)
12699 : {
12700 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12701 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12702 : OGRERR_FAILURE);
12703 :
12704 15 : if (nMappingSize < 0)
12705 0 : return OGRERR_FAILURE;
12706 :
12707 30 : std::vector<int> mapping(nMappingSize);
12708 15 : if (nMappingSize)
12709 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12710 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12711 15 : mapping);
12712 : }
12713 :
12714 : /************************************************************************/
12715 : /* GetAreaOfUse() */
12716 : /************************************************************************/
12717 :
12718 : /** \brief Return the area of use of the CRS.
12719 : *
12720 : * This method is the same as the OSRGetAreaOfUse() function.
12721 : *
12722 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12723 : * longitude, expressed in degree. Might be NULL. If the returned value is
12724 : * -1000, the bounding box is unknown.
12725 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12726 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12727 : * the bounding box is unknown.
12728 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12729 : * longitude, expressed in degree. Might be NULL. If the returned value is
12730 : * -1000, the bounding box is unknown.
12731 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12732 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12733 : * the bounding box is unknown.
12734 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12735 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12736 : * invalidated by further calls.
12737 : * @return true in case of success
12738 : * @since GDAL 3.0
12739 : */
12740 32 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12741 : double *pdfSouthLatitudeDeg,
12742 : double *pdfEastLongitudeDeg,
12743 : double *pdfNorthLatitudeDeg,
12744 : const char **ppszAreaName) const
12745 : {
12746 64 : TAKE_OPTIONAL_LOCK();
12747 :
12748 32 : d->refreshProjObj();
12749 32 : if (!d->m_pj_crs)
12750 : {
12751 0 : return false;
12752 : }
12753 32 : d->demoteFromBoundCRS();
12754 32 : const char *pszAreaName = nullptr;
12755 32 : int bSuccess = proj_get_area_of_use(
12756 32 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12757 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12758 : &pszAreaName);
12759 32 : d->undoDemoteFromBoundCRS();
12760 32 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12761 32 : if (ppszAreaName)
12762 1 : *ppszAreaName = d->m_osAreaName.c_str();
12763 32 : return CPL_TO_BOOL(bSuccess);
12764 : }
12765 :
12766 : /************************************************************************/
12767 : /* GetAreaOfUse() */
12768 : /************************************************************************/
12769 :
12770 : /** \brief Return the area of use of the CRS.
12771 : *
12772 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12773 : *
12774 : * @since GDAL 3.0
12775 : */
12776 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12777 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12778 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12779 : {
12780 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12781 :
12782 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12783 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12784 1 : pdfNorthLatitudeDeg, ppszAreaName);
12785 : }
12786 :
12787 : /************************************************************************/
12788 : /* OSRGetCRSInfoListFromDatabase() */
12789 : /************************************************************************/
12790 :
12791 : /** \brief Enumerate CRS objects from the database.
12792 : *
12793 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12794 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12795 : *
12796 : * @param pszAuthName Authority name, used to restrict the search.
12797 : * Or NULL for all authorities.
12798 : * @param params Additional criteria. Must be set to NULL for now.
12799 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12800 : * the size of the result list. Might be NULL
12801 : * @return an array of OSRCRSInfo* pointers to be freed with
12802 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12803 : *
12804 : * @since GDAL 3.0
12805 : */
12806 : OSRCRSInfo **
12807 12 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12808 : CPL_UNUSED const OSRCRSListParameters *params,
12809 : int *pnOutResultCount)
12810 : {
12811 12 : int nResultCount = 0;
12812 12 : auto projList = proj_get_crs_info_list_from_database(
12813 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12814 12 : if (pnOutResultCount)
12815 12 : *pnOutResultCount = nResultCount;
12816 12 : if (!projList)
12817 : {
12818 0 : return nullptr;
12819 : }
12820 12 : auto res = new OSRCRSInfo *[nResultCount + 1];
12821 56167 : for (int i = 0; i < nResultCount; i++)
12822 : {
12823 56155 : res[i] = new OSRCRSInfo;
12824 112310 : res[i]->pszAuthName = projList[i]->auth_name
12825 56155 : ? CPLStrdup(projList[i]->auth_name)
12826 : : nullptr;
12827 56155 : res[i]->pszCode =
12828 56155 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12829 56155 : res[i]->pszName =
12830 56155 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12831 56155 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12832 56155 : switch (projList[i]->type)
12833 : {
12834 5273 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12835 5273 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12836 5273 : break;
12837 1821 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12838 1821 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12839 1821 : break;
12840 1910 : case PJ_TYPE_GEOCENTRIC_CRS:
12841 1910 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12842 1910 : break;
12843 42888 : case PJ_TYPE_PROJECTED_CRS:
12844 42888 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12845 42888 : break;
12846 1820 : case PJ_TYPE_VERTICAL_CRS:
12847 1820 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12848 1820 : break;
12849 2443 : case PJ_TYPE_COMPOUND_CRS:
12850 2443 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12851 2443 : break;
12852 0 : default:
12853 0 : break;
12854 : }
12855 56155 : res[i]->bDeprecated = projList[i]->deprecated;
12856 56155 : res[i]->bBboxValid = projList[i]->bbox_valid;
12857 56155 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12858 56155 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12859 56155 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12860 56155 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12861 112310 : res[i]->pszAreaName = projList[i]->area_name
12862 56155 : ? CPLStrdup(projList[i]->area_name)
12863 : : nullptr;
12864 56155 : res[i]->pszProjectionMethod =
12865 56155 : projList[i]->projection_method_name
12866 56155 : ? CPLStrdup(projList[i]->projection_method_name)
12867 : : nullptr;
12868 : }
12869 12 : res[nResultCount] = nullptr;
12870 12 : proj_crs_info_list_destroy(projList);
12871 12 : return res;
12872 : }
12873 :
12874 : /************************************************************************/
12875 : /* OSRDestroyCRSInfoList() */
12876 : /************************************************************************/
12877 :
12878 : /** \brief Destroy the result returned by
12879 : * OSRGetCRSInfoListFromDatabase().
12880 : *
12881 : * @since GDAL 3.0
12882 : */
12883 12 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12884 : {
12885 12 : if (list)
12886 : {
12887 56167 : for (int i = 0; list[i] != nullptr; i++)
12888 : {
12889 56155 : CPLFree(list[i]->pszAuthName);
12890 56155 : CPLFree(list[i]->pszCode);
12891 56155 : CPLFree(list[i]->pszName);
12892 56155 : CPLFree(list[i]->pszAreaName);
12893 56155 : CPLFree(list[i]->pszProjectionMethod);
12894 56155 : delete list[i];
12895 : }
12896 12 : delete[] list;
12897 : }
12898 12 : }
12899 :
12900 : /************************************************************************/
12901 : /* OSRGetAuthorityListFromDatabase() */
12902 : /************************************************************************/
12903 :
12904 : /** \brief Return the list of CRS authorities used in the PROJ database.
12905 : *
12906 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
12907 : *
12908 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
12909 : *
12910 : * @return nullptr in case of error, or a NULL terminated list of strings to
12911 : * free with CSLDestroy()
12912 : * @since GDAL 3.10
12913 : */
12914 2 : char **OSRGetAuthorityListFromDatabase()
12915 : {
12916 : PROJ_STRING_LIST list =
12917 2 : proj_get_authorities_from_database(OSRGetProjTLSContext());
12918 2 : if (!list)
12919 : {
12920 0 : return nullptr;
12921 : }
12922 2 : int count = 0;
12923 12 : while (list[count])
12924 10 : ++count;
12925 2 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
12926 12 : for (int i = 0; i < count; ++i)
12927 10 : res[i] = CPLStrdup(list[i]);
12928 2 : proj_string_list_destroy(list);
12929 2 : return res;
12930 : }
12931 :
12932 : /************************************************************************/
12933 : /* UpdateCoordinateSystemFromGeogCRS() */
12934 : /************************************************************************/
12935 :
12936 : /*! @cond Doxygen_Suppress */
12937 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12938 : *
12939 : * @since GDAL 3.1
12940 : */
12941 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12942 : {
12943 1 : TAKE_OPTIONAL_LOCK();
12944 :
12945 1 : d->refreshProjObj();
12946 1 : if (!d->m_pj_crs)
12947 0 : return;
12948 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12949 0 : return;
12950 1 : if (GetAxesCount() == 3)
12951 0 : return;
12952 1 : auto ctxt = d->getPROJContext();
12953 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12954 1 : if (!baseCRS)
12955 0 : return;
12956 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12957 1 : if (!baseCRSCS)
12958 : {
12959 0 : proj_destroy(baseCRS);
12960 0 : return;
12961 : }
12962 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12963 : {
12964 0 : proj_destroy(baseCRSCS);
12965 0 : proj_destroy(baseCRS);
12966 0 : return;
12967 : }
12968 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12969 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12970 : {
12971 0 : proj_destroy(baseCRSCS);
12972 0 : proj_destroy(baseCRS);
12973 0 : proj_destroy(projCS);
12974 0 : return;
12975 : }
12976 :
12977 : PJ_AXIS_DESCRIPTION axis[3];
12978 4 : for (int i = 0; i < 3; i++)
12979 : {
12980 3 : const char *name = nullptr;
12981 3 : const char *abbreviation = nullptr;
12982 3 : const char *direction = nullptr;
12983 3 : double unit_conv_factor = 0;
12984 3 : const char *unit_name = nullptr;
12985 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
12986 : &abbreviation, &direction, &unit_conv_factor,
12987 : &unit_name, nullptr, nullptr);
12988 3 : axis[i].name = CPLStrdup(name);
12989 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
12990 3 : axis[i].direction = CPLStrdup(direction);
12991 3 : axis[i].unit_name = CPLStrdup(unit_name);
12992 3 : axis[i].unit_conv_factor = unit_conv_factor;
12993 3 : axis[i].unit_type = PJ_UT_LINEAR;
12994 : }
12995 1 : proj_destroy(baseCRSCS);
12996 1 : proj_destroy(projCS);
12997 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
12998 4 : for (int i = 0; i < 3; i++)
12999 : {
13000 3 : CPLFree(axis[i].name);
13001 3 : CPLFree(axis[i].abbreviation);
13002 3 : CPLFree(axis[i].direction);
13003 3 : CPLFree(axis[i].unit_name);
13004 : }
13005 1 : if (!cs)
13006 : {
13007 0 : proj_destroy(baseCRS);
13008 0 : return;
13009 : }
13010 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13011 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13012 : conversion, cs);
13013 1 : proj_destroy(baseCRS);
13014 1 : proj_destroy(conversion);
13015 1 : proj_destroy(cs);
13016 1 : d->setPjCRS(crs);
13017 : }
13018 :
13019 : /*! @endcond */
13020 :
13021 : /************************************************************************/
13022 : /* PromoteTo3D() */
13023 : /************************************************************************/
13024 :
13025 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13026 : *
13027 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13028 : * units.
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.1 and PROJ 6.3
13034 : */
13035 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13036 : {
13037 84 : TAKE_OPTIONAL_LOCK();
13038 :
13039 42 : d->refreshProjObj();
13040 42 : if (!d->m_pj_crs)
13041 0 : return OGRERR_FAILURE;
13042 : auto newPj =
13043 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13044 42 : if (!newPj)
13045 0 : return OGRERR_FAILURE;
13046 42 : d->setPjCRS(newPj);
13047 42 : return OGRERR_NONE;
13048 : }
13049 :
13050 : /************************************************************************/
13051 : /* OSRPromoteTo3D() */
13052 : /************************************************************************/
13053 :
13054 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13055 : *
13056 : * See OGRSpatialReference::PromoteTo3D()
13057 : *
13058 : * @since GDAL 3.1 and PROJ 6.3
13059 : */
13060 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13061 : {
13062 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13063 :
13064 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13065 : }
13066 :
13067 : /************************************************************************/
13068 : /* DemoteTo2D() */
13069 : /************************************************************************/
13070 :
13071 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13072 : *
13073 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13074 : * be used.
13075 : * @return OGRERR_NONE if no error occurred.
13076 : * @since GDAL 3.2 and PROJ 6.3
13077 : */
13078 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13079 : {
13080 90 : TAKE_OPTIONAL_LOCK();
13081 :
13082 45 : d->refreshProjObj();
13083 45 : if (!d->m_pj_crs)
13084 0 : return OGRERR_FAILURE;
13085 : auto newPj =
13086 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13087 45 : if (!newPj)
13088 0 : return OGRERR_FAILURE;
13089 45 : d->setPjCRS(newPj);
13090 45 : return OGRERR_NONE;
13091 : }
13092 :
13093 : /************************************************************************/
13094 : /* OSRDemoteTo2D() */
13095 : /************************************************************************/
13096 :
13097 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13098 : *
13099 : * See OGRSpatialReference::DemoteTo2D()
13100 : *
13101 : * @since GDAL 3.2 and PROJ 6.3
13102 : */
13103 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13104 : {
13105 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13106 :
13107 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13108 : }
13109 :
13110 : /************************************************************************/
13111 : /* GetEPSGGeogCS() */
13112 : /************************************************************************/
13113 :
13114 : /** Try to establish what the EPSG code for this coordinate systems
13115 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13116 : *
13117 : * @return EPSG code
13118 : */
13119 :
13120 332 : int OGRSpatialReference::GetEPSGGeogCS() const
13121 :
13122 : {
13123 664 : TAKE_OPTIONAL_LOCK();
13124 :
13125 : /* -------------------------------------------------------------------- */
13126 : /* Check axis order. */
13127 : /* -------------------------------------------------------------------- */
13128 664 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13129 332 : if (!poGeogCRS)
13130 0 : return -1;
13131 :
13132 332 : bool ret = false;
13133 332 : poGeogCRS->d->demoteFromBoundCRS();
13134 332 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13135 332 : poGeogCRS->d->m_pj_crs);
13136 332 : poGeogCRS->d->undoDemoteFromBoundCRS();
13137 332 : if (cs)
13138 : {
13139 332 : const char *pszDirection = nullptr;
13140 332 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13141 : &pszDirection, nullptr, nullptr, nullptr,
13142 332 : nullptr))
13143 : {
13144 332 : if (EQUAL(pszDirection, "north"))
13145 : {
13146 134 : ret = true;
13147 : }
13148 : }
13149 :
13150 332 : proj_destroy(cs);
13151 : }
13152 332 : if (!ret)
13153 198 : return -1;
13154 :
13155 : /* -------------------------------------------------------------------- */
13156 : /* Do we already have it? */
13157 : /* -------------------------------------------------------------------- */
13158 134 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13159 134 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13160 59 : return atoi(GetAuthorityCode("GEOGCS"));
13161 :
13162 : /* -------------------------------------------------------------------- */
13163 : /* Get the datum and geogcs names. */
13164 : /* -------------------------------------------------------------------- */
13165 :
13166 75 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13167 75 : const char *pszDatum = GetAttrValue("DATUM");
13168 :
13169 : // We can only operate on coordinate systems with a geogcs.
13170 150 : OGRSpatialReference oSRSTmp;
13171 75 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13172 : {
13173 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13174 : // export to WKT1, so try to extract the geographic CRS through PROJ
13175 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13176 1 : oSRSTmp.CopyGeogCSFrom(this);
13177 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13178 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13179 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13180 : {
13181 0 : return -1;
13182 : }
13183 : }
13184 :
13185 : // Lookup geographic CRS name
13186 75 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13187 75 : PJ_OBJ_LIST *list = proj_create_from_name(
13188 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13189 75 : if (list)
13190 : {
13191 75 : const auto listSize = proj_list_get_count(list);
13192 75 : if (listSize == 1)
13193 : {
13194 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13195 49 : if (crs)
13196 : {
13197 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13198 49 : const char *pszCode = proj_get_id_code(crs, 0);
13199 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13200 : {
13201 47 : const int nCode = atoi(pszCode);
13202 47 : proj_destroy(crs);
13203 47 : proj_list_destroy(list);
13204 47 : return nCode;
13205 : }
13206 2 : proj_destroy(crs);
13207 : }
13208 : }
13209 28 : proj_list_destroy(list);
13210 : }
13211 :
13212 : /* -------------------------------------------------------------------- */
13213 : /* Is this a "well known" geographic coordinate system? */
13214 : /* -------------------------------------------------------------------- */
13215 84 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13216 28 : strstr(pszDatum, "WGS") ||
13217 28 : strstr(pszGEOGCS, "World Geodetic System") ||
13218 28 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13219 84 : strstr(pszDatum, "World Geodetic System") ||
13220 28 : strstr(pszDatum, "World_Geodetic_System");
13221 :
13222 84 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13223 28 : strstr(pszDatum, "NAD") ||
13224 28 : strstr(pszGEOGCS, "North American") ||
13225 28 : strstr(pszGEOGCS, "North_American") ||
13226 84 : strstr(pszDatum, "North American") ||
13227 28 : strstr(pszDatum, "North_American");
13228 :
13229 28 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13230 0 : return 4326;
13231 :
13232 28 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13233 0 : return 4322;
13234 :
13235 : // This is questionable as there are several 'flavors' of NAD83 that
13236 : // are not the same as 4269
13237 28 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13238 0 : return 4269;
13239 :
13240 28 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13241 0 : return 4267;
13242 :
13243 : /* -------------------------------------------------------------------- */
13244 : /* If we know the datum, associate the most likely GCS with */
13245 : /* it. */
13246 : /* -------------------------------------------------------------------- */
13247 28 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13248 28 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13249 28 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13250 0 : GetPrimeMeridian() == 0.0)
13251 : {
13252 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13253 :
13254 0 : if (nDatum >= 6000 && nDatum <= 6999)
13255 0 : return nDatum - 2000;
13256 : }
13257 :
13258 28 : return -1;
13259 : }
13260 :
13261 : /************************************************************************/
13262 : /* SetCoordinateEpoch() */
13263 : /************************************************************************/
13264 :
13265 : /** Set the coordinate epoch, as decimal year.
13266 : *
13267 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13268 : * change with time. To be unambiguous the coordinates must always be qualified
13269 : * with the epoch at which they are valid. The coordinate epoch is not
13270 : * necessarily the epoch at which the observation was collected.
13271 : *
13272 : * Pedantically the coordinate epoch of an observation belongs to the
13273 : * observation, and not to the CRS, however it is often more practical to
13274 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13275 : * CRS (see IsDynamic())
13276 : *
13277 : * This method is the same as the OSRSetCoordinateEpoch() function.
13278 : *
13279 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13280 : * @since OGR 3.4
13281 : */
13282 :
13283 787 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13284 : {
13285 787 : d->m_coordinateEpoch = dfCoordinateEpoch;
13286 787 : }
13287 :
13288 : /************************************************************************/
13289 : /* OSRSetCoordinateEpoch() */
13290 : /************************************************************************/
13291 :
13292 : /** \brief Set the coordinate epoch, as decimal year.
13293 : *
13294 : * See OGRSpatialReference::SetCoordinateEpoch()
13295 : *
13296 : * @since OGR 3.4
13297 : */
13298 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13299 : {
13300 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13301 :
13302 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13303 31 : dfCoordinateEpoch);
13304 : }
13305 :
13306 : /************************************************************************/
13307 : /* GetCoordinateEpoch() */
13308 : /************************************************************************/
13309 :
13310 : /** Return the coordinate epoch, as decimal year.
13311 : *
13312 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13313 : * change with time. To be unambiguous the coordinates must always be qualified
13314 : * with the epoch at which they are valid. The coordinate epoch is not
13315 : * necessarily the epoch at which the observation was collected.
13316 : *
13317 : * Pedantically the coordinate epoch of an observation belongs to the
13318 : * observation, and not to the CRS, however it is often more practical to
13319 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13320 : * CRS (see IsDynamic())
13321 : *
13322 : * This method is the same as the OSRGetCoordinateEpoch() function.
13323 : *
13324 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13325 : * if not set, or relevant.
13326 : * @since OGR 3.4
13327 : */
13328 :
13329 10679 : double OGRSpatialReference::GetCoordinateEpoch() const
13330 : {
13331 10679 : return d->m_coordinateEpoch;
13332 : }
13333 :
13334 : /************************************************************************/
13335 : /* OSRGetCoordinateEpoch() */
13336 : /************************************************************************/
13337 :
13338 : /** \brief Get the coordinate epoch, as decimal year.
13339 : *
13340 : * See OGRSpatialReference::GetCoordinateEpoch()
13341 : *
13342 : * @since OGR 3.4
13343 : */
13344 617 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13345 : {
13346 617 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13347 :
13348 617 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13349 : }
|