Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGRSpatialReference class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
9 : * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_spatialref.h"
16 :
17 : #include <cmath>
18 : #include <cstddef>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <limits>
23 : #include <string>
24 : #include <mutex>
25 : #include <set>
26 : #include <vector>
27 :
28 : #include "cpl_atomic_ops.h"
29 : #include "cpl_conv.h"
30 : #include "cpl_csv.h"
31 : #include "cpl_error.h"
32 : #include "cpl_error_internal.h"
33 : #include "cpl_http.h"
34 : #include "cpl_json.h"
35 : #include "cpl_multiproc.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 : #include "ogr_core.h"
39 : #include "ogr_p.h"
40 : #include "ogr_proj_p.h"
41 : #include "ogr_srs_api.h"
42 : #include "ogrmitabspatialref.h"
43 :
44 : #include "proj.h"
45 : #include "proj_experimental.h"
46 : #include "proj_constants.h"
47 :
48 : bool GDALThreadLocalDatasetCacheIsInDestruction();
49 :
50 : // Exists since 8.0.1
51 : #ifndef PROJ_AT_LEAST_VERSION
52 : #define PROJ_COMPUTE_VERSION(maj, min, patch) \
53 : ((maj)*10000 + (min)*100 + (patch))
54 : #define PROJ_VERSION_NUMBER \
55 : PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, \
56 : PROJ_VERSION_PATCH)
57 : #define PROJ_AT_LEAST_VERSION(maj, min, patch) \
58 : (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
59 : #endif
60 :
61 : #define STRINGIFY(s) #s
62 : #define XSTRINGIFY(s) STRINGIFY(s)
63 :
64 : struct OGRSpatialReference::Private
65 : {
66 : struct Listener final : public OGR_SRSNode::Listener
67 : {
68 : OGRSpatialReference::Private *m_poObj = nullptr;
69 :
70 227965 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71 : {
72 227974 : }
73 :
74 : Listener(const Listener &) = delete;
75 : Listener &operator=(const Listener &) = delete;
76 :
77 : void notifyChange(OGR_SRSNode *) override;
78 : };
79 :
80 : OGRSpatialReference *m_poSelf = nullptr;
81 : PJ *m_pj_crs = nullptr;
82 :
83 : // Temporary state used for object construction
84 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85 : CPLString m_osPrimeMeridianName{};
86 : CPLString m_osAngularUnits{};
87 : CPLString m_osLinearUnits{};
88 : CPLString m_osAxisName[3]{};
89 :
90 : std::vector<std::string> m_wktImportWarnings{};
91 : std::vector<std::string> m_wktImportErrors{};
92 : CPLString m_osAreaName{};
93 : CPLString m_celestialBodyName{};
94 :
95 : bool m_bIsThreadSafe = false;
96 : bool m_bNodesChanged = false;
97 : bool m_bNodesWKT2 = false;
98 : OGR_SRSNode *m_poRoot = nullptr;
99 :
100 : double dfFromGreenwich = 0.0;
101 : double dfToMeter = 0.0;
102 : double dfToDegrees = 0.0;
103 : double m_dfAngularUnitToRadian = 0.0;
104 :
105 : int nRefCount = 1;
106 : int bNormInfoSet = FALSE;
107 :
108 : PJ *m_pj_geod_base_crs_temp = nullptr;
109 : PJ *m_pj_proj_crs_cs_temp = nullptr;
110 :
111 : bool m_pj_crs_modified_during_demote = false;
112 : PJ *m_pj_bound_crs_target = nullptr;
113 : PJ *m_pj_bound_crs_co = nullptr;
114 : PJ *m_pj_crs_backup = nullptr;
115 : OGR_SRSNode *m_poRootBackup = nullptr;
116 :
117 : bool m_bMorphToESRI = false;
118 : bool m_bHasCenterLong = false;
119 :
120 : std::shared_ptr<Listener> m_poListener{};
121 :
122 : std::recursive_mutex m_mutex{};
123 :
124 : OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125 : std::vector<int> m_axisMapping{1, 2, 3};
126 :
127 : double m_coordinateEpoch = 0; // as decimal year
128 :
129 : explicit Private(OGRSpatialReference *poSelf);
130 : ~Private();
131 : Private(const Private &) = delete;
132 : Private &operator=(const Private &) = delete;
133 :
134 2 : void SetThreadSafe()
135 : {
136 2 : m_bIsThreadSafe = true;
137 2 : }
138 :
139 : void clear();
140 : void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141 : void setRoot(OGR_SRSNode *poRoot);
142 : void refreshProjObj();
143 : void nodesChanged();
144 : void refreshRootFromProjObj();
145 : void invalidateNodes();
146 :
147 : void setMorphToESRI(bool b);
148 :
149 : PJ *getGeodBaseCRS();
150 : PJ *getProjCRSCoordSys();
151 :
152 : const char *getProjCRSName();
153 : OGRErr replaceConversionAndUnref(PJ *conv);
154 :
155 : void demoteFromBoundCRS();
156 : void undoDemoteFromBoundCRS();
157 :
158 1139880 : PJ_CONTEXT *getPROJContext()
159 : {
160 1139880 : 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 5156890 : explicit OptionalLockGuard(Private *p) : m_private(*p)
180 : {
181 5156890 : if (m_private.m_bIsThreadSafe)
182 3798 : m_private.m_mutex.lock();
183 5156890 : }
184 :
185 5156980 : ~OptionalLockGuard()
186 5156980 : {
187 5156980 : if (m_private.m_bIsThreadSafe)
188 3798 : m_private.m_mutex.unlock();
189 5156980 : }
190 : };
191 :
192 5156950 : inline OptionalLockGuard GetOptionalLockGuard()
193 : {
194 5156950 : return OptionalLockGuard(this);
195 : }
196 : };
197 :
198 2108770 : void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199 : {
200 2108770 : m_poObj->nodesChanged();
201 2108770 : }
202 :
203 : #define TAKE_OPTIONAL_LOCK() \
204 : auto lock = d->GetOptionalLockGuard(); \
205 : CPL_IGNORE_RET_VAL(lock)
206 :
207 227775 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208 : {
209 : const char *pszDefaultAMS =
210 227775 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211 228051 : if (pszDefaultAMS)
212 : {
213 1 : if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
214 0 : return OAMS_AUTHORITY_COMPLIANT;
215 1 : else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
216 1 : return OAMS_TRADITIONAL_GIS_ORDER;
217 : else
218 : {
219 0 : CPLError(CE_Failure, CPLE_AppDefined,
220 : "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
221 : pszDefaultAMS);
222 : }
223 : }
224 228050 : return OAMS_AUTHORITY_COMPLIANT;
225 : }
226 :
227 228008 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228 : : m_poSelf(poSelf),
229 228008 : m_poListener(std::shared_ptr<Listener>(new Listener(this)))
230 : {
231 : // Get the default value for m_axisMappingStrategy from the
232 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
233 227788 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
234 228051 : }
235 :
236 905813 : OGRSpatialReference::Private::~Private()
237 : {
238 : // In case we destroy the object not in the thread that created it,
239 : // we need to reassign the PROJ context. Having the context bundled inside
240 : // PJ* deeply sucks...
241 226512 : PJ_CONTEXT *pj_context_to_destroy = nullptr;
242 : PJ_CONTEXT *ctxt;
243 226512 : if (GDALThreadLocalDatasetCacheIsInDestruction())
244 : {
245 184 : pj_context_to_destroy = proj_context_create();
246 184 : ctxt = pj_context_to_destroy;
247 : }
248 : else
249 : {
250 226306 : ctxt = getPROJContext();
251 : }
252 :
253 226514 : proj_assign_context(m_pj_crs, ctxt);
254 226514 : proj_destroy(m_pj_crs);
255 :
256 226513 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
257 226513 : proj_destroy(m_pj_geod_base_crs_temp);
258 :
259 226509 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
260 226511 : proj_destroy(m_pj_proj_crs_cs_temp);
261 :
262 226510 : proj_assign_context(m_pj_bound_crs_target, ctxt);
263 226510 : proj_destroy(m_pj_bound_crs_target);
264 :
265 226511 : proj_assign_context(m_pj_bound_crs_co, ctxt);
266 226512 : proj_destroy(m_pj_bound_crs_co);
267 :
268 226511 : proj_assign_context(m_pj_crs_backup, ctxt);
269 226511 : proj_destroy(m_pj_crs_backup);
270 :
271 226506 : delete m_poRootBackup;
272 226513 : delete m_poRoot;
273 226513 : proj_context_destroy(pj_context_to_destroy);
274 226449 : }
275 :
276 119003 : void OGRSpatialReference::Private::clear()
277 : {
278 119003 : proj_assign_context(m_pj_crs, getPROJContext());
279 119003 : proj_destroy(m_pj_crs);
280 119003 : m_pj_crs = nullptr;
281 :
282 119003 : delete m_poRoot;
283 119003 : m_poRoot = nullptr;
284 119003 : m_bNodesChanged = false;
285 :
286 119003 : m_wktImportWarnings.clear();
287 119003 : m_wktImportErrors.clear();
288 :
289 119002 : m_pj_crs_modified_during_demote = false;
290 119002 : m_pjType = PJ_TYPE_UNKNOWN;
291 119002 : m_osPrimeMeridianName.clear();
292 119003 : m_osAngularUnits.clear();
293 119002 : m_osLinearUnits.clear();
294 :
295 119002 : bNormInfoSet = FALSE;
296 119002 : dfFromGreenwich = 1.0;
297 119002 : dfToMeter = 1.0;
298 119002 : dfToDegrees = 1.0;
299 119002 : m_dfAngularUnitToRadian = 0.0;
300 :
301 119002 : m_bMorphToESRI = false;
302 119002 : m_bHasCenterLong = false;
303 :
304 119002 : m_coordinateEpoch = 0.0;
305 119002 : }
306 :
307 26480 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
308 : {
309 26480 : m_poRoot = poRoot;
310 26480 : if (m_poRoot)
311 : {
312 26480 : m_poRoot->RegisterListener(m_poListener);
313 : }
314 26480 : nodesChanged();
315 26480 : }
316 :
317 179968 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
318 : bool doRefreshAxisMapping)
319 : {
320 179968 : auto ctxt = getPROJContext();
321 :
322 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
323 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
324 : {
325 : const double dfEpoch =
326 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
327 : if (!std::isnan(dfEpoch))
328 : {
329 : m_poSelf->SetCoordinateEpoch(dfEpoch);
330 : }
331 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
332 : proj_destroy(pj_crsIn);
333 : pj_crsIn = crs;
334 : }
335 : #endif
336 :
337 179968 : proj_assign_context(m_pj_crs, ctxt);
338 179968 : proj_destroy(m_pj_crs);
339 179968 : m_pj_crs = pj_crsIn;
340 179968 : if (m_pj_crs)
341 : {
342 179917 : m_pjType = proj_get_type(m_pj_crs);
343 : }
344 179968 : if (m_pj_crs_backup)
345 : {
346 21 : m_pj_crs_modified_during_demote = true;
347 : }
348 179968 : invalidateNodes();
349 179967 : if (doRefreshAxisMapping)
350 : {
351 179948 : refreshAxisMapping();
352 : }
353 179966 : }
354 :
355 726493 : void OGRSpatialReference::Private::refreshProjObj()
356 : {
357 726493 : if (m_bNodesChanged && m_poRoot)
358 : {
359 8233 : char *pszWKT = nullptr;
360 8233 : m_poRoot->exportToWkt(&pszWKT);
361 8233 : auto poRootBackup = m_poRoot;
362 8233 : m_poRoot = nullptr;
363 8233 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
364 8233 : clear();
365 8233 : m_coordinateEpoch = dfCoordinateEpochBackup;
366 8233 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
367 :
368 8233 : const char *const options[] = {
369 : "STRICT=NO",
370 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
371 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
372 : #endif
373 : nullptr
374 : };
375 8233 : PROJ_STRING_LIST warnings = nullptr;
376 8233 : PROJ_STRING_LIST errors = nullptr;
377 8233 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
378 : &warnings, &errors));
379 16071 : for (auto iter = warnings; iter && *iter; ++iter)
380 : {
381 7838 : m_wktImportWarnings.push_back(*iter);
382 : }
383 8448 : for (auto iter = errors; iter && *iter; ++iter)
384 : {
385 215 : m_wktImportErrors.push_back(*iter);
386 : }
387 8233 : proj_string_list_destroy(warnings);
388 8233 : proj_string_list_destroy(errors);
389 :
390 8233 : CPLFree(pszWKT);
391 :
392 8233 : m_poRoot = poRootBackup;
393 8233 : m_bNodesChanged = false;
394 : }
395 726493 : }
396 :
397 28602 : void OGRSpatialReference::Private::refreshRootFromProjObj()
398 : {
399 28602 : CPLAssert(m_poRoot == nullptr);
400 :
401 28602 : if (m_pj_crs)
402 : {
403 52872 : CPLStringList aosOptions;
404 26436 : if (!m_bMorphToESRI)
405 : {
406 26432 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
407 26432 : aosOptions.SetNameValue("MULTILINE", "NO");
408 : }
409 26436 : aosOptions.SetNameValue("STRICT", "NO");
410 :
411 : const char *pszWKT;
412 : {
413 26436 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
414 26436 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
415 26436 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
416 26436 : aosOptions.List());
417 26436 : m_bNodesWKT2 = false;
418 : }
419 26436 : if (!m_bMorphToESRI && pszWKT == nullptr)
420 : {
421 70 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
422 70 : aosOptions.List());
423 70 : m_bNodesWKT2 = true;
424 : }
425 26436 : if (pszWKT)
426 : {
427 26436 : auto root = new OGR_SRSNode();
428 26436 : setRoot(root);
429 26436 : root->importFromWkt(&pszWKT);
430 26436 : m_bNodesChanged = false;
431 : }
432 : }
433 28602 : }
434 :
435 208123 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
436 : {
437 208123 : const char *pszName1 = nullptr;
438 208123 : const char *pszDirection1 = nullptr;
439 208123 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
440 : nullptr, nullptr, nullptr, nullptr);
441 208123 : const char *pszName2 = nullptr;
442 208123 : const char *pszDirection2 = nullptr;
443 208123 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
444 : nullptr, nullptr, nullptr, nullptr);
445 208122 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
446 91717 : EQUAL(pszDirection2, "east"))
447 : {
448 90310 : return true;
449 : }
450 117812 : if (pszDirection1 && pszDirection2 &&
451 117813 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
452 116423 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
453 2777 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
454 1083 : STARTS_WITH_CI(pszName2, "easting"))
455 : {
456 1083 : return true;
457 : }
458 116729 : return false;
459 : }
460 :
461 265623 : void OGRSpatialReference::Private::refreshAxisMapping()
462 : {
463 265623 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
464 57978 : return;
465 :
466 207645 : bool doUndoDemote = false;
467 207645 : if (m_pj_crs_backup == nullptr)
468 : {
469 207623 : doUndoDemote = true;
470 207623 : demoteFromBoundCRS();
471 : }
472 207645 : const auto ctxt = getPROJContext();
473 207646 : PJ *horizCRS = nullptr;
474 207646 : int axisCount = 0;
475 207646 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
476 : {
477 218 : axisCount = 1;
478 : }
479 207428 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
480 : {
481 1100 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
482 1100 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
483 : {
484 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
485 222 : if (baseCRS)
486 : {
487 222 : proj_destroy(horizCRS);
488 222 : horizCRS = baseCRS;
489 : }
490 : }
491 :
492 1100 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
493 1100 : if (vertCRS)
494 : {
495 1097 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
496 : {
497 391 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
498 391 : if (baseCRS)
499 : {
500 391 : proj_destroy(vertCRS);
501 391 : vertCRS = baseCRS;
502 : }
503 : }
504 :
505 1097 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
506 1097 : if (cs)
507 : {
508 1097 : axisCount += proj_cs_get_axis_count(ctxt, cs);
509 1097 : proj_destroy(cs);
510 : }
511 1097 : proj_destroy(vertCRS);
512 : }
513 : }
514 : else
515 : {
516 206328 : horizCRS = m_pj_crs;
517 : }
518 :
519 207646 : bool bSwitchForGisFriendlyOrder = false;
520 207646 : if (horizCRS)
521 : {
522 207425 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
523 207425 : if (cs)
524 : {
525 207425 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
526 207425 : axisCount += nHorizCSAxisCount;
527 207425 : if (nHorizCSAxisCount >= 2)
528 : {
529 207415 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
530 : }
531 207425 : proj_destroy(cs);
532 : }
533 : }
534 207634 : if (horizCRS != m_pj_crs)
535 : {
536 1318 : proj_destroy(horizCRS);
537 : }
538 207634 : if (doUndoDemote)
539 : {
540 207625 : undoDemoteFromBoundCRS();
541 : }
542 :
543 207634 : m_axisMapping.resize(axisCount);
544 207644 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
545 61383 : !bSwitchForGisFriendlyOrder)
546 : {
547 541027 : for (int i = 0; i < axisCount; i++)
548 : {
549 361151 : m_axisMapping[i] = i + 1;
550 179876 : }
551 : }
552 : else
553 : {
554 27768 : m_axisMapping[0] = 2;
555 27768 : m_axisMapping[1] = 1;
556 27768 : if (axisCount == 3)
557 : {
558 338 : m_axisMapping[2] = 3;
559 : }
560 : }
561 : }
562 :
563 2135250 : void OGRSpatialReference::Private::nodesChanged()
564 : {
565 2135250 : m_bNodesChanged = true;
566 2135250 : }
567 :
568 180224 : void OGRSpatialReference::Private::invalidateNodes()
569 : {
570 180224 : delete m_poRoot;
571 180224 : m_poRoot = nullptr;
572 180224 : m_bNodesChanged = false;
573 180224 : }
574 :
575 256 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
576 : {
577 256 : invalidateNodes();
578 256 : m_bMorphToESRI = b;
579 256 : }
580 :
581 611413 : void OGRSpatialReference::Private::demoteFromBoundCRS()
582 : {
583 611413 : CPLAssert(m_pj_bound_crs_target == nullptr);
584 611413 : CPLAssert(m_pj_bound_crs_co == nullptr);
585 611413 : CPLAssert(m_poRootBackup == nullptr);
586 611413 : CPLAssert(m_pj_crs_backup == nullptr);
587 :
588 611413 : m_pj_crs_modified_during_demote = false;
589 :
590 611413 : if (m_pjType == PJ_TYPE_BOUND_CRS)
591 : {
592 2759 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
593 2759 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
594 2759 : m_pj_bound_crs_co =
595 2759 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
596 :
597 2759 : m_poRootBackup = m_poRoot;
598 2759 : m_poRoot = nullptr;
599 2759 : m_pj_crs_backup = m_pj_crs;
600 2759 : m_pj_crs = baseCRS;
601 2759 : m_pjType = proj_get_type(m_pj_crs);
602 : }
603 611413 : }
604 :
605 611414 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
606 : {
607 611414 : if (m_pj_bound_crs_target)
608 : {
609 2759 : CPLAssert(m_poRoot == nullptr);
610 2759 : CPLAssert(m_pj_crs);
611 2759 : if (!m_pj_crs_modified_during_demote)
612 : {
613 2739 : proj_destroy(m_pj_crs);
614 2739 : m_pj_crs = m_pj_crs_backup;
615 2739 : m_pjType = proj_get_type(m_pj_crs);
616 2739 : m_poRoot = m_poRootBackup;
617 : }
618 : else
619 : {
620 20 : delete m_poRootBackup;
621 20 : m_poRootBackup = nullptr;
622 20 : proj_destroy(m_pj_crs_backup);
623 20 : m_pj_crs_backup = nullptr;
624 20 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
625 20 : m_pj_bound_crs_target,
626 20 : m_pj_bound_crs_co),
627 : false);
628 : }
629 : }
630 :
631 611414 : m_poRootBackup = nullptr;
632 611414 : m_pj_crs_backup = nullptr;
633 611414 : proj_destroy(m_pj_bound_crs_target);
634 611414 : m_pj_bound_crs_target = nullptr;
635 611414 : proj_destroy(m_pj_bound_crs_co);
636 611413 : m_pj_bound_crs_co = nullptr;
637 611413 : m_pj_crs_modified_during_demote = false;
638 611413 : }
639 :
640 151958 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
641 : const char *pszTargetKey)
642 : {
643 151958 : if (pszTargetKey)
644 : {
645 57693 : demoteFromBoundCRS();
646 57693 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
647 29762 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
648 28009 : EQUAL(pszTargetKey, "GEOGCS"))
649 : {
650 6952 : pszTargetKey = nullptr;
651 : }
652 50741 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
653 20 : EQUAL(pszTargetKey, "GEOCCS"))
654 : {
655 0 : pszTargetKey = nullptr;
656 : }
657 50741 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
658 28377 : EQUAL(pszTargetKey, "PROJCS"))
659 : {
660 3988 : pszTargetKey = nullptr;
661 : }
662 46753 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
663 4 : EQUAL(pszTargetKey, "VERT_CS"))
664 : {
665 2 : pszTargetKey = nullptr;
666 : }
667 57693 : undoDemoteFromBoundCRS();
668 : }
669 151958 : return pszTargetKey;
670 : }
671 :
672 9257 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
673 : {
674 9257 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
675 9204 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
676 : {
677 53 : return m_pj_crs;
678 : }
679 :
680 9204 : auto ctxt = getPROJContext();
681 9204 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
682 : {
683 4221 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
684 4221 : proj_destroy(m_pj_geod_base_crs_temp);
685 4221 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
686 4221 : return m_pj_geod_base_crs_temp;
687 : }
688 :
689 4983 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
690 4983 : proj_destroy(m_pj_geod_base_crs_temp);
691 4983 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
692 : nullptr, 0);
693 4983 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
694 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
695 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
696 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
697 4983 : proj_destroy(cs);
698 :
699 4983 : return m_pj_geod_base_crs_temp;
700 : }
701 :
702 5184 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
703 : {
704 5184 : auto ctxt = getPROJContext();
705 5184 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
706 : {
707 4207 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
708 4207 : proj_destroy(m_pj_proj_crs_cs_temp);
709 4207 : m_pj_proj_crs_cs_temp =
710 4207 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
711 4207 : return m_pj_proj_crs_cs_temp;
712 : }
713 :
714 977 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
715 977 : proj_destroy(m_pj_proj_crs_cs_temp);
716 977 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
717 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
718 977 : return m_pj_proj_crs_cs_temp;
719 : }
720 :
721 5235 : const char *OGRSpatialReference::Private::getProjCRSName()
722 : {
723 5235 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
724 : {
725 4222 : return proj_get_name(m_pj_crs);
726 : }
727 :
728 1013 : return "unnamed";
729 : }
730 :
731 1384 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
732 : {
733 1384 : refreshProjObj();
734 :
735 1384 : demoteFromBoundCRS();
736 :
737 : auto projCRS =
738 1384 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
739 1384 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
740 1384 : proj_destroy(conv);
741 :
742 1384 : setPjCRS(projCRS);
743 :
744 1384 : undoDemoteFromBoundCRS();
745 1384 : return OGRERR_NONE;
746 : }
747 :
748 : /************************************************************************/
749 : /* ToPointer() */
750 : /************************************************************************/
751 :
752 25169 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
753 : {
754 25169 : return OGRSpatialReference::FromHandle(hSRS);
755 : }
756 :
757 : /************************************************************************/
758 : /* ToHandle() */
759 : /************************************************************************/
760 :
761 4207 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
762 : {
763 4207 : return OGRSpatialReference::ToHandle(poSRS);
764 : }
765 :
766 : /************************************************************************/
767 : /* OGRsnPrintDouble() */
768 : /************************************************************************/
769 :
770 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
771 :
772 126 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
773 :
774 : {
775 126 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
776 :
777 126 : const size_t nLen = strlen(pszStrBuf);
778 :
779 : // The following hack is intended to truncate some "precision" in cases
780 : // that appear to be roundoff error.
781 126 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
782 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
783 : {
784 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
785 : }
786 :
787 : // Force to user periods regardless of locale.
788 126 : if (strchr(pszStrBuf, ',') != nullptr)
789 : {
790 0 : char *const pszDelim = strchr(pszStrBuf, ',');
791 0 : *pszDelim = '.';
792 : }
793 126 : }
794 :
795 : /************************************************************************/
796 : /* OGRSpatialReference() */
797 : /************************************************************************/
798 :
799 : /**
800 : * \brief Constructor.
801 : *
802 : * This constructor takes an optional string argument which if passed
803 : * should be a WKT representation of an SRS. Passing this is equivalent
804 : * to not passing it, and then calling importFromWkt() with the WKT string.
805 : *
806 : * Note that newly created objects are given a reference count of one.
807 : *
808 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
809 : * object are assumed to be in the order of the axis of the CRS definition
810 : (which
811 : * for example means latitude first, longitude second for geographic CRS
812 : belonging
813 : * to the EPSG authority). It is possible to define a data axis to CRS axis
814 : * mapping strategy with the SetAxisMappingStrategy() method.
815 : *
816 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
817 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
818 : later
819 : * being the default value when the option is not set) to control the value of
820 : the
821 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
822 : * created. Calling SetAxisMappingStrategy() will override this default value.
823 :
824 : * The C function OSRNewSpatialReference() does the same thing as this
825 : * constructor.
826 : *
827 : * @param pszWKT well known text definition to which the object should
828 : * be initialized, or NULL (the default).
829 : */
830 :
831 225547 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
832 225547 : : d(new Private(this))
833 : {
834 225384 : if (pszWKT != nullptr)
835 280 : importFromWkt(pszWKT);
836 225384 : }
837 :
838 : /************************************************************************/
839 : /* OSRNewSpatialReference() */
840 : /************************************************************************/
841 :
842 : /**
843 : * \brief Constructor.
844 : *
845 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
846 : * object are assumed to be in the order of the axis of the CRS definition
847 : * (which for example means latitude first, longitude second for geographic CRS
848 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
849 : * axis mapping strategy with the SetAxisMappingStrategy() method.
850 : *
851 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
852 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
853 : * later being the default value when the option is not set) to control the
854 : * value of the data axis to CRS axis mapping strategy when a
855 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
856 : * override this default value.
857 : *
858 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
859 : */
860 2957 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
861 :
862 : {
863 2957 : OGRSpatialReference *poSRS = new OGRSpatialReference();
864 :
865 2957 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
866 : {
867 65 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
868 : {
869 0 : delete poSRS;
870 0 : poSRS = nullptr;
871 : }
872 : }
873 :
874 2957 : return ToHandle(poSRS);
875 : }
876 :
877 : /************************************************************************/
878 : /* OGRSpatialReference() */
879 : /************************************************************************/
880 :
881 : /** Copy constructor. See also Clone().
882 : * @param oOther other spatial reference
883 : */
884 2465 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
885 2465 : : d(new Private(this))
886 : {
887 2465 : *this = oOther;
888 2465 : }
889 :
890 : /************************************************************************/
891 : /* OGRSpatialReference() */
892 : /************************************************************************/
893 :
894 : /** Move constructor.
895 : * @param oOther other spatial reference
896 : */
897 29 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
898 29 : : d(std::move(oOther.d))
899 : {
900 29 : }
901 :
902 : /************************************************************************/
903 : /* ~OGRSpatialReference() */
904 : /************************************************************************/
905 :
906 : /**
907 : * \brief OGRSpatialReference destructor.
908 : *
909 : * The C function OSRDestroySpatialReference() does the same thing as this
910 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
911 : *
912 : * @deprecated
913 : */
914 :
915 282309 : OGRSpatialReference::~OGRSpatialReference()
916 :
917 : {
918 282246 : }
919 :
920 : /************************************************************************/
921 : /* DestroySpatialReference() */
922 : /************************************************************************/
923 :
924 : /**
925 : * \brief OGRSpatialReference destructor.
926 : *
927 : * This static method will destroy a OGRSpatialReference. It is
928 : * equivalent to calling delete on the object, but it ensures that the
929 : * deallocation is properly executed within the OGR libraries heap on
930 : * platforms where this can matter (win32).
931 : *
932 : * This function is the same as OSRDestroySpatialReference()
933 : *
934 : * @param poSRS the object to delete
935 : *
936 : * @since GDAL 1.7.0
937 : */
938 :
939 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
940 : {
941 0 : delete poSRS;
942 0 : }
943 :
944 : /************************************************************************/
945 : /* OSRDestroySpatialReference() */
946 : /************************************************************************/
947 :
948 : /**
949 : * \brief OGRSpatialReference destructor.
950 : *
951 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
952 : * and OGRSpatialReference::DestroySpatialReference()
953 : *
954 : * @param hSRS the object to delete
955 : */
956 9100 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
957 :
958 : {
959 9100 : delete ToPointer(hSRS);
960 9100 : }
961 :
962 : /************************************************************************/
963 : /* Clear() */
964 : /************************************************************************/
965 :
966 : /**
967 : * \brief Wipe current definition.
968 : *
969 : * Returns OGRSpatialReference to a state with no definition, as it
970 : * exists when first created. It does not affect reference counts.
971 : */
972 :
973 110769 : void OGRSpatialReference::Clear()
974 :
975 : {
976 110769 : d->clear();
977 110769 : }
978 :
979 : /************************************************************************/
980 : /* operator=() */
981 : /************************************************************************/
982 :
983 : /** Assignment operator.
984 : * @param oSource SRS to assign to *this
985 : * @return *this
986 : */
987 : OGRSpatialReference &
988 25929 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
989 :
990 : {
991 25929 : if (&oSource != this)
992 : {
993 25928 : Clear();
994 : #ifdef CPPCHECK
995 : // Otherwise cppcheck would protest that nRefCount isn't modified
996 : d->nRefCount = (d->nRefCount + 1) - 1;
997 : #endif
998 :
999 25929 : oSource.d->refreshProjObj();
1000 25928 : if (oSource.d->m_pj_crs)
1001 25639 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1002 25930 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1003 11931 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1004 13997 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1005 124 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1006 :
1007 25929 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1008 : }
1009 :
1010 25929 : return *this;
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* operator=() */
1015 : /************************************************************************/
1016 :
1017 : /** Move assignment operator.
1018 : * @param oSource SRS to assign to *this
1019 : * @return *this
1020 : */
1021 : OGRSpatialReference &
1022 4528 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1023 :
1024 : {
1025 4528 : if (&oSource != this)
1026 : {
1027 4527 : d = std::move(oSource.d);
1028 : }
1029 :
1030 4528 : return *this;
1031 : }
1032 :
1033 : /************************************************************************/
1034 : /* AssignAndSetThreadSafe() */
1035 : /************************************************************************/
1036 :
1037 : /** Assignment method, with thread-safety.
1038 : *
1039 : * Same as an assignment operator, but asking also that the *this instance
1040 : * becomes thread-safe.
1041 : *
1042 : * @param oSource SRS to assign to *this
1043 : * @return *this
1044 : * @since 3.10
1045 : */
1046 :
1047 : OGRSpatialReference &
1048 2 : OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1049 : {
1050 2 : *this = oSource;
1051 2 : d->SetThreadSafe();
1052 2 : return *this;
1053 : }
1054 :
1055 : /************************************************************************/
1056 : /* Reference() */
1057 : /************************************************************************/
1058 :
1059 : /**
1060 : * \brief Increments the reference count by one.
1061 : *
1062 : * The reference count is used keep track of the number of OGRGeometry objects
1063 : * referencing this SRS.
1064 : *
1065 : * The method does the same thing as the C function OSRReference().
1066 : *
1067 : * @return the updated reference count.
1068 : */
1069 :
1070 4121470 : int OGRSpatialReference::Reference()
1071 :
1072 : {
1073 4121470 : return CPLAtomicInc(&d->nRefCount);
1074 : }
1075 :
1076 : /************************************************************************/
1077 : /* OSRReference() */
1078 : /************************************************************************/
1079 :
1080 : /**
1081 : * \brief Increments the reference count by one.
1082 : *
1083 : * This function is the same as OGRSpatialReference::Reference()
1084 : */
1085 977 : int OSRReference(OGRSpatialReferenceH hSRS)
1086 :
1087 : {
1088 977 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1089 :
1090 977 : return ToPointer(hSRS)->Reference();
1091 : }
1092 :
1093 : /************************************************************************/
1094 : /* Dereference() */
1095 : /************************************************************************/
1096 :
1097 : /**
1098 : * \brief Decrements the reference count by one.
1099 : *
1100 : * The method does the same thing as the C function OSRDereference().
1101 : *
1102 : * @return the updated reference count.
1103 : */
1104 :
1105 4161320 : int OGRSpatialReference::Dereference()
1106 :
1107 : {
1108 4161320 : if (d->nRefCount <= 0)
1109 0 : CPLDebug("OSR",
1110 : "Dereference() called on an object with refcount %d,"
1111 : "likely already destroyed!",
1112 0 : d->nRefCount);
1113 4161320 : return CPLAtomicDec(&d->nRefCount);
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /* OSRDereference() */
1118 : /************************************************************************/
1119 :
1120 : /**
1121 : * \brief Decrements the reference count by one.
1122 : *
1123 : * This function is the same as OGRSpatialReference::Dereference()
1124 : */
1125 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1126 :
1127 : {
1128 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1129 :
1130 0 : return ToPointer(hSRS)->Dereference();
1131 : }
1132 :
1133 : /************************************************************************/
1134 : /* GetReferenceCount() */
1135 : /************************************************************************/
1136 :
1137 : /**
1138 : * \brief Fetch current reference count.
1139 : *
1140 : * @return the current reference count.
1141 : */
1142 180 : int OGRSpatialReference::GetReferenceCount() const
1143 : {
1144 180 : return d->nRefCount;
1145 : }
1146 :
1147 : /************************************************************************/
1148 : /* Release() */
1149 : /************************************************************************/
1150 :
1151 : /**
1152 : * \brief Decrements the reference count by one, and destroy if zero.
1153 : *
1154 : * The method does the same thing as the C function OSRRelease().
1155 : */
1156 :
1157 4158540 : void OGRSpatialReference::Release()
1158 :
1159 : {
1160 4158540 : if (Dereference() <= 0)
1161 40030 : delete this;
1162 4158540 : }
1163 :
1164 : /************************************************************************/
1165 : /* OSRRelease() */
1166 : /************************************************************************/
1167 :
1168 : /**
1169 : * \brief Decrements the reference count by one, and destroy if zero.
1170 : *
1171 : * This function is the same as OGRSpatialReference::Release()
1172 : */
1173 6191 : void OSRRelease(OGRSpatialReferenceH hSRS)
1174 :
1175 : {
1176 6191 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1177 :
1178 6191 : ToPointer(hSRS)->Release();
1179 : }
1180 :
1181 87256 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1182 : {
1183 87256 : TAKE_OPTIONAL_LOCK();
1184 :
1185 87256 : if (!d->m_poRoot)
1186 : {
1187 25672 : d->refreshRootFromProjObj();
1188 : }
1189 174512 : return d->m_poRoot;
1190 : }
1191 :
1192 7856 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1193 : {
1194 7856 : TAKE_OPTIONAL_LOCK();
1195 :
1196 7856 : if (!d->m_poRoot)
1197 : {
1198 2930 : d->refreshRootFromProjObj();
1199 : }
1200 15712 : return d->m_poRoot;
1201 : }
1202 :
1203 : /************************************************************************/
1204 : /* SetRoot() */
1205 : /************************************************************************/
1206 :
1207 : /**
1208 : * \brief Set the root SRS node.
1209 : *
1210 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1211 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1212 : * is assumed by the OGRSpatialReference.
1213 : *
1214 : * @param poNewRoot object to assign as root.
1215 : */
1216 :
1217 44 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1218 :
1219 : {
1220 44 : if (d->m_poRoot != poNewRoot)
1221 : {
1222 44 : delete d->m_poRoot;
1223 44 : d->setRoot(poNewRoot);
1224 : }
1225 44 : }
1226 :
1227 : /************************************************************************/
1228 : /* GetAttrNode() */
1229 : /************************************************************************/
1230 :
1231 : /**
1232 : * \brief Find named node in tree.
1233 : *
1234 : * This method does a pre-order traversal of the node tree searching for
1235 : * a node with this exact value (case insensitive), and returns it. Leaf
1236 : * nodes are not considered, under the assumption that they are just
1237 : * attribute value nodes.
1238 : *
1239 : * If a node appears more than once in the tree (such as UNIT for instance),
1240 : * the first encountered will be returned. Use GetNode() on a subtree to be
1241 : * more specific.
1242 : *
1243 : * @param pszNodePath the name of the node to search for. May contain multiple
1244 : * components such as "GEOGCS|UNIT".
1245 : *
1246 : * @return a pointer to the node found, or NULL if none.
1247 : */
1248 :
1249 84124 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1250 :
1251 : {
1252 84124 : if (strchr(pszNodePath, '|') == nullptr)
1253 : {
1254 : // Fast path
1255 46093 : OGR_SRSNode *poNode = GetRoot();
1256 46093 : if (poNode)
1257 44891 : poNode = poNode->GetNode(pszNodePath);
1258 46093 : return poNode;
1259 : }
1260 :
1261 : char **papszPathTokens =
1262 38031 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1263 :
1264 38031 : if (CSLCount(papszPathTokens) < 1)
1265 : {
1266 0 : CSLDestroy(papszPathTokens);
1267 0 : return nullptr;
1268 : }
1269 :
1270 38031 : OGR_SRSNode *poNode = GetRoot();
1271 115354 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1272 : {
1273 77323 : poNode = poNode->GetNode(papszPathTokens[i]);
1274 : }
1275 :
1276 38031 : CSLDestroy(papszPathTokens);
1277 :
1278 38031 : return poNode;
1279 : }
1280 :
1281 : /**
1282 : * \brief Find named node in tree.
1283 : *
1284 : * This method does a pre-order traversal of the node tree searching for
1285 : * a node with this exact value (case insensitive), and returns it. Leaf
1286 : * nodes are not considered, under the assumption that they are just
1287 : * attribute value nodes.
1288 : *
1289 : * If a node appears more than once in the tree (such as UNIT for instance),
1290 : * the first encountered will be returned. Use GetNode() on a subtree to be
1291 : * more specific.
1292 : *
1293 : * @param pszNodePath the name of the node to search for. May contain multiple
1294 : * components such as "GEOGCS|UNIT".
1295 : *
1296 : * @return a pointer to the node found, or NULL if none.
1297 : */
1298 :
1299 : const OGR_SRSNode *
1300 76150 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1301 :
1302 : {
1303 : OGR_SRSNode *poNode =
1304 76150 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1305 :
1306 76150 : return poNode;
1307 : }
1308 :
1309 : /************************************************************************/
1310 : /* GetAttrValue() */
1311 : /************************************************************************/
1312 :
1313 : /**
1314 : * \brief Fetch indicated attribute of named node.
1315 : *
1316 : * This method uses GetAttrNode() to find the named node, and then extracts
1317 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1318 : * would return the second child of the UNIT node, which is normally the
1319 : * length of the linear unit in meters.
1320 : *
1321 : * This method does the same thing as the C function OSRGetAttrValue().
1322 : *
1323 : * @param pszNodeName the tree node to look for (case insensitive).
1324 : * @param iAttr the child of the node to fetch (zero based).
1325 : *
1326 : * @return the requested value, or NULL if it fails for any reason.
1327 : */
1328 :
1329 23168 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1330 : int iAttr) const
1331 :
1332 : {
1333 23168 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1334 23168 : if (poNode == nullptr)
1335 : {
1336 10026 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1337 : {
1338 14 : return GetAttrValue("METHOD", iAttr);
1339 : }
1340 10012 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1341 : {
1342 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1343 : }
1344 10012 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1345 : {
1346 1 : return GetAttrValue("PROJCRS", iAttr);
1347 : }
1348 10011 : return nullptr;
1349 : }
1350 :
1351 13142 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1352 0 : return nullptr;
1353 :
1354 13142 : return poNode->GetChild(iAttr)->GetValue();
1355 : }
1356 :
1357 : /************************************************************************/
1358 : /* OSRGetAttrValue() */
1359 : /************************************************************************/
1360 :
1361 : /**
1362 : * \brief Fetch indicated attribute of named node.
1363 : *
1364 : * This function is the same as OGRSpatialReference::GetAttrValue()
1365 : */
1366 34 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1367 : const char *pszKey, int iChild)
1368 :
1369 : {
1370 34 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1371 :
1372 34 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1373 : }
1374 :
1375 : /************************************************************************/
1376 : /* GetName() */
1377 : /************************************************************************/
1378 :
1379 : /**
1380 : * \brief Return the CRS name.
1381 : *
1382 : * The returned value is only short lived and should not be used after other
1383 : * calls to methods on this object.
1384 : *
1385 : * @since GDAL 3.0
1386 : */
1387 :
1388 5683 : const char *OGRSpatialReference::GetName() const
1389 : {
1390 11366 : TAKE_OPTIONAL_LOCK();
1391 :
1392 5683 : d->refreshProjObj();
1393 5683 : if (!d->m_pj_crs)
1394 113 : return nullptr;
1395 5570 : const char *pszName = proj_get_name(d->m_pj_crs);
1396 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1397 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1398 : {
1399 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1400 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1401 : if (baseCRS)
1402 : {
1403 : pszName = proj_get_name(baseCRS);
1404 : // pszName still remains valid after proj_destroy(), since
1405 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1406 : proj_destroy(baseCRS);
1407 : }
1408 : }
1409 : #endif
1410 5570 : return pszName;
1411 : }
1412 :
1413 : /************************************************************************/
1414 : /* OSRGetName() */
1415 : /************************************************************************/
1416 :
1417 : /**
1418 : * \brief Return the CRS name.
1419 : *
1420 : * The returned value is only short lived and should not be used after other
1421 : * calls to methods on this object.
1422 : *
1423 : * @since GDAL 3.0
1424 : */
1425 44 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1426 :
1427 : {
1428 44 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1429 :
1430 44 : return ToPointer(hSRS)->GetName();
1431 : }
1432 :
1433 : /************************************************************************/
1434 : /* GetCelestialBodyName() */
1435 : /************************************************************************/
1436 :
1437 : /**
1438 : * \brief Return the name of the celestial body of this CRS.
1439 : *
1440 : * e.g. "Earth" for an Earth CRS
1441 : *
1442 : * The returned value is only short lived and should not be used after other
1443 : * calls to methods on this object.
1444 : *
1445 : * @since GDAL 3.12 and PROJ 8.1
1446 : */
1447 :
1448 3 : const char *OGRSpatialReference::GetCelestialBodyName() const
1449 : {
1450 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
1451 :
1452 : TAKE_OPTIONAL_LOCK();
1453 :
1454 : d->refreshProjObj();
1455 : if (!d->m_pj_crs)
1456 : return nullptr;
1457 : d->demoteFromBoundCRS();
1458 : const char *name =
1459 : proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1460 : if (name)
1461 : {
1462 : d->m_celestialBodyName = name;
1463 : }
1464 : d->undoDemoteFromBoundCRS();
1465 : return d->m_celestialBodyName.c_str();
1466 : #else
1467 3 : if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1468 : 0.05 * SRS_WGS84_SEMIMAJOR)
1469 3 : return "Earth";
1470 0 : const char *pszAuthName = GetAuthorityName(nullptr);
1471 0 : if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1472 0 : return "Earth";
1473 0 : return nullptr;
1474 : #endif
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* OSRGetCelestialBodyName() */
1479 : /************************************************************************/
1480 :
1481 : /**
1482 : * \brief Return the name of the celestial body of this CRS.
1483 : *
1484 : * e.g. "Earth" for an Earth CRS
1485 : *
1486 : * The returned value is only short lived and should not be used after other
1487 : * calls to methods on this object.
1488 : *
1489 : * @since GDAL 3.12 and PROJ 8.1
1490 : */
1491 :
1492 1 : const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1493 :
1494 : {
1495 1 : VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1496 :
1497 1 : return ToPointer(hSRS)->GetCelestialBodyName();
1498 : }
1499 :
1500 : /************************************************************************/
1501 : /* Clone() */
1502 : /************************************************************************/
1503 :
1504 : /**
1505 : * \brief Make a duplicate of this OGRSpatialReference.
1506 : *
1507 : * This method is the same as the C function OSRClone().
1508 : *
1509 : * @return a new SRS, which becomes the responsibility of the caller.
1510 : */
1511 :
1512 31649 : OGRSpatialReference *OGRSpatialReference::Clone() const
1513 :
1514 : {
1515 31649 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1516 :
1517 31648 : TAKE_OPTIONAL_LOCK();
1518 :
1519 31649 : d->refreshProjObj();
1520 31649 : if (d->m_pj_crs != nullptr)
1521 31594 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1522 31649 : if (d->m_bHasCenterLong && d->m_poRoot)
1523 : {
1524 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1525 : }
1526 31648 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1527 31649 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1528 31649 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1529 63298 : return poNewRef;
1530 : }
1531 :
1532 : /************************************************************************/
1533 : /* OSRClone() */
1534 : /************************************************************************/
1535 :
1536 : /**
1537 : * \brief Make a duplicate of this OGRSpatialReference.
1538 : *
1539 : * This function is the same as OGRSpatialReference::Clone()
1540 : */
1541 1099 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1542 :
1543 : {
1544 1099 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1545 :
1546 1099 : return ToHandle(ToPointer(hSRS)->Clone());
1547 : }
1548 :
1549 : /************************************************************************/
1550 : /* dumpReadable() */
1551 : /************************************************************************/
1552 :
1553 : /** Dump pretty wkt to stdout, mostly for debugging.
1554 : */
1555 0 : void OGRSpatialReference::dumpReadable()
1556 :
1557 : {
1558 0 : char *pszPrettyWkt = nullptr;
1559 :
1560 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1561 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1562 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1563 0 : CPLFree(pszPrettyWkt);
1564 0 : }
1565 :
1566 : /************************************************************************/
1567 : /* exportToPrettyWkt() */
1568 : /************************************************************************/
1569 :
1570 : /**
1571 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1572 : * person.
1573 : *
1574 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1575 : * Issues</a> page for implementation details of WKT 1 in OGR.
1576 : *
1577 : * Note that the returned WKT string should be freed with
1578 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1579 : *
1580 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1581 : * option. Valid values are the one of the FORMAT option of
1582 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1583 : *
1584 : * This method is the same as the C function OSRExportToPrettyWkt().
1585 : *
1586 : * @param ppszResult the resulting string is returned in this pointer.
1587 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1588 : * stripped off.
1589 : *
1590 : * @return OGRERR_NONE if successful.
1591 : */
1592 :
1593 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1594 : int bSimplify) const
1595 :
1596 : {
1597 116 : CPLStringList aosOptions;
1598 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1599 58 : if (bSimplify)
1600 : {
1601 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1602 : }
1603 116 : return exportToWkt(ppszResult, aosOptions.List());
1604 : }
1605 :
1606 : /************************************************************************/
1607 : /* OSRExportToPrettyWkt() */
1608 : /************************************************************************/
1609 :
1610 : /**
1611 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1612 : * person.
1613 : *
1614 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1615 : * option. Valid values are the one of the FORMAT option of
1616 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1617 : *
1618 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1619 : */
1620 :
1621 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1622 : char **ppszReturn, int bSimplify)
1623 :
1624 : {
1625 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1626 :
1627 56 : *ppszReturn = nullptr;
1628 :
1629 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1630 : }
1631 :
1632 : /************************************************************************/
1633 : /* exportToWkt() */
1634 : /************************************************************************/
1635 :
1636 : /**
1637 : * \brief Convert this SRS into WKT 1 format.
1638 : *
1639 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1640 : * Issues</a> page for implementation details of WKT 1 in OGR.
1641 : *
1642 : * Note that the returned WKT string should be freed with
1643 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1644 : *
1645 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1646 : * option. Valid values are the one of the FORMAT option of
1647 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1648 : *
1649 : * This method is the same as the C function OSRExportToWkt().
1650 : *
1651 : * @param ppszResult the resulting string is returned in this pointer.
1652 : *
1653 : * @return OGRERR_NONE if successful.
1654 : */
1655 :
1656 12369 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1657 :
1658 : {
1659 12369 : return exportToWkt(ppszResult, nullptr);
1660 : }
1661 :
1662 : /************************************************************************/
1663 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1664 : /************************************************************************/
1665 :
1666 573 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1667 : bool onlyIfEPSGCode,
1668 : bool canModifyHorizPart)
1669 : {
1670 573 : PJ *ret = nullptr;
1671 573 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1672 : {
1673 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1674 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1675 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1676 26 : vertCRS &&
1677 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1678 : {
1679 : auto boundHoriz =
1680 : canModifyHorizPart
1681 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1682 3 : : proj_clone(ctx, horizCRS);
1683 : auto boundVert =
1684 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1685 3 : if (boundHoriz && boundVert)
1686 : {
1687 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1688 : boundHoriz, boundVert);
1689 : }
1690 3 : proj_destroy(boundHoriz);
1691 3 : proj_destroy(boundVert);
1692 : }
1693 13 : proj_destroy(horizCRS);
1694 13 : proj_destroy(vertCRS);
1695 : }
1696 1080 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1697 520 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1698 : {
1699 242 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1700 : }
1701 573 : return ret;
1702 : }
1703 :
1704 : /************************************************************************/
1705 : /* exportToWkt() */
1706 : /************************************************************************/
1707 :
1708 : /**
1709 : * Convert this SRS into a WKT string.
1710 : *
1711 : * Note that the returned WKT string should be freed with
1712 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1713 : *
1714 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1715 : * Issues</a> page for implementation details of WKT 1 in OGR.
1716 : *
1717 : * @param ppszResult the resulting string is returned in this pointer.
1718 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1719 : * supported options are
1720 : * <ul>
1721 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1722 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1723 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1724 : * node is returned.
1725 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1726 : * node is returned.
1727 : * WKT1 is an alias of WKT1_GDAL.
1728 : * WKT2 will default to the latest revision implemented (currently
1729 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1730 : * </li>
1731 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1732 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1733 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1734 : * height (for example for use with LAS 1.4 WKT1).
1735 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1736 : * </ul>
1737 : *
1738 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1739 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1740 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1741 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1742 : * TOWGS84[] node may be added.
1743 : *
1744 : * @return OGRERR_NONE if successful.
1745 : * @since GDAL 3.0
1746 : */
1747 :
1748 16910 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1749 : const char *const *papszOptions) const
1750 : {
1751 : // In the past calling this method was thread-safe, even if we never
1752 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1753 : // so this is no longer thread-safe.
1754 33820 : std::lock_guard oLock(d->m_mutex);
1755 :
1756 16910 : d->refreshProjObj();
1757 16910 : if (!d->m_pj_crs)
1758 : {
1759 21 : *ppszResult = CPLStrdup("");
1760 21 : return OGRERR_FAILURE;
1761 : }
1762 :
1763 16889 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1764 : {
1765 0 : return d->m_poRoot->exportToWkt(ppszResult);
1766 : }
1767 :
1768 16889 : auto ctxt = d->getPROJContext();
1769 16889 : auto wktFormat = PJ_WKT1_GDAL;
1770 : const char *pszFormat =
1771 16889 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1772 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1773 16889 : if (EQUAL(pszFormat, "DEFAULT"))
1774 14283 : pszFormat = "";
1775 :
1776 16889 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1777 : {
1778 646 : wktFormat = PJ_WKT1_ESRI;
1779 : }
1780 16243 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1781 15529 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1782 : {
1783 719 : wktFormat = PJ_WKT1_GDAL;
1784 : }
1785 15524 : else if (EQUAL(pszFormat, "WKT2_2015"))
1786 : {
1787 293 : wktFormat = PJ_WKT2_2015;
1788 : }
1789 15231 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1790 14882 : EQUAL(pszFormat, "WKT2_2019"))
1791 : {
1792 1177 : wktFormat = PJ_WKT2_2018;
1793 : }
1794 14054 : else if (pszFormat[0] == '\0')
1795 : {
1796 : // cppcheck-suppress knownConditionTrueFalse
1797 14054 : if (IsDerivedGeographic())
1798 : {
1799 2 : wktFormat = PJ_WKT2_2018;
1800 : }
1801 27474 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1802 13422 : GetAxesCount() == 3)
1803 : {
1804 56 : wktFormat = PJ_WKT2_2018;
1805 : }
1806 : }
1807 : else
1808 : {
1809 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1810 0 : *ppszResult = CPLStrdup("");
1811 0 : return OGRERR_FAILURE;
1812 : }
1813 :
1814 33778 : CPLStringList aosOptions;
1815 16889 : if (wktFormat != PJ_WKT1_ESRI)
1816 : {
1817 16243 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1818 : }
1819 : aosOptions.SetNameValue(
1820 16889 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1821 :
1822 16889 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1823 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1824 16889 : if (pszAllowEllpsHeightAsVertCS)
1825 : {
1826 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1827 0 : pszAllowEllpsHeightAsVertCS);
1828 : }
1829 :
1830 16889 : PJ *boundCRS = nullptr;
1831 31604 : if (wktFormat == PJ_WKT1_GDAL &&
1832 14715 : CPLTestBool(CSLFetchNameValueDef(
1833 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1834 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1835 : {
1836 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1837 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1838 : }
1839 :
1840 33778 : CPLErrorAccumulator oErrorAccumulator;
1841 : const char *pszWKT;
1842 : {
1843 16889 : auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1844 16889 : CPL_IGNORE_RET_VAL(oAccumulator);
1845 16889 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1846 16889 : aosOptions.List());
1847 : }
1848 16891 : for (const auto &oError : oErrorAccumulator.GetErrors())
1849 : {
1850 32 : if (pszFormat[0] == '\0' &&
1851 14 : (oError.msg.find("Unsupported conversion method") !=
1852 2 : std::string::npos ||
1853 2 : oError.msg.find("can only be exported to WKT2") !=
1854 0 : std::string::npos ||
1855 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1856 : std::string::npos))
1857 : {
1858 14 : CPLErrorReset();
1859 : // If we cannot export in the default mode (WKT1), retry with WKT2
1860 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1861 14 : PJ_WKT2_2018, aosOptions.List());
1862 14 : break;
1863 : }
1864 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1865 : }
1866 :
1867 16889 : if (!pszWKT)
1868 : {
1869 2 : *ppszResult = CPLStrdup("");
1870 2 : proj_destroy(boundCRS);
1871 2 : return OGRERR_FAILURE;
1872 : }
1873 :
1874 16887 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1875 : {
1876 5 : OGR_SRSNode oRoot;
1877 5 : oRoot.importFromWkt(&pszWKT);
1878 5 : oRoot.StripNodes("AXIS");
1879 5 : if (EQUAL(pszFormat, "SFSQL"))
1880 : {
1881 3 : oRoot.StripNodes("TOWGS84");
1882 : }
1883 5 : oRoot.StripNodes("AUTHORITY");
1884 5 : oRoot.StripNodes("EXTENSION");
1885 : OGRErr eErr;
1886 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1887 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1888 : else
1889 3 : eErr = oRoot.exportToWkt(ppszResult);
1890 5 : proj_destroy(boundCRS);
1891 5 : return eErr;
1892 : }
1893 :
1894 16882 : *ppszResult = CPLStrdup(pszWKT);
1895 :
1896 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1897 16882 : if (wktFormat == PJ_WKT2_2018)
1898 : {
1899 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1900 : // related to a wrong EPSG code assigned to UTM South conversions
1901 1235 : char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1902 1235 : if (pszPtr)
1903 : {
1904 297 : pszPtr += strlen("CONVERSION[\"UTM zone ");
1905 297 : const int nZone = atoi(pszPtr);
1906 890 : while (*pszPtr >= '0' && *pszPtr <= '9')
1907 593 : ++pszPtr;
1908 297 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1909 1 : pszPtr[1] == '"' && pszPtr[2] == ',')
1910 : {
1911 1 : pszPtr += 3;
1912 1 : int nLevel = 0;
1913 1 : bool bInString = false;
1914 : // Find the ID node corresponding to this CONVERSION node
1915 480 : while (*pszPtr)
1916 : {
1917 480 : if (bInString)
1918 : {
1919 197 : if (*pszPtr == '"' && pszPtr[1] == '"')
1920 : {
1921 0 : ++pszPtr;
1922 : }
1923 197 : else if (*pszPtr == '"')
1924 : {
1925 17 : bInString = false;
1926 : }
1927 : }
1928 283 : else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1929 : {
1930 1 : if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1931 : 17000 + nZone)))
1932 : {
1933 1 : CPLAssert(pszPtr[11] == '7');
1934 1 : CPLAssert(pszPtr[12] == '0');
1935 1 : pszPtr[11] = '6';
1936 1 : pszPtr[12] = '1';
1937 : }
1938 1 : break;
1939 : }
1940 282 : else if (*pszPtr == '"')
1941 : {
1942 17 : bInString = true;
1943 : }
1944 265 : else if (*pszPtr == '[')
1945 : {
1946 17 : ++nLevel;
1947 : }
1948 248 : else if (*pszPtr == ']')
1949 : {
1950 17 : --nLevel;
1951 : }
1952 :
1953 479 : ++pszPtr;
1954 : }
1955 : }
1956 : }
1957 : }
1958 : #endif
1959 :
1960 16882 : proj_destroy(boundCRS);
1961 16882 : return OGRERR_NONE;
1962 : }
1963 :
1964 : /************************************************************************/
1965 : /* exportToWkt() */
1966 : /************************************************************************/
1967 :
1968 : /**
1969 : * Convert this SRS into a WKT string.
1970 : *
1971 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1972 : * Issues</a> page for implementation details of WKT 1 in OGR.
1973 : *
1974 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1975 : * supported options are
1976 : * <ul>
1977 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1978 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1979 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1980 : * node is returned.
1981 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1982 : * node is returned.
1983 : * WKT1 is an alias of WKT1_GDAL.
1984 : * WKT2 will default to the latest revision implemented (currently
1985 : * WKT2_2019)
1986 : * </li>
1987 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1988 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1989 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1990 : * height (for example for use with LAS 1.4 WKT1).
1991 : * Requires PROJ 7.2.1.</li>
1992 : * </ul>
1993 : *
1994 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1995 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1996 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1997 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1998 : * TOWGS84[] node may be added.
1999 : *
2000 : * @return a non-empty string if successful.
2001 : * @since GDAL 3.9
2002 : */
2003 :
2004 : std::string
2005 287 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2006 : {
2007 287 : std::string osWKT;
2008 287 : char *pszWKT = nullptr;
2009 287 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2010 287 : osWKT = pszWKT;
2011 287 : CPLFree(pszWKT);
2012 574 : return osWKT;
2013 : }
2014 :
2015 : /************************************************************************/
2016 : /* OSRExportToWkt() */
2017 : /************************************************************************/
2018 :
2019 : /**
2020 : * \brief Convert this SRS into WKT 1 format.
2021 : *
2022 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2023 : * Issues</a> page for implementation details of WKT in OGR.
2024 : *
2025 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2026 : * option. Valid values are the one of the FORMAT option of
2027 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2028 : *
2029 : * This function is the same as OGRSpatialReference::exportToWkt().
2030 : */
2031 :
2032 903 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2033 :
2034 : {
2035 903 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2036 :
2037 903 : *ppszReturn = nullptr;
2038 :
2039 903 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
2040 : }
2041 :
2042 : /************************************************************************/
2043 : /* OSRExportToWktEx() */
2044 : /************************************************************************/
2045 :
2046 : /**
2047 : * \brief Convert this SRS into WKT format.
2048 : *
2049 : * This function is the same as OGRSpatialReference::exportToWkt(char **
2050 : * ppszResult,const char* const* papszOptions ) const
2051 : *
2052 : * @since GDAL 3.0
2053 : */
2054 :
2055 1334 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2056 : const char *const *papszOptions)
2057 : {
2058 1334 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2059 :
2060 1334 : *ppszReturn = nullptr;
2061 :
2062 1334 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2063 : }
2064 :
2065 : /************************************************************************/
2066 : /* exportToPROJJSON() */
2067 : /************************************************************************/
2068 :
2069 : /**
2070 : * Convert this SRS into a PROJJSON string.
2071 : *
2072 : * Note that the returned JSON string should be freed with
2073 : * CPLFree() when no longer needed. It is the responsibility of the caller.
2074 : *
2075 : * @param ppszResult the resulting string is returned in this pointer.
2076 : * @param papszOptions NULL terminated list of options, or NULL. Currently
2077 : * supported options are
2078 : * <ul>
2079 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
2080 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2081 : * on).</li>
2082 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2083 : * disable it.</li>
2084 : * </ul>
2085 : *
2086 : * @return OGRERR_NONE if successful.
2087 : * @since GDAL 3.1 and PROJ 6.2
2088 : */
2089 :
2090 2481 : OGRErr OGRSpatialReference::exportToPROJJSON(
2091 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2092 : {
2093 4962 : TAKE_OPTIONAL_LOCK();
2094 :
2095 2481 : d->refreshProjObj();
2096 2481 : if (!d->m_pj_crs)
2097 : {
2098 1 : *ppszResult = nullptr;
2099 1 : return OGRERR_FAILURE;
2100 : }
2101 :
2102 : const char *pszPROJJSON =
2103 2480 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2104 :
2105 2480 : if (!pszPROJJSON)
2106 : {
2107 0 : *ppszResult = CPLStrdup("");
2108 0 : return OGRERR_FAILURE;
2109 : }
2110 :
2111 2480 : *ppszResult = CPLStrdup(pszPROJJSON);
2112 :
2113 : #if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2114 : {
2115 : // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2116 : // related to a wrong EPSG code assigned to UTM South conversions
2117 2480 : char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2118 2480 : if (pszPtr)
2119 : {
2120 237 : pszPtr += strlen("\"name\": \"UTM zone ");
2121 237 : const int nZone = atoi(pszPtr);
2122 710 : while (*pszPtr >= '0' && *pszPtr <= '9')
2123 473 : ++pszPtr;
2124 237 : if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2125 : {
2126 4 : pszPtr += 2;
2127 4 : int nLevel = 0;
2128 4 : bool bInString = false;
2129 : // Find the id node corresponding to this conversion node
2130 5299 : while (*pszPtr)
2131 : {
2132 5299 : if (bInString)
2133 : {
2134 1950 : if (*pszPtr == '\\')
2135 : {
2136 0 : ++pszPtr;
2137 : }
2138 1950 : else if (*pszPtr == '"')
2139 : {
2140 244 : bInString = false;
2141 : }
2142 : }
2143 3349 : else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2144 : {
2145 4 : const char *pszNextEndCurl = strchr(pszPtr, '}');
2146 : const char *pszAuthEPSG =
2147 4 : strstr(pszPtr, "\"authority\": \"EPSG\"");
2148 4 : char *pszCode = strstr(
2149 : pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2150 4 : if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2151 4 : pszNextEndCurl - pszAuthEPSG > 0 &&
2152 4 : pszNextEndCurl - pszCode > 0)
2153 : {
2154 4 : CPLAssert(pszCode[9] == '7');
2155 4 : CPLAssert(pszCode[10] == '0');
2156 4 : pszCode[9] = '6';
2157 4 : pszCode[10] = '1';
2158 : }
2159 4 : break;
2160 : }
2161 3345 : else if (*pszPtr == '"')
2162 : {
2163 244 : bInString = true;
2164 : }
2165 3101 : else if (*pszPtr == '{' || *pszPtr == '[')
2166 : {
2167 60 : ++nLevel;
2168 : }
2169 3041 : else if (*pszPtr == '}' || *pszPtr == ']')
2170 : {
2171 60 : --nLevel;
2172 : }
2173 :
2174 5295 : ++pszPtr;
2175 : }
2176 : }
2177 : }
2178 : }
2179 : #endif
2180 :
2181 2480 : return OGRERR_NONE;
2182 : }
2183 :
2184 : /************************************************************************/
2185 : /* OSRExportToPROJJSON() */
2186 : /************************************************************************/
2187 :
2188 : /**
2189 : * \brief Convert this SRS into PROJJSON format.
2190 : *
2191 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2192 : *
2193 : * @since GDAL 3.1 and PROJ 6.2
2194 : */
2195 :
2196 72 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2197 : const char *const *papszOptions)
2198 : {
2199 72 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2200 :
2201 72 : *ppszReturn = nullptr;
2202 :
2203 72 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2204 : }
2205 :
2206 : /************************************************************************/
2207 : /* importFromWkt() */
2208 : /************************************************************************/
2209 :
2210 : /**
2211 : * \brief Import from WKT string.
2212 : *
2213 : * This method will wipe the existing SRS definition, and
2214 : * reassign it based on the contents of the passed WKT string. Only as
2215 : * much of the input string as needed to construct this SRS is consumed from
2216 : * the input string, and the input string pointer
2217 : * is then updated to point to the remaining (unused) input.
2218 : *
2219 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2220 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
2221 : * and the coordinate epoch potentially present used as the coordinate epoch
2222 : * property of the OGRSpatialReference object.
2223 : *
2224 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2225 : * Issues</a> page for implementation details of WKT in OGR.
2226 : *
2227 : * This method is the same as the C function OSRImportFromWkt().
2228 : *
2229 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2230 : * point to remaining unused input text.
2231 : *
2232 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2233 : * fails for any reason.
2234 : * @since GDAL 2.3
2235 : */
2236 :
2237 17090 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2238 :
2239 : {
2240 17090 : return importFromWkt(ppszInput, nullptr);
2241 : }
2242 :
2243 : /************************************************************************/
2244 : /* importFromWkt() */
2245 : /************************************************************************/
2246 :
2247 : /*! @cond Doxygen_Suppress */
2248 :
2249 21 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2250 : CSLConstList papszOptions)
2251 :
2252 : {
2253 21 : return importFromWkt(&pszInput, papszOptions);
2254 : }
2255 :
2256 17111 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2257 : CSLConstList papszOptions)
2258 :
2259 : {
2260 34222 : TAKE_OPTIONAL_LOCK();
2261 :
2262 17111 : if (!ppszInput || !*ppszInput)
2263 0 : return OGRERR_FAILURE;
2264 :
2265 17111 : if (strlen(*ppszInput) > 100 * 1000 &&
2266 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2267 : {
2268 0 : CPLError(CE_Failure, CPLE_NotSupported,
2269 : "Suspiciously large input for importFromWkt(). Rejecting it. "
2270 : "You can remove this limitation by definition the "
2271 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2272 0 : return OGRERR_FAILURE;
2273 : }
2274 :
2275 17111 : Clear();
2276 :
2277 17111 : bool canCache = false;
2278 17111 : auto tlsCache = OSRGetProjTLSCache();
2279 34222 : std::string osWkt;
2280 17111 : if (**ppszInput)
2281 : {
2282 16555 : osWkt = *ppszInput;
2283 16555 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2284 16555 : if (cachedObj)
2285 : {
2286 14754 : d->setPjCRS(cachedObj);
2287 : }
2288 : else
2289 : {
2290 3602 : CPLStringList aosOptions(papszOptions);
2291 1801 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2292 1801 : aosOptions.SetNameValue("STRICT", "NO");
2293 1801 : PROJ_STRING_LIST warnings = nullptr;
2294 1801 : PROJ_STRING_LIST errors = nullptr;
2295 1801 : auto ctxt = d->getPROJContext();
2296 1801 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2297 : &warnings, &errors);
2298 1801 : d->setPjCRS(pj);
2299 :
2300 1850 : for (auto iter = warnings; iter && *iter; ++iter)
2301 : {
2302 49 : d->m_wktImportWarnings.push_back(*iter);
2303 : }
2304 2037 : for (auto iter = errors; iter && *iter; ++iter)
2305 : {
2306 236 : d->m_wktImportErrors.push_back(*iter);
2307 236 : if (!d->m_pj_crs)
2308 : {
2309 34 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2310 : }
2311 : }
2312 1801 : if (warnings == nullptr && errors == nullptr)
2313 : {
2314 1523 : canCache = true;
2315 : }
2316 1801 : proj_string_list_destroy(warnings);
2317 1801 : proj_string_list_destroy(errors);
2318 : }
2319 : }
2320 17111 : if (!d->m_pj_crs)
2321 590 : return OGRERR_CORRUPT_DATA;
2322 :
2323 : // Only accept CRS objects
2324 16521 : if (!proj_is_crs(d->m_pj_crs))
2325 : {
2326 0 : Clear();
2327 0 : return OGRERR_CORRUPT_DATA;
2328 : }
2329 :
2330 16521 : if (canCache)
2331 : {
2332 1523 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2333 : }
2334 :
2335 16521 : if (strstr(*ppszInput, "CENTER_LONG"))
2336 : {
2337 0 : auto poRoot = new OGR_SRSNode();
2338 0 : d->setRoot(poRoot);
2339 0 : const char *pszTmp = *ppszInput;
2340 0 : poRoot->importFromWkt(&pszTmp);
2341 0 : d->m_bHasCenterLong = true;
2342 : }
2343 :
2344 : // TODO? we don't really update correctly since we assume that the
2345 : // passed string is only WKT.
2346 16521 : *ppszInput += strlen(*ppszInput);
2347 16521 : return OGRERR_NONE;
2348 :
2349 : #if no_longer_implemented_for_now
2350 : /* -------------------------------------------------------------------- */
2351 : /* The following seems to try and detect and unconsumed */
2352 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2353 : /* import and attach it to the existing root. Likely we will */
2354 : /* need to extend this somewhat to bring it into an acceptable */
2355 : /* OGRSpatialReference organization at some point. */
2356 : /* -------------------------------------------------------------------- */
2357 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2358 : {
2359 : if (((*ppszInput)[0]) == ',')
2360 : (*ppszInput)++;
2361 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2362 : poRoot->AddChild(poNewChild);
2363 : return poNewChild->importFromWkt(ppszInput);
2364 : }
2365 : #endif
2366 : }
2367 :
2368 : /*! @endcond */
2369 :
2370 : /**
2371 : * \brief Import from WKT string.
2372 : *
2373 : * This method will wipe the existing SRS definition, and
2374 : * reassign it based on the contents of the passed WKT string. Only as
2375 : * much of the input string as needed to construct this SRS is consumed from
2376 : * the input string, and the input string pointer
2377 : * is then updated to point to the remaining (unused) input.
2378 : *
2379 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2380 : * Issues</a> page for implementation details of WKT in OGR.
2381 : *
2382 : * This method is the same as the C function OSRImportFromWkt().
2383 : *
2384 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2385 : * point to remaining unused input text.
2386 : *
2387 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2388 : * fails for any reason.
2389 : * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2390 : * char*)
2391 : */
2392 :
2393 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2394 :
2395 : {
2396 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2397 : }
2398 :
2399 : /**
2400 : * \brief Import from WKT string.
2401 : *
2402 : * This method will wipe the existing SRS definition, and
2403 : * reassign it based on the contents of the passed WKT string. Only as
2404 : * much of the input string as needed to construct this SRS is consumed from
2405 : * the input string, and the input string pointer
2406 : * is then updated to point to the remaining (unused) input.
2407 : *
2408 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2409 : * Issues</a> page for implementation details of WKT in OGR.
2410 : *
2411 : * @param pszInput Input WKT
2412 : *
2413 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2414 : * fails for any reason.
2415 : * @since GDAL 2.3
2416 : */
2417 :
2418 16800 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2419 : {
2420 16800 : return importFromWkt(&pszInput);
2421 : }
2422 :
2423 : /************************************************************************/
2424 : /* Validate() */
2425 : /************************************************************************/
2426 :
2427 : /**
2428 : * \brief Validate CRS imported with importFromWkt() or with modified with
2429 : * direct node manipulations. Otherwise the CRS should be always valid.
2430 : *
2431 : * This method attempts to verify that the spatial reference system is
2432 : * well formed, and consists of known tokens. The validation is not
2433 : * comprehensive.
2434 : *
2435 : * This method is the same as the C function OSRValidate().
2436 : *
2437 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2438 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2439 : * but contains non-standard PROJECTION[] values.
2440 : */
2441 :
2442 116 : OGRErr OGRSpatialReference::Validate() const
2443 :
2444 : {
2445 232 : TAKE_OPTIONAL_LOCK();
2446 :
2447 154 : for (const auto &str : d->m_wktImportErrors)
2448 : {
2449 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2450 : }
2451 116 : for (const auto &str : d->m_wktImportWarnings)
2452 : {
2453 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2454 : }
2455 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2456 : {
2457 37 : return OGRERR_CORRUPT_DATA;
2458 : }
2459 79 : if (!d->m_wktImportWarnings.empty())
2460 : {
2461 0 : return OGRERR_UNSUPPORTED_SRS;
2462 : }
2463 79 : return OGRERR_NONE;
2464 : }
2465 :
2466 : /************************************************************************/
2467 : /* OSRValidate() */
2468 : /************************************************************************/
2469 : /**
2470 : * \brief Validate SRS tokens.
2471 : *
2472 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2473 : */
2474 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2475 :
2476 : {
2477 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2478 :
2479 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2480 : }
2481 :
2482 : /************************************************************************/
2483 : /* OSRImportFromWkt() */
2484 : /************************************************************************/
2485 :
2486 : /**
2487 : * \brief Import from WKT string.
2488 : *
2489 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2490 : * Issues</a> page for implementation details of WKT in OGR.
2491 : *
2492 : * This function is the same as OGRSpatialReference::importFromWkt().
2493 : */
2494 :
2495 290 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2496 :
2497 : {
2498 290 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2499 :
2500 290 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2501 : }
2502 :
2503 : /************************************************************************/
2504 : /* SetNode() */
2505 : /************************************************************************/
2506 :
2507 : /**
2508 : * \brief Set attribute value in spatial reference.
2509 : *
2510 : * Missing intermediate nodes in the path will be created if not already
2511 : * in existence. If the attribute has no children one will be created and
2512 : * assigned the value otherwise the zeroth child will be assigned the value.
2513 : *
2514 : * This method does the same as the C function OSRSetAttrValue().
2515 : *
2516 : * @param pszNodePath full path to attribute to be set. For instance
2517 : * "PROJCS|GEOGCS|UNIT".
2518 : *
2519 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2520 : * This may be NULL if you just want to force creation of the intermediate
2521 : * path.
2522 : *
2523 : * @return OGRERR_NONE on success.
2524 : */
2525 :
2526 583 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2527 : const char *pszNewNodeValue)
2528 :
2529 : {
2530 1166 : TAKE_OPTIONAL_LOCK();
2531 :
2532 : char **papszPathTokens =
2533 583 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2534 :
2535 583 : if (CSLCount(papszPathTokens) < 1)
2536 : {
2537 0 : CSLDestroy(papszPathTokens);
2538 0 : return OGRERR_FAILURE;
2539 : }
2540 :
2541 1018 : if (GetRoot() == nullptr ||
2542 435 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2543 : {
2544 268 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2545 116 : CSLCount(papszPathTokens) == 1)
2546 : {
2547 116 : CSLDestroy(papszPathTokens);
2548 116 : return SetProjCS(pszNewNodeValue);
2549 : }
2550 : else
2551 : {
2552 36 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2553 : }
2554 : }
2555 :
2556 467 : OGR_SRSNode *poNode = GetRoot();
2557 725 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2558 : {
2559 258 : int j = 0; // Used after for.
2560 :
2561 645 : for (; j < poNode->GetChildCount(); j++)
2562 : {
2563 585 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2564 : {
2565 198 : poNode = poNode->GetChild(j);
2566 198 : j = -1;
2567 198 : break;
2568 : }
2569 : }
2570 :
2571 258 : if (j != -1)
2572 : {
2573 60 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2574 60 : poNode->AddChild(poNewNode);
2575 60 : poNode = poNewNode;
2576 : }
2577 : }
2578 :
2579 467 : CSLDestroy(papszPathTokens);
2580 :
2581 467 : if (pszNewNodeValue != nullptr)
2582 : {
2583 467 : if (poNode->GetChildCount() > 0)
2584 371 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2585 : else
2586 96 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2587 : };
2588 467 : return OGRERR_NONE;
2589 : }
2590 :
2591 : /************************************************************************/
2592 : /* OSRSetAttrValue() */
2593 : /************************************************************************/
2594 :
2595 : /**
2596 : * \brief Set attribute value in spatial reference.
2597 : *
2598 : * This function is the same as OGRSpatialReference::SetNode()
2599 : */
2600 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2601 : const char *pszPath, const char *pszValue)
2602 :
2603 : {
2604 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2605 :
2606 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2607 : }
2608 :
2609 : /************************************************************************/
2610 : /* SetNode() */
2611 : /************************************************************************/
2612 :
2613 : /**
2614 : * \brief Set attribute value in spatial reference.
2615 : *
2616 : * Missing intermediate nodes in the path will be created if not already
2617 : * in existence. If the attribute has no children one will be created and
2618 : * assigned the value otherwise the zeroth child will be assigned the value.
2619 : *
2620 : * This method does the same as the C function OSRSetAttrValue().
2621 : *
2622 : * @param pszNodePath full path to attribute to be set. For instance
2623 : * "PROJCS|GEOGCS|UNIT".
2624 : *
2625 : * @param dfValue value to be assigned to node.
2626 : *
2627 : * @return OGRERR_NONE on success.
2628 : */
2629 :
2630 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2631 :
2632 : {
2633 0 : char szValue[64] = {'\0'};
2634 :
2635 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2636 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2637 : else
2638 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2639 :
2640 0 : return SetNode(pszNodePath, szValue);
2641 : }
2642 :
2643 : /************************************************************************/
2644 : /* SetAngularUnits() */
2645 : /************************************************************************/
2646 :
2647 : /**
2648 : * \brief Set the angular units for the geographic coordinate system.
2649 : *
2650 : * This method creates a UNIT subnode with the specified values as a
2651 : * child of the GEOGCS node.
2652 : *
2653 : * This method does the same as the C function OSRSetAngularUnits().
2654 : *
2655 : * @param pszUnitsName the units name to be used. Some preferred units
2656 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2657 : *
2658 : * @param dfInRadians the value to multiple by an angle in the indicated
2659 : * units to transform to radians. Some standard conversion factors can
2660 : * be found in ogr_srs_api.h.
2661 : *
2662 : * @return OGRERR_NONE on success.
2663 : */
2664 :
2665 1346 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2666 : double dfInRadians)
2667 :
2668 : {
2669 2692 : TAKE_OPTIONAL_LOCK();
2670 :
2671 1346 : d->bNormInfoSet = FALSE;
2672 :
2673 1346 : d->refreshProjObj();
2674 1346 : if (!d->m_pj_crs)
2675 0 : return OGRERR_FAILURE;
2676 1346 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2677 1346 : if (!geodCRS)
2678 0 : return OGRERR_FAILURE;
2679 1346 : proj_destroy(geodCRS);
2680 1346 : d->demoteFromBoundCRS();
2681 1346 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2682 : pszUnitsName, dfInRadians,
2683 : nullptr, nullptr));
2684 1346 : d->undoDemoteFromBoundCRS();
2685 :
2686 1346 : d->m_osAngularUnits = pszUnitsName;
2687 1346 : d->m_dfAngularUnitToRadian = dfInRadians;
2688 :
2689 1346 : return OGRERR_NONE;
2690 : }
2691 :
2692 : /************************************************************************/
2693 : /* OSRSetAngularUnits() */
2694 : /************************************************************************/
2695 :
2696 : /**
2697 : * \brief Set the angular units for the geographic coordinate system.
2698 : *
2699 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2700 : */
2701 45 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2702 : double dfInRadians)
2703 :
2704 : {
2705 45 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2706 :
2707 45 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2708 : }
2709 :
2710 : /************************************************************************/
2711 : /* GetAngularUnits() */
2712 : /************************************************************************/
2713 :
2714 : /**
2715 : * \brief Fetch angular geographic coordinate system units.
2716 : *
2717 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2718 : * will be assumed. This method only checks directly under the GEOGCS node
2719 : * for units.
2720 : *
2721 : * This method does the same thing as the C function OSRGetAngularUnits().
2722 : *
2723 : * @param ppszName a pointer to be updated with the pointer to the units name.
2724 : * The returned value remains internal to the OGRSpatialReference and should
2725 : * not be freed, or modified. It may be invalidated on the next
2726 : * OGRSpatialReference call.
2727 : *
2728 : * @return the value to multiply by angular distances to transform them to
2729 : * radians.
2730 : * @since GDAL 2.3.0
2731 : */
2732 :
2733 7584 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2734 :
2735 : {
2736 15168 : TAKE_OPTIONAL_LOCK();
2737 :
2738 7584 : d->refreshProjObj();
2739 :
2740 7584 : if (!d->m_osAngularUnits.empty())
2741 : {
2742 2134 : if (ppszName != nullptr)
2743 148 : *ppszName = d->m_osAngularUnits.c_str();
2744 2134 : return d->m_dfAngularUnitToRadian;
2745 : }
2746 :
2747 : do
2748 : {
2749 5450 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2750 : {
2751 113 : break;
2752 : }
2753 :
2754 : auto geodCRS =
2755 5339 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2756 5339 : if (!geodCRS)
2757 : {
2758 0 : break;
2759 : }
2760 : auto coordSys =
2761 5339 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2762 5339 : proj_destroy(geodCRS);
2763 5339 : if (!coordSys)
2764 : {
2765 0 : break;
2766 : }
2767 5339 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2768 : PJ_CS_TYPE_ELLIPSOIDAL)
2769 : {
2770 2 : proj_destroy(coordSys);
2771 2 : break;
2772 : }
2773 :
2774 5337 : double dfConvFactor = 0.0;
2775 5337 : const char *pszUnitName = nullptr;
2776 5337 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2777 : nullptr, nullptr, &dfConvFactor,
2778 : &pszUnitName, nullptr, nullptr))
2779 : {
2780 0 : proj_destroy(coordSys);
2781 0 : break;
2782 : }
2783 :
2784 5337 : d->m_osAngularUnits = pszUnitName;
2785 :
2786 5337 : proj_destroy(coordSys);
2787 5337 : d->m_dfAngularUnitToRadian = dfConvFactor;
2788 : } while (false);
2789 :
2790 5450 : if (d->m_osAngularUnits.empty())
2791 : {
2792 113 : d->m_osAngularUnits = "degree";
2793 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2794 : }
2795 :
2796 5450 : if (ppszName != nullptr)
2797 3059 : *ppszName = d->m_osAngularUnits.c_str();
2798 5450 : return d->m_dfAngularUnitToRadian;
2799 : }
2800 :
2801 : /**
2802 : * \brief Fetch angular geographic coordinate system units.
2803 : *
2804 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2805 : * will be assumed. This method only checks directly under the GEOGCS node
2806 : * for units.
2807 : *
2808 : * This method does the same thing as the C function OSRGetAngularUnits().
2809 : *
2810 : * @param ppszName a pointer to be updated with the pointer to the units name.
2811 : * The returned value remains internal to the OGRSpatialReference and should
2812 : * not be freed, or modified. It may be invalidated on the next
2813 : * OGRSpatialReference call.
2814 : *
2815 : * @return the value to multiply by angular distances to transform them to
2816 : * radians.
2817 : * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2818 : */
2819 :
2820 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2821 :
2822 : {
2823 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2824 : }
2825 :
2826 : /************************************************************************/
2827 : /* OSRGetAngularUnits() */
2828 : /************************************************************************/
2829 :
2830 : /**
2831 : * \brief Fetch angular geographic coordinate system units.
2832 : *
2833 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2834 : */
2835 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2836 :
2837 : {
2838 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2839 :
2840 1 : return ToPointer(hSRS)->GetAngularUnits(
2841 1 : const_cast<const char **>(ppszName));
2842 : }
2843 :
2844 : /************************************************************************/
2845 : /* SetLinearUnitsAndUpdateParameters() */
2846 : /************************************************************************/
2847 :
2848 : /**
2849 : * \brief Set the linear units for the projection.
2850 : *
2851 : * This method creates a UNIT subnode with the specified values as a
2852 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2853 : * SetLinearUnits() method, but it also updates all existing linear
2854 : * projection parameter values from the old units to the new units.
2855 : *
2856 : * @param pszName the units name to be used. Some preferred units
2857 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2858 : * and SRS_UL_US_FOOT.
2859 : *
2860 : * @param dfInMeters the value to multiple by a length in the indicated
2861 : * units to transform to meters. Some standard conversion factors can
2862 : * be found in ogr_srs_api.h.
2863 : *
2864 : * @param pszUnitAuthority Unit authority name. Or nullptr
2865 : *
2866 : * @param pszUnitCode Unit code. Or nullptr
2867 : *
2868 : * @return OGRERR_NONE on success.
2869 : */
2870 :
2871 39 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2872 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2873 : const char *pszUnitCode)
2874 :
2875 : {
2876 78 : TAKE_OPTIONAL_LOCK();
2877 :
2878 39 : if (dfInMeters <= 0.0)
2879 0 : return OGRERR_FAILURE;
2880 :
2881 39 : d->refreshProjObj();
2882 39 : if (!d->m_pj_crs)
2883 0 : return OGRERR_FAILURE;
2884 :
2885 39 : d->demoteFromBoundCRS();
2886 39 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2887 : {
2888 78 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2889 39 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2890 : pszUnitAuthority, pszUnitCode, true));
2891 : }
2892 39 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2893 : pszName, dfInMeters,
2894 : pszUnitAuthority, pszUnitCode));
2895 39 : d->undoDemoteFromBoundCRS();
2896 :
2897 39 : d->m_osLinearUnits = pszName;
2898 39 : d->dfToMeter = dfInMeters;
2899 :
2900 39 : return OGRERR_NONE;
2901 : }
2902 :
2903 : /************************************************************************/
2904 : /* OSRSetLinearUnitsAndUpdateParameters() */
2905 : /************************************************************************/
2906 :
2907 : /**
2908 : * \brief Set the linear units for the projection.
2909 : *
2910 : * This function is the same as
2911 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2912 : */
2913 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2914 : const char *pszUnits,
2915 : double dfInMeters)
2916 :
2917 : {
2918 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2919 : OGRERR_FAILURE);
2920 :
2921 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2922 1 : dfInMeters);
2923 : }
2924 :
2925 : /************************************************************************/
2926 : /* SetLinearUnits() */
2927 : /************************************************************************/
2928 :
2929 : /**
2930 : * \brief Set the linear units for the projection.
2931 : *
2932 : * This method creates a UNIT subnode with the specified values as a
2933 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2934 : * Geographic 3D CRS the vertical axis units will be set.
2935 : *
2936 : * This method does the same as the C function OSRSetLinearUnits().
2937 : *
2938 : * @param pszUnitsName the units name to be used. Some preferred units
2939 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2940 : * and SRS_UL_US_FOOT.
2941 : *
2942 : * @param dfInMeters the value to multiple by a length in the indicated
2943 : * units to transform to meters. Some standard conversion factors can
2944 : * be found in ogr_srs_api.h.
2945 : *
2946 : * @return OGRERR_NONE on success.
2947 : */
2948 :
2949 7087 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2950 : double dfInMeters)
2951 :
2952 : {
2953 7087 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2954 : }
2955 :
2956 : /************************************************************************/
2957 : /* OSRSetLinearUnits() */
2958 : /************************************************************************/
2959 :
2960 : /**
2961 : * \brief Set the linear units for the projection.
2962 : *
2963 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2964 : */
2965 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2966 : double dfInMeters)
2967 :
2968 : {
2969 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2970 :
2971 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2972 : }
2973 :
2974 : /************************************************************************/
2975 : /* SetTargetLinearUnits() */
2976 : /************************************************************************/
2977 :
2978 : /**
2979 : * \brief Set the linear units for the projection.
2980 : *
2981 : * This method creates a UNIT subnode with the specified values as a
2982 : * child of the target node.
2983 : *
2984 : * This method does the same as the C function OSRSetTargetLinearUnits().
2985 : *
2986 : * @param pszTargetKey the keyword to set the linear units for.
2987 : * i.e. "PROJCS" or "VERT_CS"
2988 : *
2989 : * @param pszUnitsName the units name to be used. Some preferred units
2990 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2991 : * and SRS_UL_US_FOOT.
2992 : *
2993 : * @param dfInMeters the value to multiple by a length in the indicated
2994 : * units to transform to meters. Some standard conversion factors can
2995 : * be found in ogr_srs_api.h.
2996 : *
2997 : * @param pszUnitAuthority Unit authority name. Or nullptr
2998 : *
2999 : * @param pszUnitCode Unit code. Or nullptr
3000 : *
3001 : * @return OGRERR_NONE on success.
3002 : *
3003 : * @since OGR 1.9.0
3004 : */
3005 :
3006 11230 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3007 : const char *pszUnitsName,
3008 : double dfInMeters,
3009 : const char *pszUnitAuthority,
3010 : const char *pszUnitCode)
3011 :
3012 : {
3013 22460 : TAKE_OPTIONAL_LOCK();
3014 :
3015 11230 : if (dfInMeters <= 0.0)
3016 0 : return OGRERR_FAILURE;
3017 :
3018 11230 : d->refreshProjObj();
3019 11230 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3020 11230 : if (pszTargetKey == nullptr)
3021 : {
3022 11230 : if (!d->m_pj_crs)
3023 0 : return OGRERR_FAILURE;
3024 :
3025 11230 : d->demoteFromBoundCRS();
3026 11230 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3027 : {
3028 16674 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3029 8337 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3030 : pszUnitAuthority, pszUnitCode, false));
3031 : }
3032 22460 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
3033 11230 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3034 : pszUnitAuthority, pszUnitCode));
3035 11230 : d->undoDemoteFromBoundCRS();
3036 :
3037 11230 : d->m_osLinearUnits = pszUnitsName;
3038 11230 : d->dfToMeter = dfInMeters;
3039 :
3040 11230 : return OGRERR_NONE;
3041 : }
3042 :
3043 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3044 :
3045 0 : if (poCS == nullptr)
3046 0 : return OGRERR_FAILURE;
3047 :
3048 0 : char szValue[128] = {'\0'};
3049 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
3050 0 : dfInMeters > std::numeric_limits<int>::min() &&
3051 0 : dfInMeters == static_cast<int>(dfInMeters))
3052 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3053 : else
3054 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3055 :
3056 0 : OGR_SRSNode *poUnits = nullptr;
3057 0 : if (poCS->FindChild("UNIT") >= 0)
3058 : {
3059 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3060 0 : if (poUnits->GetChildCount() < 2)
3061 0 : return OGRERR_FAILURE;
3062 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
3063 0 : poUnits->GetChild(1)->SetValue(szValue);
3064 0 : if (poUnits->FindChild("AUTHORITY") != -1)
3065 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3066 : }
3067 : else
3068 : {
3069 0 : poUnits = new OGR_SRSNode("UNIT");
3070 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3071 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
3072 :
3073 0 : poCS->AddChild(poUnits);
3074 : }
3075 :
3076 0 : return OGRERR_NONE;
3077 : }
3078 :
3079 : /************************************************************************/
3080 : /* OSRSetLinearUnits() */
3081 : /************************************************************************/
3082 :
3083 : /**
3084 : * \brief Set the linear units for the target node.
3085 : *
3086 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3087 : *
3088 : * @since OGR 1.9.0
3089 : */
3090 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3091 : const char *pszTargetKey, const char *pszUnits,
3092 : double dfInMeters)
3093 :
3094 : {
3095 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3096 :
3097 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3098 1 : dfInMeters);
3099 : }
3100 :
3101 : /************************************************************************/
3102 : /* GetLinearUnits() */
3103 : /************************************************************************/
3104 :
3105 : /**
3106 : * \brief Fetch linear projection units.
3107 : *
3108 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3109 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3110 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3111 : * axis units will be returned.
3112 : *
3113 : * This method does the same thing as the C function OSRGetLinearUnits()
3114 : *
3115 : * @param ppszName a pointer to be updated with the pointer to the units name.
3116 : * The returned value remains internal to the OGRSpatialReference and should
3117 : * not be freed, or modified. It may be invalidated on the next
3118 : * OGRSpatialReference call.
3119 : *
3120 : * @return the value to multiply by linear distances to transform them to
3121 : * meters.
3122 : * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3123 : */
3124 :
3125 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3126 :
3127 : {
3128 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3129 : }
3130 :
3131 : /**
3132 : * \brief Fetch linear projection units.
3133 : *
3134 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3135 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3136 : * for units.
3137 : *
3138 : * This method does the same thing as the C function OSRGetLinearUnits()
3139 : *
3140 : * @param ppszName a pointer to be updated with the pointer to the units name.
3141 : * The returned value remains internal to the OGRSpatialReference and should
3142 : * not be freed, or modified. It may be invalidated on the next
3143 : * OGRSpatialReference call.
3144 : *
3145 : * @return the value to multiply by linear distances to transform them to
3146 : * meters.
3147 : * @since GDAL 2.3.0
3148 : */
3149 :
3150 19610 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3151 :
3152 : {
3153 19610 : return GetTargetLinearUnits(nullptr, ppszName);
3154 : }
3155 :
3156 : /************************************************************************/
3157 : /* OSRGetLinearUnits() */
3158 : /************************************************************************/
3159 :
3160 : /**
3161 : * \brief Fetch linear projection units.
3162 : *
3163 : * This function is the same as OGRSpatialReference::GetLinearUnits()
3164 : */
3165 227 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3166 :
3167 : {
3168 227 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3169 :
3170 227 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3171 : }
3172 :
3173 : /************************************************************************/
3174 : /* GetTargetLinearUnits() */
3175 : /************************************************************************/
3176 :
3177 : /**
3178 : * \brief Fetch linear units for target.
3179 : *
3180 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3181 : *
3182 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3183 : *
3184 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3185 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3186 : * GEOCCS, GEOGCS and VERT_CS are looked up)
3187 : * @param ppszName a pointer to be updated with the pointer to the units name.
3188 : * The returned value remains internal to the OGRSpatialReference and should not
3189 : * be freed, or modified. It may be invalidated on the next
3190 : * OGRSpatialReference call. ppszName can be set to NULL.
3191 : *
3192 : * @return the value to multiply by linear distances to transform them to
3193 : * meters.
3194 : *
3195 : * @since OGR 1.9.0
3196 : * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3197 : * const.
3198 : */
3199 :
3200 19761 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3201 : const char **ppszName) const
3202 :
3203 : {
3204 39522 : TAKE_OPTIONAL_LOCK();
3205 :
3206 19761 : d->refreshProjObj();
3207 :
3208 19761 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3209 19761 : if (pszTargetKey == nullptr)
3210 : {
3211 : // Use cached result if available
3212 19670 : if (!d->m_osLinearUnits.empty())
3213 : {
3214 8367 : if (ppszName)
3215 7600 : *ppszName = d->m_osLinearUnits.c_str();
3216 8367 : return d->dfToMeter;
3217 : }
3218 :
3219 : while (true)
3220 : {
3221 11303 : if (d->m_pj_crs == nullptr)
3222 : {
3223 242 : break;
3224 : }
3225 :
3226 11061 : d->demoteFromBoundCRS();
3227 11061 : PJ *coordSys = nullptr;
3228 11061 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3229 : {
3230 30 : for (int iComponent = 0; iComponent < 2; iComponent++)
3231 : {
3232 30 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3233 30 : d->m_pj_crs, iComponent);
3234 30 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3235 : {
3236 : auto temp =
3237 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
3238 0 : proj_destroy(subCRS);
3239 0 : subCRS = temp;
3240 : }
3241 60 : if (subCRS &&
3242 30 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3243 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3244 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3245 : {
3246 24 : coordSys = proj_crs_get_coordinate_system(
3247 : d->getPROJContext(), subCRS);
3248 24 : proj_destroy(subCRS);
3249 24 : break;
3250 : }
3251 6 : else if (subCRS)
3252 : {
3253 6 : proj_destroy(subCRS);
3254 : }
3255 : }
3256 24 : if (coordSys == nullptr)
3257 : {
3258 0 : d->undoDemoteFromBoundCRS();
3259 0 : break;
3260 : }
3261 : }
3262 : else
3263 : {
3264 11037 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3265 11037 : d->m_pj_crs);
3266 : }
3267 :
3268 11061 : d->undoDemoteFromBoundCRS();
3269 11061 : if (!coordSys)
3270 : {
3271 0 : break;
3272 : }
3273 11061 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3274 :
3275 11061 : if (csType != PJ_CS_TYPE_CARTESIAN &&
3276 2173 : csType != PJ_CS_TYPE_VERTICAL &&
3277 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3278 : csType != PJ_CS_TYPE_SPHERICAL)
3279 : {
3280 0 : proj_destroy(coordSys);
3281 0 : break;
3282 : }
3283 :
3284 11061 : int axis = 0;
3285 :
3286 11061 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3287 : csType == PJ_CS_TYPE_SPHERICAL)
3288 : {
3289 : const int axisCount =
3290 2173 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3291 :
3292 2173 : if (axisCount == 3)
3293 : {
3294 4 : axis = 2;
3295 : }
3296 : else
3297 : {
3298 2169 : proj_destroy(coordSys);
3299 2169 : break;
3300 : }
3301 : }
3302 :
3303 8892 : double dfConvFactor = 0.0;
3304 8892 : const char *pszUnitName = nullptr;
3305 8892 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3306 : nullptr, nullptr, nullptr, &dfConvFactor,
3307 : &pszUnitName, nullptr, nullptr))
3308 : {
3309 0 : proj_destroy(coordSys);
3310 0 : break;
3311 : }
3312 :
3313 8892 : d->m_osLinearUnits = pszUnitName;
3314 8892 : d->dfToMeter = dfConvFactor;
3315 8892 : if (ppszName)
3316 1162 : *ppszName = d->m_osLinearUnits.c_str();
3317 :
3318 8892 : proj_destroy(coordSys);
3319 8892 : return dfConvFactor;
3320 : }
3321 :
3322 2411 : d->m_osLinearUnits = "unknown";
3323 2411 : d->dfToMeter = 1.0;
3324 :
3325 2411 : if (ppszName != nullptr)
3326 2225 : *ppszName = d->m_osLinearUnits.c_str();
3327 2411 : return 1.0;
3328 : }
3329 :
3330 91 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3331 :
3332 91 : if (ppszName != nullptr)
3333 37 : *ppszName = "unknown";
3334 :
3335 91 : if (poCS == nullptr)
3336 53 : return 1.0;
3337 :
3338 114 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3339 : {
3340 114 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3341 :
3342 114 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3343 : {
3344 38 : if (ppszName != nullptr)
3345 37 : *ppszName = poChild->GetChild(0)->GetValue();
3346 :
3347 38 : return CPLAtof(poChild->GetChild(1)->GetValue());
3348 : }
3349 : }
3350 :
3351 0 : return 1.0;
3352 : }
3353 :
3354 : /**
3355 : * \brief Fetch linear units for target.
3356 : *
3357 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3358 : *
3359 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3360 : *
3361 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3362 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3363 : * GEOCCS and VERT_CS are looked up)
3364 : * @param ppszName a pointer to be updated with the pointer to the units name.
3365 : * The returned value remains internal to the OGRSpatialReference and should not
3366 : * be freed, or modified. It may be invalidated on the next
3367 : * OGRSpatialReference call. ppszName can be set to NULL.
3368 : *
3369 : * @return the value to multiply by linear distances to transform them to
3370 : * meters.
3371 : *
3372 : * @since GDAL 2.3.0
3373 : */
3374 :
3375 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3376 : char **ppszName) const
3377 :
3378 : {
3379 0 : return GetTargetLinearUnits(pszTargetKey,
3380 0 : const_cast<const char **>(ppszName));
3381 : }
3382 :
3383 : /************************************************************************/
3384 : /* OSRGetTargetLinearUnits() */
3385 : /************************************************************************/
3386 :
3387 : /**
3388 : * \brief Fetch linear projection units.
3389 : *
3390 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3391 : *
3392 : * @since OGR 1.9.0
3393 : */
3394 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3395 : const char *pszTargetKey, char **ppszName)
3396 :
3397 : {
3398 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3399 :
3400 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3401 4 : pszTargetKey, const_cast<const char **>(ppszName));
3402 : }
3403 :
3404 : /************************************************************************/
3405 : /* GetPrimeMeridian() */
3406 : /************************************************************************/
3407 :
3408 : /**
3409 : * \brief Fetch prime meridian info.
3410 : *
3411 : * Returns the offset of the prime meridian from greenwich in degrees,
3412 : * and the prime meridian name (if requested). If no PRIMEM value exists
3413 : * in the coordinate system definition a value of "Greenwich" and an
3414 : * offset of 0.0 is assumed.
3415 : *
3416 : * If the prime meridian name is returned, the pointer is to an internal
3417 : * copy of the name. It should not be freed, altered or depended on after
3418 : * the next OGR call.
3419 : *
3420 : * This method is the same as the C function OSRGetPrimeMeridian().
3421 : *
3422 : * @param ppszName return location for prime meridian name. If NULL, name
3423 : * is not returned.
3424 : *
3425 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3426 : * degrees.
3427 : * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3428 : */
3429 :
3430 1434 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3431 :
3432 : {
3433 2868 : TAKE_OPTIONAL_LOCK();
3434 :
3435 1434 : d->refreshProjObj();
3436 :
3437 1434 : if (!d->m_osPrimeMeridianName.empty())
3438 : {
3439 60 : if (ppszName != nullptr)
3440 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3441 60 : return d->dfFromGreenwich;
3442 : }
3443 :
3444 : while (true)
3445 : {
3446 1374 : if (!d->m_pj_crs)
3447 0 : break;
3448 :
3449 1374 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3450 1374 : if (!pm)
3451 0 : break;
3452 :
3453 1374 : d->m_osPrimeMeridianName = proj_get_name(pm);
3454 1374 : if (ppszName)
3455 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3456 1374 : double dfLongitude = 0.0;
3457 1374 : double dfConvFactor = 0.0;
3458 1374 : proj_prime_meridian_get_parameters(
3459 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3460 1374 : proj_destroy(pm);
3461 2748 : d->dfFromGreenwich =
3462 1374 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3463 1374 : return d->dfFromGreenwich;
3464 : }
3465 :
3466 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3467 0 : d->dfFromGreenwich = 0.0;
3468 0 : if (ppszName != nullptr)
3469 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3470 0 : return d->dfFromGreenwich;
3471 : }
3472 :
3473 : /**
3474 : * \brief Fetch prime meridian info.
3475 : *
3476 : * Returns the offset of the prime meridian from greenwich in degrees,
3477 : * and the prime meridian name (if requested). If no PRIMEM value exists
3478 : * in the coordinate system definition a value of "Greenwich" and an
3479 : * offset of 0.0 is assumed.
3480 : *
3481 : * If the prime meridian name is returned, the pointer is to an internal
3482 : * copy of the name. It should not be freed, altered or depended on after
3483 : * the next OGR call.
3484 : *
3485 : * This method is the same as the C function OSRGetPrimeMeridian().
3486 : *
3487 : * @param ppszName return location for prime meridian name. If NULL, name
3488 : * is not returned.
3489 : *
3490 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3491 : * degrees.
3492 : * @since GDAL 2.3.0
3493 : */
3494 :
3495 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3496 :
3497 : {
3498 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3499 : }
3500 :
3501 : /************************************************************************/
3502 : /* OSRGetPrimeMeridian() */
3503 : /************************************************************************/
3504 :
3505 : /**
3506 : * \brief Fetch prime meridian info.
3507 : *
3508 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3509 : */
3510 0 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3511 :
3512 : {
3513 0 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3514 :
3515 0 : return ToPointer(hSRS)->GetPrimeMeridian(
3516 0 : const_cast<const char **>(ppszName));
3517 : }
3518 :
3519 : /************************************************************************/
3520 : /* SetGeogCS() */
3521 : /************************************************************************/
3522 :
3523 : /**
3524 : * \brief Set geographic coordinate system.
3525 : *
3526 : * This method is used to set the datum, ellipsoid, prime meridian and
3527 : * angular units for a geographic coordinate system. It can be used on its
3528 : * own to establish a geographic spatial reference, or applied to a
3529 : * projected coordinate system to establish the underlying geographic
3530 : * coordinate system.
3531 : *
3532 : * This method does the same as the C function OSRSetGeogCS().
3533 : *
3534 : * @param pszGeogName user visible name for the geographic coordinate system
3535 : * (not to serve as a key).
3536 : *
3537 : * @param pszDatumName key name for this datum. The OpenGIS specification
3538 : * lists some known values, and otherwise EPSG datum names with a standard
3539 : * transformation are considered legal keys.
3540 : *
3541 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3542 : *
3543 : * @param dfSemiMajor the semi major axis of the spheroid.
3544 : *
3545 : * @param dfInvFlattening the inverse flattening for the spheroid.
3546 : * This can be computed from the semi minor axis as
3547 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3548 : *
3549 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3550 : * If this is NULL a default value of "Greenwich" will be used.
3551 : *
3552 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3553 : * meridian. Always in Degrees
3554 : *
3555 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3556 : * standard names). If NULL a value of "degrees" will be assumed.
3557 : *
3558 : * @param dfConvertToRadians value to multiply angular units by to transform
3559 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3560 : * pszAngularUnits is NULL.
3561 : *
3562 : * @return OGRERR_NONE on success.
3563 : */
3564 :
3565 9216 : OGRErr OGRSpatialReference::SetGeogCS(
3566 : const char *pszGeogName, const char *pszDatumName,
3567 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3568 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3569 : double dfConvertToRadians)
3570 :
3571 : {
3572 18432 : TAKE_OPTIONAL_LOCK();
3573 :
3574 9216 : d->bNormInfoSet = FALSE;
3575 9216 : d->m_osAngularUnits.clear();
3576 9216 : d->m_dfAngularUnitToRadian = 0.0;
3577 9216 : d->m_osPrimeMeridianName.clear();
3578 9216 : d->dfFromGreenwich = 0.0;
3579 :
3580 : /* -------------------------------------------------------------------- */
3581 : /* For a geocentric coordinate system we want to set the datum */
3582 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3583 : /* temporary srs and use the copy method which has special */
3584 : /* handling for GEOCCS. */
3585 : /* -------------------------------------------------------------------- */
3586 9216 : if (IsGeocentric())
3587 : {
3588 4 : OGRSpatialReference oGCS;
3589 :
3590 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3591 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3592 : dfConvertToRadians);
3593 2 : return CopyGeogCSFrom(&oGCS);
3594 : }
3595 :
3596 9214 : auto cs = proj_create_ellipsoidal_2D_cs(
3597 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3598 : dfConvertToRadians);
3599 : // Prime meridian expressed in Degree
3600 9214 : auto obj = proj_create_geographic_crs(
3601 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3602 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3603 9214 : proj_destroy(cs);
3604 :
3605 13897 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3606 4683 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3607 : {
3608 4531 : d->setPjCRS(obj);
3609 : }
3610 4683 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3611 : {
3612 9366 : d->setPjCRS(
3613 4683 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3614 4683 : proj_destroy(obj);
3615 : }
3616 : else
3617 : {
3618 0 : proj_destroy(obj);
3619 : }
3620 :
3621 9214 : return OGRERR_NONE;
3622 : }
3623 :
3624 : /************************************************************************/
3625 : /* OSRSetGeogCS() */
3626 : /************************************************************************/
3627 :
3628 : /**
3629 : * \brief Set geographic coordinate system.
3630 : *
3631 : * This function is the same as OGRSpatialReference::SetGeogCS()
3632 : */
3633 18 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3634 : const char *pszDatumName, const char *pszSpheroidName,
3635 : double dfSemiMajor, double dfInvFlattening,
3636 : const char *pszPMName, double dfPMOffset,
3637 : const char *pszAngularUnits, double dfConvertToRadians)
3638 :
3639 : {
3640 18 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3641 :
3642 18 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3643 : pszSpheroidName, dfSemiMajor,
3644 : dfInvFlattening, pszPMName, dfPMOffset,
3645 18 : pszAngularUnits, dfConvertToRadians);
3646 : }
3647 :
3648 : /************************************************************************/
3649 : /* SetWellKnownGeogCS() */
3650 : /************************************************************************/
3651 :
3652 : /**
3653 : * \brief Set a GeogCS based on well known name.
3654 : *
3655 : * This may be called on an empty OGRSpatialReference to make a geographic
3656 : * coordinate system, or on something with an existing PROJCS node to
3657 : * set the underlying geographic coordinate system of a projected coordinate
3658 : * system.
3659 : *
3660 : * The following well known text values are currently supported,
3661 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3662 : * <ul>
3663 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3664 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3665 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3666 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3667 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3668 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3669 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3670 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3671 : * </ul>
3672 : *
3673 : * @param pszName name of well known geographic coordinate system.
3674 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3675 : * recognised, the target object is already initialized, or an EPSG value
3676 : * can't be successfully looked up.
3677 : */
3678 :
3679 2925 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3680 :
3681 : {
3682 5850 : TAKE_OPTIONAL_LOCK();
3683 :
3684 : /* -------------------------------------------------------------------- */
3685 : /* Check for EPSG authority numbers. */
3686 : /* -------------------------------------------------------------------- */
3687 2925 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3688 : {
3689 84 : OGRSpatialReference oSRS2;
3690 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3691 42 : if (eErr != OGRERR_NONE)
3692 0 : return eErr;
3693 :
3694 42 : if (!oSRS2.IsGeographic())
3695 0 : return OGRERR_FAILURE;
3696 :
3697 42 : return CopyGeogCSFrom(&oSRS2);
3698 : }
3699 :
3700 : /* -------------------------------------------------------------------- */
3701 : /* Check for simple names. */
3702 : /* -------------------------------------------------------------------- */
3703 2883 : const char *pszWKT = nullptr;
3704 :
3705 2883 : if (EQUAL(pszName, "WGS84"))
3706 : {
3707 2098 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3708 : }
3709 785 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3710 : {
3711 604 : pszWKT =
3712 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3713 : "ELLIPSOID[\"WGS "
3714 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3715 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3716 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3717 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3718 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3719 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3720 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3721 : "ID[\"OGC\",\"CRS84\"]]";
3722 : }
3723 181 : else if (EQUAL(pszName, "WGS72"))
3724 19 : pszWKT =
3725 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3726 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3727 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3728 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3729 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3730 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3731 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3732 :
3733 162 : else if (EQUAL(pszName, "NAD27"))
3734 134 : pszWKT =
3735 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3736 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3737 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3738 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3739 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3740 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3741 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3742 :
3743 28 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3744 0 : pszWKT =
3745 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3746 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3747 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3748 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3749 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3750 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3751 :
3752 28 : else if (EQUAL(pszName, "NAD83"))
3753 24 : pszWKT =
3754 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3755 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3756 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3757 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3758 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3759 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3760 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3761 : "\"EPSG\",\"4269\"]]";
3762 :
3763 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3764 0 : pszWKT =
3765 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3766 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3767 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3768 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3769 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3770 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3771 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3772 :
3773 : else
3774 4 : return OGRERR_FAILURE;
3775 :
3776 : /* -------------------------------------------------------------------- */
3777 : /* Import the WKT */
3778 : /* -------------------------------------------------------------------- */
3779 5758 : OGRSpatialReference oSRS2;
3780 2879 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3781 2879 : if (eErr != OGRERR_NONE)
3782 0 : return eErr;
3783 :
3784 : /* -------------------------------------------------------------------- */
3785 : /* Copy over. */
3786 : /* -------------------------------------------------------------------- */
3787 2879 : return CopyGeogCSFrom(&oSRS2);
3788 : }
3789 :
3790 : /************************************************************************/
3791 : /* OSRSetWellKnownGeogCS() */
3792 : /************************************************************************/
3793 :
3794 : /**
3795 : * \brief Set a GeogCS based on well known name.
3796 : *
3797 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3798 : */
3799 134 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3800 :
3801 : {
3802 134 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3803 :
3804 134 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3805 : }
3806 :
3807 : /************************************************************************/
3808 : /* CopyGeogCSFrom() */
3809 : /************************************************************************/
3810 :
3811 : /**
3812 : * \brief Copy GEOGCS from another OGRSpatialReference.
3813 : *
3814 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3815 : * If this object has a PROJCS root already, the GEOGCS is installed within
3816 : * it, otherwise it is installed as the root.
3817 : *
3818 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3819 : *
3820 : * @return OGRERR_NONE on success or an error code.
3821 : */
3822 :
3823 3515 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3824 :
3825 : {
3826 7030 : TAKE_OPTIONAL_LOCK();
3827 :
3828 3515 : d->bNormInfoSet = FALSE;
3829 3515 : d->m_osAngularUnits.clear();
3830 3515 : d->m_dfAngularUnitToRadian = 0.0;
3831 3515 : d->m_osPrimeMeridianName.clear();
3832 3515 : d->dfFromGreenwich = 0.0;
3833 :
3834 3515 : d->refreshProjObj();
3835 3515 : poSrcSRS->d->refreshProjObj();
3836 3515 : if (!poSrcSRS->d->m_pj_crs)
3837 : {
3838 1 : return OGRERR_FAILURE;
3839 : }
3840 : auto geodCRS =
3841 3514 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3842 3514 : if (!geodCRS)
3843 : {
3844 0 : return OGRERR_FAILURE;
3845 : }
3846 :
3847 : /* -------------------------------------------------------------------- */
3848 : /* Handle geocentric coordinate systems specially. We just */
3849 : /* want to copy the DATUM. */
3850 : /* -------------------------------------------------------------------- */
3851 3514 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3852 : {
3853 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3854 : #if PROJ_VERSION_MAJOR > 7 || \
3855 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3856 : if (datum == nullptr)
3857 : {
3858 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3859 : }
3860 : #endif
3861 3 : if (datum == nullptr)
3862 : {
3863 0 : proj_destroy(geodCRS);
3864 0 : return OGRERR_FAILURE;
3865 : }
3866 :
3867 3 : const char *pszUnitName = nullptr;
3868 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3869 :
3870 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3871 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3872 : unitConvFactor);
3873 3 : proj_destroy(datum);
3874 :
3875 3 : d->setPjCRS(pj_crs);
3876 : }
3877 :
3878 3511 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3879 : {
3880 324 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3881 324 : d->m_pj_crs, geodCRS);
3882 324 : d->setPjCRS(pj_crs);
3883 : }
3884 :
3885 : else
3886 : {
3887 3187 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3888 : }
3889 :
3890 : // Apply TOWGS84 of source CRS
3891 3514 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3892 : {
3893 : auto target =
3894 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3895 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3896 1 : poSrcSRS->d->m_pj_crs);
3897 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3898 : target, co));
3899 1 : proj_destroy(target);
3900 1 : proj_destroy(co);
3901 : }
3902 :
3903 3514 : proj_destroy(geodCRS);
3904 :
3905 3514 : return OGRERR_NONE;
3906 : }
3907 :
3908 : /************************************************************************/
3909 : /* OSRCopyGeogCSFrom() */
3910 : /************************************************************************/
3911 :
3912 : /**
3913 : * \brief Copy GEOGCS from another OGRSpatialReference.
3914 : *
3915 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3916 : */
3917 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3918 : const OGRSpatialReferenceH hSrcSRS)
3919 :
3920 : {
3921 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3922 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3923 :
3924 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3925 : }
3926 :
3927 : /************************************************************************/
3928 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3929 : /************************************************************************/
3930 :
3931 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3932 : *
3933 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3934 : */
3935 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3936 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3937 :
3938 : /**
3939 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3940 : */
3941 2687 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3942 : {
3943 2687 : return SET_FROM_USER_INPUT_LIMITATIONS;
3944 : }
3945 :
3946 : /************************************************************************/
3947 : /* RemoveIDFromMemberOfEnsembles() */
3948 : /************************************************************************/
3949 :
3950 : // cppcheck-suppress constParameterReference
3951 198 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3952 : {
3953 : // Remove "id" from members of datum ensembles for compatibility with
3954 : // older PROJ versions
3955 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3956 : // and https://github.com/OSGeo/PROJ/pull/3221
3957 198 : if (obj.GetType() == CPLJSONObject::Type::Object)
3958 : {
3959 243 : for (auto &subObj : obj.GetChildren())
3960 : {
3961 191 : RemoveIDFromMemberOfEnsembles(subObj);
3962 : }
3963 : }
3964 162 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3965 162 : obj.GetName() == "members")
3966 : {
3967 51 : for (auto &subObj : obj.ToArray())
3968 : {
3969 44 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3970 : {
3971 43 : subObj.Delete("id");
3972 : }
3973 : }
3974 : }
3975 198 : }
3976 :
3977 : /************************************************************************/
3978 : /* SetFromUserInput() */
3979 : /************************************************************************/
3980 :
3981 : /**
3982 : * \brief Set spatial reference from various text formats.
3983 : *
3984 : * This method will examine the provided input, and try to deduce the
3985 : * format, and then use it to initialize the spatial reference system. It
3986 : * may take the following forms:
3987 : *
3988 : * <ol>
3989 : * <li> Well Known Text definition - passed on to importFromWkt().
3990 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3991 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3992 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3993 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3994 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3995 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3996 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3997 : * WGS84 or WGS72.
3998 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3999 : * <li> PROJJSON (PROJ >= 6.2)
4000 : * </ol>
4001 : *
4002 : * It is expected that this method will be extended in the future to support
4003 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4004 : * State Plane definitions.
4005 : *
4006 : * This method is intended to be flexible, but by its nature it is
4007 : * imprecise as it must guess information about the format intended. When
4008 : * possible applications should call the specific method appropriate if the
4009 : * input is known to be in a particular format.
4010 : *
4011 : * This method does the same thing as the OSRSetFromUserInput() function.
4012 : *
4013 : * @param pszDefinition text definition to try to deduce SRS from.
4014 : *
4015 : * @return OGRERR_NONE on success, or an error code if the name isn't
4016 : * recognised, the definition is corrupt, or an EPSG value can't be
4017 : * successfully looked up.
4018 : */
4019 :
4020 18295 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4021 : {
4022 18295 : return SetFromUserInput(pszDefinition, nullptr);
4023 : }
4024 :
4025 : /**
4026 : * \brief Set spatial reference from various text formats.
4027 : *
4028 : * This method will examine the provided input, and try to deduce the
4029 : * format, and then use it to initialize the spatial reference system. It
4030 : * may take the following forms:
4031 : *
4032 : * <ol>
4033 : * <li> Well Known Text definition - passed on to importFromWkt().
4034 : * <li> "EPSG:n" - number passed on to importFromEPSG().
4035 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4036 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4037 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4038 : * <li> PROJ.4 definitions - passed on to importFromProj4().
4039 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
4040 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4041 : * WGS84 or WGS72.
4042 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4043 : * <li> PROJJSON (PROJ >= 6.2)
4044 : * </ol>
4045 : *
4046 : * It is expected that this method will be extended in the future to support
4047 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4048 : * State Plane definitions.
4049 : *
4050 : * This method is intended to be flexible, but by its nature it is
4051 : * imprecise as it must guess information about the format intended. When
4052 : * possible applications should call the specific method appropriate if the
4053 : * input is known to be in a particular format.
4054 : *
4055 : * This method does the same thing as the OSRSetFromUserInput() and
4056 : * OSRSetFromUserInputEx() functions.
4057 : *
4058 : * @param pszDefinition text definition to try to deduce SRS from.
4059 : *
4060 : * @param papszOptions NULL terminated list of options, or NULL.
4061 : * <ol>
4062 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4063 : * Whether http:// or https:// access is allowed. Defaults to YES.
4064 : * <li> ALLOW_FILE_ACCESS=YES/NO.
4065 : * Whether reading a file using the Virtual File System layer is allowed
4066 : * (can also involve network access). Defaults to YES.
4067 : * </ol>
4068 : *
4069 : * @return OGRERR_NONE on success, or an error code if the name isn't
4070 : * recognised, the definition is corrupt, or an EPSG value can't be
4071 : * successfully looked up.
4072 : */
4073 :
4074 25153 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4075 : CSLConstList papszOptions)
4076 : {
4077 50306 : TAKE_OPTIONAL_LOCK();
4078 :
4079 : // Skip leading white space
4080 25155 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
4081 2 : pszDefinition++;
4082 :
4083 25153 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4084 : {
4085 1 : pszDefinition += 6;
4086 : }
4087 :
4088 : /* -------------------------------------------------------------------- */
4089 : /* Is it a recognised syntax? */
4090 : /* -------------------------------------------------------------------- */
4091 25153 : const char *const wktKeywords[] = {
4092 : // WKT1
4093 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4094 : // WKT2"
4095 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4096 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4097 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4098 368414 : for (const char *keyword : wktKeywords)
4099 : {
4100 351807 : if (STARTS_WITH_CI(pszDefinition, keyword))
4101 : {
4102 8546 : return importFromWkt(pszDefinition);
4103 : }
4104 : }
4105 :
4106 16607 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4107 16607 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4108 : {
4109 9505 : OGRErr eStatus = OGRERR_NONE;
4110 :
4111 9505 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4112 : {
4113 : // Use proj_create() as it allows things like EPSG:3157+4617
4114 : // that are not normally supported by the below code that
4115 : // builds manually a compound CRS
4116 62 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4117 62 : if (!pj)
4118 : {
4119 1 : return OGRERR_FAILURE;
4120 : }
4121 61 : Clear();
4122 61 : d->setPjCRS(pj);
4123 61 : return OGRERR_NONE;
4124 : }
4125 : else
4126 : {
4127 : eStatus =
4128 9443 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4129 : }
4130 :
4131 9443 : return eStatus;
4132 : }
4133 :
4134 7102 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4135 6403 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4136 6402 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4137 6344 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4138 6344 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4139 6344 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4140 758 : return importFromURN(pszDefinition);
4141 :
4142 6344 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4143 6342 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4144 6341 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4145 1152 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4146 1151 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4147 5193 : return importFromCRSURL(pszDefinition);
4148 :
4149 1151 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4150 1 : return importFromWMSAUTO(pszDefinition);
4151 :
4152 : // WMS/WCS OGC codes like OGC:CRS84.
4153 1150 : if (EQUAL(pszDefinition, "OGC:CRS84"))
4154 76 : return SetWellKnownGeogCS(pszDefinition + 4);
4155 :
4156 1074 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4157 1 : return SetWellKnownGeogCS(pszDefinition);
4158 :
4159 1073 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4160 : {
4161 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
4162 0 : char *pszCode = strstr(pszFile, ",") + 1;
4163 :
4164 0 : pszCode[-1] = '\0';
4165 :
4166 0 : OGRErr err = importFromDict(pszFile, pszCode);
4167 0 : CPLFree(pszFile);
4168 :
4169 0 : return err;
4170 : }
4171 :
4172 1073 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4173 1069 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4174 : {
4175 303 : Clear();
4176 303 : return SetWellKnownGeogCS(pszDefinition);
4177 : }
4178 :
4179 : // PROJJSON
4180 770 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4181 56 : (strstr(pszDefinition, "GeodeticCRS") ||
4182 56 : strstr(pszDefinition, "GeographicCRS") ||
4183 24 : strstr(pszDefinition, "ProjectedCRS") ||
4184 0 : strstr(pszDefinition, "VerticalCRS") ||
4185 0 : strstr(pszDefinition, "BoundCRS") ||
4186 0 : strstr(pszDefinition, "CompoundCRS") ||
4187 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
4188 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
4189 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
4190 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
4191 0 : strstr(pszDefinition, "EngineeringCRS") ||
4192 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
4193 0 : strstr(pszDefinition, "ParametricCRS") ||
4194 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
4195 0 : strstr(pszDefinition, "TemporalCRS") ||
4196 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
4197 : {
4198 : PJ *pj;
4199 56 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4200 : {
4201 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4202 : // a unknown id.
4203 7 : CPLJSONDocument oCRSDoc;
4204 7 : if (!oCRSDoc.LoadMemory(pszDefinition))
4205 0 : return OGRERR_CORRUPT_DATA;
4206 7 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4207 7 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
4208 7 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4209 : }
4210 : else
4211 : {
4212 49 : pj = proj_create(d->getPROJContext(), pszDefinition);
4213 : }
4214 56 : if (!pj)
4215 : {
4216 2 : return OGRERR_FAILURE;
4217 : }
4218 54 : Clear();
4219 54 : d->setPjCRS(pj);
4220 54 : return OGRERR_NONE;
4221 : }
4222 :
4223 714 : if (strstr(pszDefinition, "+proj") != nullptr ||
4224 278 : strstr(pszDefinition, "+init") != nullptr)
4225 436 : return importFromProj4(pszDefinition);
4226 :
4227 278 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
4228 278 : STARTS_WITH_CI(pszDefinition, "https://"))
4229 : {
4230 1 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4231 : "ALLOW_NETWORK_ACCESS", "YES")))
4232 0 : return importFromUrl(pszDefinition);
4233 :
4234 1 : CPLError(CE_Failure, CPLE_AppDefined,
4235 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4236 : pszDefinition);
4237 1 : return OGRERR_FAILURE;
4238 : }
4239 :
4240 277 : if (EQUAL(pszDefinition, "osgb:BNG"))
4241 : {
4242 13 : return importFromEPSG(27700);
4243 : }
4244 :
4245 : // Used by German CityGML files
4246 264 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4247 : {
4248 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
4249 0 : return SetFromUserInput("EPSG:25832+5783");
4250 : }
4251 264 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4252 : {
4253 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4254 4 : return SetFromUserInput("EPSG:25832+7837");
4255 : }
4256 :
4257 : // Used by Japan's Fundamental Geospatial Data (FGD) GML
4258 260 : if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4259 0 : return importFromEPSG(4612); // JGD2000 (slight difference in years)
4260 260 : else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4261 10 : return importFromEPSG(6668); // JGD2011
4262 250 : else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4263 : {
4264 : // FIXME when EPSG attributes a CRS code
4265 4 : return importFromWkt(
4266 : "GEOGCRS[\"JGD2024\",\n"
4267 : " DATUM[\"Japanese Geodetic Datum 2024\",\n"
4268 : " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4269 : " LENGTHUNIT[\"metre\",1]]],\n"
4270 : " PRIMEM[\"Greenwich\",0,\n"
4271 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4272 : " CS[ellipsoidal,2],\n"
4273 : " AXIS[\"geodetic latitude (Lat)\",north,\n"
4274 : " ORDER[1],\n"
4275 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4276 : " AXIS[\"geodetic longitude (Lon)\",east,\n"
4277 : " ORDER[2],\n"
4278 : " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4279 : " USAGE[\n"
4280 : " SCOPE[\"Horizontal component of 3D system.\"],\n"
4281 : " AREA[\"Japan - onshore and offshore.\"],\n"
4282 4 : " BBOX[17.09,122.38,46.05,157.65]]]");
4283 : }
4284 :
4285 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4286 246 : const char *pszDot = strrchr(pszDefinition, ':');
4287 246 : if (pszDot)
4288 : {
4289 111 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4290 : auto authorities =
4291 111 : proj_get_authorities_from_database(d->getPROJContext());
4292 111 : if (authorities)
4293 : {
4294 111 : std::set<std::string> aosCandidateAuthorities;
4295 232 : for (auto iter = authorities; *iter; ++iter)
4296 : {
4297 231 : if (*iter == osPrefix)
4298 : {
4299 110 : aosCandidateAuthorities.clear();
4300 110 : aosCandidateAuthorities.insert(*iter);
4301 110 : break;
4302 : }
4303 : // Deal with "IAU_2015" as authority in the list and input
4304 : // "IAU:code"
4305 121 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4306 121 : 0 &&
4307 0 : (*iter)[osPrefix.size()] == '_')
4308 : {
4309 0 : aosCandidateAuthorities.insert(*iter);
4310 : }
4311 : // Deal with "IAU_2015" as authority in the list and input
4312 : // "IAU:2015:code"
4313 242 : else if (osPrefix.find(':') != std::string::npos &&
4314 121 : osPrefix.size() == strlen(*iter) &&
4315 121 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
4316 : {
4317 0 : aosCandidateAuthorities.clear();
4318 0 : aosCandidateAuthorities.insert(*iter);
4319 0 : break;
4320 : }
4321 : }
4322 :
4323 111 : proj_string_list_destroy(authorities);
4324 :
4325 111 : if (!aosCandidateAuthorities.empty())
4326 : {
4327 110 : auto obj = proj_create_from_database(
4328 : d->getPROJContext(),
4329 110 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4330 : PJ_CATEGORY_CRS, false, nullptr);
4331 110 : if (!obj)
4332 : {
4333 3 : return OGRERR_FAILURE;
4334 : }
4335 107 : Clear();
4336 107 : d->setPjCRS(obj);
4337 107 : return OGRERR_NONE;
4338 : }
4339 : }
4340 : }
4341 :
4342 : /* -------------------------------------------------------------------- */
4343 : /* Try to open it as a file. */
4344 : /* -------------------------------------------------------------------- */
4345 136 : if (!CPLTestBool(
4346 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4347 : {
4348 : VSIStatBufL sStat;
4349 20 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4350 10 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4351 : {
4352 0 : CPLError(CE_Failure, CPLE_AppDefined,
4353 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4354 : pszDefinition);
4355 0 : return OGRERR_FAILURE;
4356 : }
4357 : // We used to silently return an error without a CE_Failure message
4358 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4359 10 : return OGRERR_CORRUPT_DATA;
4360 : }
4361 :
4362 252 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4363 126 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4364 126 : if (fp == nullptr)
4365 123 : return OGRERR_CORRUPT_DATA;
4366 :
4367 3 : const size_t nBufMax = 100000;
4368 3 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4369 3 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4370 3 : VSIFCloseL(fp);
4371 :
4372 3 : if (nBytes == nBufMax - 1)
4373 : {
4374 0 : CPLDebug("OGR",
4375 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4376 : "but it is to large for our generous buffer. Is it really "
4377 : "just a WKT definition?",
4378 : pszDefinition);
4379 0 : CPLFree(pszBuffer);
4380 0 : return OGRERR_FAILURE;
4381 : }
4382 :
4383 3 : pszBuffer[nBytes] = '\0';
4384 :
4385 3 : char *pszBufPtr = pszBuffer;
4386 3 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4387 0 : pszBufPtr++;
4388 :
4389 3 : OGRErr err = OGRERR_NONE;
4390 3 : if (pszBufPtr[0] == '<')
4391 0 : err = importFromXML(pszBufPtr);
4392 3 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4393 3 : strstr(pszBuffer, "+init") != nullptr) &&
4394 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4395 0 : strstr(pszBuffer, "extension") == nullptr))
4396 0 : err = importFromProj4(pszBufPtr);
4397 : else
4398 : {
4399 3 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4400 : {
4401 0 : pszBufPtr += 6;
4402 : }
4403 :
4404 : // coverity[tainted_data]
4405 3 : err = importFromWkt(pszBufPtr);
4406 : }
4407 :
4408 3 : CPLFree(pszBuffer);
4409 :
4410 3 : return err;
4411 : }
4412 :
4413 : /************************************************************************/
4414 : /* OSRSetFromUserInput() */
4415 : /************************************************************************/
4416 :
4417 : /**
4418 : * \brief Set spatial reference from various text formats.
4419 : *
4420 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4421 : *
4422 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4423 : */
4424 299 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4425 : const char *pszDef)
4426 :
4427 : {
4428 299 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4429 :
4430 299 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4431 : }
4432 :
4433 : /************************************************************************/
4434 : /* OSRSetFromUserInputEx() */
4435 : /************************************************************************/
4436 :
4437 : /**
4438 : * \brief Set spatial reference from various text formats.
4439 : *
4440 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4441 : *
4442 : * @since GDAL 3.9
4443 : */
4444 975 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4445 : CSLConstList papszOptions)
4446 :
4447 : {
4448 975 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4449 :
4450 975 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4451 : }
4452 :
4453 : /************************************************************************/
4454 : /* ImportFromUrl() */
4455 : /************************************************************************/
4456 :
4457 : /**
4458 : * \brief Set spatial reference from a URL.
4459 : *
4460 : * This method will download the spatial reference at a given URL and
4461 : * feed it into SetFromUserInput for you.
4462 : *
4463 : * This method does the same thing as the OSRImportFromUrl() function.
4464 : *
4465 : * @param pszUrl text definition to try to deduce SRS from.
4466 : *
4467 : * @return OGRERR_NONE on success, or an error code with the curl
4468 : * error message if it is unable to download data.
4469 : */
4470 :
4471 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4472 :
4473 : {
4474 10 : TAKE_OPTIONAL_LOCK();
4475 :
4476 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4477 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4478 : {
4479 2 : CPLError(CE_Failure, CPLE_AppDefined,
4480 : "The given string is not recognized as a URL"
4481 : "starting with 'http://' -- %s",
4482 : pszUrl);
4483 2 : return OGRERR_FAILURE;
4484 : }
4485 :
4486 : /* -------------------------------------------------------------------- */
4487 : /* Fetch the result. */
4488 : /* -------------------------------------------------------------------- */
4489 3 : CPLErrorReset();
4490 :
4491 6 : std::string osUrl(pszUrl);
4492 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4493 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4494 : // to query WKT. To allow a static server to be used, rather append a
4495 : // "ogcwkt/" suffix.
4496 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4497 5 : "http://spatialreference.org/ref/"})
4498 : {
4499 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4500 : {
4501 : const CPLStringList aosTokens(
4502 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4503 3 : if (aosTokens.size() == 2)
4504 : {
4505 2 : osUrl = "https://spatialreference.org/ref/";
4506 2 : osUrl += aosTokens[0]; // authority
4507 2 : osUrl += '/';
4508 2 : osUrl += aosTokens[1]; // code
4509 2 : osUrl += "/ogcwkt/";
4510 : }
4511 3 : break;
4512 : }
4513 : }
4514 :
4515 3 : const char *pszTimeout = "TIMEOUT=10";
4516 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4517 :
4518 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4519 :
4520 : /* -------------------------------------------------------------------- */
4521 : /* Try to handle errors. */
4522 : /* -------------------------------------------------------------------- */
4523 :
4524 3 : if (psResult == nullptr)
4525 0 : return OGRERR_FAILURE;
4526 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4527 3 : psResult->pabyData == nullptr)
4528 : {
4529 0 : if (CPLGetLastErrorNo() == 0)
4530 : {
4531 0 : CPLError(CE_Failure, CPLE_AppDefined,
4532 : "No data was returned from the given URL");
4533 : }
4534 0 : CPLHTTPDestroyResult(psResult);
4535 0 : return OGRERR_FAILURE;
4536 : }
4537 :
4538 3 : if (psResult->nStatus != 0)
4539 : {
4540 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4541 : psResult->nStatus, psResult->pszErrBuf);
4542 0 : CPLHTTPDestroyResult(psResult);
4543 0 : return OGRERR_FAILURE;
4544 : }
4545 :
4546 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4547 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4548 3 : STARTS_WITH_CI(pszData, "https://"))
4549 : {
4550 0 : CPLError(CE_Failure, CPLE_AppDefined,
4551 : "The data that was downloaded also starts with 'http://' "
4552 : "and cannot be passed into SetFromUserInput. Is this "
4553 : "really a spatial reference definition? ");
4554 0 : CPLHTTPDestroyResult(psResult);
4555 0 : return OGRERR_FAILURE;
4556 : }
4557 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4558 : {
4559 0 : CPLHTTPDestroyResult(psResult);
4560 0 : return OGRERR_FAILURE;
4561 : }
4562 :
4563 3 : CPLHTTPDestroyResult(psResult);
4564 3 : return OGRERR_NONE;
4565 : }
4566 :
4567 : /************************************************************************/
4568 : /* OSRimportFromUrl() */
4569 : /************************************************************************/
4570 :
4571 : /**
4572 : * \brief Set spatial reference from a URL.
4573 : *
4574 : * This function is the same as OGRSpatialReference::importFromUrl()
4575 : */
4576 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4577 :
4578 : {
4579 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4580 :
4581 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4582 : }
4583 :
4584 : /************************************************************************/
4585 : /* importFromURNPart() */
4586 : /************************************************************************/
4587 5384 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4588 : const char *pszCode,
4589 : const char *pszURN)
4590 : {
4591 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4592 : (void)this;
4593 : (void)pszAuthority;
4594 : (void)pszCode;
4595 : (void)pszURN;
4596 : return OGRERR_FAILURE;
4597 : #else
4598 : /* -------------------------------------------------------------------- */
4599 : /* Is this an EPSG code? Note that we import it with EPSG */
4600 : /* preferred axis ordering for geographic coordinate systems. */
4601 : /* -------------------------------------------------------------------- */
4602 5384 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4603 4854 : return importFromEPSGA(atoi(pszCode));
4604 :
4605 : /* -------------------------------------------------------------------- */
4606 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4607 : /* -------------------------------------------------------------------- */
4608 530 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4609 0 : return importFromDict("IAU2000.wkt", pszCode);
4610 :
4611 : /* -------------------------------------------------------------------- */
4612 : /* Is this an OGC code? */
4613 : /* -------------------------------------------------------------------- */
4614 530 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4615 : {
4616 1 : CPLError(CE_Failure, CPLE_AppDefined,
4617 : "URN %s has unrecognized authority.", pszURN);
4618 1 : return OGRERR_FAILURE;
4619 : }
4620 :
4621 529 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4622 517 : return SetWellKnownGeogCS(pszCode);
4623 12 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4624 0 : return SetWellKnownGeogCS(pszCode);
4625 12 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4626 0 : return SetWellKnownGeogCS(pszCode);
4627 12 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4628 10 : return SetWellKnownGeogCS("CRS84");
4629 :
4630 : /* -------------------------------------------------------------------- */
4631 : /* Handle auto codes. We need to convert from format */
4632 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4633 : /* -------------------------------------------------------------------- */
4634 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4635 : {
4636 2 : char szWMSAuto[100] = {'\0'};
4637 :
4638 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4639 0 : return OGRERR_FAILURE;
4640 :
4641 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4642 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4643 : {
4644 26 : if (szWMSAuto[i] == ':')
4645 4 : szWMSAuto[i] = ',';
4646 : }
4647 :
4648 2 : return importFromWMSAUTO(szWMSAuto);
4649 : }
4650 :
4651 : /* -------------------------------------------------------------------- */
4652 : /* Not a recognise OGC item. */
4653 : /* -------------------------------------------------------------------- */
4654 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4655 : pszURN);
4656 :
4657 0 : return OGRERR_FAILURE;
4658 : #endif
4659 : }
4660 :
4661 : /************************************************************************/
4662 : /* importFromURN() */
4663 : /* */
4664 : /* See OGC recommendation paper 06-023r1 or later for details. */
4665 : /************************************************************************/
4666 :
4667 : /**
4668 : * \brief Initialize from OGC URN.
4669 : *
4670 : * Initializes this spatial reference from a coordinate system defined
4671 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4672 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4673 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4674 : *
4675 : * This method is also support through SetFromUserInput() which can
4676 : * normally be used for URNs.
4677 : *
4678 : * @param pszURN the urn string.
4679 : *
4680 : * @return OGRERR_NONE on success or an error code.
4681 : */
4682 :
4683 819 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4684 :
4685 : {
4686 819 : constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4687 1550 : if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4688 731 : CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4689 : CPL_VALUE_INTEGER)
4690 : {
4691 729 : return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4692 : }
4693 :
4694 180 : TAKE_OPTIONAL_LOCK();
4695 :
4696 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4697 :
4698 : // PROJ 8.2.0 has support for IAU codes now.
4699 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4700 : /* -------------------------------------------------------------------- */
4701 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4702 : /* -------------------------------------------------------------------- */
4703 : const char *pszIAU = strstr(pszURN, "IAU");
4704 : if (pszIAU)
4705 : {
4706 : const char *pszCode = strchr(pszIAU, ':');
4707 : if (pszCode)
4708 : {
4709 : ++pszCode;
4710 : if (*pszCode == ':')
4711 : ++pszCode;
4712 : return importFromDict("IAU2000.wkt", pszCode);
4713 : }
4714 : }
4715 : #endif
4716 :
4717 : if (strlen(pszURN) >= 1000)
4718 : {
4719 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4720 : return OGRERR_CORRUPT_DATA;
4721 : }
4722 : auto obj = proj_create(d->getPROJContext(), pszURN);
4723 : if (!obj)
4724 : {
4725 : return OGRERR_FAILURE;
4726 : }
4727 : Clear();
4728 : d->setPjCRS(obj);
4729 : return OGRERR_NONE;
4730 : #else
4731 90 : const char *pszCur = nullptr;
4732 :
4733 90 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4734 25 : pszCur = pszURN + 16;
4735 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4736 1 : pszCur = pszURN + 20;
4737 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4738 62 : pszCur = pszURN + 18;
4739 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4740 0 : pszCur = pszURN + 16;
4741 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4742 0 : pszCur = pszURN + 20;
4743 : else
4744 : {
4745 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4746 : pszURN);
4747 2 : return OGRERR_FAILURE;
4748 : }
4749 :
4750 : /* -------------------------------------------------------------------- */
4751 : /* Clear any existing definition. */
4752 : /* -------------------------------------------------------------------- */
4753 88 : Clear();
4754 :
4755 : /* -------------------------------------------------------------------- */
4756 : /* Find code (ignoring version) out of string like: */
4757 : /* */
4758 : /* authority:[version]:code */
4759 : /* -------------------------------------------------------------------- */
4760 88 : const char *pszAuthority = pszCur;
4761 :
4762 : // skip authority
4763 421 : while (*pszCur != ':' && *pszCur)
4764 333 : pszCur++;
4765 88 : if (*pszCur == ':')
4766 88 : pszCur++;
4767 :
4768 : // skip version
4769 88 : const char *pszBeforeVersion = pszCur;
4770 398 : while (*pszCur != ':' && *pszCur)
4771 310 : pszCur++;
4772 88 : if (*pszCur == ':')
4773 60 : pszCur++;
4774 : else
4775 : // We come here in the case, the content to parse is authority:code
4776 : // (instead of authority::code) which is probably illegal according to
4777 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4778 : // for example in what is returned by GeoServer.
4779 28 : pszCur = pszBeforeVersion;
4780 :
4781 88 : const char *pszCode = pszCur;
4782 :
4783 88 : const char *pszComma = strchr(pszCur, ',');
4784 88 : if (pszComma == nullptr)
4785 87 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4786 :
4787 : // There's a second part with the vertical SRS.
4788 1 : pszCur = pszComma + 1;
4789 1 : if (!STARTS_WITH(pszCur, "crs:"))
4790 : {
4791 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4792 : pszURN);
4793 0 : return OGRERR_FAILURE;
4794 : }
4795 :
4796 1 : pszCur += 4;
4797 :
4798 1 : char *pszFirstCode = CPLStrdup(pszCode);
4799 1 : pszFirstCode[pszComma - pszCode] = '\0';
4800 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4801 1 : CPLFree(pszFirstCode);
4802 :
4803 : // Do we want to turn this into a compound definition
4804 : // with a vertical datum?
4805 1 : if (eStatus != OGRERR_NONE)
4806 0 : return eStatus;
4807 :
4808 : /* -------------------------------------------------------------------- */
4809 : /* Find code (ignoring version) out of string like: */
4810 : /* */
4811 : /* authority:[version]:code */
4812 : /* -------------------------------------------------------------------- */
4813 1 : pszAuthority = pszCur;
4814 :
4815 : // skip authority
4816 5 : while (*pszCur != ':' && *pszCur)
4817 4 : pszCur++;
4818 1 : if (*pszCur == ':')
4819 1 : pszCur++;
4820 :
4821 : // skip version
4822 1 : pszBeforeVersion = pszCur;
4823 1 : while (*pszCur != ':' && *pszCur)
4824 0 : pszCur++;
4825 1 : if (*pszCur == ':')
4826 1 : pszCur++;
4827 : else
4828 0 : pszCur = pszBeforeVersion;
4829 :
4830 1 : pszCode = pszCur;
4831 :
4832 2 : OGRSpatialReference oVertSRS;
4833 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4834 1 : if (eStatus == OGRERR_NONE)
4835 : {
4836 1 : OGRSpatialReference oHorizSRS(*this);
4837 :
4838 1 : Clear();
4839 :
4840 1 : oHorizSRS.d->refreshProjObj();
4841 1 : oVertSRS.d->refreshProjObj();
4842 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4843 0 : return OGRERR_FAILURE;
4844 :
4845 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4846 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4847 :
4848 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4849 1 : osName += " + ";
4850 1 : osName += pszVertName ? pszVertName : "";
4851 :
4852 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4853 : }
4854 :
4855 1 : return eStatus;
4856 : #endif
4857 : }
4858 :
4859 : /************************************************************************/
4860 : /* importFromCRSURL() */
4861 : /* */
4862 : /* See OGC Best Practice document 11-135 for details. */
4863 : /************************************************************************/
4864 :
4865 : /**
4866 : * \brief Initialize from OGC URL.
4867 : *
4868 : * Initializes this spatial reference from a coordinate system defined
4869 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4870 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4871 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4872 : *
4873 : * This method is also supported through SetFromUserInput() which can
4874 : * normally be used for URLs.
4875 : *
4876 : * @param pszURL the URL string.
4877 : *
4878 : * @return OGRERR_NONE on success or an error code.
4879 : */
4880 :
4881 5296 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4882 :
4883 : {
4884 10592 : TAKE_OPTIONAL_LOCK();
4885 :
4886 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4887 : if (strlen(pszURL) >= 10000)
4888 : {
4889 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4890 : return OGRERR_CORRUPT_DATA;
4891 : }
4892 :
4893 : PJ *obj;
4894 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4895 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4896 : {
4897 : obj = proj_create(
4898 : d->getPROJContext(),
4899 : CPLSPrintf("IAU:%s",
4900 : pszURL +
4901 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4902 : }
4903 : else
4904 : #endif
4905 : {
4906 : obj = proj_create(d->getPROJContext(), pszURL);
4907 : }
4908 : if (!obj)
4909 : {
4910 : return OGRERR_FAILURE;
4911 : }
4912 : Clear();
4913 : d->setPjCRS(obj);
4914 : return OGRERR_NONE;
4915 : #else
4916 5296 : const char *pszCur = nullptr;
4917 :
4918 5296 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4919 2 : pszCur = pszURL + 26;
4920 5294 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4921 1 : pszCur = pszURL + 27;
4922 5293 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4923 5292 : pszCur = pszURL + 30;
4924 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4925 1 : pszCur = pszURL + 31;
4926 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4927 0 : pszCur = pszURL + 23;
4928 : else
4929 : {
4930 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4931 : pszURL);
4932 0 : return OGRERR_FAILURE;
4933 : }
4934 :
4935 5296 : if (*pszCur == '\0')
4936 : {
4937 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4938 0 : return OGRERR_FAILURE;
4939 : }
4940 :
4941 : /* -------------------------------------------------------------------- */
4942 : /* Clear any existing definition. */
4943 : /* -------------------------------------------------------------------- */
4944 5296 : Clear();
4945 :
4946 5296 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4947 : {
4948 : /* --------------------------------------------------------------------
4949 : */
4950 : /* It's a compound CRS, of the form: */
4951 : /* */
4952 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4953 : /* --------------------------------------------------------------------
4954 : */
4955 1 : pszCur += 12;
4956 :
4957 : // Extract each component CRS URL.
4958 1 : int iComponentUrl = 2;
4959 :
4960 2 : CPLString osName = "";
4961 1 : Clear();
4962 :
4963 3 : while (iComponentUrl != -1)
4964 : {
4965 2 : char searchStr[15] = {};
4966 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4967 :
4968 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
4969 :
4970 : // Figure out the next component URL.
4971 2 : char *pszComponentUrl = nullptr;
4972 :
4973 2 : if (pszUrlEnd)
4974 : {
4975 1 : size_t nLen = pszUrlEnd - pszCur;
4976 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4977 1 : strncpy(pszComponentUrl, pszCur, nLen);
4978 1 : pszComponentUrl[nLen] = '\0';
4979 :
4980 1 : ++iComponentUrl;
4981 1 : pszCur += nLen + strlen(searchStr);
4982 : }
4983 : else
4984 : {
4985 1 : if (iComponentUrl == 2)
4986 : {
4987 0 : CPLError(CE_Failure, CPLE_AppDefined,
4988 : "Compound CRS URLs must have at least two "
4989 : "component CRSs.");
4990 0 : return OGRERR_FAILURE;
4991 : }
4992 : else
4993 : {
4994 1 : pszComponentUrl = CPLStrdup(pszCur);
4995 : // no more components
4996 1 : iComponentUrl = -1;
4997 : }
4998 : }
4999 :
5000 2 : OGRSpatialReference oComponentSRS;
5001 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5002 :
5003 2 : CPLFree(pszComponentUrl);
5004 2 : pszComponentUrl = nullptr;
5005 :
5006 2 : if (eStatus == OGRERR_NONE)
5007 : {
5008 2 : if (osName.length() != 0)
5009 : {
5010 1 : osName += " + ";
5011 : }
5012 2 : osName += oComponentSRS.GetRoot()->GetValue();
5013 2 : SetNode("COMPD_CS", osName);
5014 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5015 : }
5016 : else
5017 0 : return eStatus;
5018 : }
5019 :
5020 1 : return OGRERR_NONE;
5021 : }
5022 :
5023 : /* -------------------------------------------------------------------- */
5024 : /* It's a normal CRS URL, of the form: */
5025 : /* */
5026 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
5027 : /* -------------------------------------------------------------------- */
5028 5295 : ++pszCur;
5029 5295 : const char *pszAuthority = pszCur;
5030 :
5031 : // skip authority
5032 125961 : while (*pszCur != '/' && *pszCur)
5033 120666 : pszCur++;
5034 5295 : if (*pszCur == '/')
5035 5294 : pszCur++;
5036 :
5037 : // skip version
5038 11609 : while (*pszCur != '/' && *pszCur)
5039 6314 : pszCur++;
5040 5295 : if (*pszCur == '/')
5041 5294 : pszCur++;
5042 :
5043 5295 : const char *pszCode = pszCur;
5044 :
5045 5295 : return importFromURNPart(pszAuthority, pszCode, pszURL);
5046 : #endif
5047 : }
5048 :
5049 : /************************************************************************/
5050 : /* importFromWMSAUTO() */
5051 : /************************************************************************/
5052 :
5053 : /**
5054 : * \brief Initialize from WMSAUTO string.
5055 : *
5056 : * Note that the WMS 1.3 specification does not include the
5057 : * units code, while apparently earlier specs do. We try to
5058 : * guess around this.
5059 : *
5060 : * @param pszDefinition the WMSAUTO string
5061 : *
5062 : * @return OGRERR_NONE on success or an error code.
5063 : */
5064 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5065 :
5066 : {
5067 6 : TAKE_OPTIONAL_LOCK();
5068 :
5069 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
5070 : if (strlen(pszDefinition) >= 10000)
5071 : {
5072 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5073 : return OGRERR_CORRUPT_DATA;
5074 : }
5075 :
5076 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
5077 : if (!obj)
5078 : {
5079 : return OGRERR_FAILURE;
5080 : }
5081 : Clear();
5082 : d->setPjCRS(obj);
5083 : return OGRERR_NONE;
5084 : #else
5085 : int nProjId, nUnitsId;
5086 3 : double dfRefLong, dfRefLat = 0.0;
5087 :
5088 : /* -------------------------------------------------------------------- */
5089 : /* Tokenize */
5090 : /* -------------------------------------------------------------------- */
5091 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5092 3 : pszDefinition += 5;
5093 :
5094 : char **papszTokens =
5095 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5096 :
5097 3 : if (CSLCount(papszTokens) == 4)
5098 : {
5099 0 : nProjId = atoi(papszTokens[0]);
5100 0 : nUnitsId = atoi(papszTokens[1]);
5101 0 : dfRefLong = CPLAtof(papszTokens[2]);
5102 0 : dfRefLat = CPLAtof(papszTokens[3]);
5103 : }
5104 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5105 : {
5106 0 : nProjId = atoi(papszTokens[0]);
5107 0 : nUnitsId = atoi(papszTokens[1]);
5108 0 : dfRefLong = CPLAtof(papszTokens[2]);
5109 0 : dfRefLat = 0.0;
5110 : }
5111 3 : else if (CSLCount(papszTokens) == 3)
5112 : {
5113 2 : nProjId = atoi(papszTokens[0]);
5114 2 : nUnitsId = 9001;
5115 2 : dfRefLong = CPLAtof(papszTokens[1]);
5116 2 : dfRefLat = CPLAtof(papszTokens[2]);
5117 : }
5118 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5119 : {
5120 0 : nProjId = atoi(papszTokens[0]);
5121 0 : nUnitsId = 9001;
5122 0 : dfRefLong = CPLAtof(papszTokens[1]);
5123 : }
5124 : else
5125 : {
5126 1 : CSLDestroy(papszTokens);
5127 1 : CPLError(CE_Failure, CPLE_AppDefined,
5128 : "AUTO projection has wrong number of arguments, expected\n"
5129 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
5130 : "AUTO:proj_id,ref_long,ref_lat");
5131 1 : return OGRERR_FAILURE;
5132 : }
5133 :
5134 2 : CSLDestroy(papszTokens);
5135 2 : papszTokens = nullptr;
5136 :
5137 : /* -------------------------------------------------------------------- */
5138 : /* Build coordsys. */
5139 : /* -------------------------------------------------------------------- */
5140 2 : Clear();
5141 :
5142 : /* -------------------------------------------------------------------- */
5143 : /* Set WGS84. */
5144 : /* -------------------------------------------------------------------- */
5145 2 : SetWellKnownGeogCS("WGS84");
5146 :
5147 2 : switch (nProjId)
5148 : {
5149 2 : case 42001: // Auto UTM
5150 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5151 : dfRefLat >= 0.0);
5152 2 : break;
5153 :
5154 0 : case 42002: // Auto TM (strangely very UTM-like).
5155 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
5156 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5157 0 : break;
5158 :
5159 0 : case 42003: // Auto Orthographic.
5160 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5161 0 : break;
5162 :
5163 0 : case 42004: // Auto Equirectangular
5164 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5165 0 : break;
5166 :
5167 0 : case 42005:
5168 0 : SetMollweide(dfRefLong, 0.0, 0.0);
5169 0 : break;
5170 :
5171 0 : default:
5172 0 : CPLError(CE_Failure, CPLE_AppDefined,
5173 : "Unsupported projection id in importFromWMSAUTO(): %d",
5174 : nProjId);
5175 0 : return OGRERR_FAILURE;
5176 : }
5177 :
5178 : /* -------------------------------------------------------------------- */
5179 : /* Set units. */
5180 : /* -------------------------------------------------------------------- */
5181 :
5182 2 : switch (nUnitsId)
5183 : {
5184 2 : case 9001:
5185 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5186 2 : break;
5187 :
5188 0 : case 9002:
5189 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5190 0 : break;
5191 :
5192 0 : case 9003:
5193 0 : SetTargetLinearUnits(nullptr, "US survey foot",
5194 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5195 0 : break;
5196 :
5197 0 : default:
5198 0 : CPLError(CE_Failure, CPLE_AppDefined,
5199 : "Unsupported units code (%d).", nUnitsId);
5200 0 : return OGRERR_FAILURE;
5201 : break;
5202 : }
5203 :
5204 2 : return OGRERR_NONE;
5205 : #endif
5206 : }
5207 :
5208 : /************************************************************************/
5209 : /* GetSemiMajor() */
5210 : /************************************************************************/
5211 :
5212 : /**
5213 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5214 : *
5215 : * This method does the same thing as the C function OSRGetSemiMajor().
5216 : *
5217 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5218 : * can be found.
5219 : *
5220 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5221 : */
5222 :
5223 6227 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5224 :
5225 : {
5226 12454 : TAKE_OPTIONAL_LOCK();
5227 :
5228 6227 : if (pnErr != nullptr)
5229 3345 : *pnErr = OGRERR_FAILURE;
5230 :
5231 6227 : d->refreshProjObj();
5232 6227 : if (!d->m_pj_crs)
5233 111 : return SRS_WGS84_SEMIMAJOR;
5234 :
5235 6116 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5236 6116 : if (!ellps)
5237 5 : return SRS_WGS84_SEMIMAJOR;
5238 :
5239 6111 : double dfSemiMajor = 0.0;
5240 6111 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5241 : nullptr, nullptr, nullptr);
5242 6111 : proj_destroy(ellps);
5243 :
5244 6111 : if (dfSemiMajor > 0)
5245 : {
5246 6111 : if (pnErr != nullptr)
5247 3231 : *pnErr = OGRERR_NONE;
5248 6111 : return dfSemiMajor;
5249 : }
5250 :
5251 0 : return SRS_WGS84_SEMIMAJOR;
5252 : }
5253 :
5254 : /************************************************************************/
5255 : /* OSRGetSemiMajor() */
5256 : /************************************************************************/
5257 :
5258 : /**
5259 : * \brief Get spheroid semi major axis.
5260 : *
5261 : * This function is the same as OGRSpatialReference::GetSemiMajor()
5262 : */
5263 77 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5264 :
5265 : {
5266 77 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5267 :
5268 77 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
5269 : }
5270 :
5271 : /************************************************************************/
5272 : /* GetInvFlattening() */
5273 : /************************************************************************/
5274 :
5275 : /**
5276 : * \brief Get spheroid inverse flattening.
5277 : *
5278 : * This method does the same thing as the C function OSRGetInvFlattening().
5279 : *
5280 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5281 : * can be found.
5282 : *
5283 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5284 : */
5285 :
5286 4317 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5287 :
5288 : {
5289 8634 : TAKE_OPTIONAL_LOCK();
5290 :
5291 4317 : if (pnErr != nullptr)
5292 3249 : *pnErr = OGRERR_FAILURE;
5293 :
5294 4317 : d->refreshProjObj();
5295 4317 : if (!d->m_pj_crs)
5296 111 : return SRS_WGS84_INVFLATTENING;
5297 :
5298 4206 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5299 4206 : if (!ellps)
5300 2 : return SRS_WGS84_INVFLATTENING;
5301 :
5302 4204 : double dfInvFlattening = -1.0;
5303 4204 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5304 : nullptr, &dfInvFlattening);
5305 4204 : proj_destroy(ellps);
5306 :
5307 4204 : if (dfInvFlattening >= 0.0)
5308 : {
5309 4204 : if (pnErr != nullptr)
5310 3138 : *pnErr = OGRERR_NONE;
5311 4204 : return dfInvFlattening;
5312 : }
5313 :
5314 0 : return SRS_WGS84_INVFLATTENING;
5315 : }
5316 :
5317 : /************************************************************************/
5318 : /* OSRGetInvFlattening() */
5319 : /************************************************************************/
5320 :
5321 : /**
5322 : * \brief Get spheroid inverse flattening.
5323 : *
5324 : * This function is the same as OGRSpatialReference::GetInvFlattening()
5325 : */
5326 10 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5327 :
5328 : {
5329 10 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5330 :
5331 10 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
5332 : }
5333 :
5334 : /************************************************************************/
5335 : /* GetEccentricity() */
5336 : /************************************************************************/
5337 :
5338 : /**
5339 : * \brief Get spheroid eccentricity
5340 : *
5341 : * @return eccentricity (or -1 in case of error)
5342 : * @since GDAL 2.3
5343 : */
5344 :
5345 0 : double OGRSpatialReference::GetEccentricity() const
5346 :
5347 : {
5348 0 : OGRErr eErr = OGRERR_NONE;
5349 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5350 0 : if (eErr != OGRERR_NONE)
5351 : {
5352 0 : return -1.0;
5353 : }
5354 0 : if (dfInvFlattening == 0.0)
5355 0 : return 0.0;
5356 0 : if (dfInvFlattening < 0.5)
5357 0 : return -1.0;
5358 0 : return sqrt(2.0 / dfInvFlattening -
5359 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5360 : }
5361 :
5362 : /************************************************************************/
5363 : /* GetSquaredEccentricity() */
5364 : /************************************************************************/
5365 :
5366 : /**
5367 : * \brief Get spheroid squared eccentricity
5368 : *
5369 : * @return squared eccentricity (or -1 in case of error)
5370 : * @since GDAL 2.3
5371 : */
5372 :
5373 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5374 :
5375 : {
5376 0 : OGRErr eErr = OGRERR_NONE;
5377 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5378 0 : if (eErr != OGRERR_NONE)
5379 : {
5380 0 : return -1.0;
5381 : }
5382 0 : if (dfInvFlattening == 0.0)
5383 0 : return 0.0;
5384 0 : if (dfInvFlattening < 0.5)
5385 0 : return -1.0;
5386 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5387 : }
5388 :
5389 : /************************************************************************/
5390 : /* GetSemiMinor() */
5391 : /************************************************************************/
5392 :
5393 : /**
5394 : * \brief Get spheroid semi minor axis.
5395 : *
5396 : * This method does the same thing as the C function OSRGetSemiMinor().
5397 : *
5398 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5399 : * can be found.
5400 : *
5401 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5402 : */
5403 :
5404 651 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5405 :
5406 : {
5407 651 : const double dfSemiMajor = GetSemiMajor(pnErr);
5408 651 : const double dfInvFlattening = GetInvFlattening(pnErr);
5409 :
5410 651 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5411 : }
5412 :
5413 : /************************************************************************/
5414 : /* OSRGetSemiMinor() */
5415 : /************************************************************************/
5416 :
5417 : /**
5418 : * \brief Get spheroid semi minor axis.
5419 : *
5420 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5421 : */
5422 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5423 :
5424 : {
5425 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5426 :
5427 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5428 : }
5429 :
5430 : /************************************************************************/
5431 : /* SetLocalCS() */
5432 : /************************************************************************/
5433 :
5434 : /**
5435 : * \brief Set the user visible LOCAL_CS name.
5436 : *
5437 : * This method is the same as the C function OSRSetLocalCS().
5438 : *
5439 : * This method will ensure a LOCAL_CS node is created as the root,
5440 : * and set the provided name on it. It must be used before SetLinearUnits().
5441 : *
5442 : * @param pszName the user visible name to assign. Not used as a key.
5443 : *
5444 : * @return OGRERR_NONE on success.
5445 : */
5446 :
5447 2892 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5448 :
5449 : {
5450 5784 : TAKE_OPTIONAL_LOCK();
5451 :
5452 2892 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5453 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5454 : {
5455 2892 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5456 : }
5457 : else
5458 : {
5459 0 : CPLDebug("OGR",
5460 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5461 : "It appears an incompatible object already exists.",
5462 : pszName);
5463 0 : return OGRERR_FAILURE;
5464 : }
5465 :
5466 2892 : return OGRERR_NONE;
5467 : }
5468 :
5469 : /************************************************************************/
5470 : /* OSRSetLocalCS() */
5471 : /************************************************************************/
5472 :
5473 : /**
5474 : * \brief Set the user visible LOCAL_CS name.
5475 : *
5476 : * This function is the same as OGRSpatialReference::SetLocalCS()
5477 : */
5478 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5479 :
5480 : {
5481 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5482 :
5483 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5484 : }
5485 :
5486 : /************************************************************************/
5487 : /* SetGeocCS() */
5488 : /************************************************************************/
5489 :
5490 : /**
5491 : * \brief Set the user visible GEOCCS name.
5492 : *
5493 : * This method is the same as the C function OSRSetGeocCS().
5494 :
5495 : * This method will ensure a GEOCCS node is created as the root,
5496 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5497 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5498 : * the GEOGCS.
5499 : *
5500 : * @param pszName the user visible name to assign. Not used as a key.
5501 : *
5502 : * @return OGRERR_NONE on success.
5503 : *
5504 : * @since OGR 1.9.0
5505 : */
5506 :
5507 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5508 :
5509 : {
5510 12 : TAKE_OPTIONAL_LOCK();
5511 :
5512 6 : OGRErr eErr = OGRERR_NONE;
5513 6 : d->refreshProjObj();
5514 6 : d->demoteFromBoundCRS();
5515 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5516 : {
5517 3 : d->setPjCRS(proj_create_geocentric_crs(
5518 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5519 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5520 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5521 : "Metre", 1.0));
5522 : }
5523 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5524 : {
5525 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5526 : }
5527 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5528 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5529 : {
5530 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5531 : #if PROJ_VERSION_MAJOR > 7 || \
5532 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5533 : if (datum == nullptr)
5534 : {
5535 : datum =
5536 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5537 : }
5538 : #endif
5539 1 : if (datum == nullptr)
5540 : {
5541 0 : d->undoDemoteFromBoundCRS();
5542 0 : return OGRERR_FAILURE;
5543 : }
5544 :
5545 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5546 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5547 : 0.0);
5548 1 : d->setPjCRS(pj_crs);
5549 :
5550 1 : proj_destroy(datum);
5551 : }
5552 : else
5553 : {
5554 1 : CPLDebug("OGR",
5555 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5556 : "It appears an incompatible object already exists.",
5557 : pszName);
5558 1 : eErr = OGRERR_FAILURE;
5559 : }
5560 6 : d->undoDemoteFromBoundCRS();
5561 :
5562 6 : return eErr;
5563 : }
5564 :
5565 : /************************************************************************/
5566 : /* OSRSetGeocCS() */
5567 : /************************************************************************/
5568 :
5569 : /**
5570 : * \brief Set the user visible PROJCS name.
5571 : *
5572 : * This function is the same as OGRSpatialReference::SetGeocCS()
5573 : *
5574 : * @since OGR 1.9.0
5575 : */
5576 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5577 :
5578 : {
5579 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5580 :
5581 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5582 : }
5583 :
5584 : /************************************************************************/
5585 : /* SetVertCS() */
5586 : /************************************************************************/
5587 :
5588 : /**
5589 : * \brief Set the user visible VERT_CS name.
5590 : *
5591 : * This method is the same as the C function OSRSetVertCS().
5592 :
5593 : * This method will ensure a VERT_CS node is created if needed. If the
5594 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5595 : * turned into a COMPD_CS.
5596 : *
5597 : * @param pszVertCSName the user visible name of the vertical coordinate
5598 : * system. Not used as a key.
5599 : *
5600 : * @param pszVertDatumName the user visible name of the vertical datum. It
5601 : * is helpful if this matches the EPSG name.
5602 : *
5603 : * @param nVertDatumType the OGC vertical datum type. Ignored
5604 : *
5605 : * @return OGRERR_NONE on success.
5606 : *
5607 : * @since OGR 1.9.0
5608 : */
5609 :
5610 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5611 : const char *pszVertDatumName,
5612 : int nVertDatumType)
5613 :
5614 : {
5615 1 : TAKE_OPTIONAL_LOCK();
5616 :
5617 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5618 :
5619 1 : d->refreshProjObj();
5620 :
5621 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5622 : pszVertDatumName, nullptr, 0.0);
5623 :
5624 : /* -------------------------------------------------------------------- */
5625 : /* Handle the case where we want to make a compound coordinate */
5626 : /* system. */
5627 : /* -------------------------------------------------------------------- */
5628 1 : if (IsProjected() || IsGeographic())
5629 : {
5630 1 : auto compoundCRS = proj_create_compound_crs(
5631 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5632 1 : proj_destroy(vertCRS);
5633 1 : d->setPjCRS(compoundCRS);
5634 : }
5635 : else
5636 : {
5637 0 : d->setPjCRS(vertCRS);
5638 : }
5639 2 : return OGRERR_NONE;
5640 : }
5641 :
5642 : /************************************************************************/
5643 : /* OSRSetVertCS() */
5644 : /************************************************************************/
5645 :
5646 : /**
5647 : * \brief Setup the vertical coordinate system.
5648 : *
5649 : * This function is the same as OGRSpatialReference::SetVertCS()
5650 : *
5651 : * @since OGR 1.9.0
5652 : */
5653 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5654 : const char *pszVertDatumName, int nVertDatumType)
5655 :
5656 : {
5657 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5658 :
5659 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5660 0 : nVertDatumType);
5661 : }
5662 :
5663 : /************************************************************************/
5664 : /* SetCompoundCS() */
5665 : /************************************************************************/
5666 :
5667 : /**
5668 : * \brief Setup a compound coordinate system.
5669 : *
5670 : * This method is the same as the C function OSRSetCompoundCS().
5671 :
5672 : * This method is replace the current SRS with a COMPD_CS coordinate system
5673 : * consisting of the passed in horizontal and vertical coordinate systems.
5674 : *
5675 : * @param pszName the name of the compound coordinate system.
5676 : *
5677 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5678 : *
5679 : * @param poVertSRS the vertical SRS (VERT_CS).
5680 : *
5681 : * @return OGRERR_NONE on success.
5682 : */
5683 :
5684 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5685 : const OGRSpatialReference *poHorizSRS,
5686 : const OGRSpatialReference *poVertSRS)
5687 :
5688 : {
5689 184 : TAKE_OPTIONAL_LOCK();
5690 :
5691 : /* -------------------------------------------------------------------- */
5692 : /* Verify these are legal horizontal and vertical coordinate */
5693 : /* systems. */
5694 : /* -------------------------------------------------------------------- */
5695 92 : if (!poVertSRS->IsVertical())
5696 : {
5697 0 : CPLError(CE_Failure, CPLE_AppDefined,
5698 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5699 0 : return OGRERR_FAILURE;
5700 : }
5701 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5702 : {
5703 0 : CPLError(CE_Failure, CPLE_AppDefined,
5704 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5705 : "GEOGCS.");
5706 0 : return OGRERR_FAILURE;
5707 : }
5708 :
5709 : /* -------------------------------------------------------------------- */
5710 : /* Replace with compound srs. */
5711 : /* -------------------------------------------------------------------- */
5712 92 : Clear();
5713 :
5714 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5715 92 : poHorizSRS->d->m_pj_crs,
5716 92 : poVertSRS->d->m_pj_crs);
5717 92 : d->setPjCRS(compoundCRS);
5718 :
5719 92 : return OGRERR_NONE;
5720 : }
5721 :
5722 : /************************************************************************/
5723 : /* OSRSetCompoundCS() */
5724 : /************************************************************************/
5725 :
5726 : /**
5727 : * \brief Setup a compound coordinate system.
5728 : *
5729 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5730 : */
5731 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5732 : OGRSpatialReferenceH hHorizSRS,
5733 : OGRSpatialReferenceH hVertSRS)
5734 :
5735 : {
5736 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5737 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5738 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5739 :
5740 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5741 16 : ToPointer(hVertSRS));
5742 : }
5743 :
5744 : /************************************************************************/
5745 : /* SetProjCS() */
5746 : /************************************************************************/
5747 :
5748 : /**
5749 : * \brief Set the user visible PROJCS name.
5750 : *
5751 : * This method is the same as the C function OSRSetProjCS().
5752 : *
5753 : * This method will ensure a PROJCS node is created as the root,
5754 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5755 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5756 : *
5757 : * @param pszName the user visible name to assign. Not used as a key.
5758 : *
5759 : * @return OGRERR_NONE on success.
5760 : */
5761 :
5762 4510 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5763 :
5764 : {
5765 4510 : TAKE_OPTIONAL_LOCK();
5766 :
5767 4510 : d->refreshProjObj();
5768 4510 : d->demoteFromBoundCRS();
5769 4510 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5770 : {
5771 487 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5772 : }
5773 : else
5774 : {
5775 4023 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5776 : nullptr, nullptr, nullptr,
5777 : nullptr, nullptr, 0, nullptr);
5778 4023 : auto cs = proj_create_cartesian_2D_cs(
5779 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5780 :
5781 4023 : auto projCRS = proj_create_projected_crs(
5782 4023 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5783 4023 : proj_destroy(dummyConv);
5784 4023 : proj_destroy(cs);
5785 :
5786 4023 : d->setPjCRS(projCRS);
5787 : }
5788 4510 : d->undoDemoteFromBoundCRS();
5789 9020 : return OGRERR_NONE;
5790 : }
5791 :
5792 : /************************************************************************/
5793 : /* OSRSetProjCS() */
5794 : /************************************************************************/
5795 :
5796 : /**
5797 : * \brief Set the user visible PROJCS name.
5798 : *
5799 : * This function is the same as OGRSpatialReference::SetProjCS()
5800 : */
5801 1 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5802 :
5803 : {
5804 1 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5805 :
5806 1 : return ToPointer(hSRS)->SetProjCS(pszName);
5807 : }
5808 :
5809 : /************************************************************************/
5810 : /* SetProjection() */
5811 : /************************************************************************/
5812 :
5813 : /**
5814 : * \brief Set a projection name.
5815 : *
5816 : * This method is the same as the C function OSRSetProjection().
5817 : *
5818 : * @param pszProjection the projection name, which should be selected from
5819 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5820 : *
5821 : * @return OGRERR_NONE on success.
5822 : */
5823 :
5824 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5825 :
5826 : {
5827 46 : TAKE_OPTIONAL_LOCK();
5828 :
5829 23 : OGR_SRSNode *poGeogCS = nullptr;
5830 :
5831 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5832 : {
5833 4 : poGeogCS = d->m_poRoot;
5834 4 : d->m_poRoot = nullptr;
5835 : }
5836 :
5837 23 : if (!GetAttrNode("PROJCS"))
5838 : {
5839 11 : SetNode("PROJCS", "unnamed");
5840 : }
5841 :
5842 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5843 23 : if (eErr != OGRERR_NONE)
5844 0 : return eErr;
5845 :
5846 23 : if (poGeogCS != nullptr)
5847 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5848 :
5849 23 : return OGRERR_NONE;
5850 : }
5851 :
5852 : /************************************************************************/
5853 : /* OSRSetProjection() */
5854 : /************************************************************************/
5855 :
5856 : /**
5857 : * \brief Set a projection name.
5858 : *
5859 : * This function is the same as OGRSpatialReference::SetProjection()
5860 : */
5861 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5862 :
5863 : {
5864 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5865 :
5866 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5867 : }
5868 :
5869 : /************************************************************************/
5870 : /* GetWKT2ProjectionMethod() */
5871 : /************************************************************************/
5872 :
5873 : /**
5874 : * \brief Returns info on the projection method, based on WKT2 naming
5875 : * conventions.
5876 : *
5877 : * The returned strings are short lived and should be considered to be
5878 : * invalidated by any further call to the GDAL API.
5879 : *
5880 : * @param[out] ppszMethodName Pointer to a string that will receive the
5881 : * projection method name.
5882 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5883 : * receive the name of the authority that defines the projection method.
5884 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5885 : * an authority.
5886 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5887 : * receive the code that defines the projection method.
5888 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5889 : * an authority.
5890 : *
5891 : * @return OGRERR_NONE on success.
5892 : */
5893 : OGRErr
5894 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5895 : const char **ppszMethodAuthName,
5896 : const char **ppszMethodCode) const
5897 : {
5898 2 : TAKE_OPTIONAL_LOCK();
5899 :
5900 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5901 1 : if (!conv)
5902 0 : return OGRERR_FAILURE;
5903 1 : const char *pszTmpMethodName = "";
5904 1 : const char *pszTmpMethodAuthName = "";
5905 1 : const char *pszTmpMethodCode = "";
5906 1 : int ret = proj_coordoperation_get_method_info(
5907 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5908 : &pszTmpMethodCode);
5909 : // "Internalize" temporary strings returned by PROJ
5910 1 : CPLAssert(pszTmpMethodName);
5911 1 : if (ppszMethodName)
5912 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5913 1 : if (ppszMethodAuthName)
5914 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5915 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5916 0 : : nullptr;
5917 1 : if (ppszMethodCode)
5918 0 : *ppszMethodCode =
5919 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5920 1 : proj_destroy(conv);
5921 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5922 : }
5923 :
5924 : /************************************************************************/
5925 : /* SetProjParm() */
5926 : /************************************************************************/
5927 :
5928 : /**
5929 : * \brief Set a projection parameter value.
5930 : *
5931 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5932 : *
5933 : * This method is the same as the C function OSRSetProjParm().
5934 : *
5935 : * Please check https://gdal.org/proj_list pages for
5936 : * legal parameter names for specific projections.
5937 : *
5938 : *
5939 : * @param pszParamName the parameter name, which should be selected from
5940 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5941 : *
5942 : * @param dfValue value to assign.
5943 : *
5944 : * @return OGRERR_NONE on success.
5945 : */
5946 :
5947 129 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5948 : double dfValue)
5949 :
5950 : {
5951 258 : TAKE_OPTIONAL_LOCK();
5952 :
5953 129 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5954 :
5955 129 : if (poPROJCS == nullptr)
5956 3 : return OGRERR_FAILURE;
5957 :
5958 126 : char szValue[64] = {'\0'};
5959 126 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5960 :
5961 : /* -------------------------------------------------------------------- */
5962 : /* Try to find existing parameter with this name. */
5963 : /* -------------------------------------------------------------------- */
5964 1030 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5965 : {
5966 943 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5967 :
5968 1242 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5969 1242 : poParam->GetChildCount() == 2 &&
5970 299 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5971 : {
5972 39 : poParam->GetChild(1)->SetValue(szValue);
5973 39 : return OGRERR_NONE;
5974 : }
5975 : }
5976 :
5977 : /* -------------------------------------------------------------------- */
5978 : /* Otherwise create a new parameter and append. */
5979 : /* -------------------------------------------------------------------- */
5980 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5981 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
5982 87 : poParam->AddChild(new OGR_SRSNode(szValue));
5983 :
5984 87 : poPROJCS->AddChild(poParam);
5985 :
5986 87 : return OGRERR_NONE;
5987 : }
5988 :
5989 : /************************************************************************/
5990 : /* OSRSetProjParm() */
5991 : /************************************************************************/
5992 :
5993 : /**
5994 : * \brief Set a projection parameter value.
5995 : *
5996 : * This function is the same as OGRSpatialReference::SetProjParm()
5997 : */
5998 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5999 : double dfValue)
6000 :
6001 : {
6002 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6003 :
6004 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6005 : }
6006 :
6007 : /************************************************************************/
6008 : /* FindProjParm() */
6009 : /************************************************************************/
6010 :
6011 : /**
6012 : * \brief Return the child index of the named projection parameter on
6013 : * its parent PROJCS node.
6014 : *
6015 : * @param pszParameter projection parameter to look for
6016 : * @param poPROJCS projection CS node to look in. If NULL is passed,
6017 : * the PROJCS node of the SpatialReference object will be searched.
6018 : *
6019 : * @return the child index of the named projection parameter. -1 on failure
6020 : */
6021 4792 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
6022 : const OGR_SRSNode *poPROJCS) const
6023 :
6024 : {
6025 9584 : TAKE_OPTIONAL_LOCK();
6026 :
6027 4792 : if (poPROJCS == nullptr)
6028 0 : poPROJCS = GetAttrNode("PROJCS");
6029 :
6030 4792 : if (poPROJCS == nullptr)
6031 0 : return -1;
6032 :
6033 : /* -------------------------------------------------------------------- */
6034 : /* Search for requested parameter. */
6035 : /* -------------------------------------------------------------------- */
6036 4792 : bool bIsWKT2 = false;
6037 31514 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6038 : {
6039 30897 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6040 :
6041 30897 : if (poParameter->GetChildCount() >= 2)
6042 : {
6043 21354 : const char *pszValue = poParameter->GetValue();
6044 35297 : if (EQUAL(pszValue, "PARAMETER") &&
6045 13943 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6046 : pszParameter))
6047 : {
6048 4175 : return iChild;
6049 : }
6050 17179 : else if (EQUAL(pszValue, "METHOD"))
6051 : {
6052 41 : bIsWKT2 = true;
6053 : }
6054 : }
6055 : }
6056 :
6057 : /* -------------------------------------------------------------------- */
6058 : /* Try similar names, for selected parameters. */
6059 : /* -------------------------------------------------------------------- */
6060 617 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6061 : {
6062 287 : if (bIsWKT2)
6063 : {
6064 8 : int iChild = FindProjParm(
6065 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6066 8 : if (iChild == -1)
6067 3 : iChild = FindProjParm(
6068 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6069 8 : return iChild;
6070 : }
6071 279 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6072 : }
6073 :
6074 330 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6075 : {
6076 34 : if (bIsWKT2)
6077 : {
6078 9 : int iChild = FindProjParm(
6079 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6080 9 : if (iChild == -1)
6081 0 : iChild = FindProjParm(
6082 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6083 9 : return iChild;
6084 : }
6085 25 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6086 25 : if (iChild == -1)
6087 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6088 25 : return iChild;
6089 : }
6090 :
6091 296 : return -1;
6092 : }
6093 :
6094 : /************************************************************************/
6095 : /* GetProjParm() */
6096 : /************************************************************************/
6097 :
6098 : /**
6099 : * \brief Fetch a projection parameter value.
6100 : *
6101 : * NOTE: This code should be modified to translate non degree angles into
6102 : * degrees based on the GEOGCS unit. This has not yet been done.
6103 : *
6104 : * This method is the same as the C function OSRGetProjParm().
6105 : *
6106 : * @param pszName the name of the parameter to fetch, from the set of
6107 : * SRS_PP codes in ogr_srs_api.h.
6108 : *
6109 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6110 : *
6111 : * @param pnErr place to put error code on failure. Ignored if NULL.
6112 : *
6113 : * @return value of parameter.
6114 : */
6115 :
6116 4726 : double OGRSpatialReference::GetProjParm(const char *pszName,
6117 : double dfDefaultValue,
6118 : OGRErr *pnErr) const
6119 :
6120 : {
6121 9452 : TAKE_OPTIONAL_LOCK();
6122 :
6123 4726 : d->refreshProjObj();
6124 4726 : GetRoot(); // force update of d->m_bNodesWKT2
6125 :
6126 4726 : if (pnErr != nullptr)
6127 3749 : *pnErr = OGRERR_NONE;
6128 :
6129 : /* -------------------------------------------------------------------- */
6130 : /* Find the desired parameter. */
6131 : /* -------------------------------------------------------------------- */
6132 : const OGR_SRSNode *poPROJCS =
6133 4726 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6134 4726 : if (poPROJCS == nullptr)
6135 : {
6136 258 : if (pnErr != nullptr)
6137 258 : *pnErr = OGRERR_FAILURE;
6138 258 : return dfDefaultValue;
6139 : }
6140 :
6141 4468 : const int iChild = FindProjParm(pszName, poPROJCS);
6142 4468 : if (iChild == -1)
6143 : {
6144 293 : if (IsProjected() && GetAxesCount() == 3)
6145 : {
6146 3 : OGRSpatialReference *poSRSTmp = Clone();
6147 3 : poSRSTmp->DemoteTo2D(nullptr);
6148 : const double dfRet =
6149 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6150 3 : delete poSRSTmp;
6151 3 : return dfRet;
6152 : }
6153 :
6154 290 : if (pnErr != nullptr)
6155 268 : *pnErr = OGRERR_FAILURE;
6156 290 : return dfDefaultValue;
6157 : }
6158 :
6159 4175 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6160 4175 : return CPLAtof(poParameter->GetChild(1)->GetValue());
6161 : }
6162 :
6163 : /************************************************************************/
6164 : /* OSRGetProjParm() */
6165 : /************************************************************************/
6166 :
6167 : /**
6168 : * \brief Fetch a projection parameter value.
6169 : *
6170 : * This function is the same as OGRSpatialReference::GetProjParm()
6171 : */
6172 90 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6173 : double dfDefaultValue, OGRErr *pnErr)
6174 :
6175 : {
6176 90 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6177 :
6178 90 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6179 : }
6180 :
6181 : /************************************************************************/
6182 : /* GetNormProjParm() */
6183 : /************************************************************************/
6184 :
6185 : /**
6186 : * \brief Fetch a normalized projection parameter value.
6187 : *
6188 : * This method is the same as GetProjParm() except that the value of
6189 : * the parameter is "normalized" into degrees or meters depending on
6190 : * whether it is linear or angular.
6191 : *
6192 : * This method is the same as the C function OSRGetNormProjParm().
6193 : *
6194 : * @param pszName the name of the parameter to fetch, from the set of
6195 : * SRS_PP codes in ogr_srs_api.h.
6196 : *
6197 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
6198 : *
6199 : * @param pnErr place to put error code on failure. Ignored if NULL.
6200 : *
6201 : * @return value of parameter.
6202 : */
6203 :
6204 3724 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
6205 : double dfDefaultValue,
6206 : OGRErr *pnErr) const
6207 :
6208 : {
6209 7448 : TAKE_OPTIONAL_LOCK();
6210 :
6211 3724 : GetNormInfo();
6212 :
6213 3724 : OGRErr nError = OGRERR_NONE;
6214 3724 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6215 3724 : if (pnErr != nullptr)
6216 0 : *pnErr = nError;
6217 :
6218 : // If we got the default just return it unadjusted.
6219 3724 : if (nError != OGRERR_NONE)
6220 526 : return dfRawResult;
6221 :
6222 3198 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6223 8 : dfRawResult *= d->dfToDegrees;
6224 :
6225 3198 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6226 5 : return dfRawResult * d->dfToMeter;
6227 :
6228 3193 : return dfRawResult;
6229 : }
6230 :
6231 : /************************************************************************/
6232 : /* OSRGetNormProjParm() */
6233 : /************************************************************************/
6234 :
6235 : /**
6236 : * \brief This function is the same as OGRSpatialReference::
6237 : *
6238 : * This function is the same as OGRSpatialReference::GetNormProjParm()
6239 : */
6240 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6241 : double dfDefaultValue, OGRErr *pnErr)
6242 :
6243 : {
6244 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6245 :
6246 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6247 : }
6248 :
6249 : /************************************************************************/
6250 : /* SetNormProjParm() */
6251 : /************************************************************************/
6252 :
6253 : /**
6254 : * \brief Set a projection parameter with a normalized value.
6255 : *
6256 : * This method is the same as SetProjParm() except that the value of
6257 : * the parameter passed in is assumed to be in "normalized" form (decimal
6258 : * degrees for angular values, meters for linear values. The values are
6259 : * converted in a form suitable for the GEOGCS and linear units in effect.
6260 : *
6261 : * This method is the same as the C function OSRSetNormProjParm().
6262 : *
6263 : * @param pszName the parameter name, which should be selected from
6264 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6265 : *
6266 : * @param dfValue value to assign.
6267 : *
6268 : * @return OGRERR_NONE on success.
6269 : */
6270 :
6271 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6272 :
6273 : {
6274 182 : TAKE_OPTIONAL_LOCK();
6275 :
6276 91 : GetNormInfo();
6277 :
6278 91 : if (d->dfToDegrees != 0.0 &&
6279 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6280 0 : IsAngularParameter(pszName))
6281 : {
6282 0 : dfValue /= d->dfToDegrees;
6283 : }
6284 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6285 4 : IsLinearParameter(pszName))
6286 4 : dfValue /= d->dfToMeter;
6287 :
6288 182 : return SetProjParm(pszName, dfValue);
6289 : }
6290 :
6291 : /************************************************************************/
6292 : /* OSRSetNormProjParm() */
6293 : /************************************************************************/
6294 :
6295 : /**
6296 : * \brief Set a projection parameter with a normalized value.
6297 : *
6298 : * This function is the same as OGRSpatialReference::SetNormProjParm()
6299 : */
6300 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6301 : double dfValue)
6302 :
6303 : {
6304 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6305 :
6306 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6307 : }
6308 :
6309 : /************************************************************************/
6310 : /* SetTM() */
6311 : /************************************************************************/
6312 :
6313 439 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6314 : double dfScale, double dfFalseEasting,
6315 : double dfFalseNorthing)
6316 :
6317 : {
6318 878 : TAKE_OPTIONAL_LOCK();
6319 :
6320 439 : return d->replaceConversionAndUnref(
6321 : proj_create_conversion_transverse_mercator(
6322 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6323 878 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6324 : }
6325 :
6326 : /************************************************************************/
6327 : /* OSRSetTM() */
6328 : /************************************************************************/
6329 :
6330 1 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6331 : double dfCenterLong, double dfScale, double dfFalseEasting,
6332 : double dfFalseNorthing)
6333 :
6334 : {
6335 1 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6336 :
6337 1 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6338 1 : dfFalseEasting, dfFalseNorthing);
6339 : }
6340 :
6341 : /************************************************************************/
6342 : /* SetTMVariant() */
6343 : /************************************************************************/
6344 :
6345 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6346 : double dfCenterLat,
6347 : double dfCenterLong, double dfScale,
6348 : double dfFalseEasting,
6349 : double dfFalseNorthing)
6350 :
6351 : {
6352 0 : TAKE_OPTIONAL_LOCK();
6353 :
6354 0 : SetProjection(pszVariantName);
6355 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6356 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6357 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6358 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6359 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6360 :
6361 0 : return OGRERR_NONE;
6362 : }
6363 :
6364 : /************************************************************************/
6365 : /* OSRSetTMVariant() */
6366 : /************************************************************************/
6367 :
6368 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6369 : double dfCenterLat, double dfCenterLong, double dfScale,
6370 : double dfFalseEasting, double dfFalseNorthing)
6371 :
6372 : {
6373 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6374 :
6375 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6376 : dfCenterLong, dfScale, dfFalseEasting,
6377 0 : dfFalseNorthing);
6378 : }
6379 :
6380 : /************************************************************************/
6381 : /* SetTMSO() */
6382 : /************************************************************************/
6383 :
6384 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6385 : double dfScale, double dfFalseEasting,
6386 : double dfFalseNorthing)
6387 :
6388 : {
6389 6 : TAKE_OPTIONAL_LOCK();
6390 :
6391 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6392 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6393 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6394 :
6395 3 : const char *pszName = nullptr;
6396 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6397 3 : CPLString osName = pszName ? pszName : "";
6398 :
6399 3 : d->refreshProjObj();
6400 :
6401 3 : d->demoteFromBoundCRS();
6402 :
6403 3 : auto cs = proj_create_cartesian_2D_cs(
6404 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6405 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6406 : auto projCRS =
6407 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6408 3 : d->getGeodBaseCRS(), conv, cs);
6409 3 : proj_destroy(conv);
6410 3 : proj_destroy(cs);
6411 :
6412 3 : d->setPjCRS(projCRS);
6413 :
6414 3 : d->undoDemoteFromBoundCRS();
6415 :
6416 6 : return OGRERR_NONE;
6417 : }
6418 :
6419 : /************************************************************************/
6420 : /* OSRSetTMSO() */
6421 : /************************************************************************/
6422 :
6423 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6424 : double dfCenterLong, double dfScale, double dfFalseEasting,
6425 : double dfFalseNorthing)
6426 :
6427 : {
6428 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6429 :
6430 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6431 0 : dfFalseEasting, dfFalseNorthing);
6432 : }
6433 :
6434 : /************************************************************************/
6435 : /* SetTPED() */
6436 : /************************************************************************/
6437 :
6438 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6439 : double dfLat2, double dfLong2,
6440 : double dfFalseEasting,
6441 : double dfFalseNorthing)
6442 :
6443 : {
6444 2 : TAKE_OPTIONAL_LOCK();
6445 :
6446 1 : return d->replaceConversionAndUnref(
6447 : proj_create_conversion_two_point_equidistant(
6448 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6449 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6450 : }
6451 :
6452 : /************************************************************************/
6453 : /* OSRSetTPED() */
6454 : /************************************************************************/
6455 :
6456 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6457 : double dfLat2, double dfLong2, double dfFalseEasting,
6458 : double dfFalseNorthing)
6459 :
6460 : {
6461 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6462 :
6463 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6464 0 : dfFalseEasting, dfFalseNorthing);
6465 : }
6466 :
6467 : /************************************************************************/
6468 : /* SetTMG() */
6469 : /************************************************************************/
6470 :
6471 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6472 : double dfFalseEasting,
6473 : double dfFalseNorthing)
6474 :
6475 : {
6476 0 : TAKE_OPTIONAL_LOCK();
6477 :
6478 0 : return d->replaceConversionAndUnref(
6479 : proj_create_conversion_tunisia_mapping_grid(
6480 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6481 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6482 : }
6483 :
6484 : /************************************************************************/
6485 : /* OSRSetTMG() */
6486 : /************************************************************************/
6487 :
6488 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6489 : double dfCenterLong, double dfFalseEasting,
6490 : double dfFalseNorthing)
6491 :
6492 : {
6493 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6494 :
6495 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6496 0 : dfFalseNorthing);
6497 : }
6498 :
6499 : /************************************************************************/
6500 : /* SetACEA() */
6501 : /************************************************************************/
6502 :
6503 40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6504 : double dfCenterLat, double dfCenterLong,
6505 : double dfFalseEasting,
6506 : double dfFalseNorthing)
6507 :
6508 : {
6509 80 : TAKE_OPTIONAL_LOCK();
6510 :
6511 : // Note different order of parameters. The one in PROJ is conformant with
6512 : // EPSG
6513 40 : return d->replaceConversionAndUnref(
6514 : proj_create_conversion_albers_equal_area(
6515 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6516 80 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6517 : }
6518 :
6519 : /************************************************************************/
6520 : /* OSRSetACEA() */
6521 : /************************************************************************/
6522 :
6523 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6524 : double dfCenterLat, double dfCenterLong,
6525 : double dfFalseEasting, double dfFalseNorthing)
6526 :
6527 : {
6528 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6529 :
6530 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6531 0 : dfFalseEasting, dfFalseNorthing);
6532 : }
6533 :
6534 : /************************************************************************/
6535 : /* SetAE() */
6536 : /************************************************************************/
6537 :
6538 21 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6539 : double dfFalseEasting, double dfFalseNorthing)
6540 :
6541 : {
6542 42 : TAKE_OPTIONAL_LOCK();
6543 :
6544 21 : return d->replaceConversionAndUnref(
6545 : proj_create_conversion_azimuthal_equidistant(
6546 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6547 42 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6548 : }
6549 :
6550 : /************************************************************************/
6551 : /* OSRSetAE() */
6552 : /************************************************************************/
6553 :
6554 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6555 : double dfCenterLong, double dfFalseEasting,
6556 : double dfFalseNorthing)
6557 :
6558 : {
6559 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6560 :
6561 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6562 0 : dfFalseNorthing);
6563 : }
6564 :
6565 : /************************************************************************/
6566 : /* SetBonne() */
6567 : /************************************************************************/
6568 :
6569 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6570 : double dfFalseEasting,
6571 : double dfFalseNorthing)
6572 :
6573 : {
6574 2 : TAKE_OPTIONAL_LOCK();
6575 :
6576 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6577 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6578 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6579 : }
6580 :
6581 : /************************************************************************/
6582 : /* OSRSetBonne() */
6583 : /************************************************************************/
6584 :
6585 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6586 : double dfCentralMeridian, double dfFalseEasting,
6587 : double dfFalseNorthing)
6588 :
6589 : {
6590 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6591 :
6592 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6593 0 : dfFalseNorthing);
6594 : }
6595 :
6596 : /************************************************************************/
6597 : /* SetCEA() */
6598 : /************************************************************************/
6599 :
6600 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6601 : double dfFalseEasting,
6602 : double dfFalseNorthing)
6603 :
6604 : {
6605 8 : TAKE_OPTIONAL_LOCK();
6606 :
6607 4 : return d->replaceConversionAndUnref(
6608 : proj_create_conversion_lambert_cylindrical_equal_area(
6609 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6610 8 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6611 : }
6612 :
6613 : /************************************************************************/
6614 : /* OSRSetCEA() */
6615 : /************************************************************************/
6616 :
6617 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6618 : double dfCentralMeridian, double dfFalseEasting,
6619 : double dfFalseNorthing)
6620 :
6621 : {
6622 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6623 :
6624 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6625 0 : dfFalseNorthing);
6626 : }
6627 :
6628 : /************************************************************************/
6629 : /* SetCS() */
6630 : /************************************************************************/
6631 :
6632 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6633 : double dfFalseEasting, double dfFalseNorthing)
6634 :
6635 : {
6636 10 : TAKE_OPTIONAL_LOCK();
6637 :
6638 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6639 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6640 10 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6641 : }
6642 :
6643 : /************************************************************************/
6644 : /* OSRSetCS() */
6645 : /************************************************************************/
6646 :
6647 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6648 : double dfCenterLong, double dfFalseEasting,
6649 : double dfFalseNorthing)
6650 :
6651 : {
6652 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6653 :
6654 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6655 0 : dfFalseNorthing);
6656 : }
6657 :
6658 : /************************************************************************/
6659 : /* SetEC() */
6660 : /************************************************************************/
6661 :
6662 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6663 : double dfCenterLat, double dfCenterLong,
6664 : double dfFalseEasting, double dfFalseNorthing)
6665 :
6666 : {
6667 14 : TAKE_OPTIONAL_LOCK();
6668 :
6669 : // Note: different order of arguments
6670 7 : return d->replaceConversionAndUnref(
6671 : proj_create_conversion_equidistant_conic(
6672 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6673 14 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6674 : }
6675 :
6676 : /************************************************************************/
6677 : /* OSRSetEC() */
6678 : /************************************************************************/
6679 :
6680 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6681 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6682 : double dfFalseNorthing)
6683 :
6684 : {
6685 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6686 :
6687 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6688 0 : dfFalseEasting, dfFalseNorthing);
6689 : }
6690 :
6691 : /************************************************************************/
6692 : /* SetEckert() */
6693 : /************************************************************************/
6694 :
6695 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6696 : double dfCentralMeridian,
6697 : double dfFalseEasting,
6698 : double dfFalseNorthing)
6699 :
6700 : {
6701 20 : TAKE_OPTIONAL_LOCK();
6702 :
6703 : PJ *conv;
6704 10 : if (nVariation == 1)
6705 : {
6706 1 : conv = proj_create_conversion_eckert_i(
6707 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6708 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6709 : }
6710 9 : else if (nVariation == 2)
6711 : {
6712 1 : conv = proj_create_conversion_eckert_ii(
6713 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6714 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6715 : }
6716 8 : else if (nVariation == 3)
6717 : {
6718 1 : conv = proj_create_conversion_eckert_iii(
6719 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6720 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6721 : }
6722 7 : else if (nVariation == 4)
6723 : {
6724 3 : conv = proj_create_conversion_eckert_iv(
6725 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6726 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6727 : }
6728 4 : else if (nVariation == 5)
6729 : {
6730 1 : conv = proj_create_conversion_eckert_v(
6731 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6732 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6733 : }
6734 3 : else if (nVariation == 6)
6735 : {
6736 3 : conv = proj_create_conversion_eckert_vi(
6737 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6738 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6739 : }
6740 : else
6741 : {
6742 0 : CPLError(CE_Failure, CPLE_AppDefined,
6743 : "Unsupported Eckert variation (%d).", nVariation);
6744 0 : return OGRERR_UNSUPPORTED_SRS;
6745 : }
6746 :
6747 10 : return d->replaceConversionAndUnref(conv);
6748 : }
6749 :
6750 : /************************************************************************/
6751 : /* OSRSetEckert() */
6752 : /************************************************************************/
6753 :
6754 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6755 : double dfCentralMeridian, double dfFalseEasting,
6756 : double dfFalseNorthing)
6757 :
6758 : {
6759 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6760 :
6761 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6762 0 : dfFalseEasting, dfFalseNorthing);
6763 : }
6764 :
6765 : /************************************************************************/
6766 : /* SetEckertIV() */
6767 : /* */
6768 : /* Deprecated */
6769 : /************************************************************************/
6770 :
6771 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6772 : double dfFalseEasting,
6773 : double dfFalseNorthing)
6774 :
6775 : {
6776 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6777 : }
6778 :
6779 : /************************************************************************/
6780 : /* OSRSetEckertIV() */
6781 : /************************************************************************/
6782 :
6783 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6784 : double dfFalseEasting, double dfFalseNorthing)
6785 :
6786 : {
6787 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6788 :
6789 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6790 0 : dfFalseNorthing);
6791 : }
6792 :
6793 : /************************************************************************/
6794 : /* SetEckertVI() */
6795 : /* */
6796 : /* Deprecated */
6797 : /************************************************************************/
6798 :
6799 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6800 : double dfFalseEasting,
6801 : double dfFalseNorthing)
6802 :
6803 : {
6804 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6805 : }
6806 :
6807 : /************************************************************************/
6808 : /* OSRSetEckertVI() */
6809 : /************************************************************************/
6810 :
6811 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6812 : double dfFalseEasting, double dfFalseNorthing)
6813 :
6814 : {
6815 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6816 :
6817 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6818 0 : dfFalseNorthing);
6819 : }
6820 :
6821 : /************************************************************************/
6822 : /* SetEquirectangular() */
6823 : /************************************************************************/
6824 :
6825 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6826 : double dfCenterLong,
6827 : double dfFalseEasting,
6828 : double dfFalseNorthing)
6829 :
6830 : {
6831 4 : TAKE_OPTIONAL_LOCK();
6832 :
6833 2 : if (dfCenterLat == 0.0)
6834 : {
6835 0 : return d->replaceConversionAndUnref(
6836 : proj_create_conversion_equidistant_cylindrical(
6837 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6838 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6839 : }
6840 :
6841 : // Non-standard extension with non-zero latitude of origin
6842 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6843 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6844 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6845 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6846 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6847 :
6848 2 : return OGRERR_NONE;
6849 : }
6850 :
6851 : /************************************************************************/
6852 : /* OSRSetEquirectangular() */
6853 : /************************************************************************/
6854 :
6855 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6856 : double dfCenterLong, double dfFalseEasting,
6857 : double dfFalseNorthing)
6858 :
6859 : {
6860 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6861 :
6862 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6863 0 : dfFalseEasting, dfFalseNorthing);
6864 : }
6865 :
6866 : /************************************************************************/
6867 : /* SetEquirectangular2() */
6868 : /* Generalized form */
6869 : /************************************************************************/
6870 :
6871 179 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6872 : double dfCenterLong,
6873 : double dfStdParallel1,
6874 : double dfFalseEasting,
6875 : double dfFalseNorthing)
6876 :
6877 : {
6878 358 : TAKE_OPTIONAL_LOCK();
6879 :
6880 179 : if (dfCenterLat == 0.0)
6881 : {
6882 174 : return d->replaceConversionAndUnref(
6883 : proj_create_conversion_equidistant_cylindrical(
6884 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6885 174 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6886 : }
6887 :
6888 : // Non-standard extension with non-zero latitude of origin
6889 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6890 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6891 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6892 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6893 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6894 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6895 :
6896 5 : return OGRERR_NONE;
6897 : }
6898 :
6899 : /************************************************************************/
6900 : /* OSRSetEquirectangular2() */
6901 : /************************************************************************/
6902 :
6903 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6904 : double dfCenterLong, double dfStdParallel1,
6905 : double dfFalseEasting, double dfFalseNorthing)
6906 :
6907 : {
6908 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6909 :
6910 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6911 : dfStdParallel1, dfFalseEasting,
6912 3 : dfFalseNorthing);
6913 : }
6914 :
6915 : /************************************************************************/
6916 : /* SetGS() */
6917 : /************************************************************************/
6918 :
6919 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6920 : double dfFalseEasting, double dfFalseNorthing)
6921 :
6922 : {
6923 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6924 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6925 5 : nullptr, 0.0, nullptr, 0.0));
6926 : }
6927 :
6928 : /************************************************************************/
6929 : /* OSRSetGS() */
6930 : /************************************************************************/
6931 :
6932 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6933 : double dfFalseEasting, double dfFalseNorthing)
6934 :
6935 : {
6936 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6937 :
6938 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6939 2 : dfFalseNorthing);
6940 : }
6941 :
6942 : /************************************************************************/
6943 : /* SetGH() */
6944 : /************************************************************************/
6945 :
6946 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6947 : double dfFalseEasting, double dfFalseNorthing)
6948 :
6949 : {
6950 0 : TAKE_OPTIONAL_LOCK();
6951 :
6952 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6953 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6954 0 : nullptr, 0.0, nullptr, 0.0));
6955 : }
6956 :
6957 : /************************************************************************/
6958 : /* OSRSetGH() */
6959 : /************************************************************************/
6960 :
6961 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6962 : double dfFalseEasting, double dfFalseNorthing)
6963 :
6964 : {
6965 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6966 :
6967 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6968 0 : dfFalseNorthing);
6969 : }
6970 :
6971 : /************************************************************************/
6972 : /* SetIGH() */
6973 : /************************************************************************/
6974 :
6975 0 : OGRErr OGRSpatialReference::SetIGH()
6976 :
6977 : {
6978 0 : TAKE_OPTIONAL_LOCK();
6979 :
6980 0 : return d->replaceConversionAndUnref(
6981 : proj_create_conversion_interrupted_goode_homolosine(
6982 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6983 : }
6984 :
6985 : /************************************************************************/
6986 : /* OSRSetIGH() */
6987 : /************************************************************************/
6988 :
6989 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6990 :
6991 : {
6992 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6993 :
6994 0 : return ToPointer(hSRS)->SetIGH();
6995 : }
6996 :
6997 : /************************************************************************/
6998 : /* SetGEOS() */
6999 : /************************************************************************/
7000 :
7001 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7002 : double dfSatelliteHeight,
7003 : double dfFalseEasting,
7004 : double dfFalseNorthing)
7005 :
7006 : {
7007 6 : TAKE_OPTIONAL_LOCK();
7008 :
7009 3 : return d->replaceConversionAndUnref(
7010 : proj_create_conversion_geostationary_satellite_sweep_y(
7011 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7012 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7013 : }
7014 :
7015 : /************************************************************************/
7016 : /* OSRSetGEOS() */
7017 : /************************************************************************/
7018 :
7019 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7020 : double dfSatelliteHeight, double dfFalseEasting,
7021 : double dfFalseNorthing)
7022 :
7023 : {
7024 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7025 :
7026 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7027 0 : dfFalseEasting, dfFalseNorthing);
7028 : }
7029 :
7030 : /************************************************************************/
7031 : /* SetGaussSchreiberTMercator() */
7032 : /************************************************************************/
7033 :
7034 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7035 : double dfCenterLong,
7036 : double dfScale,
7037 : double dfFalseEasting,
7038 : double dfFalseNorthing)
7039 :
7040 : {
7041 0 : TAKE_OPTIONAL_LOCK();
7042 :
7043 0 : return d->replaceConversionAndUnref(
7044 : proj_create_conversion_gauss_schreiber_transverse_mercator(
7045 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7046 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7047 : }
7048 :
7049 : /************************************************************************/
7050 : /* OSRSetGaussSchreiberTMercator() */
7051 : /************************************************************************/
7052 :
7053 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7054 : double dfCenterLat, double dfCenterLong,
7055 : double dfScale, double dfFalseEasting,
7056 : double dfFalseNorthing)
7057 :
7058 : {
7059 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7060 :
7061 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7062 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7063 : }
7064 :
7065 : /************************************************************************/
7066 : /* SetGnomonic() */
7067 : /************************************************************************/
7068 :
7069 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7070 : double dfFalseEasting,
7071 : double dfFalseNorthing)
7072 :
7073 : {
7074 4 : TAKE_OPTIONAL_LOCK();
7075 :
7076 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7077 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7078 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7079 : }
7080 :
7081 : /************************************************************************/
7082 : /* OSRSetGnomonic() */
7083 : /************************************************************************/
7084 :
7085 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7086 : double dfCenterLong, double dfFalseEasting,
7087 : double dfFalseNorthing)
7088 :
7089 : {
7090 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7091 :
7092 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7093 0 : dfFalseEasting, dfFalseNorthing);
7094 : }
7095 :
7096 : /************************************************************************/
7097 : /* SetHOMAC() */
7098 : /************************************************************************/
7099 :
7100 : /**
7101 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7102 : * azimuth angle.
7103 : *
7104 : * This projection corresponds to EPSG projection method 9815, also
7105 : * sometimes known as hotine oblique mercator (variant B).
7106 : *
7107 : * This method does the same thing as the C function OSRSetHOMAC().
7108 : *
7109 : * @param dfCenterLat Latitude of the projection origin.
7110 : * @param dfCenterLong Longitude of the projection origin.
7111 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7112 : * centerline.
7113 : * @param dfRectToSkew Angle from Rectified to Skew Grid
7114 : * @param dfScale Scale factor applies to the projection origin.
7115 : * @param dfFalseEasting False easting.
7116 : * @param dfFalseNorthing False northing.
7117 : *
7118 : * @return OGRERR_NONE on success.
7119 : */
7120 :
7121 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7122 : double dfAzimuth, double dfRectToSkew,
7123 : double dfScale, double dfFalseEasting,
7124 : double dfFalseNorthing)
7125 :
7126 : {
7127 8 : TAKE_OPTIONAL_LOCK();
7128 :
7129 4 : return d->replaceConversionAndUnref(
7130 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7131 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7132 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7133 8 : 0.0, nullptr, 0.0));
7134 : }
7135 :
7136 : /************************************************************************/
7137 : /* OSRSetHOMAC() */
7138 : /************************************************************************/
7139 :
7140 : /**
7141 : * \brief Set an Oblique Mercator projection using azimuth angle.
7142 : *
7143 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7144 : */
7145 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7146 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7147 : double dfScale, double dfFalseEasting,
7148 : double dfFalseNorthing)
7149 :
7150 : {
7151 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7152 :
7153 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7154 : dfRectToSkew, dfScale, dfFalseEasting,
7155 0 : dfFalseNorthing);
7156 : }
7157 :
7158 : /************************************************************************/
7159 : /* SetHOM() */
7160 : /************************************************************************/
7161 :
7162 : /**
7163 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7164 : *
7165 : * This projection corresponds to EPSG projection method 9812, also
7166 : * sometimes known as hotine oblique mercator (variant A)..
7167 : *
7168 : * This method does the same thing as the C function OSRSetHOM().
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 dfRectToSkew Angle from Rectified to Skew Grid
7175 : * @param dfScale Scale factor applies to the projection origin.
7176 : * @param dfFalseEasting False easting.
7177 : * @param dfFalseNorthing False northing.
7178 : *
7179 : * @return OGRERR_NONE on success.
7180 : */
7181 :
7182 13 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7183 : double dfAzimuth, double dfRectToSkew,
7184 : double dfScale, double dfFalseEasting,
7185 : double dfFalseNorthing)
7186 :
7187 : {
7188 26 : TAKE_OPTIONAL_LOCK();
7189 :
7190 13 : return d->replaceConversionAndUnref(
7191 : proj_create_conversion_hotine_oblique_mercator_variant_a(
7192 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7193 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7194 26 : 0.0, nullptr, 0.0));
7195 : }
7196 :
7197 : /************************************************************************/
7198 : /* OSRSetHOM() */
7199 : /************************************************************************/
7200 : /**
7201 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7202 : *
7203 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
7204 : */
7205 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7206 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7207 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
7208 :
7209 : {
7210 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7211 :
7212 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7213 : dfRectToSkew, dfScale, dfFalseEasting,
7214 0 : dfFalseNorthing);
7215 : }
7216 :
7217 : /************************************************************************/
7218 : /* SetHOM2PNO() */
7219 : /************************************************************************/
7220 :
7221 : /**
7222 : * \brief Set a Hotine Oblique Mercator projection using two points on
7223 : * projection centerline.
7224 : *
7225 : * This method does the same thing as the C function OSRSetHOM2PNO().
7226 : *
7227 : * @param dfCenterLat Latitude of the projection origin.
7228 : * @param dfLat1 Latitude of the first point on center line.
7229 : * @param dfLong1 Longitude of the first point on center line.
7230 : * @param dfLat2 Latitude of the second point on center line.
7231 : * @param dfLong2 Longitude of the second point on center line.
7232 : * @param dfScale Scale factor applies to the projection origin.
7233 : * @param dfFalseEasting False easting.
7234 : * @param dfFalseNorthing False northing.
7235 : *
7236 : * @return OGRERR_NONE on success.
7237 : */
7238 :
7239 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7240 : double dfLong1, double dfLat2,
7241 : double dfLong2, double dfScale,
7242 : double dfFalseEasting,
7243 : double dfFalseNorthing)
7244 :
7245 : {
7246 6 : TAKE_OPTIONAL_LOCK();
7247 :
7248 3 : return d->replaceConversionAndUnref(
7249 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7250 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7251 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7252 6 : 0.0));
7253 : }
7254 :
7255 : /************************************************************************/
7256 : /* OSRSetHOM2PNO() */
7257 : /************************************************************************/
7258 : /**
7259 : * \brief Set a Hotine Oblique Mercator projection using two points on
7260 : * projection centerline.
7261 : *
7262 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7263 : */
7264 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7265 : double dfLat1, double dfLong1, double dfLat2,
7266 : double dfLong2, double dfScale, double dfFalseEasting,
7267 : double dfFalseNorthing)
7268 :
7269 : {
7270 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7271 :
7272 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7273 : dfLong2, dfScale, dfFalseEasting,
7274 0 : dfFalseNorthing);
7275 : }
7276 :
7277 : /************************************************************************/
7278 : /* SetLOM() */
7279 : /************************************************************************/
7280 :
7281 : /**
7282 : * \brief Set a Laborde Oblique Mercator projection.
7283 : *
7284 : * @param dfCenterLat Latitude of the projection origin.
7285 : * @param dfCenterLong Longitude of the projection origin.
7286 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7287 : * centerline.
7288 : * @param dfScale Scale factor on the initiali line
7289 : * @param dfFalseEasting False easting.
7290 : * @param dfFalseNorthing False northing.
7291 : *
7292 : * @return OGRERR_NONE on success.
7293 : */
7294 :
7295 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7296 : double dfAzimuth, double dfScale,
7297 : double dfFalseEasting,
7298 : double dfFalseNorthing)
7299 :
7300 : {
7301 0 : TAKE_OPTIONAL_LOCK();
7302 :
7303 0 : return d->replaceConversionAndUnref(
7304 : proj_create_conversion_laborde_oblique_mercator(
7305 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7306 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7307 : }
7308 :
7309 : /************************************************************************/
7310 : /* SetIWMPolyconic() */
7311 : /************************************************************************/
7312 :
7313 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7314 : double dfCenterLong,
7315 : double dfFalseEasting,
7316 : double dfFalseNorthing)
7317 :
7318 : {
7319 0 : TAKE_OPTIONAL_LOCK();
7320 :
7321 0 : return d->replaceConversionAndUnref(
7322 : proj_create_conversion_international_map_world_polyconic(
7323 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7324 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7325 : }
7326 :
7327 : /************************************************************************/
7328 : /* OSRSetIWMPolyconic() */
7329 : /************************************************************************/
7330 :
7331 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7332 : double dfLat2, double dfCenterLong,
7333 : double dfFalseEasting, double dfFalseNorthing)
7334 :
7335 : {
7336 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7337 :
7338 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7339 0 : dfFalseEasting, dfFalseNorthing);
7340 : }
7341 :
7342 : /************************************************************************/
7343 : /* SetKrovak() */
7344 : /************************************************************************/
7345 :
7346 : /** Krovak east-north projection.
7347 : *
7348 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7349 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7350 : */
7351 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7352 : double dfAzimuth,
7353 : double dfPseudoStdParallel1,
7354 : double dfScale, double dfFalseEasting,
7355 : double dfFalseNorthing)
7356 :
7357 : {
7358 6 : TAKE_OPTIONAL_LOCK();
7359 :
7360 3 : return d->replaceConversionAndUnref(
7361 : proj_create_conversion_krovak_north_oriented(
7362 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7363 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7364 6 : nullptr, 0.0, nullptr, 0.0));
7365 : }
7366 :
7367 : /************************************************************************/
7368 : /* OSRSetKrovak() */
7369 : /************************************************************************/
7370 :
7371 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7372 : double dfCenterLong, double dfAzimuth,
7373 : double dfPseudoStdParallel1, double dfScale,
7374 : double dfFalseEasting, double dfFalseNorthing)
7375 :
7376 : {
7377 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7378 :
7379 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7380 : dfPseudoStdParallel1, dfScale,
7381 0 : dfFalseEasting, dfFalseNorthing);
7382 : }
7383 :
7384 : /************************************************************************/
7385 : /* SetLAEA() */
7386 : /************************************************************************/
7387 :
7388 17 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7389 : double dfFalseEasting,
7390 : double dfFalseNorthing)
7391 :
7392 : {
7393 34 : TAKE_OPTIONAL_LOCK();
7394 :
7395 17 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7396 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7397 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7398 :
7399 17 : const char *pszName = nullptr;
7400 17 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7401 17 : CPLString osName = pszName ? pszName : "";
7402 :
7403 17 : d->refreshProjObj();
7404 :
7405 17 : d->demoteFromBoundCRS();
7406 :
7407 17 : auto cs = proj_create_cartesian_2D_cs(
7408 : d->getPROJContext(),
7409 17 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7410 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7411 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7412 14 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7413 : : PJ_CART2D_EASTING_NORTHING,
7414 17 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7415 : auto projCRS =
7416 17 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7417 17 : d->getGeodBaseCRS(), conv, cs);
7418 17 : proj_destroy(conv);
7419 17 : proj_destroy(cs);
7420 :
7421 17 : d->setPjCRS(projCRS);
7422 :
7423 17 : d->undoDemoteFromBoundCRS();
7424 :
7425 34 : return OGRERR_NONE;
7426 : }
7427 :
7428 : /************************************************************************/
7429 : /* OSRSetLAEA() */
7430 : /************************************************************************/
7431 :
7432 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7433 : double dfCenterLong, double dfFalseEasting,
7434 : double dfFalseNorthing)
7435 :
7436 : {
7437 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7438 :
7439 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7440 0 : dfFalseNorthing);
7441 : }
7442 :
7443 : /************************************************************************/
7444 : /* SetLCC() */
7445 : /************************************************************************/
7446 :
7447 148 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7448 : double dfCenterLat, double dfCenterLong,
7449 : double dfFalseEasting,
7450 : double dfFalseNorthing)
7451 :
7452 : {
7453 296 : TAKE_OPTIONAL_LOCK();
7454 :
7455 148 : return d->replaceConversionAndUnref(
7456 : proj_create_conversion_lambert_conic_conformal_2sp(
7457 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7458 296 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7459 : }
7460 :
7461 : /************************************************************************/
7462 : /* OSRSetLCC() */
7463 : /************************************************************************/
7464 :
7465 1 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7466 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7467 : double dfFalseNorthing)
7468 :
7469 : {
7470 1 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7471 :
7472 1 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7473 1 : dfFalseEasting, dfFalseNorthing);
7474 : }
7475 :
7476 : /************************************************************************/
7477 : /* SetLCC1SP() */
7478 : /************************************************************************/
7479 :
7480 10 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7481 : double dfScale, double dfFalseEasting,
7482 : double dfFalseNorthing)
7483 :
7484 : {
7485 20 : TAKE_OPTIONAL_LOCK();
7486 :
7487 10 : return d->replaceConversionAndUnref(
7488 : proj_create_conversion_lambert_conic_conformal_1sp(
7489 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7490 20 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7491 : }
7492 :
7493 : /************************************************************************/
7494 : /* OSRSetLCC1SP() */
7495 : /************************************************************************/
7496 :
7497 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7498 : double dfCenterLong, double dfScale, double dfFalseEasting,
7499 : double dfFalseNorthing)
7500 :
7501 : {
7502 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7503 :
7504 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7505 0 : dfFalseEasting, dfFalseNorthing);
7506 : }
7507 :
7508 : /************************************************************************/
7509 : /* SetLCCB() */
7510 : /************************************************************************/
7511 :
7512 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7513 : double dfCenterLat, double dfCenterLong,
7514 : double dfFalseEasting,
7515 : double dfFalseNorthing)
7516 :
7517 : {
7518 4 : TAKE_OPTIONAL_LOCK();
7519 :
7520 2 : return d->replaceConversionAndUnref(
7521 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7522 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7523 4 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7524 : }
7525 :
7526 : /************************************************************************/
7527 : /* OSRSetLCCB() */
7528 : /************************************************************************/
7529 :
7530 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7531 : double dfCenterLat, double dfCenterLong,
7532 : double dfFalseEasting, double dfFalseNorthing)
7533 :
7534 : {
7535 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7536 :
7537 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7538 0 : dfFalseEasting, dfFalseNorthing);
7539 : }
7540 :
7541 : /************************************************************************/
7542 : /* SetMC() */
7543 : /************************************************************************/
7544 :
7545 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7546 : double dfFalseEasting, double dfFalseNorthing)
7547 :
7548 : {
7549 8 : TAKE_OPTIONAL_LOCK();
7550 :
7551 : (void)dfCenterLat; // ignored
7552 :
7553 4 : return d->replaceConversionAndUnref(
7554 : proj_create_conversion_miller_cylindrical(
7555 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7556 8 : nullptr, 0, nullptr, 0));
7557 : }
7558 :
7559 : /************************************************************************/
7560 : /* OSRSetMC() */
7561 : /************************************************************************/
7562 :
7563 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7564 : double dfCenterLong, double dfFalseEasting,
7565 : double dfFalseNorthing)
7566 :
7567 : {
7568 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7569 :
7570 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7571 0 : dfFalseNorthing);
7572 : }
7573 :
7574 : /************************************************************************/
7575 : /* SetMercator() */
7576 : /************************************************************************/
7577 :
7578 59 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7579 : double dfScale, double dfFalseEasting,
7580 : double dfFalseNorthing)
7581 :
7582 : {
7583 118 : TAKE_OPTIONAL_LOCK();
7584 :
7585 59 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7586 : {
7587 : // Not sure this is correct, but this is how it has been used
7588 : // historically
7589 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7590 0 : dfFalseNorthing);
7591 : }
7592 59 : return d->replaceConversionAndUnref(
7593 : proj_create_conversion_mercator_variant_a(
7594 : d->getPROJContext(),
7595 : dfCenterLat, // should be zero
7596 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7597 59 : nullptr, 0));
7598 : }
7599 :
7600 : /************************************************************************/
7601 : /* OSRSetMercator() */
7602 : /************************************************************************/
7603 :
7604 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7605 : double dfCenterLong, double dfScale,
7606 : double dfFalseEasting, double dfFalseNorthing)
7607 :
7608 : {
7609 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7610 :
7611 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7612 2 : dfFalseEasting, dfFalseNorthing);
7613 : }
7614 :
7615 : /************************************************************************/
7616 : /* SetMercator2SP() */
7617 : /************************************************************************/
7618 :
7619 31 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7620 : double dfCenterLong,
7621 : double dfFalseEasting,
7622 : double dfFalseNorthing)
7623 :
7624 : {
7625 31 : if (dfCenterLat == 0.0)
7626 : {
7627 30 : return d->replaceConversionAndUnref(
7628 : proj_create_conversion_mercator_variant_b(
7629 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7630 30 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7631 : }
7632 :
7633 1 : TAKE_OPTIONAL_LOCK();
7634 :
7635 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7636 :
7637 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7638 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7639 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7640 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7641 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7642 :
7643 1 : return OGRERR_NONE;
7644 : }
7645 :
7646 : /************************************************************************/
7647 : /* OSRSetMercator2SP() */
7648 : /************************************************************************/
7649 :
7650 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7651 : double dfCenterLat, double dfCenterLong,
7652 : double dfFalseEasting, double dfFalseNorthing)
7653 :
7654 : {
7655 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7656 :
7657 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7658 1 : dfFalseEasting, dfFalseNorthing);
7659 : }
7660 :
7661 : /************************************************************************/
7662 : /* SetMollweide() */
7663 : /************************************************************************/
7664 :
7665 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7666 : double dfFalseEasting,
7667 : double dfFalseNorthing)
7668 :
7669 : {
7670 6 : TAKE_OPTIONAL_LOCK();
7671 :
7672 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7673 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7674 6 : nullptr, 0, nullptr, 0));
7675 : }
7676 :
7677 : /************************************************************************/
7678 : /* OSRSetMollweide() */
7679 : /************************************************************************/
7680 :
7681 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7682 : double dfFalseEasting, double dfFalseNorthing)
7683 :
7684 : {
7685 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7686 :
7687 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7688 0 : dfFalseNorthing);
7689 : }
7690 :
7691 : /************************************************************************/
7692 : /* SetNZMG() */
7693 : /************************************************************************/
7694 :
7695 7 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7696 : double dfFalseEasting,
7697 : double dfFalseNorthing)
7698 :
7699 : {
7700 14 : TAKE_OPTIONAL_LOCK();
7701 :
7702 7 : return d->replaceConversionAndUnref(
7703 : proj_create_conversion_new_zealand_mapping_grid(
7704 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7705 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7706 : }
7707 :
7708 : /************************************************************************/
7709 : /* OSRSetNZMG() */
7710 : /************************************************************************/
7711 :
7712 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7713 : double dfCenterLong, double dfFalseEasting,
7714 : double dfFalseNorthing)
7715 :
7716 : {
7717 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7718 :
7719 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7720 0 : dfFalseNorthing);
7721 : }
7722 :
7723 : /************************************************************************/
7724 : /* SetOS() */
7725 : /************************************************************************/
7726 :
7727 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7728 : double dfScale, double dfFalseEasting,
7729 : double dfFalseNorthing)
7730 :
7731 : {
7732 12 : TAKE_OPTIONAL_LOCK();
7733 :
7734 6 : return d->replaceConversionAndUnref(
7735 : proj_create_conversion_oblique_stereographic(
7736 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7737 12 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7738 : }
7739 :
7740 : /************************************************************************/
7741 : /* OSRSetOS() */
7742 : /************************************************************************/
7743 :
7744 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7745 : double dfCMeridian, double dfScale, double dfFalseEasting,
7746 : double dfFalseNorthing)
7747 :
7748 : {
7749 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7750 :
7751 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7752 0 : dfFalseEasting, dfFalseNorthing);
7753 : }
7754 :
7755 : /************************************************************************/
7756 : /* SetOrthographic() */
7757 : /************************************************************************/
7758 :
7759 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7760 : double dfCenterLong,
7761 : double dfFalseEasting,
7762 : double dfFalseNorthing)
7763 :
7764 : {
7765 14 : TAKE_OPTIONAL_LOCK();
7766 :
7767 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7768 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7769 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7770 : }
7771 :
7772 : /************************************************************************/
7773 : /* OSRSetOrthographic() */
7774 : /************************************************************************/
7775 :
7776 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7777 : double dfCenterLong, double dfFalseEasting,
7778 : double dfFalseNorthing)
7779 :
7780 : {
7781 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7782 :
7783 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7784 1 : dfFalseEasting, dfFalseNorthing);
7785 : }
7786 :
7787 : /************************************************************************/
7788 : /* SetPolyconic() */
7789 : /************************************************************************/
7790 :
7791 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7792 : double dfCenterLong,
7793 : double dfFalseEasting,
7794 : double dfFalseNorthing)
7795 :
7796 : {
7797 14 : TAKE_OPTIONAL_LOCK();
7798 :
7799 : // note: it seems that by some definitions this should include a
7800 : // scale_factor parameter.
7801 7 : return d->replaceConversionAndUnref(
7802 : proj_create_conversion_american_polyconic(
7803 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7804 14 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7805 : }
7806 :
7807 : /************************************************************************/
7808 : /* OSRSetPolyconic() */
7809 : /************************************************************************/
7810 :
7811 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7812 : double dfCenterLong, double dfFalseEasting,
7813 : double dfFalseNorthing)
7814 :
7815 : {
7816 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7817 :
7818 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7819 0 : dfFalseEasting, dfFalseNorthing);
7820 : }
7821 :
7822 : /************************************************************************/
7823 : /* SetPS() */
7824 : /************************************************************************/
7825 :
7826 : /** Sets a Polar Stereographic projection.
7827 : *
7828 : * Two variants are possible:
7829 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7830 : * interpreted as the latitude of origin, combined with the scale factor
7831 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7832 : * is interpreted as the latitude of true scale. In that situation, dfScale
7833 : * must be set to 1 (it is ignored in the projection parameters)
7834 : */
7835 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7836 : double dfScale, double dfFalseEasting,
7837 : double dfFalseNorthing)
7838 :
7839 : {
7840 60 : TAKE_OPTIONAL_LOCK();
7841 :
7842 : PJ *conv;
7843 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7844 : {
7845 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7846 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7847 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7848 : }
7849 : else
7850 : {
7851 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7852 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7853 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7854 : }
7855 :
7856 30 : const char *pszName = nullptr;
7857 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7858 30 : CPLString osName = pszName ? pszName : "";
7859 :
7860 30 : d->refreshProjObj();
7861 :
7862 30 : d->demoteFromBoundCRS();
7863 :
7864 30 : auto cs = proj_create_cartesian_2D_cs(
7865 : d->getPROJContext(),
7866 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7867 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7868 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7869 : auto projCRS =
7870 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7871 30 : d->getGeodBaseCRS(), conv, cs);
7872 30 : proj_destroy(conv);
7873 30 : proj_destroy(cs);
7874 :
7875 30 : d->setPjCRS(projCRS);
7876 :
7877 30 : d->undoDemoteFromBoundCRS();
7878 :
7879 60 : return OGRERR_NONE;
7880 : }
7881 :
7882 : /************************************************************************/
7883 : /* OSRSetPS() */
7884 : /************************************************************************/
7885 :
7886 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7887 : double dfCenterLong, double dfScale, double dfFalseEasting,
7888 : double dfFalseNorthing)
7889 :
7890 : {
7891 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7892 :
7893 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7894 1 : dfFalseEasting, dfFalseNorthing);
7895 : }
7896 :
7897 : /************************************************************************/
7898 : /* SetRobinson() */
7899 : /************************************************************************/
7900 :
7901 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7902 : double dfFalseEasting,
7903 : double dfFalseNorthing)
7904 :
7905 : {
7906 8 : TAKE_OPTIONAL_LOCK();
7907 :
7908 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7909 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7910 8 : nullptr, 0, nullptr, 0));
7911 : }
7912 :
7913 : /************************************************************************/
7914 : /* OSRSetRobinson() */
7915 : /************************************************************************/
7916 :
7917 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7918 : double dfFalseEasting, double dfFalseNorthing)
7919 :
7920 : {
7921 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7922 :
7923 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7924 0 : dfFalseNorthing);
7925 : }
7926 :
7927 : /************************************************************************/
7928 : /* SetSinusoidal() */
7929 : /************************************************************************/
7930 :
7931 35 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7932 : double dfFalseEasting,
7933 : double dfFalseNorthing)
7934 :
7935 : {
7936 70 : TAKE_OPTIONAL_LOCK();
7937 :
7938 35 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7939 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7940 70 : nullptr, 0, nullptr, 0));
7941 : }
7942 :
7943 : /************************************************************************/
7944 : /* OSRSetSinusoidal() */
7945 : /************************************************************************/
7946 :
7947 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7948 : double dfFalseEasting, double dfFalseNorthing)
7949 :
7950 : {
7951 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7952 :
7953 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7954 1 : dfFalseNorthing);
7955 : }
7956 :
7957 : /************************************************************************/
7958 : /* SetStereographic() */
7959 : /************************************************************************/
7960 :
7961 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7962 : double dfCMeridian, double dfScale,
7963 : double dfFalseEasting,
7964 : double dfFalseNorthing)
7965 :
7966 : {
7967 4 : TAKE_OPTIONAL_LOCK();
7968 :
7969 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7970 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7971 4 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7972 : }
7973 :
7974 : /************************************************************************/
7975 : /* OSRSetStereographic() */
7976 : /************************************************************************/
7977 :
7978 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7979 : double dfCMeridian, double dfScale,
7980 : double dfFalseEasting, double dfFalseNorthing)
7981 :
7982 : {
7983 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7984 :
7985 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7986 0 : dfFalseEasting, dfFalseNorthing);
7987 : }
7988 :
7989 : /************************************************************************/
7990 : /* SetSOC() */
7991 : /* */
7992 : /* NOTE: This definition isn't really used in practice any more */
7993 : /* and should be considered deprecated. It seems that swiss */
7994 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
7995 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7996 : /* EPSG:2056 and Bug 423. */
7997 : /************************************************************************/
7998 :
7999 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8000 : double dfCentralMeridian,
8001 : double dfFalseEasting,
8002 : double dfFalseNorthing)
8003 :
8004 : {
8005 4 : TAKE_OPTIONAL_LOCK();
8006 :
8007 2 : return d->replaceConversionAndUnref(
8008 : proj_create_conversion_hotine_oblique_mercator_variant_b(
8009 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8010 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8011 4 : 0.0));
8012 : #if 0
8013 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8014 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8015 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8016 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8017 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8018 :
8019 : return OGRERR_NONE;
8020 : #endif
8021 : }
8022 :
8023 : /************************************************************************/
8024 : /* OSRSetSOC() */
8025 : /************************************************************************/
8026 :
8027 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8028 : double dfCentralMeridian, double dfFalseEasting,
8029 : double dfFalseNorthing)
8030 :
8031 : {
8032 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8033 :
8034 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8035 0 : dfFalseEasting, dfFalseNorthing);
8036 : }
8037 :
8038 : /************************************************************************/
8039 : /* SetVDG() */
8040 : /************************************************************************/
8041 :
8042 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8043 : double dfFalseNorthing)
8044 :
8045 : {
8046 4 : TAKE_OPTIONAL_LOCK();
8047 :
8048 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8049 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8050 4 : nullptr, 0, nullptr, 0));
8051 : }
8052 :
8053 : /************************************************************************/
8054 : /* OSRSetVDG() */
8055 : /************************************************************************/
8056 :
8057 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8058 : double dfFalseEasting, double dfFalseNorthing)
8059 :
8060 : {
8061 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8062 :
8063 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8064 0 : dfFalseNorthing);
8065 : }
8066 :
8067 : /************************************************************************/
8068 : /* SetUTM() */
8069 : /************************************************************************/
8070 :
8071 : /**
8072 : * \brief Set UTM projection definition.
8073 : *
8074 : * This will generate a projection definition with the full set of
8075 : * transverse mercator projection parameters for the given UTM zone.
8076 : * If no PROJCS[] description is set yet, one will be set to look
8077 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8078 : *
8079 : * This method is the same as the C function OSRSetUTM().
8080 : *
8081 : * @param nZone UTM zone.
8082 : *
8083 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8084 : * hemisphere.
8085 : *
8086 : * @return OGRERR_NONE on success.
8087 : */
8088 :
8089 318 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8090 :
8091 : {
8092 636 : TAKE_OPTIONAL_LOCK();
8093 :
8094 318 : if (nZone < 0 || nZone > 60)
8095 : {
8096 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8097 0 : return OGRERR_FAILURE;
8098 : }
8099 :
8100 318 : return d->replaceConversionAndUnref(
8101 318 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8102 : }
8103 :
8104 : /************************************************************************/
8105 : /* OSRSetUTM() */
8106 : /************************************************************************/
8107 :
8108 : /**
8109 : * \brief Set UTM projection definition.
8110 : *
8111 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
8112 : */
8113 19 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8114 :
8115 : {
8116 19 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8117 :
8118 19 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8119 : }
8120 :
8121 : /************************************************************************/
8122 : /* GetUTMZone() */
8123 : /* */
8124 : /* Returns zero if it isn't UTM. */
8125 : /************************************************************************/
8126 :
8127 : /**
8128 : * \brief Get utm zone information.
8129 : *
8130 : * This is the same as the C function OSRGetUTMZone().
8131 : *
8132 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8133 : * zone which is negative in the southern hemisphere instead of having the
8134 : * pbNorth flag used in the C and C++ interface.
8135 : *
8136 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8137 : * FALSE if southern.
8138 : *
8139 : * @return UTM zone number or zero if this isn't a UTM definition.
8140 : */
8141 :
8142 604 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8143 :
8144 : {
8145 1208 : TAKE_OPTIONAL_LOCK();
8146 :
8147 604 : if (IsProjected() && GetAxesCount() == 3)
8148 : {
8149 1 : OGRSpatialReference *poSRSTmp = Clone();
8150 1 : poSRSTmp->DemoteTo2D(nullptr);
8151 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8152 1 : delete poSRSTmp;
8153 1 : return nZone;
8154 : }
8155 :
8156 603 : const char *pszProjection = GetAttrValue("PROJECTION");
8157 :
8158 603 : if (pszProjection == nullptr ||
8159 523 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8160 274 : return 0;
8161 :
8162 329 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8163 5 : return 0;
8164 :
8165 324 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8166 15 : return 0;
8167 :
8168 309 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8169 6 : return 0;
8170 :
8171 303 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8172 :
8173 303 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8174 0 : return 0;
8175 :
8176 303 : if (pbNorth != nullptr)
8177 238 : *pbNorth = (dfFalseNorthing == 0);
8178 :
8179 : const double dfCentralMeridian =
8180 303 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8181 303 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8182 :
8183 606 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8184 909 : std::isnan(dfZone) ||
8185 303 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8186 0 : return 0;
8187 :
8188 303 : return static_cast<int>(dfZone);
8189 : }
8190 :
8191 : /************************************************************************/
8192 : /* OSRGetUTMZone() */
8193 : /************************************************************************/
8194 :
8195 : /**
8196 : * \brief Get utm zone information.
8197 : *
8198 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8199 : */
8200 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8201 :
8202 : {
8203 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8204 :
8205 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
8206 : }
8207 :
8208 : /************************************************************************/
8209 : /* SetWagner() */
8210 : /************************************************************************/
8211 :
8212 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
8213 : double dfCenterLat, double dfFalseEasting,
8214 : double dfFalseNorthing)
8215 :
8216 : {
8217 0 : TAKE_OPTIONAL_LOCK();
8218 :
8219 : PJ *conv;
8220 0 : if (nVariation == 1)
8221 : {
8222 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8223 : dfFalseEasting, dfFalseNorthing,
8224 : nullptr, 0.0, nullptr, 0.0);
8225 : }
8226 0 : else if (nVariation == 2)
8227 : {
8228 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8229 : dfFalseEasting, dfFalseNorthing,
8230 : nullptr, 0.0, nullptr, 0.0);
8231 : }
8232 0 : else if (nVariation == 3)
8233 : {
8234 0 : conv = proj_create_conversion_wagner_iii(
8235 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8236 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8237 : }
8238 0 : else if (nVariation == 4)
8239 : {
8240 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8241 : dfFalseEasting, dfFalseNorthing,
8242 : nullptr, 0.0, nullptr, 0.0);
8243 : }
8244 0 : else if (nVariation == 5)
8245 : {
8246 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8247 : dfFalseEasting, dfFalseNorthing,
8248 : nullptr, 0.0, nullptr, 0.0);
8249 : }
8250 0 : else if (nVariation == 6)
8251 : {
8252 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8253 : dfFalseEasting, dfFalseNorthing,
8254 : nullptr, 0.0, nullptr, 0.0);
8255 : }
8256 0 : else if (nVariation == 7)
8257 : {
8258 0 : conv = proj_create_conversion_wagner_vii(
8259 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8260 : 0.0, nullptr, 0.0);
8261 : }
8262 : else
8263 : {
8264 0 : CPLError(CE_Failure, CPLE_AppDefined,
8265 : "Unsupported Wagner variation (%d).", nVariation);
8266 0 : return OGRERR_UNSUPPORTED_SRS;
8267 : }
8268 :
8269 0 : return d->replaceConversionAndUnref(conv);
8270 : }
8271 :
8272 : /************************************************************************/
8273 : /* OSRSetWagner() */
8274 : /************************************************************************/
8275 :
8276 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8277 : double dfCenterLat, double dfFalseEasting,
8278 : double dfFalseNorthing)
8279 :
8280 : {
8281 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8282 :
8283 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8284 0 : dfFalseNorthing);
8285 : }
8286 :
8287 : /************************************************************************/
8288 : /* SetQSC() */
8289 : /************************************************************************/
8290 :
8291 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8292 : {
8293 0 : TAKE_OPTIONAL_LOCK();
8294 :
8295 0 : return d->replaceConversionAndUnref(
8296 : proj_create_conversion_quadrilateralized_spherical_cube(
8297 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8298 0 : 0, nullptr, 0));
8299 : }
8300 :
8301 : /************************************************************************/
8302 : /* OSRSetQSC() */
8303 : /************************************************************************/
8304 :
8305 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8306 : double dfCenterLong)
8307 :
8308 : {
8309 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8310 :
8311 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8312 : }
8313 :
8314 : /************************************************************************/
8315 : /* SetSCH() */
8316 : /************************************************************************/
8317 :
8318 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8319 : double dfPegHeading, double dfPegHgt)
8320 :
8321 : {
8322 0 : TAKE_OPTIONAL_LOCK();
8323 :
8324 0 : return d->replaceConversionAndUnref(
8325 : proj_create_conversion_spherical_cross_track_height(
8326 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8327 0 : nullptr, 0, nullptr, 0));
8328 : }
8329 :
8330 : /************************************************************************/
8331 : /* OSRSetSCH() */
8332 : /************************************************************************/
8333 :
8334 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8335 : double dfPegHeading, double dfPegHgt)
8336 :
8337 : {
8338 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8339 :
8340 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8341 : }
8342 :
8343 : /************************************************************************/
8344 : /* SetVerticalPerspective() */
8345 : /************************************************************************/
8346 :
8347 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
8348 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8349 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8350 : {
8351 6 : TAKE_OPTIONAL_LOCK();
8352 :
8353 3 : return d->replaceConversionAndUnref(
8354 : proj_create_conversion_vertical_perspective(
8355 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8356 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8357 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
8358 : }
8359 :
8360 : /************************************************************************/
8361 : /* OSRSetVerticalPerspective() */
8362 : /************************************************************************/
8363 :
8364 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8365 : double dfTopoOriginLat, double dfTopoOriginLon,
8366 : double dfTopoOriginHeight,
8367 : double dfViewPointHeight,
8368 : double dfFalseEasting, double dfFalseNorthing)
8369 :
8370 : {
8371 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8372 :
8373 1 : return ToPointer(hSRS)->SetVerticalPerspective(
8374 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8375 1 : dfFalseEasting, dfFalseNorthing);
8376 : }
8377 :
8378 : /************************************************************************/
8379 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
8380 : /************************************************************************/
8381 :
8382 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8383 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8384 : double dfAxisRotation)
8385 : {
8386 4 : TAKE_OPTIONAL_LOCK();
8387 :
8388 2 : d->refreshProjObj();
8389 2 : if (!d->m_pj_crs)
8390 0 : return OGRERR_FAILURE;
8391 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8392 0 : return OGRERR_FAILURE;
8393 2 : auto ctxt = d->getPROJContext();
8394 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
8395 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8396 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8397 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8398 2 : d->m_pj_crs, conv, cs));
8399 2 : proj_destroy(conv);
8400 2 : proj_destroy(cs);
8401 2 : return OGRERR_NONE;
8402 : }
8403 :
8404 : /************************************************************************/
8405 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
8406 : /************************************************************************/
8407 :
8408 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8409 : const char *pszCRSName, double dfGridNorthPoleLat,
8410 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8411 : {
8412 3 : TAKE_OPTIONAL_LOCK();
8413 :
8414 : #if PROJ_VERSION_MAJOR > 8 || \
8415 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8416 : d->refreshProjObj();
8417 : if (!d->m_pj_crs)
8418 : return OGRERR_FAILURE;
8419 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8420 : return OGRERR_FAILURE;
8421 : auto ctxt = d->getPROJContext();
8422 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8423 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8424 : nullptr, 0);
8425 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8426 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8427 : d->m_pj_crs, conv, cs));
8428 : proj_destroy(conv);
8429 : proj_destroy(cs);
8430 : return OGRERR_NONE;
8431 : #else
8432 : (void)pszCRSName;
8433 3 : SetProjection("Rotated_pole");
8434 3 : SetExtension(
8435 : "PROJCS", "PROJ4",
8436 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8437 : "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8438 : "+to_meter=0.0174532925199433 "
8439 : "+wktext",
8440 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8441 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
8442 : GetSemiMinor(nullptr)));
8443 6 : return OGRERR_NONE;
8444 : #endif
8445 : }
8446 :
8447 : /************************************************************************/
8448 : /* SetAuthority() */
8449 : /************************************************************************/
8450 :
8451 : /**
8452 : * \brief Set the authority for a node.
8453 : *
8454 : * This method is the same as the C function OSRSetAuthority().
8455 : *
8456 : * @param pszTargetKey the partial or complete path to the node to
8457 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8458 : *
8459 : * @param pszAuthority authority name, such as "EPSG".
8460 : *
8461 : * @param nCode code for value with this authority.
8462 : *
8463 : * @return OGRERR_NONE on success.
8464 : */
8465 :
8466 11760 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8467 : const char *pszAuthority, int nCode)
8468 :
8469 : {
8470 23520 : TAKE_OPTIONAL_LOCK();
8471 :
8472 11760 : d->refreshProjObj();
8473 11760 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8474 :
8475 11760 : if (pszTargetKey == nullptr)
8476 : {
8477 263 : if (!d->m_pj_crs)
8478 0 : return OGRERR_FAILURE;
8479 263 : CPLString osCode;
8480 263 : osCode.Printf("%d", nCode);
8481 263 : d->demoteFromBoundCRS();
8482 263 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8483 : pszAuthority, osCode.c_str()));
8484 263 : d->undoDemoteFromBoundCRS();
8485 263 : return OGRERR_NONE;
8486 : }
8487 :
8488 11497 : d->demoteFromBoundCRS();
8489 11497 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8490 : {
8491 3800 : CPLString osCode;
8492 3800 : osCode.Printf("%d", nCode);
8493 : auto newGeogCRS =
8494 3800 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8495 : pszAuthority, osCode.c_str());
8496 :
8497 : auto conv =
8498 3800 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8499 :
8500 3800 : auto projCRS = proj_create_projected_crs(
8501 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8502 3800 : d->getProjCRSCoordSys());
8503 :
8504 : // Preserve existing id on the PROJCRS
8505 3800 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8506 3800 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8507 3800 : if (pszProjCRSAuthName && pszProjCRSCode)
8508 : {
8509 : auto projCRSWithId =
8510 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8511 : pszProjCRSCode);
8512 0 : proj_destroy(projCRS);
8513 0 : projCRS = projCRSWithId;
8514 : }
8515 :
8516 3800 : proj_destroy(newGeogCRS);
8517 3800 : proj_destroy(conv);
8518 :
8519 3800 : d->setPjCRS(projCRS);
8520 3800 : d->undoDemoteFromBoundCRS();
8521 3800 : return OGRERR_NONE;
8522 : }
8523 7697 : d->undoDemoteFromBoundCRS();
8524 :
8525 : /* -------------------------------------------------------------------- */
8526 : /* Find the node below which the authority should be put. */
8527 : /* -------------------------------------------------------------------- */
8528 7697 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8529 :
8530 7697 : if (poNode == nullptr)
8531 0 : return OGRERR_FAILURE;
8532 :
8533 : /* -------------------------------------------------------------------- */
8534 : /* If there is an existing AUTHORITY child blow it away before */
8535 : /* trying to set a new one. */
8536 : /* -------------------------------------------------------------------- */
8537 7697 : int iOldChild = poNode->FindChild("AUTHORITY");
8538 7697 : if (iOldChild != -1)
8539 5 : poNode->DestroyChild(iOldChild);
8540 :
8541 : /* -------------------------------------------------------------------- */
8542 : /* Create a new authority node. */
8543 : /* -------------------------------------------------------------------- */
8544 7697 : char szCode[32] = {};
8545 :
8546 7697 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8547 :
8548 7697 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8549 7697 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8550 7697 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8551 :
8552 7697 : poNode->AddChild(poAuthNode);
8553 :
8554 7697 : return OGRERR_NONE;
8555 : }
8556 :
8557 : /************************************************************************/
8558 : /* OSRSetAuthority() */
8559 : /************************************************************************/
8560 :
8561 : /**
8562 : * \brief Set the authority for a node.
8563 : *
8564 : * This function is the same as OGRSpatialReference::SetAuthority().
8565 : */
8566 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8567 : const char *pszAuthority, int nCode)
8568 :
8569 : {
8570 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8571 :
8572 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8573 : }
8574 :
8575 : /************************************************************************/
8576 : /* GetAuthorityCode() */
8577 : /************************************************************************/
8578 :
8579 : /**
8580 : * \brief Get the authority code for a node.
8581 : *
8582 : * This method is used to query an AUTHORITY[] node from within the
8583 : * WKT tree, and fetch the code value.
8584 : *
8585 : * While in theory values may be non-numeric, for the EPSG authority all
8586 : * code values should be integral.
8587 : *
8588 : * This method is the same as the C function OSRGetAuthorityCode().
8589 : *
8590 : * @param pszTargetKey the partial or complete path to the node to
8591 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8592 : * search for an authority node on the root element.
8593 : *
8594 : * @return value code from authority node, or NULL on failure. The value
8595 : * returned is internal and should not be freed or modified.
8596 : */
8597 :
8598 : const char *
8599 44595 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8600 :
8601 : {
8602 89190 : TAKE_OPTIONAL_LOCK();
8603 :
8604 44595 : d->refreshProjObj();
8605 44595 : const char *pszInputTargetKey = pszTargetKey;
8606 44595 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8607 44595 : if (pszTargetKey == nullptr)
8608 : {
8609 36761 : if (!d->m_pj_crs)
8610 : {
8611 17 : return nullptr;
8612 : }
8613 36744 : d->demoteFromBoundCRS();
8614 36744 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8615 36744 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8616 : {
8617 1192 : auto ctxt = d->getPROJContext();
8618 1192 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8619 1192 : if (cs)
8620 : {
8621 1192 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8622 1192 : proj_destroy(cs);
8623 1192 : if (axisCount == 3)
8624 : {
8625 : // This might come from a COMPD_CS with a VERT_DATUM type =
8626 : // 2002 in which case, using the WKT1 representation will
8627 : // enable us to recover the EPSG code.
8628 14 : pszTargetKey = pszInputTargetKey;
8629 : }
8630 : }
8631 : }
8632 36744 : d->undoDemoteFromBoundCRS();
8633 36744 : if (ret != nullptr || pszTargetKey == nullptr)
8634 : {
8635 36744 : return ret;
8636 : }
8637 : }
8638 :
8639 : // Special key for that context
8640 7838 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8641 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8642 : {
8643 4 : auto ctxt = d->getPROJContext();
8644 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8645 4 : if (crs)
8646 : {
8647 4 : const char *ret = proj_get_id_code(crs, 0);
8648 4 : if (ret)
8649 4 : ret = CPLSPrintf("%s", ret);
8650 4 : proj_destroy(crs);
8651 4 : return ret;
8652 : }
8653 : }
8654 7834 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8655 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8656 : {
8657 4 : auto ctxt = d->getPROJContext();
8658 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8659 4 : if (crs)
8660 : {
8661 4 : const char *ret = proj_get_id_code(crs, 0);
8662 4 : if (ret)
8663 4 : ret = CPLSPrintf("%s", ret);
8664 4 : proj_destroy(crs);
8665 4 : return ret;
8666 : }
8667 : }
8668 :
8669 : /* -------------------------------------------------------------------- */
8670 : /* Find the node below which the authority should be put. */
8671 : /* -------------------------------------------------------------------- */
8672 7826 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8673 :
8674 7826 : if (poNode == nullptr)
8675 103 : return nullptr;
8676 :
8677 : /* -------------------------------------------------------------------- */
8678 : /* Fetch AUTHORITY child if there is one. */
8679 : /* -------------------------------------------------------------------- */
8680 7723 : if (poNode->FindChild("AUTHORITY") == -1)
8681 183 : return nullptr;
8682 :
8683 7540 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8684 :
8685 : /* -------------------------------------------------------------------- */
8686 : /* Create a new authority node. */
8687 : /* -------------------------------------------------------------------- */
8688 7540 : if (poNode->GetChildCount() < 2)
8689 0 : return nullptr;
8690 :
8691 7540 : return poNode->GetChild(1)->GetValue();
8692 : }
8693 :
8694 : /************************************************************************/
8695 : /* OSRGetAuthorityCode() */
8696 : /************************************************************************/
8697 :
8698 : /**
8699 : * \brief Get the authority code for a node.
8700 : *
8701 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8702 : */
8703 712 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8704 : const char *pszTargetKey)
8705 :
8706 : {
8707 712 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8708 :
8709 712 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8710 : }
8711 :
8712 : /************************************************************************/
8713 : /* GetAuthorityName() */
8714 : /************************************************************************/
8715 :
8716 : /**
8717 : * \brief Get the authority name for a node.
8718 : *
8719 : * This method is used to query an AUTHORITY[] node from within the
8720 : * WKT tree, and fetch the authority name value.
8721 : *
8722 : * The most common authority is "EPSG".
8723 : *
8724 : * This method is the same as the C function OSRGetAuthorityName().
8725 : *
8726 : * @param pszTargetKey the partial or complete path to the node to
8727 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8728 : * search for an authority node on the root element.
8729 : *
8730 : * @return value code from authority node, or NULL on failure. The value
8731 : * returned is internal and should not be freed or modified.
8732 : */
8733 :
8734 : const char *
8735 57745 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8736 :
8737 : {
8738 115490 : TAKE_OPTIONAL_LOCK();
8739 :
8740 57745 : d->refreshProjObj();
8741 57745 : const char *pszInputTargetKey = pszTargetKey;
8742 57745 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8743 57745 : if (pszTargetKey == nullptr)
8744 : {
8745 30416 : if (!d->m_pj_crs)
8746 : {
8747 18 : return nullptr;
8748 : }
8749 30398 : d->demoteFromBoundCRS();
8750 30398 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8751 30398 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8752 : {
8753 814 : auto ctxt = d->getPROJContext();
8754 814 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8755 814 : if (cs)
8756 : {
8757 814 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8758 814 : proj_destroy(cs);
8759 814 : if (axisCount == 3)
8760 : {
8761 : // This might come from a COMPD_CS with a VERT_DATUM type =
8762 : // 2002 in which case, using the WKT1 representation will
8763 : // enable us to recover the EPSG code.
8764 14 : pszTargetKey = pszInputTargetKey;
8765 : }
8766 : }
8767 : }
8768 30398 : d->undoDemoteFromBoundCRS();
8769 30398 : if (ret != nullptr || pszTargetKey == nullptr)
8770 : {
8771 30398 : return ret;
8772 : }
8773 : }
8774 :
8775 : // Special key for that context
8776 27333 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8777 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8778 : {
8779 4 : auto ctxt = d->getPROJContext();
8780 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8781 4 : if (crs)
8782 : {
8783 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8784 4 : if (ret)
8785 4 : ret = CPLSPrintf("%s", ret);
8786 4 : proj_destroy(crs);
8787 4 : return ret;
8788 : }
8789 : }
8790 27329 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8791 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8792 : {
8793 4 : auto ctxt = d->getPROJContext();
8794 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8795 4 : if (crs)
8796 : {
8797 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8798 4 : if (ret)
8799 4 : ret = CPLSPrintf("%s", ret);
8800 4 : proj_destroy(crs);
8801 4 : return ret;
8802 : }
8803 : }
8804 :
8805 : /* -------------------------------------------------------------------- */
8806 : /* Find the node below which the authority should be put. */
8807 : /* -------------------------------------------------------------------- */
8808 27321 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8809 :
8810 27321 : if (poNode == nullptr)
8811 11358 : return nullptr;
8812 :
8813 : /* -------------------------------------------------------------------- */
8814 : /* Fetch AUTHORITY child if there is one. */
8815 : /* -------------------------------------------------------------------- */
8816 15963 : if (poNode->FindChild("AUTHORITY") == -1)
8817 1448 : return nullptr;
8818 :
8819 14515 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8820 :
8821 : /* -------------------------------------------------------------------- */
8822 : /* Create a new authority node. */
8823 : /* -------------------------------------------------------------------- */
8824 14515 : if (poNode->GetChildCount() < 2)
8825 0 : return nullptr;
8826 :
8827 14515 : return poNode->GetChild(0)->GetValue();
8828 : }
8829 :
8830 : /************************************************************************/
8831 : /* OSRGetAuthorityName() */
8832 : /************************************************************************/
8833 :
8834 : /**
8835 : * \brief Get the authority name for a node.
8836 : *
8837 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8838 : */
8839 180 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8840 : const char *pszTargetKey)
8841 :
8842 : {
8843 180 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8844 :
8845 180 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8846 : }
8847 :
8848 : /************************************************************************/
8849 : /* GetOGCURN() */
8850 : /************************************************************************/
8851 :
8852 : /**
8853 : * \brief Get a OGC URN string describing the CRS, when possible
8854 : *
8855 : * This method assumes that the CRS has a top-level identifier, or is
8856 : * a compound CRS whose horizontal and vertical parts have a top-level
8857 : * identifier.
8858 : *
8859 : * @return a string to free with CPLFree(), or nullptr when no result can be
8860 : * generated
8861 : *
8862 : * @since GDAL 3.5
8863 : */
8864 :
8865 61 : char *OGRSpatialReference::GetOGCURN() const
8866 :
8867 : {
8868 122 : TAKE_OPTIONAL_LOCK();
8869 :
8870 61 : const char *pszAuthName = GetAuthorityName(nullptr);
8871 61 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8872 61 : if (pszAuthName && pszAuthCode)
8873 58 : return CPLStrdup(
8874 58 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8875 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8876 2 : return nullptr;
8877 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8878 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8879 1 : char *pszRet = nullptr;
8880 1 : if (horizCRS && vertCRS)
8881 : {
8882 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8883 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8884 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8885 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8886 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8887 : {
8888 1 : pszRet = CPLStrdup(CPLSPrintf(
8889 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8890 : horizAuthCode, vertAuthName, vertAuthCode));
8891 : }
8892 : }
8893 1 : proj_destroy(horizCRS);
8894 1 : proj_destroy(vertCRS);
8895 1 : return pszRet;
8896 : }
8897 :
8898 : /************************************************************************/
8899 : /* StripVertical() */
8900 : /************************************************************************/
8901 :
8902 : /**
8903 : * \brief Convert a compound cs into a horizontal CS.
8904 : *
8905 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8906 : * nodes are stripped resulting and only the horizontal coordinate system
8907 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8908 : *
8909 : * If this is not a compound coordinate system then nothing is changed.
8910 : *
8911 : * This method is the same as the C function OSRStripVertical().
8912 : *
8913 : * @since OGR 1.8.0
8914 : */
8915 :
8916 44 : OGRErr OGRSpatialReference::StripVertical()
8917 :
8918 : {
8919 88 : TAKE_OPTIONAL_LOCK();
8920 :
8921 44 : d->refreshProjObj();
8922 44 : d->demoteFromBoundCRS();
8923 44 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8924 : {
8925 0 : d->undoDemoteFromBoundCRS();
8926 0 : return OGRERR_NONE;
8927 : }
8928 44 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8929 44 : if (!horizCRS)
8930 : {
8931 0 : d->undoDemoteFromBoundCRS();
8932 0 : return OGRERR_FAILURE;
8933 : }
8934 :
8935 44 : bool reuseExistingBoundCRS = false;
8936 44 : if (d->m_pj_bound_crs_target)
8937 : {
8938 4 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8939 8 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8940 8 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8941 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8942 : }
8943 :
8944 44 : if (reuseExistingBoundCRS)
8945 : {
8946 4 : auto newBoundCRS = proj_crs_create_bound_crs(
8947 4 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8948 4 : d->m_pj_bound_crs_co);
8949 4 : proj_destroy(horizCRS);
8950 4 : d->undoDemoteFromBoundCRS();
8951 4 : d->setPjCRS(newBoundCRS);
8952 : }
8953 : else
8954 : {
8955 40 : d->undoDemoteFromBoundCRS();
8956 40 : d->setPjCRS(horizCRS);
8957 : }
8958 :
8959 44 : return OGRERR_NONE;
8960 : }
8961 :
8962 : /************************************************************************/
8963 : /* OSRStripVertical() */
8964 : /************************************************************************/
8965 : /**
8966 : * \brief Convert a compound cs into a horizontal CS.
8967 : *
8968 : * This function is the same as the C++ method
8969 : * OGRSpatialReference::StripVertical().
8970 : */
8971 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8972 :
8973 : {
8974 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8975 :
8976 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8977 : }
8978 :
8979 : /************************************************************************/
8980 : /* StripTOWGS84IfKnownDatumAndAllowed() */
8981 : /************************************************************************/
8982 :
8983 : /**
8984 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8985 : * and this is allowed by the user.
8986 : *
8987 : * The default behavior is to remove TOWGS84 information if the CRS has a
8988 : * known horizontal datum. This can be disabled by setting the
8989 : * OSR_STRIP_TOWGS84 configuration option to NO.
8990 : *
8991 : * @return true if TOWGS84 has been removed.
8992 : * @since OGR 3.1.0
8993 : */
8994 :
8995 8919 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8996 : {
8997 8919 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8998 : {
8999 8916 : if (StripTOWGS84IfKnownDatum())
9000 : {
9001 72 : CPLDebug("OSR", "TOWGS84 information has been removed. "
9002 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
9003 : "configuration option to NO");
9004 72 : return true;
9005 : }
9006 : }
9007 8847 : return false;
9008 : }
9009 :
9010 : /************************************************************************/
9011 : /* StripTOWGS84IfKnownDatum() */
9012 : /************************************************************************/
9013 :
9014 : /**
9015 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9016 : *
9017 : * @return true if TOWGS84 has been removed.
9018 : * @since OGR 3.1.0
9019 : */
9020 :
9021 8922 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9022 :
9023 : {
9024 17844 : TAKE_OPTIONAL_LOCK();
9025 :
9026 8922 : d->refreshProjObj();
9027 8922 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9028 : {
9029 8830 : return false;
9030 : }
9031 92 : auto ctxt = d->getPROJContext();
9032 92 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9033 92 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9034 : {
9035 3 : proj_destroy(baseCRS);
9036 3 : return false;
9037 : }
9038 :
9039 : // Known base CRS code ? Return base CRS
9040 89 : const char *pszCode = proj_get_id_code(baseCRS, 0);
9041 89 : if (pszCode)
9042 : {
9043 2 : d->setPjCRS(baseCRS);
9044 2 : return true;
9045 : }
9046 :
9047 87 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
9048 : #if PROJ_VERSION_MAJOR > 7 || \
9049 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9050 : if (datum == nullptr)
9051 : {
9052 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9053 : }
9054 : #endif
9055 87 : if (!datum)
9056 : {
9057 0 : proj_destroy(baseCRS);
9058 0 : return false;
9059 : }
9060 :
9061 : // Known datum code ? Return base CRS
9062 87 : pszCode = proj_get_id_code(datum, 0);
9063 87 : if (pszCode)
9064 : {
9065 3 : proj_destroy(datum);
9066 3 : d->setPjCRS(baseCRS);
9067 3 : return true;
9068 : }
9069 :
9070 84 : const char *name = proj_get_name(datum);
9071 84 : if (EQUAL(name, "unknown"))
9072 : {
9073 1 : proj_destroy(datum);
9074 1 : proj_destroy(baseCRS);
9075 1 : return false;
9076 : }
9077 83 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9078 : PJ_OBJ_LIST *list =
9079 83 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9080 :
9081 83 : bool knownDatumName = false;
9082 83 : if (list)
9083 : {
9084 83 : if (proj_list_get_count(list) == 1)
9085 : {
9086 70 : knownDatumName = true;
9087 : }
9088 83 : proj_list_destroy(list);
9089 : }
9090 :
9091 83 : proj_destroy(datum);
9092 83 : if (knownDatumName)
9093 : {
9094 70 : d->setPjCRS(baseCRS);
9095 70 : return true;
9096 : }
9097 13 : proj_destroy(baseCRS);
9098 13 : return false;
9099 : }
9100 :
9101 : /************************************************************************/
9102 : /* IsCompound() */
9103 : /************************************************************************/
9104 :
9105 : /**
9106 : * \brief Check if coordinate system is compound.
9107 : *
9108 : * This method is the same as the C function OSRIsCompound().
9109 : *
9110 : * @return TRUE if this is rooted with a COMPD_CS node.
9111 : */
9112 :
9113 39848 : int OGRSpatialReference::IsCompound() const
9114 :
9115 : {
9116 39848 : TAKE_OPTIONAL_LOCK();
9117 :
9118 39848 : d->refreshProjObj();
9119 39848 : d->demoteFromBoundCRS();
9120 39848 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9121 39848 : d->undoDemoteFromBoundCRS();
9122 79696 : return isCompound;
9123 : }
9124 :
9125 : /************************************************************************/
9126 : /* OSRIsCompound() */
9127 : /************************************************************************/
9128 :
9129 : /**
9130 : * \brief Check if the coordinate system is compound.
9131 : *
9132 : * This function is the same as OGRSpatialReference::IsCompound().
9133 : */
9134 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
9135 :
9136 : {
9137 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9138 :
9139 5 : return ToPointer(hSRS)->IsCompound();
9140 : }
9141 :
9142 : /************************************************************************/
9143 : /* IsProjected() */
9144 : /************************************************************************/
9145 :
9146 : /**
9147 : * \brief Check if projected coordinate system.
9148 : *
9149 : * This method is the same as the C function OSRIsProjected().
9150 : *
9151 : * @return TRUE if this contains a PROJCS node indicating a it is a
9152 : * projected coordinate system. Also if it is a CompoundCRS made of a
9153 : * ProjectedCRS
9154 : */
9155 :
9156 41422 : int OGRSpatialReference::IsProjected() const
9157 :
9158 : {
9159 41422 : TAKE_OPTIONAL_LOCK();
9160 :
9161 41422 : d->refreshProjObj();
9162 41422 : d->demoteFromBoundCRS();
9163 41422 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9164 41422 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9165 : {
9166 : auto horizCRS =
9167 142 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9168 142 : if (horizCRS)
9169 : {
9170 142 : auto horizCRSType = proj_get_type(horizCRS);
9171 142 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9172 142 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9173 : {
9174 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9175 6 : if (base)
9176 : {
9177 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9178 6 : proj_destroy(base);
9179 : }
9180 : }
9181 142 : proj_destroy(horizCRS);
9182 : }
9183 : }
9184 41422 : d->undoDemoteFromBoundCRS();
9185 82844 : return isProjected;
9186 : }
9187 :
9188 : /************************************************************************/
9189 : /* OSRIsProjected() */
9190 : /************************************************************************/
9191 : /**
9192 : * \brief Check if projected coordinate system.
9193 : *
9194 : * This function is the same as OGRSpatialReference::IsProjected().
9195 : */
9196 413 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
9197 :
9198 : {
9199 413 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9200 :
9201 413 : return ToPointer(hSRS)->IsProjected();
9202 : }
9203 :
9204 : /************************************************************************/
9205 : /* IsGeocentric() */
9206 : /************************************************************************/
9207 :
9208 : /**
9209 : * \brief Check if geocentric coordinate system.
9210 : *
9211 : * This method is the same as the C function OSRIsGeocentric().
9212 : *
9213 : * @return TRUE if this contains a GEOCCS node indicating a it is a
9214 : * geocentric coordinate system.
9215 : *
9216 : * @since OGR 1.9.0
9217 : */
9218 :
9219 16821 : int OGRSpatialReference::IsGeocentric() const
9220 :
9221 : {
9222 16821 : TAKE_OPTIONAL_LOCK();
9223 :
9224 16821 : d->refreshProjObj();
9225 16821 : d->demoteFromBoundCRS();
9226 16821 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9227 16821 : d->undoDemoteFromBoundCRS();
9228 33642 : return isGeocentric;
9229 : }
9230 :
9231 : /************************************************************************/
9232 : /* OSRIsGeocentric() */
9233 : /************************************************************************/
9234 : /**
9235 : * \brief Check if geocentric coordinate system.
9236 : *
9237 : * This function is the same as OGRSpatialReference::IsGeocentric().
9238 : *
9239 : * @since OGR 1.9.0
9240 : */
9241 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9242 :
9243 : {
9244 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9245 :
9246 2 : return ToPointer(hSRS)->IsGeocentric();
9247 : }
9248 :
9249 : /************************************************************************/
9250 : /* IsEmpty() */
9251 : /************************************************************************/
9252 :
9253 : /**
9254 : * \brief Return if the SRS is not set.
9255 : */
9256 :
9257 111760 : bool OGRSpatialReference::IsEmpty() const
9258 : {
9259 111760 : TAKE_OPTIONAL_LOCK();
9260 :
9261 111745 : d->refreshProjObj();
9262 223497 : return d->m_pj_crs == nullptr;
9263 : }
9264 :
9265 : /************************************************************************/
9266 : /* IsGeographic() */
9267 : /************************************************************************/
9268 :
9269 : /**
9270 : * \brief Check if geographic coordinate system.
9271 : *
9272 : * This method is the same as the C function OSRIsGeographic().
9273 : *
9274 : * @return TRUE if this spatial reference is geographic ... that is the
9275 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9276 : * GeographicCRS
9277 : */
9278 :
9279 57091 : int OGRSpatialReference::IsGeographic() const
9280 :
9281 : {
9282 57091 : TAKE_OPTIONAL_LOCK();
9283 :
9284 57091 : d->refreshProjObj();
9285 57091 : d->demoteFromBoundCRS();
9286 80318 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9287 23227 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9288 57091 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9289 : {
9290 : auto horizCRS =
9291 291 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9292 291 : if (horizCRS)
9293 : {
9294 291 : auto horizCRSType = proj_get_type(horizCRS);
9295 291 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9296 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9297 291 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
9298 : {
9299 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9300 13 : if (base)
9301 : {
9302 13 : horizCRSType = proj_get_type(base);
9303 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9304 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9305 13 : proj_destroy(base);
9306 : }
9307 : }
9308 291 : proj_destroy(horizCRS);
9309 : }
9310 : }
9311 57091 : d->undoDemoteFromBoundCRS();
9312 114182 : return isGeog;
9313 : }
9314 :
9315 : /************************************************************************/
9316 : /* OSRIsGeographic() */
9317 : /************************************************************************/
9318 : /**
9319 : * \brief Check if geographic coordinate system.
9320 : *
9321 : * This function is the same as OGRSpatialReference::IsGeographic().
9322 : */
9323 304 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9324 :
9325 : {
9326 304 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9327 :
9328 304 : return ToPointer(hSRS)->IsGeographic();
9329 : }
9330 :
9331 : /************************************************************************/
9332 : /* IsDerivedGeographic() */
9333 : /************************************************************************/
9334 :
9335 : /**
9336 : * \brief Check if the CRS is a derived geographic coordinate system.
9337 : * (for example a rotated long/lat grid)
9338 : *
9339 : * This method is the same as the C function OSRIsDerivedGeographic().
9340 : *
9341 : * @since GDAL 3.1.0 and PROJ 6.3.0
9342 : */
9343 :
9344 14774 : int OGRSpatialReference::IsDerivedGeographic() const
9345 :
9346 : {
9347 14774 : TAKE_OPTIONAL_LOCK();
9348 :
9349 14774 : d->refreshProjObj();
9350 14774 : d->demoteFromBoundCRS();
9351 24128 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9352 9354 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9353 : const bool isDerivedGeographic =
9354 14774 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9355 14774 : d->undoDemoteFromBoundCRS();
9356 29548 : return isDerivedGeographic ? TRUE : FALSE;
9357 : }
9358 :
9359 : /************************************************************************/
9360 : /* OSRIsDerivedGeographic() */
9361 : /************************************************************************/
9362 : /**
9363 : * \brief Check if the CRS is a derived geographic coordinate system.
9364 : * (for example a rotated long/lat grid)
9365 : *
9366 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9367 : */
9368 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9369 :
9370 : {
9371 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9372 :
9373 1 : return ToPointer(hSRS)->IsDerivedGeographic();
9374 : }
9375 :
9376 : /************************************************************************/
9377 : /* IsDerivedProjected() */
9378 : /************************************************************************/
9379 :
9380 : /**
9381 : * \brief Check if the CRS is a derived projected coordinate system.
9382 : *
9383 : * This method is the same as the C function OSRIsDerivedGeographic().
9384 : *
9385 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9386 : */
9387 :
9388 0 : int OGRSpatialReference::IsDerivedProjected() const
9389 :
9390 : {
9391 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
9392 : TAKE_OPTIONAL_LOCK();
9393 : d->refreshProjObj();
9394 : d->demoteFromBoundCRS();
9395 : const bool isDerivedProjected =
9396 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9397 : d->undoDemoteFromBoundCRS();
9398 : return isDerivedProjected ? TRUE : FALSE;
9399 : #else
9400 0 : return FALSE;
9401 : #endif
9402 : }
9403 :
9404 : /************************************************************************/
9405 : /* OSRIsDerivedProjected() */
9406 : /************************************************************************/
9407 : /**
9408 : * \brief Check if the CRS is a derived projected coordinate system.
9409 : *
9410 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
9411 : *
9412 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9413 : */
9414 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9415 :
9416 : {
9417 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9418 :
9419 0 : return ToPointer(hSRS)->IsDerivedProjected();
9420 : }
9421 :
9422 : /************************************************************************/
9423 : /* IsLocal() */
9424 : /************************************************************************/
9425 :
9426 : /**
9427 : * \brief Check if local coordinate system.
9428 : *
9429 : * This method is the same as the C function OSRIsLocal().
9430 : *
9431 : * @return TRUE if this spatial reference is local ... that is the
9432 : * root is a LOCAL_CS node.
9433 : */
9434 :
9435 7619 : int OGRSpatialReference::IsLocal() const
9436 :
9437 : {
9438 7619 : TAKE_OPTIONAL_LOCK();
9439 7619 : d->refreshProjObj();
9440 15238 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9441 : }
9442 :
9443 : /************************************************************************/
9444 : /* OSRIsLocal() */
9445 : /************************************************************************/
9446 : /**
9447 : * \brief Check if local coordinate system.
9448 : *
9449 : * This function is the same as OGRSpatialReference::IsLocal().
9450 : */
9451 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
9452 :
9453 : {
9454 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9455 :
9456 8 : return ToPointer(hSRS)->IsLocal();
9457 : }
9458 :
9459 : /************************************************************************/
9460 : /* IsVertical() */
9461 : /************************************************************************/
9462 :
9463 : /**
9464 : * \brief Check if vertical coordinate system.
9465 : *
9466 : * This method is the same as the C function OSRIsVertical().
9467 : *
9468 : * @return TRUE if this contains a VERT_CS node indicating a it is a
9469 : * vertical coordinate system. Also if it is a CompoundCRS made of a
9470 : * VerticalCRS
9471 : *
9472 : * @since OGR 1.8.0
9473 : */
9474 :
9475 8492 : int OGRSpatialReference::IsVertical() const
9476 :
9477 : {
9478 8492 : TAKE_OPTIONAL_LOCK();
9479 8492 : d->refreshProjObj();
9480 8492 : d->demoteFromBoundCRS();
9481 8492 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9482 8492 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9483 : {
9484 : auto vertCRS =
9485 32 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9486 32 : if (vertCRS)
9487 : {
9488 32 : const auto vertCRSType = proj_get_type(vertCRS);
9489 32 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9490 32 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
9491 : {
9492 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9493 0 : if (base)
9494 : {
9495 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9496 0 : proj_destroy(base);
9497 : }
9498 : }
9499 32 : proj_destroy(vertCRS);
9500 : }
9501 : }
9502 8492 : d->undoDemoteFromBoundCRS();
9503 16984 : return isVertical;
9504 : }
9505 :
9506 : /************************************************************************/
9507 : /* OSRIsVertical() */
9508 : /************************************************************************/
9509 : /**
9510 : * \brief Check if vertical coordinate system.
9511 : *
9512 : * This function is the same as OGRSpatialReference::IsVertical().
9513 : *
9514 : * @since OGR 1.8.0
9515 : */
9516 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9517 :
9518 : {
9519 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9520 :
9521 0 : return ToPointer(hSRS)->IsVertical();
9522 : }
9523 :
9524 : /************************************************************************/
9525 : /* IsDynamic() */
9526 : /************************************************************************/
9527 :
9528 : /**
9529 : * \brief Check if a CRS is a dynamic CRS.
9530 : *
9531 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9532 : * plate-fixed.
9533 : *
9534 : * This method is the same as the C function OSRIsDynamic().
9535 : *
9536 : * @return true if the CRS is dynamic
9537 : *
9538 : * @since OGR 3.4.0
9539 : *
9540 : * @see HasPointMotionOperation()
9541 : */
9542 :
9543 14394 : bool OGRSpatialReference::IsDynamic() const
9544 :
9545 : {
9546 14394 : TAKE_OPTIONAL_LOCK();
9547 14394 : bool isDynamic = false;
9548 14394 : d->refreshProjObj();
9549 14394 : d->demoteFromBoundCRS();
9550 14394 : auto ctxt = d->getPROJContext();
9551 14394 : PJ *horiz = nullptr;
9552 14394 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9553 : {
9554 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9555 : }
9556 14298 : else if (d->m_pj_crs)
9557 : {
9558 14192 : horiz = proj_clone(ctxt, d->m_pj_crs);
9559 : }
9560 14394 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9561 : {
9562 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9563 6 : if (baseCRS)
9564 : {
9565 6 : proj_destroy(horiz);
9566 6 : horiz = baseCRS;
9567 : }
9568 : }
9569 14394 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9570 14394 : if (datum)
9571 : {
9572 14266 : const auto type = proj_get_type(datum);
9573 14266 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9574 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9575 14266 : if (!isDynamic)
9576 : {
9577 14266 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9578 14266 : const char *code = proj_get_id_code(datum, 0);
9579 14266 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9580 13822 : EQUAL(code, "6326"))
9581 : {
9582 8909 : isDynamic = true;
9583 : }
9584 : }
9585 14266 : proj_destroy(datum);
9586 : }
9587 : #if PROJ_VERSION_MAJOR > 7 || \
9588 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9589 : else
9590 : {
9591 : auto ensemble =
9592 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9593 : if (ensemble)
9594 : {
9595 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9596 : if (member)
9597 : {
9598 : const auto type = proj_get_type(member);
9599 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9600 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9601 : proj_destroy(member);
9602 : }
9603 : proj_destroy(ensemble);
9604 : }
9605 : }
9606 : #endif
9607 14394 : proj_destroy(horiz);
9608 14394 : d->undoDemoteFromBoundCRS();
9609 28788 : return isDynamic;
9610 : }
9611 :
9612 : /************************************************************************/
9613 : /* OSRIsDynamic() */
9614 : /************************************************************************/
9615 : /**
9616 : * \brief Check if a CRS is a dynamic CRS.
9617 : *
9618 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9619 : * plate-fixed.
9620 : *
9621 : * This function is the same as OGRSpatialReference::IsDynamic().
9622 : *
9623 : * @since OGR 3.4.0
9624 : */
9625 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9626 :
9627 : {
9628 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9629 :
9630 0 : return ToPointer(hSRS)->IsDynamic();
9631 : }
9632 :
9633 : /************************************************************************/
9634 : /* HasPointMotionOperation() */
9635 : /************************************************************************/
9636 :
9637 : /**
9638 : * \brief Check if a CRS has at least an associated point motion operation.
9639 : *
9640 : * Some CRS are not formally declared as dynamic, but may behave as such
9641 : * in practice due to the presence of point motion operation, to perform
9642 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9643 : *
9644 : * @return true if the CRS has at least an associated point motion operation.
9645 : *
9646 : * @since OGR 3.8.0 and PROJ 9.4.0
9647 : *
9648 : * @see IsDynamic()
9649 : */
9650 :
9651 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9652 :
9653 : {
9654 : #if PROJ_VERSION_MAJOR > 9 || \
9655 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9656 : TAKE_OPTIONAL_LOCK();
9657 : d->refreshProjObj();
9658 : d->demoteFromBoundCRS();
9659 : auto ctxt = d->getPROJContext();
9660 : auto res =
9661 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9662 : d->undoDemoteFromBoundCRS();
9663 : return res;
9664 : #else
9665 5 : return false;
9666 : #endif
9667 : }
9668 :
9669 : /************************************************************************/
9670 : /* OSRHasPointMotionOperation() */
9671 : /************************************************************************/
9672 :
9673 : /**
9674 : * \brief Check if a CRS has at least an associated point motion operation.
9675 : *
9676 : * Some CRS are not formally declared as dynamic, but may behave as such
9677 : * in practice due to the presence of point motion operation, to perform
9678 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9679 : *
9680 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9681 : *
9682 : * @since OGR 3.8.0 and PROJ 9.4.0
9683 : */
9684 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9685 :
9686 : {
9687 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9688 :
9689 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9690 : }
9691 :
9692 : /************************************************************************/
9693 : /* CloneGeogCS() */
9694 : /************************************************************************/
9695 :
9696 : /**
9697 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9698 : * object.
9699 : *
9700 : * @return a new SRS, which becomes the responsibility of the caller.
9701 : */
9702 4098 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9703 :
9704 : {
9705 8196 : TAKE_OPTIONAL_LOCK();
9706 4098 : d->refreshProjObj();
9707 4098 : if (d->m_pj_crs)
9708 : {
9709 4098 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9710 0 : return nullptr;
9711 :
9712 : auto geodCRS =
9713 4098 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9714 4098 : if (geodCRS)
9715 : {
9716 4098 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9717 4098 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9718 : {
9719 : PJ *hub_crs =
9720 13 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9721 13 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9722 13 : d->m_pj_crs);
9723 13 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9724 : geodCRS, hub_crs, co);
9725 13 : proj_destroy(geodCRS);
9726 13 : geodCRS = temp;
9727 13 : proj_destroy(hub_crs);
9728 13 : proj_destroy(co);
9729 : }
9730 :
9731 : /* --------------------------------------------------------------------
9732 : */
9733 : /* We have to reconstruct the GEOGCS node for geocentric */
9734 : /* coordinate systems. */
9735 : /* --------------------------------------------------------------------
9736 : */
9737 4098 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9738 : {
9739 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9740 : #if PROJ_VERSION_MAJOR > 7 || \
9741 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9742 : if (datum == nullptr)
9743 : {
9744 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9745 : geodCRS);
9746 : }
9747 : #endif
9748 0 : if (datum)
9749 : {
9750 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9751 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9752 : nullptr, 0);
9753 0 : auto temp = proj_create_geographic_crs_from_datum(
9754 : d->getPROJContext(), "unnamed", datum, cs);
9755 0 : proj_destroy(datum);
9756 0 : proj_destroy(cs);
9757 0 : proj_destroy(geodCRS);
9758 0 : geodCRS = temp;
9759 : }
9760 : }
9761 :
9762 4098 : poNewSRS->d->setPjCRS(geodCRS);
9763 4098 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9764 2677 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9765 4098 : return poNewSRS;
9766 : }
9767 : }
9768 0 : return nullptr;
9769 : }
9770 :
9771 : /************************************************************************/
9772 : /* OSRCloneGeogCS() */
9773 : /************************************************************************/
9774 : /**
9775 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9776 : * object.
9777 : *
9778 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9779 : */
9780 123 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9781 :
9782 : {
9783 123 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9784 :
9785 123 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9786 : }
9787 :
9788 : /************************************************************************/
9789 : /* IsSameGeogCS() */
9790 : /************************************************************************/
9791 :
9792 : /**
9793 : * \brief Do the GeogCS'es match?
9794 : *
9795 : * This method is the same as the C function OSRIsSameGeogCS().
9796 : *
9797 : * @param poOther the SRS being compared against.
9798 : *
9799 : * @return TRUE if they are the same or FALSE otherwise.
9800 : */
9801 :
9802 8220 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9803 :
9804 : {
9805 8220 : return IsSameGeogCS(poOther, nullptr);
9806 : }
9807 :
9808 : /**
9809 : * \brief Do the GeogCS'es match?
9810 : *
9811 : * This method is the same as the C function OSRIsSameGeogCS().
9812 : *
9813 : * @param poOther the SRS being compared against.
9814 : * @param papszOptions options. ignored
9815 : *
9816 : * @return TRUE if they are the same or FALSE otherwise.
9817 : */
9818 :
9819 8220 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9820 : const char *const *papszOptions) const
9821 :
9822 : {
9823 16440 : TAKE_OPTIONAL_LOCK();
9824 :
9825 8220 : CPL_IGNORE_RET_VAL(papszOptions);
9826 :
9827 8220 : d->refreshProjObj();
9828 8220 : poOther->d->refreshProjObj();
9829 8220 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9830 0 : return FALSE;
9831 8220 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9832 8220 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9833 24660 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9834 8220 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9835 : {
9836 0 : return FALSE;
9837 : }
9838 :
9839 8220 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9840 : auto otherGeodCRS =
9841 8220 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9842 8220 : if (!geodCRS || !otherGeodCRS)
9843 : {
9844 0 : proj_destroy(geodCRS);
9845 0 : proj_destroy(otherGeodCRS);
9846 0 : return FALSE;
9847 : }
9848 :
9849 8220 : int ret = proj_is_equivalent_to(
9850 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9851 :
9852 8220 : proj_destroy(geodCRS);
9853 8220 : proj_destroy(otherGeodCRS);
9854 8220 : return ret;
9855 : }
9856 :
9857 : /************************************************************************/
9858 : /* OSRIsSameGeogCS() */
9859 : /************************************************************************/
9860 :
9861 : /**
9862 : * \brief Do the GeogCS'es match?
9863 : *
9864 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9865 : */
9866 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9867 :
9868 : {
9869 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9870 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9871 :
9872 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9873 : }
9874 :
9875 : /************************************************************************/
9876 : /* IsSameVertCS() */
9877 : /************************************************************************/
9878 :
9879 : /**
9880 : * \brief Do the VertCS'es match?
9881 : *
9882 : * This method is the same as the C function OSRIsSameVertCS().
9883 : *
9884 : * @param poOther the SRS being compared against.
9885 : *
9886 : * @return TRUE if they are the same or FALSE otherwise.
9887 : */
9888 :
9889 0 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9890 :
9891 : {
9892 0 : TAKE_OPTIONAL_LOCK();
9893 :
9894 : /* -------------------------------------------------------------------- */
9895 : /* Does the datum name match? */
9896 : /* -------------------------------------------------------------------- */
9897 0 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9898 0 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9899 :
9900 0 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9901 0 : !EQUAL(pszThisValue, pszOtherValue))
9902 0 : return FALSE;
9903 :
9904 : /* -------------------------------------------------------------------- */
9905 : /* Do the units match? */
9906 : /* -------------------------------------------------------------------- */
9907 0 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9908 0 : if (pszThisValue == nullptr)
9909 0 : pszThisValue = "1.0";
9910 :
9911 0 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9912 0 : if (pszOtherValue == nullptr)
9913 0 : pszOtherValue = "1.0";
9914 :
9915 0 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9916 0 : return FALSE;
9917 :
9918 0 : return TRUE;
9919 : }
9920 :
9921 : /************************************************************************/
9922 : /* OSRIsSameVertCS() */
9923 : /************************************************************************/
9924 :
9925 : /**
9926 : * \brief Do the VertCS'es match?
9927 : *
9928 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9929 : */
9930 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9931 :
9932 : {
9933 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9934 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9935 :
9936 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9937 : }
9938 :
9939 : /************************************************************************/
9940 : /* IsSame() */
9941 : /************************************************************************/
9942 :
9943 : /**
9944 : * \brief Do these two spatial references describe the same system ?
9945 : *
9946 : * @param poOtherSRS the SRS being compared to.
9947 : *
9948 : * @return TRUE if equivalent or FALSE otherwise.
9949 : */
9950 :
9951 22831 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9952 :
9953 : {
9954 22831 : return IsSame(poOtherSRS, nullptr);
9955 : }
9956 :
9957 : /**
9958 : * \brief Do these two spatial references describe the same system ?
9959 : *
9960 : * This also takes into account the data axis to CRS axis mapping by default
9961 : *
9962 : * @param poOtherSRS the SRS being compared to.
9963 : * @param papszOptions options. NULL or NULL terminated list of options.
9964 : * Currently supported options are:
9965 : * <ul>
9966 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9967 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9968 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9969 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9970 : * </ul>
9971 : *
9972 : * @return TRUE if equivalent or FALSE otherwise.
9973 : */
9974 :
9975 33514 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9976 : const char *const *papszOptions) const
9977 :
9978 : {
9979 67028 : TAKE_OPTIONAL_LOCK();
9980 :
9981 33514 : d->refreshProjObj();
9982 33514 : poOtherSRS->d->refreshProjObj();
9983 33514 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9984 51 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9985 33463 : if (!CPLTestBool(CSLFetchNameValueDef(
9986 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9987 : {
9988 32862 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9989 2791 : return false;
9990 : }
9991 :
9992 30672 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9993 : "IGNORE_COORDINATE_EPOCH", "NO")))
9994 : {
9995 30308 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9996 27 : return false;
9997 : }
9998 :
9999 30645 : bool reboundSelf = false;
10000 30645 : bool reboundOther = false;
10001 30696 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10002 51 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10003 : {
10004 14 : d->demoteFromBoundCRS();
10005 14 : reboundSelf = true;
10006 : }
10007 61225 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10008 30594 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10009 : {
10010 28 : poOtherSRS->d->demoteFromBoundCRS();
10011 28 : reboundOther = true;
10012 : }
10013 :
10014 30645 : PJ_COMPARISON_CRITERION criterion =
10015 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10016 30645 : const char *pszCriterion = CSLFetchNameValueDef(
10017 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10018 30645 : if (EQUAL(pszCriterion, "STRICT"))
10019 0 : criterion = PJ_COMP_STRICT;
10020 30645 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
10021 7907 : criterion = PJ_COMP_EQUIVALENT;
10022 22738 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10023 : {
10024 0 : CPLError(CE_Warning, CPLE_NotSupported,
10025 : "Unsupported value for CRITERION: %s", pszCriterion);
10026 : }
10027 : int ret =
10028 30645 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10029 30645 : if (reboundSelf)
10030 14 : d->undoDemoteFromBoundCRS();
10031 30645 : if (reboundOther)
10032 28 : poOtherSRS->d->undoDemoteFromBoundCRS();
10033 :
10034 30645 : return ret;
10035 : }
10036 :
10037 : /************************************************************************/
10038 : /* OSRIsSame() */
10039 : /************************************************************************/
10040 :
10041 : /**
10042 : * \brief Do these two spatial references describe the same system ?
10043 : *
10044 : * This function is the same as OGRSpatialReference::IsSame().
10045 : */
10046 27 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10047 :
10048 : {
10049 27 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10050 27 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10051 :
10052 27 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10053 : }
10054 :
10055 : /************************************************************************/
10056 : /* OSRIsSameEx() */
10057 : /************************************************************************/
10058 :
10059 : /**
10060 : * \brief Do these two spatial references describe the same system ?
10061 : *
10062 : * This function is the same as OGRSpatialReference::IsSame().
10063 : */
10064 609 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10065 : const char *const *papszOptions)
10066 : {
10067 609 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10068 609 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10069 :
10070 609 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10071 : }
10072 :
10073 : /************************************************************************/
10074 : /* convertToOtherProjection() */
10075 : /************************************************************************/
10076 :
10077 : /**
10078 : * \brief Convert to another equivalent projection
10079 : *
10080 : * Currently implemented:
10081 : * <ul>
10082 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10083 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10084 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10085 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10086 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10087 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10088 : * </ul>
10089 : *
10090 : * @param pszTargetProjection target projection.
10091 : * @param papszOptions lists of options. None supported currently.
10092 : * @return a new SRS, or NULL in case of error.
10093 : *
10094 : * @since GDAL 2.3
10095 : */
10096 89 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10097 : const char *pszTargetProjection,
10098 : CPL_UNUSED const char *const *papszOptions) const
10099 : {
10100 178 : TAKE_OPTIONAL_LOCK();
10101 :
10102 89 : if (pszTargetProjection == nullptr)
10103 1 : return nullptr;
10104 : int new_code;
10105 88 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10106 : {
10107 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10108 : }
10109 82 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10110 : {
10111 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10112 : }
10113 77 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10114 : {
10115 65 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10116 : }
10117 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10118 : {
10119 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10120 : }
10121 : else
10122 : {
10123 1 : return nullptr;
10124 : }
10125 :
10126 87 : d->refreshProjObj();
10127 87 : d->demoteFromBoundCRS();
10128 87 : OGRSpatialReference *poNewSRS = nullptr;
10129 87 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10130 : {
10131 : auto conv =
10132 86 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10133 86 : auto new_conv = proj_convert_conversion_to_other_method(
10134 : d->getPROJContext(), conv, new_code, nullptr);
10135 86 : proj_destroy(conv);
10136 86 : if (new_conv)
10137 : {
10138 : auto geodCRS =
10139 72 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10140 72 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10141 72 : d->m_pj_crs);
10142 72 : if (geodCRS && cs)
10143 : {
10144 72 : auto new_proj_crs = proj_create_projected_crs(
10145 72 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10146 : new_conv, cs);
10147 72 : proj_destroy(new_conv);
10148 72 : if (new_proj_crs)
10149 : {
10150 72 : poNewSRS = new OGRSpatialReference();
10151 :
10152 72 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10153 : {
10154 9 : auto boundCRS = proj_crs_create_bound_crs(
10155 : d->getPROJContext(), new_proj_crs,
10156 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10157 9 : if (boundCRS)
10158 : {
10159 9 : proj_destroy(new_proj_crs);
10160 9 : new_proj_crs = boundCRS;
10161 : }
10162 : }
10163 :
10164 72 : poNewSRS->d->setPjCRS(new_proj_crs);
10165 : }
10166 : }
10167 72 : proj_destroy(geodCRS);
10168 72 : proj_destroy(cs);
10169 : }
10170 : }
10171 87 : d->undoDemoteFromBoundCRS();
10172 87 : return poNewSRS;
10173 : }
10174 :
10175 : /************************************************************************/
10176 : /* OSRConvertToOtherProjection() */
10177 : /************************************************************************/
10178 :
10179 : /**
10180 : * \brief Convert to another equivalent projection
10181 : *
10182 : * Currently implemented:
10183 : * <ul>
10184 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10185 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10186 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10187 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10188 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10189 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10190 : * </ul>
10191 : *
10192 : * @param hSRS source SRS
10193 : * @param pszTargetProjection target projection.
10194 : * @param papszOptions lists of options. None supported currently.
10195 : * @return a new SRS, or NULL in case of error.
10196 : *
10197 : * @since GDAL 2.3
10198 : */
10199 : OGRSpatialReferenceH
10200 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10201 : const char *pszTargetProjection,
10202 : const char *const *papszOptions)
10203 : {
10204 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10205 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10206 28 : pszTargetProjection, papszOptions));
10207 : }
10208 :
10209 : /************************************************************************/
10210 : /* OSRFindMatches() */
10211 : /************************************************************************/
10212 :
10213 : /**
10214 : * \brief Try to identify a match between the passed SRS and a related SRS
10215 : * in a catalog.
10216 : *
10217 : * Matching may be partial, or may fail.
10218 : * Returned entries will be sorted by decreasing match confidence (first
10219 : * entry has the highest match confidence).
10220 : *
10221 : * The exact way matching is done may change in future versions. Starting with
10222 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
10223 : *
10224 : * This function is the same as OGRSpatialReference::FindMatches().
10225 : *
10226 : * @param hSRS SRS to match
10227 : * @param papszOptions NULL terminated list of options or NULL
10228 : * @param pnEntries Output parameter. Number of values in the returned array.
10229 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10230 : * will be allocated to an array of *pnEntries whose values between 0 and 100
10231 : * indicate the confidence in the match. 100 is the highest confidence level.
10232 : * The array must be freed with CPLFree().
10233 : *
10234 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10235 : * with OSRFreeSRSArray()
10236 : *
10237 : * @since GDAL 2.3
10238 : */
10239 10 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10240 : char **papszOptions, int *pnEntries,
10241 : int **ppanMatchConfidence)
10242 : {
10243 10 : if (pnEntries)
10244 10 : *pnEntries = 0;
10245 10 : if (ppanMatchConfidence)
10246 10 : *ppanMatchConfidence = nullptr;
10247 10 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10248 :
10249 10 : OGRSpatialReference *poSRS = ToPointer(hSRS);
10250 10 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10251 : }
10252 :
10253 : /************************************************************************/
10254 : /* OSRFreeSRSArray() */
10255 : /************************************************************************/
10256 :
10257 : /**
10258 : * \brief Free return of OSRIdentifyMatches()
10259 : *
10260 : * @param pahSRS array of SRS (must be NULL terminated)
10261 : * @since GDAL 2.3
10262 : */
10263 192 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10264 : {
10265 192 : if (pahSRS != nullptr)
10266 : {
10267 1739 : for (int i = 0; pahSRS[i] != nullptr; ++i)
10268 : {
10269 1565 : OSRRelease(pahSRS[i]);
10270 : }
10271 174 : CPLFree(pahSRS);
10272 : }
10273 192 : }
10274 :
10275 : /************************************************************************/
10276 : /* FindBestMatch() */
10277 : /************************************************************************/
10278 :
10279 : /**
10280 : * \brief Try to identify the best match between the passed SRS and a related
10281 : * SRS in a catalog.
10282 : *
10283 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10284 : * of filtering its output.
10285 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10286 : * will be considered. If there is a single match, it is returned.
10287 : * If there are several matches, only return the one under the
10288 : * pszPreferredAuthority, if there is a single one under that authority.
10289 : *
10290 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10291 : * 100). If set to 0, 90 is used.
10292 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10293 : * "EPSG" is used.
10294 : * @param papszOptions NULL terminated list of options or NULL. No option is
10295 : * defined at time of writing.
10296 : *
10297 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10298 : *
10299 : * @since GDAL 3.6
10300 : * @see OGRSpatialReference::FindMatches()
10301 : */
10302 : OGRSpatialReference *
10303 1412 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10304 : const char *pszPreferredAuthority,
10305 : CSLConstList papszOptions) const
10306 : {
10307 2824 : TAKE_OPTIONAL_LOCK();
10308 :
10309 1412 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
10310 :
10311 1412 : if (nMinimumMatchConfidence == 0)
10312 0 : nMinimumMatchConfidence = 90;
10313 1412 : if (pszPreferredAuthority == nullptr)
10314 200 : pszPreferredAuthority = "EPSG";
10315 :
10316 : // Try to identify the CRS with the database
10317 1412 : int nEntries = 0;
10318 1412 : int *panConfidence = nullptr;
10319 : OGRSpatialReferenceH *pahSRS =
10320 1412 : FindMatches(nullptr, &nEntries, &panConfidence);
10321 1412 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10322 : {
10323 2506 : std::vector<double> adfTOWGS84(7);
10324 1253 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10325 : {
10326 1252 : adfTOWGS84.clear();
10327 : }
10328 :
10329 1253 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10330 :
10331 : auto poBaseGeogCRS =
10332 1253 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10333 :
10334 : // If the base geographic SRS of the SRS is EPSG:4326
10335 : // with TOWGS84[0,0,0,0,0,0], then just use the official
10336 : // SRS code
10337 : // Same with EPSG:4258 (ETRS89), since it's the only known
10338 : // TOWGS84[] style transformation to WGS 84, and given the
10339 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10340 : // chance that a non-NULL TOWGS84[] will emerge.
10341 1253 : const char *pszAuthorityName = nullptr;
10342 1253 : const char *pszAuthorityCode = nullptr;
10343 1253 : const char *pszBaseAuthorityName = nullptr;
10344 1253 : const char *pszBaseAuthorityCode = nullptr;
10345 2506 : if (adfTOWGS84 == std::vector<double>(7) &&
10346 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10347 1 : EQUAL(pszAuthorityName, "EPSG") &&
10348 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10349 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10350 1 : nullptr &&
10351 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
10352 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10353 2507 : nullptr &&
10354 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
10355 1 : EQUAL(pszBaseAuthorityCode, "4258")))
10356 : {
10357 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
10358 : }
10359 :
10360 1253 : CPLFree(pahSRS);
10361 1253 : CPLFree(panConfidence);
10362 :
10363 1253 : return poSRS;
10364 : }
10365 : else
10366 : {
10367 : // If there are several matches >= nMinimumMatchConfidence, take the
10368 : // only one that is under pszPreferredAuthority
10369 159 : int iBestEntry = -1;
10370 1674 : for (int i = 0; i < nEntries; i++)
10371 : {
10372 1515 : if (panConfidence[i] >= nMinimumMatchConfidence)
10373 : {
10374 : const char *pszAuthName =
10375 3 : OGRSpatialReference::FromHandle(pahSRS[i])
10376 3 : ->GetAuthorityName(nullptr);
10377 3 : if (pszAuthName != nullptr &&
10378 3 : EQUAL(pszAuthName, pszPreferredAuthority))
10379 : {
10380 3 : if (iBestEntry < 0)
10381 3 : iBestEntry = i;
10382 : else
10383 : {
10384 0 : iBestEntry = -1;
10385 0 : break;
10386 : }
10387 : }
10388 : }
10389 : }
10390 159 : if (iBestEntry >= 0)
10391 : {
10392 3 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10393 3 : OSRFreeSRSArray(pahSRS);
10394 3 : CPLFree(panConfidence);
10395 3 : return poRet;
10396 : }
10397 : }
10398 156 : OSRFreeSRSArray(pahSRS);
10399 156 : CPLFree(panConfidence);
10400 156 : return nullptr;
10401 : }
10402 :
10403 : /************************************************************************/
10404 : /* SetTOWGS84() */
10405 : /************************************************************************/
10406 :
10407 : /**
10408 : * \brief Set the Bursa-Wolf conversion to WGS84.
10409 : *
10410 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
10411 : * if there is no existing DATUM node. It will replace
10412 : * an existing TOWGS84 node if there is one.
10413 : *
10414 : * The parameters have the same meaning as EPSG transformation 9606
10415 : * (Position Vector 7-param. transformation).
10416 : *
10417 : * This method is the same as the C function OSRSetTOWGS84().
10418 : *
10419 : * @param dfDX X child in meters.
10420 : * @param dfDY Y child in meters.
10421 : * @param dfDZ Z child in meters.
10422 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10423 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10424 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10425 : * @param dfPPM scaling factor (parts per million).
10426 : *
10427 : * @return OGRERR_NONE on success.
10428 : */
10429 :
10430 104 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10431 : double dfEX, double dfEY, double dfEZ,
10432 : double dfPPM)
10433 :
10434 : {
10435 208 : TAKE_OPTIONAL_LOCK();
10436 :
10437 104 : d->refreshProjObj();
10438 104 : if (d->m_pj_crs == nullptr)
10439 : {
10440 0 : return OGRERR_FAILURE;
10441 : }
10442 :
10443 : // Remove existing BoundCRS
10444 104 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10445 : {
10446 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10447 0 : if (!baseCRS)
10448 0 : return OGRERR_FAILURE;
10449 0 : d->setPjCRS(baseCRS);
10450 : }
10451 :
10452 : PJ_PARAM_DESCRIPTION params[7];
10453 :
10454 104 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10455 104 : params[0].auth_name = "EPSG";
10456 104 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10457 104 : params[0].value = dfDX;
10458 104 : params[0].unit_name = "metre";
10459 104 : params[0].unit_conv_factor = 1.0;
10460 104 : params[0].unit_type = PJ_UT_LINEAR;
10461 :
10462 104 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10463 104 : params[1].auth_name = "EPSG";
10464 104 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10465 104 : params[1].value = dfDY;
10466 104 : params[1].unit_name = "metre";
10467 104 : params[1].unit_conv_factor = 1.0;
10468 104 : params[1].unit_type = PJ_UT_LINEAR;
10469 :
10470 104 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10471 104 : params[2].auth_name = "EPSG";
10472 104 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10473 104 : params[2].value = dfDZ;
10474 104 : params[2].unit_name = "metre";
10475 104 : params[2].unit_conv_factor = 1.0;
10476 104 : params[2].unit_type = PJ_UT_LINEAR;
10477 :
10478 104 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10479 104 : params[3].auth_name = "EPSG";
10480 104 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10481 104 : params[3].value = dfEX;
10482 104 : params[3].unit_name = "arc-second";
10483 104 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10484 104 : params[3].unit_type = PJ_UT_ANGULAR;
10485 :
10486 104 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10487 104 : params[4].auth_name = "EPSG";
10488 104 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10489 104 : params[4].value = dfEY;
10490 104 : params[4].unit_name = "arc-second";
10491 104 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10492 104 : params[4].unit_type = PJ_UT_ANGULAR;
10493 :
10494 104 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10495 104 : params[5].auth_name = "EPSG";
10496 104 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10497 104 : params[5].value = dfEZ;
10498 104 : params[5].unit_name = "arc-second";
10499 104 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10500 104 : params[5].unit_type = PJ_UT_ANGULAR;
10501 :
10502 104 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10503 104 : params[6].auth_name = "EPSG";
10504 104 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10505 104 : params[6].value = dfPPM;
10506 104 : params[6].unit_name = "parts per million";
10507 104 : params[6].unit_conv_factor = 1e-6;
10508 104 : params[6].unit_type = PJ_UT_SCALE;
10509 :
10510 : auto sourceCRS =
10511 104 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10512 104 : if (!sourceCRS)
10513 : {
10514 0 : return OGRERR_FAILURE;
10515 : }
10516 :
10517 104 : const auto sourceType = proj_get_type(sourceCRS);
10518 :
10519 104 : auto targetCRS = proj_create_from_database(
10520 : d->getPROJContext(), "EPSG",
10521 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10522 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10523 : : "4978",
10524 : PJ_CATEGORY_CRS, false, nullptr);
10525 104 : if (!targetCRS)
10526 : {
10527 0 : proj_destroy(sourceCRS);
10528 0 : return OGRERR_FAILURE;
10529 : }
10530 :
10531 208 : CPLString osMethodCode;
10532 : osMethodCode.Printf("%d",
10533 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10534 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10535 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10536 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10537 104 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10538 :
10539 104 : auto transf = proj_create_transformation(
10540 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10541 : sourceCRS, targetCRS, nullptr,
10542 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10543 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10544 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10545 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10546 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10547 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10548 104 : proj_destroy(sourceCRS);
10549 104 : if (!transf)
10550 : {
10551 0 : proj_destroy(targetCRS);
10552 0 : return OGRERR_FAILURE;
10553 : }
10554 :
10555 104 : auto newBoundCRS = proj_crs_create_bound_crs(
10556 104 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10557 104 : proj_destroy(transf);
10558 104 : proj_destroy(targetCRS);
10559 104 : if (!newBoundCRS)
10560 : {
10561 0 : return OGRERR_FAILURE;
10562 : }
10563 :
10564 104 : d->setPjCRS(newBoundCRS);
10565 104 : return OGRERR_NONE;
10566 : }
10567 :
10568 : /************************************************************************/
10569 : /* OSRSetTOWGS84() */
10570 : /************************************************************************/
10571 :
10572 : /**
10573 : * \brief Set the Bursa-Wolf conversion to WGS84.
10574 : *
10575 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10576 : */
10577 4 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10578 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10579 : double dfPPM)
10580 :
10581 : {
10582 4 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10583 :
10584 4 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10585 4 : dfPPM);
10586 : }
10587 :
10588 : /************************************************************************/
10589 : /* GetTOWGS84() */
10590 : /************************************************************************/
10591 :
10592 : /**
10593 : * \brief Fetch TOWGS84 parameters, if available.
10594 : *
10595 : * The parameters have the same meaning as EPSG transformation 9606
10596 : * (Position Vector 7-param. transformation).
10597 : *
10598 : * @param padfCoeff array into which up to 7 coefficients are placed.
10599 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10600 : *
10601 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10602 : * TOWGS84 node available.
10603 : */
10604 :
10605 4636 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10606 :
10607 : {
10608 9272 : TAKE_OPTIONAL_LOCK();
10609 :
10610 4636 : d->refreshProjObj();
10611 4636 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10612 4588 : return OGRERR_FAILURE;
10613 :
10614 48 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10615 :
10616 48 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10617 48 : int success = proj_coordoperation_get_towgs84_values(
10618 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10619 48 : proj_destroy(transf);
10620 :
10621 48 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10622 : }
10623 :
10624 : /************************************************************************/
10625 : /* OSRGetTOWGS84() */
10626 : /************************************************************************/
10627 :
10628 : /**
10629 : * \brief Fetch TOWGS84 parameters, if available.
10630 : *
10631 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10632 : */
10633 10 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10634 : int nCoeffCount)
10635 :
10636 : {
10637 10 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10638 :
10639 10 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10640 : }
10641 :
10642 : /************************************************************************/
10643 : /* IsAngularParameter() */
10644 : /************************************************************************/
10645 :
10646 : /** Is the passed projection parameter an angular one?
10647 : *
10648 : * @return TRUE or FALSE
10649 : */
10650 :
10651 : /* static */
10652 10 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10653 :
10654 : {
10655 10 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10656 10 : STARTS_WITH_CI(pszParameterName, "lati") ||
10657 7 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10658 4 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10659 2 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10660 2 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10661 8 : return TRUE;
10662 :
10663 2 : return FALSE;
10664 : }
10665 :
10666 : /************************************************************************/
10667 : /* IsLongitudeParameter() */
10668 : /************************************************************************/
10669 :
10670 : /** Is the passed projection parameter an angular longitude
10671 : * (relative to a prime meridian)?
10672 : *
10673 : * @return TRUE or FALSE
10674 : */
10675 :
10676 : /* static */
10677 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10678 :
10679 : {
10680 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10681 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10682 0 : return TRUE;
10683 :
10684 0 : return FALSE;
10685 : }
10686 :
10687 : /************************************************************************/
10688 : /* IsLinearParameter() */
10689 : /************************************************************************/
10690 :
10691 : /** Is the passed projection parameter an linear one measured in meters or
10692 : * some similar linear measure.
10693 : *
10694 : * @return TRUE or FALSE
10695 : */
10696 :
10697 : /* static */
10698 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10699 :
10700 : {
10701 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10702 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10703 9 : return TRUE;
10704 :
10705 34 : return FALSE;
10706 : }
10707 :
10708 : /************************************************************************/
10709 : /* GetNormInfo() */
10710 : /************************************************************************/
10711 :
10712 : /**
10713 : * \brief Set the internal information for normalizing linear, and angular
10714 : * values.
10715 : */
10716 3815 : void OGRSpatialReference::GetNormInfo() const
10717 :
10718 : {
10719 3815 : TAKE_OPTIONAL_LOCK();
10720 :
10721 3815 : if (d->bNormInfoSet)
10722 2696 : return;
10723 :
10724 : /* -------------------------------------------------------------------- */
10725 : /* Initialize values. */
10726 : /* -------------------------------------------------------------------- */
10727 1119 : d->bNormInfoSet = TRUE;
10728 :
10729 1119 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10730 1119 : d->dfToMeter = GetLinearUnits(nullptr);
10731 1119 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10732 1119 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10733 1116 : d->dfToDegrees = 1.0;
10734 : }
10735 :
10736 : /************************************************************************/
10737 : /* GetExtension() */
10738 : /************************************************************************/
10739 :
10740 : /**
10741 : * \brief Fetch extension value.
10742 : *
10743 : * Fetch the value of the named EXTENSION item for the identified
10744 : * target node.
10745 : *
10746 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10747 : * @param pszName the name of the extension being fetched.
10748 : * @param pszDefault the value to return if the extension is not found.
10749 : *
10750 : * @return node value if successful or pszDefault on failure.
10751 : */
10752 :
10753 10807 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10754 : const char *pszName,
10755 : const char *pszDefault) const
10756 :
10757 : {
10758 21614 : TAKE_OPTIONAL_LOCK();
10759 :
10760 : /* -------------------------------------------------------------------- */
10761 : /* Find the target node. */
10762 : /* -------------------------------------------------------------------- */
10763 : const OGR_SRSNode *poNode =
10764 10807 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10765 :
10766 10807 : if (poNode == nullptr)
10767 2241 : return nullptr;
10768 :
10769 : /* -------------------------------------------------------------------- */
10770 : /* Fetch matching EXTENSION if there is one. */
10771 : /* -------------------------------------------------------------------- */
10772 63015 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10773 : {
10774 54471 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10775 :
10776 54495 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10777 24 : poChild->GetChildCount() >= 2)
10778 : {
10779 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10780 22 : return poChild->GetChild(1)->GetValue();
10781 : }
10782 : }
10783 :
10784 8544 : return pszDefault;
10785 : }
10786 :
10787 : /************************************************************************/
10788 : /* SetExtension() */
10789 : /************************************************************************/
10790 : /**
10791 : * \brief Set extension value.
10792 : *
10793 : * Set the value of the named EXTENSION item for the identified
10794 : * target node.
10795 : *
10796 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10797 : * @param pszName the name of the extension being fetched.
10798 : * @param pszValue the value to set
10799 : *
10800 : * @return OGRERR_NONE on success
10801 : */
10802 :
10803 20 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10804 : const char *pszName,
10805 : const char *pszValue)
10806 :
10807 : {
10808 40 : TAKE_OPTIONAL_LOCK();
10809 :
10810 : /* -------------------------------------------------------------------- */
10811 : /* Find the target node. */
10812 : /* -------------------------------------------------------------------- */
10813 20 : OGR_SRSNode *poNode = nullptr;
10814 :
10815 20 : if (pszTargetKey == nullptr)
10816 0 : poNode = GetRoot();
10817 : else
10818 20 : poNode = GetAttrNode(pszTargetKey);
10819 :
10820 20 : if (poNode == nullptr)
10821 0 : return OGRERR_FAILURE;
10822 :
10823 : /* -------------------------------------------------------------------- */
10824 : /* Fetch matching EXTENSION if there is one. */
10825 : /* -------------------------------------------------------------------- */
10826 151 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10827 : {
10828 137 : OGR_SRSNode *poChild = poNode->GetChild(i);
10829 :
10830 143 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10831 6 : poChild->GetChildCount() >= 2)
10832 : {
10833 6 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10834 : {
10835 6 : poChild->GetChild(1)->SetValue(pszValue);
10836 6 : return OGRERR_NONE;
10837 : }
10838 : }
10839 : }
10840 :
10841 : /* -------------------------------------------------------------------- */
10842 : /* Create a new EXTENSION node. */
10843 : /* -------------------------------------------------------------------- */
10844 14 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10845 14 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10846 14 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10847 :
10848 14 : poNode->AddChild(poAuthNode);
10849 :
10850 14 : return OGRERR_NONE;
10851 : }
10852 :
10853 : /************************************************************************/
10854 : /* OSRCleanup() */
10855 : /************************************************************************/
10856 :
10857 : static void CleanupSRSWGS84Mutex();
10858 :
10859 : /**
10860 : * \brief Cleanup cached SRS related memory.
10861 : *
10862 : * This function will attempt to cleanup any cache spatial reference
10863 : * related information, such as cached tables of coordinate systems.
10864 : *
10865 : * This function should not be called concurrently with any other GDAL/OGR
10866 : * function. It is meant at being called once before process termination
10867 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10868 : * thread-specific resources before thread termination.
10869 : */
10870 1126 : void OSRCleanup(void)
10871 :
10872 : {
10873 1126 : OGRCTDumpStatistics();
10874 1126 : CSVDeaccess(nullptr);
10875 1126 : CleanupSRSWGS84Mutex();
10876 1126 : OSRCTCleanCache();
10877 1126 : OSRCleanupTLSContext();
10878 1126 : }
10879 :
10880 : /************************************************************************/
10881 : /* GetAxesCount() */
10882 : /************************************************************************/
10883 :
10884 : /**
10885 : * \brief Return the number of axis of the coordinate system of the CRS.
10886 : *
10887 : * @since GDAL 3.0
10888 : */
10889 36433 : int OGRSpatialReference::GetAxesCount() const
10890 : {
10891 72866 : TAKE_OPTIONAL_LOCK();
10892 :
10893 36433 : int axisCount = 0;
10894 36433 : d->refreshProjObj();
10895 36433 : if (d->m_pj_crs == nullptr)
10896 : {
10897 0 : return 0;
10898 : }
10899 36433 : d->demoteFromBoundCRS();
10900 36433 : auto ctxt = d->getPROJContext();
10901 36433 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10902 : {
10903 29 : for (int i = 0;; i++)
10904 : {
10905 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10906 87 : if (!subCRS)
10907 29 : break;
10908 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10909 : {
10910 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10911 17 : if (baseCRS)
10912 : {
10913 17 : proj_destroy(subCRS);
10914 17 : subCRS = baseCRS;
10915 : }
10916 : }
10917 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10918 58 : if (cs)
10919 : {
10920 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10921 58 : proj_destroy(cs);
10922 : }
10923 58 : proj_destroy(subCRS);
10924 58 : }
10925 : }
10926 : else
10927 : {
10928 36404 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10929 36404 : if (cs)
10930 : {
10931 36404 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10932 36404 : proj_destroy(cs);
10933 : }
10934 : }
10935 36433 : d->undoDemoteFromBoundCRS();
10936 36433 : return axisCount;
10937 : }
10938 :
10939 : /************************************************************************/
10940 : /* OSRGetAxesCount() */
10941 : /************************************************************************/
10942 :
10943 : /**
10944 : * \brief Return the number of axis of the coordinate system of the CRS.
10945 : *
10946 : * This method is the equivalent of the C++ method
10947 : * OGRSpatialReference::GetAxesCount()
10948 : *
10949 : * @since GDAL 3.1
10950 : */
10951 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10952 :
10953 : {
10954 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10955 :
10956 6 : return ToPointer(hSRS)->GetAxesCount();
10957 : }
10958 :
10959 : /************************************************************************/
10960 : /* GetAxis() */
10961 : /************************************************************************/
10962 :
10963 : /**
10964 : * \brief Fetch the orientation of one axis.
10965 : *
10966 : * Fetches the request axis (iAxis - zero based) from the
10967 : * indicated portion of the coordinate system (pszTargetKey) which
10968 : * should be either "GEOGCS" or "PROJCS".
10969 : *
10970 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10971 : *
10972 : * This method is equivalent to the C function OSRGetAxis().
10973 : *
10974 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10975 : * "GEOGCS").
10976 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10977 : * @param peOrientation location into which to place the fetch orientation, may
10978 : * be NULL.
10979 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10980 : * factor. May be NULL. Only set if pszTargetKey == NULL
10981 : *
10982 : * @return the name of the axis or NULL on failure.
10983 : */
10984 :
10985 6870 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10986 : OGRAxisOrientation *peOrientation,
10987 : double *pdfConvUnit) const
10988 :
10989 : {
10990 13740 : TAKE_OPTIONAL_LOCK();
10991 :
10992 6870 : if (peOrientation != nullptr)
10993 6777 : *peOrientation = OAO_Other;
10994 6870 : if (pdfConvUnit != nullptr)
10995 85 : *pdfConvUnit = 0;
10996 :
10997 6870 : d->refreshProjObj();
10998 6870 : if (d->m_pj_crs == nullptr)
10999 : {
11000 3 : return nullptr;
11001 : }
11002 :
11003 6867 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11004 6867 : if (pszTargetKey == nullptr && iAxis <= 2)
11005 : {
11006 6867 : auto ctxt = d->getPROJContext();
11007 :
11008 6867 : int iAxisModified = iAxis;
11009 :
11010 6867 : d->demoteFromBoundCRS();
11011 :
11012 6867 : PJ *cs = nullptr;
11013 6867 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11014 : {
11015 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11016 134 : if (horizCRS)
11017 : {
11018 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11019 : {
11020 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11021 6 : if (baseCRS)
11022 : {
11023 6 : proj_destroy(horizCRS);
11024 6 : horizCRS = baseCRS;
11025 : }
11026 : }
11027 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11028 134 : proj_destroy(horizCRS);
11029 134 : if (cs)
11030 : {
11031 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11032 : {
11033 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11034 44 : proj_destroy(cs);
11035 44 : cs = nullptr;
11036 : }
11037 : }
11038 : }
11039 :
11040 134 : if (cs == nullptr)
11041 : {
11042 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11043 44 : if (vertCRS)
11044 : {
11045 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11046 : {
11047 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11048 30 : if (baseCRS)
11049 : {
11050 30 : proj_destroy(vertCRS);
11051 30 : vertCRS = baseCRS;
11052 : }
11053 : }
11054 :
11055 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11056 44 : proj_destroy(vertCRS);
11057 : }
11058 : }
11059 : }
11060 : else
11061 : {
11062 6733 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11063 : }
11064 :
11065 6867 : if (cs)
11066 : {
11067 6867 : const char *pszName = nullptr;
11068 6867 : const char *pszOrientation = nullptr;
11069 6867 : double dfConvFactor = 0.0;
11070 6867 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11071 : &pszOrientation, &dfConvFactor, nullptr,
11072 : nullptr, nullptr);
11073 :
11074 6867 : if (pdfConvUnit != nullptr)
11075 : {
11076 85 : *pdfConvUnit = dfConvFactor;
11077 : }
11078 :
11079 6867 : if (pszName && pszOrientation)
11080 : {
11081 6867 : d->m_osAxisName[iAxis] = pszName;
11082 6867 : if (peOrientation)
11083 : {
11084 6774 : if (EQUAL(pszOrientation, "NORTH"))
11085 4278 : *peOrientation = OAO_North;
11086 2496 : else if (EQUAL(pszOrientation, "EAST"))
11087 2453 : *peOrientation = OAO_East;
11088 43 : else if (EQUAL(pszOrientation, "SOUTH"))
11089 32 : *peOrientation = OAO_South;
11090 11 : else if (EQUAL(pszOrientation, "WEST"))
11091 0 : *peOrientation = OAO_West;
11092 11 : else if (EQUAL(pszOrientation, "UP"))
11093 1 : *peOrientation = OAO_Up;
11094 10 : else if (EQUAL(pszOrientation, "DOWN"))
11095 0 : *peOrientation = OAO_Down;
11096 : }
11097 6867 : proj_destroy(cs);
11098 6867 : d->undoDemoteFromBoundCRS();
11099 6867 : return d->m_osAxisName[iAxis].c_str();
11100 : }
11101 0 : proj_destroy(cs);
11102 : }
11103 0 : d->undoDemoteFromBoundCRS();
11104 : }
11105 :
11106 : /* -------------------------------------------------------------------- */
11107 : /* Find the target node. */
11108 : /* -------------------------------------------------------------------- */
11109 0 : const OGR_SRSNode *poNode = nullptr;
11110 :
11111 0 : if (pszTargetKey == nullptr)
11112 0 : poNode = GetRoot();
11113 : else
11114 0 : poNode = GetAttrNode(pszTargetKey);
11115 :
11116 0 : if (poNode == nullptr)
11117 0 : return nullptr;
11118 :
11119 : /* -------------------------------------------------------------------- */
11120 : /* Find desired child AXIS. */
11121 : /* -------------------------------------------------------------------- */
11122 0 : const OGR_SRSNode *poAxis = nullptr;
11123 0 : const int nChildCount = poNode->GetChildCount();
11124 :
11125 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
11126 : {
11127 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11128 :
11129 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
11130 0 : continue;
11131 :
11132 0 : if (iAxis == 0)
11133 : {
11134 0 : poAxis = poChild;
11135 0 : break;
11136 : }
11137 0 : iAxis--;
11138 : }
11139 :
11140 0 : if (poAxis == nullptr)
11141 0 : return nullptr;
11142 :
11143 0 : if (poAxis->GetChildCount() < 2)
11144 0 : return nullptr;
11145 :
11146 : /* -------------------------------------------------------------------- */
11147 : /* Extract name and orientation if possible. */
11148 : /* -------------------------------------------------------------------- */
11149 0 : if (peOrientation != nullptr)
11150 : {
11151 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11152 :
11153 0 : if (EQUAL(pszOrientation, "NORTH"))
11154 0 : *peOrientation = OAO_North;
11155 0 : else if (EQUAL(pszOrientation, "EAST"))
11156 0 : *peOrientation = OAO_East;
11157 0 : else if (EQUAL(pszOrientation, "SOUTH"))
11158 0 : *peOrientation = OAO_South;
11159 0 : else if (EQUAL(pszOrientation, "WEST"))
11160 0 : *peOrientation = OAO_West;
11161 0 : else if (EQUAL(pszOrientation, "UP"))
11162 0 : *peOrientation = OAO_Up;
11163 0 : else if (EQUAL(pszOrientation, "DOWN"))
11164 0 : *peOrientation = OAO_Down;
11165 0 : else if (EQUAL(pszOrientation, "OTHER"))
11166 0 : *peOrientation = OAO_Other;
11167 : else
11168 : {
11169 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11170 : pszOrientation);
11171 : }
11172 : }
11173 :
11174 0 : return poAxis->GetChild(0)->GetValue();
11175 : }
11176 :
11177 : /************************************************************************/
11178 : /* OSRGetAxis() */
11179 : /************************************************************************/
11180 :
11181 : /**
11182 : * \brief Fetch the orientation of one axis.
11183 : *
11184 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11185 : */
11186 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11187 : int iAxis, OGRAxisOrientation *peOrientation)
11188 :
11189 : {
11190 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11191 :
11192 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11193 : }
11194 :
11195 : /************************************************************************/
11196 : /* OSRAxisEnumToName() */
11197 : /************************************************************************/
11198 :
11199 : /**
11200 : * \brief Return the string representation for the OGRAxisOrientation
11201 : * enumeration.
11202 : *
11203 : * For example "NORTH" for OAO_North.
11204 : *
11205 : * @return an internal string
11206 : */
11207 396 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11208 :
11209 : {
11210 396 : if (eOrientation == OAO_North)
11211 198 : return "NORTH";
11212 198 : if (eOrientation == OAO_East)
11213 198 : return "EAST";
11214 0 : if (eOrientation == OAO_South)
11215 0 : return "SOUTH";
11216 0 : if (eOrientation == OAO_West)
11217 0 : return "WEST";
11218 0 : if (eOrientation == OAO_Up)
11219 0 : return "UP";
11220 0 : if (eOrientation == OAO_Down)
11221 0 : return "DOWN";
11222 0 : if (eOrientation == OAO_Other)
11223 0 : return "OTHER";
11224 :
11225 0 : return "UNKNOWN";
11226 : }
11227 :
11228 : /************************************************************************/
11229 : /* SetAxes() */
11230 : /************************************************************************/
11231 :
11232 : /**
11233 : * \brief Set the axes for a coordinate system.
11234 : *
11235 : * Set the names, and orientations of the axes for either a projected
11236 : * (PROJCS) or geographic (GEOGCS) coordinate system.
11237 : *
11238 : * This method is equivalent to the C function OSRSetAxes().
11239 : *
11240 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11241 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11242 : * @param eXAxisOrientation normally OAO_East.
11243 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11244 : * @param eYAxisOrientation normally OAO_North.
11245 : *
11246 : * @return OGRERR_NONE on success or an error code.
11247 : */
11248 :
11249 198 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11250 : const char *pszXAxisName,
11251 : OGRAxisOrientation eXAxisOrientation,
11252 : const char *pszYAxisName,
11253 : OGRAxisOrientation eYAxisOrientation)
11254 :
11255 : {
11256 396 : TAKE_OPTIONAL_LOCK();
11257 :
11258 : /* -------------------------------------------------------------------- */
11259 : /* Find the target node. */
11260 : /* -------------------------------------------------------------------- */
11261 198 : OGR_SRSNode *poNode = nullptr;
11262 :
11263 198 : if (pszTargetKey == nullptr)
11264 198 : poNode = GetRoot();
11265 : else
11266 0 : poNode = GetAttrNode(pszTargetKey);
11267 :
11268 198 : if (poNode == nullptr)
11269 0 : return OGRERR_FAILURE;
11270 :
11271 : /* -------------------------------------------------------------------- */
11272 : /* Strip any existing AXIS children. */
11273 : /* -------------------------------------------------------------------- */
11274 594 : while (poNode->FindChild("AXIS") >= 0)
11275 396 : poNode->DestroyChild(poNode->FindChild("AXIS"));
11276 :
11277 : /* -------------------------------------------------------------------- */
11278 : /* Insert desired axes */
11279 : /* -------------------------------------------------------------------- */
11280 198 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11281 :
11282 198 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11283 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11284 :
11285 198 : poNode->AddChild(poAxis);
11286 :
11287 198 : poAxis = new OGR_SRSNode("AXIS");
11288 :
11289 198 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11290 198 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11291 :
11292 198 : poNode->AddChild(poAxis);
11293 :
11294 198 : return OGRERR_NONE;
11295 : }
11296 :
11297 : /************************************************************************/
11298 : /* OSRSetAxes() */
11299 : /************************************************************************/
11300 : /**
11301 : * \brief Set the axes for a coordinate system.
11302 : *
11303 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11304 : */
11305 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11306 : const char *pszXAxisName,
11307 : OGRAxisOrientation eXAxisOrientation,
11308 : const char *pszYAxisName,
11309 : OGRAxisOrientation eYAxisOrientation)
11310 : {
11311 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11312 :
11313 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11314 : eXAxisOrientation, pszYAxisName,
11315 0 : eYAxisOrientation);
11316 : }
11317 :
11318 : /************************************************************************/
11319 : /* OSRExportToMICoordSys() */
11320 : /************************************************************************/
11321 : /**
11322 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11323 : *
11324 : * This method is the equivalent of the C++ method
11325 : * OGRSpatialReference::exportToMICoordSys
11326 : */
11327 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11328 :
11329 : {
11330 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11331 :
11332 5 : *ppszReturn = nullptr;
11333 :
11334 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11335 : }
11336 :
11337 : /************************************************************************/
11338 : /* exportToMICoordSys() */
11339 : /************************************************************************/
11340 :
11341 : /**
11342 : * \brief Export coordinate system in Mapinfo style CoordSys format.
11343 : *
11344 : * Note that the returned WKT string should be freed with
11345 : * CPLFree() when no longer needed. It is the responsibility of the caller.
11346 : *
11347 : * This method is the same as the C function OSRExportToMICoordSys().
11348 : *
11349 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11350 : * definition will be assigned.
11351 : *
11352 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11353 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11354 : */
11355 :
11356 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11357 :
11358 : {
11359 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
11360 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11361 7 : return OGRERR_NONE;
11362 :
11363 0 : return OGRERR_FAILURE;
11364 : }
11365 :
11366 : /************************************************************************/
11367 : /* OSRImportFromMICoordSys() */
11368 : /************************************************************************/
11369 : /**
11370 : * \brief Import Mapinfo style CoordSys definition.
11371 : *
11372 : * This method is the equivalent of the C++ method
11373 : * OGRSpatialReference::importFromMICoordSys
11374 : */
11375 :
11376 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11377 : const char *pszCoordSys)
11378 :
11379 : {
11380 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11381 :
11382 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11383 : }
11384 :
11385 : /************************************************************************/
11386 : /* importFromMICoordSys() */
11387 : /************************************************************************/
11388 :
11389 : /**
11390 : * \brief Import Mapinfo style CoordSys definition.
11391 : *
11392 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11393 : * definition string.
11394 : *
11395 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
11396 : *
11397 : * @param pszCoordSys Mapinfo style CoordSys definition string.
11398 : *
11399 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11400 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11401 : */
11402 :
11403 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11404 :
11405 : {
11406 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11407 :
11408 17 : if (poResult == nullptr)
11409 0 : return OGRERR_FAILURE;
11410 :
11411 17 : *this = *poResult;
11412 17 : delete poResult;
11413 :
11414 17 : return OGRERR_NONE;
11415 : }
11416 :
11417 : /************************************************************************/
11418 : /* OSRCalcInvFlattening() */
11419 : /************************************************************************/
11420 :
11421 : /**
11422 : * \brief Compute inverse flattening from semi-major and semi-minor axis
11423 : *
11424 : * @param dfSemiMajor Semi-major axis length.
11425 : * @param dfSemiMinor Semi-minor axis length.
11426 : *
11427 : * @return inverse flattening, or 0 if both axis are equal.
11428 : * @since GDAL 2.0
11429 : */
11430 :
11431 8319 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11432 : {
11433 8319 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11434 27 : return 0;
11435 8292 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11436 : {
11437 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11438 : "OSRCalcInvFlattening(): Wrong input values");
11439 0 : return 0;
11440 : }
11441 :
11442 8292 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11443 : }
11444 :
11445 : /************************************************************************/
11446 : /* OSRCalcInvFlattening() */
11447 : /************************************************************************/
11448 :
11449 : /**
11450 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11451 : *
11452 : * @param dfSemiMajor Semi-major axis length.
11453 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
11454 : *
11455 : * @return semi-minor axis
11456 : * @since GDAL 2.0
11457 : */
11458 :
11459 655 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11460 : double dfInvFlattening)
11461 : {
11462 655 : if (fabs(dfInvFlattening) < 0.000000000001)
11463 103 : return dfSemiMajor;
11464 552 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11465 : {
11466 0 : CPLError(CE_Failure, CPLE_IllegalArg,
11467 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11468 0 : return dfSemiMajor;
11469 : }
11470 :
11471 552 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11472 : }
11473 :
11474 : /************************************************************************/
11475 : /* GetWGS84SRS() */
11476 : /************************************************************************/
11477 :
11478 : static OGRSpatialReference *poSRSWGS84 = nullptr;
11479 : static CPLMutex *hMutex = nullptr;
11480 :
11481 : /**
11482 : * \brief Returns an instance of a SRS object with WGS84 WKT.
11483 : *
11484 : * Note: the instance will have
11485 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11486 : *
11487 : * The reference counter of the returned object is not increased by this
11488 : * operation.
11489 : *
11490 : * @return instance.
11491 : * @since GDAL 2.0
11492 : */
11493 :
11494 843 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11495 : {
11496 843 : CPLMutexHolderD(&hMutex);
11497 843 : if (poSRSWGS84 == nullptr)
11498 : {
11499 4 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11500 4 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11501 : }
11502 1686 : return poSRSWGS84;
11503 : }
11504 :
11505 : /************************************************************************/
11506 : /* CleanupSRSWGS84Mutex() */
11507 : /************************************************************************/
11508 :
11509 1126 : static void CleanupSRSWGS84Mutex()
11510 : {
11511 1126 : if (hMutex != nullptr)
11512 : {
11513 2 : poSRSWGS84->Release();
11514 2 : poSRSWGS84 = nullptr;
11515 2 : CPLDestroyMutex(hMutex);
11516 2 : hMutex = nullptr;
11517 : }
11518 1126 : }
11519 :
11520 : /************************************************************************/
11521 : /* OSRImportFromProj4() */
11522 : /************************************************************************/
11523 : /**
11524 : * \brief Import PROJ coordinate string.
11525 : *
11526 : * This function is the same as OGRSpatialReference::importFromProj4().
11527 : */
11528 186 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11529 :
11530 : {
11531 186 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11532 :
11533 186 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11534 : }
11535 :
11536 : /************************************************************************/
11537 : /* importFromProj4() */
11538 : /************************************************************************/
11539 :
11540 : /**
11541 : * \brief Import PROJ coordinate string.
11542 : *
11543 : * The OGRSpatialReference is initialized from the passed PROJs style
11544 : * coordinate system string.
11545 : *
11546 : * Example:
11547 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11548 : *
11549 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11550 : * a legacy syntax that should be avoided in the future. In particular they will
11551 : * result in CRS objects whose axis order might not correspond to the official
11552 : * EPSG axis order.
11553 : *
11554 : * This method is the equivalent of the C function OSRImportFromProj4().
11555 : *
11556 : * @param pszProj4 the PROJ style string.
11557 : *
11558 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11559 : */
11560 :
11561 716 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11562 :
11563 : {
11564 1432 : TAKE_OPTIONAL_LOCK();
11565 :
11566 716 : if (strlen(pszProj4) >= 10000)
11567 : {
11568 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11569 1 : return OGRERR_CORRUPT_DATA;
11570 : }
11571 :
11572 : /* -------------------------------------------------------------------- */
11573 : /* Clear any existing definition. */
11574 : /* -------------------------------------------------------------------- */
11575 715 : Clear();
11576 :
11577 715 : CPLString osProj4(pszProj4);
11578 715 : if (osProj4.find("type=crs") == std::string::npos)
11579 : {
11580 706 : osProj4 += " +type=crs";
11581 : }
11582 :
11583 717 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11584 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11585 : {
11586 2 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11587 : "+init=epsg:XXXX syntax is deprecated. It might return "
11588 : "a CRS with a non-EPSG compliant axis order.");
11589 : }
11590 715 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11591 715 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11592 715 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11593 715 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11594 : }
11595 :
11596 : /************************************************************************/
11597 : /* OSRExportToProj4() */
11598 : /************************************************************************/
11599 : /**
11600 : * \brief Export coordinate system in PROJ.4 legacy format.
11601 : *
11602 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11603 : * PROJ >= 6 is significantly different from earlier versions. In particular
11604 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11605 : * will be missing most of the time. PROJ strings to encode CRS should be
11606 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11607 : * is the recommended way.
11608 : *
11609 : * This function is the same as OGRSpatialReference::exportToProj4().
11610 : */
11611 427 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11612 : char **ppszReturn)
11613 :
11614 : {
11615 427 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11616 :
11617 427 : *ppszReturn = nullptr;
11618 :
11619 427 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11620 : }
11621 :
11622 : /************************************************************************/
11623 : /* exportToProj4() */
11624 : /************************************************************************/
11625 :
11626 : /**
11627 : * \brief Export coordinate system in PROJ.4 legacy format.
11628 : *
11629 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11630 : * PROJ >= 6 is significantly different from earlier versions. In particular
11631 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11632 : * will be missing most of the time. PROJ strings to encode CRS should be
11633 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11634 : * representation is the recommended way.
11635 : *
11636 : * Converts the loaded coordinate reference system into PROJ format
11637 : * to the extent possible. The string returned in ppszProj4 should be
11638 : * deallocated by the caller with CPLFree() when no longer needed.
11639 : *
11640 : * LOCAL_CS coordinate systems are not translatable. An empty string
11641 : * will be returned along with OGRERR_NONE.
11642 : *
11643 : * Special processing for Transverse Mercator:
11644 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11645 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11646 : * for the tmerc and utm projection methods, rather than the more accurate
11647 : * method.
11648 : *
11649 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11650 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11651 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11652 : * added. This automatic addition may be disabled by setting the
11653 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11654 : *
11655 : * This method is the equivalent of the C function OSRExportToProj4().
11656 : *
11657 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11658 : * will be assigned.
11659 : *
11660 : * @return OGRERR_NONE on success or an error code on failure.
11661 : */
11662 :
11663 1393 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11664 :
11665 : {
11666 : // In the past calling this method was thread-safe, even if we never
11667 : // guaranteed it. Now proj_as_proj_string() will cache the result
11668 : // internally, so this is no longer thread-safe.
11669 2786 : std::lock_guard oLock(d->m_mutex);
11670 :
11671 1393 : d->refreshProjObj();
11672 1393 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11673 : {
11674 4 : *ppszProj4 = CPLStrdup("");
11675 4 : return OGRERR_FAILURE;
11676 : }
11677 :
11678 : // OSR_USE_ETMERC is here just for legacy
11679 1389 : bool bForceApproxTMerc = false;
11680 1389 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11681 1389 : if (pszUseETMERC && pszUseETMERC[0])
11682 : {
11683 0 : CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11684 : "OSR_USE_ETMERC is a legacy configuration option, which "
11685 : "now has only effect when set to NO (YES is the default). "
11686 : "Use OSR_USE_APPROX_TMERC=YES instead");
11687 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11688 : }
11689 : else
11690 : {
11691 : const char *pszUseApproxTMERC =
11692 1389 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11693 1389 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11694 : {
11695 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11696 : }
11697 : }
11698 1389 : const char *options[] = {
11699 1389 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11700 :
11701 1389 : const char *projString = proj_as_proj_string(
11702 1389 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11703 :
11704 1389 : PJ *boundCRS = nullptr;
11705 2774 : if (projString &&
11706 1385 : (strstr(projString, "+datum=") == nullptr ||
11707 2784 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11708 555 : CPLTestBool(
11709 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11710 : {
11711 555 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11712 555 : d->getPROJContext(), d->m_pj_crs, true,
11713 555 : strstr(projString, "+datum=") == nullptr);
11714 555 : if (boundCRS)
11715 : {
11716 227 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11717 : PJ_PROJ_4, options);
11718 : }
11719 : }
11720 :
11721 1389 : if (projString == nullptr)
11722 : {
11723 4 : *ppszProj4 = CPLStrdup("");
11724 4 : proj_destroy(boundCRS);
11725 4 : return OGRERR_FAILURE;
11726 : }
11727 1385 : *ppszProj4 = CPLStrdup(projString);
11728 1385 : proj_destroy(boundCRS);
11729 1385 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11730 1385 : if (pszTypeCrs)
11731 1385 : *pszTypeCrs = '\0';
11732 1385 : return OGRERR_NONE;
11733 : }
11734 :
11735 : /************************************************************************/
11736 : /* morphToESRI() */
11737 : /************************************************************************/
11738 : /**
11739 : * \brief Convert in place to ESRI WKT format.
11740 : *
11741 : * The value nodes of this coordinate system are modified in various manners
11742 : * more closely map onto the ESRI concept of WKT format. This includes
11743 : * renaming a variety of projections and arguments, and stripping out
11744 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11745 : *
11746 : * \note Since GDAL 3.0, this function has only user-visible effects at
11747 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11748 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11749 : *
11750 : * This does the same as the C function OSRMorphToESRI().
11751 : *
11752 : * @return OGRERR_NONE unless something goes badly wrong.
11753 : * @deprecated
11754 : */
11755 :
11756 235 : OGRErr OGRSpatialReference::morphToESRI()
11757 :
11758 : {
11759 235 : TAKE_OPTIONAL_LOCK();
11760 :
11761 235 : d->refreshProjObj();
11762 235 : d->setMorphToESRI(true);
11763 :
11764 470 : return OGRERR_NONE;
11765 : }
11766 :
11767 : /************************************************************************/
11768 : /* OSRMorphToESRI() */
11769 : /************************************************************************/
11770 :
11771 : /**
11772 : * \brief Convert in place to ESRI WKT format.
11773 : *
11774 : * This function is the same as the C++ method
11775 : * OGRSpatialReference::morphToESRI().
11776 : */
11777 71 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11778 :
11779 : {
11780 71 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11781 :
11782 71 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11783 : }
11784 :
11785 : /************************************************************************/
11786 : /* morphFromESRI() */
11787 : /************************************************************************/
11788 :
11789 : /**
11790 : * \brief Convert in place from ESRI WKT format.
11791 : *
11792 : * The value notes of this coordinate system are modified in various manners
11793 : * to adhere more closely to the WKT standard. This mostly involves
11794 : * translating a variety of ESRI names for projections, arguments and
11795 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11796 : * translation of EPSG to WKT for the CT specification.
11797 : *
11798 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11799 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11800 : * effect is to undo the effect of a potential prior call to morphToESRI().
11801 : *
11802 : * This does the same as the C function OSRMorphFromESRI().
11803 : *
11804 : * @return OGRERR_NONE unless something goes badly wrong.
11805 : * @deprecated
11806 : */
11807 :
11808 21 : OGRErr OGRSpatialReference::morphFromESRI()
11809 :
11810 : {
11811 21 : TAKE_OPTIONAL_LOCK();
11812 :
11813 21 : d->refreshProjObj();
11814 21 : d->setMorphToESRI(false);
11815 :
11816 42 : return OGRERR_NONE;
11817 : }
11818 :
11819 : /************************************************************************/
11820 : /* OSRMorphFromESRI() */
11821 : /************************************************************************/
11822 :
11823 : /**
11824 : * \brief Convert in place from ESRI WKT format.
11825 : *
11826 : * This function is the same as the C++ method
11827 : * OGRSpatialReference::morphFromESRI().
11828 : */
11829 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11830 :
11831 : {
11832 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11833 :
11834 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11835 : }
11836 :
11837 : /************************************************************************/
11838 : /* FindMatches() */
11839 : /************************************************************************/
11840 :
11841 : /**
11842 : * \brief Try to identify a match between the passed SRS and a related SRS
11843 : * in a catalog.
11844 : *
11845 : * Matching may be partial, or may fail.
11846 : * Returned entries will be sorted by decreasing match confidence (first
11847 : * entry has the highest match confidence).
11848 : *
11849 : * The exact way matching is done may change in future versions. Starting with
11850 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11851 : *
11852 : * This method is the same as OSRFindMatches().
11853 : *
11854 : * @param papszOptions NULL terminated list of options or NULL
11855 : * @param pnEntries Output parameter. Number of values in the returned array.
11856 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11857 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11858 : * indicate the confidence in the match. 100 is the highest confidence level.
11859 : * The array must be freed with CPLFree().
11860 : *
11861 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11862 : * with OSRFreeSRSArray()
11863 : *
11864 : * @since GDAL 2.3
11865 : *
11866 : * @see OGRSpatialReference::FindBestMatch()
11867 : */
11868 : OGRSpatialReferenceH *
11869 1427 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11870 : int **ppanMatchConfidence) const
11871 : {
11872 2854 : TAKE_OPTIONAL_LOCK();
11873 :
11874 1427 : CPL_IGNORE_RET_VAL(papszOptions);
11875 :
11876 1427 : if (pnEntries)
11877 1427 : *pnEntries = 0;
11878 1427 : if (ppanMatchConfidence)
11879 1427 : *ppanMatchConfidence = nullptr;
11880 :
11881 1427 : d->refreshProjObj();
11882 1427 : if (!d->m_pj_crs)
11883 0 : return nullptr;
11884 :
11885 1427 : int *panConfidence = nullptr;
11886 1427 : auto ctxt = d->getPROJContext();
11887 : auto list =
11888 1427 : proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11889 1427 : if (!list)
11890 0 : return nullptr;
11891 :
11892 1427 : const int nMatches = proj_list_get_count(list);
11893 :
11894 1427 : if (pnEntries)
11895 1427 : *pnEntries = static_cast<int>(nMatches);
11896 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11897 1427 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11898 1427 : if (ppanMatchConfidence)
11899 : {
11900 1427 : *ppanMatchConfidence =
11901 1427 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11902 : }
11903 :
11904 1427 : bool bSortAgain = false;
11905 :
11906 4245 : for (int i = 0; i < nMatches; i++)
11907 : {
11908 2818 : PJ *obj = proj_list_get(ctxt, list, i);
11909 2818 : CPLAssert(obj);
11910 2818 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11911 2818 : poSRS->d->setPjCRS(obj);
11912 2818 : pahRet[i] = ToHandle(poSRS);
11913 :
11914 : // Identify matches that only differ by axis order
11915 9 : if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11916 2836 : poSRS->GetAxesCount() == 2 &&
11917 2827 : GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11918 : {
11919 9 : OGRAxisOrientation eThisAxis0 = OAO_Other;
11920 9 : OGRAxisOrientation eThisAxis1 = OAO_Other;
11921 9 : OGRAxisOrientation eSRSAxis0 = OAO_Other;
11922 9 : OGRAxisOrientation eSRSAxis1 = OAO_Other;
11923 9 : GetAxis(nullptr, 0, &eThisAxis0);
11924 9 : GetAxis(nullptr, 1, &eThisAxis1);
11925 9 : poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11926 9 : poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11927 9 : if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11928 9 : eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11929 : {
11930 : auto pj_crs_normalized =
11931 9 : proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11932 9 : if (pj_crs_normalized)
11933 : {
11934 9 : if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11935 9 : PJ_COMP_EQUIVALENT))
11936 : {
11937 3 : bSortAgain = true;
11938 3 : panConfidence[i] = 90;
11939 3 : poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11940 : }
11941 9 : proj_destroy(pj_crs_normalized);
11942 : }
11943 : }
11944 : }
11945 :
11946 2818 : if (ppanMatchConfidence)
11947 2818 : (*ppanMatchConfidence)[i] = panConfidence[i];
11948 : }
11949 :
11950 1427 : if (bSortAgain)
11951 : {
11952 3 : std::vector<int> anIndices;
11953 12 : for (int i = 0; i < nMatches; ++i)
11954 9 : anIndices.push_back(i);
11955 :
11956 3 : std::stable_sort(anIndices.begin(), anIndices.end(),
11957 9 : [&panConfidence](int i, int j)
11958 9 : { return panConfidence[i] > panConfidence[j]; });
11959 :
11960 : OGRSpatialReferenceH *pahRetSorted =
11961 : static_cast<OGRSpatialReferenceH *>(
11962 3 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11963 12 : for (int i = 0; i < nMatches; ++i)
11964 : {
11965 9 : pahRetSorted[i] = pahRet[anIndices[i]];
11966 9 : if (ppanMatchConfidence)
11967 9 : (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11968 : }
11969 3 : CPLFree(pahRet);
11970 3 : pahRet = pahRetSorted;
11971 : }
11972 :
11973 1427 : pahRet[nMatches] = nullptr;
11974 1427 : proj_list_destroy(list);
11975 1427 : proj_int_list_destroy(panConfidence);
11976 :
11977 1427 : return pahRet;
11978 : }
11979 :
11980 : /************************************************************************/
11981 : /* importFromEPSGA() */
11982 : /************************************************************************/
11983 :
11984 : /**
11985 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11986 : * code.
11987 : *
11988 : * This method will initialize the spatial reference based on the
11989 : * passed in EPSG CRS code found in the PROJ database.
11990 : *
11991 : * Since GDAL 3.0, this method is identical to importFromEPSG().
11992 : *
11993 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11994 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11995 : * such method available for the CRS. This behavior might not always be
11996 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11997 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11998 : * The AddGuessedTOWGS84() method can also be used for that purpose.
11999 : *
12000 : * The method will also by default substitute a deprecated EPSG code by its
12001 : * non-deprecated replacement. If this behavior is not desired, the
12002 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12003 : *
12004 : * This method is the same as the C function OSRImportFromEPSGA().
12005 : *
12006 : * @param nCode a CRS code.
12007 : *
12008 : * @return OGRERR_NONE on success, or an error code on failure.
12009 : */
12010 :
12011 42974 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12012 :
12013 : {
12014 85948 : TAKE_OPTIONAL_LOCK();
12015 :
12016 42974 : Clear();
12017 :
12018 : const char *pszUseNonDeprecated =
12019 42975 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12020 : const bool bUseNonDeprecated =
12021 42975 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12022 42973 : const bool bAddTOWGS84 = CPLTestBool(
12023 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12024 42975 : auto tlsCache = OSRGetProjTLSCache();
12025 42975 : if (tlsCache)
12026 : {
12027 : auto cachedObj =
12028 42975 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12029 42974 : if (cachedObj)
12030 : {
12031 32848 : d->setPjCRS(cachedObj);
12032 32848 : return OGRERR_NONE;
12033 : }
12034 : }
12035 :
12036 20253 : CPLString osCode;
12037 10127 : osCode.Printf("%d", nCode);
12038 : PJ *obj;
12039 10126 : constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12040 10126 : if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12041 : {
12042 10121 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12043 : osCode.c_str(), PJ_CATEGORY_CRS, true,
12044 : nullptr);
12045 10121 : if (!obj)
12046 : {
12047 23 : return OGRERR_FAILURE;
12048 : }
12049 : }
12050 : else
12051 : {
12052 : // Likely to be an ESRI CRS...
12053 5 : CPLErr eLastErrorType = CE_None;
12054 5 : CPLErrorNum eLastErrorNum = CPLE_None;
12055 5 : std::string osLastErrorMsg;
12056 5 : bool bIsESRI = false;
12057 : {
12058 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12059 5 : CPLErrorReset();
12060 5 : obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12061 : osCode.c_str(), PJ_CATEGORY_CRS,
12062 : true, nullptr);
12063 5 : if (!obj)
12064 : {
12065 2 : eLastErrorType = CPLGetLastErrorType();
12066 2 : eLastErrorNum = CPLGetLastErrorNo();
12067 2 : osLastErrorMsg = CPLGetLastErrorMsg();
12068 2 : obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12069 : osCode.c_str(), PJ_CATEGORY_CRS,
12070 : true, nullptr);
12071 2 : if (obj)
12072 1 : bIsESRI = true;
12073 : }
12074 : }
12075 5 : if (!obj)
12076 : {
12077 1 : if (eLastErrorType != CE_None)
12078 1 : CPLError(eLastErrorType, eLastErrorNum, "%s",
12079 : osLastErrorMsg.c_str());
12080 1 : return OGRERR_FAILURE;
12081 : }
12082 4 : if (bIsESRI)
12083 : {
12084 1 : CPLError(CE_Warning, CPLE_AppDefined,
12085 : "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12086 : "Assuming ESRI:%d was meant",
12087 : nCode, nCode, nCode);
12088 : }
12089 : }
12090 :
12091 10102 : if (bUseNonDeprecated && proj_is_deprecated(obj))
12092 : {
12093 410 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12094 410 : if (list)
12095 : {
12096 410 : const auto count = proj_list_get_count(list);
12097 410 : if (count == 1)
12098 : {
12099 : auto nonDeprecated =
12100 359 : proj_list_get(d->getPROJContext(), list, 0);
12101 359 : if (nonDeprecated)
12102 : {
12103 359 : if (pszUseNonDeprecated == nullptr)
12104 : {
12105 : const char *pszNewAuth =
12106 359 : proj_get_id_auth_name(nonDeprecated, 0);
12107 : const char *pszNewCode =
12108 359 : proj_get_id_code(nonDeprecated, 0);
12109 359 : CPLError(CE_Warning, CPLE_AppDefined,
12110 : "CRS EPSG:%d is deprecated. "
12111 : "Its non-deprecated replacement %s:%s "
12112 : "will be used instead. "
12113 : "To use the original CRS, set the "
12114 : "OSR_USE_NON_DEPRECATED "
12115 : "configuration option to NO.",
12116 : nCode, pszNewAuth ? pszNewAuth : "(null)",
12117 : pszNewCode ? pszNewCode : "(null)");
12118 : }
12119 359 : proj_destroy(obj);
12120 359 : obj = nonDeprecated;
12121 : }
12122 : }
12123 : }
12124 410 : proj_list_destroy(list);
12125 : }
12126 :
12127 10102 : if (bAddTOWGS84)
12128 : {
12129 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12130 : obj, nullptr);
12131 1 : if (boundCRS)
12132 : {
12133 1 : proj_destroy(obj);
12134 1 : obj = boundCRS;
12135 : }
12136 : }
12137 :
12138 10102 : d->setPjCRS(obj);
12139 :
12140 10103 : if (tlsCache)
12141 : {
12142 10103 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12143 : obj);
12144 : }
12145 :
12146 10103 : return OGRERR_NONE;
12147 : }
12148 :
12149 : /************************************************************************/
12150 : /* AddGuessedTOWGS84() */
12151 : /************************************************************************/
12152 :
12153 : /**
12154 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12155 : * to WGS84.
12156 : *
12157 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12158 : * to WGS84 when there is one and only one such method available for the CRS.
12159 : * Note: this is more restrictive to how GDAL < 3 worked.
12160 : *
12161 : * This method is the same as the C function OSRAddGuessedTOWGS84().
12162 : *
12163 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12164 : * already a transformation to WGS84 or none matching could be found).
12165 : *
12166 : * @since GDAL 3.0.3
12167 : */
12168 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12169 : {
12170 36 : TAKE_OPTIONAL_LOCK();
12171 :
12172 18 : d->refreshProjObj();
12173 18 : if (!d->m_pj_crs)
12174 0 : return OGRERR_FAILURE;
12175 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12176 18 : d->getPROJContext(), d->m_pj_crs, false, true);
12177 18 : if (!boundCRS)
12178 : {
12179 0 : return OGRERR_FAILURE;
12180 : }
12181 18 : d->setPjCRS(boundCRS);
12182 18 : return OGRERR_NONE;
12183 : }
12184 :
12185 : /************************************************************************/
12186 : /* OSRImportFromEPSGA() */
12187 : /************************************************************************/
12188 :
12189 : /**
12190 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
12191 : * to WGS84.
12192 : *
12193 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12194 : *
12195 : * @since GDAL 3.0.3
12196 : */
12197 :
12198 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12199 :
12200 : {
12201 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12202 :
12203 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12204 : }
12205 :
12206 : /************************************************************************/
12207 : /* OSRImportFromEPSGA() */
12208 : /************************************************************************/
12209 :
12210 : /**
12211 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12212 : * code.
12213 : *
12214 : * This function is the same as OGRSpatialReference::importFromEPSGA().
12215 : */
12216 :
12217 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12218 :
12219 : {
12220 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12221 :
12222 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12223 : }
12224 :
12225 : /************************************************************************/
12226 : /* importFromEPSG() */
12227 : /************************************************************************/
12228 :
12229 : /**
12230 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12231 : * code.
12232 : *
12233 : * This method will initialize the spatial reference based on the
12234 : * passed in EPSG CRS code found in the PROJ database.
12235 : *
12236 : * This method is the same as the C function OSRImportFromEPSG().
12237 : *
12238 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12239 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
12240 : * such method available for the CRS. This behavior might not always be
12241 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12242 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12243 : *
12244 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12245 : *
12246 : * @return OGRERR_NONE on success, or an error code on failure.
12247 : */
12248 :
12249 38085 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12250 :
12251 : {
12252 38085 : return importFromEPSGA(nCode);
12253 : }
12254 :
12255 : /************************************************************************/
12256 : /* OSRImportFromEPSG() */
12257 : /************************************************************************/
12258 :
12259 : /**
12260 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
12261 : * code.
12262 : *
12263 : * This function is the same as OGRSpatialReference::importFromEPSG().
12264 : */
12265 :
12266 1456 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12267 :
12268 : {
12269 1456 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12270 :
12271 1456 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12272 : }
12273 :
12274 : /************************************************************************/
12275 : /* EPSGTreatsAsLatLong() */
12276 : /************************************************************************/
12277 :
12278 : /**
12279 : * \brief This method returns TRUE if this geographic coordinate
12280 : * system should be treated as having lat/long coordinate ordering.
12281 : *
12282 : * Currently this returns TRUE for all geographic coordinate systems
12283 : * with axes set defining it as lat, long (prior to GDAL 3.10, it
12284 : * also checked that the CRS had belonged to EPSG authority, but this check
12285 : * has now been removed).
12286 : *
12287 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12288 : * geographic CRS imported with importFromEPSG() would cause this method to
12289 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12290 : * is now equivalent to importFromEPSGA().
12291 : *
12292 : * FALSE will be returned for all coordinate systems that are not geographic,
12293 : * or whose axes ordering is not latitude, longitude.
12294 : *
12295 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
12296 : *
12297 : * @return TRUE or FALSE.
12298 : */
12299 :
12300 866 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
12301 :
12302 : {
12303 1732 : TAKE_OPTIONAL_LOCK();
12304 :
12305 866 : if (!IsGeographic())
12306 695 : return FALSE;
12307 :
12308 171 : d->demoteFromBoundCRS();
12309 :
12310 171 : bool ret = false;
12311 171 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12312 : {
12313 : auto horizCRS =
12314 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12315 3 : if (horizCRS)
12316 : {
12317 : auto cs =
12318 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12319 3 : if (cs)
12320 : {
12321 3 : const char *pszDirection = nullptr;
12322 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12323 : nullptr, &pszDirection, nullptr,
12324 3 : nullptr, nullptr, nullptr))
12325 : {
12326 3 : if (EQUAL(pszDirection, "north"))
12327 : {
12328 3 : ret = true;
12329 : }
12330 : }
12331 :
12332 3 : proj_destroy(cs);
12333 : }
12334 :
12335 3 : proj_destroy(horizCRS);
12336 : }
12337 : }
12338 : else
12339 : {
12340 : auto cs =
12341 168 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12342 168 : if (cs)
12343 : {
12344 168 : const char *pszDirection = nullptr;
12345 168 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12346 : nullptr, &pszDirection, nullptr, nullptr,
12347 168 : nullptr, nullptr))
12348 : {
12349 168 : if (EQUAL(pszDirection, "north"))
12350 : {
12351 119 : ret = true;
12352 : }
12353 : }
12354 :
12355 168 : proj_destroy(cs);
12356 : }
12357 : }
12358 171 : d->undoDemoteFromBoundCRS();
12359 :
12360 171 : return ret;
12361 : }
12362 :
12363 : /************************************************************************/
12364 : /* OSREPSGTreatsAsLatLong() */
12365 : /************************************************************************/
12366 :
12367 : /**
12368 : * \brief This function returns TRUE if this geographic coordinate
12369 : * system should be treated as having lat/long coordinate ordering.
12370 : *
12371 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12372 : */
12373 :
12374 180 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12375 :
12376 : {
12377 180 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12378 :
12379 180 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12380 : }
12381 :
12382 : /************************************************************************/
12383 : /* EPSGTreatsAsNorthingEasting() */
12384 : /************************************************************************/
12385 :
12386 : /**
12387 : * \brief This method returns TRUE if this projected coordinate
12388 : * system should be treated as having northing/easting coordinate ordering.
12389 : *
12390 : * Currently this returns TRUE for all projected coordinate systems
12391 : * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12392 : * also checked that the CRS had belonged to EPSG authority, but this check
12393 : * has now been removed).
12394 : *
12395 : * \note Important change of behavior since GDAL 3.0. In previous versions,
12396 : * projected CRS with northing, easting axis order imported with
12397 : * importFromEPSG() would cause this method to
12398 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12399 : * is now equivalent to importFromEPSGA().
12400 : *
12401 : * FALSE will be returned for all coordinate systems that are not projected,
12402 : * or whose axes ordering is not northing, easting.
12403 : *
12404 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12405 : *
12406 : * @return TRUE or FALSE.
12407 : *
12408 : * @since OGR 1.10.0
12409 : */
12410 :
12411 754 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12412 :
12413 : {
12414 1508 : TAKE_OPTIONAL_LOCK();
12415 :
12416 754 : if (!IsProjected())
12417 46 : return FALSE;
12418 :
12419 708 : d->demoteFromBoundCRS();
12420 : PJ *projCRS;
12421 708 : const auto ctxt = d->getPROJContext();
12422 708 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12423 : {
12424 4 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12425 4 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12426 : {
12427 0 : d->undoDemoteFromBoundCRS();
12428 0 : proj_destroy(projCRS);
12429 0 : return FALSE;
12430 : }
12431 : }
12432 : else
12433 : {
12434 704 : projCRS = proj_clone(ctxt, d->m_pj_crs);
12435 : }
12436 :
12437 708 : bool ret = false;
12438 708 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12439 708 : proj_destroy(projCRS);
12440 708 : d->undoDemoteFromBoundCRS();
12441 :
12442 708 : if (cs)
12443 : {
12444 708 : ret = isNorthEastAxisOrder(ctxt, cs);
12445 708 : proj_destroy(cs);
12446 : }
12447 :
12448 708 : return ret;
12449 : }
12450 :
12451 : /************************************************************************/
12452 : /* OSREPSGTreatsAsNorthingEasting() */
12453 : /************************************************************************/
12454 :
12455 : /**
12456 : * \brief This function returns TRUE if this projected coordinate
12457 : * system should be treated as having northing/easting coordinate ordering.
12458 : *
12459 : * This function is the same as
12460 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12461 : *
12462 : * @since OGR 1.10.0
12463 : */
12464 :
12465 187 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12466 :
12467 : {
12468 187 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12469 :
12470 187 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12471 : }
12472 :
12473 : /************************************************************************/
12474 : /* ImportFromESRIWisconsinWKT() */
12475 : /* */
12476 : /* Search a ESRI State Plane WKT and import it. */
12477 : /************************************************************************/
12478 :
12479 : // This is only used by the HFA driver and somewhat dubious we really need that
12480 : // Coming from an old ESRI merge
12481 :
12482 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12483 : double centralMeridian,
12484 : double latOfOrigin,
12485 : const char *unitsName,
12486 : const char *crsName)
12487 : {
12488 2 : TAKE_OPTIONAL_LOCK();
12489 :
12490 1 : if (centralMeridian < -93 || centralMeridian > -87)
12491 0 : return OGRERR_FAILURE;
12492 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
12493 0 : return OGRERR_FAILURE;
12494 :
12495 : // If the CS name is known.
12496 1 : if (!prjName && !unitsName && crsName)
12497 : {
12498 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12499 0 : PJ_OBJ_LIST *list = proj_create_from_name(
12500 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12501 0 : if (list)
12502 : {
12503 0 : if (proj_list_get_count(list) == 1)
12504 : {
12505 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12506 0 : if (crs)
12507 : {
12508 0 : Clear();
12509 0 : d->setPjCRS(crs);
12510 0 : proj_list_destroy(list);
12511 0 : return OGRERR_NONE;
12512 : }
12513 : }
12514 0 : proj_list_destroy(list);
12515 : }
12516 0 : return OGRERR_FAILURE;
12517 : }
12518 :
12519 1 : if (prjName == nullptr || unitsName == nullptr)
12520 : {
12521 0 : return OGRERR_FAILURE;
12522 : }
12523 :
12524 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12525 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12526 : "NAD_1983_HARN_WISCRS_", &type, 1,
12527 : true, 0, nullptr);
12528 1 : if (list)
12529 : {
12530 1 : const auto listSize = proj_list_get_count(list);
12531 8 : for (int i = 0; i < listSize; i++)
12532 : {
12533 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
12534 8 : if (!crs)
12535 : {
12536 7 : continue;
12537 : }
12538 :
12539 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12540 8 : if (!conv)
12541 : {
12542 0 : proj_destroy(crs);
12543 0 : continue;
12544 : }
12545 8 : const char *pszMethodCode = nullptr;
12546 8 : proj_coordoperation_get_method_info(
12547 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12548 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12549 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12550 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12551 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12552 : nMethodCode ==
12553 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12554 : {
12555 3 : proj_destroy(crs);
12556 3 : proj_destroy(conv);
12557 3 : continue;
12558 : }
12559 :
12560 : auto coordSys =
12561 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12562 5 : if (!coordSys)
12563 : {
12564 0 : proj_destroy(crs);
12565 0 : proj_destroy(conv);
12566 0 : continue;
12567 : }
12568 :
12569 5 : double dfConvFactor = 0.0;
12570 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12571 : nullptr, nullptr, &dfConvFactor, nullptr,
12572 : nullptr, nullptr);
12573 5 : proj_destroy(coordSys);
12574 :
12575 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12576 1 : (!EQUAL(unitsName, "meters") &&
12577 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12578 : 1e-10))
12579 : {
12580 4 : proj_destroy(crs);
12581 4 : proj_destroy(conv);
12582 4 : continue;
12583 : }
12584 :
12585 1 : int idx_lat = proj_coordoperation_get_param_index(
12586 : d->getPROJContext(), conv,
12587 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12588 1 : double valueLat = -1000;
12589 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12590 : nullptr, nullptr, nullptr, &valueLat,
12591 : nullptr, nullptr, nullptr, nullptr,
12592 : nullptr, nullptr);
12593 1 : int idx_lon = proj_coordoperation_get_param_index(
12594 : d->getPROJContext(), conv,
12595 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12596 1 : double valueLong = -1000;
12597 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12598 : nullptr, nullptr, nullptr, &valueLong,
12599 : nullptr, nullptr, nullptr, nullptr,
12600 : nullptr, nullptr);
12601 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12602 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
12603 : {
12604 1 : Clear();
12605 1 : d->setPjCRS(crs);
12606 1 : proj_list_destroy(list);
12607 1 : proj_destroy(conv);
12608 1 : return OGRERR_NONE;
12609 : }
12610 :
12611 0 : proj_destroy(crs);
12612 0 : proj_destroy(conv);
12613 : }
12614 0 : proj_list_destroy(list);
12615 : }
12616 :
12617 0 : return OGRERR_FAILURE;
12618 : }
12619 :
12620 : /************************************************************************/
12621 : /* GetAxisMappingStrategy() */
12622 : /************************************************************************/
12623 :
12624 : /** \brief Return the data axis to CRS axis mapping strategy.
12625 : *
12626 : * <ul>
12627 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12628 : * lat/long order, the data will still be long/lat ordered. Similarly for
12629 : * a projected CRS with northing/easting order, the data will still be
12630 : * easting/northing ordered.
12631 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12632 : * the CRS axis.
12633 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12634 : * SetDataAxisToSRSAxisMapping()
12635 : * </ul>
12636 : * @return the data axis to CRS axis mapping strategy.
12637 : * @since GDAL 3.0
12638 : */
12639 72 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12640 : {
12641 72 : TAKE_OPTIONAL_LOCK();
12642 :
12643 144 : return d->m_axisMappingStrategy;
12644 : }
12645 :
12646 : /************************************************************************/
12647 : /* OSRGetAxisMappingStrategy() */
12648 : /************************************************************************/
12649 :
12650 : /** \brief Return the data axis to CRS axis mapping strategy.
12651 : *
12652 : * See OGRSpatialReference::GetAxisMappingStrategy()
12653 : * @since GDAL 3.0
12654 : */
12655 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12656 : {
12657 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12658 :
12659 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12660 : }
12661 :
12662 : /************************************************************************/
12663 : /* SetAxisMappingStrategy() */
12664 : /************************************************************************/
12665 :
12666 : /** \brief Set the data axis to CRS axis mapping strategy.
12667 : *
12668 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12669 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12670 : * later being the default value when the option is not set) to control the
12671 : * value of the data axis to CRS axis mapping strategy when a
12672 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12673 : * override this default value.
12674 : *
12675 : * See OGRSpatialReference::GetAxisMappingStrategy()
12676 : * @since GDAL 3.0
12677 : */
12678 85681 : void OGRSpatialReference::SetAxisMappingStrategy(
12679 : OSRAxisMappingStrategy strategy)
12680 : {
12681 171142 : TAKE_OPTIONAL_LOCK();
12682 :
12683 85534 : d->m_axisMappingStrategy = strategy;
12684 85551 : d->refreshAxisMapping();
12685 85446 : }
12686 :
12687 : /************************************************************************/
12688 : /* OSRSetAxisMappingStrategy() */
12689 : /************************************************************************/
12690 :
12691 : /** \brief Set the data axis to CRS axis mapping strategy.
12692 : *
12693 : * See OGRSpatialReference::SetAxisMappingStrategy()
12694 : * @since GDAL 3.0
12695 : */
12696 788 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12697 : OSRAxisMappingStrategy strategy)
12698 : {
12699 788 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12700 :
12701 788 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12702 : }
12703 :
12704 : /************************************************************************/
12705 : /* GetDataAxisToSRSAxisMapping() */
12706 : /************************************************************************/
12707 :
12708 : /** \brief Return the data axis to SRS axis mapping.
12709 : *
12710 : * The number of elements of the vector will be the number of axis of the CRS.
12711 : * Values start at 1.
12712 : *
12713 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12714 : * for the first axis of the CRS.
12715 : *
12716 : * @since GDAL 3.0
12717 : */
12718 4196110 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12719 : {
12720 4196110 : TAKE_OPTIONAL_LOCK();
12721 :
12722 8392110 : return d->m_axisMapping;
12723 : }
12724 :
12725 : /************************************************************************/
12726 : /* OSRGetDataAxisToSRSAxisMapping() */
12727 : /************************************************************************/
12728 :
12729 : /** \brief Return the data axis to SRS axis mapping.
12730 : *
12731 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12732 : *
12733 : * @since GDAL 3.0
12734 : */
12735 214 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12736 : int *pnCount)
12737 : {
12738 214 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12739 214 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12740 :
12741 : const auto &v =
12742 214 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12743 214 : *pnCount = static_cast<int>(v.size());
12744 214 : return v.data();
12745 : }
12746 :
12747 : /************************************************************************/
12748 : /* SetDataAxisToSRSAxisMapping() */
12749 : /************************************************************************/
12750 :
12751 : /** \brief Set a custom data axis to CRS axis mapping.
12752 : *
12753 : * The number of elements of the mapping vector should be the number of axis
12754 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12755 : * check that, beyond checking there are at least 2 elements, so that this
12756 : * method and setting the CRS can be done in any order).
12757 : * This is taken into account by OGRCoordinateTransformation to transform the
12758 : * order of coordinates to the order expected by the CRS before
12759 : * transformation, and back to the data order after transformation.
12760 : *
12761 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12762 : * axis of the CRS. A negative value can also be used to ask for a sign
12763 : * reversal during coordinate transformation (to deal with northing vs southing,
12764 : * easting vs westing, heights vs depths).
12765 : *
12766 : * When used with OGRCoordinateTransformation,
12767 : * - the only valid values for mapping[0] (data axis number for the first axis
12768 : * of the CRS) are 1, 2, -1, -2.
12769 : * - the only valid values for mapping[1] (data axis number for the second axis
12770 : * of the CRS) are 1, 2, -1, -2.
12771 : * - the only valid values mapping[2] are 3 or -3.
12772 : * Note: this method does not validate the values of mapping[].
12773 : *
12774 : * mapping=[2,1] typically expresses the inversion of axis between the data
12775 : * axis and the CRS axis for a 2D CRS.
12776 : *
12777 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12778 : *
12779 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12780 : *
12781 : * @param mapping The new data axis to CRS axis mapping.
12782 : *
12783 : * @since GDAL 3.0
12784 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12785 : */
12786 8267 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12787 : const std::vector<int> &mapping)
12788 : {
12789 16534 : TAKE_OPTIONAL_LOCK();
12790 :
12791 8267 : if (mapping.size() < 2)
12792 0 : return OGRERR_FAILURE;
12793 8267 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12794 8267 : d->m_axisMapping = mapping;
12795 8267 : return OGRERR_NONE;
12796 : }
12797 :
12798 : /************************************************************************/
12799 : /* OSRSetDataAxisToSRSAxisMapping() */
12800 : /************************************************************************/
12801 :
12802 : /** \brief Set a custom data axis to CRS axis mapping.
12803 : *
12804 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12805 : *
12806 : * This is the same as the C++ method
12807 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12808 : *
12809 : * @since GDAL 3.1
12810 : */
12811 15 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12812 : int nMappingSize, const int *panMapping)
12813 : {
12814 15 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12815 15 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12816 : OGRERR_FAILURE);
12817 :
12818 15 : if (nMappingSize < 0)
12819 0 : return OGRERR_FAILURE;
12820 :
12821 30 : std::vector<int> mapping(nMappingSize);
12822 15 : if (nMappingSize)
12823 15 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12824 15 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12825 15 : mapping);
12826 : }
12827 :
12828 : /************************************************************************/
12829 : /* GetAreaOfUse() */
12830 : /************************************************************************/
12831 :
12832 : /** \brief Return the area of use of the CRS.
12833 : *
12834 : * This method is the same as the OSRGetAreaOfUse() function.
12835 : *
12836 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12837 : * longitude, expressed in degree. Might be NULL. If the returned value is
12838 : * -1000, the bounding box is unknown.
12839 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12840 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12841 : * the bounding box is unknown.
12842 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12843 : * longitude, expressed in degree. Might be NULL. If the returned value is
12844 : * -1000, the bounding box is unknown.
12845 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12846 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12847 : * the bounding box is unknown.
12848 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12849 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12850 : * invalidated by further calls.
12851 : * @return true in case of success
12852 : * @since GDAL 3.0
12853 : */
12854 32 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12855 : double *pdfSouthLatitudeDeg,
12856 : double *pdfEastLongitudeDeg,
12857 : double *pdfNorthLatitudeDeg,
12858 : const char **ppszAreaName) const
12859 : {
12860 64 : TAKE_OPTIONAL_LOCK();
12861 :
12862 32 : d->refreshProjObj();
12863 32 : if (!d->m_pj_crs)
12864 : {
12865 0 : return false;
12866 : }
12867 32 : d->demoteFromBoundCRS();
12868 32 : const char *pszAreaName = nullptr;
12869 32 : int bSuccess = proj_get_area_of_use(
12870 32 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12871 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12872 : &pszAreaName);
12873 32 : d->undoDemoteFromBoundCRS();
12874 32 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12875 32 : if (ppszAreaName)
12876 1 : *ppszAreaName = d->m_osAreaName.c_str();
12877 32 : return CPL_TO_BOOL(bSuccess);
12878 : }
12879 :
12880 : /************************************************************************/
12881 : /* GetAreaOfUse() */
12882 : /************************************************************************/
12883 :
12884 : /** \brief Return the area of use of the CRS.
12885 : *
12886 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12887 : *
12888 : * @since GDAL 3.0
12889 : */
12890 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12891 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12892 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12893 : {
12894 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12895 :
12896 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12897 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12898 1 : pdfNorthLatitudeDeg, ppszAreaName);
12899 : }
12900 :
12901 : /************************************************************************/
12902 : /* OSRGetCRSInfoListFromDatabase() */
12903 : /************************************************************************/
12904 :
12905 : /** \brief Enumerate CRS objects from the database.
12906 : *
12907 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12908 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12909 : *
12910 : * @param pszAuthName Authority name, used to restrict the search.
12911 : * Or NULL for all authorities.
12912 : * @param params Additional criteria. Must be set to NULL for now.
12913 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12914 : * the size of the result list. Might be NULL
12915 : * @return an array of OSRCRSInfo* pointers to be freed with
12916 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12917 : *
12918 : * @since GDAL 3.0
12919 : */
12920 : OSRCRSInfo **
12921 24 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12922 : CPL_UNUSED const OSRCRSListParameters *params,
12923 : int *pnOutResultCount)
12924 : {
12925 24 : int nResultCount = 0;
12926 24 : auto projList = proj_get_crs_info_list_from_database(
12927 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12928 24 : if (pnOutResultCount)
12929 24 : *pnOutResultCount = nResultCount;
12930 24 : if (!projList)
12931 : {
12932 0 : return nullptr;
12933 : }
12934 24 : auto res = new OSRCRSInfo *[nResultCount + 1];
12935 89181 : for (int i = 0; i < nResultCount; i++)
12936 : {
12937 89157 : res[i] = new OSRCRSInfo;
12938 178314 : res[i]->pszAuthName = projList[i]->auth_name
12939 89157 : ? CPLStrdup(projList[i]->auth_name)
12940 : : nullptr;
12941 89157 : res[i]->pszCode =
12942 89157 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12943 89157 : res[i]->pszName =
12944 89157 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12945 89157 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12946 89157 : switch (projList[i]->type)
12947 : {
12948 8727 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12949 8727 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12950 8727 : break;
12951 2811 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12952 2811 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12953 2811 : break;
12954 3066 : case PJ_TYPE_GEOCENTRIC_CRS:
12955 3066 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12956 3066 : break;
12957 67740 : case PJ_TYPE_PROJECTED_CRS:
12958 67740 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12959 67740 : break;
12960 2808 : case PJ_TYPE_VERTICAL_CRS:
12961 2808 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12962 2808 : break;
12963 4005 : case PJ_TYPE_COMPOUND_CRS:
12964 4005 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12965 4005 : break;
12966 0 : default:
12967 0 : break;
12968 : }
12969 89157 : res[i]->bDeprecated = projList[i]->deprecated;
12970 89157 : res[i]->bBboxValid = projList[i]->bbox_valid;
12971 89157 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12972 89157 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12973 89157 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12974 89157 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12975 178314 : res[i]->pszAreaName = projList[i]->area_name
12976 89157 : ? CPLStrdup(projList[i]->area_name)
12977 : : nullptr;
12978 89157 : res[i]->pszProjectionMethod =
12979 89157 : projList[i]->projection_method_name
12980 89157 : ? CPLStrdup(projList[i]->projection_method_name)
12981 : : nullptr;
12982 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
12983 : res[i]->pszCelestialBodyName =
12984 : projList[i]->celestial_body_name
12985 : ? CPLStrdup(projList[i]->celestial_body_name)
12986 : : nullptr;
12987 : #else
12988 89157 : res[i]->pszCelestialBodyName =
12989 89157 : res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
12990 178314 : ? CPLStrdup("Earth")
12991 : : nullptr;
12992 : #endif
12993 : }
12994 24 : res[nResultCount] = nullptr;
12995 24 : proj_crs_info_list_destroy(projList);
12996 24 : return res;
12997 : }
12998 :
12999 : /************************************************************************/
13000 : /* OSRDestroyCRSInfoList() */
13001 : /************************************************************************/
13002 :
13003 : /** \brief Destroy the result returned by
13004 : * OSRGetCRSInfoListFromDatabase().
13005 : *
13006 : * @since GDAL 3.0
13007 : */
13008 24 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13009 : {
13010 24 : if (list)
13011 : {
13012 89181 : for (int i = 0; list[i] != nullptr; i++)
13013 : {
13014 89157 : CPLFree(list[i]->pszAuthName);
13015 89157 : CPLFree(list[i]->pszCode);
13016 89157 : CPLFree(list[i]->pszName);
13017 89157 : CPLFree(list[i]->pszAreaName);
13018 89157 : CPLFree(list[i]->pszProjectionMethod);
13019 89157 : CPLFree(list[i]->pszCelestialBodyName);
13020 89157 : delete list[i];
13021 : }
13022 24 : delete[] list;
13023 : }
13024 24 : }
13025 :
13026 : /************************************************************************/
13027 : /* OSRGetAuthorityListFromDatabase() */
13028 : /************************************************************************/
13029 :
13030 : /** \brief Return the list of CRS authorities used in the PROJ database.
13031 : *
13032 : * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13033 : *
13034 : * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13035 : *
13036 : * @return nullptr in case of error, or a NULL terminated list of strings to
13037 : * free with CSLDestroy()
13038 : * @since GDAL 3.10
13039 : */
13040 4 : char **OSRGetAuthorityListFromDatabase()
13041 : {
13042 : PROJ_STRING_LIST list =
13043 4 : proj_get_authorities_from_database(OSRGetProjTLSContext());
13044 4 : if (!list)
13045 : {
13046 0 : return nullptr;
13047 : }
13048 4 : int count = 0;
13049 24 : while (list[count])
13050 20 : ++count;
13051 4 : char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13052 24 : for (int i = 0; i < count; ++i)
13053 20 : res[i] = CPLStrdup(list[i]);
13054 4 : proj_string_list_destroy(list);
13055 4 : return res;
13056 : }
13057 :
13058 : /************************************************************************/
13059 : /* UpdateCoordinateSystemFromGeogCRS() */
13060 : /************************************************************************/
13061 :
13062 : /*! @cond Doxygen_Suppress */
13063 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13064 : *
13065 : * @since GDAL 3.1
13066 : */
13067 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13068 : {
13069 1 : TAKE_OPTIONAL_LOCK();
13070 :
13071 1 : d->refreshProjObj();
13072 1 : if (!d->m_pj_crs)
13073 0 : return;
13074 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13075 0 : return;
13076 1 : if (GetAxesCount() == 3)
13077 0 : return;
13078 1 : auto ctxt = d->getPROJContext();
13079 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13080 1 : if (!baseCRS)
13081 0 : return;
13082 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13083 1 : if (!baseCRSCS)
13084 : {
13085 0 : proj_destroy(baseCRS);
13086 0 : return;
13087 : }
13088 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13089 : {
13090 0 : proj_destroy(baseCRSCS);
13091 0 : proj_destroy(baseCRS);
13092 0 : return;
13093 : }
13094 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13095 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13096 : {
13097 0 : proj_destroy(baseCRSCS);
13098 0 : proj_destroy(baseCRS);
13099 0 : proj_destroy(projCS);
13100 0 : return;
13101 : }
13102 :
13103 : PJ_AXIS_DESCRIPTION axis[3];
13104 4 : for (int i = 0; i < 3; i++)
13105 : {
13106 3 : const char *name = nullptr;
13107 3 : const char *abbreviation = nullptr;
13108 3 : const char *direction = nullptr;
13109 3 : double unit_conv_factor = 0;
13110 3 : const char *unit_name = nullptr;
13111 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13112 : &abbreviation, &direction, &unit_conv_factor,
13113 : &unit_name, nullptr, nullptr);
13114 3 : axis[i].name = CPLStrdup(name);
13115 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
13116 3 : axis[i].direction = CPLStrdup(direction);
13117 3 : axis[i].unit_name = CPLStrdup(unit_name);
13118 3 : axis[i].unit_conv_factor = unit_conv_factor;
13119 3 : axis[i].unit_type = PJ_UT_LINEAR;
13120 : }
13121 1 : proj_destroy(baseCRSCS);
13122 1 : proj_destroy(projCS);
13123 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13124 4 : for (int i = 0; i < 3; i++)
13125 : {
13126 3 : CPLFree(axis[i].name);
13127 3 : CPLFree(axis[i].abbreviation);
13128 3 : CPLFree(axis[i].direction);
13129 3 : CPLFree(axis[i].unit_name);
13130 : }
13131 1 : if (!cs)
13132 : {
13133 0 : proj_destroy(baseCRS);
13134 0 : return;
13135 : }
13136 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13137 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13138 : conversion, cs);
13139 1 : proj_destroy(baseCRS);
13140 1 : proj_destroy(conversion);
13141 1 : proj_destroy(cs);
13142 1 : d->setPjCRS(crs);
13143 : }
13144 :
13145 : /*! @endcond */
13146 :
13147 : /************************************************************************/
13148 : /* PromoteTo3D() */
13149 : /************************************************************************/
13150 :
13151 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13152 : *
13153 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
13154 : * units.
13155 : *
13156 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13157 : * be used.
13158 : * @return OGRERR_NONE if no error occurred.
13159 : * @since GDAL 3.1 and PROJ 6.3
13160 : */
13161 42 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13162 : {
13163 84 : TAKE_OPTIONAL_LOCK();
13164 :
13165 42 : d->refreshProjObj();
13166 42 : if (!d->m_pj_crs)
13167 0 : return OGRERR_FAILURE;
13168 : auto newPj =
13169 42 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13170 42 : if (!newPj)
13171 0 : return OGRERR_FAILURE;
13172 42 : d->setPjCRS(newPj);
13173 42 : return OGRERR_NONE;
13174 : }
13175 :
13176 : /************************************************************************/
13177 : /* OSRPromoteTo3D() */
13178 : /************************************************************************/
13179 :
13180 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
13181 : *
13182 : * See OGRSpatialReference::PromoteTo3D()
13183 : *
13184 : * @since GDAL 3.1 and PROJ 6.3
13185 : */
13186 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13187 : {
13188 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13189 :
13190 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13191 : }
13192 :
13193 : /************************************************************************/
13194 : /* DemoteTo2D() */
13195 : /************************************************************************/
13196 :
13197 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13198 : *
13199 : * @param pszName New name for the CRS. If set to NULL, the previous name will
13200 : * be used.
13201 : * @return OGRERR_NONE if no error occurred.
13202 : * @since GDAL 3.2 and PROJ 6.3
13203 : */
13204 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13205 : {
13206 90 : TAKE_OPTIONAL_LOCK();
13207 :
13208 45 : d->refreshProjObj();
13209 45 : if (!d->m_pj_crs)
13210 0 : return OGRERR_FAILURE;
13211 : auto newPj =
13212 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13213 45 : if (!newPj)
13214 0 : return OGRERR_FAILURE;
13215 45 : d->setPjCRS(newPj);
13216 45 : return OGRERR_NONE;
13217 : }
13218 :
13219 : /************************************************************************/
13220 : /* OSRDemoteTo2D() */
13221 : /************************************************************************/
13222 :
13223 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
13224 : *
13225 : * See OGRSpatialReference::DemoteTo2D()
13226 : *
13227 : * @since GDAL 3.2 and PROJ 6.3
13228 : */
13229 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13230 : {
13231 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13232 :
13233 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13234 : }
13235 :
13236 : /************************************************************************/
13237 : /* GetEPSGGeogCS() */
13238 : /************************************************************************/
13239 :
13240 : /** Try to establish what the EPSG code for this coordinate systems
13241 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
13242 : *
13243 : * @return EPSG code
13244 : */
13245 :
13246 342 : int OGRSpatialReference::GetEPSGGeogCS() const
13247 :
13248 : {
13249 684 : TAKE_OPTIONAL_LOCK();
13250 :
13251 : /* -------------------------------------------------------------------- */
13252 : /* Check axis order. */
13253 : /* -------------------------------------------------------------------- */
13254 684 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13255 342 : if (!poGeogCRS)
13256 0 : return -1;
13257 :
13258 342 : bool ret = false;
13259 342 : poGeogCRS->d->demoteFromBoundCRS();
13260 342 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13261 342 : poGeogCRS->d->m_pj_crs);
13262 342 : poGeogCRS->d->undoDemoteFromBoundCRS();
13263 342 : if (cs)
13264 : {
13265 342 : const char *pszDirection = nullptr;
13266 342 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13267 : &pszDirection, nullptr, nullptr, nullptr,
13268 342 : nullptr))
13269 : {
13270 342 : if (EQUAL(pszDirection, "north"))
13271 : {
13272 143 : ret = true;
13273 : }
13274 : }
13275 :
13276 342 : proj_destroy(cs);
13277 : }
13278 342 : if (!ret)
13279 199 : return -1;
13280 :
13281 : /* -------------------------------------------------------------------- */
13282 : /* Do we already have it? */
13283 : /* -------------------------------------------------------------------- */
13284 143 : const char *pszAuthName = GetAuthorityName("GEOGCS");
13285 143 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13286 66 : return atoi(GetAuthorityCode("GEOGCS"));
13287 :
13288 : /* -------------------------------------------------------------------- */
13289 : /* Get the datum and geogcs names. */
13290 : /* -------------------------------------------------------------------- */
13291 :
13292 77 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
13293 77 : const char *pszDatum = GetAttrValue("DATUM");
13294 :
13295 : // We can only operate on coordinate systems with a geogcs.
13296 154 : OGRSpatialReference oSRSTmp;
13297 77 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13298 : {
13299 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13300 : // export to WKT1, so try to extract the geographic CRS through PROJ
13301 : // API with CopyGeogCSFrom() and get the nodes' values from it.
13302 1 : oSRSTmp.CopyGeogCSFrom(this);
13303 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13304 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
13305 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
13306 : {
13307 0 : return -1;
13308 : }
13309 : }
13310 :
13311 : // Lookup geographic CRS name
13312 77 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13313 77 : PJ_OBJ_LIST *list = proj_create_from_name(
13314 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13315 77 : if (list)
13316 : {
13317 77 : const auto listSize = proj_list_get_count(list);
13318 77 : if (listSize == 1)
13319 : {
13320 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
13321 49 : if (crs)
13322 : {
13323 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
13324 49 : const char *pszCode = proj_get_id_code(crs, 0);
13325 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13326 : {
13327 47 : const int nCode = atoi(pszCode);
13328 47 : proj_destroy(crs);
13329 47 : proj_list_destroy(list);
13330 47 : return nCode;
13331 : }
13332 2 : proj_destroy(crs);
13333 : }
13334 : }
13335 30 : proj_list_destroy(list);
13336 : }
13337 :
13338 : /* -------------------------------------------------------------------- */
13339 : /* Is this a "well known" geographic coordinate system? */
13340 : /* -------------------------------------------------------------------- */
13341 90 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13342 30 : strstr(pszDatum, "WGS") ||
13343 30 : strstr(pszGEOGCS, "World Geodetic System") ||
13344 30 : strstr(pszGEOGCS, "World_Geodetic_System") ||
13345 90 : strstr(pszDatum, "World Geodetic System") ||
13346 30 : strstr(pszDatum, "World_Geodetic_System");
13347 :
13348 90 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13349 30 : strstr(pszDatum, "NAD") ||
13350 30 : strstr(pszGEOGCS, "North American") ||
13351 30 : strstr(pszGEOGCS, "North_American") ||
13352 90 : strstr(pszDatum, "North American") ||
13353 30 : strstr(pszDatum, "North_American");
13354 :
13355 30 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13356 0 : return 4326;
13357 :
13358 30 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13359 0 : return 4322;
13360 :
13361 : // This is questionable as there are several 'flavors' of NAD83 that
13362 : // are not the same as 4269
13363 30 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13364 0 : return 4269;
13365 :
13366 30 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13367 0 : return 4267;
13368 :
13369 : /* -------------------------------------------------------------------- */
13370 : /* If we know the datum, associate the most likely GCS with */
13371 : /* it. */
13372 : /* -------------------------------------------------------------------- */
13373 30 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13374 30 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13375 30 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13376 0 : GetPrimeMeridian() == 0.0)
13377 : {
13378 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13379 :
13380 0 : if (nDatum >= 6000 && nDatum <= 6999)
13381 0 : return nDatum - 2000;
13382 : }
13383 :
13384 30 : return -1;
13385 : }
13386 :
13387 : /************************************************************************/
13388 : /* SetCoordinateEpoch() */
13389 : /************************************************************************/
13390 :
13391 : /** Set the coordinate epoch, as decimal year.
13392 : *
13393 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13394 : * change with time. To be unambiguous the coordinates must always be qualified
13395 : * with the epoch at which they are valid. The coordinate epoch is not
13396 : * necessarily the epoch at which the observation was collected.
13397 : *
13398 : * Pedantically the coordinate epoch of an observation belongs to the
13399 : * observation, and not to the CRS, however it is often more practical to
13400 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13401 : * CRS (see IsDynamic())
13402 : *
13403 : * This method is the same as the OSRSetCoordinateEpoch() function.
13404 : *
13405 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13406 : * @since OGR 3.4
13407 : */
13408 :
13409 831 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13410 : {
13411 831 : d->m_coordinateEpoch = dfCoordinateEpoch;
13412 831 : }
13413 :
13414 : /************************************************************************/
13415 : /* OSRSetCoordinateEpoch() */
13416 : /************************************************************************/
13417 :
13418 : /** \brief Set the coordinate epoch, as decimal year.
13419 : *
13420 : * See OGRSpatialReference::SetCoordinateEpoch()
13421 : *
13422 : * @since OGR 3.4
13423 : */
13424 31 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13425 : {
13426 31 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13427 :
13428 31 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13429 31 : dfCoordinateEpoch);
13430 : }
13431 :
13432 : /************************************************************************/
13433 : /* GetCoordinateEpoch() */
13434 : /************************************************************************/
13435 :
13436 : /** Return the coordinate epoch, as decimal year.
13437 : *
13438 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13439 : * change with time. To be unambiguous the coordinates must always be qualified
13440 : * with the epoch at which they are valid. The coordinate epoch is not
13441 : * necessarily the epoch at which the observation was collected.
13442 : *
13443 : * Pedantically the coordinate epoch of an observation belongs to the
13444 : * observation, and not to the CRS, however it is often more practical to
13445 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
13446 : * CRS (see IsDynamic())
13447 : *
13448 : * This method is the same as the OSRGetCoordinateEpoch() function.
13449 : *
13450 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13451 : * if not set, or relevant.
13452 : * @since OGR 3.4
13453 : */
13454 :
13455 12308 : double OGRSpatialReference::GetCoordinateEpoch() const
13456 : {
13457 12308 : return d->m_coordinateEpoch;
13458 : }
13459 :
13460 : /************************************************************************/
13461 : /* OSRGetCoordinateEpoch() */
13462 : /************************************************************************/
13463 :
13464 : /** \brief Get the coordinate epoch, as decimal year.
13465 : *
13466 : * See OGRSpatialReference::GetCoordinateEpoch()
13467 : *
13468 : * @since OGR 3.4
13469 : */
13470 665 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13471 : {
13472 665 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13473 :
13474 665 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13475 : }
|