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 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "ogr_spatialref.h"
32 :
33 : #include <cmath>
34 : #include <cstddef>
35 : #include <cstdio>
36 : #include <cstdlib>
37 : #include <cstring>
38 : #include <limits>
39 : #include <string>
40 : #include <mutex>
41 : #include <set>
42 : #include <vector>
43 :
44 : #include "cpl_atomic_ops.h"
45 : #include "cpl_conv.h"
46 : #include "cpl_csv.h"
47 : #include "cpl_error.h"
48 : #include "cpl_error_internal.h"
49 : #include "cpl_http.h"
50 : #include "cpl_json.h"
51 : #include "cpl_multiproc.h"
52 : #include "cpl_string.h"
53 : #include "cpl_vsi.h"
54 : #include "ogr_core.h"
55 : #include "ogr_p.h"
56 : #include "ogr_proj_p.h"
57 : #include "ogr_srs_api.h"
58 :
59 : #include "proj.h"
60 : #include "proj_experimental.h"
61 : #include "proj_constants.h"
62 :
63 : // Exists since 8.0.1
64 : #ifndef PROJ_AT_LEAST_VERSION
65 : #define PROJ_COMPUTE_VERSION(maj, min, patch) \
66 : ((maj)*10000 + (min)*100 + (patch))
67 : #define PROJ_VERSION_NUMBER \
68 : PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, \
69 : PROJ_VERSION_PATCH)
70 : #define PROJ_AT_LEAST_VERSION(maj, min, patch) \
71 : (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
72 : #endif
73 :
74 : #define STRINGIFY(s) #s
75 : #define XSTRINGIFY(s) STRINGIFY(s)
76 :
77 : struct OGRSpatialReference::Private
78 : {
79 : struct Listener : public OGR_SRSNode::Listener
80 : {
81 : OGRSpatialReference::Private *m_poObj = nullptr;
82 :
83 192776 : explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
84 : {
85 192773 : }
86 :
87 : Listener(const Listener &) = delete;
88 : Listener &operator=(const Listener &) = delete;
89 :
90 1645250 : void notifyChange(OGR_SRSNode *) override
91 : {
92 1645250 : m_poObj->nodesChanged();
93 1645250 : }
94 : };
95 :
96 : OGRSpatialReference *m_poSelf = nullptr;
97 : PJ *m_pj_crs = nullptr;
98 :
99 : // Temporary state used for object construction
100 : PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
101 : CPLString m_osPrimeMeridianName{};
102 : CPLString m_osAngularUnits{};
103 : CPLString m_osLinearUnits{};
104 : CPLString m_osAxisName[3]{};
105 :
106 : std::vector<std::string> m_wktImportWarnings{};
107 : std::vector<std::string> m_wktImportErrors{};
108 : CPLString m_osAreaName{};
109 :
110 : bool m_bNodesChanged = false;
111 : bool m_bNodesWKT2 = false;
112 : OGR_SRSNode *m_poRoot = nullptr;
113 :
114 : double dfFromGreenwich = 0.0;
115 : double dfToMeter = 0.0;
116 : double dfToDegrees = 0.0;
117 : double m_dfAngularUnitToRadian = 0.0;
118 :
119 : int nRefCount = 1;
120 : int bNormInfoSet = FALSE;
121 :
122 : PJ *m_pj_geod_base_crs_temp = nullptr;
123 : PJ *m_pj_proj_crs_cs_temp = nullptr;
124 :
125 : bool m_pj_crs_modified_during_demote = false;
126 : PJ *m_pj_bound_crs_target = nullptr;
127 : PJ *m_pj_bound_crs_co = nullptr;
128 : PJ *m_pj_crs_backup = nullptr;
129 : OGR_SRSNode *m_poRootBackup = nullptr;
130 :
131 : bool m_bMorphToESRI = false;
132 : bool m_bHasCenterLong = false;
133 :
134 : std::shared_ptr<Listener> m_poListener{};
135 :
136 : std::mutex m_mutex{};
137 :
138 : OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
139 : std::vector<int> m_axisMapping{1, 2, 3};
140 :
141 : double m_coordinateEpoch = 0; // as decimal year
142 :
143 : explicit Private(OGRSpatialReference *poSelf);
144 : ~Private();
145 : Private(const Private &) = delete;
146 : Private &operator=(const Private &) = delete;
147 :
148 : void clear();
149 : void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
150 : void setRoot(OGR_SRSNode *poRoot);
151 : void refreshProjObj();
152 : void nodesChanged();
153 : void refreshRootFromProjObj();
154 : void invalidateNodes();
155 :
156 : void setMorphToESRI(bool b);
157 :
158 : PJ *getGeodBaseCRS();
159 : PJ *getProjCRSCoordSys();
160 :
161 : const char *getProjCRSName();
162 : OGRErr replaceConversionAndUnref(PJ *conv);
163 :
164 : void demoteFromBoundCRS();
165 : void undoDemoteFromBoundCRS();
166 :
167 972868 : PJ_CONTEXT *getPROJContext()
168 : {
169 972868 : return OSRGetProjTLSContext();
170 : }
171 :
172 : const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
173 :
174 : void refreshAxisMapping();
175 : };
176 :
177 192781 : static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
178 : {
179 : const char *pszDefaultAMS =
180 192781 : CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
181 192793 : if (pszDefaultAMS)
182 : {
183 1 : if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
184 0 : return OAMS_AUTHORITY_COMPLIANT;
185 1 : else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
186 1 : return OAMS_TRADITIONAL_GIS_ORDER;
187 : else
188 : {
189 0 : CPLError(CE_Failure, CPLE_AppDefined,
190 : "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
191 : pszDefaultAMS);
192 : }
193 : }
194 192792 : return OAMS_AUTHORITY_COMPLIANT;
195 : }
196 :
197 192784 : OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
198 : : m_poSelf(poSelf),
199 192784 : m_poListener(std::shared_ptr<Listener>(new Listener(this)))
200 : {
201 : // Get the default value for m_axisMappingStrategy from the
202 : // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
203 192746 : m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
204 192794 : }
205 :
206 767855 : OGRSpatialReference::Private::~Private()
207 : {
208 : // In case we destroy the object not in the thread that created it,
209 : // we need to reassign the PROJ context. Having the context bundled inside
210 : // PJ* deeply sucks...
211 191972 : auto ctxt = getPROJContext();
212 :
213 191972 : proj_assign_context(m_pj_crs, ctxt);
214 191972 : proj_destroy(m_pj_crs);
215 :
216 191971 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
217 191971 : proj_destroy(m_pj_geod_base_crs_temp);
218 :
219 191971 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
220 191971 : proj_destroy(m_pj_proj_crs_cs_temp);
221 :
222 191971 : proj_assign_context(m_pj_bound_crs_target, ctxt);
223 191971 : proj_destroy(m_pj_bound_crs_target);
224 :
225 191971 : proj_assign_context(m_pj_bound_crs_co, ctxt);
226 191971 : proj_destroy(m_pj_bound_crs_co);
227 :
228 191970 : proj_assign_context(m_pj_crs_backup, ctxt);
229 191970 : proj_destroy(m_pj_crs_backup);
230 :
231 191970 : delete m_poRootBackup;
232 191970 : delete m_poRoot;
233 191963 : }
234 :
235 96188 : void OGRSpatialReference::Private::clear()
236 : {
237 96188 : proj_assign_context(m_pj_crs, getPROJContext());
238 96188 : proj_destroy(m_pj_crs);
239 96188 : m_pj_crs = nullptr;
240 :
241 96188 : delete m_poRoot;
242 96188 : m_poRoot = nullptr;
243 96188 : m_bNodesChanged = false;
244 :
245 96188 : m_wktImportWarnings.clear();
246 96188 : m_wktImportErrors.clear();
247 :
248 96188 : m_pj_crs_modified_during_demote = false;
249 96188 : m_pjType = PJ_TYPE_UNKNOWN;
250 96188 : m_osPrimeMeridianName.clear();
251 96188 : m_osAngularUnits.clear();
252 96188 : m_osLinearUnits.clear();
253 :
254 96188 : bNormInfoSet = FALSE;
255 96188 : dfFromGreenwich = 1.0;
256 96188 : dfToMeter = 1.0;
257 96188 : dfToDegrees = 1.0;
258 96188 : m_dfAngularUnitToRadian = 0.0;
259 :
260 96188 : m_bMorphToESRI = false;
261 96188 : m_bHasCenterLong = false;
262 :
263 96188 : m_coordinateEpoch = 0.0;
264 96188 : }
265 :
266 21020 : void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
267 : {
268 21020 : m_poRoot = poRoot;
269 21020 : if (m_poRoot)
270 : {
271 21020 : m_poRoot->RegisterListener(m_poListener);
272 : }
273 21020 : nodesChanged();
274 21020 : }
275 :
276 158597 : void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
277 : bool doRefreshAxisMapping)
278 : {
279 158597 : auto ctxt = getPROJContext();
280 :
281 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
282 : if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
283 : {
284 : const double dfEpoch =
285 : proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
286 : if (!std::isnan(dfEpoch))
287 : {
288 : m_poSelf->SetCoordinateEpoch(dfEpoch);
289 : }
290 : auto crs = proj_get_source_crs(ctxt, pj_crsIn);
291 : proj_destroy(pj_crsIn);
292 : pj_crsIn = crs;
293 : }
294 : #endif
295 :
296 158597 : proj_assign_context(m_pj_crs, ctxt);
297 158597 : proj_destroy(m_pj_crs);
298 158597 : m_pj_crs = pj_crsIn;
299 158597 : if (m_pj_crs)
300 : {
301 158545 : m_pjType = proj_get_type(m_pj_crs);
302 : }
303 158597 : if (m_pj_crs_backup)
304 : {
305 63 : m_pj_crs_modified_during_demote = true;
306 : }
307 158597 : invalidateNodes();
308 158597 : if (doRefreshAxisMapping)
309 : {
310 158535 : refreshAxisMapping();
311 : }
312 158597 : }
313 :
314 534306 : void OGRSpatialReference::Private::refreshProjObj()
315 : {
316 534306 : if (m_bNodesChanged && m_poRoot)
317 : {
318 6613 : char *pszWKT = nullptr;
319 6613 : m_poRoot->exportToWkt(&pszWKT);
320 6613 : auto poRootBackup = m_poRoot;
321 6613 : m_poRoot = nullptr;
322 6613 : const double dfCoordinateEpochBackup = m_coordinateEpoch;
323 6613 : clear();
324 6613 : m_coordinateEpoch = dfCoordinateEpochBackup;
325 6613 : m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
326 :
327 6613 : const char *const options[] = {
328 : "STRICT=NO",
329 : #if PROJ_AT_LEAST_VERSION(9, 1, 0)
330 : "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
331 : #endif
332 : nullptr
333 : };
334 6613 : PROJ_STRING_LIST warnings = nullptr;
335 6613 : PROJ_STRING_LIST errors = nullptr;
336 6613 : setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
337 : &warnings, &errors));
338 12929 : for (auto iter = warnings; iter && *iter; ++iter)
339 : {
340 6316 : m_wktImportWarnings.push_back(*iter);
341 : }
342 6792 : for (auto iter = errors; iter && *iter; ++iter)
343 : {
344 179 : m_wktImportErrors.push_back(*iter);
345 : }
346 6613 : proj_string_list_destroy(warnings);
347 6613 : proj_string_list_destroy(errors);
348 :
349 6613 : CPLFree(pszWKT);
350 :
351 6613 : m_poRoot = poRootBackup;
352 6613 : m_bNodesChanged = false;
353 : }
354 534306 : }
355 :
356 23110 : void OGRSpatialReference::Private::refreshRootFromProjObj()
357 : {
358 23110 : CPLAssert(m_poRoot == nullptr);
359 :
360 23110 : if (m_pj_crs)
361 : {
362 41958 : CPLStringList aosOptions;
363 20979 : if (!m_bMorphToESRI)
364 : {
365 20975 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
366 20975 : aosOptions.SetNameValue("MULTILINE", "NO");
367 : }
368 20979 : aosOptions.SetNameValue("STRICT", "NO");
369 :
370 : const char *pszWKT;
371 : {
372 20979 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
373 20979 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
374 20979 : m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
375 20979 : aosOptions.List());
376 20979 : m_bNodesWKT2 = false;
377 : }
378 20979 : if (!m_bMorphToESRI && pszWKT == nullptr)
379 : {
380 50 : pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
381 50 : aosOptions.List());
382 50 : m_bNodesWKT2 = true;
383 : }
384 20979 : if (pszWKT)
385 : {
386 20979 : auto root = new OGR_SRSNode();
387 20979 : setRoot(root);
388 20979 : root->importFromWkt(&pszWKT);
389 20979 : m_bNodesChanged = false;
390 : }
391 : }
392 23110 : }
393 :
394 191313 : static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
395 : {
396 191313 : const char *pszName1 = nullptr;
397 191313 : const char *pszDirection1 = nullptr;
398 191313 : proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
399 : nullptr, nullptr, nullptr, nullptr);
400 191313 : const char *pszName2 = nullptr;
401 191313 : const char *pszDirection2 = nullptr;
402 191313 : proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
403 : nullptr, nullptr, nullptr, nullptr);
404 191313 : if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
405 95100 : EQUAL(pszDirection2, "east"))
406 : {
407 94558 : return true;
408 : }
409 96755 : if (pszDirection1 && pszDirection2 &&
410 96755 : ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
411 96230 : (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
412 1037 : pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
413 230 : STARTS_WITH_CI(pszName2, "easting"))
414 : {
415 230 : return true;
416 : }
417 96525 : return false;
418 : }
419 :
420 237868 : void OGRSpatialReference::Private::refreshAxisMapping()
421 : {
422 237868 : if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
423 46867 : return;
424 :
425 191001 : bool doUndoDemote = false;
426 191001 : if (m_pj_crs_backup == nullptr)
427 : {
428 190938 : doUndoDemote = true;
429 190938 : demoteFromBoundCRS();
430 : }
431 191001 : const auto ctxt = getPROJContext();
432 191001 : PJ *horizCRS = nullptr;
433 191001 : int axisCount = 0;
434 191001 : if (m_pjType == PJ_TYPE_VERTICAL_CRS)
435 : {
436 217 : axisCount = 1;
437 : }
438 190784 : else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
439 : {
440 1051 : horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
441 1051 : if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
442 : {
443 222 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
444 222 : if (baseCRS)
445 : {
446 222 : proj_destroy(horizCRS);
447 222 : horizCRS = baseCRS;
448 : }
449 : }
450 :
451 1051 : auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
452 1051 : if (vertCRS)
453 : {
454 1048 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
455 : {
456 373 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
457 373 : if (baseCRS)
458 : {
459 373 : proj_destroy(vertCRS);
460 373 : vertCRS = baseCRS;
461 : }
462 : }
463 :
464 1048 : auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
465 1048 : if (cs)
466 : {
467 1048 : axisCount += proj_cs_get_axis_count(ctxt, cs);
468 1048 : proj_destroy(cs);
469 : }
470 1048 : proj_destroy(vertCRS);
471 : }
472 : }
473 : else
474 : {
475 189733 : horizCRS = m_pj_crs;
476 : }
477 :
478 191001 : bool bSwitchForGisFriendlyOrder = false;
479 191001 : if (horizCRS)
480 : {
481 190781 : auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
482 190781 : if (cs)
483 : {
484 190781 : int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
485 190781 : axisCount += nHorizCSAxisCount;
486 190781 : if (nHorizCSAxisCount >= 2)
487 : {
488 190771 : bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
489 : }
490 190781 : proj_destroy(cs);
491 : }
492 : }
493 191001 : if (horizCRS != m_pj_crs)
494 : {
495 1268 : proj_destroy(horizCRS);
496 : }
497 191001 : if (doUndoDemote)
498 : {
499 190937 : undoDemoteFromBoundCRS();
500 : }
501 :
502 191001 : m_axisMapping.resize(axisCount);
503 191000 : if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
504 63104 : !bSwitchForGisFriendlyOrder)
505 : {
506 478423 : for (int i = 0; i < axisCount; i++)
507 : {
508 319381 : m_axisMapping[i] = i + 1;
509 159042 : }
510 : }
511 : else
512 : {
513 31959 : m_axisMapping[0] = 2;
514 31959 : m_axisMapping[1] = 1;
515 31959 : if (axisCount == 3)
516 : {
517 322 : m_axisMapping[2] = 3;
518 : }
519 : }
520 : }
521 :
522 1666270 : void OGRSpatialReference::Private::nodesChanged()
523 : {
524 1666270 : m_bNodesChanged = true;
525 1666270 : }
526 :
527 158892 : void OGRSpatialReference::Private::invalidateNodes()
528 : {
529 158892 : delete m_poRoot;
530 158892 : m_poRoot = nullptr;
531 158892 : m_bNodesChanged = false;
532 158892 : }
533 :
534 295 : void OGRSpatialReference::Private::setMorphToESRI(bool b)
535 : {
536 295 : invalidateNodes();
537 295 : m_bMorphToESRI = b;
538 295 : }
539 :
540 487385 : void OGRSpatialReference::Private::demoteFromBoundCRS()
541 : {
542 487385 : CPLAssert(m_pj_bound_crs_target == nullptr);
543 487385 : CPLAssert(m_pj_bound_crs_co == nullptr);
544 487385 : CPLAssert(m_poRootBackup == nullptr);
545 487385 : CPLAssert(m_pj_crs_backup == nullptr);
546 :
547 487385 : m_pj_crs_modified_during_demote = false;
548 :
549 487385 : if (m_pjType == PJ_TYPE_BOUND_CRS)
550 : {
551 4336 : auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
552 4336 : m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
553 4336 : m_pj_bound_crs_co =
554 4336 : proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
555 :
556 4336 : m_poRootBackup = m_poRoot;
557 4336 : m_poRoot = nullptr;
558 4336 : m_pj_crs_backup = m_pj_crs;
559 4336 : m_pj_crs = baseCRS;
560 4336 : m_pjType = proj_get_type(m_pj_crs);
561 : }
562 487385 : }
563 :
564 487385 : void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
565 : {
566 487385 : if (m_pj_bound_crs_target)
567 : {
568 4336 : CPLAssert(m_poRoot == nullptr);
569 4336 : CPLAssert(m_pj_crs);
570 4336 : if (!m_pj_crs_modified_during_demote)
571 : {
572 4274 : proj_destroy(m_pj_crs);
573 4274 : m_pj_crs = m_pj_crs_backup;
574 4274 : m_pjType = proj_get_type(m_pj_crs);
575 4274 : m_poRoot = m_poRootBackup;
576 : }
577 : else
578 : {
579 62 : delete m_poRootBackup;
580 62 : m_poRootBackup = nullptr;
581 62 : proj_destroy(m_pj_crs_backup);
582 62 : m_pj_crs_backup = nullptr;
583 62 : setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
584 62 : m_pj_bound_crs_target,
585 62 : m_pj_bound_crs_co),
586 : false);
587 : }
588 : }
589 :
590 487385 : m_poRootBackup = nullptr;
591 487385 : m_pj_crs_backup = nullptr;
592 487385 : proj_destroy(m_pj_bound_crs_target);
593 487385 : m_pj_bound_crs_target = nullptr;
594 487385 : proj_destroy(m_pj_bound_crs_co);
595 487384 : m_pj_bound_crs_co = nullptr;
596 487384 : m_pj_crs_modified_during_demote = false;
597 487384 : }
598 :
599 92396 : const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
600 : const char *pszTargetKey)
601 : {
602 92396 : if (pszTargetKey)
603 : {
604 39030 : demoteFromBoundCRS();
605 39030 : if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
606 24166 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
607 14938 : EQUAL(pszTargetKey, "GEOGCS"))
608 : {
609 3354 : pszTargetKey = nullptr;
610 : }
611 35676 : else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
612 20 : EQUAL(pszTargetKey, "GEOCCS"))
613 : {
614 0 : pszTargetKey = nullptr;
615 : }
616 35676 : else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
617 22793 : EQUAL(pszTargetKey, "PROJCS"))
618 : {
619 3253 : pszTargetKey = nullptr;
620 : }
621 32423 : else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
622 4 : EQUAL(pszTargetKey, "VERT_CS"))
623 : {
624 2 : pszTargetKey = nullptr;
625 : }
626 39030 : undoDemoteFromBoundCRS();
627 : }
628 92396 : return pszTargetKey;
629 : }
630 :
631 7650 : PJ *OGRSpatialReference::Private::getGeodBaseCRS()
632 : {
633 7650 : if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
634 7579 : m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
635 : {
636 71 : return m_pj_crs;
637 : }
638 :
639 7579 : auto ctxt = getPROJContext();
640 7579 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
641 : {
642 3412 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
643 3412 : proj_destroy(m_pj_geod_base_crs_temp);
644 3412 : m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
645 3412 : return m_pj_geod_base_crs_temp;
646 : }
647 :
648 4167 : proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
649 4167 : proj_destroy(m_pj_geod_base_crs_temp);
650 4167 : auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
651 : nullptr, 0);
652 4167 : m_pj_geod_base_crs_temp = proj_create_geographic_crs(
653 : ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
654 : SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
655 : SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
656 4167 : proj_destroy(cs);
657 :
658 4167 : return m_pj_geod_base_crs_temp;
659 : }
660 :
661 4370 : PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
662 : {
663 4370 : auto ctxt = getPROJContext();
664 4370 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
665 : {
666 3399 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
667 3399 : proj_destroy(m_pj_proj_crs_cs_temp);
668 3399 : m_pj_proj_crs_cs_temp =
669 3399 : proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
670 3399 : return m_pj_proj_crs_cs_temp;
671 : }
672 :
673 971 : proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
674 971 : proj_destroy(m_pj_proj_crs_cs_temp);
675 971 : m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
676 : ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
677 971 : return m_pj_proj_crs_cs_temp;
678 : }
679 :
680 4417 : const char *OGRSpatialReference::Private::getProjCRSName()
681 : {
682 4417 : if (m_pjType == PJ_TYPE_PROJECTED_CRS)
683 : {
684 3413 : return proj_get_name(m_pj_crs);
685 : }
686 :
687 1004 : return "unnamed";
688 : }
689 :
690 1334 : OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
691 : {
692 1334 : refreshProjObj();
693 :
694 1334 : demoteFromBoundCRS();
695 :
696 : auto projCRS =
697 1334 : proj_create_projected_crs(getPROJContext(), getProjCRSName(),
698 1334 : getGeodBaseCRS(), conv, getProjCRSCoordSys());
699 1334 : proj_destroy(conv);
700 :
701 1334 : setPjCRS(projCRS);
702 :
703 1334 : undoDemoteFromBoundCRS();
704 1334 : return OGRERR_NONE;
705 : }
706 :
707 : /************************************************************************/
708 : /* ToPointer() */
709 : /************************************************************************/
710 :
711 19805 : static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
712 : {
713 19805 : return OGRSpatialReference::FromHandle(hSRS);
714 : }
715 :
716 : /************************************************************************/
717 : /* ToHandle() */
718 : /************************************************************************/
719 :
720 3445 : static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
721 : {
722 3445 : return OGRSpatialReference::ToHandle(poSRS);
723 : }
724 :
725 : /************************************************************************/
726 : /* OGRsnPrintDouble() */
727 : /************************************************************************/
728 :
729 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
730 :
731 128 : void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
732 :
733 : {
734 128 : CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
735 :
736 128 : const size_t nLen = strlen(pszStrBuf);
737 :
738 : // The following hack is intended to truncate some "precision" in cases
739 : // that appear to be roundoff error.
740 128 : if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
741 8 : strcmp(pszStrBuf + nLen - 6, "000001") == 0))
742 : {
743 0 : CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
744 : }
745 :
746 : // Force to user periods regardless of locale.
747 128 : if (strchr(pszStrBuf, ',') != nullptr)
748 : {
749 0 : char *const pszDelim = strchr(pszStrBuf, ',');
750 0 : *pszDelim = '.';
751 : }
752 128 : }
753 :
754 : /************************************************************************/
755 : /* OGRSpatialReference() */
756 : /************************************************************************/
757 :
758 : /**
759 : * \brief Constructor.
760 : *
761 : * This constructor takes an optional string argument which if passed
762 : * should be a WKT representation of an SRS. Passing this is equivalent
763 : * to not passing it, and then calling importFromWkt() with the WKT string.
764 : *
765 : * Note that newly created objects are given a reference count of one.
766 : *
767 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
768 : * object are assumed to be in the order of the axis of the CRS definition
769 : (which
770 : * for example means latitude first, longitude second for geographic CRS
771 : belonging
772 : * to the EPSG authority). It is possible to define a data axis to CRS axis
773 : * mapping strategy with the SetAxisMappingStrategy() method.
774 : *
775 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
776 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
777 : later
778 : * being the default value when the option is not set) to control the value of
779 : the
780 : * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
781 : * created. Calling SetAxisMappingStrategy() will override this default value.
782 :
783 : * The C function OSRNewSpatialReference() does the same thing as this
784 : * constructor.
785 : *
786 : * @param pszWKT well known text definition to which the object should
787 : * be initialized, or NULL (the default).
788 : */
789 :
790 190646 : OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
791 190646 : : d(new Private(this))
792 : {
793 190605 : if (pszWKT != nullptr)
794 1018 : importFromWkt(pszWKT);
795 190605 : }
796 :
797 : /************************************************************************/
798 : /* OSRNewSpatialReference() */
799 : /************************************************************************/
800 :
801 : /**
802 : * \brief Constructor.
803 : *
804 : * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
805 : * object are assumed to be in the order of the axis of the CRS definition
806 : * (which for example means latitude first, longitude second for geographic CRS
807 : * belonging to the EPSG authority). It is possible to define a data axis to CRS
808 : * axis mapping strategy with the SetAxisMappingStrategy() method.
809 : *
810 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
811 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
812 : * later being the default value when the option is not set) to control the
813 : * value of the data axis to CRS axis mapping strategy when a
814 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
815 : * override this default value.
816 : *
817 : * This function is the same as OGRSpatialReference::OGRSpatialReference()
818 : */
819 2693 : OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
820 :
821 : {
822 2693 : OGRSpatialReference *poSRS = new OGRSpatialReference();
823 :
824 2693 : if (pszWKT != nullptr && strlen(pszWKT) > 0)
825 : {
826 307 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
827 : {
828 1 : delete poSRS;
829 1 : poSRS = nullptr;
830 : }
831 : }
832 :
833 2693 : return ToHandle(poSRS);
834 : }
835 :
836 : /************************************************************************/
837 : /* OGRSpatialReference() */
838 : /************************************************************************/
839 :
840 : /** Copy constructor. See also Clone().
841 : * @param oOther other spatial reference
842 : */
843 2131 : OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
844 2131 : : d(new Private(this))
845 : {
846 2131 : *this = oOther;
847 2131 : }
848 :
849 : /************************************************************************/
850 : /* OGRSpatialReference() */
851 : /************************************************************************/
852 :
853 : /** Move constructor.
854 : * @param oOther other spatial reference
855 : */
856 28 : OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
857 28 : : d(std::move(oOther.d))
858 : {
859 28 : }
860 :
861 : /************************************************************************/
862 : /* ~OGRSpatialReference() */
863 : /************************************************************************/
864 :
865 : /**
866 : * \brief OGRSpatialReference destructor.
867 : *
868 : * The C function OSRDestroySpatialReference() does the same thing as this
869 : * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
870 : *
871 : * @deprecated
872 : */
873 :
874 237541 : OGRSpatialReference::~OGRSpatialReference()
875 :
876 : {
877 237540 : }
878 :
879 : /************************************************************************/
880 : /* DestroySpatialReference() */
881 : /************************************************************************/
882 :
883 : /**
884 : * \brief OGRSpatialReference destructor.
885 : *
886 : * This static method will destroy a OGRSpatialReference. It is
887 : * equivalent to calling delete on the object, but it ensures that the
888 : * deallocation is properly executed within the OGR libraries heap on
889 : * platforms where this can matter (win32).
890 : *
891 : * This function is the same as OSRDestroySpatialReference()
892 : *
893 : * @param poSRS the object to delete
894 : *
895 : * @since GDAL 1.7.0
896 : */
897 :
898 0 : void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
899 : {
900 0 : delete poSRS;
901 0 : }
902 :
903 : /************************************************************************/
904 : /* OSRDestroySpatialReference() */
905 : /************************************************************************/
906 :
907 : /**
908 : * \brief OGRSpatialReference destructor.
909 : *
910 : * This function is the same as OGRSpatialReference::~OGRSpatialReference()
911 : * and OGRSpatialReference::DestroySpatialReference()
912 : *
913 : * @param hSRS the object to delete
914 : */
915 6114 : void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
916 :
917 : {
918 6114 : delete ToPointer(hSRS);
919 6114 : }
920 :
921 : /************************************************************************/
922 : /* Clear() */
923 : /************************************************************************/
924 :
925 : /**
926 : * \brief Wipe current definition.
927 : *
928 : * Returns OGRSpatialReference to a state with no definition, as it
929 : * exists when first created. It does not affect reference counts.
930 : */
931 :
932 89575 : void OGRSpatialReference::Clear()
933 :
934 : {
935 89575 : d->clear();
936 89575 : }
937 :
938 : /************************************************************************/
939 : /* operator=() */
940 : /************************************************************************/
941 :
942 : /** Assignment operator.
943 : * @param oSource SRS to assign to *this
944 : * @return *this
945 : */
946 : OGRSpatialReference &
947 17809 : OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
948 :
949 : {
950 17809 : if (&oSource != this)
951 : {
952 17808 : Clear();
953 : #ifdef CPPCHECK
954 : // Otherwise cppcheck would protest that nRefCount isn't modified
955 : d->nRefCount = (d->nRefCount + 1) - 1;
956 : #endif
957 :
958 17808 : oSource.d->refreshProjObj();
959 17808 : if (oSource.d->m_pj_crs)
960 17562 : d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
961 17808 : if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
962 9420 : SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
963 8388 : else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
964 107 : SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
965 :
966 17808 : d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
967 : }
968 :
969 17809 : return *this;
970 : }
971 :
972 : /************************************************************************/
973 : /* operator=() */
974 : /************************************************************************/
975 :
976 : /** Move assignment operator.
977 : * @param oSource SRS to assign to *this
978 : * @return *this
979 : */
980 : OGRSpatialReference &
981 3784 : OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
982 :
983 : {
984 3784 : if (&oSource != this)
985 : {
986 3783 : d = std::move(oSource.d);
987 : }
988 :
989 3784 : return *this;
990 : }
991 :
992 : /************************************************************************/
993 : /* Reference() */
994 : /************************************************************************/
995 :
996 : /**
997 : * \brief Increments the reference count by one.
998 : *
999 : * The reference count is used keep track of the number of OGRGeometry objects
1000 : * referencing this SRS.
1001 : *
1002 : * The method does the same thing as the C function OSRReference().
1003 : *
1004 : * @return the updated reference count.
1005 : */
1006 :
1007 448624 : int OGRSpatialReference::Reference()
1008 :
1009 : {
1010 448624 : return CPLAtomicInc(&d->nRefCount);
1011 : }
1012 :
1013 : /************************************************************************/
1014 : /* OSRReference() */
1015 : /************************************************************************/
1016 :
1017 : /**
1018 : * \brief Increments the reference count by one.
1019 : *
1020 : * This function is the same as OGRSpatialReference::Reference()
1021 : */
1022 911 : int OSRReference(OGRSpatialReferenceH hSRS)
1023 :
1024 : {
1025 911 : VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1026 :
1027 911 : return ToPointer(hSRS)->Reference();
1028 : }
1029 :
1030 : /************************************************************************/
1031 : /* Dereference() */
1032 : /************************************************************************/
1033 :
1034 : /**
1035 : * \brief Decrements the reference count by one.
1036 : *
1037 : * The method does the same thing as the C function OSRDereference().
1038 : *
1039 : * @return the updated reference count.
1040 : */
1041 :
1042 482148 : int OGRSpatialReference::Dereference()
1043 :
1044 : {
1045 482148 : if (d->nRefCount <= 0)
1046 0 : CPLDebug("OSR",
1047 : "Dereference() called on an object with refcount %d,"
1048 : "likely already destroyed!",
1049 0 : d->nRefCount);
1050 482148 : return CPLAtomicDec(&d->nRefCount);
1051 : }
1052 :
1053 : /************************************************************************/
1054 : /* OSRDereference() */
1055 : /************************************************************************/
1056 :
1057 : /**
1058 : * \brief Decrements the reference count by one.
1059 : *
1060 : * This function is the same as OGRSpatialReference::Dereference()
1061 : */
1062 0 : int OSRDereference(OGRSpatialReferenceH hSRS)
1063 :
1064 : {
1065 0 : VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1066 :
1067 0 : return ToPointer(hSRS)->Dereference();
1068 : }
1069 :
1070 : /************************************************************************/
1071 : /* GetReferenceCount() */
1072 : /************************************************************************/
1073 :
1074 : /**
1075 : * \brief Fetch current reference count.
1076 : *
1077 : * @return the current reference count.
1078 : */
1079 96 : int OGRSpatialReference::GetReferenceCount() const
1080 : {
1081 96 : return d->nRefCount;
1082 : }
1083 :
1084 : /************************************************************************/
1085 : /* Release() */
1086 : /************************************************************************/
1087 :
1088 : /**
1089 : * \brief Decrements the reference count by one, and destroy if zero.
1090 : *
1091 : * The method does the same thing as the C function OSRRelease().
1092 : */
1093 :
1094 479225 : void OGRSpatialReference::Release()
1095 :
1096 : {
1097 479225 : if (Dereference() <= 0)
1098 33468 : delete this;
1099 479225 : }
1100 :
1101 : /************************************************************************/
1102 : /* OSRRelease() */
1103 : /************************************************************************/
1104 :
1105 : /**
1106 : * \brief Decrements the reference count by one, and destroy if zero.
1107 : *
1108 : * This function is the same as OGRSpatialReference::Release()
1109 : */
1110 5775 : void OSRRelease(OGRSpatialReferenceH hSRS)
1111 :
1112 : {
1113 5775 : VALIDATE_POINTER0(hSRS, "OSRRelease");
1114 :
1115 5775 : ToPointer(hSRS)->Release();
1116 : }
1117 :
1118 64285 : OGR_SRSNode *OGRSpatialReference::GetRoot()
1119 : {
1120 64285 : if (!d->m_poRoot)
1121 : {
1122 20711 : d->refreshRootFromProjObj();
1123 : }
1124 64285 : return d->m_poRoot;
1125 : }
1126 :
1127 6962 : const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1128 : {
1129 6962 : if (!d->m_poRoot)
1130 : {
1131 2399 : d->refreshRootFromProjObj();
1132 : }
1133 6962 : return d->m_poRoot;
1134 : }
1135 :
1136 : /************************************************************************/
1137 : /* SetRoot() */
1138 : /************************************************************************/
1139 :
1140 : /**
1141 : * \brief Set the root SRS node.
1142 : *
1143 : * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1144 : * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1145 : * is assumed by the OGRSpatialReference.
1146 : *
1147 : * @param poNewRoot object to assign as root.
1148 : */
1149 :
1150 41 : void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1151 :
1152 : {
1153 41 : if (d->m_poRoot != poNewRoot)
1154 : {
1155 41 : delete d->m_poRoot;
1156 41 : d->setRoot(poNewRoot);
1157 : }
1158 41 : }
1159 :
1160 : /************************************************************************/
1161 : /* GetAttrNode() */
1162 : /************************************************************************/
1163 :
1164 : /**
1165 : * \brief Find named node in tree.
1166 : *
1167 : * This method does a pre-order traversal of the node tree searching for
1168 : * a node with this exact value (case insensitive), and returns it. Leaf
1169 : * nodes are not considered, under the assumption that they are just
1170 : * attribute value nodes.
1171 : *
1172 : * If a node appears more than once in the tree (such as UNIT for instance),
1173 : * the first encountered will be returned. Use GetNode() on a subtree to be
1174 : * more specific.
1175 : *
1176 : * @param pszNodePath the name of the node to search for. May contain multiple
1177 : * components such as "GEOGCS|UNIT".
1178 : *
1179 : * @return a pointer to the node found, or NULL if none.
1180 : */
1181 :
1182 61341 : OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1183 :
1184 : {
1185 61341 : if (strchr(pszNodePath, '|') == nullptr)
1186 : {
1187 : // Fast path
1188 36055 : OGR_SRSNode *poNode = GetRoot();
1189 36055 : if (poNode)
1190 34871 : poNode = poNode->GetNode(pszNodePath);
1191 36055 : return poNode;
1192 : }
1193 :
1194 : char **papszPathTokens =
1195 25286 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1196 :
1197 25286 : if (CSLCount(papszPathTokens) < 1)
1198 : {
1199 0 : CSLDestroy(papszPathTokens);
1200 0 : return nullptr;
1201 : }
1202 :
1203 25286 : OGR_SRSNode *poNode = GetRoot();
1204 77143 : for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1205 : {
1206 51857 : poNode = poNode->GetNode(papszPathTokens[i]);
1207 : }
1208 :
1209 25286 : CSLDestroy(papszPathTokens);
1210 :
1211 25286 : return poNode;
1212 : }
1213 :
1214 : /**
1215 : * \brief Find named node in tree.
1216 : *
1217 : * This method does a pre-order traversal of the node tree searching for
1218 : * a node with this exact value (case insensitive), and returns it. Leaf
1219 : * nodes are not considered, under the assumption that they are just
1220 : * attribute value nodes.
1221 : *
1222 : * If a node appears more than once in the tree (such as UNIT for instance),
1223 : * the first encountered will be returned. Use GetNode() on a subtree to be
1224 : * more specific.
1225 : *
1226 : * @param pszNodePath the name of the node to search for. May contain multiple
1227 : * components such as "GEOGCS|UNIT".
1228 : *
1229 : * @return a pointer to the node found, or NULL if none.
1230 : */
1231 :
1232 : const OGR_SRSNode *
1233 54947 : OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1234 :
1235 : {
1236 : OGR_SRSNode *poNode =
1237 54947 : const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1238 :
1239 54947 : return poNode;
1240 : }
1241 :
1242 : /************************************************************************/
1243 : /* GetAttrValue() */
1244 : /************************************************************************/
1245 :
1246 : /**
1247 : * \brief Fetch indicated attribute of named node.
1248 : *
1249 : * This method uses GetAttrNode() to find the named node, and then extracts
1250 : * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1251 : * would return the second child of the UNIT node, which is normally the
1252 : * length of the linear unit in meters.
1253 : *
1254 : * This method does the same thing as the C function OSRGetAttrValue().
1255 : *
1256 : * @param pszNodeName the tree node to look for (case insensitive).
1257 : * @param iAttr the child of the node to fetch (zero based).
1258 : *
1259 : * @return the requested value, or NULL if it fails for any reason.
1260 : */
1261 :
1262 18184 : const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1263 : int iAttr) const
1264 :
1265 : {
1266 18184 : const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1267 18184 : if (poNode == nullptr)
1268 : {
1269 7061 : if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1270 : {
1271 8 : return GetAttrValue("METHOD", iAttr);
1272 : }
1273 7053 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1274 : {
1275 0 : return GetAttrValue("PROJCRS|METHOD", iAttr);
1276 : }
1277 7053 : else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1278 : {
1279 1 : return GetAttrValue("PROJCRS", iAttr);
1280 : }
1281 7052 : return nullptr;
1282 : }
1283 :
1284 11123 : if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1285 0 : return nullptr;
1286 :
1287 11123 : return poNode->GetChild(iAttr)->GetValue();
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* OSRGetAttrValue() */
1292 : /************************************************************************/
1293 :
1294 : /**
1295 : * \brief Fetch indicated attribute of named node.
1296 : *
1297 : * This function is the same as OGRSpatialReference::GetAttrValue()
1298 : */
1299 62 : const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1300 : const char *pszKey, int iChild)
1301 :
1302 : {
1303 62 : VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1304 :
1305 62 : return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1306 : }
1307 :
1308 : /************************************************************************/
1309 : /* GetName() */
1310 : /************************************************************************/
1311 :
1312 : /**
1313 : * \brief Return the CRS name.
1314 : *
1315 : * The returned value is only short lived and should not be used after other
1316 : * calls to methods on this object.
1317 : *
1318 : * @since GDAL 3.0
1319 : */
1320 :
1321 4012 : const char *OGRSpatialReference::GetName() const
1322 : {
1323 4012 : d->refreshProjObj();
1324 4012 : if (!d->m_pj_crs)
1325 112 : return nullptr;
1326 3900 : const char *pszName = proj_get_name(d->m_pj_crs);
1327 : #if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1328 : if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1329 : {
1330 : // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1331 : PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1332 : if (baseCRS)
1333 : {
1334 : pszName = proj_get_name(baseCRS);
1335 : // pszName still remains valid after proj_destroy(), since
1336 : // d->m_pj_crs keeps a reference to the base CRS C++ object.
1337 : proj_destroy(baseCRS);
1338 : }
1339 : }
1340 : #endif
1341 3900 : return pszName;
1342 : }
1343 :
1344 : /************************************************************************/
1345 : /* OSRGetName() */
1346 : /************************************************************************/
1347 :
1348 : /**
1349 : * \brief Return the CRS name.
1350 : *
1351 : * The returned value is only short lived and should not be used after other
1352 : * calls to methods on this object.
1353 : *
1354 : * @since GDAL 3.0
1355 : */
1356 38 : const char *OSRGetName(OGRSpatialReferenceH hSRS)
1357 :
1358 : {
1359 38 : VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1360 :
1361 38 : return ToPointer(hSRS)->GetName();
1362 : }
1363 :
1364 : /************************************************************************/
1365 : /* Clone() */
1366 : /************************************************************************/
1367 :
1368 : /**
1369 : * \brief Make a duplicate of this OGRSpatialReference.
1370 : *
1371 : * This method is the same as the C function OSRClone().
1372 : *
1373 : * @return a new SRS, which becomes the responsibility of the caller.
1374 : */
1375 :
1376 22132 : OGRSpatialReference *OGRSpatialReference::Clone() const
1377 :
1378 : {
1379 22132 : OGRSpatialReference *poNewRef = new OGRSpatialReference();
1380 :
1381 22132 : d->refreshProjObj();
1382 22132 : if (d->m_pj_crs != nullptr)
1383 22081 : poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1384 22132 : if (d->m_bHasCenterLong && d->m_poRoot)
1385 : {
1386 0 : poNewRef->d->setRoot(d->m_poRoot->Clone());
1387 : }
1388 22132 : poNewRef->d->m_axisMapping = d->m_axisMapping;
1389 22132 : poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1390 22132 : poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1391 22132 : return poNewRef;
1392 : }
1393 :
1394 : /************************************************************************/
1395 : /* OSRClone() */
1396 : /************************************************************************/
1397 :
1398 : /**
1399 : * \brief Make a duplicate of this OGRSpatialReference.
1400 : *
1401 : * This function is the same as OGRSpatialReference::Clone()
1402 : */
1403 581 : OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1404 :
1405 : {
1406 581 : VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1407 :
1408 581 : return ToHandle(ToPointer(hSRS)->Clone());
1409 : }
1410 :
1411 : /************************************************************************/
1412 : /* dumpReadable() */
1413 : /************************************************************************/
1414 :
1415 : /** Dump pretty wkt to stdout, mostly for debugging.
1416 : */
1417 0 : void OGRSpatialReference::dumpReadable()
1418 :
1419 : {
1420 0 : char *pszPrettyWkt = nullptr;
1421 :
1422 0 : const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1423 0 : exportToWkt(&pszPrettyWkt, apszOptions);
1424 0 : printf("%s\n", pszPrettyWkt); /*ok*/
1425 0 : CPLFree(pszPrettyWkt);
1426 0 : }
1427 :
1428 : /************************************************************************/
1429 : /* exportToPrettyWkt() */
1430 : /************************************************************************/
1431 :
1432 : /**
1433 : * Convert this SRS into a nicely formatted WKT 1 string for display to a
1434 : * person.
1435 : *
1436 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1437 : * Issues</a> page for implementation details of WKT 1 in OGR.
1438 : *
1439 : * Note that the returned WKT string should be freed with
1440 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1441 : *
1442 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1443 : * option. Valid values are the one of the FORMAT option of
1444 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1445 : *
1446 : * This method is the same as the C function OSRExportToPrettyWkt().
1447 : *
1448 : * @param ppszResult the resulting string is returned in this pointer.
1449 : * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1450 : * stripped off.
1451 : *
1452 : * @return OGRERR_NONE if successful.
1453 : */
1454 :
1455 58 : OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1456 : int bSimplify) const
1457 :
1458 : {
1459 116 : CPLStringList aosOptions;
1460 58 : aosOptions.SetNameValue("MULTILINE", "YES");
1461 58 : if (bSimplify)
1462 : {
1463 0 : aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1464 : }
1465 116 : return exportToWkt(ppszResult, aosOptions.List());
1466 : }
1467 :
1468 : /************************************************************************/
1469 : /* OSRExportToPrettyWkt() */
1470 : /************************************************************************/
1471 :
1472 : /**
1473 : * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1474 : * person.
1475 : *
1476 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1477 : * option. Valid values are the one of the FORMAT option of
1478 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1479 : *
1480 : * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1481 : */
1482 :
1483 56 : OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1484 : char **ppszReturn, int bSimplify)
1485 :
1486 : {
1487 56 : VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1488 :
1489 56 : *ppszReturn = nullptr;
1490 :
1491 56 : return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1492 : }
1493 :
1494 : /************************************************************************/
1495 : /* exportToWkt() */
1496 : /************************************************************************/
1497 :
1498 : /**
1499 : * \brief Convert this SRS into WKT 1 format.
1500 : *
1501 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1502 : * Issues</a> page for implementation details of WKT 1 in OGR.
1503 : *
1504 : * Note that the returned WKT string should be freed with
1505 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1506 : *
1507 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1508 : * option. Valid values are the one of the FORMAT option of
1509 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1510 : *
1511 : * This method is the same as the C function OSRExportToWkt().
1512 : *
1513 : * @param ppszResult the resulting string is returned in this pointer.
1514 : *
1515 : * @return OGRERR_NONE if successful.
1516 : */
1517 :
1518 11954 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1519 :
1520 : {
1521 11954 : return exportToWkt(ppszResult, nullptr);
1522 : }
1523 :
1524 : /************************************************************************/
1525 : /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1526 : /************************************************************************/
1527 :
1528 539 : static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1529 : bool onlyIfEPSGCode,
1530 : bool canModifyHorizPart)
1531 : {
1532 539 : PJ *ret = nullptr;
1533 539 : if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1534 : {
1535 13 : auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1536 13 : auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1537 13 : if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1538 26 : vertCRS &&
1539 10 : (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1540 : {
1541 : auto boundHoriz =
1542 : canModifyHorizPart
1543 3 : ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1544 3 : : proj_clone(ctx, horizCRS);
1545 : auto boundVert =
1546 3 : proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1547 3 : if (boundHoriz && boundVert)
1548 : {
1549 3 : ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1550 : boundHoriz, boundVert);
1551 : }
1552 3 : proj_destroy(boundHoriz);
1553 3 : proj_destroy(boundVert);
1554 : }
1555 13 : proj_destroy(horizCRS);
1556 13 : proj_destroy(vertCRS);
1557 : }
1558 1015 : else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1559 489 : (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1560 : {
1561 224 : ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1562 : }
1563 539 : return ret;
1564 : }
1565 :
1566 : /************************************************************************/
1567 : /* exportToWkt() */
1568 : /************************************************************************/
1569 :
1570 : /**
1571 : * Convert this SRS into a WKT string.
1572 : *
1573 : * Note that the returned WKT string should be freed with
1574 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1575 : *
1576 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1577 : * Issues</a> page for implementation details of WKT 1 in OGR.
1578 : *
1579 : * @param ppszResult the resulting string is returned in this pointer.
1580 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1581 : * supported options are
1582 : * <ul>
1583 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1584 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1585 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1586 : * node is returned.
1587 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1588 : * node is returned.
1589 : * WKT1 is an alias of WKT1_GDAL.
1590 : * WKT2 will default to the latest revision implemented (currently
1591 : * WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1592 : * </li>
1593 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1594 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1595 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1596 : * height (for example for use with LAS 1.4 WKT1).
1597 : * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1598 : * </ul>
1599 : *
1600 : * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1601 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1602 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1603 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1604 : * TOWGS84[] node may be added.
1605 : *
1606 : * @return OGRERR_NONE if successful.
1607 : * @since GDAL 3.0
1608 : */
1609 :
1610 15430 : OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1611 : const char *const *papszOptions) const
1612 : {
1613 : // In the past calling this method was thread-safe, even if we never
1614 : // guaranteed it. Now proj_as_wkt() will cache the result internally,
1615 : // so this is no longer thread-safe.
1616 30860 : std::lock_guard<std::mutex> oLock(d->m_mutex);
1617 :
1618 15430 : d->refreshProjObj();
1619 15430 : if (!d->m_pj_crs)
1620 : {
1621 21 : *ppszResult = CPLStrdup("");
1622 21 : return OGRERR_FAILURE;
1623 : }
1624 :
1625 15409 : if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1626 : {
1627 0 : return d->m_poRoot->exportToWkt(ppszResult);
1628 : }
1629 :
1630 15409 : auto ctxt = d->getPROJContext();
1631 15409 : auto wktFormat = PJ_WKT1_GDAL;
1632 : const char *pszFormat =
1633 15409 : CSLFetchNameValueDef(papszOptions, "FORMAT",
1634 : CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1635 15409 : if (EQUAL(pszFormat, "DEFAULT"))
1636 13301 : pszFormat = "";
1637 :
1638 15409 : if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1639 : {
1640 561 : wktFormat = PJ_WKT1_ESRI;
1641 : }
1642 14848 : else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1643 14244 : EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1644 : {
1645 609 : wktFormat = PJ_WKT1_GDAL;
1646 : }
1647 14239 : else if (EQUAL(pszFormat, "WKT2_2015"))
1648 : {
1649 232 : wktFormat = PJ_WKT2_2015;
1650 : }
1651 14007 : else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1652 13729 : EQUAL(pszFormat, "WKT2_2019"))
1653 : {
1654 968 : wktFormat = PJ_WKT2_2018;
1655 : }
1656 13039 : else if (pszFormat[0] == '\0')
1657 : {
1658 : // cppcheck-suppress knownConditionTrueFalse
1659 13039 : if (IsDerivedGeographic())
1660 : {
1661 2 : wktFormat = PJ_WKT2_2018;
1662 : }
1663 25444 : else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1664 12407 : GetAxesCount() == 3)
1665 : {
1666 56 : wktFormat = PJ_WKT2_2018;
1667 : }
1668 : }
1669 : else
1670 : {
1671 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1672 0 : *ppszResult = CPLStrdup("");
1673 0 : return OGRERR_FAILURE;
1674 : }
1675 :
1676 30818 : CPLStringList aosOptions;
1677 15409 : if (wktFormat != PJ_WKT1_ESRI)
1678 : {
1679 14848 : aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1680 : }
1681 : aosOptions.SetNameValue(
1682 15409 : "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1683 :
1684 15409 : const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1685 : papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1686 15409 : if (pszAllowEllpsHeightAsVertCS)
1687 : {
1688 : aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1689 0 : pszAllowEllpsHeightAsVertCS);
1690 : }
1691 :
1692 15409 : PJ *boundCRS = nullptr;
1693 28999 : if (wktFormat == PJ_WKT1_GDAL &&
1694 13590 : CPLTestBool(CSLFetchNameValueDef(
1695 : papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1696 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1697 : {
1698 0 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1699 0 : d->getPROJContext(), d->m_pj_crs, true, true);
1700 : }
1701 :
1702 30818 : std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
1703 15409 : CPLInstallErrorHandlerAccumulator(aoErrors);
1704 15409 : const char *pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1705 15409 : wktFormat, aosOptions.List());
1706 15409 : CPLUninstallErrorHandlerAccumulator();
1707 15411 : for (const auto &oError : aoErrors)
1708 : {
1709 32 : if (pszFormat[0] == '\0' &&
1710 14 : (oError.msg.find("Unsupported conversion method") !=
1711 2 : std::string::npos ||
1712 2 : oError.msg.find("can only be exported to WKT2") !=
1713 0 : std::string::npos ||
1714 0 : oError.msg.find("can only be exported since WKT2:2019") !=
1715 : std::string::npos))
1716 : {
1717 14 : CPLErrorReset();
1718 : // If we cannot export in the default mode (WKT1), retry with WKT2
1719 14 : pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1720 14 : PJ_WKT2_2018, aosOptions.List());
1721 14 : break;
1722 : }
1723 2 : CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1724 : }
1725 :
1726 15409 : if (!pszWKT)
1727 : {
1728 2 : *ppszResult = CPLStrdup("");
1729 2 : proj_destroy(boundCRS);
1730 2 : return OGRERR_FAILURE;
1731 : }
1732 :
1733 15407 : if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1734 : {
1735 5 : OGR_SRSNode oRoot;
1736 5 : oRoot.importFromWkt(&pszWKT);
1737 5 : oRoot.StripNodes("AXIS");
1738 5 : if (EQUAL(pszFormat, "SFSQL"))
1739 : {
1740 3 : oRoot.StripNodes("TOWGS84");
1741 : }
1742 5 : oRoot.StripNodes("AUTHORITY");
1743 5 : oRoot.StripNodes("EXTENSION");
1744 : OGRErr eErr;
1745 5 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1746 2 : eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1747 : else
1748 3 : eErr = oRoot.exportToWkt(ppszResult);
1749 5 : proj_destroy(boundCRS);
1750 5 : return eErr;
1751 : }
1752 :
1753 15402 : *ppszResult = CPLStrdup(pszWKT);
1754 15402 : proj_destroy(boundCRS);
1755 15402 : return OGRERR_NONE;
1756 : }
1757 :
1758 : /************************************************************************/
1759 : /* exportToWkt() */
1760 : /************************************************************************/
1761 :
1762 : /**
1763 : * Convert this SRS into a WKT string.
1764 : *
1765 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1766 : * Issues</a> page for implementation details of WKT 1 in OGR.
1767 : *
1768 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1769 : * supported options are
1770 : * <ul>
1771 : * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1772 : * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1773 : * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1774 : * node is returned.
1775 : * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1776 : * node is returned.
1777 : * WKT1 is an alias of WKT1_GDAL.
1778 : * WKT2 will default to the latest revision implemented (currently
1779 : * WKT2_2019)
1780 : * </li>
1781 : * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1782 : * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1783 : * be exported as a compound CRS whose vertical part represents an ellipsoidal
1784 : * height (for example for use with LAS 1.4 WKT1).
1785 : * Requires PROJ 7.2.1.</li>
1786 : * </ul>
1787 : *
1788 : * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1789 : * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1790 : * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1791 : * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1792 : * TOWGS84[] node may be added.
1793 : *
1794 : * @return a non-empty string if successful.
1795 : * @since GDAL 3.9
1796 : */
1797 :
1798 : std::string
1799 92 : OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1800 : {
1801 92 : std::string osWKT;
1802 92 : char *pszWKT = nullptr;
1803 92 : if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1804 92 : osWKT = pszWKT;
1805 92 : CPLFree(pszWKT);
1806 184 : return osWKT;
1807 : }
1808 :
1809 : /************************************************************************/
1810 : /* OSRExportToWkt() */
1811 : /************************************************************************/
1812 :
1813 : /**
1814 : * \brief Convert this SRS into WKT 1 format.
1815 : *
1816 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1817 : * Issues</a> page for implementation details of WKT in OGR.
1818 : *
1819 : * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1820 : * option. Valid values are the one of the FORMAT option of
1821 : * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1822 : *
1823 : * This function is the same as OGRSpatialReference::exportToWkt().
1824 : */
1825 :
1826 830 : OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1827 :
1828 : {
1829 830 : VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1830 :
1831 830 : *ppszReturn = nullptr;
1832 :
1833 830 : return ToPointer(hSRS)->exportToWkt(ppszReturn);
1834 : }
1835 :
1836 : /************************************************************************/
1837 : /* OSRExportToWktEx() */
1838 : /************************************************************************/
1839 :
1840 : /**
1841 : * \brief Convert this SRS into WKT format.
1842 : *
1843 : * This function is the same as OGRSpatialReference::exportToWkt(char **
1844 : * ppszResult,const char* const* papszOptions ) const
1845 : *
1846 : * @since GDAL 3.0
1847 : */
1848 :
1849 874 : OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1850 : const char *const *papszOptions)
1851 : {
1852 874 : VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1853 :
1854 874 : *ppszReturn = nullptr;
1855 :
1856 874 : return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
1857 : }
1858 :
1859 : /************************************************************************/
1860 : /* exportToPROJJSON() */
1861 : /************************************************************************/
1862 :
1863 : /**
1864 : * Convert this SRS into a PROJJSON string.
1865 : *
1866 : * Note that the returned JSON string should be freed with
1867 : * CPLFree() when no longer needed. It is the responsibility of the caller.
1868 : *
1869 : * @param ppszResult the resulting string is returned in this pointer.
1870 : * @param papszOptions NULL terminated list of options, or NULL. Currently
1871 : * supported options are
1872 : * <ul>
1873 : * <li>MULTILINE=YES/NO. Defaults to YES</li>
1874 : * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1875 : * on).</li>
1876 : * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
1877 : * disable it.</li>
1878 : * </ul>
1879 : *
1880 : * @return OGRERR_NONE if successful.
1881 : * @since GDAL 3.1 and PROJ 6.2
1882 : */
1883 :
1884 2077 : OGRErr OGRSpatialReference::exportToPROJJSON(
1885 : char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
1886 : {
1887 2077 : d->refreshProjObj();
1888 2077 : if (!d->m_pj_crs)
1889 : {
1890 0 : *ppszResult = nullptr;
1891 0 : return OGRERR_FAILURE;
1892 : }
1893 :
1894 : const char *pszPROJJSON =
1895 2077 : proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
1896 :
1897 2077 : if (!pszPROJJSON)
1898 : {
1899 0 : *ppszResult = CPLStrdup("");
1900 0 : return OGRERR_FAILURE;
1901 : }
1902 :
1903 2077 : *ppszResult = CPLStrdup(pszPROJJSON);
1904 2077 : return OGRERR_NONE;
1905 : }
1906 :
1907 : /************************************************************************/
1908 : /* OSRExportToPROJJSON() */
1909 : /************************************************************************/
1910 :
1911 : /**
1912 : * \brief Convert this SRS into PROJJSON format.
1913 : *
1914 : * This function is the same as OGRSpatialReference::exportToPROJJSON() const
1915 : *
1916 : * @since GDAL 3.1 and PROJ 6.2
1917 : */
1918 :
1919 51 : OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
1920 : const char *const *papszOptions)
1921 : {
1922 51 : VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
1923 :
1924 51 : *ppszReturn = nullptr;
1925 :
1926 51 : return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
1927 : }
1928 :
1929 : /************************************************************************/
1930 : /* importFromWkt() */
1931 : /************************************************************************/
1932 :
1933 : /**
1934 : * \brief Import from WKT string.
1935 : *
1936 : * This method will wipe the existing SRS definition, and
1937 : * reassign it based on the contents of the passed WKT string. Only as
1938 : * much of the input string as needed to construct this SRS is consumed from
1939 : * the input string, and the input string pointer
1940 : * is then updated to point to the remaining (unused) input.
1941 : *
1942 : * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
1943 : * the CRS contained in it will be used to fill the OGRSpatialReference object,
1944 : * and the coordinate epoch potentially present used as the coordinate epoch
1945 : * property of the OGRSpatialReference object.
1946 : *
1947 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1948 : * Issues</a> page for implementation details of WKT in OGR.
1949 : *
1950 : * This method is the same as the C function OSRImportFromWkt().
1951 : *
1952 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
1953 : * point to remaining unused input text.
1954 : *
1955 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
1956 : * fails for any reason.
1957 : * @since GDAL 2.3
1958 : */
1959 :
1960 26449 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
1961 :
1962 : {
1963 26449 : return importFromWkt(ppszInput, nullptr);
1964 : }
1965 :
1966 : /************************************************************************/
1967 : /* importFromWkt() */
1968 : /************************************************************************/
1969 :
1970 : /*! @cond Doxygen_Suppress */
1971 :
1972 16 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
1973 : CSLConstList papszOptions)
1974 :
1975 : {
1976 16 : return importFromWkt(&pszInput, papszOptions);
1977 : }
1978 :
1979 26465 : OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
1980 : CSLConstList papszOptions)
1981 :
1982 : {
1983 26465 : if (!ppszInput || !*ppszInput)
1984 0 : return OGRERR_FAILURE;
1985 :
1986 26465 : if (strlen(*ppszInput) > 100 * 1000 &&
1987 0 : CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
1988 : {
1989 0 : CPLError(CE_Failure, CPLE_NotSupported,
1990 : "Suspiciously large input for importFromWkt(). Rejecting it. "
1991 : "You can remove this limitation by definition the "
1992 : "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
1993 0 : return OGRERR_FAILURE;
1994 : }
1995 :
1996 26465 : Clear();
1997 :
1998 26465 : bool canCache = false;
1999 26465 : auto tlsCache = OSRGetProjTLSCache();
2000 52930 : std::string osWkt;
2001 26465 : if (**ppszInput)
2002 : {
2003 25927 : osWkt = *ppszInput;
2004 25927 : auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2005 25927 : if (cachedObj)
2006 : {
2007 24262 : d->setPjCRS(cachedObj);
2008 : }
2009 : else
2010 : {
2011 3330 : CPLStringList aosOptions(papszOptions);
2012 1665 : if (aosOptions.FetchNameValue("STRICT") == nullptr)
2013 1665 : aosOptions.SetNameValue("STRICT", "NO");
2014 1665 : PROJ_STRING_LIST warnings = nullptr;
2015 1665 : PROJ_STRING_LIST errors = nullptr;
2016 1665 : auto ctxt = d->getPROJContext();
2017 1665 : auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2018 : &warnings, &errors);
2019 1665 : d->setPjCRS(pj);
2020 :
2021 1714 : for (auto iter = warnings; iter && *iter; ++iter)
2022 : {
2023 49 : d->m_wktImportWarnings.push_back(*iter);
2024 : }
2025 1906 : for (auto iter = errors; iter && *iter; ++iter)
2026 : {
2027 241 : d->m_wktImportErrors.push_back(*iter);
2028 241 : if (!d->m_pj_crs)
2029 : {
2030 35 : CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2031 : }
2032 : }
2033 1665 : if (warnings == nullptr && errors == nullptr)
2034 : {
2035 1382 : canCache = true;
2036 : }
2037 1665 : proj_string_list_destroy(warnings);
2038 1665 : proj_string_list_destroy(errors);
2039 : }
2040 : }
2041 26465 : if (!d->m_pj_crs)
2042 573 : return OGRERR_CORRUPT_DATA;
2043 :
2044 : // Only accept CRS objects
2045 25892 : if (!proj_is_crs(d->m_pj_crs))
2046 : {
2047 0 : Clear();
2048 0 : return OGRERR_CORRUPT_DATA;
2049 : }
2050 :
2051 25892 : if (canCache)
2052 : {
2053 1382 : tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2054 : }
2055 :
2056 25892 : if (strstr(*ppszInput, "CENTER_LONG"))
2057 : {
2058 0 : auto poRoot = new OGR_SRSNode();
2059 0 : d->setRoot(poRoot);
2060 0 : const char *pszTmp = *ppszInput;
2061 0 : poRoot->importFromWkt(&pszTmp);
2062 0 : d->m_bHasCenterLong = true;
2063 : }
2064 :
2065 : // TODO? we don't really update correctly since we assume that the
2066 : // passed string is only WKT.
2067 25892 : *ppszInput += strlen(*ppszInput);
2068 25892 : return OGRERR_NONE;
2069 :
2070 : #if no_longer_implemented_for_now
2071 : /* -------------------------------------------------------------------- */
2072 : /* The following seems to try and detect and unconsumed */
2073 : /* VERTCS[] coordinate system definition (ESRI style) and to */
2074 : /* import and attach it to the existing root. Likely we will */
2075 : /* need to extend this somewhat to bring it into an acceptable */
2076 : /* OGRSpatialReference organization at some point. */
2077 : /* -------------------------------------------------------------------- */
2078 : if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2079 : {
2080 : if (((*ppszInput)[0]) == ',')
2081 : (*ppszInput)++;
2082 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
2083 : poRoot->AddChild(poNewChild);
2084 : return poNewChild->importFromWkt(ppszInput);
2085 : }
2086 : #endif
2087 : }
2088 :
2089 : /*! @endcond */
2090 :
2091 : /**
2092 : * \brief Import from WKT string.
2093 : *
2094 : * This method will wipe the existing SRS definition, and
2095 : * reassign it based on the contents of the passed WKT string. Only as
2096 : * much of the input string as needed to construct this SRS is consumed from
2097 : * the input string, and the input string pointer
2098 : * is then updated to point to the remaining (unused) input.
2099 : *
2100 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2101 : * Issues</a> page for implementation details of WKT in OGR.
2102 : *
2103 : * This method is the same as the C function OSRImportFromWkt().
2104 : *
2105 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
2106 : * point to remaining unused input text.
2107 : *
2108 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2109 : * fails for any reason.
2110 : * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2111 : * char*)
2112 : */
2113 :
2114 0 : OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2115 :
2116 : {
2117 0 : return importFromWkt(const_cast<const char **>(ppszInput));
2118 : }
2119 :
2120 : /**
2121 : * \brief Import from WKT string.
2122 : *
2123 : * This method will wipe the existing SRS definition, and
2124 : * reassign it based on the contents of the passed WKT string. Only as
2125 : * much of the input string as needed to construct this SRS is consumed from
2126 : * the input string, and the input string pointer
2127 : * is then updated to point to the remaining (unused) input.
2128 : *
2129 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2130 : * Issues</a> page for implementation details of WKT in OGR.
2131 : *
2132 : * @param pszInput Input WKT
2133 : *
2134 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2135 : * fails for any reason.
2136 : * @since GDAL 2.3
2137 : */
2138 :
2139 26156 : OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2140 : {
2141 26156 : return importFromWkt(&pszInput);
2142 : }
2143 :
2144 : /************************************************************************/
2145 : /* Validate() */
2146 : /************************************************************************/
2147 :
2148 : /**
2149 : * \brief Validate CRS imported with importFromWkt() or with modified with
2150 : * direct node manipulations. Otherwise the CRS should be always valid.
2151 : *
2152 : * This method attempts to verify that the spatial reference system is
2153 : * well formed, and consists of known tokens. The validation is not
2154 : * comprehensive.
2155 : *
2156 : * This method is the same as the C function OSRValidate().
2157 : *
2158 : * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2159 : * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2160 : * but contains non-standard PROJECTION[] values.
2161 : */
2162 :
2163 116 : OGRErr OGRSpatialReference::Validate() const
2164 :
2165 : {
2166 154 : for (const auto &str : d->m_wktImportErrors)
2167 : {
2168 38 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2169 : }
2170 116 : for (const auto &str : d->m_wktImportWarnings)
2171 : {
2172 0 : CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2173 : }
2174 116 : if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2175 : {
2176 37 : return OGRERR_CORRUPT_DATA;
2177 : }
2178 79 : if (!d->m_wktImportWarnings.empty())
2179 : {
2180 0 : return OGRERR_UNSUPPORTED_SRS;
2181 : }
2182 79 : return OGRERR_NONE;
2183 : }
2184 :
2185 : /************************************************************************/
2186 : /* OSRValidate() */
2187 : /************************************************************************/
2188 : /**
2189 : * \brief Validate SRS tokens.
2190 : *
2191 : * This function is the same as the C++ method OGRSpatialReference::Validate().
2192 : */
2193 114 : OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2194 :
2195 : {
2196 114 : VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2197 :
2198 114 : return OGRSpatialReference::FromHandle(hSRS)->Validate();
2199 : }
2200 :
2201 : /************************************************************************/
2202 : /* OSRImportFromWkt() */
2203 : /************************************************************************/
2204 :
2205 : /**
2206 : * \brief Import from WKT string.
2207 : *
2208 : * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2209 : * Issues</a> page for implementation details of WKT in OGR.
2210 : *
2211 : * This function is the same as OGRSpatialReference::importFromWkt().
2212 : */
2213 :
2214 293 : OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2215 :
2216 : {
2217 293 : VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2218 :
2219 293 : return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2220 : }
2221 :
2222 : /************************************************************************/
2223 : /* SetNode() */
2224 : /************************************************************************/
2225 :
2226 : /**
2227 : * \brief Set attribute value in spatial reference.
2228 : *
2229 : * Missing intermediate nodes in the path will be created if not already
2230 : * in existence. If the attribute has no children one will be created and
2231 : * assigned the value otherwise the zeroth child will be assigned the value.
2232 : *
2233 : * This method does the same as the C function OSRSetAttrValue().
2234 : *
2235 : * @param pszNodePath full path to attribute to be set. For instance
2236 : * "PROJCS|GEOGCS|UNIT".
2237 : *
2238 : * @param pszNewNodeValue value to be assigned to node, such as "meter".
2239 : * This may be NULL if you just want to force creation of the intermediate
2240 : * path.
2241 : *
2242 : * @return OGRERR_NONE on success.
2243 : */
2244 :
2245 534 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2246 : const char *pszNewNodeValue)
2247 :
2248 : {
2249 : char **papszPathTokens =
2250 534 : CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2251 :
2252 534 : if (CSLCount(papszPathTokens) < 1)
2253 : {
2254 0 : CSLDestroy(papszPathTokens);
2255 0 : return OGRERR_FAILURE;
2256 : }
2257 :
2258 935 : if (GetRoot() == nullptr ||
2259 401 : !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2260 : {
2261 241 : if (EQUAL(papszPathTokens[0], "PROJCS") &&
2262 104 : CSLCount(papszPathTokens) == 1)
2263 : {
2264 104 : CSLDestroy(papszPathTokens);
2265 104 : return SetProjCS(pszNewNodeValue);
2266 : }
2267 : else
2268 : {
2269 33 : SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2270 : }
2271 : }
2272 :
2273 430 : OGR_SRSNode *poNode = GetRoot();
2274 667 : for (int i = 1; papszPathTokens[i] != nullptr; i++)
2275 : {
2276 237 : int j = 0; // Used after for.
2277 :
2278 582 : for (; j < poNode->GetChildCount(); j++)
2279 : {
2280 534 : if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2281 : {
2282 189 : poNode = poNode->GetChild(j);
2283 189 : j = -1;
2284 189 : break;
2285 : }
2286 : }
2287 :
2288 237 : if (j != -1)
2289 : {
2290 48 : OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2291 48 : poNode->AddChild(poNewNode);
2292 48 : poNode = poNewNode;
2293 : }
2294 : }
2295 :
2296 430 : CSLDestroy(papszPathTokens);
2297 :
2298 430 : if (pszNewNodeValue != nullptr)
2299 : {
2300 430 : if (poNode->GetChildCount() > 0)
2301 349 : poNode->GetChild(0)->SetValue(pszNewNodeValue);
2302 : else
2303 81 : poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2304 : };
2305 430 : return OGRERR_NONE;
2306 : }
2307 :
2308 : /************************************************************************/
2309 : /* OSRSetAttrValue() */
2310 : /************************************************************************/
2311 :
2312 : /**
2313 : * \brief Set attribute value in spatial reference.
2314 : *
2315 : * This function is the same as OGRSpatialReference::SetNode()
2316 : */
2317 1 : OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2318 : const char *pszPath, const char *pszValue)
2319 :
2320 : {
2321 1 : VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2322 :
2323 1 : return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2324 : }
2325 :
2326 : /************************************************************************/
2327 : /* SetNode() */
2328 : /************************************************************************/
2329 :
2330 : /**
2331 : * \brief Set attribute value in spatial reference.
2332 : *
2333 : * Missing intermediate nodes in the path will be created if not already
2334 : * in existence. If the attribute has no children one will be created and
2335 : * assigned the value otherwise the zeroth child will be assigned the value.
2336 : *
2337 : * This method does the same as the C function OSRSetAttrValue().
2338 : *
2339 : * @param pszNodePath full path to attribute to be set. For instance
2340 : * "PROJCS|GEOGCS|UNIT".
2341 : *
2342 : * @param dfValue value to be assigned to node.
2343 : *
2344 : * @return OGRERR_NONE on success.
2345 : */
2346 :
2347 0 : OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2348 :
2349 : {
2350 0 : char szValue[64] = {'\0'};
2351 :
2352 0 : if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2353 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2354 : else
2355 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2356 :
2357 0 : return SetNode(pszNodePath, szValue);
2358 : }
2359 :
2360 : /************************************************************************/
2361 : /* SetAngularUnits() */
2362 : /************************************************************************/
2363 :
2364 : /**
2365 : * \brief Set the angular units for the geographic coordinate system.
2366 : *
2367 : * This method creates a UNIT subnode with the specified values as a
2368 : * child of the GEOGCS node.
2369 : *
2370 : * This method does the same as the C function OSRSetAngularUnits().
2371 : *
2372 : * @param pszUnitsName the units name to be used. Some preferred units
2373 : * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2374 : *
2375 : * @param dfInRadians the value to multiple by an angle in the indicated
2376 : * units to transform to radians. Some standard conversion factors can
2377 : * be found in ogr_srs_api.h.
2378 : *
2379 : * @return OGRERR_NONE on success.
2380 : */
2381 :
2382 990 : OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2383 : double dfInRadians)
2384 :
2385 : {
2386 990 : d->bNormInfoSet = FALSE;
2387 :
2388 990 : d->refreshProjObj();
2389 990 : if (!d->m_pj_crs)
2390 0 : return OGRERR_FAILURE;
2391 990 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2392 990 : if (!geodCRS)
2393 0 : return OGRERR_FAILURE;
2394 990 : proj_destroy(geodCRS);
2395 990 : d->demoteFromBoundCRS();
2396 990 : d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2397 : pszUnitsName, dfInRadians,
2398 : nullptr, nullptr));
2399 990 : d->undoDemoteFromBoundCRS();
2400 :
2401 990 : d->m_osAngularUnits = pszUnitsName;
2402 990 : d->m_dfAngularUnitToRadian = dfInRadians;
2403 :
2404 990 : return OGRERR_NONE;
2405 : }
2406 :
2407 : /************************************************************************/
2408 : /* OSRSetAngularUnits() */
2409 : /************************************************************************/
2410 :
2411 : /**
2412 : * \brief Set the angular units for the geographic coordinate system.
2413 : *
2414 : * This function is the same as OGRSpatialReference::SetAngularUnits()
2415 : */
2416 36 : OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2417 : double dfInRadians)
2418 :
2419 : {
2420 36 : VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2421 :
2422 36 : return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* GetAngularUnits() */
2427 : /************************************************************************/
2428 :
2429 : /**
2430 : * \brief Fetch angular geographic coordinate system units.
2431 : *
2432 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2433 : * will be assumed. This method only checks directly under the GEOGCS node
2434 : * for units.
2435 : *
2436 : * This method does the same thing as the C function OSRGetAngularUnits().
2437 : *
2438 : * @param ppszName a pointer to be updated with the pointer to the units name.
2439 : * The returned value remains internal to the OGRSpatialReference and should
2440 : * not be freed, or modified. It may be invalidated on the next
2441 : * OGRSpatialReference call.
2442 : *
2443 : * @return the value to multiply by angular distances to transform them to
2444 : * radians.
2445 : * @since GDAL 2.3.0
2446 : */
2447 :
2448 3521 : double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2449 :
2450 : {
2451 3521 : d->refreshProjObj();
2452 :
2453 3521 : if (!d->m_osAngularUnits.empty())
2454 : {
2455 373 : if (ppszName != nullptr)
2456 209 : *ppszName = d->m_osAngularUnits.c_str();
2457 373 : return d->m_dfAngularUnitToRadian;
2458 : }
2459 :
2460 : do
2461 : {
2462 3148 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2463 : {
2464 113 : break;
2465 : }
2466 :
2467 : auto geodCRS =
2468 3037 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2469 3037 : if (!geodCRS)
2470 : {
2471 0 : break;
2472 : }
2473 : auto coordSys =
2474 3037 : proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2475 3037 : proj_destroy(geodCRS);
2476 3037 : if (!coordSys)
2477 : {
2478 0 : break;
2479 : }
2480 3037 : if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2481 : PJ_CS_TYPE_ELLIPSOIDAL)
2482 : {
2483 2 : proj_destroy(coordSys);
2484 2 : break;
2485 : }
2486 :
2487 3035 : double dfConvFactor = 0.0;
2488 3035 : const char *pszUnitName = nullptr;
2489 3035 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2490 : nullptr, nullptr, &dfConvFactor,
2491 : &pszUnitName, nullptr, nullptr))
2492 : {
2493 0 : proj_destroy(coordSys);
2494 0 : break;
2495 : }
2496 :
2497 3035 : d->m_osAngularUnits = pszUnitName;
2498 :
2499 3035 : proj_destroy(coordSys);
2500 3035 : d->m_dfAngularUnitToRadian = dfConvFactor;
2501 : } while (false);
2502 :
2503 3148 : if (d->m_osAngularUnits.empty())
2504 : {
2505 113 : d->m_osAngularUnits = "degree";
2506 113 : d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2507 : }
2508 :
2509 3148 : if (ppszName != nullptr)
2510 1909 : *ppszName = d->m_osAngularUnits.c_str();
2511 3148 : return d->m_dfAngularUnitToRadian;
2512 : }
2513 :
2514 : /**
2515 : * \brief Fetch angular geographic coordinate system units.
2516 : *
2517 : * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2518 : * will be assumed. This method only checks directly under the GEOGCS node
2519 : * for units.
2520 : *
2521 : * This method does the same thing as the C function OSRGetAngularUnits().
2522 : *
2523 : * @param ppszName a pointer to be updated with the pointer to the units name.
2524 : * The returned value remains internal to the OGRSpatialReference and should
2525 : * not be freed, or modified. It may be invalidated on the next
2526 : * OGRSpatialReference call.
2527 : *
2528 : * @return the value to multiply by angular distances to transform them to
2529 : * radians.
2530 : * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2531 : */
2532 :
2533 0 : double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2534 :
2535 : {
2536 0 : return GetAngularUnits(const_cast<const char **>(ppszName));
2537 : }
2538 :
2539 : /************************************************************************/
2540 : /* OSRGetAngularUnits() */
2541 : /************************************************************************/
2542 :
2543 : /**
2544 : * \brief Fetch angular geographic coordinate system units.
2545 : *
2546 : * This function is the same as OGRSpatialReference::GetAngularUnits()
2547 : */
2548 1 : double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2549 :
2550 : {
2551 1 : VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2552 :
2553 1 : return ToPointer(hSRS)->GetAngularUnits(
2554 1 : const_cast<const char **>(ppszName));
2555 : }
2556 :
2557 : /************************************************************************/
2558 : /* SetLinearUnitsAndUpdateParameters() */
2559 : /************************************************************************/
2560 :
2561 : /**
2562 : * \brief Set the linear units for the projection.
2563 : *
2564 : * This method creates a UNIT subnode with the specified values as a
2565 : * child of the PROJCS or LOCAL_CS node. It works the same as the
2566 : * SetLinearUnits() method, but it also updates all existing linear
2567 : * projection parameter values from the old units to the new units.
2568 : *
2569 : * @param pszName the units name to be used. Some preferred units
2570 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2571 : * and SRS_UL_US_FOOT.
2572 : *
2573 : * @param dfInMeters the value to multiple by a length in the indicated
2574 : * units to transform to meters. Some standard conversion factors can
2575 : * be found in ogr_srs_api.h.
2576 : *
2577 : * @param pszUnitAuthority Unit authority name. Or nullptr
2578 : *
2579 : * @param pszUnitCode Unit code. Or nullptr
2580 : *
2581 : * @return OGRERR_NONE on success.
2582 : */
2583 :
2584 36 : OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2585 : const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2586 : const char *pszUnitCode)
2587 :
2588 : {
2589 36 : if (dfInMeters <= 0.0)
2590 0 : return OGRERR_FAILURE;
2591 :
2592 36 : d->refreshProjObj();
2593 36 : if (!d->m_pj_crs)
2594 0 : return OGRERR_FAILURE;
2595 :
2596 36 : d->demoteFromBoundCRS();
2597 36 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2598 : {
2599 72 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2600 36 : d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2601 : pszUnitAuthority, pszUnitCode, true));
2602 : }
2603 36 : d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2604 : pszName, dfInMeters,
2605 : pszUnitAuthority, pszUnitCode));
2606 36 : d->undoDemoteFromBoundCRS();
2607 :
2608 36 : d->m_osLinearUnits = pszName;
2609 36 : d->dfToMeter = dfInMeters;
2610 :
2611 36 : return OGRERR_NONE;
2612 : }
2613 :
2614 : /************************************************************************/
2615 : /* OSRSetLinearUnitsAndUpdateParameters() */
2616 : /************************************************************************/
2617 :
2618 : /**
2619 : * \brief Set the linear units for the projection.
2620 : *
2621 : * This function is the same as
2622 : * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2623 : */
2624 1 : OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2625 : const char *pszUnits,
2626 : double dfInMeters)
2627 :
2628 : {
2629 1 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2630 : OGRERR_FAILURE);
2631 :
2632 1 : return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2633 1 : dfInMeters);
2634 : }
2635 :
2636 : /************************************************************************/
2637 : /* SetLinearUnits() */
2638 : /************************************************************************/
2639 :
2640 : /**
2641 : * \brief Set the linear units for the projection.
2642 : *
2643 : * This method creates a UNIT subnode with the specified values as a
2644 : * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2645 : * Geographic 3D CRS the vertical axis units will be set.
2646 : *
2647 : * This method does the same as the C function OSRSetLinearUnits().
2648 : *
2649 : * @param pszUnitsName the units name to be used. Some preferred units
2650 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2651 : * and SRS_UL_US_FOOT.
2652 : *
2653 : * @param dfInMeters the value to multiple by a length in the indicated
2654 : * units to transform to meters. Some standard conversion factors can
2655 : * be found in ogr_srs_api.h.
2656 : *
2657 : * @return OGRERR_NONE on success.
2658 : */
2659 :
2660 6281 : OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2661 : double dfInMeters)
2662 :
2663 : {
2664 6281 : return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2665 : }
2666 :
2667 : /************************************************************************/
2668 : /* OSRSetLinearUnits() */
2669 : /************************************************************************/
2670 :
2671 : /**
2672 : * \brief Set the linear units for the projection.
2673 : *
2674 : * This function is the same as OGRSpatialReference::SetLinearUnits()
2675 : */
2676 7 : OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2677 : double dfInMeters)
2678 :
2679 : {
2680 7 : VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2681 :
2682 7 : return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2683 : }
2684 :
2685 : /************************************************************************/
2686 : /* SetTargetLinearUnits() */
2687 : /************************************************************************/
2688 :
2689 : /**
2690 : * \brief Set the linear units for the projection.
2691 : *
2692 : * This method creates a UNIT subnode with the specified values as a
2693 : * child of the target node.
2694 : *
2695 : * This method does the same as the C function OSRSetTargetLinearUnits().
2696 : *
2697 : * @param pszTargetKey the keyword to set the linear units for.
2698 : * i.e. "PROJCS" or "VERT_CS"
2699 : *
2700 : * @param pszUnitsName the units name to be used. Some preferred units
2701 : * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2702 : * and SRS_UL_US_FOOT.
2703 : *
2704 : * @param dfInMeters the value to multiple by a length in the indicated
2705 : * units to transform to meters. Some standard conversion factors can
2706 : * be found in ogr_srs_api.h.
2707 : *
2708 : * @param pszUnitAuthority Unit authority name. Or nullptr
2709 : *
2710 : * @param pszUnitCode Unit code. Or nullptr
2711 : *
2712 : * @return OGRERR_NONE on success.
2713 : *
2714 : * @since OGR 1.9.0
2715 : */
2716 :
2717 9637 : OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2718 : const char *pszUnitsName,
2719 : double dfInMeters,
2720 : const char *pszUnitAuthority,
2721 : const char *pszUnitCode)
2722 :
2723 : {
2724 9637 : if (dfInMeters <= 0.0)
2725 0 : return OGRERR_FAILURE;
2726 :
2727 9637 : d->refreshProjObj();
2728 9637 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2729 9637 : if (pszTargetKey == nullptr)
2730 : {
2731 9637 : if (!d->m_pj_crs)
2732 0 : return OGRERR_FAILURE;
2733 :
2734 9637 : d->demoteFromBoundCRS();
2735 9637 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2736 : {
2737 13508 : d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2738 6754 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2739 : pszUnitAuthority, pszUnitCode, false));
2740 : }
2741 19274 : d->setPjCRS(proj_crs_alter_cs_linear_unit(
2742 9637 : d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2743 : pszUnitAuthority, pszUnitCode));
2744 9637 : d->undoDemoteFromBoundCRS();
2745 :
2746 9637 : d->m_osLinearUnits = pszUnitsName;
2747 9637 : d->dfToMeter = dfInMeters;
2748 :
2749 9637 : return OGRERR_NONE;
2750 : }
2751 :
2752 0 : OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
2753 :
2754 0 : if (poCS == nullptr)
2755 0 : return OGRERR_FAILURE;
2756 :
2757 0 : char szValue[128] = {'\0'};
2758 0 : if (dfInMeters < std::numeric_limits<int>::max() &&
2759 0 : dfInMeters > std::numeric_limits<int>::min() &&
2760 0 : dfInMeters == static_cast<int>(dfInMeters))
2761 0 : snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
2762 : else
2763 0 : OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
2764 :
2765 0 : OGR_SRSNode *poUnits = nullptr;
2766 0 : if (poCS->FindChild("UNIT") >= 0)
2767 : {
2768 0 : poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
2769 0 : if (poUnits->GetChildCount() < 2)
2770 0 : return OGRERR_FAILURE;
2771 0 : poUnits->GetChild(0)->SetValue(pszUnitsName);
2772 0 : poUnits->GetChild(1)->SetValue(szValue);
2773 0 : if (poUnits->FindChild("AUTHORITY") != -1)
2774 0 : poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
2775 : }
2776 : else
2777 : {
2778 0 : poUnits = new OGR_SRSNode("UNIT");
2779 0 : poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
2780 0 : poUnits->AddChild(new OGR_SRSNode(szValue));
2781 :
2782 0 : poCS->AddChild(poUnits);
2783 : }
2784 :
2785 0 : return OGRERR_NONE;
2786 : }
2787 :
2788 : /************************************************************************/
2789 : /* OSRSetLinearUnits() */
2790 : /************************************************************************/
2791 :
2792 : /**
2793 : * \brief Set the linear units for the target node.
2794 : *
2795 : * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
2796 : *
2797 : * @since OGR 1.9.0
2798 : */
2799 1 : OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
2800 : const char *pszTargetKey, const char *pszUnits,
2801 : double dfInMeters)
2802 :
2803 : {
2804 1 : VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
2805 :
2806 1 : return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
2807 1 : dfInMeters);
2808 : }
2809 :
2810 : /************************************************************************/
2811 : /* GetLinearUnits() */
2812 : /************************************************************************/
2813 :
2814 : /**
2815 : * \brief Fetch linear projection units.
2816 : *
2817 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
2818 : * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
2819 : * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
2820 : * axis units will be returned.
2821 : *
2822 : * This method does the same thing as the C function OSRGetLinearUnits()
2823 : *
2824 : * @param ppszName a pointer to be updated with the pointer to the units name.
2825 : * The returned value remains internal to the OGRSpatialReference and should
2826 : * not be freed, or modified. It may be invalidated on the next
2827 : * OGRSpatialReference call.
2828 : *
2829 : * @return the value to multiply by linear distances to transform them to
2830 : * meters.
2831 : * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
2832 : */
2833 :
2834 0 : double OGRSpatialReference::GetLinearUnits(char **ppszName) const
2835 :
2836 : {
2837 0 : return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
2838 : }
2839 :
2840 : /**
2841 : * \brief Fetch linear projection units.
2842 : *
2843 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
2844 : * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
2845 : * for units.
2846 : *
2847 : * This method does the same thing as the C function OSRGetLinearUnits()
2848 : *
2849 : * @param ppszName a pointer to be updated with the pointer to the units name.
2850 : * The returned value remains internal to the OGRSpatialReference and should
2851 : * not be freed, or modified. It may be invalidated on the next
2852 : * OGRSpatialReference call.
2853 : *
2854 : * @return the value to multiply by linear distances to transform them to
2855 : * meters.
2856 : * @since GDAL 2.3.0
2857 : */
2858 :
2859 13375 : double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
2860 :
2861 : {
2862 13375 : return GetTargetLinearUnits(nullptr, ppszName);
2863 : }
2864 :
2865 : /************************************************************************/
2866 : /* OSRGetLinearUnits() */
2867 : /************************************************************************/
2868 :
2869 : /**
2870 : * \brief Fetch linear projection units.
2871 : *
2872 : * This function is the same as OGRSpatialReference::GetLinearUnits()
2873 : */
2874 203 : double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2875 :
2876 : {
2877 203 : VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
2878 :
2879 203 : return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
2880 : }
2881 :
2882 : /************************************************************************/
2883 : /* GetTargetLinearUnits() */
2884 : /************************************************************************/
2885 :
2886 : /**
2887 : * \brief Fetch linear units for target.
2888 : *
2889 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
2890 : *
2891 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
2892 : *
2893 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
2894 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
2895 : * GEOCCS, GEOGCS and VERT_CS are looked up)
2896 : * @param ppszName a pointer to be updated with the pointer to the units name.
2897 : * The returned value remains internal to the OGRSpatialReference and should not
2898 : * be freed, or modified. It may be invalidated on the next
2899 : * OGRSpatialReference call. ppszName can be set to NULL.
2900 : *
2901 : * @return the value to multiply by linear distances to transform them to
2902 : * meters.
2903 : *
2904 : * @since OGR 1.9.0
2905 : * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
2906 : * const.
2907 : */
2908 :
2909 13614 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
2910 : const char **ppszName) const
2911 :
2912 : {
2913 13614 : d->refreshProjObj();
2914 :
2915 13614 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2916 13614 : if (pszTargetKey == nullptr)
2917 : {
2918 : // Use cached result if available
2919 13434 : if (!d->m_osLinearUnits.empty())
2920 : {
2921 6699 : if (ppszName)
2922 6070 : *ppszName = d->m_osLinearUnits.c_str();
2923 6699 : return d->dfToMeter;
2924 : }
2925 :
2926 : while (true)
2927 : {
2928 6735 : if (d->m_pj_crs == nullptr)
2929 : {
2930 226 : break;
2931 : }
2932 :
2933 6509 : d->demoteFromBoundCRS();
2934 6509 : PJ *coordSys = nullptr;
2935 6509 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
2936 : {
2937 30 : for (int iComponent = 0; iComponent < 2; iComponent++)
2938 : {
2939 30 : auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
2940 30 : d->m_pj_crs, iComponent);
2941 30 : if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
2942 : {
2943 : auto temp =
2944 0 : proj_get_source_crs(d->getPROJContext(), subCRS);
2945 0 : proj_destroy(subCRS);
2946 0 : subCRS = temp;
2947 : }
2948 60 : if (subCRS &&
2949 30 : (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
2950 16 : proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
2951 12 : proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
2952 : {
2953 24 : coordSys = proj_crs_get_coordinate_system(
2954 : d->getPROJContext(), subCRS);
2955 24 : proj_destroy(subCRS);
2956 24 : break;
2957 : }
2958 6 : else if (subCRS)
2959 : {
2960 6 : proj_destroy(subCRS);
2961 : }
2962 : }
2963 24 : if (coordSys == nullptr)
2964 : {
2965 0 : d->undoDemoteFromBoundCRS();
2966 0 : break;
2967 : }
2968 : }
2969 : else
2970 : {
2971 6485 : coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
2972 6485 : d->m_pj_crs);
2973 : }
2974 :
2975 6509 : d->undoDemoteFromBoundCRS();
2976 6509 : if (!coordSys)
2977 : {
2978 0 : break;
2979 : }
2980 6509 : auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
2981 :
2982 6509 : if (csType != PJ_CS_TYPE_CARTESIAN &&
2983 1303 : csType != PJ_CS_TYPE_VERTICAL &&
2984 0 : csType != PJ_CS_TYPE_ELLIPSOIDAL &&
2985 : csType != PJ_CS_TYPE_SPHERICAL)
2986 : {
2987 0 : proj_destroy(coordSys);
2988 0 : break;
2989 : }
2990 :
2991 6509 : int axis = 0;
2992 :
2993 6509 : if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
2994 : csType == PJ_CS_TYPE_SPHERICAL)
2995 : {
2996 : const int axisCount =
2997 1303 : proj_cs_get_axis_count(d->getPROJContext(), coordSys);
2998 :
2999 1303 : if (axisCount == 3)
3000 : {
3001 4 : axis = 2;
3002 : }
3003 : else
3004 : {
3005 1299 : proj_destroy(coordSys);
3006 1299 : break;
3007 : }
3008 : }
3009 :
3010 5210 : double dfConvFactor = 0.0;
3011 5210 : const char *pszUnitName = nullptr;
3012 5210 : if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3013 : nullptr, nullptr, nullptr, &dfConvFactor,
3014 : &pszUnitName, nullptr, nullptr))
3015 : {
3016 0 : proj_destroy(coordSys);
3017 0 : break;
3018 : }
3019 :
3020 5210 : d->m_osLinearUnits = pszUnitName;
3021 5210 : d->dfToMeter = dfConvFactor;
3022 5210 : if (ppszName)
3023 951 : *ppszName = d->m_osLinearUnits.c_str();
3024 :
3025 5210 : proj_destroy(coordSys);
3026 5210 : return dfConvFactor;
3027 : }
3028 :
3029 1525 : d->m_osLinearUnits = "unknown";
3030 1525 : d->dfToMeter = 1.0;
3031 :
3032 1525 : if (ppszName != nullptr)
3033 1346 : *ppszName = d->m_osLinearUnits.c_str();
3034 1525 : return 1.0;
3035 : }
3036 :
3037 180 : const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3038 :
3039 180 : if (ppszName != nullptr)
3040 34 : *ppszName = "unknown";
3041 :
3042 180 : if (poCS == nullptr)
3043 145 : return 1.0;
3044 :
3045 105 : for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3046 : {
3047 105 : const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3048 :
3049 105 : if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3050 : {
3051 35 : if (ppszName != nullptr)
3052 34 : *ppszName = poChild->GetChild(0)->GetValue();
3053 :
3054 35 : return CPLAtof(poChild->GetChild(1)->GetValue());
3055 : }
3056 : }
3057 :
3058 0 : return 1.0;
3059 : }
3060 :
3061 : /**
3062 : * \brief Fetch linear units for target.
3063 : *
3064 : * If no units are available, a value of "Meters" and 1.0 will be assumed.
3065 : *
3066 : * This method does the same thing as the C function OSRGetTargetLinearUnits()
3067 : *
3068 : * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3069 : * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3070 : * GEOCCS and VERT_CS are looked up)
3071 : * @param ppszName a pointer to be updated with the pointer to the units name.
3072 : * The returned value remains internal to the OGRSpatialReference and should not
3073 : * be freed, or modified. It may be invalidated on the next
3074 : * OGRSpatialReference call. ppszName can be set to NULL.
3075 : *
3076 : * @return the value to multiply by linear distances to transform them to
3077 : * meters.
3078 : *
3079 : * @since GDAL 2.3.0
3080 : */
3081 :
3082 0 : double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3083 : char **ppszName) const
3084 :
3085 : {
3086 0 : return GetTargetLinearUnits(pszTargetKey,
3087 0 : const_cast<const char **>(ppszName));
3088 : }
3089 :
3090 : /************************************************************************/
3091 : /* OSRGetTargetLinearUnits() */
3092 : /************************************************************************/
3093 :
3094 : /**
3095 : * \brief Fetch linear projection units.
3096 : *
3097 : * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3098 : *
3099 : * @since OGR 1.9.0
3100 : */
3101 4 : double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3102 : const char *pszTargetKey, char **ppszName)
3103 :
3104 : {
3105 4 : VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3106 :
3107 4 : return ToPointer(hSRS)->GetTargetLinearUnits(
3108 4 : pszTargetKey, const_cast<const char **>(ppszName));
3109 : }
3110 :
3111 : /************************************************************************/
3112 : /* GetPrimeMeridian() */
3113 : /************************************************************************/
3114 :
3115 : /**
3116 : * \brief Fetch prime meridian info.
3117 : *
3118 : * Returns the offset of the prime meridian from greenwich in degrees,
3119 : * and the prime meridian name (if requested). If no PRIMEM value exists
3120 : * in the coordinate system definition a value of "Greenwich" and an
3121 : * offset of 0.0 is assumed.
3122 : *
3123 : * If the prime meridian name is returned, the pointer is to an internal
3124 : * copy of the name. It should not be freed, altered or depended on after
3125 : * the next OGR call.
3126 : *
3127 : * This method is the same as the C function OSRGetPrimeMeridian().
3128 : *
3129 : * @param ppszName return location for prime meridian name. If NULL, name
3130 : * is not returned.
3131 : *
3132 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3133 : * degrees.
3134 : * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3135 : */
3136 :
3137 1313 : double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3138 :
3139 : {
3140 1313 : d->refreshProjObj();
3141 :
3142 1313 : if (!d->m_osPrimeMeridianName.empty())
3143 : {
3144 39 : if (ppszName != nullptr)
3145 1 : *ppszName = d->m_osPrimeMeridianName.c_str();
3146 39 : return d->dfFromGreenwich;
3147 : }
3148 :
3149 : while (true)
3150 : {
3151 1274 : if (!d->m_pj_crs)
3152 0 : break;
3153 :
3154 1274 : auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3155 1274 : if (!pm)
3156 0 : break;
3157 :
3158 1274 : d->m_osPrimeMeridianName = proj_get_name(pm);
3159 1274 : if (ppszName)
3160 30 : *ppszName = d->m_osPrimeMeridianName.c_str();
3161 1274 : double dfLongitude = 0.0;
3162 1274 : double dfConvFactor = 0.0;
3163 1274 : proj_prime_meridian_get_parameters(
3164 : d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3165 1274 : proj_destroy(pm);
3166 2548 : d->dfFromGreenwich =
3167 1274 : dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3168 1274 : return d->dfFromGreenwich;
3169 : }
3170 :
3171 0 : d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3172 0 : d->dfFromGreenwich = 0.0;
3173 0 : if (ppszName != nullptr)
3174 0 : *ppszName = d->m_osPrimeMeridianName.c_str();
3175 0 : return d->dfFromGreenwich;
3176 : }
3177 :
3178 : /**
3179 : * \brief Fetch prime meridian info.
3180 : *
3181 : * Returns the offset of the prime meridian from greenwich in degrees,
3182 : * and the prime meridian name (if requested). If no PRIMEM value exists
3183 : * in the coordinate system definition a value of "Greenwich" and an
3184 : * offset of 0.0 is assumed.
3185 : *
3186 : * If the prime meridian name is returned, the pointer is to an internal
3187 : * copy of the name. It should not be freed, altered or depended on after
3188 : * the next OGR call.
3189 : *
3190 : * This method is the same as the C function OSRGetPrimeMeridian().
3191 : *
3192 : * @param ppszName return location for prime meridian name. If NULL, name
3193 : * is not returned.
3194 : *
3195 : * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3196 : * degrees.
3197 : * @since GDAL 2.3.0
3198 : */
3199 :
3200 0 : double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3201 :
3202 : {
3203 0 : return GetPrimeMeridian(const_cast<const char **>(ppszName));
3204 : }
3205 :
3206 : /************************************************************************/
3207 : /* OSRGetPrimeMeridian() */
3208 : /************************************************************************/
3209 :
3210 : /**
3211 : * \brief Fetch prime meridian info.
3212 : *
3213 : * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3214 : */
3215 1 : double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3216 :
3217 : {
3218 1 : VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3219 :
3220 1 : return ToPointer(hSRS)->GetPrimeMeridian(
3221 1 : const_cast<const char **>(ppszName));
3222 : }
3223 :
3224 : /************************************************************************/
3225 : /* SetGeogCS() */
3226 : /************************************************************************/
3227 :
3228 : /**
3229 : * \brief Set geographic coordinate system.
3230 : *
3231 : * This method is used to set the datum, ellipsoid, prime meridian and
3232 : * angular units for a geographic coordinate system. It can be used on its
3233 : * own to establish a geographic spatial reference, or applied to a
3234 : * projected coordinate system to establish the underlying geographic
3235 : * coordinate system.
3236 : *
3237 : * This method does the same as the C function OSRSetGeogCS().
3238 : *
3239 : * @param pszGeogName user visible name for the geographic coordinate system
3240 : * (not to serve as a key).
3241 : *
3242 : * @param pszDatumName key name for this datum. The OpenGIS specification
3243 : * lists some known values, and otherwise EPSG datum names with a standard
3244 : * transformation are considered legal keys.
3245 : *
3246 : * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3247 : *
3248 : * @param dfSemiMajor the semi major axis of the spheroid.
3249 : *
3250 : * @param dfInvFlattening the inverse flattening for the spheroid.
3251 : * This can be computed from the semi minor axis as
3252 : * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3253 : *
3254 : * @param pszPMName the name of the prime meridian (not to serve as a key)
3255 : * If this is NULL a default value of "Greenwich" will be used.
3256 : *
3257 : * @param dfPMOffset the longitude of Greenwich relative to this prime
3258 : * meridian. Always in Degrees
3259 : *
3260 : * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3261 : * standard names). If NULL a value of "degrees" will be assumed.
3262 : *
3263 : * @param dfConvertToRadians value to multiply angular units by to transform
3264 : * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3265 : * pszAngularUnits is NULL.
3266 : *
3267 : * @return OGRERR_NONE on success.
3268 : */
3269 :
3270 6195 : OGRErr OGRSpatialReference::SetGeogCS(
3271 : const char *pszGeogName, const char *pszDatumName,
3272 : const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3273 : const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3274 : double dfConvertToRadians)
3275 :
3276 : {
3277 6195 : d->bNormInfoSet = FALSE;
3278 6195 : d->m_osAngularUnits.clear();
3279 6195 : d->m_dfAngularUnitToRadian = 0.0;
3280 6195 : d->m_osPrimeMeridianName.clear();
3281 6195 : d->dfFromGreenwich = 0.0;
3282 :
3283 : /* -------------------------------------------------------------------- */
3284 : /* For a geocentric coordinate system we want to set the datum */
3285 : /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3286 : /* temporary srs and use the copy method which has special */
3287 : /* handling for GEOCCS. */
3288 : /* -------------------------------------------------------------------- */
3289 6195 : if (IsGeocentric())
3290 : {
3291 4 : OGRSpatialReference oGCS;
3292 :
3293 2 : oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3294 : dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3295 : dfConvertToRadians);
3296 2 : return CopyGeogCSFrom(&oGCS);
3297 : }
3298 :
3299 6193 : auto cs = proj_create_ellipsoidal_2D_cs(
3300 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3301 : dfConvertToRadians);
3302 : // Prime meridian expressed in Degree
3303 6193 : auto obj = proj_create_geographic_crs(
3304 : d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3305 : dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3306 6193 : proj_destroy(cs);
3307 :
3308 10084 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3309 3891 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3310 : {
3311 2302 : d->setPjCRS(obj);
3312 : }
3313 3891 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3314 : {
3315 7782 : d->setPjCRS(
3316 3891 : proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3317 3891 : proj_destroy(obj);
3318 : }
3319 : else
3320 : {
3321 0 : proj_destroy(obj);
3322 : }
3323 :
3324 6193 : return OGRERR_NONE;
3325 : }
3326 :
3327 : /************************************************************************/
3328 : /* OSRSetGeogCS() */
3329 : /************************************************************************/
3330 :
3331 : /**
3332 : * \brief Set geographic coordinate system.
3333 : *
3334 : * This function is the same as OGRSpatialReference::SetGeogCS()
3335 : */
3336 26 : OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3337 : const char *pszDatumName, const char *pszSpheroidName,
3338 : double dfSemiMajor, double dfInvFlattening,
3339 : const char *pszPMName, double dfPMOffset,
3340 : const char *pszAngularUnits, double dfConvertToRadians)
3341 :
3342 : {
3343 26 : VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3344 :
3345 26 : return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3346 : pszSpheroidName, dfSemiMajor,
3347 : dfInvFlattening, pszPMName, dfPMOffset,
3348 26 : pszAngularUnits, dfConvertToRadians);
3349 : }
3350 :
3351 : /************************************************************************/
3352 : /* SetWellKnownGeogCS() */
3353 : /************************************************************************/
3354 :
3355 : /**
3356 : * \brief Set a GeogCS based on well known name.
3357 : *
3358 : * This may be called on an empty OGRSpatialReference to make a geographic
3359 : * coordinate system, or on something with an existing PROJCS node to
3360 : * set the underlying geographic coordinate system of a projected coordinate
3361 : * system.
3362 : *
3363 : * The following well known text values are currently supported,
3364 : * Except for "EPSG:n", the others are without dependency on EPSG data files:
3365 : * <ul>
3366 : * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3367 : * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3368 : * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3369 : * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3370 : * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3371 : * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3372 : * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3373 : * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3374 : * </ul>
3375 : *
3376 : * @param pszName name of well known geographic coordinate system.
3377 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3378 : * recognised, the target object is already initialized, or an EPSG value
3379 : * can't be successfully looked up.
3380 : */
3381 :
3382 12787 : OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3383 :
3384 : {
3385 : /* -------------------------------------------------------------------- */
3386 : /* Check for EPSG authority numbers. */
3387 : /* -------------------------------------------------------------------- */
3388 12787 : if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3389 : {
3390 84 : OGRSpatialReference oSRS2;
3391 42 : const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3392 42 : if (eErr != OGRERR_NONE)
3393 0 : return eErr;
3394 :
3395 42 : if (!oSRS2.IsGeographic())
3396 0 : return OGRERR_FAILURE;
3397 :
3398 42 : return CopyGeogCSFrom(&oSRS2);
3399 : }
3400 :
3401 : /* -------------------------------------------------------------------- */
3402 : /* Check for simple names. */
3403 : /* -------------------------------------------------------------------- */
3404 12745 : const char *pszWKT = nullptr;
3405 :
3406 12745 : if (EQUAL(pszName, "WGS84"))
3407 : {
3408 11927 : pszWKT = SRS_WKT_WGS84_LAT_LONG;
3409 : }
3410 818 : else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3411 : {
3412 94 : pszWKT =
3413 : "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3414 : "ELLIPSOID[\"WGS "
3415 : "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3416 : "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3417 : "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3418 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3419 : "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3420 : "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3421 : "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3422 : "ID[\"OGC\",\"CRS84\"]]";
3423 : }
3424 724 : else if (EQUAL(pszName, "WGS72"))
3425 48 : pszWKT =
3426 : "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3427 : "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3428 : "AUTHORITY[\"EPSG\",\"6322\"]],"
3429 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3430 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3431 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3432 : "AUTHORITY[\"EPSG\",\"4322\"]]";
3433 :
3434 676 : else if (EQUAL(pszName, "NAD27"))
3435 175 : pszWKT =
3436 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3437 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3438 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3439 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3440 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3441 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3442 : "AUTHORITY[\"EPSG\",\"4267\"]]";
3443 :
3444 501 : else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3445 0 : pszWKT =
3446 : "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3447 : "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3448 : "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3449 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3450 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3451 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3452 :
3453 501 : else if (EQUAL(pszName, "NAD83"))
3454 497 : pszWKT =
3455 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3456 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3457 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3458 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3459 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3460 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3461 : "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3462 : "\"EPSG\",\"4269\"]]";
3463 :
3464 4 : else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3465 0 : pszWKT =
3466 : "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3467 : "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3468 : "AUTHORITY[\"EPSG\",\"7019\"]],"
3469 : "AUTHORITY[\"EPSG\",\"6269\"]],"
3470 : "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3471 : "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3472 : "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3473 :
3474 : else
3475 4 : return OGRERR_FAILURE;
3476 :
3477 : /* -------------------------------------------------------------------- */
3478 : /* Import the WKT */
3479 : /* -------------------------------------------------------------------- */
3480 25482 : OGRSpatialReference oSRS2;
3481 12741 : const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3482 12741 : if (eErr != OGRERR_NONE)
3483 0 : return eErr;
3484 :
3485 : /* -------------------------------------------------------------------- */
3486 : /* Copy over. */
3487 : /* -------------------------------------------------------------------- */
3488 12741 : return CopyGeogCSFrom(&oSRS2);
3489 : }
3490 :
3491 : /************************************************************************/
3492 : /* OSRSetWellKnownGeogCS() */
3493 : /************************************************************************/
3494 :
3495 : /**
3496 : * \brief Set a GeogCS based on well known name.
3497 : *
3498 : * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3499 : */
3500 116 : OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3501 :
3502 : {
3503 116 : VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3504 :
3505 116 : return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3506 : }
3507 :
3508 : /************************************************************************/
3509 : /* CopyGeogCSFrom() */
3510 : /************************************************************************/
3511 :
3512 : /**
3513 : * \brief Copy GEOGCS from another OGRSpatialReference.
3514 : *
3515 : * The GEOGCS information is copied into this OGRSpatialReference from another.
3516 : * If this object has a PROJCS root already, the GEOGCS is installed within
3517 : * it, otherwise it is installed as the root.
3518 : *
3519 : * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3520 : *
3521 : * @return OGRERR_NONE on success or an error code.
3522 : */
3523 :
3524 13336 : OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3525 :
3526 : {
3527 13336 : d->bNormInfoSet = FALSE;
3528 13336 : d->m_osAngularUnits.clear();
3529 13336 : d->m_dfAngularUnitToRadian = 0.0;
3530 13336 : d->m_osPrimeMeridianName.clear();
3531 13336 : d->dfFromGreenwich = 0.0;
3532 :
3533 13336 : d->refreshProjObj();
3534 13336 : poSrcSRS->d->refreshProjObj();
3535 13336 : if (!poSrcSRS->d->m_pj_crs)
3536 : {
3537 0 : return OGRERR_FAILURE;
3538 : }
3539 : auto geodCRS =
3540 13336 : proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3541 13336 : if (!geodCRS)
3542 : {
3543 0 : return OGRERR_FAILURE;
3544 : }
3545 :
3546 : /* -------------------------------------------------------------------- */
3547 : /* Handle geocentric coordinate systems specially. We just */
3548 : /* want to copy the DATUM. */
3549 : /* -------------------------------------------------------------------- */
3550 13336 : if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3551 : {
3552 3 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3553 : #if PROJ_VERSION_MAJOR > 7 || \
3554 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3555 : if (datum == nullptr)
3556 : {
3557 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3558 : }
3559 : #endif
3560 3 : if (datum == nullptr)
3561 : {
3562 0 : proj_destroy(geodCRS);
3563 0 : return OGRERR_FAILURE;
3564 : }
3565 :
3566 3 : const char *pszUnitName = nullptr;
3567 3 : double unitConvFactor = GetLinearUnits(&pszUnitName);
3568 :
3569 3 : auto pj_crs = proj_create_geocentric_crs_from_datum(
3570 3 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3571 : unitConvFactor);
3572 3 : proj_destroy(datum);
3573 :
3574 3 : d->setPjCRS(pj_crs);
3575 : }
3576 :
3577 13333 : else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3578 : {
3579 300 : auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3580 300 : d->m_pj_crs, geodCRS);
3581 300 : d->setPjCRS(pj_crs);
3582 : }
3583 :
3584 : else
3585 : {
3586 13033 : d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3587 : }
3588 :
3589 : // Apply TOWGS84 of source CRS
3590 13336 : if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3591 : {
3592 : auto target =
3593 1 : proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3594 1 : auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3595 1 : poSrcSRS->d->m_pj_crs);
3596 1 : d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3597 : target, co));
3598 1 : proj_destroy(target);
3599 1 : proj_destroy(co);
3600 : }
3601 :
3602 13336 : proj_destroy(geodCRS);
3603 :
3604 13336 : return OGRERR_NONE;
3605 : }
3606 :
3607 : /************************************************************************/
3608 : /* OSRCopyGeogCSFrom() */
3609 : /************************************************************************/
3610 :
3611 : /**
3612 : * \brief Copy GEOGCS from another OGRSpatialReference.
3613 : *
3614 : * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3615 : */
3616 1 : OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3617 : const OGRSpatialReferenceH hSrcSRS)
3618 :
3619 : {
3620 1 : VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3621 1 : VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3622 :
3623 1 : return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3624 : }
3625 :
3626 : /************************************************************************/
3627 : /* SET_FROM_USER_INPUT_LIMITATIONS_get() */
3628 : /************************************************************************/
3629 :
3630 : /** Limitations for OGRSpatialReference::SetFromUserInput().
3631 : *
3632 : * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3633 : */
3634 : const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3635 : "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3636 :
3637 : /**
3638 : * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3639 : */
3640 2331 : CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3641 : {
3642 2331 : return SET_FROM_USER_INPUT_LIMITATIONS;
3643 : }
3644 :
3645 : /************************************************************************/
3646 : /* RemoveIDFromMemberOfEnsembles() */
3647 : /************************************************************************/
3648 :
3649 : // cppcheck-suppress constParameterReference
3650 73 : static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3651 : {
3652 : // Remove "id" from members of datum ensembles for compatibility with
3653 : // older PROJ versions
3654 : // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3655 : // and https://github.com/OSGeo/PROJ/pull/3221
3656 73 : if (obj.GetType() == CPLJSONObject::Type::Object)
3657 : {
3658 89 : for (auto &subObj : obj.GetChildren())
3659 : {
3660 70 : RemoveIDFromMemberOfEnsembles(subObj);
3661 : }
3662 : }
3663 60 : else if (obj.GetType() == CPLJSONObject::Type::Array &&
3664 60 : obj.GetName() == "members")
3665 : {
3666 18 : for (auto &subObj : obj.ToArray())
3667 : {
3668 15 : if (subObj.GetType() == CPLJSONObject::Type::Object)
3669 : {
3670 14 : subObj.Delete("id");
3671 : }
3672 : }
3673 : }
3674 73 : }
3675 :
3676 : /************************************************************************/
3677 : /* SetFromUserInput() */
3678 : /************************************************************************/
3679 :
3680 : /**
3681 : * \brief Set spatial reference from various text formats.
3682 : *
3683 : * This method will examine the provided input, and try to deduce the
3684 : * format, and then use it to initialize the spatial reference system. It
3685 : * may take the following forms:
3686 : *
3687 : * <ol>
3688 : * <li> Well Known Text definition - passed on to importFromWkt().
3689 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3690 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3691 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3692 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3693 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3694 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3695 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3696 : * WGS84 or WGS72.
3697 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3698 : * <li> PROJJSON (PROJ >= 6.2)
3699 : * </ol>
3700 : *
3701 : * It is expected that this method will be extended in the future to support
3702 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3703 : * State Plane definitions.
3704 : *
3705 : * This method is intended to be flexible, but by its nature it is
3706 : * imprecise as it must guess information about the format intended. When
3707 : * possible applications should call the specific method appropriate if the
3708 : * input is known to be in a particular format.
3709 : *
3710 : * This method does the same thing as the OSRSetFromUserInput() function.
3711 : *
3712 : * @param pszDefinition text definition to try to deduce SRS from.
3713 : *
3714 : * @return OGRERR_NONE on success, or an error code if the name isn't
3715 : * recognised, the definition is corrupt, or an EPSG value can't be
3716 : * successfully looked up.
3717 : */
3718 :
3719 13189 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3720 : {
3721 13189 : return SetFromUserInput(pszDefinition, nullptr);
3722 : }
3723 :
3724 : /**
3725 : * \brief Set spatial reference from various text formats.
3726 : *
3727 : * This method will examine the provided input, and try to deduce the
3728 : * format, and then use it to initialize the spatial reference system. It
3729 : * may take the following forms:
3730 : *
3731 : * <ol>
3732 : * <li> Well Known Text definition - passed on to importFromWkt().
3733 : * <li> "EPSG:n" - number passed on to importFromEPSG().
3734 : * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3735 : * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3736 : * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3737 : * <li> PROJ.4 definitions - passed on to importFromProj4().
3738 : * <li> filename - file read for WKT, XML or PROJ.4 definition.
3739 : * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3740 : * WGS84 or WGS72.
3741 : * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3742 : * <li> PROJJSON (PROJ >= 6.2)
3743 : * </ol>
3744 : *
3745 : * It is expected that this method will be extended in the future to support
3746 : * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3747 : * State Plane definitions.
3748 : *
3749 : * This method is intended to be flexible, but by its nature it is
3750 : * imprecise as it must guess information about the format intended. When
3751 : * possible applications should call the specific method appropriate if the
3752 : * input is known to be in a particular format.
3753 : *
3754 : * This method does the same thing as the OSRSetFromUserInput() and
3755 : * OSRSetFromUserInputEx() functions.
3756 : *
3757 : * @param pszDefinition text definition to try to deduce SRS from.
3758 : *
3759 : * @param papszOptions NULL terminated list of options, or NULL.
3760 : * <ol>
3761 : * <li> ALLOW_NETWORK_ACCESS=YES/NO.
3762 : * Whether http:// or https:// access is allowed. Defaults to YES.
3763 : * <li> ALLOW_FILE_ACCESS=YES/NO.
3764 : * Whether reading a file using the Virtual File System layer is allowed
3765 : * (can also involve network access). Defaults to YES.
3766 : * </ol>
3767 : *
3768 : * @return OGRERR_NONE on success, or an error code if the name isn't
3769 : * recognised, the definition is corrupt, or an EPSG value can't be
3770 : * successfully looked up.
3771 : */
3772 :
3773 16915 : OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
3774 : CSLConstList papszOptions)
3775 : {
3776 : // Skip leading white space
3777 16915 : while (isspace(static_cast<unsigned char>(*pszDefinition)))
3778 2 : pszDefinition++;
3779 :
3780 16913 : if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
3781 : {
3782 1 : pszDefinition += 6;
3783 : }
3784 :
3785 : /* -------------------------------------------------------------------- */
3786 : /* Is it a recognised syntax? */
3787 : /* -------------------------------------------------------------------- */
3788 16913 : const char *const wktKeywords[] = {
3789 : // WKT1
3790 : "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
3791 : // WKT2"
3792 : "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
3793 : "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
3794 : "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
3795 208473 : for (const char *keyword : wktKeywords)
3796 : {
3797 199321 : if (STARTS_WITH_CI(pszDefinition, keyword))
3798 : {
3799 7761 : return importFromWkt(pszDefinition);
3800 : }
3801 : }
3802 :
3803 9152 : const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
3804 9152 : if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
3805 : {
3806 7029 : OGRErr eStatus = OGRERR_NONE;
3807 :
3808 7029 : if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
3809 : {
3810 : // Use proj_create() as it allows things like EPSG:3157+4617
3811 : // that are not normally supported by the below code that
3812 : // builds manually a compound CRS
3813 57 : PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
3814 57 : if (!pj)
3815 : {
3816 0 : return OGRERR_FAILURE;
3817 : }
3818 57 : Clear();
3819 57 : d->setPjCRS(pj);
3820 57 : return OGRERR_NONE;
3821 : }
3822 : else
3823 : {
3824 : eStatus =
3825 6972 : importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
3826 : }
3827 :
3828 6972 : return eStatus;
3829 : }
3830 :
3831 2123 : if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
3832 1487 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
3833 1486 : STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
3834 1428 : STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
3835 1428 : STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
3836 1428 : STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
3837 695 : return importFromURN(pszDefinition);
3838 :
3839 1428 : if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
3840 1426 : STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
3841 1425 : STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
3842 842 : STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
3843 841 : STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
3844 587 : return importFromCRSURL(pszDefinition);
3845 :
3846 841 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
3847 1 : return importFromWMSAUTO(pszDefinition);
3848 :
3849 : // WMS/WCS OGC codes like OGC:CRS84.
3850 840 : if (EQUAL(pszDefinition, "OGC:CRS84"))
3851 23 : return SetWellKnownGeogCS(pszDefinition + 4);
3852 :
3853 817 : if (STARTS_WITH_CI(pszDefinition, "CRS:"))
3854 1 : return SetWellKnownGeogCS(pszDefinition);
3855 :
3856 816 : if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
3857 : {
3858 0 : char *pszFile = CPLStrdup(pszDefinition + 5);
3859 0 : char *pszCode = strstr(pszFile, ",") + 1;
3860 :
3861 0 : pszCode[-1] = '\0';
3862 :
3863 0 : OGRErr err = importFromDict(pszFile, pszCode);
3864 0 : CPLFree(pszFile);
3865 :
3866 0 : return err;
3867 : }
3868 :
3869 816 : if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
3870 814 : EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
3871 : {
3872 247 : Clear();
3873 247 : return SetWellKnownGeogCS(pszDefinition);
3874 : }
3875 :
3876 : // PROJJSON
3877 569 : if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
3878 44 : (strstr(pszDefinition, "GeodeticCRS") ||
3879 44 : strstr(pszDefinition, "GeographicCRS") ||
3880 16 : strstr(pszDefinition, "ProjectedCRS") ||
3881 0 : strstr(pszDefinition, "VerticalCRS") ||
3882 0 : strstr(pszDefinition, "BoundCRS") ||
3883 0 : strstr(pszDefinition, "CompoundCRS") ||
3884 0 : strstr(pszDefinition, "DerivedGeodeticCRS") ||
3885 0 : strstr(pszDefinition, "DerivedGeographicCRS") ||
3886 0 : strstr(pszDefinition, "DerivedProjectedCRS") ||
3887 0 : strstr(pszDefinition, "DerivedVerticalCRS") ||
3888 0 : strstr(pszDefinition, "EngineeringCRS") ||
3889 0 : strstr(pszDefinition, "DerivedEngineeringCRS") ||
3890 0 : strstr(pszDefinition, "ParametricCRS") ||
3891 0 : strstr(pszDefinition, "DerivedParametricCRS") ||
3892 0 : strstr(pszDefinition, "TemporalCRS") ||
3893 0 : strstr(pszDefinition, "DerivedTemporalCRS")))
3894 : {
3895 : PJ *pj;
3896 44 : if (strstr(pszDefinition, "datum_ensemble") != nullptr)
3897 : {
3898 : // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
3899 : // a unknown id.
3900 3 : CPLJSONDocument oCRSDoc;
3901 3 : if (!oCRSDoc.LoadMemory(pszDefinition))
3902 0 : return OGRERR_CORRUPT_DATA;
3903 3 : CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
3904 3 : RemoveIDFromMemberOfEnsembles(oCRSRoot);
3905 3 : pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
3906 : }
3907 : else
3908 : {
3909 41 : pj = proj_create(d->getPROJContext(), pszDefinition);
3910 : }
3911 44 : if (!pj)
3912 : {
3913 2 : return OGRERR_FAILURE;
3914 : }
3915 42 : Clear();
3916 42 : d->setPjCRS(pj);
3917 42 : return OGRERR_NONE;
3918 : }
3919 :
3920 525 : if (strstr(pszDefinition, "+proj") != nullptr ||
3921 195 : strstr(pszDefinition, "+init") != nullptr)
3922 330 : return importFromProj4(pszDefinition);
3923 :
3924 195 : if (STARTS_WITH_CI(pszDefinition, "http://") ||
3925 195 : STARTS_WITH_CI(pszDefinition, "https://"))
3926 : {
3927 1 : if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
3928 : "ALLOW_NETWORK_ACCESS", "YES")))
3929 0 : return importFromUrl(pszDefinition);
3930 :
3931 1 : CPLError(CE_Failure, CPLE_AppDefined,
3932 : "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
3933 : pszDefinition);
3934 1 : return OGRERR_FAILURE;
3935 : }
3936 :
3937 194 : if (EQUAL(pszDefinition, "osgb:BNG"))
3938 : {
3939 13 : return importFromEPSG(27700);
3940 : }
3941 :
3942 : // Used by German CityGML files
3943 181 : if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
3944 : {
3945 : // "ETRS89 / UTM Zone 32N + DHHN92 height"
3946 0 : return SetFromUserInput("EPSG:25832+5783");
3947 : }
3948 181 : else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
3949 : {
3950 : // "ETRS89 / UTM Zone 32N + DHHN2016 height"
3951 4 : return SetFromUserInput("EPSG:25832+7837");
3952 : }
3953 :
3954 : // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
3955 177 : const char *pszDot = strrchr(pszDefinition, ':');
3956 177 : if (pszDot)
3957 : {
3958 107 : CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
3959 : auto authorities =
3960 107 : proj_get_authorities_from_database(d->getPROJContext());
3961 107 : if (authorities)
3962 : {
3963 107 : std::set<std::string> aosCandidateAuthorities;
3964 232 : for (auto iter = authorities; *iter; ++iter)
3965 : {
3966 229 : if (*iter == osPrefix)
3967 : {
3968 104 : aosCandidateAuthorities.clear();
3969 104 : aosCandidateAuthorities.insert(*iter);
3970 104 : break;
3971 : }
3972 : // Deal with "IAU_2015" as authority in the list and input
3973 : // "IAU:code"
3974 125 : else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
3975 125 : 0 &&
3976 0 : (*iter)[osPrefix.size()] == '_')
3977 : {
3978 0 : aosCandidateAuthorities.insert(*iter);
3979 : }
3980 : // Deal with "IAU_2015" as authority in the list and input
3981 : // "IAU:2015:code"
3982 250 : else if (osPrefix.find(':') != std::string::npos &&
3983 125 : osPrefix.size() == strlen(*iter) &&
3984 125 : CPLString(osPrefix).replaceAll(':', '_') == *iter)
3985 : {
3986 0 : aosCandidateAuthorities.clear();
3987 0 : aosCandidateAuthorities.insert(*iter);
3988 0 : break;
3989 : }
3990 : }
3991 :
3992 107 : proj_string_list_destroy(authorities);
3993 :
3994 107 : if (!aosCandidateAuthorities.empty())
3995 : {
3996 104 : auto obj = proj_create_from_database(
3997 : d->getPROJContext(),
3998 104 : aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
3999 : PJ_CATEGORY_CRS, false, nullptr);
4000 104 : if (!obj)
4001 : {
4002 3 : return OGRERR_FAILURE;
4003 : }
4004 101 : Clear();
4005 101 : d->setPjCRS(obj);
4006 101 : return OGRERR_NONE;
4007 : }
4008 : }
4009 : }
4010 :
4011 : /* -------------------------------------------------------------------- */
4012 : /* Try to open it as a file. */
4013 : /* -------------------------------------------------------------------- */
4014 73 : if (!CPLTestBool(
4015 : CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4016 : {
4017 : VSIStatBufL sStat;
4018 24 : if (STARTS_WITH(pszDefinition, "/vsi") ||
4019 12 : VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4020 : {
4021 0 : CPLError(CE_Failure, CPLE_AppDefined,
4022 : "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4023 : pszDefinition);
4024 0 : return OGRERR_FAILURE;
4025 : }
4026 : // We used to silently return an error without a CE_Failure message
4027 : // Cf https://github.com/Toblerity/Fiona/issues/1063
4028 12 : return OGRERR_CORRUPT_DATA;
4029 : }
4030 :
4031 122 : CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4032 61 : VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4033 61 : if (fp == nullptr)
4034 56 : return OGRERR_CORRUPT_DATA;
4035 :
4036 5 : const size_t nBufMax = 100000;
4037 5 : char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4038 5 : const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4039 5 : VSIFCloseL(fp);
4040 :
4041 5 : if (nBytes == nBufMax - 1)
4042 : {
4043 0 : CPLDebug("OGR",
4044 : "OGRSpatialReference::SetFromUserInput(%s), opened file "
4045 : "but it is to large for our generous buffer. Is it really "
4046 : "just a WKT definition?",
4047 : pszDefinition);
4048 0 : CPLFree(pszBuffer);
4049 0 : return OGRERR_FAILURE;
4050 : }
4051 :
4052 5 : pszBuffer[nBytes] = '\0';
4053 :
4054 5 : char *pszBufPtr = pszBuffer;
4055 5 : while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4056 0 : pszBufPtr++;
4057 :
4058 5 : OGRErr err = OGRERR_NONE;
4059 5 : if (pszBufPtr[0] == '<')
4060 0 : err = importFromXML(pszBufPtr);
4061 5 : else if ((strstr(pszBuffer, "+proj") != nullptr ||
4062 5 : strstr(pszBuffer, "+init") != nullptr) &&
4063 0 : (strstr(pszBuffer, "EXTENSION") == nullptr &&
4064 0 : strstr(pszBuffer, "extension") == nullptr))
4065 0 : err = importFromProj4(pszBufPtr);
4066 : else
4067 : {
4068 5 : if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4069 : {
4070 3 : pszBufPtr += 6;
4071 : }
4072 :
4073 : // coverity[tainted_data]
4074 5 : err = importFromWkt(pszBufPtr);
4075 : }
4076 :
4077 5 : CPLFree(pszBuffer);
4078 :
4079 5 : return err;
4080 : }
4081 :
4082 : /************************************************************************/
4083 : /* OSRSetFromUserInput() */
4084 : /************************************************************************/
4085 :
4086 : /**
4087 : * \brief Set spatial reference from various text formats.
4088 : *
4089 : * This function is the same as OGRSpatialReference::SetFromUserInput()
4090 : *
4091 : * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4092 : */
4093 236 : OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4094 : const char *pszDef)
4095 :
4096 : {
4097 236 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4098 :
4099 236 : return ToPointer(hSRS)->SetFromUserInput(pszDef);
4100 : }
4101 :
4102 : /************************************************************************/
4103 : /* OSRSetFromUserInputEx() */
4104 : /************************************************************************/
4105 :
4106 : /**
4107 : * \brief Set spatial reference from various text formats.
4108 : *
4109 : * This function is the same as OGRSpatialReference::SetFromUserInput().
4110 : *
4111 : * @since GDAL 3.9
4112 : */
4113 676 : OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4114 : CSLConstList papszOptions)
4115 :
4116 : {
4117 676 : VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4118 :
4119 676 : return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4120 : }
4121 :
4122 : /************************************************************************/
4123 : /* ImportFromUrl() */
4124 : /************************************************************************/
4125 :
4126 : /**
4127 : * \brief Set spatial reference from a URL.
4128 : *
4129 : * This method will download the spatial reference at a given URL and
4130 : * feed it into SetFromUserInput for you.
4131 : *
4132 : * This method does the same thing as the OSRImportFromUrl() function.
4133 : *
4134 : * @param pszUrl text definition to try to deduce SRS from.
4135 : *
4136 : * @return OGRERR_NONE on success, or an error code with the curl
4137 : * error message if it is unable to download data.
4138 : */
4139 :
4140 5 : OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4141 :
4142 : {
4143 5 : if (!STARTS_WITH_CI(pszUrl, "http://") &&
4144 3 : !STARTS_WITH_CI(pszUrl, "https://"))
4145 : {
4146 2 : CPLError(CE_Failure, CPLE_AppDefined,
4147 : "The given string is not recognized as a URL"
4148 : "starting with 'http://' -- %s",
4149 : pszUrl);
4150 2 : return OGRERR_FAILURE;
4151 : }
4152 :
4153 : /* -------------------------------------------------------------------- */
4154 : /* Fetch the result. */
4155 : /* -------------------------------------------------------------------- */
4156 3 : CPLErrorReset();
4157 :
4158 6 : std::string osUrl(pszUrl);
4159 : // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4160 : // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4161 : // to query WKT. To allow a static server to be used, rather append a
4162 : // "ogcwkt/" suffix.
4163 2 : for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4164 5 : "http://spatialreference.org/ref/"})
4165 : {
4166 5 : if (STARTS_WITH(pszUrl, pszPrefix))
4167 : {
4168 : const CPLStringList aosTokens(
4169 6 : CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4170 3 : if (aosTokens.size() == 2)
4171 : {
4172 2 : osUrl = "https://spatialreference.org/ref/";
4173 2 : osUrl += aosTokens[0]; // authority
4174 2 : osUrl += '/';
4175 2 : osUrl += aosTokens[1]; // code
4176 2 : osUrl += "/ogcwkt/";
4177 : }
4178 3 : break;
4179 : }
4180 : }
4181 :
4182 3 : const char *pszTimeout = "TIMEOUT=10";
4183 3 : char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4184 :
4185 3 : CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4186 :
4187 : /* -------------------------------------------------------------------- */
4188 : /* Try to handle errors. */
4189 : /* -------------------------------------------------------------------- */
4190 :
4191 3 : if (psResult == nullptr)
4192 0 : return OGRERR_FAILURE;
4193 6 : if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4194 3 : psResult->pabyData == nullptr)
4195 : {
4196 0 : if (CPLGetLastErrorNo() == 0)
4197 : {
4198 0 : CPLError(CE_Failure, CPLE_AppDefined,
4199 : "No data was returned from the given URL");
4200 : }
4201 0 : CPLHTTPDestroyResult(psResult);
4202 0 : return OGRERR_FAILURE;
4203 : }
4204 :
4205 3 : if (psResult->nStatus != 0)
4206 : {
4207 0 : CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4208 : psResult->nStatus, psResult->pszErrBuf);
4209 0 : CPLHTTPDestroyResult(psResult);
4210 0 : return OGRERR_FAILURE;
4211 : }
4212 :
4213 3 : const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4214 3 : if (STARTS_WITH_CI(pszData, "http://") ||
4215 3 : STARTS_WITH_CI(pszData, "https://"))
4216 : {
4217 0 : CPLError(CE_Failure, CPLE_AppDefined,
4218 : "The data that was downloaded also starts with 'http://' "
4219 : "and cannot be passed into SetFromUserInput. Is this "
4220 : "really a spatial reference definition? ");
4221 0 : CPLHTTPDestroyResult(psResult);
4222 0 : return OGRERR_FAILURE;
4223 : }
4224 3 : if (OGRERR_NONE != SetFromUserInput(pszData))
4225 : {
4226 0 : CPLHTTPDestroyResult(psResult);
4227 0 : return OGRERR_FAILURE;
4228 : }
4229 :
4230 3 : CPLHTTPDestroyResult(psResult);
4231 3 : return OGRERR_NONE;
4232 : }
4233 :
4234 : /************************************************************************/
4235 : /* OSRimportFromUrl() */
4236 : /************************************************************************/
4237 :
4238 : /**
4239 : * \brief Set spatial reference from a URL.
4240 : *
4241 : * This function is the same as OGRSpatialReference::importFromUrl()
4242 : */
4243 3 : OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4244 :
4245 : {
4246 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4247 :
4248 3 : return ToPointer(hSRS)->importFromUrl(pszUrl);
4249 : }
4250 :
4251 : /************************************************************************/
4252 : /* importFromURNPart() */
4253 : /************************************************************************/
4254 1443 : OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4255 : const char *pszCode,
4256 : const char *pszURN)
4257 : {
4258 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4259 : (void)this;
4260 : (void)pszAuthority;
4261 : (void)pszCode;
4262 : (void)pszURN;
4263 : return OGRERR_FAILURE;
4264 : #else
4265 : /* -------------------------------------------------------------------- */
4266 : /* Is this an EPSG code? Note that we import it with EPSG */
4267 : /* preferred axis ordering for geographic coordinate systems. */
4268 : /* -------------------------------------------------------------------- */
4269 1443 : if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4270 1370 : return importFromEPSGA(atoi(pszCode));
4271 :
4272 : /* -------------------------------------------------------------------- */
4273 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4274 : /* -------------------------------------------------------------------- */
4275 73 : if (STARTS_WITH_CI(pszAuthority, "IAU"))
4276 0 : return importFromDict("IAU2000.wkt", pszCode);
4277 :
4278 : /* -------------------------------------------------------------------- */
4279 : /* Is this an OGC code? */
4280 : /* -------------------------------------------------------------------- */
4281 73 : if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4282 : {
4283 1 : CPLError(CE_Failure, CPLE_AppDefined,
4284 : "URN %s has unrecognized authority.", pszURN);
4285 1 : return OGRERR_FAILURE;
4286 : }
4287 :
4288 72 : if (STARTS_WITH_CI(pszCode, "CRS84"))
4289 66 : return SetWellKnownGeogCS(pszCode);
4290 6 : else if (STARTS_WITH_CI(pszCode, "CRS83"))
4291 0 : return SetWellKnownGeogCS(pszCode);
4292 6 : else if (STARTS_WITH_CI(pszCode, "CRS27"))
4293 0 : return SetWellKnownGeogCS(pszCode);
4294 6 : else if (STARTS_WITH_CI(pszCode, "84")) // urn:ogc:def:crs:OGC:2:84
4295 4 : return SetWellKnownGeogCS("CRS84");
4296 :
4297 : /* -------------------------------------------------------------------- */
4298 : /* Handle auto codes. We need to convert from format */
4299 : /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
4300 : /* -------------------------------------------------------------------- */
4301 2 : else if (STARTS_WITH_CI(pszCode, "AUTO"))
4302 : {
4303 2 : char szWMSAuto[100] = {'\0'};
4304 :
4305 2 : if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4306 0 : return OGRERR_FAILURE;
4307 :
4308 2 : snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4309 28 : for (int i = 5; szWMSAuto[i] != '\0'; i++)
4310 : {
4311 26 : if (szWMSAuto[i] == ':')
4312 4 : szWMSAuto[i] = ',';
4313 : }
4314 :
4315 2 : return importFromWMSAUTO(szWMSAuto);
4316 : }
4317 :
4318 : /* -------------------------------------------------------------------- */
4319 : /* Not a recognise OGC item. */
4320 : /* -------------------------------------------------------------------- */
4321 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4322 : pszURN);
4323 :
4324 0 : return OGRERR_FAILURE;
4325 : #endif
4326 : }
4327 :
4328 : /************************************************************************/
4329 : /* importFromURN() */
4330 : /* */
4331 : /* See OGC recommendation paper 06-023r1 or later for details. */
4332 : /************************************************************************/
4333 :
4334 : /**
4335 : * \brief Initialize from OGC URN.
4336 : *
4337 : * Initializes this spatial reference from a coordinate system defined
4338 : * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4339 : * paper 06-023r1. Currently EPSG and OGC authority values are supported,
4340 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4341 : *
4342 : * This method is also support through SetFromUserInput() which can
4343 : * normally be used for URNs.
4344 : *
4345 : * @param pszURN the urn string.
4346 : *
4347 : * @return OGRERR_NONE on success or an error code.
4348 : */
4349 :
4350 756 : OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4351 :
4352 : {
4353 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4354 :
4355 : // PROJ 8.2.0 has support for IAU codes now.
4356 : #if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4357 : /* -------------------------------------------------------------------- */
4358 : /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
4359 : /* -------------------------------------------------------------------- */
4360 : const char *pszIAU = strstr(pszURN, "IAU");
4361 : if (pszIAU)
4362 : {
4363 : const char *pszCode = strchr(pszIAU, ':');
4364 : if (pszCode)
4365 : {
4366 : ++pszCode;
4367 : if (*pszCode == ':')
4368 : ++pszCode;
4369 : return importFromDict("IAU2000.wkt", pszCode);
4370 : }
4371 : }
4372 : #endif
4373 :
4374 : if (strlen(pszURN) >= 1000)
4375 : {
4376 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4377 : return OGRERR_CORRUPT_DATA;
4378 : }
4379 : auto obj = proj_create(d->getPROJContext(), pszURN);
4380 : if (!obj)
4381 : {
4382 : return OGRERR_FAILURE;
4383 : }
4384 : Clear();
4385 : d->setPjCRS(obj);
4386 : return OGRERR_NONE;
4387 : #else
4388 756 : const char *pszCur = nullptr;
4389 :
4390 756 : if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4391 691 : pszCur = pszURN + 16;
4392 65 : else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4393 1 : pszCur = pszURN + 20;
4394 64 : else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4395 62 : pszCur = pszURN + 18;
4396 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4397 0 : pszCur = pszURN + 16;
4398 2 : else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4399 0 : pszCur = pszURN + 20;
4400 : else
4401 : {
4402 2 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4403 : pszURN);
4404 2 : return OGRERR_FAILURE;
4405 : }
4406 :
4407 : /* -------------------------------------------------------------------- */
4408 : /* Clear any existing definition. */
4409 : /* -------------------------------------------------------------------- */
4410 754 : Clear();
4411 :
4412 : /* -------------------------------------------------------------------- */
4413 : /* Find code (ignoring version) out of string like: */
4414 : /* */
4415 : /* authority:[version]:code */
4416 : /* -------------------------------------------------------------------- */
4417 754 : const char *pszAuthority = pszCur;
4418 :
4419 : // skip authority
4420 3757 : while (*pszCur != ':' && *pszCur)
4421 3003 : pszCur++;
4422 754 : if (*pszCur == ':')
4423 754 : pszCur++;
4424 :
4425 : // skip version
4426 754 : const char *pszBeforeVersion = pszCur;
4427 1034 : while (*pszCur != ':' && *pszCur)
4428 280 : pszCur++;
4429 754 : if (*pszCur == ':')
4430 726 : pszCur++;
4431 : else
4432 : // We come here in the case, the content to parse is authority:code
4433 : // (instead of authority::code) which is probably illegal according to
4434 : // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4435 : // for example in what is returned by GeoServer.
4436 28 : pszCur = pszBeforeVersion;
4437 :
4438 754 : const char *pszCode = pszCur;
4439 :
4440 754 : const char *pszComma = strchr(pszCur, ',');
4441 754 : if (pszComma == nullptr)
4442 753 : return importFromURNPart(pszAuthority, pszCode, pszURN);
4443 :
4444 : // There's a second part with the vertical SRS.
4445 1 : pszCur = pszComma + 1;
4446 1 : if (!STARTS_WITH(pszCur, "crs:"))
4447 : {
4448 0 : CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4449 : pszURN);
4450 0 : return OGRERR_FAILURE;
4451 : }
4452 :
4453 1 : pszCur += 4;
4454 :
4455 1 : char *pszFirstCode = CPLStrdup(pszCode);
4456 1 : pszFirstCode[pszComma - pszCode] = '\0';
4457 1 : OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4458 1 : CPLFree(pszFirstCode);
4459 :
4460 : // Do we want to turn this into a compound definition
4461 : // with a vertical datum?
4462 1 : if (eStatus != OGRERR_NONE)
4463 0 : return eStatus;
4464 :
4465 : /* -------------------------------------------------------------------- */
4466 : /* Find code (ignoring version) out of string like: */
4467 : /* */
4468 : /* authority:[version]:code */
4469 : /* -------------------------------------------------------------------- */
4470 1 : pszAuthority = pszCur;
4471 :
4472 : // skip authority
4473 5 : while (*pszCur != ':' && *pszCur)
4474 4 : pszCur++;
4475 1 : if (*pszCur == ':')
4476 1 : pszCur++;
4477 :
4478 : // skip version
4479 1 : pszBeforeVersion = pszCur;
4480 1 : while (*pszCur != ':' && *pszCur)
4481 0 : pszCur++;
4482 1 : if (*pszCur == ':')
4483 1 : pszCur++;
4484 : else
4485 0 : pszCur = pszBeforeVersion;
4486 :
4487 1 : pszCode = pszCur;
4488 :
4489 2 : OGRSpatialReference oVertSRS;
4490 1 : eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4491 1 : if (eStatus == OGRERR_NONE)
4492 : {
4493 1 : OGRSpatialReference oHorizSRS(*this);
4494 :
4495 1 : Clear();
4496 :
4497 1 : oHorizSRS.d->refreshProjObj();
4498 1 : oVertSRS.d->refreshProjObj();
4499 1 : if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4500 0 : return OGRERR_FAILURE;
4501 :
4502 1 : const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4503 1 : const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4504 :
4505 2 : CPLString osName = pszHorizName ? pszHorizName : "";
4506 1 : osName += " + ";
4507 1 : osName += pszVertName ? pszVertName : "";
4508 :
4509 1 : SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4510 : }
4511 :
4512 1 : return eStatus;
4513 : #endif
4514 : }
4515 :
4516 : /************************************************************************/
4517 : /* importFromCRSURL() */
4518 : /* */
4519 : /* See OGC Best Practice document 11-135 for details. */
4520 : /************************************************************************/
4521 :
4522 : /**
4523 : * \brief Initialize from OGC URL.
4524 : *
4525 : * Initializes this spatial reference from a coordinate system defined
4526 : * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4527 : * paper 11-135. Currently EPSG and OGC authority values are supported,
4528 : * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4529 : *
4530 : * This method is also supported through SetFromUserInput() which can
4531 : * normally be used for URLs.
4532 : *
4533 : * @param pszURL the URL string.
4534 : *
4535 : * @return OGRERR_NONE on success or an error code.
4536 : */
4537 :
4538 689 : OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4539 :
4540 : {
4541 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4542 : if (strlen(pszURL) >= 10000)
4543 : {
4544 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4545 : return OGRERR_CORRUPT_DATA;
4546 : }
4547 :
4548 : PJ *obj;
4549 : #if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4550 : if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4551 : {
4552 : obj = proj_create(
4553 : d->getPROJContext(),
4554 : CPLSPrintf("IAU:%s",
4555 : pszURL +
4556 : strlen("http://www.opengis.net/def/crs/IAU/0/")));
4557 : }
4558 : else
4559 : #endif
4560 : {
4561 : obj = proj_create(d->getPROJContext(), pszURL);
4562 : }
4563 : if (!obj)
4564 : {
4565 : return OGRERR_FAILURE;
4566 : }
4567 : Clear();
4568 : d->setPjCRS(obj);
4569 : return OGRERR_NONE;
4570 : #else
4571 689 : const char *pszCur = nullptr;
4572 :
4573 689 : if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4574 2 : pszCur = pszURL + 26;
4575 687 : else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4576 1 : pszCur = pszURL + 27;
4577 686 : else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4578 685 : pszCur = pszURL + 30;
4579 1 : else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4580 1 : pszCur = pszURL + 31;
4581 0 : else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4582 0 : pszCur = pszURL + 23;
4583 : else
4584 : {
4585 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4586 : pszURL);
4587 0 : return OGRERR_FAILURE;
4588 : }
4589 :
4590 689 : if (*pszCur == '\0')
4591 : {
4592 0 : CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4593 0 : return OGRERR_FAILURE;
4594 : }
4595 :
4596 : /* -------------------------------------------------------------------- */
4597 : /* Clear any existing definition. */
4598 : /* -------------------------------------------------------------------- */
4599 689 : Clear();
4600 :
4601 689 : if (STARTS_WITH_CI(pszCur, "-compound?1="))
4602 : {
4603 : /* --------------------------------------------------------------------
4604 : */
4605 : /* It's a compound CRS, of the form: */
4606 : /* */
4607 : /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4608 : /* --------------------------------------------------------------------
4609 : */
4610 1 : pszCur += 12;
4611 :
4612 : // Extract each component CRS URL.
4613 1 : int iComponentUrl = 2;
4614 :
4615 2 : CPLString osName = "";
4616 1 : Clear();
4617 :
4618 3 : while (iComponentUrl != -1)
4619 : {
4620 2 : char searchStr[15] = {};
4621 2 : snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4622 :
4623 2 : const char *pszUrlEnd = strstr(pszCur, searchStr);
4624 :
4625 : // Figure out the next component URL.
4626 2 : char *pszComponentUrl = nullptr;
4627 :
4628 2 : if (pszUrlEnd)
4629 : {
4630 1 : size_t nLen = pszUrlEnd - pszCur;
4631 1 : pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4632 1 : strncpy(pszComponentUrl, pszCur, nLen);
4633 1 : pszComponentUrl[nLen] = '\0';
4634 :
4635 1 : ++iComponentUrl;
4636 1 : pszCur += nLen + strlen(searchStr);
4637 : }
4638 : else
4639 : {
4640 1 : if (iComponentUrl == 2)
4641 : {
4642 0 : CPLError(CE_Failure, CPLE_AppDefined,
4643 : "Compound CRS URLs must have at least two "
4644 : "component CRSs.");
4645 0 : return OGRERR_FAILURE;
4646 : }
4647 : else
4648 : {
4649 1 : pszComponentUrl = CPLStrdup(pszCur);
4650 : // no more components
4651 1 : iComponentUrl = -1;
4652 : }
4653 : }
4654 :
4655 2 : OGRSpatialReference oComponentSRS;
4656 2 : OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4657 :
4658 2 : CPLFree(pszComponentUrl);
4659 2 : pszComponentUrl = nullptr;
4660 :
4661 2 : if (eStatus == OGRERR_NONE)
4662 : {
4663 2 : if (osName.length() != 0)
4664 : {
4665 1 : osName += " + ";
4666 : }
4667 2 : osName += oComponentSRS.GetRoot()->GetValue();
4668 2 : SetNode("COMPD_CS", osName);
4669 2 : GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4670 : }
4671 : else
4672 0 : return eStatus;
4673 : }
4674 :
4675 1 : return OGRERR_NONE;
4676 : }
4677 :
4678 : /* -------------------------------------------------------------------- */
4679 : /* It's a normal CRS URL, of the form: */
4680 : /* */
4681 : /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
4682 : /* -------------------------------------------------------------------- */
4683 688 : ++pszCur;
4684 688 : const char *pszAuthority = pszCur;
4685 :
4686 : // skip authority
4687 103377 : while (*pszCur != '/' && *pszCur)
4688 102689 : pszCur++;
4689 688 : if (*pszCur == '/')
4690 687 : pszCur++;
4691 :
4692 : // skip version
4693 1493 : while (*pszCur != '/' && *pszCur)
4694 805 : pszCur++;
4695 688 : if (*pszCur == '/')
4696 687 : pszCur++;
4697 :
4698 688 : const char *pszCode = pszCur;
4699 :
4700 688 : return importFromURNPart(pszAuthority, pszCode, pszURL);
4701 : #endif
4702 : }
4703 :
4704 : /************************************************************************/
4705 : /* importFromWMSAUTO() */
4706 : /************************************************************************/
4707 :
4708 : /**
4709 : * \brief Initialize from WMSAUTO string.
4710 : *
4711 : * Note that the WMS 1.3 specification does not include the
4712 : * units code, while apparently earlier specs do. We try to
4713 : * guess around this.
4714 : *
4715 : * @param pszDefinition the WMSAUTO string
4716 : *
4717 : * @return OGRERR_NONE on success or an error code.
4718 : */
4719 3 : OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4720 :
4721 : {
4722 : #if PROJ_AT_LEAST_VERSION(8, 1, 0)
4723 : if (strlen(pszDefinition) >= 10000)
4724 : {
4725 : CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4726 : return OGRERR_CORRUPT_DATA;
4727 : }
4728 :
4729 : auto obj = proj_create(d->getPROJContext(), pszDefinition);
4730 : if (!obj)
4731 : {
4732 : return OGRERR_FAILURE;
4733 : }
4734 : Clear();
4735 : d->setPjCRS(obj);
4736 : return OGRERR_NONE;
4737 : #else
4738 : int nProjId, nUnitsId;
4739 3 : double dfRefLong, dfRefLat = 0.0;
4740 :
4741 : /* -------------------------------------------------------------------- */
4742 : /* Tokenize */
4743 : /* -------------------------------------------------------------------- */
4744 3 : if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4745 3 : pszDefinition += 5;
4746 :
4747 : char **papszTokens =
4748 3 : CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
4749 :
4750 3 : if (CSLCount(papszTokens) == 4)
4751 : {
4752 0 : nProjId = atoi(papszTokens[0]);
4753 0 : nUnitsId = atoi(papszTokens[1]);
4754 0 : dfRefLong = CPLAtof(papszTokens[2]);
4755 0 : dfRefLat = CPLAtof(papszTokens[3]);
4756 : }
4757 3 : else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
4758 : {
4759 0 : nProjId = atoi(papszTokens[0]);
4760 0 : nUnitsId = atoi(papszTokens[1]);
4761 0 : dfRefLong = CPLAtof(papszTokens[2]);
4762 0 : dfRefLat = 0.0;
4763 : }
4764 3 : else if (CSLCount(papszTokens) == 3)
4765 : {
4766 2 : nProjId = atoi(papszTokens[0]);
4767 2 : nUnitsId = 9001;
4768 2 : dfRefLong = CPLAtof(papszTokens[1]);
4769 2 : dfRefLat = CPLAtof(papszTokens[2]);
4770 : }
4771 1 : else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
4772 : {
4773 0 : nProjId = atoi(papszTokens[0]);
4774 0 : nUnitsId = 9001;
4775 0 : dfRefLong = CPLAtof(papszTokens[1]);
4776 : }
4777 : else
4778 : {
4779 1 : CSLDestroy(papszTokens);
4780 1 : CPLError(CE_Failure, CPLE_AppDefined,
4781 : "AUTO projection has wrong number of arguments, expected\n"
4782 : "AUTO:proj_id,units_id,ref_long,ref_lat or"
4783 : "AUTO:proj_id,ref_long,ref_lat");
4784 1 : return OGRERR_FAILURE;
4785 : }
4786 :
4787 2 : CSLDestroy(papszTokens);
4788 2 : papszTokens = nullptr;
4789 :
4790 : /* -------------------------------------------------------------------- */
4791 : /* Build coordsys. */
4792 : /* -------------------------------------------------------------------- */
4793 2 : Clear();
4794 :
4795 : /* -------------------------------------------------------------------- */
4796 : /* Set WGS84. */
4797 : /* -------------------------------------------------------------------- */
4798 2 : SetWellKnownGeogCS("WGS84");
4799 :
4800 2 : switch (nProjId)
4801 : {
4802 2 : case 42001: // Auto UTM
4803 2 : SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
4804 : dfRefLat >= 0.0);
4805 2 : break;
4806 :
4807 0 : case 42002: // Auto TM (strangely very UTM-like).
4808 0 : SetTM(0, dfRefLong, 0.9996, 500000.0,
4809 : (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
4810 0 : break;
4811 :
4812 0 : case 42003: // Auto Orthographic.
4813 0 : SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
4814 0 : break;
4815 :
4816 0 : case 42004: // Auto Equirectangular
4817 0 : SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
4818 0 : break;
4819 :
4820 0 : case 42005:
4821 0 : SetMollweide(dfRefLong, 0.0, 0.0);
4822 0 : break;
4823 :
4824 0 : default:
4825 0 : CPLError(CE_Failure, CPLE_AppDefined,
4826 : "Unsupported projection id in importFromWMSAUTO(): %d",
4827 : nProjId);
4828 0 : return OGRERR_FAILURE;
4829 : }
4830 :
4831 : /* -------------------------------------------------------------------- */
4832 : /* Set units. */
4833 : /* -------------------------------------------------------------------- */
4834 :
4835 2 : switch (nUnitsId)
4836 : {
4837 2 : case 9001:
4838 2 : SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
4839 2 : break;
4840 :
4841 0 : case 9002:
4842 0 : SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
4843 0 : break;
4844 :
4845 0 : case 9003:
4846 0 : SetTargetLinearUnits(nullptr, "US survey foot",
4847 : CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
4848 0 : break;
4849 :
4850 0 : default:
4851 0 : CPLError(CE_Failure, CPLE_AppDefined,
4852 : "Unsupported units code (%d).", nUnitsId);
4853 0 : return OGRERR_FAILURE;
4854 : break;
4855 : }
4856 :
4857 2 : return OGRERR_NONE;
4858 : #endif
4859 : }
4860 :
4861 : /************************************************************************/
4862 : /* GetSemiMajor() */
4863 : /************************************************************************/
4864 :
4865 : /**
4866 : * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
4867 : *
4868 : * This method does the same thing as the C function OSRGetSemiMajor().
4869 : *
4870 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
4871 : * can be found.
4872 : *
4873 : * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
4874 : */
4875 :
4876 4847 : double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
4877 :
4878 : {
4879 4847 : if (pnErr != nullptr)
4880 2253 : *pnErr = OGRERR_FAILURE;
4881 :
4882 4847 : d->refreshProjObj();
4883 4847 : if (!d->m_pj_crs)
4884 111 : return SRS_WGS84_SEMIMAJOR;
4885 :
4886 4736 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
4887 4736 : if (!ellps)
4888 4 : return SRS_WGS84_SEMIMAJOR;
4889 :
4890 4732 : double dfSemiMajor = 0.0;
4891 4732 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
4892 : nullptr, nullptr, nullptr);
4893 4732 : proj_destroy(ellps);
4894 :
4895 4732 : if (dfSemiMajor > 0)
4896 : {
4897 4732 : if (pnErr != nullptr)
4898 2140 : *pnErr = OGRERR_NONE;
4899 4732 : return dfSemiMajor;
4900 : }
4901 :
4902 0 : return SRS_WGS84_SEMIMAJOR;
4903 : }
4904 :
4905 : /************************************************************************/
4906 : /* OSRGetSemiMajor() */
4907 : /************************************************************************/
4908 :
4909 : /**
4910 : * \brief Get spheroid semi major axis.
4911 : *
4912 : * This function is the same as OGRSpatialReference::GetSemiMajor()
4913 : */
4914 58 : double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
4915 :
4916 : {
4917 58 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
4918 :
4919 58 : return ToPointer(hSRS)->GetSemiMajor(pnErr);
4920 : }
4921 :
4922 : /************************************************************************/
4923 : /* GetInvFlattening() */
4924 : /************************************************************************/
4925 :
4926 : /**
4927 : * \brief Get spheroid inverse flattening.
4928 : *
4929 : * This method does the same thing as the C function OSRGetInvFlattening().
4930 : *
4931 : * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
4932 : * can be found.
4933 : *
4934 : * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
4935 : */
4936 :
4937 3242 : double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
4938 :
4939 : {
4940 3242 : if (pnErr != nullptr)
4941 2189 : *pnErr = OGRERR_FAILURE;
4942 :
4943 3242 : d->refreshProjObj();
4944 3242 : if (!d->m_pj_crs)
4945 111 : return SRS_WGS84_INVFLATTENING;
4946 :
4947 3131 : auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
4948 3131 : if (!ellps)
4949 2 : return SRS_WGS84_INVFLATTENING;
4950 :
4951 3129 : double dfInvFlattening = -1.0;
4952 3129 : proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
4953 : nullptr, &dfInvFlattening);
4954 3129 : proj_destroy(ellps);
4955 :
4956 3129 : if (dfInvFlattening >= 0.0)
4957 : {
4958 3129 : if (pnErr != nullptr)
4959 2078 : *pnErr = OGRERR_NONE;
4960 3129 : return dfInvFlattening;
4961 : }
4962 :
4963 0 : return SRS_WGS84_INVFLATTENING;
4964 : }
4965 :
4966 : /************************************************************************/
4967 : /* OSRGetInvFlattening() */
4968 : /************************************************************************/
4969 :
4970 : /**
4971 : * \brief Get spheroid inverse flattening.
4972 : *
4973 : * This function is the same as OGRSpatialReference::GetInvFlattening()
4974 : */
4975 11 : double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
4976 :
4977 : {
4978 11 : VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
4979 :
4980 11 : return ToPointer(hSRS)->GetInvFlattening(pnErr);
4981 : }
4982 :
4983 : /************************************************************************/
4984 : /* GetEccentricity() */
4985 : /************************************************************************/
4986 :
4987 : /**
4988 : * \brief Get spheroid eccentricity
4989 : *
4990 : * @return eccentricity (or -1 in case of error)
4991 : * @since GDAL 2.3
4992 : */
4993 :
4994 0 : double OGRSpatialReference::GetEccentricity() const
4995 :
4996 : {
4997 0 : OGRErr eErr = OGRERR_NONE;
4998 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
4999 0 : if (eErr != OGRERR_NONE)
5000 : {
5001 0 : return -1.0;
5002 : }
5003 0 : if (dfInvFlattening == 0.0)
5004 0 : return 0.0;
5005 0 : if (dfInvFlattening < 0.5)
5006 0 : return -1.0;
5007 0 : return sqrt(2.0 / dfInvFlattening -
5008 0 : 1.0 / (dfInvFlattening * dfInvFlattening));
5009 : }
5010 :
5011 : /************************************************************************/
5012 : /* GetSquaredEccentricity() */
5013 : /************************************************************************/
5014 :
5015 : /**
5016 : * \brief Get spheroid squared eccentricity
5017 : *
5018 : * @return squared eccentricity (or -1 in case of error)
5019 : * @since GDAL 2.3
5020 : */
5021 :
5022 0 : double OGRSpatialReference::GetSquaredEccentricity() const
5023 :
5024 : {
5025 0 : OGRErr eErr = OGRERR_NONE;
5026 0 : const double dfInvFlattening = GetInvFlattening(&eErr);
5027 0 : if (eErr != OGRERR_NONE)
5028 : {
5029 0 : return -1.0;
5030 : }
5031 0 : if (dfInvFlattening == 0.0)
5032 0 : return 0.0;
5033 0 : if (dfInvFlattening < 0.5)
5034 0 : return -1.0;
5035 0 : return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5036 : }
5037 :
5038 : /************************************************************************/
5039 : /* GetSemiMinor() */
5040 : /************************************************************************/
5041 :
5042 : /**
5043 : * \brief Get spheroid semi minor axis.
5044 : *
5045 : * This method does the same thing as the C function OSRGetSemiMinor().
5046 : *
5047 : * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5048 : * can be found.
5049 : *
5050 : * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5051 : */
5052 :
5053 630 : double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5054 :
5055 : {
5056 630 : const double dfSemiMajor = GetSemiMajor(pnErr);
5057 630 : const double dfInvFlattening = GetInvFlattening(pnErr);
5058 :
5059 630 : return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5060 : }
5061 :
5062 : /************************************************************************/
5063 : /* OSRGetSemiMinor() */
5064 : /************************************************************************/
5065 :
5066 : /**
5067 : * \brief Get spheroid semi minor axis.
5068 : *
5069 : * This function is the same as OGRSpatialReference::GetSemiMinor()
5070 : */
5071 4 : double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5072 :
5073 : {
5074 4 : VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5075 :
5076 4 : return ToPointer(hSRS)->GetSemiMinor(pnErr);
5077 : }
5078 :
5079 : /************************************************************************/
5080 : /* SetLocalCS() */
5081 : /************************************************************************/
5082 :
5083 : /**
5084 : * \brief Set the user visible LOCAL_CS name.
5085 : *
5086 : * This method is the same as the C function OSRSetLocalCS().
5087 : *
5088 : * This method will ensure a LOCAL_CS node is created as the root,
5089 : * and set the provided name on it. It must be used before SetLinearUnits().
5090 : *
5091 : * @param pszName the user visible name to assign. Not used as a key.
5092 : *
5093 : * @return OGRERR_NONE on success.
5094 : */
5095 :
5096 2881 : OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5097 :
5098 : {
5099 2881 : if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5100 0 : d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5101 : {
5102 2881 : d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5103 : }
5104 : else
5105 : {
5106 0 : CPLDebug("OGR",
5107 : "OGRSpatialReference::SetLocalCS(%s) failed. "
5108 : "It appears an incompatible object already exists.",
5109 : pszName);
5110 0 : return OGRERR_FAILURE;
5111 : }
5112 :
5113 2881 : return OGRERR_NONE;
5114 : }
5115 :
5116 : /************************************************************************/
5117 : /* OSRSetLocalCS() */
5118 : /************************************************************************/
5119 :
5120 : /**
5121 : * \brief Set the user visible LOCAL_CS name.
5122 : *
5123 : * This function is the same as OGRSpatialReference::SetLocalCS()
5124 : */
5125 1 : OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5126 :
5127 : {
5128 1 : VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5129 :
5130 1 : return ToPointer(hSRS)->SetLocalCS(pszName);
5131 : }
5132 :
5133 : /************************************************************************/
5134 : /* SetGeocCS() */
5135 : /************************************************************************/
5136 :
5137 : /**
5138 : * \brief Set the user visible GEOCCS name.
5139 : *
5140 : * This method is the same as the C function OSRSetGeocCS().
5141 :
5142 : * This method will ensure a GEOCCS node is created as the root,
5143 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5144 : * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5145 : * the GEOGCS.
5146 : *
5147 : * @param pszName the user visible name to assign. Not used as a key.
5148 : *
5149 : * @return OGRERR_NONE on success.
5150 : *
5151 : * @since OGR 1.9.0
5152 : */
5153 :
5154 6 : OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5155 :
5156 : {
5157 6 : OGRErr eErr = OGRERR_NONE;
5158 6 : d->refreshProjObj();
5159 6 : d->demoteFromBoundCRS();
5160 6 : if (d->m_pjType == PJ_TYPE_UNKNOWN)
5161 : {
5162 3 : d->setPjCRS(proj_create_geocentric_crs(
5163 : d->getPROJContext(), pszName, "World Geodetic System 1984",
5164 : "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5165 : SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5166 : "Metre", 1.0));
5167 : }
5168 3 : else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5169 : {
5170 1 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5171 : }
5172 3 : else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5173 1 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5174 : {
5175 1 : auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5176 : #if PROJ_VERSION_MAJOR > 7 || \
5177 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5178 : if (datum == nullptr)
5179 : {
5180 : datum =
5181 : proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5182 : }
5183 : #endif
5184 1 : if (datum == nullptr)
5185 : {
5186 0 : d->undoDemoteFromBoundCRS();
5187 0 : return OGRERR_FAILURE;
5188 : }
5189 :
5190 1 : auto pj_crs = proj_create_geocentric_crs_from_datum(
5191 1 : d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5192 : 0.0);
5193 1 : d->setPjCRS(pj_crs);
5194 :
5195 1 : proj_destroy(datum);
5196 : }
5197 : else
5198 : {
5199 1 : CPLDebug("OGR",
5200 : "OGRSpatialReference::SetGeocCS(%s) failed. "
5201 : "It appears an incompatible object already exists.",
5202 : pszName);
5203 1 : eErr = OGRERR_FAILURE;
5204 : }
5205 6 : d->undoDemoteFromBoundCRS();
5206 :
5207 6 : return eErr;
5208 : }
5209 :
5210 : /************************************************************************/
5211 : /* OSRSetGeocCS() */
5212 : /************************************************************************/
5213 :
5214 : /**
5215 : * \brief Set the user visible PROJCS name.
5216 : *
5217 : * This function is the same as OGRSpatialReference::SetGeocCS()
5218 : *
5219 : * @since OGR 1.9.0
5220 : */
5221 4 : OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5222 :
5223 : {
5224 4 : VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5225 :
5226 4 : return ToPointer(hSRS)->SetGeocCS(pszName);
5227 : }
5228 :
5229 : /************************************************************************/
5230 : /* SetVertCS() */
5231 : /************************************************************************/
5232 :
5233 : /**
5234 : * \brief Set the user visible VERT_CS name.
5235 : *
5236 : * This method is the same as the C function OSRSetVertCS().
5237 :
5238 : * This method will ensure a VERT_CS node is created if needed. If the
5239 : * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5240 : * turned into a COMPD_CS.
5241 : *
5242 : * @param pszVertCSName the user visible name of the vertical coordinate
5243 : * system. Not used as a key.
5244 : *
5245 : * @param pszVertDatumName the user visible name of the vertical datum. It
5246 : * is helpful if this matches the EPSG name.
5247 : *
5248 : * @param nVertDatumType the OGC vertical datum type. Ignored
5249 : *
5250 : * @return OGRERR_NONE on success.
5251 : *
5252 : * @since OGR 1.9.0
5253 : */
5254 :
5255 1 : OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5256 : const char *pszVertDatumName,
5257 : int nVertDatumType)
5258 :
5259 : {
5260 1 : CPL_IGNORE_RET_VAL(nVertDatumType);
5261 :
5262 1 : d->refreshProjObj();
5263 :
5264 1 : auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5265 : pszVertDatumName, nullptr, 0.0);
5266 :
5267 : /* -------------------------------------------------------------------- */
5268 : /* Handle the case where we want to make a compound coordinate */
5269 : /* system. */
5270 : /* -------------------------------------------------------------------- */
5271 1 : if (IsProjected() || IsGeographic())
5272 : {
5273 1 : auto compoundCRS = proj_create_compound_crs(
5274 1 : d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5275 1 : proj_destroy(vertCRS);
5276 1 : d->setPjCRS(compoundCRS);
5277 : }
5278 : else
5279 : {
5280 0 : d->setPjCRS(vertCRS);
5281 : }
5282 1 : return OGRERR_NONE;
5283 : }
5284 :
5285 : /************************************************************************/
5286 : /* OSRSetVertCS() */
5287 : /************************************************************************/
5288 :
5289 : /**
5290 : * \brief Setup the vertical coordinate system.
5291 : *
5292 : * This function is the same as OGRSpatialReference::SetVertCS()
5293 : *
5294 : * @since OGR 1.9.0
5295 : */
5296 0 : OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5297 : const char *pszVertDatumName, int nVertDatumType)
5298 :
5299 : {
5300 0 : VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5301 :
5302 0 : return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5303 0 : nVertDatumType);
5304 : }
5305 :
5306 : /************************************************************************/
5307 : /* SetCompoundCS() */
5308 : /************************************************************************/
5309 :
5310 : /**
5311 : * \brief Setup a compound coordinate system.
5312 : *
5313 : * This method is the same as the C function OSRSetCompoundCS().
5314 :
5315 : * This method is replace the current SRS with a COMPD_CS coordinate system
5316 : * consisting of the passed in horizontal and vertical coordinate systems.
5317 : *
5318 : * @param pszName the name of the compound coordinate system.
5319 : *
5320 : * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5321 : *
5322 : * @param poVertSRS the vertical SRS (VERT_CS).
5323 : *
5324 : * @return OGRERR_NONE on success.
5325 : */
5326 :
5327 92 : OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5328 : const OGRSpatialReference *poHorizSRS,
5329 : const OGRSpatialReference *poVertSRS)
5330 :
5331 : {
5332 : /* -------------------------------------------------------------------- */
5333 : /* Verify these are legal horizontal and vertical coordinate */
5334 : /* systems. */
5335 : /* -------------------------------------------------------------------- */
5336 92 : if (!poVertSRS->IsVertical())
5337 : {
5338 0 : CPLError(CE_Failure, CPLE_AppDefined,
5339 : "SetCompoundCS() fails, vertical component is not VERT_CS.");
5340 0 : return OGRERR_FAILURE;
5341 : }
5342 92 : if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5343 : {
5344 0 : CPLError(CE_Failure, CPLE_AppDefined,
5345 : "SetCompoundCS() fails, horizontal component is not PROJCS or "
5346 : "GEOGCS.");
5347 0 : return OGRERR_FAILURE;
5348 : }
5349 :
5350 : /* -------------------------------------------------------------------- */
5351 : /* Replace with compound srs. */
5352 : /* -------------------------------------------------------------------- */
5353 92 : Clear();
5354 :
5355 92 : auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5356 92 : poHorizSRS->d->m_pj_crs,
5357 92 : poVertSRS->d->m_pj_crs);
5358 92 : d->setPjCRS(compoundCRS);
5359 :
5360 92 : return OGRERR_NONE;
5361 : }
5362 :
5363 : /************************************************************************/
5364 : /* OSRSetCompoundCS() */
5365 : /************************************************************************/
5366 :
5367 : /**
5368 : * \brief Setup a compound coordinate system.
5369 : *
5370 : * This function is the same as OGRSpatialReference::SetCompoundCS()
5371 : */
5372 8 : OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5373 : OGRSpatialReferenceH hHorizSRS,
5374 : OGRSpatialReferenceH hVertSRS)
5375 :
5376 : {
5377 8 : VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5378 8 : VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5379 8 : VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5380 :
5381 16 : return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5382 16 : ToPointer(hVertSRS));
5383 : }
5384 :
5385 : /************************************************************************/
5386 : /* SetProjCS() */
5387 : /************************************************************************/
5388 :
5389 : /**
5390 : * \brief Set the user visible PROJCS name.
5391 : *
5392 : * This method is the same as the C function OSRSetProjCS().
5393 : *
5394 : * This method will ensure a PROJCS node is created as the root,
5395 : * and set the provided name on it. If used on a GEOGCS coordinate system,
5396 : * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5397 : *
5398 : * @param pszName the user visible name to assign. Not used as a key.
5399 : *
5400 : * @return OGRERR_NONE on success.
5401 : */
5402 :
5403 3714 : OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5404 :
5405 : {
5406 3714 : d->refreshProjObj();
5407 3714 : d->demoteFromBoundCRS();
5408 3714 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5409 : {
5410 480 : d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5411 : }
5412 : else
5413 : {
5414 3234 : auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5415 : nullptr, nullptr, nullptr,
5416 : nullptr, nullptr, 0, nullptr);
5417 3234 : auto cs = proj_create_cartesian_2D_cs(
5418 : d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5419 :
5420 3234 : auto projCRS = proj_create_projected_crs(
5421 3234 : d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5422 3234 : proj_destroy(dummyConv);
5423 3234 : proj_destroy(cs);
5424 :
5425 3234 : d->setPjCRS(projCRS);
5426 : }
5427 3714 : d->undoDemoteFromBoundCRS();
5428 3714 : return OGRERR_NONE;
5429 : }
5430 :
5431 : /************************************************************************/
5432 : /* OSRSetProjCS() */
5433 : /************************************************************************/
5434 :
5435 : /**
5436 : * \brief Set the user visible PROJCS name.
5437 : *
5438 : * This function is the same as OGRSpatialReference::SetProjCS()
5439 : */
5440 7 : OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5441 :
5442 : {
5443 7 : VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5444 :
5445 7 : return ToPointer(hSRS)->SetProjCS(pszName);
5446 : }
5447 :
5448 : /************************************************************************/
5449 : /* SetProjection() */
5450 : /************************************************************************/
5451 :
5452 : /**
5453 : * \brief Set a projection name.
5454 : *
5455 : * This method is the same as the C function OSRSetProjection().
5456 : *
5457 : * @param pszProjection the projection name, which should be selected from
5458 : * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5459 : *
5460 : * @return OGRERR_NONE on success.
5461 : */
5462 :
5463 23 : OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5464 :
5465 : {
5466 23 : OGR_SRSNode *poGeogCS = nullptr;
5467 :
5468 23 : if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5469 : {
5470 4 : poGeogCS = d->m_poRoot;
5471 4 : d->m_poRoot = nullptr;
5472 : }
5473 :
5474 23 : if (!GetAttrNode("PROJCS"))
5475 : {
5476 11 : SetNode("PROJCS", "unnamed");
5477 : }
5478 :
5479 23 : const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5480 23 : if (eErr != OGRERR_NONE)
5481 0 : return eErr;
5482 :
5483 23 : if (poGeogCS != nullptr)
5484 4 : d->m_poRoot->InsertChild(poGeogCS, 1);
5485 :
5486 23 : return OGRERR_NONE;
5487 : }
5488 :
5489 : /************************************************************************/
5490 : /* OSRSetProjection() */
5491 : /************************************************************************/
5492 :
5493 : /**
5494 : * \brief Set a projection name.
5495 : *
5496 : * This function is the same as OGRSpatialReference::SetProjection()
5497 : */
5498 0 : OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5499 :
5500 : {
5501 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5502 :
5503 0 : return ToPointer(hSRS)->SetProjection(pszProjection);
5504 : }
5505 :
5506 : /************************************************************************/
5507 : /* GetWKT2ProjectionMethod() */
5508 : /************************************************************************/
5509 :
5510 : /**
5511 : * \brief Returns info on the projection method, based on WKT2 naming
5512 : * conventions.
5513 : *
5514 : * The returned strings are short lived and should be considered to be
5515 : * invalidated by any further call to the GDAL API.
5516 : *
5517 : * @param[out] ppszMethodName Pointer to a string that will receive the
5518 : * projection method name.
5519 : * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5520 : * receive the name of the authority that defines the projection method.
5521 : * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5522 : * an authority.
5523 : * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5524 : * receive the code that defines the projection method.
5525 : * *ppszMethodCode may be nullptr if the projection method is not linked to
5526 : * an authority.
5527 : *
5528 : * @return OGRERR_NONE on success.
5529 : */
5530 : OGRErr
5531 1 : OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5532 : const char **ppszMethodAuthName,
5533 : const char **ppszMethodCode) const
5534 : {
5535 1 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5536 1 : if (!conv)
5537 0 : return OGRERR_FAILURE;
5538 1 : const char *pszTmpMethodName = "";
5539 1 : const char *pszTmpMethodAuthName = "";
5540 1 : const char *pszTmpMethodCode = "";
5541 1 : int ret = proj_coordoperation_get_method_info(
5542 : d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5543 : &pszTmpMethodCode);
5544 : // "Internalize" temporary strings returned by PROJ
5545 1 : CPLAssert(pszTmpMethodName);
5546 1 : if (ppszMethodName)
5547 1 : *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5548 1 : if (ppszMethodAuthName)
5549 0 : *ppszMethodAuthName = pszTmpMethodAuthName
5550 0 : ? CPLSPrintf("%s", pszTmpMethodAuthName)
5551 0 : : nullptr;
5552 1 : if (ppszMethodCode)
5553 0 : *ppszMethodCode =
5554 0 : pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5555 1 : proj_destroy(conv);
5556 1 : return ret ? OGRERR_NONE : OGRERR_FAILURE;
5557 : }
5558 :
5559 : /************************************************************************/
5560 : /* SetProjParm() */
5561 : /************************************************************************/
5562 :
5563 : /**
5564 : * \brief Set a projection parameter value.
5565 : *
5566 : * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5567 : *
5568 : * This method is the same as the C function OSRSetProjParm().
5569 : *
5570 : * Please check https://gdal.org/proj_list pages for
5571 : * legal parameter names for specific projections.
5572 : *
5573 : *
5574 : * @param pszParamName the parameter name, which should be selected from
5575 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5576 : *
5577 : * @param dfValue value to assign.
5578 : *
5579 : * @return OGRERR_NONE on success.
5580 : */
5581 :
5582 133 : OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5583 : double dfValue)
5584 :
5585 : {
5586 133 : OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5587 :
5588 133 : if (poPROJCS == nullptr)
5589 5 : return OGRERR_FAILURE;
5590 :
5591 128 : char szValue[64] = {'\0'};
5592 128 : OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5593 :
5594 : /* -------------------------------------------------------------------- */
5595 : /* Try to find existing parameter with this name. */
5596 : /* -------------------------------------------------------------------- */
5597 1040 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5598 : {
5599 953 : OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5600 :
5601 1256 : if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5602 1256 : poParam->GetChildCount() == 2 &&
5603 303 : EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5604 : {
5605 41 : poParam->GetChild(1)->SetValue(szValue);
5606 41 : return OGRERR_NONE;
5607 : }
5608 : }
5609 :
5610 : /* -------------------------------------------------------------------- */
5611 : /* Otherwise create a new parameter and append. */
5612 : /* -------------------------------------------------------------------- */
5613 87 : OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5614 87 : poParam->AddChild(new OGR_SRSNode(pszParamName));
5615 87 : poParam->AddChild(new OGR_SRSNode(szValue));
5616 :
5617 87 : poPROJCS->AddChild(poParam);
5618 :
5619 87 : return OGRERR_NONE;
5620 : }
5621 :
5622 : /************************************************************************/
5623 : /* OSRSetProjParm() */
5624 : /************************************************************************/
5625 :
5626 : /**
5627 : * \brief Set a projection parameter value.
5628 : *
5629 : * This function is the same as OGRSpatialReference::SetProjParm()
5630 : */
5631 0 : OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5632 : double dfValue)
5633 :
5634 : {
5635 0 : VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5636 :
5637 0 : return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5638 : }
5639 :
5640 : /************************************************************************/
5641 : /* FindProjParm() */
5642 : /************************************************************************/
5643 :
5644 : /**
5645 : * \brief Return the child index of the named projection parameter on
5646 : * its parent PROJCS node.
5647 : *
5648 : * @param pszParameter projection parameter to look for
5649 : * @param poPROJCS projection CS node to look in. If NULL is passed,
5650 : * the PROJCS node of the SpatialReference object will be searched.
5651 : *
5652 : * @return the child index of the named projection parameter. -1 on failure
5653 : */
5654 4316 : int OGRSpatialReference::FindProjParm(const char *pszParameter,
5655 : const OGR_SRSNode *poPROJCS) const
5656 :
5657 : {
5658 4316 : if (poPROJCS == nullptr)
5659 0 : poPROJCS = GetAttrNode("PROJCS");
5660 :
5661 4316 : if (poPROJCS == nullptr)
5662 0 : return -1;
5663 :
5664 : /* -------------------------------------------------------------------- */
5665 : /* Search for requested parameter. */
5666 : /* -------------------------------------------------------------------- */
5667 4316 : bool bIsWKT2 = false;
5668 27604 : for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5669 : {
5670 27193 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5671 :
5672 27193 : if (poParameter->GetChildCount() >= 2)
5673 : {
5674 18586 : const char *pszValue = poParameter->GetValue();
5675 31215 : if (EQUAL(pszValue, "PARAMETER") &&
5676 12629 : EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5677 : pszParameter))
5678 : {
5679 3905 : return iChild;
5680 : }
5681 14681 : else if (EQUAL(pszValue, "METHOD"))
5682 : {
5683 25 : bIsWKT2 = true;
5684 : }
5685 : }
5686 : }
5687 :
5688 : /* -------------------------------------------------------------------- */
5689 : /* Try similar names, for selected parameters. */
5690 : /* -------------------------------------------------------------------- */
5691 411 : if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5692 : {
5693 184 : if (bIsWKT2)
5694 : {
5695 4 : int iChild = FindProjParm(
5696 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5697 4 : if (iChild == -1)
5698 3 : iChild = FindProjParm(
5699 : EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5700 4 : return iChild;
5701 : }
5702 180 : return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5703 : }
5704 :
5705 227 : if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5706 : {
5707 27 : if (bIsWKT2)
5708 : {
5709 5 : int iChild = FindProjParm(
5710 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5711 5 : if (iChild == -1)
5712 0 : iChild = FindProjParm(
5713 : EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5714 5 : return iChild;
5715 : }
5716 22 : int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5717 22 : if (iChild == -1)
5718 0 : iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5719 22 : return iChild;
5720 : }
5721 :
5722 200 : return -1;
5723 : }
5724 :
5725 : /************************************************************************/
5726 : /* GetProjParm() */
5727 : /************************************************************************/
5728 :
5729 : /**
5730 : * \brief Fetch a projection parameter value.
5731 : *
5732 : * NOTE: This code should be modified to translate non degree angles into
5733 : * degrees based on the GEOGCS unit. This has not yet been done.
5734 : *
5735 : * This method is the same as the C function OSRGetProjParm().
5736 : *
5737 : * @param pszName the name of the parameter to fetch, from the set of
5738 : * SRS_PP codes in ogr_srs_api.h.
5739 : *
5740 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
5741 : *
5742 : * @param pnErr place to put error code on failure. Ignored if NULL.
5743 : *
5744 : * @return value of parameter.
5745 : */
5746 :
5747 4351 : double OGRSpatialReference::GetProjParm(const char *pszName,
5748 : double dfDefaultValue,
5749 : OGRErr *pnErr) const
5750 :
5751 : {
5752 4351 : d->refreshProjObj();
5753 4351 : GetRoot(); // force update of d->m_bNodesWKT2
5754 :
5755 4351 : if (pnErr != nullptr)
5756 3395 : *pnErr = OGRERR_NONE;
5757 :
5758 : /* -------------------------------------------------------------------- */
5759 : /* Find the desired parameter. */
5760 : /* -------------------------------------------------------------------- */
5761 : const OGR_SRSNode *poPROJCS =
5762 4351 : GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
5763 4351 : if (poPROJCS == nullptr)
5764 : {
5765 249 : if (pnErr != nullptr)
5766 248 : *pnErr = OGRERR_FAILURE;
5767 249 : return dfDefaultValue;
5768 : }
5769 :
5770 4102 : const int iChild = FindProjParm(pszName, poPROJCS);
5771 4102 : if (iChild == -1)
5772 : {
5773 197 : if (IsProjected() && GetAxesCount() == 3)
5774 : {
5775 3 : OGRSpatialReference *poSRSTmp = Clone();
5776 3 : poSRSTmp->DemoteTo2D(nullptr);
5777 : const double dfRet =
5778 3 : poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
5779 3 : delete poSRSTmp;
5780 3 : return dfRet;
5781 : }
5782 :
5783 194 : if (pnErr != nullptr)
5784 172 : *pnErr = OGRERR_FAILURE;
5785 194 : return dfDefaultValue;
5786 : }
5787 :
5788 3905 : const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5789 3905 : return CPLAtof(poParameter->GetChild(1)->GetValue());
5790 : }
5791 :
5792 : /************************************************************************/
5793 : /* OSRGetProjParm() */
5794 : /************************************************************************/
5795 :
5796 : /**
5797 : * \brief Fetch a projection parameter value.
5798 : *
5799 : * This function is the same as OGRSpatialReference::GetProjParm()
5800 : */
5801 91 : double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
5802 : double dfDefaultValue, OGRErr *pnErr)
5803 :
5804 : {
5805 91 : VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
5806 :
5807 91 : return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
5808 : }
5809 :
5810 : /************************************************************************/
5811 : /* GetNormProjParm() */
5812 : /************************************************************************/
5813 :
5814 : /**
5815 : * \brief Fetch a normalized projection parameter value.
5816 : *
5817 : * This method is the same as GetProjParm() except that the value of
5818 : * the parameter is "normalized" into degrees or meters depending on
5819 : * whether it is linear or angular.
5820 : *
5821 : * This method is the same as the C function OSRGetNormProjParm().
5822 : *
5823 : * @param pszName the name of the parameter to fetch, from the set of
5824 : * SRS_PP codes in ogr_srs_api.h.
5825 : *
5826 : * @param dfDefaultValue the value to return if this parameter doesn't exist.
5827 : *
5828 : * @param pnErr place to put error code on failure. Ignored if NULL.
5829 : *
5830 : * @return value of parameter.
5831 : */
5832 :
5833 3369 : double OGRSpatialReference::GetNormProjParm(const char *pszName,
5834 : double dfDefaultValue,
5835 : OGRErr *pnErr) const
5836 :
5837 : {
5838 3369 : GetNormInfo();
5839 :
5840 3369 : OGRErr nError = OGRERR_NONE;
5841 3369 : double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
5842 3369 : if (pnErr != nullptr)
5843 0 : *pnErr = nError;
5844 :
5845 : // If we got the default just return it unadjusted.
5846 3369 : if (nError != OGRERR_NONE)
5847 420 : return dfRawResult;
5848 :
5849 2949 : if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
5850 4 : dfRawResult *= d->dfToDegrees;
5851 :
5852 2949 : if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
5853 5 : return dfRawResult * d->dfToMeter;
5854 :
5855 2944 : return dfRawResult;
5856 : }
5857 :
5858 : /************************************************************************/
5859 : /* OSRGetNormProjParm() */
5860 : /************************************************************************/
5861 :
5862 : /**
5863 : * \brief This function is the same as OGRSpatialReference::
5864 : *
5865 : * This function is the same as OGRSpatialReference::GetNormProjParm()
5866 : */
5867 1 : double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
5868 : double dfDefaultValue, OGRErr *pnErr)
5869 :
5870 : {
5871 1 : VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
5872 :
5873 1 : return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
5874 : }
5875 :
5876 : /************************************************************************/
5877 : /* SetNormProjParm() */
5878 : /************************************************************************/
5879 :
5880 : /**
5881 : * \brief Set a projection parameter with a normalized value.
5882 : *
5883 : * This method is the same as SetProjParm() except that the value of
5884 : * the parameter passed in is assumed to be in "normalized" form (decimal
5885 : * degrees for angular values, meters for linear values. The values are
5886 : * converted in a form suitable for the GEOGCS and linear units in effect.
5887 : *
5888 : * This method is the same as the C function OSRSetNormProjParm().
5889 : *
5890 : * @param pszName the parameter name, which should be selected from
5891 : * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5892 : *
5893 : * @param dfValue value to assign.
5894 : *
5895 : * @return OGRERR_NONE on success.
5896 : */
5897 :
5898 91 : OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
5899 :
5900 : {
5901 91 : GetNormInfo();
5902 :
5903 91 : if (d->dfToDegrees != 0.0 &&
5904 91 : (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
5905 0 : IsAngularParameter(pszName))
5906 : {
5907 0 : dfValue /= d->dfToDegrees;
5908 : }
5909 95 : else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
5910 4 : IsLinearParameter(pszName))
5911 4 : dfValue /= d->dfToMeter;
5912 :
5913 91 : return SetProjParm(pszName, dfValue);
5914 : }
5915 :
5916 : /************************************************************************/
5917 : /* OSRSetNormProjParm() */
5918 : /************************************************************************/
5919 :
5920 : /**
5921 : * \brief Set a projection parameter with a normalized value.
5922 : *
5923 : * This function is the same as OGRSpatialReference::SetNormProjParm()
5924 : */
5925 0 : OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5926 : double dfValue)
5927 :
5928 : {
5929 0 : VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
5930 :
5931 0 : return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
5932 : }
5933 :
5934 : /************************************************************************/
5935 : /* SetTM() */
5936 : /************************************************************************/
5937 :
5938 397 : OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
5939 : double dfScale, double dfFalseEasting,
5940 : double dfFalseNorthing)
5941 :
5942 : {
5943 397 : return d->replaceConversionAndUnref(
5944 : proj_create_conversion_transverse_mercator(
5945 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
5946 397 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
5947 : }
5948 :
5949 : /************************************************************************/
5950 : /* OSRSetTM() */
5951 : /************************************************************************/
5952 :
5953 5 : OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
5954 : double dfCenterLong, double dfScale, double dfFalseEasting,
5955 : double dfFalseNorthing)
5956 :
5957 : {
5958 5 : VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
5959 :
5960 5 : return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
5961 5 : dfFalseEasting, dfFalseNorthing);
5962 : }
5963 :
5964 : /************************************************************************/
5965 : /* SetTMVariant() */
5966 : /************************************************************************/
5967 :
5968 0 : OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
5969 : double dfCenterLat,
5970 : double dfCenterLong, double dfScale,
5971 : double dfFalseEasting,
5972 : double dfFalseNorthing)
5973 :
5974 : {
5975 0 : SetProjection(pszVariantName);
5976 0 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
5977 0 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
5978 0 : SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
5979 0 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
5980 0 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
5981 :
5982 0 : return OGRERR_NONE;
5983 : }
5984 :
5985 : /************************************************************************/
5986 : /* OSRSetTMVariant() */
5987 : /************************************************************************/
5988 :
5989 0 : OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
5990 : double dfCenterLat, double dfCenterLong, double dfScale,
5991 : double dfFalseEasting, double dfFalseNorthing)
5992 :
5993 : {
5994 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
5995 :
5996 0 : return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
5997 : dfCenterLong, dfScale, dfFalseEasting,
5998 0 : dfFalseNorthing);
5999 : }
6000 :
6001 : /************************************************************************/
6002 : /* SetTMSO() */
6003 : /************************************************************************/
6004 :
6005 3 : OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6006 : double dfScale, double dfFalseEasting,
6007 : double dfFalseNorthing)
6008 :
6009 : {
6010 3 : auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6011 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6012 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6013 :
6014 3 : const char *pszName = nullptr;
6015 3 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6016 3 : CPLString osName = pszName ? pszName : "";
6017 :
6018 3 : d->refreshProjObj();
6019 :
6020 3 : d->demoteFromBoundCRS();
6021 :
6022 3 : auto cs = proj_create_cartesian_2D_cs(
6023 : d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6024 3 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6025 : auto projCRS =
6026 3 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6027 3 : d->getGeodBaseCRS(), conv, cs);
6028 3 : proj_destroy(conv);
6029 3 : proj_destroy(cs);
6030 :
6031 3 : d->setPjCRS(projCRS);
6032 :
6033 3 : d->undoDemoteFromBoundCRS();
6034 :
6035 6 : return OGRERR_NONE;
6036 : }
6037 :
6038 : /************************************************************************/
6039 : /* OSRSetTMSO() */
6040 : /************************************************************************/
6041 :
6042 0 : OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6043 : double dfCenterLong, double dfScale, double dfFalseEasting,
6044 : double dfFalseNorthing)
6045 :
6046 : {
6047 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6048 :
6049 0 : return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6050 0 : dfFalseEasting, dfFalseNorthing);
6051 : }
6052 :
6053 : /************************************************************************/
6054 : /* SetTPED() */
6055 : /************************************************************************/
6056 :
6057 1 : OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6058 : double dfLat2, double dfLong2,
6059 : double dfFalseEasting,
6060 : double dfFalseNorthing)
6061 :
6062 : {
6063 1 : return d->replaceConversionAndUnref(
6064 : proj_create_conversion_two_point_equidistant(
6065 : d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6066 1 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6067 : }
6068 :
6069 : /************************************************************************/
6070 : /* OSRSetTPED() */
6071 : /************************************************************************/
6072 :
6073 0 : OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6074 : double dfLat2, double dfLong2, double dfFalseEasting,
6075 : double dfFalseNorthing)
6076 :
6077 : {
6078 0 : VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6079 :
6080 0 : return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6081 0 : dfFalseEasting, dfFalseNorthing);
6082 : }
6083 :
6084 : /************************************************************************/
6085 : /* SetTMG() */
6086 : /************************************************************************/
6087 :
6088 0 : OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6089 : double dfFalseEasting,
6090 : double dfFalseNorthing)
6091 :
6092 : {
6093 0 : return d->replaceConversionAndUnref(
6094 : proj_create_conversion_tunisia_mapping_grid(
6095 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6096 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6097 : }
6098 :
6099 : /************************************************************************/
6100 : /* OSRSetTMG() */
6101 : /************************************************************************/
6102 :
6103 0 : OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6104 : double dfCenterLong, double dfFalseEasting,
6105 : double dfFalseNorthing)
6106 :
6107 : {
6108 0 : VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6109 :
6110 0 : return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6111 0 : dfFalseNorthing);
6112 : }
6113 :
6114 : /************************************************************************/
6115 : /* SetACEA() */
6116 : /************************************************************************/
6117 :
6118 40 : OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6119 : double dfCenterLat, double dfCenterLong,
6120 : double dfFalseEasting,
6121 : double dfFalseNorthing)
6122 :
6123 : {
6124 : // Note different order of parameters. The one in PROJ is conformant with
6125 : // EPSG
6126 40 : return d->replaceConversionAndUnref(
6127 : proj_create_conversion_albers_equal_area(
6128 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6129 40 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6130 : }
6131 :
6132 : /************************************************************************/
6133 : /* OSRSetACEA() */
6134 : /************************************************************************/
6135 :
6136 0 : OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6137 : double dfCenterLat, double dfCenterLong,
6138 : double dfFalseEasting, double dfFalseNorthing)
6139 :
6140 : {
6141 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6142 :
6143 0 : return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6144 0 : dfFalseEasting, dfFalseNorthing);
6145 : }
6146 :
6147 : /************************************************************************/
6148 : /* SetAE() */
6149 : /************************************************************************/
6150 :
6151 19 : OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6152 : double dfFalseEasting, double dfFalseNorthing)
6153 :
6154 : {
6155 19 : return d->replaceConversionAndUnref(
6156 : proj_create_conversion_azimuthal_equidistant(
6157 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6158 19 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6159 : }
6160 :
6161 : /************************************************************************/
6162 : /* OSRSetAE() */
6163 : /************************************************************************/
6164 :
6165 0 : OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6166 : double dfCenterLong, double dfFalseEasting,
6167 : double dfFalseNorthing)
6168 :
6169 : {
6170 0 : VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6171 :
6172 0 : return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6173 0 : dfFalseNorthing);
6174 : }
6175 :
6176 : /************************************************************************/
6177 : /* SetBonne() */
6178 : /************************************************************************/
6179 :
6180 1 : OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6181 : double dfFalseEasting,
6182 : double dfFalseNorthing)
6183 :
6184 : {
6185 1 : return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6186 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6187 1 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6188 : }
6189 :
6190 : /************************************************************************/
6191 : /* OSRSetBonne() */
6192 : /************************************************************************/
6193 :
6194 0 : OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6195 : double dfCentralMeridian, double dfFalseEasting,
6196 : double dfFalseNorthing)
6197 :
6198 : {
6199 0 : VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6200 :
6201 0 : return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6202 0 : dfFalseNorthing);
6203 : }
6204 :
6205 : /************************************************************************/
6206 : /* SetCEA() */
6207 : /************************************************************************/
6208 :
6209 4 : OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6210 : double dfFalseEasting,
6211 : double dfFalseNorthing)
6212 :
6213 : {
6214 4 : return d->replaceConversionAndUnref(
6215 : proj_create_conversion_lambert_cylindrical_equal_area(
6216 : d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6217 4 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6218 : }
6219 :
6220 : /************************************************************************/
6221 : /* OSRSetCEA() */
6222 : /************************************************************************/
6223 :
6224 0 : OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6225 : double dfCentralMeridian, double dfFalseEasting,
6226 : double dfFalseNorthing)
6227 :
6228 : {
6229 0 : VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6230 :
6231 0 : return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6232 0 : dfFalseNorthing);
6233 : }
6234 :
6235 : /************************************************************************/
6236 : /* SetCS() */
6237 : /************************************************************************/
6238 :
6239 5 : OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6240 : double dfFalseEasting, double dfFalseNorthing)
6241 :
6242 : {
6243 5 : return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6244 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6245 5 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6246 : }
6247 :
6248 : /************************************************************************/
6249 : /* OSRSetCS() */
6250 : /************************************************************************/
6251 :
6252 0 : OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6253 : double dfCenterLong, double dfFalseEasting,
6254 : double dfFalseNorthing)
6255 :
6256 : {
6257 0 : VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6258 :
6259 0 : return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6260 0 : dfFalseNorthing);
6261 : }
6262 :
6263 : /************************************************************************/
6264 : /* SetEC() */
6265 : /************************************************************************/
6266 :
6267 7 : OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6268 : double dfCenterLat, double dfCenterLong,
6269 : double dfFalseEasting, double dfFalseNorthing)
6270 :
6271 : {
6272 : // Note: different order of arguments
6273 7 : return d->replaceConversionAndUnref(
6274 : proj_create_conversion_equidistant_conic(
6275 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6276 7 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6277 : }
6278 :
6279 : /************************************************************************/
6280 : /* OSRSetEC() */
6281 : /************************************************************************/
6282 :
6283 0 : OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6284 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6285 : double dfFalseNorthing)
6286 :
6287 : {
6288 0 : VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6289 :
6290 0 : return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6291 0 : dfFalseEasting, dfFalseNorthing);
6292 : }
6293 :
6294 : /************************************************************************/
6295 : /* SetEckert() */
6296 : /************************************************************************/
6297 :
6298 10 : OGRErr OGRSpatialReference::SetEckert(int nVariation, // 1-6.
6299 : double dfCentralMeridian,
6300 : double dfFalseEasting,
6301 : double dfFalseNorthing)
6302 :
6303 : {
6304 : PJ *conv;
6305 10 : if (nVariation == 1)
6306 : {
6307 1 : conv = proj_create_conversion_eckert_i(
6308 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6309 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6310 : }
6311 9 : else if (nVariation == 2)
6312 : {
6313 1 : conv = proj_create_conversion_eckert_ii(
6314 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6315 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6316 : }
6317 8 : else if (nVariation == 3)
6318 : {
6319 1 : conv = proj_create_conversion_eckert_iii(
6320 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6321 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6322 : }
6323 7 : else if (nVariation == 4)
6324 : {
6325 3 : conv = proj_create_conversion_eckert_iv(
6326 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6327 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6328 : }
6329 4 : else if (nVariation == 5)
6330 : {
6331 1 : conv = proj_create_conversion_eckert_v(
6332 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6333 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6334 : }
6335 3 : else if (nVariation == 6)
6336 : {
6337 3 : conv = proj_create_conversion_eckert_vi(
6338 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6339 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6340 : }
6341 : else
6342 : {
6343 0 : CPLError(CE_Failure, CPLE_AppDefined,
6344 : "Unsupported Eckert variation (%d).", nVariation);
6345 0 : return OGRERR_UNSUPPORTED_SRS;
6346 : }
6347 :
6348 10 : return d->replaceConversionAndUnref(conv);
6349 : }
6350 :
6351 : /************************************************************************/
6352 : /* OSRSetEckert() */
6353 : /************************************************************************/
6354 :
6355 0 : OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6356 : double dfCentralMeridian, double dfFalseEasting,
6357 : double dfFalseNorthing)
6358 :
6359 : {
6360 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6361 :
6362 0 : return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6363 0 : dfFalseEasting, dfFalseNorthing);
6364 : }
6365 :
6366 : /************************************************************************/
6367 : /* SetEckertIV() */
6368 : /* */
6369 : /* Deprecated */
6370 : /************************************************************************/
6371 :
6372 2 : OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6373 : double dfFalseEasting,
6374 : double dfFalseNorthing)
6375 :
6376 : {
6377 2 : return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6378 : }
6379 :
6380 : /************************************************************************/
6381 : /* OSRSetEckertIV() */
6382 : /************************************************************************/
6383 :
6384 0 : OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6385 : double dfFalseEasting, double dfFalseNorthing)
6386 :
6387 : {
6388 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6389 :
6390 0 : return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6391 0 : dfFalseNorthing);
6392 : }
6393 :
6394 : /************************************************************************/
6395 : /* SetEckertVI() */
6396 : /* */
6397 : /* Deprecated */
6398 : /************************************************************************/
6399 :
6400 2 : OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6401 : double dfFalseEasting,
6402 : double dfFalseNorthing)
6403 :
6404 : {
6405 2 : return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6406 : }
6407 :
6408 : /************************************************************************/
6409 : /* OSRSetEckertVI() */
6410 : /************************************************************************/
6411 :
6412 0 : OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6413 : double dfFalseEasting, double dfFalseNorthing)
6414 :
6415 : {
6416 0 : VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6417 :
6418 0 : return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6419 0 : dfFalseNorthing);
6420 : }
6421 :
6422 : /************************************************************************/
6423 : /* SetEquirectangular() */
6424 : /************************************************************************/
6425 :
6426 2 : OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6427 : double dfCenterLong,
6428 : double dfFalseEasting,
6429 : double dfFalseNorthing)
6430 :
6431 : {
6432 2 : if (dfCenterLat == 0.0)
6433 : {
6434 0 : return d->replaceConversionAndUnref(
6435 : proj_create_conversion_equidistant_cylindrical(
6436 : d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6437 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6438 : }
6439 :
6440 : // Non-standard extension with non-zero latitude of origin
6441 2 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6442 2 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6443 2 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6444 2 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6445 2 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6446 :
6447 2 : return OGRERR_NONE;
6448 : }
6449 :
6450 : /************************************************************************/
6451 : /* OSRSetEquirectangular() */
6452 : /************************************************************************/
6453 :
6454 0 : OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6455 : double dfCenterLong, double dfFalseEasting,
6456 : double dfFalseNorthing)
6457 :
6458 : {
6459 0 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6460 :
6461 0 : return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6462 0 : dfFalseEasting, dfFalseNorthing);
6463 : }
6464 :
6465 : /************************************************************************/
6466 : /* SetEquirectangular2() */
6467 : /* Generalized form */
6468 : /************************************************************************/
6469 :
6470 174 : OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6471 : double dfCenterLong,
6472 : double dfStdParallel1,
6473 : double dfFalseEasting,
6474 : double dfFalseNorthing)
6475 :
6476 : {
6477 174 : if (dfCenterLat == 0.0)
6478 : {
6479 169 : return d->replaceConversionAndUnref(
6480 : proj_create_conversion_equidistant_cylindrical(
6481 : d->getPROJContext(), dfStdParallel1, dfCenterLong,
6482 169 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6483 : }
6484 :
6485 : // Non-standard extension with non-zero latitude of origin
6486 5 : SetProjection(SRS_PT_EQUIRECTANGULAR);
6487 5 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6488 5 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6489 5 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6490 5 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6491 5 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6492 :
6493 5 : return OGRERR_NONE;
6494 : }
6495 :
6496 : /************************************************************************/
6497 : /* OSRSetEquirectangular2() */
6498 : /************************************************************************/
6499 :
6500 3 : OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6501 : double dfCenterLong, double dfStdParallel1,
6502 : double dfFalseEasting, double dfFalseNorthing)
6503 :
6504 : {
6505 3 : VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6506 :
6507 3 : return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6508 : dfStdParallel1, dfFalseEasting,
6509 3 : dfFalseNorthing);
6510 : }
6511 :
6512 : /************************************************************************/
6513 : /* SetGS() */
6514 : /************************************************************************/
6515 :
6516 5 : OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6517 : double dfFalseEasting, double dfFalseNorthing)
6518 :
6519 : {
6520 5 : return d->replaceConversionAndUnref(proj_create_conversion_gall(
6521 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6522 5 : nullptr, 0.0, nullptr, 0.0));
6523 : }
6524 :
6525 : /************************************************************************/
6526 : /* OSRSetGS() */
6527 : /************************************************************************/
6528 :
6529 2 : OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6530 : double dfFalseEasting, double dfFalseNorthing)
6531 :
6532 : {
6533 2 : VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6534 :
6535 2 : return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6536 2 : dfFalseNorthing);
6537 : }
6538 :
6539 : /************************************************************************/
6540 : /* SetGH() */
6541 : /************************************************************************/
6542 :
6543 0 : OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6544 : double dfFalseEasting, double dfFalseNorthing)
6545 :
6546 : {
6547 0 : return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6548 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6549 0 : nullptr, 0.0, nullptr, 0.0));
6550 : }
6551 :
6552 : /************************************************************************/
6553 : /* OSRSetGH() */
6554 : /************************************************************************/
6555 :
6556 0 : OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6557 : double dfFalseEasting, double dfFalseNorthing)
6558 :
6559 : {
6560 0 : VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6561 :
6562 0 : return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6563 0 : dfFalseNorthing);
6564 : }
6565 :
6566 : /************************************************************************/
6567 : /* SetIGH() */
6568 : /************************************************************************/
6569 :
6570 0 : OGRErr OGRSpatialReference::SetIGH()
6571 :
6572 : {
6573 0 : return d->replaceConversionAndUnref(
6574 : proj_create_conversion_interrupted_goode_homolosine(
6575 0 : d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6576 : }
6577 :
6578 : /************************************************************************/
6579 : /* OSRSetIGH() */
6580 : /************************************************************************/
6581 :
6582 0 : OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6583 :
6584 : {
6585 0 : VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6586 :
6587 0 : return ToPointer(hSRS)->SetIGH();
6588 : }
6589 :
6590 : /************************************************************************/
6591 : /* SetGEOS() */
6592 : /************************************************************************/
6593 :
6594 3 : OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6595 : double dfSatelliteHeight,
6596 : double dfFalseEasting,
6597 : double dfFalseNorthing)
6598 :
6599 : {
6600 3 : return d->replaceConversionAndUnref(
6601 : proj_create_conversion_geostationary_satellite_sweep_y(
6602 : d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6603 3 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6604 : }
6605 :
6606 : /************************************************************************/
6607 : /* OSRSetGEOS() */
6608 : /************************************************************************/
6609 :
6610 0 : OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6611 : double dfSatelliteHeight, double dfFalseEasting,
6612 : double dfFalseNorthing)
6613 :
6614 : {
6615 0 : VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6616 :
6617 0 : return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6618 0 : dfFalseEasting, dfFalseNorthing);
6619 : }
6620 :
6621 : /************************************************************************/
6622 : /* SetGaussSchreiberTMercator() */
6623 : /************************************************************************/
6624 :
6625 0 : OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6626 : double dfCenterLong,
6627 : double dfScale,
6628 : double dfFalseEasting,
6629 : double dfFalseNorthing)
6630 :
6631 : {
6632 0 : return d->replaceConversionAndUnref(
6633 : proj_create_conversion_gauss_schreiber_transverse_mercator(
6634 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6635 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6636 : }
6637 :
6638 : /************************************************************************/
6639 : /* OSRSetGaussSchreiberTMercator() */
6640 : /************************************************************************/
6641 :
6642 0 : OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6643 : double dfCenterLat, double dfCenterLong,
6644 : double dfScale, double dfFalseEasting,
6645 : double dfFalseNorthing)
6646 :
6647 : {
6648 0 : VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6649 :
6650 0 : return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6651 0 : dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6652 : }
6653 :
6654 : /************************************************************************/
6655 : /* SetGnomonic() */
6656 : /************************************************************************/
6657 :
6658 2 : OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6659 : double dfFalseEasting,
6660 : double dfFalseNorthing)
6661 :
6662 : {
6663 2 : return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6664 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6665 2 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6666 : }
6667 :
6668 : /************************************************************************/
6669 : /* OSRSetGnomonic() */
6670 : /************************************************************************/
6671 :
6672 0 : OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6673 : double dfCenterLong, double dfFalseEasting,
6674 : double dfFalseNorthing)
6675 :
6676 : {
6677 0 : VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6678 :
6679 0 : return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6680 0 : dfFalseEasting, dfFalseNorthing);
6681 : }
6682 :
6683 : /************************************************************************/
6684 : /* SetHOMAC() */
6685 : /************************************************************************/
6686 :
6687 : /**
6688 : * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6689 : * azimuth angle.
6690 : *
6691 : * This projection corresponds to EPSG projection method 9815, also
6692 : * sometimes known as hotine oblique mercator (variant B).
6693 : *
6694 : * This method does the same thing as the C function OSRSetHOMAC().
6695 : *
6696 : * @param dfCenterLat Latitude of the projection origin.
6697 : * @param dfCenterLong Longitude of the projection origin.
6698 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6699 : * centerline.
6700 : * @param dfRectToSkew Angle from Rectified to Skew Grid
6701 : * @param dfScale Scale factor applies to the projection origin.
6702 : * @param dfFalseEasting False easting.
6703 : * @param dfFalseNorthing False northing.
6704 : *
6705 : * @return OGRERR_NONE on success.
6706 : */
6707 :
6708 4 : OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
6709 : double dfAzimuth, double dfRectToSkew,
6710 : double dfScale, double dfFalseEasting,
6711 : double dfFalseNorthing)
6712 :
6713 : {
6714 4 : return d->replaceConversionAndUnref(
6715 : proj_create_conversion_hotine_oblique_mercator_variant_b(
6716 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
6717 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
6718 4 : 0.0, nullptr, 0.0));
6719 : }
6720 :
6721 : /************************************************************************/
6722 : /* OSRSetHOMAC() */
6723 : /************************************************************************/
6724 :
6725 : /**
6726 : * \brief Set an Oblique Mercator projection using azimuth angle.
6727 : *
6728 : * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
6729 : */
6730 0 : OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
6731 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
6732 : double dfScale, double dfFalseEasting,
6733 : double dfFalseNorthing)
6734 :
6735 : {
6736 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
6737 :
6738 0 : return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
6739 : dfRectToSkew, dfScale, dfFalseEasting,
6740 0 : dfFalseNorthing);
6741 : }
6742 :
6743 : /************************************************************************/
6744 : /* SetHOM() */
6745 : /************************************************************************/
6746 :
6747 : /**
6748 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
6749 : *
6750 : * This projection corresponds to EPSG projection method 9812, also
6751 : * sometimes known as hotine oblique mercator (variant A)..
6752 : *
6753 : * This method does the same thing as the C function OSRSetHOM().
6754 : *
6755 : * @param dfCenterLat Latitude of the projection origin.
6756 : * @param dfCenterLong Longitude of the projection origin.
6757 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6758 : * centerline.
6759 : * @param dfRectToSkew Angle from Rectified to Skew Grid
6760 : * @param dfScale Scale factor applies to the projection origin.
6761 : * @param dfFalseEasting False easting.
6762 : * @param dfFalseNorthing False northing.
6763 : *
6764 : * @return OGRERR_NONE on success.
6765 : */
6766 :
6767 12 : OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
6768 : double dfAzimuth, double dfRectToSkew,
6769 : double dfScale, double dfFalseEasting,
6770 : double dfFalseNorthing)
6771 :
6772 : {
6773 12 : return d->replaceConversionAndUnref(
6774 : proj_create_conversion_hotine_oblique_mercator_variant_a(
6775 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
6776 : dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
6777 12 : 0.0, nullptr, 0.0));
6778 : }
6779 :
6780 : /************************************************************************/
6781 : /* OSRSetHOM() */
6782 : /************************************************************************/
6783 : /**
6784 : * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
6785 : *
6786 : * This is the same as the C++ method OGRSpatialReference::SetHOM()
6787 : */
6788 0 : OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6789 : double dfCenterLong, double dfAzimuth, double dfRectToSkew,
6790 : double dfScale, double dfFalseEasting, double dfFalseNorthing)
6791 :
6792 : {
6793 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
6794 :
6795 0 : return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
6796 : dfRectToSkew, dfScale, dfFalseEasting,
6797 0 : dfFalseNorthing);
6798 : }
6799 :
6800 : /************************************************************************/
6801 : /* SetHOM2PNO() */
6802 : /************************************************************************/
6803 :
6804 : /**
6805 : * \brief Set a Hotine Oblique Mercator projection using two points on
6806 : * projection centerline.
6807 : *
6808 : * This method does the same thing as the C function OSRSetHOM2PNO().
6809 : *
6810 : * @param dfCenterLat Latitude of the projection origin.
6811 : * @param dfLat1 Latitude of the first point on center line.
6812 : * @param dfLong1 Longitude of the first point on center line.
6813 : * @param dfLat2 Latitude of the second point on center line.
6814 : * @param dfLong2 Longitude of the second point on center line.
6815 : * @param dfScale Scale factor applies to the projection origin.
6816 : * @param dfFalseEasting False easting.
6817 : * @param dfFalseNorthing False northing.
6818 : *
6819 : * @return OGRERR_NONE on success.
6820 : */
6821 :
6822 3 : OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
6823 : double dfLong1, double dfLat2,
6824 : double dfLong2, double dfScale,
6825 : double dfFalseEasting,
6826 : double dfFalseNorthing)
6827 :
6828 : {
6829 3 : return d->replaceConversionAndUnref(
6830 : proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
6831 : d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
6832 : dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
6833 3 : 0.0));
6834 : }
6835 :
6836 : /************************************************************************/
6837 : /* OSRSetHOM2PNO() */
6838 : /************************************************************************/
6839 : /**
6840 : * \brief Set a Hotine Oblique Mercator projection using two points on
6841 : * projection centerline.
6842 : *
6843 : * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
6844 : */
6845 0 : OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6846 : double dfLat1, double dfLong1, double dfLat2,
6847 : double dfLong2, double dfScale, double dfFalseEasting,
6848 : double dfFalseNorthing)
6849 :
6850 : {
6851 0 : VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
6852 :
6853 0 : return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
6854 : dfLong2, dfScale, dfFalseEasting,
6855 0 : dfFalseNorthing);
6856 : }
6857 :
6858 : /************************************************************************/
6859 : /* SetLOM() */
6860 : /************************************************************************/
6861 :
6862 : /**
6863 : * \brief Set a Laborde Oblique Mercator projection.
6864 : *
6865 : * @param dfCenterLat Latitude of the projection origin.
6866 : * @param dfCenterLong Longitude of the projection origin.
6867 : * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6868 : * centerline.
6869 : * @param dfScale Scale factor on the initiali line
6870 : * @param dfFalseEasting False easting.
6871 : * @param dfFalseNorthing False northing.
6872 : *
6873 : * @return OGRERR_NONE on success.
6874 : */
6875 :
6876 0 : OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
6877 : double dfAzimuth, double dfScale,
6878 : double dfFalseEasting,
6879 : double dfFalseNorthing)
6880 :
6881 : {
6882 0 : return d->replaceConversionAndUnref(
6883 : proj_create_conversion_laborde_oblique_mercator(
6884 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
6885 0 : dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6886 : }
6887 :
6888 : /************************************************************************/
6889 : /* SetIWMPolyconic() */
6890 : /************************************************************************/
6891 :
6892 0 : OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
6893 : double dfCenterLong,
6894 : double dfFalseEasting,
6895 : double dfFalseNorthing)
6896 :
6897 : {
6898 0 : return d->replaceConversionAndUnref(
6899 : proj_create_conversion_international_map_world_polyconic(
6900 : d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
6901 0 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6902 : }
6903 :
6904 : /************************************************************************/
6905 : /* OSRSetIWMPolyconic() */
6906 : /************************************************************************/
6907 :
6908 0 : OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
6909 : double dfLat2, double dfCenterLong,
6910 : double dfFalseEasting, double dfFalseNorthing)
6911 :
6912 : {
6913 0 : VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
6914 :
6915 0 : return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
6916 0 : dfFalseEasting, dfFalseNorthing);
6917 : }
6918 :
6919 : /************************************************************************/
6920 : /* SetKrovak() */
6921 : /************************************************************************/
6922 :
6923 : /** Krovak east-north projection.
6924 : *
6925 : * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
6926 : * to PROJ and should be respectively set to 30.28813972222222 and 78.5
6927 : */
6928 3 : OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
6929 : double dfAzimuth,
6930 : double dfPseudoStdParallel1,
6931 : double dfScale, double dfFalseEasting,
6932 : double dfFalseNorthing)
6933 :
6934 : {
6935 3 : return d->replaceConversionAndUnref(
6936 : proj_create_conversion_krovak_north_oriented(
6937 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
6938 : dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
6939 3 : nullptr, 0.0, nullptr, 0.0));
6940 : }
6941 :
6942 : /************************************************************************/
6943 : /* OSRSetKrovak() */
6944 : /************************************************************************/
6945 :
6946 0 : OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
6947 : double dfCenterLong, double dfAzimuth,
6948 : double dfPseudoStdParallel1, double dfScale,
6949 : double dfFalseEasting, double dfFalseNorthing)
6950 :
6951 : {
6952 0 : VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
6953 :
6954 0 : return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
6955 : dfPseudoStdParallel1, dfScale,
6956 0 : dfFalseEasting, dfFalseNorthing);
6957 : }
6958 :
6959 : /************************************************************************/
6960 : /* SetLAEA() */
6961 : /************************************************************************/
6962 :
6963 13 : OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
6964 : double dfFalseEasting,
6965 : double dfFalseNorthing)
6966 :
6967 : {
6968 13 : auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
6969 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6970 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6971 :
6972 13 : const char *pszName = nullptr;
6973 13 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6974 13 : CPLString osName = pszName ? pszName : "";
6975 :
6976 13 : d->refreshProjObj();
6977 :
6978 13 : d->demoteFromBoundCRS();
6979 :
6980 13 : auto cs = proj_create_cartesian_2D_cs(
6981 : d->getPROJContext(),
6982 13 : std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
6983 : ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
6984 0 : : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
6985 10 : ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
6986 : : PJ_CART2D_EASTING_NORTHING,
6987 13 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6988 : auto projCRS =
6989 13 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6990 13 : d->getGeodBaseCRS(), conv, cs);
6991 13 : proj_destroy(conv);
6992 13 : proj_destroy(cs);
6993 :
6994 13 : d->setPjCRS(projCRS);
6995 :
6996 13 : d->undoDemoteFromBoundCRS();
6997 :
6998 26 : return OGRERR_NONE;
6999 : }
7000 :
7001 : /************************************************************************/
7002 : /* OSRSetLAEA() */
7003 : /************************************************************************/
7004 :
7005 0 : OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7006 : double dfCenterLong, double dfFalseEasting,
7007 : double dfFalseNorthing)
7008 :
7009 : {
7010 0 : VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7011 :
7012 0 : return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7013 0 : dfFalseNorthing);
7014 : }
7015 :
7016 : /************************************************************************/
7017 : /* SetLCC() */
7018 : /************************************************************************/
7019 :
7020 145 : OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7021 : double dfCenterLat, double dfCenterLong,
7022 : double dfFalseEasting,
7023 : double dfFalseNorthing)
7024 :
7025 : {
7026 145 : return d->replaceConversionAndUnref(
7027 : proj_create_conversion_lambert_conic_conformal_2sp(
7028 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7029 145 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7030 : }
7031 :
7032 : /************************************************************************/
7033 : /* OSRSetLCC() */
7034 : /************************************************************************/
7035 :
7036 3 : OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7037 : double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7038 : double dfFalseNorthing)
7039 :
7040 : {
7041 3 : VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7042 :
7043 3 : return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7044 3 : dfFalseEasting, dfFalseNorthing);
7045 : }
7046 :
7047 : /************************************************************************/
7048 : /* SetLCC1SP() */
7049 : /************************************************************************/
7050 :
7051 7 : OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7052 : double dfScale, double dfFalseEasting,
7053 : double dfFalseNorthing)
7054 :
7055 : {
7056 7 : return d->replaceConversionAndUnref(
7057 : proj_create_conversion_lambert_conic_conformal_1sp(
7058 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7059 7 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7060 : }
7061 :
7062 : /************************************************************************/
7063 : /* OSRSetLCC1SP() */
7064 : /************************************************************************/
7065 :
7066 0 : OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7067 : double dfCenterLong, double dfScale, double dfFalseEasting,
7068 : double dfFalseNorthing)
7069 :
7070 : {
7071 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7072 :
7073 0 : return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7074 0 : dfFalseEasting, dfFalseNorthing);
7075 : }
7076 :
7077 : /************************************************************************/
7078 : /* SetLCCB() */
7079 : /************************************************************************/
7080 :
7081 2 : OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7082 : double dfCenterLat, double dfCenterLong,
7083 : double dfFalseEasting,
7084 : double dfFalseNorthing)
7085 :
7086 : {
7087 2 : return d->replaceConversionAndUnref(
7088 : proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7089 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7090 2 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7091 : }
7092 :
7093 : /************************************************************************/
7094 : /* OSRSetLCCB() */
7095 : /************************************************************************/
7096 :
7097 0 : OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7098 : double dfCenterLat, double dfCenterLong,
7099 : double dfFalseEasting, double dfFalseNorthing)
7100 :
7101 : {
7102 0 : VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7103 :
7104 0 : return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7105 0 : dfFalseEasting, dfFalseNorthing);
7106 : }
7107 :
7108 : /************************************************************************/
7109 : /* SetMC() */
7110 : /************************************************************************/
7111 :
7112 4 : OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7113 : double dfFalseEasting, double dfFalseNorthing)
7114 :
7115 : {
7116 : (void)dfCenterLat; // ignored
7117 :
7118 4 : return d->replaceConversionAndUnref(
7119 : proj_create_conversion_miller_cylindrical(
7120 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7121 4 : nullptr, 0, nullptr, 0));
7122 : }
7123 :
7124 : /************************************************************************/
7125 : /* OSRSetMC() */
7126 : /************************************************************************/
7127 :
7128 0 : OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7129 : double dfCenterLong, double dfFalseEasting,
7130 : double dfFalseNorthing)
7131 :
7132 : {
7133 0 : VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7134 :
7135 0 : return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7136 0 : dfFalseNorthing);
7137 : }
7138 :
7139 : /************************************************************************/
7140 : /* SetMercator() */
7141 : /************************************************************************/
7142 :
7143 56 : OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7144 : double dfScale, double dfFalseEasting,
7145 : double dfFalseNorthing)
7146 :
7147 : {
7148 56 : if (dfCenterLat != 0.0 && dfScale == 1.0)
7149 : {
7150 : // Not sure this is correct, but this is how it has been used
7151 : // historically
7152 0 : return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7153 0 : dfFalseNorthing);
7154 : }
7155 56 : return d->replaceConversionAndUnref(
7156 : proj_create_conversion_mercator_variant_a(
7157 : d->getPROJContext(),
7158 : dfCenterLat, // should be zero
7159 : dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7160 56 : nullptr, 0));
7161 : }
7162 :
7163 : /************************************************************************/
7164 : /* OSRSetMercator() */
7165 : /************************************************************************/
7166 :
7167 2 : OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7168 : double dfCenterLong, double dfScale,
7169 : double dfFalseEasting, double dfFalseNorthing)
7170 :
7171 : {
7172 2 : VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7173 :
7174 2 : return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7175 2 : dfFalseEasting, dfFalseNorthing);
7176 : }
7177 :
7178 : /************************************************************************/
7179 : /* SetMercator2SP() */
7180 : /************************************************************************/
7181 :
7182 30 : OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7183 : double dfCenterLong,
7184 : double dfFalseEasting,
7185 : double dfFalseNorthing)
7186 :
7187 : {
7188 30 : if (dfCenterLat == 0.0)
7189 : {
7190 29 : return d->replaceConversionAndUnref(
7191 : proj_create_conversion_mercator_variant_b(
7192 : d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7193 29 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7194 : }
7195 :
7196 1 : SetProjection(SRS_PT_MERCATOR_2SP);
7197 :
7198 1 : SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7199 1 : SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7200 1 : SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7201 1 : SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7202 1 : SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7203 :
7204 1 : return OGRERR_NONE;
7205 : }
7206 :
7207 : /************************************************************************/
7208 : /* OSRSetMercator2SP() */
7209 : /************************************************************************/
7210 :
7211 1 : OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7212 : double dfCenterLat, double dfCenterLong,
7213 : double dfFalseEasting, double dfFalseNorthing)
7214 :
7215 : {
7216 1 : VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7217 :
7218 1 : return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7219 1 : dfFalseEasting, dfFalseNorthing);
7220 : }
7221 :
7222 : /************************************************************************/
7223 : /* SetMollweide() */
7224 : /************************************************************************/
7225 :
7226 3 : OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7227 : double dfFalseEasting,
7228 : double dfFalseNorthing)
7229 :
7230 : {
7231 3 : return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7232 : d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7233 3 : nullptr, 0, nullptr, 0));
7234 : }
7235 :
7236 : /************************************************************************/
7237 : /* OSRSetMollweide() */
7238 : /************************************************************************/
7239 :
7240 0 : OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7241 : double dfFalseEasting, double dfFalseNorthing)
7242 :
7243 : {
7244 0 : VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7245 :
7246 0 : return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7247 0 : dfFalseNorthing);
7248 : }
7249 :
7250 : /************************************************************************/
7251 : /* SetNZMG() */
7252 : /************************************************************************/
7253 :
7254 6 : OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7255 : double dfFalseEasting,
7256 : double dfFalseNorthing)
7257 :
7258 : {
7259 6 : return d->replaceConversionAndUnref(
7260 : proj_create_conversion_new_zealand_mapping_grid(
7261 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7262 6 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7263 : }
7264 :
7265 : /************************************************************************/
7266 : /* OSRSetNZMG() */
7267 : /************************************************************************/
7268 :
7269 0 : OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7270 : double dfCenterLong, double dfFalseEasting,
7271 : double dfFalseNorthing)
7272 :
7273 : {
7274 0 : VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7275 :
7276 0 : return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7277 0 : dfFalseNorthing);
7278 : }
7279 :
7280 : /************************************************************************/
7281 : /* SetOS() */
7282 : /************************************************************************/
7283 :
7284 6 : OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7285 : double dfScale, double dfFalseEasting,
7286 : double dfFalseNorthing)
7287 :
7288 : {
7289 6 : return d->replaceConversionAndUnref(
7290 : proj_create_conversion_oblique_stereographic(
7291 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7292 6 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7293 : }
7294 :
7295 : /************************************************************************/
7296 : /* OSRSetOS() */
7297 : /************************************************************************/
7298 :
7299 0 : OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7300 : double dfCMeridian, double dfScale, double dfFalseEasting,
7301 : double dfFalseNorthing)
7302 :
7303 : {
7304 0 : VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7305 :
7306 0 : return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7307 0 : dfFalseEasting, dfFalseNorthing);
7308 : }
7309 :
7310 : /************************************************************************/
7311 : /* SetOrthographic() */
7312 : /************************************************************************/
7313 :
7314 7 : OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7315 : double dfCenterLong,
7316 : double dfFalseEasting,
7317 : double dfFalseNorthing)
7318 :
7319 : {
7320 7 : return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7321 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7322 7 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7323 : }
7324 :
7325 : /************************************************************************/
7326 : /* OSRSetOrthographic() */
7327 : /************************************************************************/
7328 :
7329 1 : OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7330 : double dfCenterLong, double dfFalseEasting,
7331 : double dfFalseNorthing)
7332 :
7333 : {
7334 1 : VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7335 :
7336 1 : return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7337 1 : dfFalseEasting, dfFalseNorthing);
7338 : }
7339 :
7340 : /************************************************************************/
7341 : /* SetPolyconic() */
7342 : /************************************************************************/
7343 :
7344 7 : OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7345 : double dfCenterLong,
7346 : double dfFalseEasting,
7347 : double dfFalseNorthing)
7348 :
7349 : {
7350 : // note: it seems that by some definitions this should include a
7351 : // scale_factor parameter.
7352 7 : return d->replaceConversionAndUnref(
7353 : proj_create_conversion_american_polyconic(
7354 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7355 7 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7356 : }
7357 :
7358 : /************************************************************************/
7359 : /* OSRSetPolyconic() */
7360 : /************************************************************************/
7361 :
7362 0 : OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7363 : double dfCenterLong, double dfFalseEasting,
7364 : double dfFalseNorthing)
7365 :
7366 : {
7367 0 : VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7368 :
7369 0 : return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7370 0 : dfFalseEasting, dfFalseNorthing);
7371 : }
7372 :
7373 : /************************************************************************/
7374 : /* SetPS() */
7375 : /************************************************************************/
7376 :
7377 : /** Sets a Polar Stereographic projection.
7378 : *
7379 : * Two variants are possible:
7380 : * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7381 : * interpreted as the latitude of origin, combined with the scale factor
7382 : * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7383 : * is interpreted as the latitude of true scale. In that situation, dfScale
7384 : * must be set to 1 (it is ignored in the projection parameters)
7385 : */
7386 30 : OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7387 : double dfScale, double dfFalseEasting,
7388 : double dfFalseNorthing)
7389 :
7390 : {
7391 : PJ *conv;
7392 30 : if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7393 : {
7394 20 : conv = proj_create_conversion_polar_stereographic_variant_b(
7395 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7396 : dfFalseNorthing, nullptr, 0, nullptr, 0);
7397 : }
7398 : else
7399 : {
7400 10 : conv = proj_create_conversion_polar_stereographic_variant_a(
7401 : d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7402 : dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7403 : }
7404 :
7405 30 : const char *pszName = nullptr;
7406 30 : double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7407 30 : CPLString osName = pszName ? pszName : "";
7408 :
7409 30 : d->refreshProjObj();
7410 :
7411 30 : d->demoteFromBoundCRS();
7412 :
7413 30 : auto cs = proj_create_cartesian_2D_cs(
7414 : d->getPROJContext(),
7415 : dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7416 : : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7417 30 : !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7418 : auto projCRS =
7419 30 : proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7420 30 : d->getGeodBaseCRS(), conv, cs);
7421 30 : proj_destroy(conv);
7422 30 : proj_destroy(cs);
7423 :
7424 30 : d->setPjCRS(projCRS);
7425 :
7426 30 : d->undoDemoteFromBoundCRS();
7427 :
7428 60 : return OGRERR_NONE;
7429 : }
7430 :
7431 : /************************************************************************/
7432 : /* OSRSetPS() */
7433 : /************************************************************************/
7434 :
7435 1 : OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7436 : double dfCenterLong, double dfScale, double dfFalseEasting,
7437 : double dfFalseNorthing)
7438 :
7439 : {
7440 1 : VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7441 :
7442 1 : return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7443 1 : dfFalseEasting, dfFalseNorthing);
7444 : }
7445 :
7446 : /************************************************************************/
7447 : /* SetRobinson() */
7448 : /************************************************************************/
7449 :
7450 4 : OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7451 : double dfFalseEasting,
7452 : double dfFalseNorthing)
7453 :
7454 : {
7455 4 : return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7456 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7457 4 : nullptr, 0, nullptr, 0));
7458 : }
7459 :
7460 : /************************************************************************/
7461 : /* OSRSetRobinson() */
7462 : /************************************************************************/
7463 :
7464 0 : OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7465 : double dfFalseEasting, double dfFalseNorthing)
7466 :
7467 : {
7468 0 : VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7469 :
7470 0 : return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7471 0 : dfFalseNorthing);
7472 : }
7473 :
7474 : /************************************************************************/
7475 : /* SetSinusoidal() */
7476 : /************************************************************************/
7477 :
7478 33 : OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7479 : double dfFalseEasting,
7480 : double dfFalseNorthing)
7481 :
7482 : {
7483 33 : return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7484 : d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7485 33 : nullptr, 0, nullptr, 0));
7486 : }
7487 :
7488 : /************************************************************************/
7489 : /* OSRSetSinusoidal() */
7490 : /************************************************************************/
7491 :
7492 1 : OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7493 : double dfFalseEasting, double dfFalseNorthing)
7494 :
7495 : {
7496 1 : VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7497 :
7498 1 : return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7499 1 : dfFalseNorthing);
7500 : }
7501 :
7502 : /************************************************************************/
7503 : /* SetStereographic() */
7504 : /************************************************************************/
7505 :
7506 2 : OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7507 : double dfCMeridian, double dfScale,
7508 : double dfFalseEasting,
7509 : double dfFalseNorthing)
7510 :
7511 : {
7512 2 : return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7513 : d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7514 2 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7515 : }
7516 :
7517 : /************************************************************************/
7518 : /* OSRSetStereographic() */
7519 : /************************************************************************/
7520 :
7521 0 : OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7522 : double dfCMeridian, double dfScale,
7523 : double dfFalseEasting, double dfFalseNorthing)
7524 :
7525 : {
7526 0 : VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7527 :
7528 0 : return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7529 0 : dfFalseEasting, dfFalseNorthing);
7530 : }
7531 :
7532 : /************************************************************************/
7533 : /* SetSOC() */
7534 : /* */
7535 : /* NOTE: This definition isn't really used in practice any more */
7536 : /* and should be considered deprecated. It seems that swiss */
7537 : /* oblique mercator is now define as Hotine_Oblique_Mercator */
7538 : /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7539 : /* EPSG:2056 and Bug 423. */
7540 : /************************************************************************/
7541 :
7542 2 : OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7543 : double dfCentralMeridian,
7544 : double dfFalseEasting,
7545 : double dfFalseNorthing)
7546 :
7547 : {
7548 2 : return d->replaceConversionAndUnref(
7549 : proj_create_conversion_hotine_oblique_mercator_variant_b(
7550 : d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7551 : 90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7552 2 : 0.0));
7553 : #if 0
7554 : SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7555 : SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7556 : SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7557 : SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7558 : SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7559 :
7560 : return OGRERR_NONE;
7561 : #endif
7562 : }
7563 :
7564 : /************************************************************************/
7565 : /* OSRSetSOC() */
7566 : /************************************************************************/
7567 :
7568 0 : OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7569 : double dfCentralMeridian, double dfFalseEasting,
7570 : double dfFalseNorthing)
7571 :
7572 : {
7573 0 : VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7574 :
7575 0 : return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7576 0 : dfFalseEasting, dfFalseNorthing);
7577 : }
7578 :
7579 : /************************************************************************/
7580 : /* SetVDG() */
7581 : /************************************************************************/
7582 :
7583 2 : OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7584 : double dfFalseNorthing)
7585 :
7586 : {
7587 2 : return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7588 : d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7589 2 : nullptr, 0, nullptr, 0));
7590 : }
7591 :
7592 : /************************************************************************/
7593 : /* OSRSetVDG() */
7594 : /************************************************************************/
7595 :
7596 0 : OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7597 : double dfFalseEasting, double dfFalseNorthing)
7598 :
7599 : {
7600 0 : VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7601 :
7602 0 : return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7603 0 : dfFalseNorthing);
7604 : }
7605 :
7606 : /************************************************************************/
7607 : /* SetUTM() */
7608 : /************************************************************************/
7609 :
7610 : /**
7611 : * \brief Set UTM projection definition.
7612 : *
7613 : * This will generate a projection definition with the full set of
7614 : * transverse mercator projection parameters for the given UTM zone.
7615 : * If no PROJCS[] description is set yet, one will be set to look
7616 : * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7617 : *
7618 : * This method is the same as the C function OSRSetUTM().
7619 : *
7620 : * @param nZone UTM zone.
7621 : *
7622 : * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7623 : * hemisphere.
7624 : *
7625 : * @return OGRERR_NONE on success.
7626 : */
7627 :
7628 331 : OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7629 :
7630 : {
7631 331 : if (nZone < 0 || nZone > 60)
7632 : {
7633 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7634 0 : return OGRERR_FAILURE;
7635 : }
7636 :
7637 331 : return d->replaceConversionAndUnref(
7638 331 : proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7639 : }
7640 :
7641 : /************************************************************************/
7642 : /* OSRSetUTM() */
7643 : /************************************************************************/
7644 :
7645 : /**
7646 : * \brief Set UTM projection definition.
7647 : *
7648 : * This is the same as the C++ method OGRSpatialReference::SetUTM()
7649 : */
7650 20 : OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
7651 :
7652 : {
7653 20 : VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
7654 :
7655 20 : return ToPointer(hSRS)->SetUTM(nZone, bNorth);
7656 : }
7657 :
7658 : /************************************************************************/
7659 : /* GetUTMZone() */
7660 : /* */
7661 : /* Returns zero if it isn't UTM. */
7662 : /************************************************************************/
7663 :
7664 : /**
7665 : * \brief Get utm zone information.
7666 : *
7667 : * This is the same as the C function OSRGetUTMZone().
7668 : *
7669 : * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
7670 : * zone which is negative in the southern hemisphere instead of having the
7671 : * pbNorth flag used in the C and C++ interface.
7672 : *
7673 : * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
7674 : * FALSE if southern.
7675 : *
7676 : * @return UTM zone number or zero if this isn't a UTM definition.
7677 : */
7678 :
7679 612 : int OGRSpatialReference::GetUTMZone(int *pbNorth) const
7680 :
7681 : {
7682 612 : if (IsProjected() && GetAxesCount() == 3)
7683 : {
7684 1 : OGRSpatialReference *poSRSTmp = Clone();
7685 1 : poSRSTmp->DemoteTo2D(nullptr);
7686 1 : const int nZone = poSRSTmp->GetUTMZone(pbNorth);
7687 1 : delete poSRSTmp;
7688 1 : return nZone;
7689 : }
7690 :
7691 611 : const char *pszProjection = GetAttrValue("PROJECTION");
7692 :
7693 611 : if (pszProjection == nullptr ||
7694 507 : !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
7695 296 : return 0;
7696 :
7697 315 : if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
7698 5 : return 0;
7699 :
7700 310 : if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
7701 9 : return 0;
7702 :
7703 301 : if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
7704 3 : return 0;
7705 :
7706 298 : const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
7707 :
7708 298 : if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
7709 0 : return 0;
7710 :
7711 298 : if (pbNorth != nullptr)
7712 237 : *pbNorth = (dfFalseNorthing == 0);
7713 :
7714 : const double dfCentralMeridian =
7715 298 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
7716 298 : const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
7717 :
7718 298 : if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
7719 894 : CPLIsNan(dfZone) ||
7720 298 : std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
7721 0 : return 0;
7722 :
7723 298 : return static_cast<int>(dfZone);
7724 : }
7725 :
7726 : /************************************************************************/
7727 : /* OSRGetUTMZone() */
7728 : /************************************************************************/
7729 :
7730 : /**
7731 : * \brief Get utm zone information.
7732 : *
7733 : * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
7734 : */
7735 6 : int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
7736 :
7737 : {
7738 6 : VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
7739 :
7740 6 : return ToPointer(hSRS)->GetUTMZone(pbNorth);
7741 : }
7742 :
7743 : /************************************************************************/
7744 : /* SetWagner() */
7745 : /************************************************************************/
7746 :
7747 0 : OGRErr OGRSpatialReference::SetWagner(int nVariation, // 1--7.
7748 : double dfCenterLat, double dfFalseEasting,
7749 : double dfFalseNorthing)
7750 :
7751 : {
7752 : PJ *conv;
7753 0 : if (nVariation == 1)
7754 : {
7755 0 : conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
7756 : dfFalseEasting, dfFalseNorthing,
7757 : nullptr, 0.0, nullptr, 0.0);
7758 : }
7759 0 : else if (nVariation == 2)
7760 : {
7761 0 : conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
7762 : dfFalseEasting, dfFalseNorthing,
7763 : nullptr, 0.0, nullptr, 0.0);
7764 : }
7765 0 : else if (nVariation == 3)
7766 : {
7767 0 : conv = proj_create_conversion_wagner_iii(
7768 : d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
7769 : dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7770 : }
7771 0 : else if (nVariation == 4)
7772 : {
7773 0 : conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
7774 : dfFalseEasting, dfFalseNorthing,
7775 : nullptr, 0.0, nullptr, 0.0);
7776 : }
7777 0 : else if (nVariation == 5)
7778 : {
7779 0 : conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
7780 : dfFalseEasting, dfFalseNorthing,
7781 : nullptr, 0.0, nullptr, 0.0);
7782 : }
7783 0 : else if (nVariation == 6)
7784 : {
7785 0 : conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
7786 : dfFalseEasting, dfFalseNorthing,
7787 : nullptr, 0.0, nullptr, 0.0);
7788 : }
7789 0 : else if (nVariation == 7)
7790 : {
7791 0 : conv = proj_create_conversion_wagner_vii(
7792 : d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
7793 : 0.0, nullptr, 0.0);
7794 : }
7795 : else
7796 : {
7797 0 : CPLError(CE_Failure, CPLE_AppDefined,
7798 : "Unsupported Wagner variation (%d).", nVariation);
7799 0 : return OGRERR_UNSUPPORTED_SRS;
7800 : }
7801 :
7802 0 : return d->replaceConversionAndUnref(conv);
7803 : }
7804 :
7805 : /************************************************************************/
7806 : /* OSRSetWagner() */
7807 : /************************************************************************/
7808 :
7809 0 : OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
7810 : double dfCenterLat, double dfFalseEasting,
7811 : double dfFalseNorthing)
7812 :
7813 : {
7814 0 : VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
7815 :
7816 0 : return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
7817 0 : dfFalseNorthing);
7818 : }
7819 :
7820 : /************************************************************************/
7821 : /* SetQSC() */
7822 : /************************************************************************/
7823 :
7824 0 : OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
7825 : {
7826 0 : return d->replaceConversionAndUnref(
7827 : proj_create_conversion_quadrilateralized_spherical_cube(
7828 : d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
7829 0 : 0, nullptr, 0));
7830 : }
7831 :
7832 : /************************************************************************/
7833 : /* OSRSetQSC() */
7834 : /************************************************************************/
7835 :
7836 0 : OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7837 : double dfCenterLong)
7838 :
7839 : {
7840 0 : VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
7841 :
7842 0 : return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
7843 : }
7844 :
7845 : /************************************************************************/
7846 : /* SetSCH() */
7847 : /************************************************************************/
7848 :
7849 0 : OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
7850 : double dfPegHeading, double dfPegHgt)
7851 :
7852 : {
7853 0 : return d->replaceConversionAndUnref(
7854 : proj_create_conversion_spherical_cross_track_height(
7855 : d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
7856 0 : nullptr, 0, nullptr, 0));
7857 : }
7858 :
7859 : /************************************************************************/
7860 : /* OSRSetSCH() */
7861 : /************************************************************************/
7862 :
7863 0 : OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
7864 : double dfPegHeading, double dfPegHgt)
7865 :
7866 : {
7867 0 : VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
7868 :
7869 0 : return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
7870 : }
7871 :
7872 : /************************************************************************/
7873 : /* SetVerticalPerspective() */
7874 : /************************************************************************/
7875 :
7876 3 : OGRErr OGRSpatialReference::SetVerticalPerspective(
7877 : double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
7878 : double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
7879 : {
7880 3 : return d->replaceConversionAndUnref(
7881 : proj_create_conversion_vertical_perspective(
7882 : d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
7883 : dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
7884 3 : dfFalseNorthing, nullptr, 0, nullptr, 0));
7885 : }
7886 :
7887 : /************************************************************************/
7888 : /* OSRSetVerticalPerspective() */
7889 : /************************************************************************/
7890 :
7891 1 : OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
7892 : double dfTopoOriginLat, double dfTopoOriginLon,
7893 : double dfTopoOriginHeight,
7894 : double dfViewPointHeight,
7895 : double dfFalseEasting, double dfFalseNorthing)
7896 :
7897 : {
7898 1 : VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
7899 :
7900 1 : return ToPointer(hSRS)->SetVerticalPerspective(
7901 : dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
7902 1 : dfFalseEasting, dfFalseNorthing);
7903 : }
7904 :
7905 : /************************************************************************/
7906 : /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
7907 : /************************************************************************/
7908 :
7909 2 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
7910 : const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
7911 : double dfAxisRotation)
7912 : {
7913 2 : d->refreshProjObj();
7914 2 : if (!d->m_pj_crs)
7915 0 : return OGRERR_FAILURE;
7916 2 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
7917 0 : return OGRERR_FAILURE;
7918 2 : auto ctxt = d->getPROJContext();
7919 2 : auto conv = proj_create_conversion_pole_rotation_grib_convention(
7920 : ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
7921 2 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
7922 4 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
7923 2 : d->m_pj_crs, conv, cs));
7924 2 : proj_destroy(conv);
7925 2 : proj_destroy(cs);
7926 2 : return OGRERR_NONE;
7927 : }
7928 :
7929 : /************************************************************************/
7930 : /* SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention() */
7931 : /************************************************************************/
7932 :
7933 3 : OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
7934 : const char *pszCRSName, double dfGridNorthPoleLat,
7935 : double dfGridNorthPoleLon, double dfNorthPoleGridLon)
7936 : {
7937 : #if PROJ_VERSION_MAJOR > 8 || \
7938 : (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
7939 : d->refreshProjObj();
7940 : if (!d->m_pj_crs)
7941 : return OGRERR_FAILURE;
7942 : if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
7943 : return OGRERR_FAILURE;
7944 : auto ctxt = d->getPROJContext();
7945 : auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
7946 : ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
7947 : nullptr, 0);
7948 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
7949 : d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
7950 : d->m_pj_crs, conv, cs));
7951 : proj_destroy(conv);
7952 : proj_destroy(cs);
7953 : return OGRERR_NONE;
7954 : #else
7955 : (void)pszCRSName;
7956 3 : SetProjection("Rotated_pole");
7957 3 : SetExtension(
7958 : "PROJCS", "PROJ4",
7959 : CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.18g +o_lon_p=%.18g "
7960 : "+o_lat_p=%.18g +a=%.18g +b=%.18g "
7961 : "+to_meter=0.0174532925199433 "
7962 : "+wktext",
7963 : 180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
7964 : dfGridNorthPoleLat, GetSemiMajor(nullptr),
7965 : GetSemiMinor(nullptr)));
7966 3 : return OGRERR_NONE;
7967 : #endif
7968 : }
7969 :
7970 : /************************************************************************/
7971 : /* SetAuthority() */
7972 : /************************************************************************/
7973 :
7974 : /**
7975 : * \brief Set the authority for a node.
7976 : *
7977 : * This method is the same as the C function OSRSetAuthority().
7978 : *
7979 : * @param pszTargetKey the partial or complete path to the node to
7980 : * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
7981 : *
7982 : * @param pszAuthority authority name, such as "EPSG".
7983 : *
7984 : * @param nCode code for value with this authority.
7985 : *
7986 : * @return OGRERR_NONE on success.
7987 : */
7988 :
7989 9393 : OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
7990 : const char *pszAuthority, int nCode)
7991 :
7992 : {
7993 9393 : d->refreshProjObj();
7994 9393 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
7995 :
7996 9393 : if (pszTargetKey == nullptr)
7997 : {
7998 224 : if (!d->m_pj_crs)
7999 0 : return OGRERR_FAILURE;
8000 224 : CPLString osCode;
8001 224 : osCode.Printf("%d", nCode);
8002 224 : d->demoteFromBoundCRS();
8003 224 : d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8004 : pszAuthority, osCode.c_str()));
8005 224 : d->undoDemoteFromBoundCRS();
8006 224 : return OGRERR_NONE;
8007 : }
8008 :
8009 9169 : d->demoteFromBoundCRS();
8010 9169 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8011 : {
8012 3036 : CPLString osCode;
8013 3036 : osCode.Printf("%d", nCode);
8014 : auto newGeogCRS =
8015 3036 : proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8016 : pszAuthority, osCode.c_str());
8017 :
8018 : auto conv =
8019 3036 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8020 :
8021 3036 : auto projCRS = proj_create_projected_crs(
8022 : d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8023 3036 : d->getProjCRSCoordSys());
8024 :
8025 : // Preserve existing id on the PROJCRS
8026 3036 : const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8027 3036 : const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8028 3036 : if (pszProjCRSAuthName && pszProjCRSCode)
8029 : {
8030 : auto projCRSWithId =
8031 0 : proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8032 : pszProjCRSCode);
8033 0 : proj_destroy(projCRS);
8034 0 : projCRS = projCRSWithId;
8035 : }
8036 :
8037 3036 : proj_destroy(newGeogCRS);
8038 3036 : proj_destroy(conv);
8039 :
8040 3036 : d->setPjCRS(projCRS);
8041 3036 : d->undoDemoteFromBoundCRS();
8042 3036 : return OGRERR_NONE;
8043 : }
8044 6133 : d->undoDemoteFromBoundCRS();
8045 :
8046 : /* -------------------------------------------------------------------- */
8047 : /* Find the node below which the authority should be put. */
8048 : /* -------------------------------------------------------------------- */
8049 6133 : OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8050 :
8051 6133 : if (poNode == nullptr)
8052 0 : return OGRERR_FAILURE;
8053 :
8054 : /* -------------------------------------------------------------------- */
8055 : /* If there is an existing AUTHORITY child blow it away before */
8056 : /* trying to set a new one. */
8057 : /* -------------------------------------------------------------------- */
8058 6133 : int iOldChild = poNode->FindChild("AUTHORITY");
8059 6133 : if (iOldChild != -1)
8060 5 : poNode->DestroyChild(iOldChild);
8061 :
8062 : /* -------------------------------------------------------------------- */
8063 : /* Create a new authority node. */
8064 : /* -------------------------------------------------------------------- */
8065 6133 : char szCode[32] = {};
8066 :
8067 6133 : snprintf(szCode, sizeof(szCode), "%d", nCode);
8068 :
8069 6133 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8070 6133 : poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8071 6133 : poAuthNode->AddChild(new OGR_SRSNode(szCode));
8072 :
8073 6133 : poNode->AddChild(poAuthNode);
8074 :
8075 6133 : return OGRERR_NONE;
8076 : }
8077 :
8078 : /************************************************************************/
8079 : /* OSRSetAuthority() */
8080 : /************************************************************************/
8081 :
8082 : /**
8083 : * \brief Set the authority for a node.
8084 : *
8085 : * This function is the same as OGRSpatialReference::SetAuthority().
8086 : */
8087 0 : OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8088 : const char *pszAuthority, int nCode)
8089 :
8090 : {
8091 0 : VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8092 :
8093 0 : return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8094 : }
8095 :
8096 : /************************************************************************/
8097 : /* GetAuthorityCode() */
8098 : /************************************************************************/
8099 :
8100 : /**
8101 : * \brief Get the authority code for a node.
8102 : *
8103 : * This method is used to query an AUTHORITY[] node from within the
8104 : * WKT tree, and fetch the code value.
8105 : *
8106 : * While in theory values may be non-numeric, for the EPSG authority all
8107 : * code values should be integral.
8108 : *
8109 : * This method is the same as the C function OSRGetAuthorityCode().
8110 : *
8111 : * @param pszTargetKey the partial or complete path to the node to
8112 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8113 : * search for an authority node on the root element.
8114 : *
8115 : * @return value code from authority node, or NULL on failure. The value
8116 : * returned is internal and should not be freed or modified.
8117 : */
8118 :
8119 : const char *
8120 20674 : OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8121 :
8122 : {
8123 20674 : d->refreshProjObj();
8124 20674 : const char *pszInputTargetKey = pszTargetKey;
8125 20674 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8126 20674 : if (pszTargetKey == nullptr)
8127 : {
8128 15463 : if (!d->m_pj_crs)
8129 : {
8130 13 : return nullptr;
8131 : }
8132 15450 : d->demoteFromBoundCRS();
8133 15450 : auto ret = proj_get_id_code(d->m_pj_crs, 0);
8134 15450 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8135 : {
8136 1103 : auto ctxt = d->getPROJContext();
8137 1103 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8138 1103 : if (cs)
8139 : {
8140 1103 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8141 1103 : proj_destroy(cs);
8142 1103 : if (axisCount == 3)
8143 : {
8144 : // This might come from a COMPD_CS with a VERT_DATUM type =
8145 : // 2002 in which case, using the WKT1 representation will
8146 : // enable us to recover the EPSG code.
8147 14 : pszTargetKey = pszInputTargetKey;
8148 : }
8149 : }
8150 : }
8151 15450 : d->undoDemoteFromBoundCRS();
8152 15450 : if (ret != nullptr || pszTargetKey == nullptr)
8153 : {
8154 15450 : return ret;
8155 : }
8156 : }
8157 :
8158 : // Special key for that context
8159 5215 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8160 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8161 : {
8162 4 : auto ctxt = d->getPROJContext();
8163 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8164 4 : if (crs)
8165 : {
8166 4 : const char *ret = proj_get_id_code(crs, 0);
8167 4 : if (ret)
8168 4 : ret = CPLSPrintf("%s", ret);
8169 4 : proj_destroy(crs);
8170 4 : return ret;
8171 : }
8172 : }
8173 5211 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8174 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8175 : {
8176 4 : auto ctxt = d->getPROJContext();
8177 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8178 4 : if (crs)
8179 : {
8180 4 : const char *ret = proj_get_id_code(crs, 0);
8181 4 : if (ret)
8182 4 : ret = CPLSPrintf("%s", ret);
8183 4 : proj_destroy(crs);
8184 4 : return ret;
8185 : }
8186 : }
8187 :
8188 : /* -------------------------------------------------------------------- */
8189 : /* Find the node below which the authority should be put. */
8190 : /* -------------------------------------------------------------------- */
8191 5203 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8192 :
8193 5203 : if (poNode == nullptr)
8194 84 : return nullptr;
8195 :
8196 : /* -------------------------------------------------------------------- */
8197 : /* Fetch AUTHORITY child if there is one. */
8198 : /* -------------------------------------------------------------------- */
8199 5119 : if (poNode->FindChild("AUTHORITY") == -1)
8200 182 : return nullptr;
8201 :
8202 4937 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8203 :
8204 : /* -------------------------------------------------------------------- */
8205 : /* Create a new authority node. */
8206 : /* -------------------------------------------------------------------- */
8207 4937 : if (poNode->GetChildCount() < 2)
8208 0 : return nullptr;
8209 :
8210 4937 : return poNode->GetChild(1)->GetValue();
8211 : }
8212 :
8213 : /************************************************************************/
8214 : /* OSRGetAuthorityCode() */
8215 : /************************************************************************/
8216 :
8217 : /**
8218 : * \brief Get the authority code for a node.
8219 : *
8220 : * This function is the same as OGRSpatialReference::GetAuthorityCode().
8221 : */
8222 643 : const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8223 : const char *pszTargetKey)
8224 :
8225 : {
8226 643 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8227 :
8228 643 : return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8229 : }
8230 :
8231 : /************************************************************************/
8232 : /* GetAuthorityName() */
8233 : /************************************************************************/
8234 :
8235 : /**
8236 : * \brief Get the authority name for a node.
8237 : *
8238 : * This method is used to query an AUTHORITY[] node from within the
8239 : * WKT tree, and fetch the authority name value.
8240 : *
8241 : * The most common authority is "EPSG".
8242 : *
8243 : * This method is the same as the C function OSRGetAuthorityName().
8244 : *
8245 : * @param pszTargetKey the partial or complete path to the node to
8246 : * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8247 : * search for an authority node on the root element.
8248 : *
8249 : * @return value code from authority node, or NULL on failure. The value
8250 : * returned is internal and should not be freed or modified.
8251 : */
8252 :
8253 : const char *
8254 33958 : OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8255 :
8256 : {
8257 33958 : d->refreshProjObj();
8258 33958 : const char *pszInputTargetKey = pszTargetKey;
8259 33958 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8260 33958 : if (pszTargetKey == nullptr)
8261 : {
8262 16097 : if (!d->m_pj_crs)
8263 : {
8264 14 : return nullptr;
8265 : }
8266 16083 : d->demoteFromBoundCRS();
8267 16083 : auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8268 16083 : if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8269 : {
8270 793 : auto ctxt = d->getPROJContext();
8271 793 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8272 793 : if (cs)
8273 : {
8274 793 : const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8275 793 : proj_destroy(cs);
8276 793 : if (axisCount == 3)
8277 : {
8278 : // This might come from a COMPD_CS with a VERT_DATUM type =
8279 : // 2002 in which case, using the WKT1 representation will
8280 : // enable us to recover the EPSG code.
8281 14 : pszTargetKey = pszInputTargetKey;
8282 : }
8283 : }
8284 : }
8285 16083 : d->undoDemoteFromBoundCRS();
8286 16083 : if (ret != nullptr || pszTargetKey == nullptr)
8287 : {
8288 16083 : return ret;
8289 : }
8290 : }
8291 :
8292 : // Special key for that context
8293 17865 : else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8294 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8295 : {
8296 4 : auto ctxt = d->getPROJContext();
8297 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8298 4 : if (crs)
8299 : {
8300 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8301 4 : if (ret)
8302 4 : ret = CPLSPrintf("%s", ret);
8303 4 : proj_destroy(crs);
8304 4 : return ret;
8305 : }
8306 : }
8307 17861 : else if (EQUAL(pszTargetKey, "VERTCRS") &&
8308 4 : d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8309 : {
8310 4 : auto ctxt = d->getPROJContext();
8311 4 : auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8312 4 : if (crs)
8313 : {
8314 4 : const char *ret = proj_get_id_auth_name(crs, 0);
8315 4 : if (ret)
8316 4 : ret = CPLSPrintf("%s", ret);
8317 4 : proj_destroy(crs);
8318 4 : return ret;
8319 : }
8320 : }
8321 :
8322 : /* -------------------------------------------------------------------- */
8323 : /* Find the node below which the authority should be put. */
8324 : /* -------------------------------------------------------------------- */
8325 17853 : const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8326 :
8327 17853 : if (poNode == nullptr)
8328 7038 : return nullptr;
8329 :
8330 : /* -------------------------------------------------------------------- */
8331 : /* Fetch AUTHORITY child if there is one. */
8332 : /* -------------------------------------------------------------------- */
8333 10815 : if (poNode->FindChild("AUTHORITY") == -1)
8334 1477 : return nullptr;
8335 :
8336 9338 : poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8337 :
8338 : /* -------------------------------------------------------------------- */
8339 : /* Create a new authority node. */
8340 : /* -------------------------------------------------------------------- */
8341 9338 : if (poNode->GetChildCount() < 2)
8342 0 : return nullptr;
8343 :
8344 9338 : return poNode->GetChild(0)->GetValue();
8345 : }
8346 :
8347 : /************************************************************************/
8348 : /* OSRGetAuthorityName() */
8349 : /************************************************************************/
8350 :
8351 : /**
8352 : * \brief Get the authority name for a node.
8353 : *
8354 : * This function is the same as OGRSpatialReference::GetAuthorityName().
8355 : */
8356 221 : const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8357 : const char *pszTargetKey)
8358 :
8359 : {
8360 221 : VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8361 :
8362 221 : return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8363 : }
8364 :
8365 : /************************************************************************/
8366 : /* GetOGCURN() */
8367 : /************************************************************************/
8368 :
8369 : /**
8370 : * \brief Get a OGC URN string describing the CRS, when possible
8371 : *
8372 : * This method assumes that the CRS has a top-level identifier, or is
8373 : * a compound CRS whose horizontal and vertical parts have a top-level
8374 : * identifier.
8375 : *
8376 : * @return a string to free with CPLFree(), or nulptr when no result can be
8377 : * generated
8378 : *
8379 : * @since GDAL 3.5
8380 : */
8381 :
8382 53 : char *OGRSpatialReference::GetOGCURN() const
8383 :
8384 : {
8385 53 : const char *pszAuthName = GetAuthorityName(nullptr);
8386 53 : const char *pszAuthCode = GetAuthorityCode(nullptr);
8387 53 : if (pszAuthName && pszAuthCode)
8388 50 : return CPLStrdup(
8389 50 : CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8390 3 : if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8391 2 : return nullptr;
8392 1 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8393 1 : auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8394 1 : char *pszRet = nullptr;
8395 1 : if (horizCRS && vertCRS)
8396 : {
8397 1 : auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8398 1 : auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8399 1 : auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8400 1 : auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8401 1 : if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8402 : {
8403 1 : pszRet = CPLStrdup(CPLSPrintf(
8404 : "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8405 : horizAuthCode, vertAuthName, vertAuthCode));
8406 : }
8407 : }
8408 1 : proj_destroy(horizCRS);
8409 1 : proj_destroy(vertCRS);
8410 1 : return pszRet;
8411 : }
8412 :
8413 : /************************************************************************/
8414 : /* StripVertical() */
8415 : /************************************************************************/
8416 :
8417 : /**
8418 : * \brief Convert a compound cs into a horizontal CS.
8419 : *
8420 : * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8421 : * nodes are stripped resulting and only the horizontal coordinate system
8422 : * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8423 : *
8424 : * If this is not a compound coordinate system then nothing is changed.
8425 : *
8426 : * This method is the same as the C function OSRStripVertical().
8427 : *
8428 : * @since OGR 1.8.0
8429 : */
8430 :
8431 41 : OGRErr OGRSpatialReference::StripVertical()
8432 :
8433 : {
8434 41 : d->refreshProjObj();
8435 41 : d->demoteFromBoundCRS();
8436 41 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8437 : {
8438 0 : d->undoDemoteFromBoundCRS();
8439 0 : return OGRERR_NONE;
8440 : }
8441 41 : auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8442 41 : if (!horizCRS)
8443 : {
8444 0 : d->undoDemoteFromBoundCRS();
8445 0 : return OGRERR_FAILURE;
8446 : }
8447 :
8448 41 : bool reuseExistingBoundCRS = false;
8449 41 : if (d->m_pj_bound_crs_target)
8450 : {
8451 2 : auto type = proj_get_type(d->m_pj_bound_crs_target);
8452 4 : reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8453 4 : type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8454 : type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8455 : }
8456 :
8457 41 : if (reuseExistingBoundCRS)
8458 : {
8459 2 : auto newBoundCRS = proj_crs_create_bound_crs(
8460 2 : d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8461 2 : d->m_pj_bound_crs_co);
8462 2 : proj_destroy(horizCRS);
8463 2 : d->undoDemoteFromBoundCRS();
8464 2 : d->setPjCRS(newBoundCRS);
8465 : }
8466 : else
8467 : {
8468 39 : d->undoDemoteFromBoundCRS();
8469 39 : d->setPjCRS(horizCRS);
8470 : }
8471 :
8472 41 : return OGRERR_NONE;
8473 : }
8474 :
8475 : /************************************************************************/
8476 : /* OSRStripVertical() */
8477 : /************************************************************************/
8478 : /**
8479 : * \brief Convert a compound cs into a horizontal CS.
8480 : *
8481 : * This function is the same as the C++ method
8482 : * OGRSpatialReference::StripVertical().
8483 : */
8484 1 : OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8485 :
8486 : {
8487 1 : VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8488 :
8489 1 : return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8490 : }
8491 :
8492 : /************************************************************************/
8493 : /* StripTOWGS84IfKnownDatumAndAllowed() */
8494 : /************************************************************************/
8495 :
8496 : /**
8497 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8498 : * and this is allowed by the user.
8499 : *
8500 : * The default behavior is to remove TOWGS84 information if the CRS has a
8501 : * known horizontal datum. This can be disabled by setting the
8502 : * OSR_STRIP_TOWGS84 configuration option to NO.
8503 : *
8504 : * @return true if TOWGS84 has been removed.
8505 : * @since OGR 3.1.0
8506 : */
8507 :
8508 5744 : bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8509 : {
8510 5744 : if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8511 : {
8512 5741 : if (StripTOWGS84IfKnownDatum())
8513 : {
8514 6 : CPLDebug("OSR", "TOWGS84 information has been removed. "
8515 : "It can be kept by setting the OSR_STRIP_TOWGS84 "
8516 : "configuration option to NO");
8517 6 : return true;
8518 : }
8519 : }
8520 5738 : return false;
8521 : }
8522 :
8523 : /************************************************************************/
8524 : /* StripTOWGS84IfKnownDatum() */
8525 : /************************************************************************/
8526 :
8527 : /**
8528 : * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8529 : *
8530 : * @return true if TOWGS84 has been removed.
8531 : * @since OGR 3.1.0
8532 : */
8533 :
8534 5747 : bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8535 :
8536 : {
8537 5747 : d->refreshProjObj();
8538 5747 : if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8539 : {
8540 5726 : return false;
8541 : }
8542 21 : auto ctxt = d->getPROJContext();
8543 21 : auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8544 21 : if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8545 : {
8546 1 : proj_destroy(baseCRS);
8547 1 : return false;
8548 : }
8549 :
8550 : // Known base CRS code ? Return base CRS
8551 20 : const char *pszCode = proj_get_id_code(baseCRS, 0);
8552 20 : if (pszCode)
8553 : {
8554 2 : d->setPjCRS(baseCRS);
8555 2 : return true;
8556 : }
8557 :
8558 18 : auto datum = proj_crs_get_datum(ctxt, baseCRS);
8559 : #if PROJ_VERSION_MAJOR > 7 || \
8560 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8561 : if (datum == nullptr)
8562 : {
8563 : datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8564 : }
8565 : #endif
8566 18 : if (!datum)
8567 : {
8568 0 : proj_destroy(baseCRS);
8569 0 : return false;
8570 : }
8571 :
8572 : // Known datum code ? Return base CRS
8573 18 : pszCode = proj_get_id_code(datum, 0);
8574 18 : if (pszCode)
8575 : {
8576 3 : proj_destroy(datum);
8577 3 : d->setPjCRS(baseCRS);
8578 3 : return true;
8579 : }
8580 :
8581 15 : const char *name = proj_get_name(datum);
8582 15 : if (EQUAL(name, "unknown"))
8583 : {
8584 1 : proj_destroy(datum);
8585 1 : proj_destroy(baseCRS);
8586 1 : return false;
8587 : }
8588 14 : const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8589 : PJ_OBJ_LIST *list =
8590 14 : proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8591 :
8592 14 : bool knownDatumName = false;
8593 14 : if (list)
8594 : {
8595 14 : if (proj_list_get_count(list) == 1)
8596 : {
8597 4 : knownDatumName = true;
8598 : }
8599 14 : proj_list_destroy(list);
8600 : }
8601 :
8602 14 : proj_destroy(datum);
8603 14 : if (knownDatumName)
8604 : {
8605 4 : d->setPjCRS(baseCRS);
8606 4 : return true;
8607 : }
8608 10 : proj_destroy(baseCRS);
8609 10 : return false;
8610 : }
8611 :
8612 : /************************************************************************/
8613 : /* IsCompound() */
8614 : /************************************************************************/
8615 :
8616 : /**
8617 : * \brief Check if coordinate system is compound.
8618 : *
8619 : * This method is the same as the C function OSRIsCompound().
8620 : *
8621 : * @return TRUE if this is rooted with a COMPD_CS node.
8622 : */
8623 :
8624 29443 : int OGRSpatialReference::IsCompound() const
8625 :
8626 : {
8627 29443 : d->refreshProjObj();
8628 29443 : d->demoteFromBoundCRS();
8629 29443 : bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
8630 29443 : d->undoDemoteFromBoundCRS();
8631 29443 : return isCompound;
8632 : }
8633 :
8634 : /************************************************************************/
8635 : /* OSRIsCompound() */
8636 : /************************************************************************/
8637 :
8638 : /**
8639 : * \brief Check if the coordinate system is compound.
8640 : *
8641 : * This function is the same as OGRSpatialReference::IsCompound().
8642 : */
8643 5 : int OSRIsCompound(OGRSpatialReferenceH hSRS)
8644 :
8645 : {
8646 5 : VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
8647 :
8648 5 : return ToPointer(hSRS)->IsCompound();
8649 : }
8650 :
8651 : /************************************************************************/
8652 : /* IsProjected() */
8653 : /************************************************************************/
8654 :
8655 : /**
8656 : * \brief Check if projected coordinate system.
8657 : *
8658 : * This method is the same as the C function OSRIsProjected().
8659 : *
8660 : * @return TRUE if this contains a PROJCS node indicating a it is a
8661 : * projected coordinate system. Also if it is a CompoundCRS made of a
8662 : * ProjectedCRS
8663 : */
8664 :
8665 40772 : int OGRSpatialReference::IsProjected() const
8666 :
8667 : {
8668 40772 : d->refreshProjObj();
8669 40772 : d->demoteFromBoundCRS();
8670 40772 : bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
8671 40772 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8672 : {
8673 : auto horizCRS =
8674 141 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8675 141 : if (horizCRS)
8676 : {
8677 141 : auto horizCRSType = proj_get_type(horizCRS);
8678 141 : isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
8679 141 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
8680 : {
8681 6 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
8682 6 : if (base)
8683 : {
8684 6 : isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
8685 6 : proj_destroy(base);
8686 : }
8687 : }
8688 141 : proj_destroy(horizCRS);
8689 : }
8690 : }
8691 40772 : d->undoDemoteFromBoundCRS();
8692 40772 : return isProjected;
8693 : }
8694 :
8695 : /************************************************************************/
8696 : /* OSRIsProjected() */
8697 : /************************************************************************/
8698 : /**
8699 : * \brief Check if projected coordinate system.
8700 : *
8701 : * This function is the same as OGRSpatialReference::IsProjected().
8702 : */
8703 190 : int OSRIsProjected(OGRSpatialReferenceH hSRS)
8704 :
8705 : {
8706 190 : VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
8707 :
8708 190 : return ToPointer(hSRS)->IsProjected();
8709 : }
8710 :
8711 : /************************************************************************/
8712 : /* IsGeocentric() */
8713 : /************************************************************************/
8714 :
8715 : /**
8716 : * \brief Check if geocentric coordinate system.
8717 : *
8718 : * This method is the same as the C function OSRIsGeocentric().
8719 : *
8720 : * @return TRUE if this contains a GEOCCS node indicating a it is a
8721 : * geocentric coordinate system.
8722 : *
8723 : * @since OGR 1.9.0
8724 : */
8725 :
8726 10966 : int OGRSpatialReference::IsGeocentric() const
8727 :
8728 : {
8729 10966 : d->refreshProjObj();
8730 10966 : d->demoteFromBoundCRS();
8731 10966 : bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
8732 10966 : d->undoDemoteFromBoundCRS();
8733 10966 : return isGeocentric;
8734 : }
8735 :
8736 : /************************************************************************/
8737 : /* OSRIsGeocentric() */
8738 : /************************************************************************/
8739 : /**
8740 : * \brief Check if geocentric coordinate system.
8741 : *
8742 : * This function is the same as OGRSpatialReference::IsGeocentric().
8743 : *
8744 : * @since OGR 1.9.0
8745 : */
8746 2 : int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
8747 :
8748 : {
8749 2 : VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
8750 :
8751 2 : return ToPointer(hSRS)->IsGeocentric();
8752 : }
8753 :
8754 : /************************************************************************/
8755 : /* IsEmpty() */
8756 : /************************************************************************/
8757 :
8758 : /**
8759 : * \brief Return if the SRS is not set.
8760 : */
8761 :
8762 46790 : bool OGRSpatialReference::IsEmpty() const
8763 : {
8764 46790 : d->refreshProjObj();
8765 46790 : return d->m_pj_crs == nullptr;
8766 : }
8767 :
8768 : /************************************************************************/
8769 : /* IsGeographic() */
8770 : /************************************************************************/
8771 :
8772 : /**
8773 : * \brief Check if geographic coordinate system.
8774 : *
8775 : * This method is the same as the C function OSRIsGeographic().
8776 : *
8777 : * @return TRUE if this spatial reference is geographic ... that is the
8778 : * root is a GEOGCS node. Also if it is a CompoundCRS made of a
8779 : * GeographicCRS
8780 : */
8781 :
8782 50661 : int OGRSpatialReference::IsGeographic() const
8783 :
8784 : {
8785 50661 : d->refreshProjObj();
8786 50661 : d->demoteFromBoundCRS();
8787 70638 : bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8788 19977 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8789 50661 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8790 : {
8791 : auto horizCRS =
8792 291 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8793 291 : if (horizCRS)
8794 : {
8795 291 : auto horizCRSType = proj_get_type(horizCRS);
8796 291 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8797 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8798 291 : if (horizCRSType == PJ_TYPE_BOUND_CRS)
8799 : {
8800 13 : auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
8801 13 : if (base)
8802 : {
8803 13 : horizCRSType = proj_get_type(base);
8804 13 : isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8805 : horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8806 13 : proj_destroy(base);
8807 : }
8808 : }
8809 291 : proj_destroy(horizCRS);
8810 : }
8811 : }
8812 50661 : d->undoDemoteFromBoundCRS();
8813 50661 : return isGeog;
8814 : }
8815 :
8816 : /************************************************************************/
8817 : /* OSRIsGeographic() */
8818 : /************************************************************************/
8819 : /**
8820 : * \brief Check if geographic coordinate system.
8821 : *
8822 : * This function is the same as OGRSpatialReference::IsGeographic().
8823 : */
8824 134 : int OSRIsGeographic(OGRSpatialReferenceH hSRS)
8825 :
8826 : {
8827 134 : VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
8828 :
8829 134 : return ToPointer(hSRS)->IsGeographic();
8830 : }
8831 :
8832 : /************************************************************************/
8833 : /* IsDerivedGeographic() */
8834 : /************************************************************************/
8835 :
8836 : /**
8837 : * \brief Check if the CRS is a derived geographic coordinate system.
8838 : * (for example a rotated long/lat grid)
8839 : *
8840 : * This method is the same as the C function OSRIsDerivedGeographic().
8841 : *
8842 : * @since GDAL 3.1.0 and PROJ 6.3.0
8843 : */
8844 :
8845 13758 : int OGRSpatialReference::IsDerivedGeographic() const
8846 :
8847 : {
8848 13758 : d->refreshProjObj();
8849 13758 : d->demoteFromBoundCRS();
8850 22586 : const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8851 8828 : d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8852 : const bool isDerivedGeographic =
8853 13758 : isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
8854 13758 : d->undoDemoteFromBoundCRS();
8855 13758 : return isDerivedGeographic ? TRUE : FALSE;
8856 : }
8857 :
8858 : /************************************************************************/
8859 : /* OSRIsDerivedGeographic() */
8860 : /************************************************************************/
8861 : /**
8862 : * \brief Check if the CRS is a derived geographic coordinate system.
8863 : * (for example a rotated long/lat grid)
8864 : *
8865 : * This function is the same as OGRSpatialReference::IsDerivedGeographic().
8866 : */
8867 1 : int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
8868 :
8869 : {
8870 1 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
8871 :
8872 1 : return ToPointer(hSRS)->IsDerivedGeographic();
8873 : }
8874 :
8875 : /************************************************************************/
8876 : /* IsDerivedProjected() */
8877 : /************************************************************************/
8878 :
8879 : /**
8880 : * \brief Check if the CRS is a derived projected coordinate system.
8881 : *
8882 : * This method is the same as the C function OSRIsDerivedGeographic().
8883 : *
8884 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
8885 : */
8886 :
8887 0 : int OGRSpatialReference::IsDerivedProjected() const
8888 :
8889 : {
8890 : #if PROJ_AT_LEAST_VERSION(9, 2, 0)
8891 : d->refreshProjObj();
8892 : d->demoteFromBoundCRS();
8893 : const bool isDerivedProjected =
8894 : d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
8895 : d->undoDemoteFromBoundCRS();
8896 : return isDerivedProjected ? TRUE : FALSE;
8897 : #else
8898 0 : return FALSE;
8899 : #endif
8900 : }
8901 :
8902 : /************************************************************************/
8903 : /* OSRIsDerivedProjected() */
8904 : /************************************************************************/
8905 : /**
8906 : * \brief Check if the CRS is a derived projected coordinate system.
8907 : *
8908 : * This function is the same as OGRSpatialReference::IsDerivedProjected().
8909 : *
8910 : * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
8911 : */
8912 0 : int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
8913 :
8914 : {
8915 0 : VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
8916 :
8917 0 : return ToPointer(hSRS)->IsDerivedProjected();
8918 : }
8919 :
8920 : /************************************************************************/
8921 : /* IsLocal() */
8922 : /************************************************************************/
8923 :
8924 : /**
8925 : * \brief Check if local coordinate system.
8926 : *
8927 : * This method is the same as the C function OSRIsLocal().
8928 : *
8929 : * @return TRUE if this spatial reference is local ... that is the
8930 : * root is a LOCAL_CS node.
8931 : */
8932 :
8933 5577 : int OGRSpatialReference::IsLocal() const
8934 :
8935 : {
8936 5577 : d->refreshProjObj();
8937 5577 : return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
8938 : }
8939 :
8940 : /************************************************************************/
8941 : /* OSRIsLocal() */
8942 : /************************************************************************/
8943 : /**
8944 : * \brief Check if local coordinate system.
8945 : *
8946 : * This function is the same as OGRSpatialReference::IsLocal().
8947 : */
8948 8 : int OSRIsLocal(OGRSpatialReferenceH hSRS)
8949 :
8950 : {
8951 8 : VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
8952 :
8953 8 : return ToPointer(hSRS)->IsLocal();
8954 : }
8955 :
8956 : /************************************************************************/
8957 : /* IsVertical() */
8958 : /************************************************************************/
8959 :
8960 : /**
8961 : * \brief Check if vertical coordinate system.
8962 : *
8963 : * This method is the same as the C function OSRIsVertical().
8964 : *
8965 : * @return TRUE if this contains a VERT_CS node indicating a it is a
8966 : * vertical coordinate system. Also if it is a CompoundCRS made of a
8967 : * VerticalCRS
8968 : *
8969 : * @since OGR 1.8.0
8970 : */
8971 :
8972 4589 : int OGRSpatialReference::IsVertical() const
8973 :
8974 : {
8975 4589 : d->refreshProjObj();
8976 4589 : d->demoteFromBoundCRS();
8977 4589 : bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
8978 4589 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8979 : {
8980 : auto vertCRS =
8981 33 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8982 33 : if (vertCRS)
8983 : {
8984 33 : const auto vertCRSType = proj_get_type(vertCRS);
8985 33 : isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
8986 33 : if (vertCRSType == PJ_TYPE_BOUND_CRS)
8987 : {
8988 0 : auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
8989 0 : if (base)
8990 : {
8991 0 : isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
8992 0 : proj_destroy(base);
8993 : }
8994 : }
8995 33 : proj_destroy(vertCRS);
8996 : }
8997 : }
8998 4589 : d->undoDemoteFromBoundCRS();
8999 4589 : return isVertical;
9000 : }
9001 :
9002 : /************************************************************************/
9003 : /* OSRIsVertical() */
9004 : /************************************************************************/
9005 : /**
9006 : * \brief Check if vertical coordinate system.
9007 : *
9008 : * This function is the same as OGRSpatialReference::IsVertical().
9009 : *
9010 : * @since OGR 1.8.0
9011 : */
9012 0 : int OSRIsVertical(OGRSpatialReferenceH hSRS)
9013 :
9014 : {
9015 0 : VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9016 :
9017 0 : return ToPointer(hSRS)->IsVertical();
9018 : }
9019 :
9020 : /************************************************************************/
9021 : /* IsDynamic() */
9022 : /************************************************************************/
9023 :
9024 : /**
9025 : * \brief Check if a CRS is a dynamic CRS.
9026 : *
9027 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9028 : * plate-fixed.
9029 : *
9030 : * This method is the same as the C function OSRIsDynamic().
9031 : *
9032 : * @return true if the CRS is dynamic
9033 : *
9034 : * @since OGR 3.4.0
9035 : *
9036 : * @see HasPointMotionOperation()
9037 : */
9038 :
9039 9762 : bool OGRSpatialReference::IsDynamic() const
9040 :
9041 : {
9042 9762 : bool isDynamic = false;
9043 9762 : d->refreshProjObj();
9044 9762 : d->demoteFromBoundCRS();
9045 9762 : auto ctxt = d->getPROJContext();
9046 9762 : PJ *horiz = nullptr;
9047 9762 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9048 : {
9049 96 : horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9050 : }
9051 9666 : else if (d->m_pj_crs)
9052 : {
9053 9623 : horiz = proj_clone(ctxt, d->m_pj_crs);
9054 : }
9055 9762 : if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9056 : {
9057 6 : auto baseCRS = proj_get_source_crs(ctxt, horiz);
9058 6 : if (baseCRS)
9059 : {
9060 6 : proj_destroy(horiz);
9061 6 : horiz = baseCRS;
9062 : }
9063 : }
9064 9762 : auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9065 9762 : if (datum)
9066 : {
9067 9708 : const auto type = proj_get_type(datum);
9068 9708 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9069 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9070 9708 : if (!isDynamic)
9071 : {
9072 9708 : const char *auth_name = proj_get_id_auth_name(datum, 0);
9073 9708 : const char *code = proj_get_id_code(datum, 0);
9074 9708 : if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9075 9319 : EQUAL(code, "6326"))
9076 : {
9077 5138 : isDynamic = true;
9078 : }
9079 : }
9080 9708 : proj_destroy(datum);
9081 : }
9082 : #if PROJ_VERSION_MAJOR > 7 || \
9083 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9084 : else
9085 : {
9086 : auto ensemble =
9087 : horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9088 : if (ensemble)
9089 : {
9090 : auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9091 : if (member)
9092 : {
9093 : const auto type = proj_get_type(member);
9094 : isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9095 : type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9096 : proj_destroy(member);
9097 : }
9098 : proj_destroy(ensemble);
9099 : }
9100 : }
9101 : #endif
9102 9762 : proj_destroy(horiz);
9103 9762 : d->undoDemoteFromBoundCRS();
9104 9762 : return isDynamic;
9105 : }
9106 :
9107 : /************************************************************************/
9108 : /* OSRIsDynamic() */
9109 : /************************************************************************/
9110 : /**
9111 : * \brief Check if a CRS is a dynamic CRS.
9112 : *
9113 : * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9114 : * plate-fixed.
9115 : *
9116 : * This function is the same as OGRSpatialReference::IsDynamic().
9117 : *
9118 : * @since OGR 3.4.0
9119 : */
9120 0 : int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9121 :
9122 : {
9123 0 : VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9124 :
9125 0 : return ToPointer(hSRS)->IsDynamic();
9126 : }
9127 :
9128 : /************************************************************************/
9129 : /* HasPointMotionOperation() */
9130 : /************************************************************************/
9131 :
9132 : /**
9133 : * \brief Check if a CRS has at least an associated point motion operation.
9134 : *
9135 : * Some CRS are not formally declared as dynamic, but may behave as such
9136 : * in practice due to the prsence of point motion operation, to perform
9137 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9138 : *
9139 : * @return true if the CRS has at least an associated point motion operation.
9140 : *
9141 : * @since OGR 3.8.0 and PROJ 9.4.0
9142 : *
9143 : * @see IsDynamic()
9144 : */
9145 :
9146 5 : bool OGRSpatialReference::HasPointMotionOperation() const
9147 :
9148 : {
9149 : #if PROJ_VERSION_MAJOR > 9 || \
9150 : (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9151 : d->refreshProjObj();
9152 : d->demoteFromBoundCRS();
9153 : auto ctxt = d->getPROJContext();
9154 : auto res =
9155 : CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9156 : d->undoDemoteFromBoundCRS();
9157 : return res;
9158 : #else
9159 5 : return false;
9160 : #endif
9161 : }
9162 :
9163 : /************************************************************************/
9164 : /* OSRHasPointMotionOperation() */
9165 : /************************************************************************/
9166 :
9167 : /**
9168 : * \brief Check if a CRS has at least an associated point motion operation.
9169 : *
9170 : * Some CRS are not formally declared as dynamic, but may behave as such
9171 : * in practice due to the prsence of point motion operation, to perform
9172 : * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9173 : *
9174 : * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9175 : *
9176 : * @since OGR 3.8.0 and PROJ 9.4.0
9177 : */
9178 0 : int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9179 :
9180 : {
9181 0 : VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9182 :
9183 0 : return ToPointer(hSRS)->HasPointMotionOperation();
9184 : }
9185 :
9186 : /************************************************************************/
9187 : /* CloneGeogCS() */
9188 : /************************************************************************/
9189 :
9190 : /**
9191 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9192 : * object.
9193 : *
9194 : * @return a new SRS, which becomes the responsibility of the caller.
9195 : */
9196 3284 : OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9197 :
9198 : {
9199 3284 : d->refreshProjObj();
9200 3284 : if (d->m_pj_crs)
9201 : {
9202 3284 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9203 0 : return nullptr;
9204 :
9205 : auto geodCRS =
9206 3284 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9207 3284 : if (geodCRS)
9208 : {
9209 3284 : OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9210 3284 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9211 : {
9212 : PJ *hub_crs =
9213 81 : proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9214 81 : PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9215 81 : d->m_pj_crs);
9216 81 : auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9217 : geodCRS, hub_crs, co);
9218 81 : proj_destroy(geodCRS);
9219 81 : geodCRS = temp;
9220 81 : proj_destroy(hub_crs);
9221 81 : proj_destroy(co);
9222 : }
9223 :
9224 : /* --------------------------------------------------------------------
9225 : */
9226 : /* We have to reconstruct the GEOGCS node for geocentric */
9227 : /* coordinate systems. */
9228 : /* --------------------------------------------------------------------
9229 : */
9230 3284 : if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9231 : {
9232 0 : auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9233 : #if PROJ_VERSION_MAJOR > 7 || \
9234 : (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9235 : if (datum == nullptr)
9236 : {
9237 : datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9238 : geodCRS);
9239 : }
9240 : #endif
9241 0 : if (datum)
9242 : {
9243 0 : auto cs = proj_create_ellipsoidal_2D_cs(
9244 : d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9245 : nullptr, 0);
9246 0 : auto temp = proj_create_geographic_crs_from_datum(
9247 : d->getPROJContext(), "unnamed", datum, cs);
9248 0 : proj_destroy(datum);
9249 0 : proj_destroy(cs);
9250 0 : proj_destroy(geodCRS);
9251 0 : geodCRS = temp;
9252 : }
9253 : }
9254 :
9255 3284 : poNewSRS->d->setPjCRS(geodCRS);
9256 3284 : if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9257 2283 : poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9258 3284 : return poNewSRS;
9259 : }
9260 : }
9261 0 : return nullptr;
9262 : }
9263 :
9264 : /************************************************************************/
9265 : /* OSRCloneGeogCS() */
9266 : /************************************************************************/
9267 : /**
9268 : * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9269 : * object.
9270 : *
9271 : * This function is the same as OGRSpatialReference::CloneGeogCS().
9272 : */
9273 143 : OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9274 :
9275 : {
9276 143 : VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9277 :
9278 143 : return ToHandle(ToPointer(hSource)->CloneGeogCS());
9279 : }
9280 :
9281 : /************************************************************************/
9282 : /* IsSameGeogCS() */
9283 : /************************************************************************/
9284 :
9285 : /**
9286 : * \brief Do the GeogCS'es match?
9287 : *
9288 : * This method is the same as the C function OSRIsSameGeogCS().
9289 : *
9290 : * @param poOther the SRS being compared against.
9291 : *
9292 : * @return TRUE if they are the same or FALSE otherwise.
9293 : */
9294 :
9295 5305 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9296 :
9297 : {
9298 5305 : return IsSameGeogCS(poOther, nullptr);
9299 : }
9300 :
9301 : /**
9302 : * \brief Do the GeogCS'es match?
9303 : *
9304 : * This method is the same as the C function OSRIsSameGeogCS().
9305 : *
9306 : * @param poOther the SRS being compared against.
9307 : * @param papszOptions options. ignored
9308 : *
9309 : * @return TRUE if they are the same or FALSE otherwise.
9310 : */
9311 :
9312 5305 : int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9313 : const char *const *papszOptions) const
9314 :
9315 : {
9316 5305 : CPL_IGNORE_RET_VAL(papszOptions);
9317 :
9318 5305 : d->refreshProjObj();
9319 5305 : poOther->d->refreshProjObj();
9320 5305 : if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9321 0 : return FALSE;
9322 5305 : if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9323 5305 : d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9324 15915 : poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9325 5305 : poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9326 : {
9327 0 : return FALSE;
9328 : }
9329 :
9330 5305 : auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9331 : auto otherGeodCRS =
9332 5305 : proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9333 5305 : if (!geodCRS || !otherGeodCRS)
9334 : {
9335 0 : proj_destroy(geodCRS);
9336 0 : proj_destroy(otherGeodCRS);
9337 0 : return FALSE;
9338 : }
9339 :
9340 5305 : int ret = proj_is_equivalent_to(
9341 : geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9342 :
9343 5305 : proj_destroy(geodCRS);
9344 5305 : proj_destroy(otherGeodCRS);
9345 5305 : return ret;
9346 : }
9347 :
9348 : /************************************************************************/
9349 : /* OSRIsSameGeogCS() */
9350 : /************************************************************************/
9351 :
9352 : /**
9353 : * \brief Do the GeogCS'es match?
9354 : *
9355 : * This function is the same as OGRSpatialReference::IsSameGeogCS().
9356 : */
9357 0 : int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9358 :
9359 : {
9360 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9361 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9362 :
9363 0 : return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9364 : }
9365 :
9366 : /************************************************************************/
9367 : /* IsSameVertCS() */
9368 : /************************************************************************/
9369 :
9370 : /**
9371 : * \brief Do the VertCS'es match?
9372 : *
9373 : * This method is the same as the C function OSRIsSameVertCS().
9374 : *
9375 : * @param poOther the SRS being compared against.
9376 : *
9377 : * @return TRUE if they are the same or FALSE otherwise.
9378 : */
9379 :
9380 2 : int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9381 :
9382 : {
9383 : /* -------------------------------------------------------------------- */
9384 : /* Does the datum name match? */
9385 : /* -------------------------------------------------------------------- */
9386 2 : const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9387 2 : const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9388 :
9389 2 : if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9390 2 : !EQUAL(pszThisValue, pszOtherValue))
9391 1 : return FALSE;
9392 :
9393 : /* -------------------------------------------------------------------- */
9394 : /* Do the units match? */
9395 : /* -------------------------------------------------------------------- */
9396 1 : pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9397 1 : if (pszThisValue == nullptr)
9398 0 : pszThisValue = "1.0";
9399 :
9400 1 : pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9401 1 : if (pszOtherValue == nullptr)
9402 0 : pszOtherValue = "1.0";
9403 :
9404 1 : if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9405 0 : return FALSE;
9406 :
9407 1 : return TRUE;
9408 : }
9409 :
9410 : /************************************************************************/
9411 : /* OSRIsSameVertCS() */
9412 : /************************************************************************/
9413 :
9414 : /**
9415 : * \brief Do the VertCS'es match?
9416 : *
9417 : * This function is the same as OGRSpatialReference::IsSameVertCS().
9418 : */
9419 0 : int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9420 :
9421 : {
9422 0 : VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9423 0 : VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9424 :
9425 0 : return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9426 : }
9427 :
9428 : /************************************************************************/
9429 : /* IsSame() */
9430 : /************************************************************************/
9431 :
9432 : /**
9433 : * \brief Do these two spatial references describe the same system ?
9434 : *
9435 : * @param poOtherSRS the SRS being compared to.
9436 : *
9437 : * @return TRUE if equivalent or FALSE otherwise.
9438 : */
9439 :
9440 24146 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9441 :
9442 : {
9443 24146 : return IsSame(poOtherSRS, nullptr);
9444 : }
9445 :
9446 : /**
9447 : * \brief Do these two spatial references describe the same system ?
9448 : *
9449 : * This also takes into account the data axis to CRS axis mapping by default
9450 : *
9451 : * @param poOtherSRS the SRS being compared to.
9452 : * @param papszOptions options. NULL or NULL terminated list of options.
9453 : * Currently supported options are:
9454 : * <ul>
9455 : * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9456 : * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9457 : * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9458 : * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9459 : * </ul>
9460 : *
9461 : * @return TRUE if equivalent or FALSE otherwise.
9462 : */
9463 :
9464 32132 : int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9465 : const char *const *papszOptions) const
9466 :
9467 : {
9468 32132 : d->refreshProjObj();
9469 32132 : poOtherSRS->d->refreshProjObj();
9470 32132 : if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9471 50 : return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9472 32082 : if (!CPLTestBool(CSLFetchNameValueDef(
9473 : papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9474 : {
9475 31596 : if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9476 1997 : return false;
9477 : }
9478 :
9479 30085 : if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9480 : "IGNORE_COORDINATE_EPOCH", "NO")))
9481 : {
9482 29803 : if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9483 25 : return false;
9484 : }
9485 :
9486 30060 : bool reboundSelf = false;
9487 30060 : bool reboundOther = false;
9488 30112 : if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9489 52 : poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9490 : {
9491 17 : d->demoteFromBoundCRS();
9492 17 : reboundSelf = true;
9493 : }
9494 60051 : else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9495 30008 : poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9496 : {
9497 68 : poOtherSRS->d->demoteFromBoundCRS();
9498 68 : reboundOther = true;
9499 : }
9500 :
9501 30060 : PJ_COMPARISON_CRITERION criterion =
9502 : PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9503 30060 : const char *pszCriterion = CSLFetchNameValueDef(
9504 : papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9505 30060 : if (EQUAL(pszCriterion, "STRICT"))
9506 0 : criterion = PJ_COMP_STRICT;
9507 30060 : else if (EQUAL(pszCriterion, "EQUIVALENT"))
9508 5765 : criterion = PJ_COMP_EQUIVALENT;
9509 24295 : else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9510 : {
9511 0 : CPLError(CE_Warning, CPLE_NotSupported,
9512 : "Unsupported value for CRITERION: %s", pszCriterion);
9513 : }
9514 : int ret =
9515 30060 : proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9516 30060 : if (reboundSelf)
9517 17 : d->undoDemoteFromBoundCRS();
9518 30060 : if (reboundOther)
9519 68 : poOtherSRS->d->undoDemoteFromBoundCRS();
9520 :
9521 30060 : return ret;
9522 : }
9523 :
9524 : /************************************************************************/
9525 : /* OSRIsSame() */
9526 : /************************************************************************/
9527 :
9528 : /**
9529 : * \brief Do these two spatial references describe the same system ?
9530 : *
9531 : * This function is the same as OGRSpatialReference::IsSame().
9532 : */
9533 25 : int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9534 :
9535 : {
9536 25 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9537 25 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9538 :
9539 25 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9540 : }
9541 :
9542 : /************************************************************************/
9543 : /* OSRIsSameEx() */
9544 : /************************************************************************/
9545 :
9546 : /**
9547 : * \brief Do these two spatial references describe the same system ?
9548 : *
9549 : * This function is the same as OGRSpatialReference::IsSame().
9550 : */
9551 589 : int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9552 : const char *const *papszOptions)
9553 : {
9554 589 : VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9555 589 : VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9556 :
9557 589 : return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9558 : }
9559 :
9560 : /************************************************************************/
9561 : /* convertToOtherProjection() */
9562 : /************************************************************************/
9563 :
9564 : /**
9565 : * \brief Convert to another equivalent projection
9566 : *
9567 : * Currently implemented:
9568 : * <ul>
9569 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9570 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9571 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9572 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9573 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9574 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9575 : * </ul>
9576 : *
9577 : * @param pszTargetProjection target projection.
9578 : * @param papszOptions lists of options. None supported currently.
9579 : * @return a new SRS, or NULL in case of error.
9580 : *
9581 : * @since GDAL 2.3
9582 : */
9583 87 : OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
9584 : const char *pszTargetProjection,
9585 : CPL_UNUSED const char *const *papszOptions) const
9586 : {
9587 87 : if (pszTargetProjection == nullptr)
9588 1 : return nullptr;
9589 : int new_code;
9590 86 : if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
9591 : {
9592 6 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
9593 : }
9594 80 : else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
9595 : {
9596 5 : new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
9597 : }
9598 75 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
9599 : {
9600 63 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
9601 : }
9602 12 : else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
9603 : {
9604 11 : new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
9605 : }
9606 : else
9607 : {
9608 1 : return nullptr;
9609 : }
9610 :
9611 85 : d->refreshProjObj();
9612 85 : d->demoteFromBoundCRS();
9613 85 : OGRSpatialReference *poNewSRS = nullptr;
9614 85 : if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
9615 : {
9616 : auto conv =
9617 84 : proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
9618 84 : auto new_conv = proj_convert_conversion_to_other_method(
9619 : d->getPROJContext(), conv, new_code, nullptr);
9620 84 : proj_destroy(conv);
9621 84 : if (new_conv)
9622 : {
9623 : auto geodCRS =
9624 70 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9625 70 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
9626 70 : d->m_pj_crs);
9627 70 : if (geodCRS && cs)
9628 : {
9629 70 : auto new_proj_crs = proj_create_projected_crs(
9630 70 : d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
9631 : new_conv, cs);
9632 70 : proj_destroy(new_conv);
9633 70 : if (new_proj_crs)
9634 : {
9635 70 : poNewSRS = new OGRSpatialReference();
9636 :
9637 70 : if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
9638 : {
9639 9 : auto boundCRS = proj_crs_create_bound_crs(
9640 : d->getPROJContext(), new_proj_crs,
9641 9 : d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
9642 9 : if (boundCRS)
9643 : {
9644 9 : proj_destroy(new_proj_crs);
9645 9 : new_proj_crs = boundCRS;
9646 : }
9647 : }
9648 :
9649 70 : poNewSRS->d->setPjCRS(new_proj_crs);
9650 : }
9651 : }
9652 70 : proj_destroy(geodCRS);
9653 70 : proj_destroy(cs);
9654 : }
9655 : }
9656 85 : d->undoDemoteFromBoundCRS();
9657 85 : return poNewSRS;
9658 : }
9659 :
9660 : /************************************************************************/
9661 : /* OSRConvertToOtherProjection() */
9662 : /************************************************************************/
9663 :
9664 : /**
9665 : * \brief Convert to another equivalent projection
9666 : *
9667 : * Currently implemented:
9668 : * <ul>
9669 : * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9670 : * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9671 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9672 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9673 : * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9674 : * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9675 : * </ul>
9676 : *
9677 : * @param hSRS source SRS
9678 : * @param pszTargetProjection target projection.
9679 : * @param papszOptions lists of options. None supported currently.
9680 : * @return a new SRS, or NULL in case of error.
9681 : *
9682 : * @since GDAL 2.3
9683 : */
9684 : OGRSpatialReferenceH
9685 28 : OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
9686 : const char *pszTargetProjection,
9687 : const char *const *papszOptions)
9688 : {
9689 28 : VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
9690 28 : return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
9691 28 : pszTargetProjection, papszOptions));
9692 : }
9693 :
9694 : /************************************************************************/
9695 : /* OSRFindMatches() */
9696 : /************************************************************************/
9697 :
9698 : /**
9699 : * \brief Try to identify a match between the passed SRS and a related SRS
9700 : * in a catalog.
9701 : *
9702 : * Matching may be partial, or may fail.
9703 : * Returned entries will be sorted by decreasing match confidence (first
9704 : * entry has the highest match confidence).
9705 : *
9706 : * The exact way matching is done may change in future versions. Starting with
9707 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
9708 : *
9709 : * This function is the same as OGRSpatialReference::FindMatches().
9710 : *
9711 : * @param hSRS SRS to match
9712 : * @param papszOptions NULL terminated list of options or NULL
9713 : * @param pnEntries Output parameter. Number of values in the returned array.
9714 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
9715 : * will be allocated to an array of *pnEntries whose values between 0 and 100
9716 : * indicate the confidence in the match. 100 is the highest confidence level.
9717 : * The array must be freed with CPLFree().
9718 : *
9719 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
9720 : * with OSRFreeSRSArray()
9721 : *
9722 : * @since GDAL 2.3
9723 : */
9724 8 : OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
9725 : char **papszOptions, int *pnEntries,
9726 : int **ppanMatchConfidence)
9727 : {
9728 8 : if (pnEntries)
9729 8 : *pnEntries = 0;
9730 8 : if (ppanMatchConfidence)
9731 8 : *ppanMatchConfidence = nullptr;
9732 8 : VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
9733 :
9734 8 : OGRSpatialReference *poSRS = ToPointer(hSRS);
9735 8 : return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
9736 : }
9737 :
9738 : /************************************************************************/
9739 : /* OSRFreeSRSArray() */
9740 : /************************************************************************/
9741 :
9742 : /**
9743 : * \brief Free return of OSRIdentifyMatches()
9744 : *
9745 : * @param pahSRS array of SRS (must be NULL terminated)
9746 : * @since GDAL 2.3
9747 : */
9748 130 : void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
9749 : {
9750 130 : if (pahSRS != nullptr)
9751 : {
9752 2109 : for (int i = 0; pahSRS[i] != nullptr; ++i)
9753 : {
9754 1997 : OSRRelease(pahSRS[i]);
9755 : }
9756 112 : CPLFree(pahSRS);
9757 : }
9758 130 : }
9759 :
9760 : /************************************************************************/
9761 : /* FindBestMatch() */
9762 : /************************************************************************/
9763 :
9764 : /**
9765 : * \brief Try to identify the best match between the passed SRS and a related
9766 : * SRS in a catalog.
9767 : *
9768 : * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
9769 : * of filtering its output.
9770 : * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
9771 : * will be considered. If there is a single match, it is returned.
9772 : * If there are several matches, only return the one under the
9773 : * pszPreferredAuthority, if there is a single one under that authority.
9774 : *
9775 : * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
9776 : * 100). If set to 0, 90 is used.
9777 : * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
9778 : * "EPSG" is used.
9779 : * @param papszOptions NULL terminated list of options or NULL. No option is
9780 : * defined at time of writing.
9781 : *
9782 : * @return a new OGRSpatialReference* object to free with Release(), or nullptr
9783 : *
9784 : * @since GDAL 3.6
9785 : * @see OGRSpatialReference::FindMatches()
9786 : */
9787 : OGRSpatialReference *
9788 971 : OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
9789 : const char *pszPreferredAuthority,
9790 : CSLConstList papszOptions) const
9791 : {
9792 971 : CPL_IGNORE_RET_VAL(papszOptions); // ignored for now.
9793 :
9794 971 : if (nMinimumMatchConfidence == 0)
9795 0 : nMinimumMatchConfidence = 90;
9796 971 : if (pszPreferredAuthority == nullptr)
9797 0 : pszPreferredAuthority = "EPSG";
9798 :
9799 : // Try to identify the CRS with the database
9800 971 : int nEntries = 0;
9801 971 : int *panConfidence = nullptr;
9802 : OGRSpatialReferenceH *pahSRS =
9803 971 : FindMatches(nullptr, &nEntries, &panConfidence);
9804 971 : if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
9805 : {
9806 1776 : std::vector<double> adfTOWGS84(7);
9807 888 : if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
9808 : {
9809 887 : adfTOWGS84.clear();
9810 : }
9811 :
9812 888 : auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
9813 :
9814 : auto poBaseGeogCRS =
9815 888 : std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
9816 :
9817 : // If the base geographic SRS of the SRS is EPSG:4326
9818 : // with TOWGS84[0,0,0,0,0,0], then just use the official
9819 : // SRS code
9820 : // Same with EPSG:4258 (ETRS89), since it's the only known
9821 : // TOWGS84[] style transformation to WGS 84, and given the
9822 : // "fuzzy" nature of both ETRS89 and WGS 84, there's little
9823 : // chance that a non-NULL TOWGS84[] will emerge.
9824 888 : const char *pszAuthorityName = nullptr;
9825 888 : const char *pszAuthorityCode = nullptr;
9826 888 : const char *pszBaseAuthorityName = nullptr;
9827 888 : const char *pszBaseAuthorityCode = nullptr;
9828 1776 : if (adfTOWGS84 == std::vector<double>(7) &&
9829 1 : (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
9830 1 : EQUAL(pszAuthorityName, "EPSG") &&
9831 1 : (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
9832 1 : (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
9833 1 : nullptr &&
9834 1 : EQUAL(pszBaseAuthorityName, "EPSG") &&
9835 1 : (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
9836 1777 : nullptr &&
9837 1 : (EQUAL(pszBaseAuthorityCode, "4326") ||
9838 1 : EQUAL(pszBaseAuthorityCode, "4258")))
9839 : {
9840 1 : poSRS->importFromEPSG(atoi(pszAuthorityCode));
9841 : }
9842 :
9843 888 : CPLFree(pahSRS);
9844 888 : CPLFree(panConfidence);
9845 :
9846 888 : return poSRS;
9847 : }
9848 : else
9849 : {
9850 : // If there are several matches >= nMinimumMatchConfidence, take the
9851 : // only one that is under pszPreferredAuthority
9852 83 : int iBestEntry = -1;
9853 1299 : for (int i = 0; i < nEntries; i++)
9854 : {
9855 1216 : if (panConfidence[i] >= nMinimumMatchConfidence)
9856 : {
9857 : const char *pszAuthName =
9858 0 : OGRSpatialReference::FromHandle(pahSRS[i])
9859 0 : ->GetAuthorityName(nullptr);
9860 0 : if (pszAuthName != nullptr &&
9861 0 : EQUAL(pszAuthName, pszPreferredAuthority))
9862 : {
9863 0 : if (iBestEntry < 0)
9864 0 : iBestEntry = i;
9865 : else
9866 : {
9867 0 : iBestEntry = -1;
9868 0 : break;
9869 : }
9870 : }
9871 : }
9872 : }
9873 83 : if (iBestEntry >= 0)
9874 : {
9875 0 : auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
9876 0 : OSRFreeSRSArray(pahSRS);
9877 0 : CPLFree(panConfidence);
9878 0 : return poRet;
9879 : }
9880 : }
9881 83 : OSRFreeSRSArray(pahSRS);
9882 83 : CPLFree(panConfidence);
9883 83 : return nullptr;
9884 : }
9885 :
9886 : /************************************************************************/
9887 : /* SetTOWGS84() */
9888 : /************************************************************************/
9889 :
9890 : /**
9891 : * \brief Set the Bursa-Wolf conversion to WGS84.
9892 : *
9893 : * This will create the TOWGS84 node as a child of the DATUM. It will fail
9894 : * if there is no existing DATUM node. It will replace
9895 : * an existing TOWGS84 node if there is one.
9896 : *
9897 : * The parameters have the same meaning as EPSG transformation 9606
9898 : * (Position Vector 7-param. transformation).
9899 : *
9900 : * This method is the same as the C function OSRSetTOWGS84().
9901 : *
9902 : * @param dfDX X child in meters.
9903 : * @param dfDY Y child in meters.
9904 : * @param dfDZ Z child in meters.
9905 : * @param dfEX X rotation in arc seconds (optional, defaults to zero).
9906 : * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
9907 : * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
9908 : * @param dfPPM scaling factor (parts per million).
9909 : *
9910 : * @return OGRERR_NONE on success.
9911 : */
9912 :
9913 114 : OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
9914 : double dfEX, double dfEY, double dfEZ,
9915 : double dfPPM)
9916 :
9917 : {
9918 114 : d->refreshProjObj();
9919 114 : if (d->m_pj_crs == nullptr)
9920 : {
9921 0 : return OGRERR_FAILURE;
9922 : }
9923 :
9924 : // Remove existing BoundCRS
9925 114 : if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9926 : {
9927 0 : auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
9928 0 : if (!baseCRS)
9929 0 : return OGRERR_FAILURE;
9930 0 : d->setPjCRS(baseCRS);
9931 : }
9932 :
9933 : PJ_PARAM_DESCRIPTION params[7];
9934 :
9935 114 : params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
9936 114 : params[0].auth_name = "EPSG";
9937 114 : params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
9938 114 : params[0].value = dfDX;
9939 114 : params[0].unit_name = "metre";
9940 114 : params[0].unit_conv_factor = 1.0;
9941 114 : params[0].unit_type = PJ_UT_LINEAR;
9942 :
9943 114 : params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
9944 114 : params[1].auth_name = "EPSG";
9945 114 : params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
9946 114 : params[1].value = dfDY;
9947 114 : params[1].unit_name = "metre";
9948 114 : params[1].unit_conv_factor = 1.0;
9949 114 : params[1].unit_type = PJ_UT_LINEAR;
9950 :
9951 114 : params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
9952 114 : params[2].auth_name = "EPSG";
9953 114 : params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
9954 114 : params[2].value = dfDZ;
9955 114 : params[2].unit_name = "metre";
9956 114 : params[2].unit_conv_factor = 1.0;
9957 114 : params[2].unit_type = PJ_UT_LINEAR;
9958 :
9959 114 : params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
9960 114 : params[3].auth_name = "EPSG";
9961 114 : params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
9962 114 : params[3].value = dfEX;
9963 114 : params[3].unit_name = "arc-second";
9964 114 : params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
9965 114 : params[3].unit_type = PJ_UT_ANGULAR;
9966 :
9967 114 : params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
9968 114 : params[4].auth_name = "EPSG";
9969 114 : params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
9970 114 : params[4].value = dfEY;
9971 114 : params[4].unit_name = "arc-second";
9972 114 : params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
9973 114 : params[4].unit_type = PJ_UT_ANGULAR;
9974 :
9975 114 : params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
9976 114 : params[5].auth_name = "EPSG";
9977 114 : params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
9978 114 : params[5].value = dfEZ;
9979 114 : params[5].unit_name = "arc-second";
9980 114 : params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
9981 114 : params[5].unit_type = PJ_UT_ANGULAR;
9982 :
9983 114 : params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
9984 114 : params[6].auth_name = "EPSG";
9985 114 : params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
9986 114 : params[6].value = dfPPM;
9987 114 : params[6].unit_name = "parts per million";
9988 114 : params[6].unit_conv_factor = 1e-6;
9989 114 : params[6].unit_type = PJ_UT_SCALE;
9990 :
9991 : auto sourceCRS =
9992 114 : proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9993 114 : if (!sourceCRS)
9994 : {
9995 0 : return OGRERR_FAILURE;
9996 : }
9997 :
9998 114 : const auto sourceType = proj_get_type(sourceCRS);
9999 :
10000 114 : auto targetCRS = proj_create_from_database(
10001 : d->getPROJContext(), "EPSG",
10002 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326"
10003 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10004 : : "4978",
10005 : PJ_CATEGORY_CRS, false, nullptr);
10006 114 : if (!targetCRS)
10007 : {
10008 0 : proj_destroy(sourceCRS);
10009 0 : return OGRERR_FAILURE;
10010 : }
10011 :
10012 228 : CPLString osMethodCode;
10013 : osMethodCode.Printf("%d",
10014 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10015 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10016 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10017 0 : ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10018 114 : : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10019 :
10020 114 : auto transf = proj_create_transformation(
10021 : d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10022 : sourceCRS, targetCRS, nullptr,
10023 : sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10024 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10025 : : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10026 0 : ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10027 : : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10028 : "EPSG", osMethodCode.c_str(), 7, params, -1);
10029 114 : proj_destroy(sourceCRS);
10030 114 : if (!transf)
10031 : {
10032 0 : proj_destroy(targetCRS);
10033 0 : return OGRERR_FAILURE;
10034 : }
10035 :
10036 114 : auto newBoundCRS = proj_crs_create_bound_crs(
10037 114 : d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10038 114 : proj_destroy(transf);
10039 114 : proj_destroy(targetCRS);
10040 114 : if (!newBoundCRS)
10041 : {
10042 0 : return OGRERR_FAILURE;
10043 : }
10044 :
10045 114 : d->setPjCRS(newBoundCRS);
10046 114 : return OGRERR_NONE;
10047 : }
10048 :
10049 : /************************************************************************/
10050 : /* OSRSetTOWGS84() */
10051 : /************************************************************************/
10052 :
10053 : /**
10054 : * \brief Set the Bursa-Wolf conversion to WGS84.
10055 : *
10056 : * This function is the same as OGRSpatialReference::SetTOWGS84().
10057 : */
10058 10 : OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10059 : double dfDZ, double dfEX, double dfEY, double dfEZ,
10060 : double dfPPM)
10061 :
10062 : {
10063 10 : VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10064 :
10065 10 : return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10066 10 : dfPPM);
10067 : }
10068 :
10069 : /************************************************************************/
10070 : /* GetTOWGS84() */
10071 : /************************************************************************/
10072 :
10073 : /**
10074 : * \brief Fetch TOWGS84 parameters, if available.
10075 : *
10076 : * The parameters have the same meaning as EPSG transformation 9606
10077 : * (Position Vector 7-param. transformation).
10078 : *
10079 : * @param padfCoeff array into which up to 7 coefficients are placed.
10080 : * @param nCoeffCount size of padfCoeff - defaults to 7.
10081 : *
10082 : * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10083 : * TOWGS84 node available.
10084 : */
10085 :
10086 3205 : OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10087 :
10088 : {
10089 3205 : d->refreshProjObj();
10090 3205 : if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10091 3145 : return OGRERR_FAILURE;
10092 :
10093 60 : memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10094 :
10095 60 : auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10096 60 : int success = proj_coordoperation_get_towgs84_values(
10097 : d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10098 60 : proj_destroy(transf);
10099 :
10100 60 : return success ? OGRERR_NONE : OGRERR_FAILURE;
10101 : }
10102 :
10103 : /************************************************************************/
10104 : /* OSRGetTOWGS84() */
10105 : /************************************************************************/
10106 :
10107 : /**
10108 : * \brief Fetch TOWGS84 parameters, if available.
10109 : *
10110 : * This function is the same as OGRSpatialReference::GetTOWGS84().
10111 : */
10112 11 : OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10113 : int nCoeffCount)
10114 :
10115 : {
10116 11 : VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10117 :
10118 11 : return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10119 : }
10120 :
10121 : /************************************************************************/
10122 : /* IsAngularParameter() */
10123 : /************************************************************************/
10124 :
10125 : /** Is the passed projection parameter an angular one?
10126 : *
10127 : * @return TRUE or FALSE
10128 : */
10129 :
10130 4 : int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10131 :
10132 : {
10133 4 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10134 4 : STARTS_WITH_CI(pszParameterName, "lati") ||
10135 3 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10136 2 : STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10137 0 : EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10138 0 : EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10139 4 : return TRUE;
10140 :
10141 0 : return FALSE;
10142 : }
10143 :
10144 : /************************************************************************/
10145 : /* IsLongitudeParameter() */
10146 : /************************************************************************/
10147 :
10148 : /** Is the passed projection parameter an angular longitude
10149 : * (relative to a prime meridian)?
10150 : *
10151 : * @return TRUE or FALSE
10152 : */
10153 :
10154 0 : int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10155 :
10156 : {
10157 0 : if (STARTS_WITH_CI(pszParameterName, "long") ||
10158 0 : EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10159 0 : return TRUE;
10160 :
10161 0 : return FALSE;
10162 : }
10163 :
10164 : /************************************************************************/
10165 : /* IsLinearParameter() */
10166 : /************************************************************************/
10167 :
10168 : /** Is the passed projection parameter an linear one measured in meters or
10169 : * some similar linear measure.
10170 : *
10171 : * @return TRUE or FALSE
10172 : */
10173 43 : int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10174 :
10175 : {
10176 43 : if (STARTS_WITH_CI(pszParameterName, "false_") ||
10177 34 : EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10178 9 : return TRUE;
10179 :
10180 34 : return FALSE;
10181 : }
10182 :
10183 : /************************************************************************/
10184 : /* GetNormInfo() */
10185 : /************************************************************************/
10186 :
10187 : /**
10188 : * \brief Set the internal information for normalizing linear, and angular
10189 : * values.
10190 : */
10191 3460 : void OGRSpatialReference::GetNormInfo() const
10192 :
10193 : {
10194 3460 : if (d->bNormInfoSet)
10195 2434 : return;
10196 :
10197 : /* -------------------------------------------------------------------- */
10198 : /* Initialize values. */
10199 : /* -------------------------------------------------------------------- */
10200 1026 : d->bNormInfoSet = TRUE;
10201 :
10202 1026 : d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10203 1026 : d->dfToMeter = GetLinearUnits(nullptr);
10204 1026 : d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10205 1026 : if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10206 1025 : d->dfToDegrees = 1.0;
10207 : }
10208 :
10209 : /************************************************************************/
10210 : /* GetExtension() */
10211 : /************************************************************************/
10212 :
10213 : /**
10214 : * \brief Fetch extension value.
10215 : *
10216 : * Fetch the value of the named EXTENSION item for the identified
10217 : * target node.
10218 : *
10219 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10220 : * @param pszName the name of the extension being fetched.
10221 : * @param pszDefault the value to return if the extension is not found.
10222 : *
10223 : * @return node value if successful or pszDefault on failure.
10224 : */
10225 :
10226 7914 : const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10227 : const char *pszName,
10228 : const char *pszDefault) const
10229 :
10230 : {
10231 : /* -------------------------------------------------------------------- */
10232 : /* Find the target node. */
10233 : /* -------------------------------------------------------------------- */
10234 : const OGR_SRSNode *poNode =
10235 7914 : pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10236 :
10237 7914 : if (poNode == nullptr)
10238 1360 : return nullptr;
10239 :
10240 : /* -------------------------------------------------------------------- */
10241 : /* Fetch matching EXTENSION if there is one. */
10242 : /* -------------------------------------------------------------------- */
10243 48446 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10244 : {
10245 41914 : const OGR_SRSNode *poChild = poNode->GetChild(i);
10246 :
10247 41938 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10248 24 : poChild->GetChildCount() >= 2)
10249 : {
10250 24 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10251 22 : return poChild->GetChild(1)->GetValue();
10252 : }
10253 : }
10254 :
10255 6532 : return pszDefault;
10256 : }
10257 :
10258 : /************************************************************************/
10259 : /* SetExtension() */
10260 : /************************************************************************/
10261 : /**
10262 : * \brief Set extension value.
10263 : *
10264 : * Set the value of the named EXTENSION item for the identified
10265 : * target node.
10266 : *
10267 : * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10268 : * @param pszName the name of the extension being fetched.
10269 : * @param pszValue the value to set
10270 : *
10271 : * @return OGRERR_NONE on success
10272 : */
10273 :
10274 18 : OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10275 : const char *pszName,
10276 : const char *pszValue)
10277 :
10278 : {
10279 : /* -------------------------------------------------------------------- */
10280 : /* Find the target node. */
10281 : /* -------------------------------------------------------------------- */
10282 18 : OGR_SRSNode *poNode = nullptr;
10283 :
10284 18 : if (pszTargetKey == nullptr)
10285 0 : poNode = GetRoot();
10286 : else
10287 18 : poNode = GetAttrNode(pszTargetKey);
10288 :
10289 18 : if (poNode == nullptr)
10290 0 : return OGRERR_FAILURE;
10291 :
10292 : /* -------------------------------------------------------------------- */
10293 : /* Fetch matching EXTENSION if there is one. */
10294 : /* -------------------------------------------------------------------- */
10295 139 : for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10296 : {
10297 126 : OGR_SRSNode *poChild = poNode->GetChild(i);
10298 :
10299 131 : if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10300 5 : poChild->GetChildCount() >= 2)
10301 : {
10302 5 : if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10303 : {
10304 5 : poChild->GetChild(1)->SetValue(pszValue);
10305 5 : return OGRERR_NONE;
10306 : }
10307 : }
10308 : }
10309 :
10310 : /* -------------------------------------------------------------------- */
10311 : /* Create a new EXTENSION node. */
10312 : /* -------------------------------------------------------------------- */
10313 13 : OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10314 13 : poAuthNode->AddChild(new OGR_SRSNode(pszName));
10315 13 : poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10316 :
10317 13 : poNode->AddChild(poAuthNode);
10318 :
10319 13 : return OGRERR_NONE;
10320 : }
10321 :
10322 : /************************************************************************/
10323 : /* OSRCleanup() */
10324 : /************************************************************************/
10325 :
10326 : static void CleanupSRSWGS84Mutex();
10327 :
10328 : /**
10329 : * \brief Cleanup cached SRS related memory.
10330 : *
10331 : * This function will attempt to cleanup any cache spatial reference
10332 : * related information, such as cached tables of coordinate systems.
10333 : *
10334 : * This function should not be called concurrently with any other GDAL/OGR
10335 : * function. It is meant at being called once before process termination
10336 : * (typically from the main thread). CPLCleanupTLS() might be used to clean
10337 : * thread-specific resources before thread termination.
10338 : */
10339 857 : void OSRCleanup(void)
10340 :
10341 : {
10342 857 : OGRCTDumpStatistics();
10343 857 : CSVDeaccess(nullptr);
10344 857 : CleanupSRSWGS84Mutex();
10345 857 : OSRCTCleanCache();
10346 857 : OSRCleanupTLSContext();
10347 857 : }
10348 :
10349 : /************************************************************************/
10350 : /* GetAxesCount() */
10351 : /************************************************************************/
10352 :
10353 : /**
10354 : * \brief Return the number of axis of the coordinate system of the CRS.
10355 : *
10356 : * @since GDAL 3.0
10357 : */
10358 27920 : int OGRSpatialReference::GetAxesCount() const
10359 : {
10360 27920 : int axisCount = 0;
10361 27920 : d->refreshProjObj();
10362 27920 : if (d->m_pj_crs == nullptr)
10363 : {
10364 0 : return 0;
10365 : }
10366 27920 : d->demoteFromBoundCRS();
10367 27920 : auto ctxt = d->getPROJContext();
10368 27920 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10369 : {
10370 29 : for (int i = 0;; i++)
10371 : {
10372 87 : auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10373 87 : if (!subCRS)
10374 29 : break;
10375 58 : if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10376 : {
10377 17 : auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10378 17 : if (baseCRS)
10379 : {
10380 17 : proj_destroy(subCRS);
10381 17 : subCRS = baseCRS;
10382 : }
10383 : }
10384 58 : auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10385 58 : if (cs)
10386 : {
10387 58 : axisCount += proj_cs_get_axis_count(ctxt, cs);
10388 58 : proj_destroy(cs);
10389 : }
10390 58 : proj_destroy(subCRS);
10391 58 : }
10392 : }
10393 : else
10394 : {
10395 27891 : auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10396 27891 : if (cs)
10397 : {
10398 27891 : axisCount = proj_cs_get_axis_count(ctxt, cs);
10399 27891 : proj_destroy(cs);
10400 : }
10401 : }
10402 27920 : d->undoDemoteFromBoundCRS();
10403 27920 : return axisCount;
10404 : }
10405 :
10406 : /************************************************************************/
10407 : /* OSRGetAxesCount() */
10408 : /************************************************************************/
10409 :
10410 : /**
10411 : * \brief Return the number of axis of the coordinate system of the CRS.
10412 : *
10413 : * This method is the equivalent of the C++ method
10414 : * OGRSpatialReference::GetAxesCount()
10415 : *
10416 : * @since GDAL 3.1
10417 : */
10418 6 : int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10419 :
10420 : {
10421 6 : VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10422 :
10423 6 : return ToPointer(hSRS)->GetAxesCount();
10424 : }
10425 :
10426 : /************************************************************************/
10427 : /* GetAxis() */
10428 : /************************************************************************/
10429 :
10430 : /**
10431 : * \brief Fetch the orientation of one axis.
10432 : *
10433 : * Fetches the request axis (iAxis - zero based) from the
10434 : * indicated portion of the coordinate system (pszTargetKey) which
10435 : * should be either "GEOGCS" or "PROJCS".
10436 : *
10437 : * No CPLError is issued on routine failures (such as not finding the AXIS).
10438 : *
10439 : * This method is equivalent to the C function OSRGetAxis().
10440 : *
10441 : * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10442 : * "GEOGCS").
10443 : * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10444 : * @param peOrientation location into which to place the fetch orientation, may
10445 : * be NULL.
10446 : * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10447 : * factor. May be NULL. Only set if pszTargetKey == NULL
10448 : *
10449 : * @return the name of the axis or NULL on failure.
10450 : */
10451 :
10452 5121 : const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10453 : OGRAxisOrientation *peOrientation,
10454 : double *pdfConvUnit) const
10455 :
10456 : {
10457 5121 : if (peOrientation != nullptr)
10458 5028 : *peOrientation = OAO_Other;
10459 5121 : if (pdfConvUnit != nullptr)
10460 85 : *pdfConvUnit = 0;
10461 :
10462 5121 : d->refreshProjObj();
10463 5121 : if (d->m_pj_crs == nullptr)
10464 : {
10465 1 : return nullptr;
10466 : }
10467 :
10468 5120 : pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10469 5120 : if (pszTargetKey == nullptr && iAxis <= 2)
10470 : {
10471 5120 : auto ctxt = d->getPROJContext();
10472 :
10473 5120 : int iAxisModified = iAxis;
10474 :
10475 5120 : d->demoteFromBoundCRS();
10476 :
10477 5120 : PJ *cs = nullptr;
10478 5120 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10479 : {
10480 134 : auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10481 134 : if (horizCRS)
10482 : {
10483 134 : if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10484 : {
10485 6 : auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10486 6 : if (baseCRS)
10487 : {
10488 6 : proj_destroy(horizCRS);
10489 6 : horizCRS = baseCRS;
10490 : }
10491 : }
10492 134 : cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10493 134 : proj_destroy(horizCRS);
10494 134 : if (cs)
10495 : {
10496 134 : if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10497 : {
10498 44 : iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10499 44 : proj_destroy(cs);
10500 44 : cs = nullptr;
10501 : }
10502 : }
10503 : }
10504 :
10505 134 : if (cs == nullptr)
10506 : {
10507 44 : auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10508 44 : if (vertCRS)
10509 : {
10510 44 : if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10511 : {
10512 30 : auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10513 30 : if (baseCRS)
10514 : {
10515 30 : proj_destroy(vertCRS);
10516 30 : vertCRS = baseCRS;
10517 : }
10518 : }
10519 :
10520 44 : cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10521 44 : proj_destroy(vertCRS);
10522 : }
10523 : }
10524 : }
10525 : else
10526 : {
10527 4986 : cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10528 : }
10529 :
10530 5120 : if (cs)
10531 : {
10532 5120 : const char *pszName = nullptr;
10533 5120 : const char *pszOrientation = nullptr;
10534 5120 : double dfConvFactor = 0.0;
10535 5120 : proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10536 : &pszOrientation, &dfConvFactor, nullptr,
10537 : nullptr, nullptr);
10538 :
10539 5120 : if (pdfConvUnit != nullptr)
10540 : {
10541 85 : *pdfConvUnit = dfConvFactor;
10542 : }
10543 :
10544 5120 : if (pszName && pszOrientation)
10545 : {
10546 5120 : d->m_osAxisName[iAxis] = pszName;
10547 5120 : if (peOrientation)
10548 : {
10549 5027 : if (EQUAL(pszOrientation, "NORTH"))
10550 3155 : *peOrientation = OAO_North;
10551 1872 : else if (EQUAL(pszOrientation, "EAST"))
10552 1834 : *peOrientation = OAO_East;
10553 38 : else if (EQUAL(pszOrientation, "SOUTH"))
10554 31 : *peOrientation = OAO_South;
10555 7 : else if (EQUAL(pszOrientation, "WEST"))
10556 0 : *peOrientation = OAO_West;
10557 7 : else if (EQUAL(pszOrientation, "UP"))
10558 1 : *peOrientation = OAO_Up;
10559 6 : else if (EQUAL(pszOrientation, "DOWN"))
10560 0 : *peOrientation = OAO_Down;
10561 : }
10562 5120 : proj_destroy(cs);
10563 5120 : d->undoDemoteFromBoundCRS();
10564 5120 : return d->m_osAxisName[iAxis].c_str();
10565 : }
10566 0 : proj_destroy(cs);
10567 : }
10568 0 : d->undoDemoteFromBoundCRS();
10569 : }
10570 :
10571 : /* -------------------------------------------------------------------- */
10572 : /* Find the target node. */
10573 : /* -------------------------------------------------------------------- */
10574 0 : const OGR_SRSNode *poNode = nullptr;
10575 :
10576 0 : if (pszTargetKey == nullptr)
10577 0 : poNode = GetRoot();
10578 : else
10579 0 : poNode = GetAttrNode(pszTargetKey);
10580 :
10581 0 : if (poNode == nullptr)
10582 0 : return nullptr;
10583 :
10584 : /* -------------------------------------------------------------------- */
10585 : /* Find desired child AXIS. */
10586 : /* -------------------------------------------------------------------- */
10587 0 : const OGR_SRSNode *poAxis = nullptr;
10588 0 : const int nChildCount = poNode->GetChildCount();
10589 :
10590 0 : for (int iChild = 0; iChild < nChildCount; iChild++)
10591 : {
10592 0 : const OGR_SRSNode *poChild = poNode->GetChild(iChild);
10593 :
10594 0 : if (!EQUAL(poChild->GetValue(), "AXIS"))
10595 0 : continue;
10596 :
10597 0 : if (iAxis == 0)
10598 : {
10599 0 : poAxis = poChild;
10600 0 : break;
10601 : }
10602 0 : iAxis--;
10603 : }
10604 :
10605 0 : if (poAxis == nullptr)
10606 0 : return nullptr;
10607 :
10608 0 : if (poAxis->GetChildCount() < 2)
10609 0 : return nullptr;
10610 :
10611 : /* -------------------------------------------------------------------- */
10612 : /* Extract name and orientation if possible. */
10613 : /* -------------------------------------------------------------------- */
10614 0 : if (peOrientation != nullptr)
10615 : {
10616 0 : const char *pszOrientation = poAxis->GetChild(1)->GetValue();
10617 :
10618 0 : if (EQUAL(pszOrientation, "NORTH"))
10619 0 : *peOrientation = OAO_North;
10620 0 : else if (EQUAL(pszOrientation, "EAST"))
10621 0 : *peOrientation = OAO_East;
10622 0 : else if (EQUAL(pszOrientation, "SOUTH"))
10623 0 : *peOrientation = OAO_South;
10624 0 : else if (EQUAL(pszOrientation, "WEST"))
10625 0 : *peOrientation = OAO_West;
10626 0 : else if (EQUAL(pszOrientation, "UP"))
10627 0 : *peOrientation = OAO_Up;
10628 0 : else if (EQUAL(pszOrientation, "DOWN"))
10629 0 : *peOrientation = OAO_Down;
10630 0 : else if (EQUAL(pszOrientation, "OTHER"))
10631 0 : *peOrientation = OAO_Other;
10632 : else
10633 : {
10634 0 : CPLDebug("OSR", "Unrecognized orientation value '%s'.",
10635 : pszOrientation);
10636 : }
10637 : }
10638 :
10639 0 : return poAxis->GetChild(0)->GetValue();
10640 : }
10641 :
10642 : /************************************************************************/
10643 : /* OSRGetAxis() */
10644 : /************************************************************************/
10645 :
10646 : /**
10647 : * \brief Fetch the orientation of one axis.
10648 : *
10649 : * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
10650 : */
10651 13 : const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
10652 : int iAxis, OGRAxisOrientation *peOrientation)
10653 :
10654 : {
10655 13 : VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
10656 :
10657 13 : return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
10658 : }
10659 :
10660 : /************************************************************************/
10661 : /* OSRAxisEnumToName() */
10662 : /************************************************************************/
10663 :
10664 : /**
10665 : * \brief Return the string representation for the OGRAxisOrientation
10666 : * enumeration.
10667 : *
10668 : * For example "NORTH" for OAO_North.
10669 : *
10670 : * @return an internal string
10671 : */
10672 276 : const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
10673 :
10674 : {
10675 276 : if (eOrientation == OAO_North)
10676 138 : return "NORTH";
10677 138 : if (eOrientation == OAO_East)
10678 138 : return "EAST";
10679 0 : if (eOrientation == OAO_South)
10680 0 : return "SOUTH";
10681 0 : if (eOrientation == OAO_West)
10682 0 : return "WEST";
10683 0 : if (eOrientation == OAO_Up)
10684 0 : return "UP";
10685 0 : if (eOrientation == OAO_Down)
10686 0 : return "DOWN";
10687 0 : if (eOrientation == OAO_Other)
10688 0 : return "OTHER";
10689 :
10690 0 : return "UNKNOWN";
10691 : }
10692 :
10693 : /************************************************************************/
10694 : /* SetAxes() */
10695 : /************************************************************************/
10696 :
10697 : /**
10698 : * \brief Set the axes for a coordinate system.
10699 : *
10700 : * Set the names, and orientations of the axes for either a projected
10701 : * (PROJCS) or geographic (GEOGCS) coordinate system.
10702 : *
10703 : * This method is equivalent to the C function OSRSetAxes().
10704 : *
10705 : * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
10706 : * @param pszXAxisName name of first axis, normally "Long" or "Easting".
10707 : * @param eXAxisOrientation normally OAO_East.
10708 : * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
10709 : * @param eYAxisOrientation normally OAO_North.
10710 : *
10711 : * @return OGRERR_NONE on success or an error code.
10712 : */
10713 :
10714 138 : OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
10715 : const char *pszXAxisName,
10716 : OGRAxisOrientation eXAxisOrientation,
10717 : const char *pszYAxisName,
10718 : OGRAxisOrientation eYAxisOrientation)
10719 :
10720 : {
10721 : /* -------------------------------------------------------------------- */
10722 : /* Find the target node. */
10723 : /* -------------------------------------------------------------------- */
10724 138 : OGR_SRSNode *poNode = nullptr;
10725 :
10726 138 : if (pszTargetKey == nullptr)
10727 138 : poNode = GetRoot();
10728 : else
10729 0 : poNode = GetAttrNode(pszTargetKey);
10730 :
10731 138 : if (poNode == nullptr)
10732 0 : return OGRERR_FAILURE;
10733 :
10734 : /* -------------------------------------------------------------------- */
10735 : /* Strip any existing AXIS children. */
10736 : /* -------------------------------------------------------------------- */
10737 414 : while (poNode->FindChild("AXIS") >= 0)
10738 276 : poNode->DestroyChild(poNode->FindChild("AXIS"));
10739 :
10740 : /* -------------------------------------------------------------------- */
10741 : /* Insert desired axes */
10742 : /* -------------------------------------------------------------------- */
10743 138 : OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
10744 :
10745 138 : poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
10746 138 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
10747 :
10748 138 : poNode->AddChild(poAxis);
10749 :
10750 138 : poAxis = new OGR_SRSNode("AXIS");
10751 :
10752 138 : poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
10753 138 : poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
10754 :
10755 138 : poNode->AddChild(poAxis);
10756 :
10757 138 : return OGRERR_NONE;
10758 : }
10759 :
10760 : /************************************************************************/
10761 : /* OSRSetAxes() */
10762 : /************************************************************************/
10763 : /**
10764 : * \brief Set the axes for a coordinate system.
10765 : *
10766 : * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
10767 : */
10768 0 : OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
10769 : const char *pszXAxisName,
10770 : OGRAxisOrientation eXAxisOrientation,
10771 : const char *pszYAxisName,
10772 : OGRAxisOrientation eYAxisOrientation)
10773 : {
10774 0 : VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
10775 :
10776 0 : return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
10777 : eXAxisOrientation, pszYAxisName,
10778 0 : eYAxisOrientation);
10779 : }
10780 :
10781 : #ifdef HAVE_MITAB
10782 : char CPL_DLL *MITABSpatialRef2CoordSys(const OGRSpatialReference *);
10783 : OGRSpatialReference CPL_DLL *MITABCoordSys2SpatialRef(const char *);
10784 : #endif
10785 :
10786 : /************************************************************************/
10787 : /* OSRExportToMICoordSys() */
10788 : /************************************************************************/
10789 : /**
10790 : * \brief Export coordinate system in Mapinfo style CoordSys format.
10791 : *
10792 : * This method is the equivalent of the C++ method
10793 : * OGRSpatialReference::exportToMICoordSys
10794 : */
10795 5 : OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
10796 :
10797 : {
10798 5 : VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
10799 :
10800 5 : *ppszReturn = nullptr;
10801 :
10802 5 : return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
10803 : }
10804 :
10805 : /************************************************************************/
10806 : /* exportToMICoordSys() */
10807 : /************************************************************************/
10808 :
10809 : /**
10810 : * \brief Export coordinate system in Mapinfo style CoordSys format.
10811 : *
10812 : * Note that the returned WKT string should be freed with
10813 : * CPLFree() when no longer needed. It is the responsibility of the caller.
10814 : *
10815 : * This method is the same as the C function OSRExportToMICoordSys().
10816 : *
10817 : * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
10818 : * definition will be assigned.
10819 : *
10820 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
10821 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
10822 : */
10823 :
10824 7 : OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
10825 :
10826 : {
10827 : #ifdef HAVE_MITAB
10828 7 : *ppszResult = MITABSpatialRef2CoordSys(this);
10829 7 : if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
10830 7 : return OGRERR_NONE;
10831 :
10832 0 : return OGRERR_FAILURE;
10833 : #else
10834 : CPLError(CE_Failure, CPLE_NotSupported,
10835 : "MITAB not available, CoordSys support disabled.");
10836 :
10837 : return OGRERR_UNSUPPORTED_OPERATION;
10838 : #endif
10839 : }
10840 :
10841 : /************************************************************************/
10842 : /* OSRImportFromMICoordSys() */
10843 : /************************************************************************/
10844 : /**
10845 : * \brief Import Mapinfo style CoordSys definition.
10846 : *
10847 : * This method is the equivalent of the C++ method
10848 : * OGRSpatialReference::importFromMICoordSys
10849 : */
10850 :
10851 3 : OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
10852 : const char *pszCoordSys)
10853 :
10854 : {
10855 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
10856 :
10857 3 : return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
10858 : }
10859 :
10860 : /************************************************************************/
10861 : /* importFromMICoordSys() */
10862 : /************************************************************************/
10863 :
10864 : /**
10865 : * \brief Import Mapinfo style CoordSys definition.
10866 : *
10867 : * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
10868 : * definition string.
10869 : *
10870 : * This method is the equivalent of the C function OSRImportFromMICoordSys().
10871 : *
10872 : * @param pszCoordSys Mapinfo style CoordSys definition string.
10873 : *
10874 : * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
10875 : * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
10876 : */
10877 :
10878 17 : OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
10879 :
10880 : {
10881 : #ifdef HAVE_MITAB
10882 17 : OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
10883 :
10884 17 : if (poResult == nullptr)
10885 0 : return OGRERR_FAILURE;
10886 :
10887 17 : *this = *poResult;
10888 17 : delete poResult;
10889 :
10890 17 : return OGRERR_NONE;
10891 : #else
10892 : CPLError(CE_Failure, CPLE_NotSupported,
10893 : "MITAB not available, CoordSys support disabled.");
10894 :
10895 : return OGRERR_UNSUPPORTED_OPERATION;
10896 : #endif
10897 : }
10898 :
10899 : /************************************************************************/
10900 : /* OSRCalcInvFlattening() */
10901 : /************************************************************************/
10902 :
10903 : /**
10904 : * \brief Compute inverse flattening from semi-major and semi-minor axis
10905 : *
10906 : * @param dfSemiMajor Semi-major axis length.
10907 : * @param dfSemiMinor Semi-minor axis length.
10908 : *
10909 : * @return inverse flattening, or 0 if both axis are equal.
10910 : * @since GDAL 2.0
10911 : */
10912 :
10913 5311 : double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
10914 : {
10915 5311 : if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
10916 26 : return 0;
10917 5285 : if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
10918 : {
10919 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10920 : "OSRCalcInvFlattening(): Wrong input values");
10921 0 : return 0;
10922 : }
10923 :
10924 5285 : return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
10925 : }
10926 :
10927 : /************************************************************************/
10928 : /* OSRCalcInvFlattening() */
10929 : /************************************************************************/
10930 :
10931 : /**
10932 : * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
10933 : *
10934 : * @param dfSemiMajor Semi-major axis length.
10935 : * @param dfInvFlattening Inverse flattening or 0 for sphere.
10936 : *
10937 : * @return semi-minor axis
10938 : * @since GDAL 2.0
10939 : */
10940 :
10941 634 : double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
10942 : double dfInvFlattening)
10943 : {
10944 634 : if (fabs(dfInvFlattening) < 0.000000000001)
10945 101 : return dfSemiMajor;
10946 533 : if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
10947 : {
10948 0 : CPLError(CE_Failure, CPLE_IllegalArg,
10949 : "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
10950 0 : return dfSemiMajor;
10951 : }
10952 :
10953 533 : return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
10954 : }
10955 :
10956 : /************************************************************************/
10957 : /* GetWGS84SRS() */
10958 : /************************************************************************/
10959 :
10960 : static OGRSpatialReference *poSRSWGS84 = nullptr;
10961 : static CPLMutex *hMutex = nullptr;
10962 :
10963 : /**
10964 : * \brief Returns an instance of a SRS object with WGS84 WKT.
10965 : *
10966 : * Note: the instance will have
10967 : * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
10968 : *
10969 : * The reference counter of the returned object is not increased by this
10970 : * operation.
10971 : *
10972 : * @return instance.
10973 : * @since GDAL 2.0
10974 : */
10975 :
10976 816 : OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
10977 : {
10978 816 : CPLMutexHolderD(&hMutex);
10979 816 : if (poSRSWGS84 == nullptr)
10980 : {
10981 4 : poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
10982 4 : poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
10983 : }
10984 1632 : return poSRSWGS84;
10985 : }
10986 :
10987 : /************************************************************************/
10988 : /* CleanupSRSWGS84Mutex() */
10989 : /************************************************************************/
10990 :
10991 857 : static void CleanupSRSWGS84Mutex()
10992 : {
10993 857 : if (hMutex != nullptr)
10994 : {
10995 2 : poSRSWGS84->Release();
10996 2 : poSRSWGS84 = nullptr;
10997 2 : CPLDestroyMutex(hMutex);
10998 2 : hMutex = nullptr;
10999 : }
11000 857 : }
11001 :
11002 : /************************************************************************/
11003 : /* OSRImportFromProj4() */
11004 : /************************************************************************/
11005 : /**
11006 : * \brief Import PROJ coordinate string.
11007 : *
11008 : * This function is the same as OGRSpatialReference::importFromProj4().
11009 : */
11010 186 : OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11011 :
11012 : {
11013 186 : VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11014 :
11015 186 : return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11016 : }
11017 :
11018 : /************************************************************************/
11019 : /* importFromProj4() */
11020 : /************************************************************************/
11021 :
11022 : /**
11023 : * \brief Import PROJ coordinate string.
11024 : *
11025 : * The OGRSpatialReference is initialized from the passed PROJs style
11026 : * coordinate system string.
11027 : *
11028 : * Example:
11029 : * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11030 : *
11031 : * It is also possible to import "+init=epsg:n" style definitions. Those are
11032 : * a legacy syntax that should be avoided in the future. In particular they will
11033 : * result in CRS objects whose axis order might not correspond to the official
11034 : * EPSG axis order.
11035 : *
11036 : * This method is the equivalent of the C function OSRImportFromProj4().
11037 : *
11038 : * @param pszProj4 the PROJ style string.
11039 : *
11040 : * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11041 : */
11042 :
11043 542 : OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11044 :
11045 : {
11046 542 : if (strlen(pszProj4) >= 10000)
11047 : {
11048 1 : CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11049 1 : return OGRERR_CORRUPT_DATA;
11050 : }
11051 :
11052 : /* -------------------------------------------------------------------- */
11053 : /* Clear any existing definition. */
11054 : /* -------------------------------------------------------------------- */
11055 541 : Clear();
11056 :
11057 541 : CPLString osProj4(pszProj4);
11058 541 : if (osProj4.find("type=crs") == std::string::npos)
11059 : {
11060 532 : osProj4 += " +type=crs";
11061 : }
11062 :
11063 543 : if (osProj4.find("+init=epsg:") != std::string::npos &&
11064 2 : getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11065 : {
11066 : static bool bHasWarned = false;
11067 2 : if (!bHasWarned)
11068 : {
11069 1 : CPLError(CE_Warning, CPLE_AppDefined,
11070 : "+init=epsg:XXXX syntax is deprecated. It might return "
11071 : "a CRS with a non-EPSG compliant axis order.");
11072 1 : bHasWarned = true;
11073 : }
11074 : }
11075 541 : proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11076 541 : d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11077 541 : proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11078 541 : return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11079 : }
11080 :
11081 : /************************************************************************/
11082 : /* OSRExportToProj4() */
11083 : /************************************************************************/
11084 : /**
11085 : * \brief Export coordinate system in PROJ.4 legacy format.
11086 : *
11087 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11088 : * PROJ >= 6 is significantly different from earlier versions. In particular
11089 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11090 : * will be missing most of the time. PROJ strings to encode CRS should be
11091 : * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11092 : * is the recommended way.
11093 : *
11094 : * This function is the same as OGRSpatialReference::exportToProj4().
11095 : */
11096 442 : OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11097 : char **ppszReturn)
11098 :
11099 : {
11100 442 : VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11101 :
11102 442 : *ppszReturn = nullptr;
11103 :
11104 442 : return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11105 : }
11106 :
11107 : /************************************************************************/
11108 : /* exportToProj4() */
11109 : /************************************************************************/
11110 :
11111 : /**
11112 : * \brief Export coordinate system in PROJ.4 legacy format.
11113 : *
11114 : * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
11115 : * PROJ >= 6 is significantly different from earlier versions. In particular
11116 : * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11117 : * will be missing most of the time. PROJ strings to encode CRS should be
11118 : * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11119 : * representation is the recommended way.
11120 : *
11121 : * Converts the loaded coordinate reference system into PROJ format
11122 : * to the extent possible. The string returned in ppszProj4 should be
11123 : * deallocated by the caller with CPLFree() when no longer needed.
11124 : *
11125 : * LOCAL_CS coordinate systems are not translatable. An empty string
11126 : * will be returned along with OGRERR_NONE.
11127 : *
11128 : * Special processing for Transverse Mercator:
11129 : * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11130 : * set to YES, the PROJ definition built from the SRS will use the +approx flag
11131 : * for the tmerc and utm projection methods, rather than the more accurate
11132 : * method.
11133 : *
11134 : * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11135 : * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11136 : * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11137 : * added. This automatic addition may be disabled by setting the
11138 : * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11139 : *
11140 : * This method is the equivalent of the C function OSRExportToProj4().
11141 : *
11142 : * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11143 : * will be assigned.
11144 : *
11145 : * @return OGRERR_NONE on success or an error code on failure.
11146 : */
11147 :
11148 1386 : OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11149 :
11150 : {
11151 : // In the past calling this method was thread-safe, even if we never
11152 : // guaranteed it. Now proj_as_proj_string() will cache the result
11153 : // internally, so this is no longer thread-safe.
11154 2772 : std::lock_guard<std::mutex> oLock(d->m_mutex);
11155 :
11156 1386 : d->refreshProjObj();
11157 1386 : if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11158 : {
11159 24 : *ppszProj4 = CPLStrdup("");
11160 24 : return OGRERR_FAILURE;
11161 : }
11162 :
11163 : // OSR_USE_ETMERC is here just for legacy
11164 1362 : bool bForceApproxTMerc = false;
11165 1362 : const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11166 1362 : if (pszUseETMERC && pszUseETMERC[0])
11167 : {
11168 : static bool bHasWarned = false;
11169 0 : if (!bHasWarned)
11170 : {
11171 0 : CPLError(CE_Warning, CPLE_AppDefined,
11172 : "OSR_USE_ETMERC is a legacy configuration option, which "
11173 : "now has only effect when set to NO (YES is the default). "
11174 : "Use OSR_USE_APPROX_TMERC=YES instead");
11175 0 : bHasWarned = true;
11176 : }
11177 0 : bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11178 : }
11179 : else
11180 : {
11181 : const char *pszUseApproxTMERC =
11182 1362 : CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11183 1362 : if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11184 : {
11185 2 : bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11186 : }
11187 : }
11188 1362 : const char *options[] = {
11189 1362 : bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11190 :
11191 1362 : const char *projString = proj_as_proj_string(
11192 1362 : d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11193 :
11194 1362 : PJ *boundCRS = nullptr;
11195 2720 : if (projString &&
11196 1358 : (strstr(projString, "+datum=") == nullptr ||
11197 2730 : d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11198 521 : CPLTestBool(
11199 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11200 : {
11201 521 : boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11202 521 : d->getPROJContext(), d->m_pj_crs, true,
11203 521 : strstr(projString, "+datum=") == nullptr);
11204 521 : if (boundCRS)
11205 : {
11206 209 : projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11207 : PJ_PROJ_4, options);
11208 : }
11209 : }
11210 :
11211 1362 : if (projString == nullptr)
11212 : {
11213 4 : *ppszProj4 = CPLStrdup("");
11214 4 : proj_destroy(boundCRS);
11215 4 : return OGRERR_FAILURE;
11216 : }
11217 1358 : *ppszProj4 = CPLStrdup(projString);
11218 1358 : proj_destroy(boundCRS);
11219 1358 : char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11220 1358 : if (pszTypeCrs)
11221 1358 : *pszTypeCrs = '\0';
11222 1358 : return OGRERR_NONE;
11223 : }
11224 :
11225 : /************************************************************************/
11226 : /* morphToESRI() */
11227 : /************************************************************************/
11228 : /**
11229 : * \brief Convert in place to ESRI WKT format.
11230 : *
11231 : * The value nodes of this coordinate system are modified in various manners
11232 : * more closely map onto the ESRI concept of WKT format. This includes
11233 : * renaming a variety of projections and arguments, and stripping out
11234 : * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11235 : *
11236 : * \note Since GDAL 3.0, this function has only user-visible effects at
11237 : * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11238 : * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11239 : *
11240 : * This does the same as the C function OSRMorphToESRI().
11241 : *
11242 : * @return OGRERR_NONE unless something goes badly wrong.
11243 : * @deprecated
11244 : */
11245 :
11246 268 : OGRErr OGRSpatialReference::morphToESRI()
11247 :
11248 : {
11249 268 : d->refreshProjObj();
11250 268 : d->setMorphToESRI(true);
11251 :
11252 268 : return OGRERR_NONE;
11253 : }
11254 :
11255 : /************************************************************************/
11256 : /* OSRMorphToESRI() */
11257 : /************************************************************************/
11258 :
11259 : /**
11260 : * \brief Convert in place to ESRI WKT format.
11261 : *
11262 : * This function is the same as the C++ method
11263 : * OGRSpatialReference::morphToESRI().
11264 : */
11265 103 : OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11266 :
11267 : {
11268 103 : VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11269 :
11270 103 : return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11271 : }
11272 :
11273 : /************************************************************************/
11274 : /* morphFromESRI() */
11275 : /************************************************************************/
11276 :
11277 : /**
11278 : * \brief Convert in place from ESRI WKT format.
11279 : *
11280 : * The value notes of this coordinate system are modified in various manners
11281 : * to adhere more closely to the WKT standard. This mostly involves
11282 : * translating a variety of ESRI names for projections, arguments and
11283 : * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11284 : * translation of EPSG to WKT for the CT specification.
11285 : *
11286 : * \note Since GDAL 3.0, this function is essentially a no-operation, since
11287 : * morphing from ESRI is automatically done by importFromWkt(). Its only
11288 : * effect is to undo the effect of a potential prior call to morphToESRI().
11289 : *
11290 : * This does the same as the C function OSRMorphFromESRI().
11291 : *
11292 : * @return OGRERR_NONE unless something goes badly wrong.
11293 : * @deprecated
11294 : */
11295 :
11296 27 : OGRErr OGRSpatialReference::morphFromESRI()
11297 :
11298 : {
11299 27 : d->refreshProjObj();
11300 27 : d->setMorphToESRI(false);
11301 :
11302 27 : return OGRERR_NONE;
11303 : }
11304 :
11305 : /************************************************************************/
11306 : /* OSRMorphFromESRI() */
11307 : /************************************************************************/
11308 :
11309 : /**
11310 : * \brief Convert in place from ESRI WKT format.
11311 : *
11312 : * This function is the same as the C++ method
11313 : * OGRSpatialReference::morphFromESRI().
11314 : */
11315 20 : OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11316 :
11317 : {
11318 20 : VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11319 :
11320 20 : return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11321 : }
11322 :
11323 : /************************************************************************/
11324 : /* FindMatches() */
11325 : /************************************************************************/
11326 :
11327 : /**
11328 : * \brief Try to identify a match between the passed SRS and a related SRS
11329 : * in a catalog.
11330 : *
11331 : * Matching may be partial, or may fail.
11332 : * Returned entries will be sorted by decreasing match confidence (first
11333 : * entry has the highest match confidence).
11334 : *
11335 : * The exact way matching is done may change in future versions. Starting with
11336 : * GDAL 3.0, it relies on PROJ' proj_identify() function.
11337 : *
11338 : * This method is the same as OSRFindMatches().
11339 : *
11340 : * @param papszOptions NULL terminated list of options or NULL
11341 : * @param pnEntries Output parameter. Number of values in the returned array.
11342 : * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11343 : * will be allocated to an array of *pnEntries whose values between 0 and 100
11344 : * indicate the confidence in the match. 100 is the highest confidence level.
11345 : * The array must be freed with CPLFree().
11346 : *
11347 : * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11348 : * with OSRFreeSRSArray()
11349 : *
11350 : * @since GDAL 2.3
11351 : *
11352 : * @see OGRSpatialReference::FindBestMatch()
11353 : */
11354 : OGRSpatialReferenceH *
11355 998 : OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11356 : int **ppanMatchConfidence) const
11357 : {
11358 998 : CPL_IGNORE_RET_VAL(papszOptions);
11359 :
11360 998 : if (pnEntries)
11361 998 : *pnEntries = 0;
11362 998 : if (ppanMatchConfidence)
11363 998 : *ppanMatchConfidence = nullptr;
11364 :
11365 998 : d->refreshProjObj();
11366 998 : if (!d->m_pj_crs)
11367 0 : return nullptr;
11368 :
11369 998 : int *panConfidence = nullptr;
11370 998 : auto list = proj_identify(d->getPROJContext(), d->m_pj_crs, nullptr,
11371 : nullptr, &panConfidence);
11372 998 : if (!list)
11373 0 : return nullptr;
11374 :
11375 998 : const int nMatches = proj_list_get_count(list);
11376 :
11377 998 : if (pnEntries)
11378 998 : *pnEntries = static_cast<int>(nMatches);
11379 : OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11380 998 : CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11381 998 : if (ppanMatchConfidence)
11382 : {
11383 998 : *ppanMatchConfidence =
11384 998 : static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11385 : }
11386 3878 : for (int i = 0; i < nMatches; i++)
11387 : {
11388 2880 : PJ *obj = proj_list_get(d->getPROJContext(), list, i);
11389 2880 : CPLAssert(obj);
11390 2880 : OGRSpatialReference *poSRS = new OGRSpatialReference();
11391 2880 : poSRS->d->setPjCRS(obj);
11392 2880 : pahRet[i] = ToHandle(poSRS);
11393 2880 : if (ppanMatchConfidence)
11394 2880 : (*ppanMatchConfidence)[i] = panConfidence[i];
11395 : }
11396 998 : pahRet[nMatches] = nullptr;
11397 998 : proj_list_destroy(list);
11398 998 : proj_int_list_destroy(panConfidence);
11399 :
11400 998 : return pahRet;
11401 : }
11402 :
11403 : /************************************************************************/
11404 : /* importFromEPSGA() */
11405 : /************************************************************************/
11406 :
11407 : /**
11408 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11409 : * code.
11410 : *
11411 : * This method will initialize the spatial reference based on the
11412 : * passed in EPSG CRS code found in the PROJ database.
11413 : *
11414 : * Since GDAL 3.0, this method is identical to importFromEPSG().
11415 : *
11416 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11417 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11418 : * such method available for the CRS. This behavior might not always be
11419 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11420 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11421 : * The AddGuessedTOWGS84() method can also be used for that purpose.
11422 : *
11423 : * The method will also by default substitute a deprecated EPSG code by its
11424 : * non-deprecated replacement. If this behavior is not desired, the
11425 : * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11426 : *
11427 : * This method is the same as the C function OSRImportFromEPSGA().
11428 : *
11429 : * @param nCode a CRS code.
11430 : *
11431 : * @return OGRERR_NONE on success, or an error code on failure.
11432 : */
11433 :
11434 30809 : OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11435 :
11436 : {
11437 30809 : Clear();
11438 :
11439 : const char *pszUseNonDeprecated =
11440 30809 : CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11441 : const bool bUseNonDeprecated =
11442 30809 : CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11443 30809 : const bool bAddTOWGS84 = CPLTestBool(
11444 : CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11445 30809 : auto tlsCache = OSRGetProjTLSCache();
11446 30809 : if (tlsCache)
11447 : {
11448 : auto cachedObj =
11449 30809 : tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11450 30809 : if (cachedObj)
11451 : {
11452 22988 : d->setPjCRS(cachedObj);
11453 22988 : return OGRERR_NONE;
11454 : }
11455 : }
11456 :
11457 15641 : CPLString osCode;
11458 7820 : osCode.Printf("%d", nCode);
11459 : auto obj =
11460 7821 : proj_create_from_database(d->getPROJContext(), "EPSG", osCode.c_str(),
11461 : PJ_CATEGORY_CRS, true, nullptr);
11462 7821 : if (!obj)
11463 : {
11464 20 : return OGRERR_FAILURE;
11465 : }
11466 :
11467 7801 : if (bUseNonDeprecated && proj_is_deprecated(obj))
11468 : {
11469 411 : auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11470 411 : if (list)
11471 : {
11472 411 : const auto count = proj_list_get_count(list);
11473 411 : if (count == 1)
11474 : {
11475 : auto nonDeprecated =
11476 360 : proj_list_get(d->getPROJContext(), list, 0);
11477 360 : if (nonDeprecated)
11478 : {
11479 360 : if (pszUseNonDeprecated == nullptr)
11480 : {
11481 : const char *pszNewAuth =
11482 360 : proj_get_id_auth_name(nonDeprecated, 0);
11483 : const char *pszNewCode =
11484 360 : proj_get_id_code(nonDeprecated, 0);
11485 360 : CPLError(CE_Warning, CPLE_AppDefined,
11486 : "CRS EPSG:%d is deprecated. "
11487 : "Its non-deprecated replacement %s:%s "
11488 : "will be used instead. "
11489 : "To use the original CRS, set the "
11490 : "OSR_USE_NON_DEPRECATED "
11491 : "configuration option to NO.",
11492 : nCode, pszNewAuth ? pszNewAuth : "(null)",
11493 : pszNewCode ? pszNewCode : "(null)");
11494 : }
11495 360 : proj_destroy(obj);
11496 360 : obj = nonDeprecated;
11497 : }
11498 : }
11499 : }
11500 411 : proj_list_destroy(list);
11501 : }
11502 :
11503 7801 : if (bAddTOWGS84)
11504 : {
11505 1 : auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
11506 : obj, nullptr);
11507 1 : if (boundCRS)
11508 : {
11509 1 : proj_destroy(obj);
11510 1 : obj = boundCRS;
11511 : }
11512 : }
11513 :
11514 7801 : d->setPjCRS(obj);
11515 :
11516 7800 : if (tlsCache)
11517 : {
11518 7800 : tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
11519 : obj);
11520 : }
11521 :
11522 7800 : return OGRERR_NONE;
11523 : }
11524 :
11525 : /************************************************************************/
11526 : /* AddGuessedTOWGS84() */
11527 : /************************************************************************/
11528 :
11529 : /**
11530 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
11531 : * to WGS84.
11532 : *
11533 : * This method try to attach a 3-parameter or 7-parameter Helmert transformation
11534 : * to WGS84 when there is one and only one such method available for the CRS.
11535 : * Note: this is more restrictive to how GDAL < 3 worked.
11536 : *
11537 : * This method is the same as the C function OSRAddGuessedTOWGS84().
11538 : *
11539 : * @return OGRERR_NONE on success, or an error code on failure (the CRS has
11540 : * already a transformation to WGS84 or none matching could be found).
11541 : *
11542 : * @since GDAL 3.0.3
11543 : */
11544 18 : OGRErr OGRSpatialReference::AddGuessedTOWGS84()
11545 : {
11546 18 : d->refreshProjObj();
11547 18 : if (!d->m_pj_crs)
11548 0 : return OGRERR_FAILURE;
11549 18 : auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11550 18 : d->getPROJContext(), d->m_pj_crs, false, true);
11551 18 : if (!boundCRS)
11552 : {
11553 0 : return OGRERR_FAILURE;
11554 : }
11555 18 : d->setPjCRS(boundCRS);
11556 18 : return OGRERR_NONE;
11557 : }
11558 :
11559 : /************************************************************************/
11560 : /* OSRImportFromEPSGA() */
11561 : /************************************************************************/
11562 :
11563 : /**
11564 : * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
11565 : * to WGS84.
11566 : *
11567 : * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
11568 : *
11569 : * @since GDAL 3.0.3
11570 : */
11571 :
11572 2 : OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
11573 :
11574 : {
11575 2 : VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
11576 :
11577 2 : return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
11578 : }
11579 :
11580 : /************************************************************************/
11581 : /* OSRImportFromEPSGA() */
11582 : /************************************************************************/
11583 :
11584 : /**
11585 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11586 : * code.
11587 : *
11588 : * This function is the same as OGRSpatialReference::importFromEPSGA().
11589 : */
11590 :
11591 3 : OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
11592 :
11593 : {
11594 3 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
11595 :
11596 3 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
11597 : }
11598 :
11599 : /************************************************************************/
11600 : /* importFromEPSG() */
11601 : /************************************************************************/
11602 :
11603 : /**
11604 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11605 : * code.
11606 : *
11607 : * This method will initialize the spatial reference based on the
11608 : * passed in EPSG CRS code found in the PROJ database.
11609 : *
11610 : * This method is the same as the C function OSRImportFromEPSG().
11611 : *
11612 : * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11613 : * 7-parameter Helmert transformation to WGS84 when there is one and only one
11614 : * such method available for the CRS. This behavior might not always be
11615 : * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11616 : * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11617 : *
11618 : * @param nCode a GCS or PCS code from the horizontal coordinate system table.
11619 : *
11620 : * @return OGRERR_NONE on success, or an error code on failure.
11621 : */
11622 :
11623 29403 : OGRErr OGRSpatialReference::importFromEPSG(int nCode)
11624 :
11625 : {
11626 29403 : return importFromEPSGA(nCode);
11627 : }
11628 :
11629 : /************************************************************************/
11630 : /* OSRImportFromEPSG() */
11631 : /************************************************************************/
11632 :
11633 : /**
11634 : * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS
11635 : * code.
11636 : *
11637 : * This function is the same as OGRSpatialReference::importFromEPSG().
11638 : */
11639 :
11640 1327 : OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
11641 :
11642 : {
11643 1327 : VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
11644 :
11645 1327 : return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
11646 : }
11647 :
11648 : /************************************************************************/
11649 : /* EPSGTreatsAsLatLong() */
11650 : /************************************************************************/
11651 :
11652 : /**
11653 : * \brief This method returns TRUE if EPSG feels this geographic coordinate
11654 : * system should be treated as having lat/long coordinate ordering.
11655 : *
11656 : * Currently this returns TRUE for all geographic coordinate systems
11657 : * with an EPSG code set, and axes set defining it as lat, long.
11658 : *
11659 : * \note Important change of behavior since GDAL 3.0. In previous versions,
11660 : * geographic CRS imported with importFromEPSG() would cause this method to
11661 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
11662 : * is now equivalent to importFromEPSGA().
11663 : *
11664 : * FALSE will be returned for all coordinate systems that are not geographic,
11665 : * or that do not have an EPSG code set.
11666 : *
11667 : * This method is the same as the C function OSREPSGTreatsAsLatLong().
11668 : *
11669 : * @return TRUE or FALSE.
11670 : */
11671 :
11672 677 : int OGRSpatialReference::EPSGTreatsAsLatLong() const
11673 :
11674 : {
11675 677 : if (!IsGeographic())
11676 536 : return FALSE;
11677 :
11678 141 : d->demoteFromBoundCRS();
11679 141 : const char *pszAuth = proj_get_id_auth_name(d->m_pj_crs, 0);
11680 141 : if (pszAuth == nullptr || !EQUAL(pszAuth, "EPSG"))
11681 : {
11682 25 : d->undoDemoteFromBoundCRS();
11683 25 : return FALSE;
11684 : }
11685 :
11686 116 : bool ret = false;
11687 116 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11688 : {
11689 : auto horizCRS =
11690 3 : proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
11691 3 : if (horizCRS)
11692 : {
11693 : auto cs =
11694 3 : proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
11695 3 : if (cs)
11696 : {
11697 3 : const char *pszDirection = nullptr;
11698 3 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
11699 : nullptr, &pszDirection, nullptr,
11700 3 : nullptr, nullptr, nullptr))
11701 : {
11702 3 : if (EQUAL(pszDirection, "north"))
11703 : {
11704 3 : ret = true;
11705 : }
11706 : }
11707 :
11708 3 : proj_destroy(cs);
11709 : }
11710 :
11711 3 : proj_destroy(horizCRS);
11712 : }
11713 : }
11714 : else
11715 : {
11716 : auto cs =
11717 113 : proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
11718 113 : if (cs)
11719 : {
11720 113 : const char *pszDirection = nullptr;
11721 113 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
11722 : nullptr, &pszDirection, nullptr, nullptr,
11723 113 : nullptr, nullptr))
11724 : {
11725 113 : if (EQUAL(pszDirection, "north"))
11726 : {
11727 113 : ret = true;
11728 : }
11729 : }
11730 :
11731 113 : proj_destroy(cs);
11732 : }
11733 : }
11734 116 : d->undoDemoteFromBoundCRS();
11735 :
11736 116 : return ret;
11737 : }
11738 :
11739 : /************************************************************************/
11740 : /* OSREPSGTreatsAsLatLong() */
11741 : /************************************************************************/
11742 :
11743 : /**
11744 : * \brief This function returns TRUE if EPSG feels this geographic coordinate
11745 : * system should be treated as having lat/long coordinate ordering.
11746 : *
11747 : * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
11748 : */
11749 :
11750 155 : int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
11751 :
11752 : {
11753 155 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
11754 :
11755 155 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
11756 : }
11757 :
11758 : /************************************************************************/
11759 : /* EPSGTreatsAsNorthingEasting() */
11760 : /************************************************************************/
11761 :
11762 : /**
11763 : * \brief This method returns TRUE if EPSG feels this projected coordinate
11764 : * system should be treated as having northing/easting coordinate ordering.
11765 : *
11766 : * Currently this returns TRUE for all projected coordinate systems
11767 : * with an EPSG code set, and axes set defining it as northing, easting.
11768 : *
11769 : * \note Important change of behavior since GDAL 3.0. In previous versions,
11770 : * projected CRS with northing, easting axis order imported with
11771 : * importFromEPSG() would cause this method to
11772 : * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
11773 : * is now equivalent to importFromEPSGA().
11774 : *
11775 : * FALSE will be returned for all coordinate systems that are not projected,
11776 : * or that do not have an EPSG code set.
11777 : *
11778 : * This method is the same as the C function EPSGTreatsAsNorthingEasting().
11779 : *
11780 : * @return TRUE or FALSE.
11781 : *
11782 : * @since OGR 1.10.0
11783 : */
11784 :
11785 565 : int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
11786 :
11787 : {
11788 565 : if (!IsProjected())
11789 21 : return FALSE;
11790 :
11791 544 : d->demoteFromBoundCRS();
11792 : PJ *projCRS;
11793 544 : const auto ctxt = d->getPROJContext();
11794 544 : if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11795 : {
11796 2 : projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11797 2 : if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
11798 : {
11799 2 : d->undoDemoteFromBoundCRS();
11800 2 : proj_destroy(projCRS);
11801 2 : return FALSE;
11802 : }
11803 : }
11804 : else
11805 : {
11806 542 : projCRS = proj_clone(ctxt, d->m_pj_crs);
11807 : }
11808 542 : const char *pszAuth = proj_get_id_auth_name(projCRS, 0);
11809 542 : if (pszAuth == nullptr || !EQUAL(pszAuth, "EPSG"))
11810 : {
11811 0 : d->undoDemoteFromBoundCRS();
11812 0 : proj_destroy(projCRS);
11813 0 : return FALSE;
11814 : }
11815 :
11816 542 : bool ret = false;
11817 542 : auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
11818 542 : proj_destroy(projCRS);
11819 542 : d->undoDemoteFromBoundCRS();
11820 :
11821 542 : if (cs)
11822 : {
11823 542 : ret = isNorthEastAxisOrder(ctxt, cs);
11824 542 : proj_destroy(cs);
11825 : }
11826 :
11827 542 : return ret;
11828 : }
11829 :
11830 : /************************************************************************/
11831 : /* OSREPSGTreatsAsNorthingEasting() */
11832 : /************************************************************************/
11833 :
11834 : /**
11835 : * \brief This function returns TRUE if EPSG feels this projected coordinate
11836 : * system should be treated as having northing/easting coordinate ordering.
11837 : *
11838 : * This function is the same as
11839 : * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
11840 : *
11841 : * @since OGR 1.10.0
11842 : */
11843 :
11844 160 : int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
11845 :
11846 : {
11847 160 : VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
11848 :
11849 160 : return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
11850 : }
11851 :
11852 : /************************************************************************/
11853 : /* ImportFromESRIWisconsinWKT() */
11854 : /* */
11855 : /* Search a ESRI State Plane WKT and import it. */
11856 : /************************************************************************/
11857 :
11858 : // This is only used by the HFA driver and somewhat dubious we really need that
11859 : // Coming from an old ESRI merge
11860 :
11861 1 : OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
11862 : double centralMeridian,
11863 : double latOfOrigin,
11864 : const char *unitsName,
11865 : const char *crsName)
11866 : {
11867 1 : if (centralMeridian < -93 || centralMeridian > -87)
11868 0 : return OGRERR_FAILURE;
11869 1 : if (latOfOrigin < 40 || latOfOrigin > 47)
11870 0 : return OGRERR_FAILURE;
11871 :
11872 : // If the CS name is known.
11873 1 : if (!prjName && !unitsName && crsName)
11874 : {
11875 0 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
11876 0 : PJ_OBJ_LIST *list = proj_create_from_name(
11877 : d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
11878 0 : if (list)
11879 : {
11880 0 : if (proj_list_get_count(list) == 1)
11881 : {
11882 0 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
11883 0 : if (crs)
11884 : {
11885 0 : Clear();
11886 0 : d->setPjCRS(crs);
11887 0 : proj_list_destroy(list);
11888 0 : return OGRERR_NONE;
11889 : }
11890 : }
11891 0 : proj_list_destroy(list);
11892 : }
11893 0 : return OGRERR_FAILURE;
11894 : }
11895 :
11896 1 : if (prjName == nullptr || unitsName == nullptr)
11897 : {
11898 0 : return OGRERR_FAILURE;
11899 : }
11900 :
11901 1 : const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
11902 1 : PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
11903 : "NAD_1983_HARN_WISCRS_", &type, 1,
11904 : true, 0, nullptr);
11905 1 : if (list)
11906 : {
11907 1 : const auto listSize = proj_list_get_count(list);
11908 8 : for (int i = 0; i < listSize; i++)
11909 : {
11910 8 : auto crs = proj_list_get(d->getPROJContext(), list, i);
11911 8 : if (!crs)
11912 : {
11913 7 : continue;
11914 : }
11915 :
11916 8 : auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
11917 8 : if (!conv)
11918 : {
11919 0 : proj_destroy(crs);
11920 0 : continue;
11921 : }
11922 8 : const char *pszMethodCode = nullptr;
11923 8 : proj_coordoperation_get_method_info(
11924 : d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
11925 8 : const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
11926 8 : if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
11927 : nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
11928 3 : (EQUAL(prjName, "Lambert_Conformal_Conic") &&
11929 : nMethodCode ==
11930 : EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
11931 : {
11932 3 : proj_destroy(crs);
11933 3 : proj_destroy(conv);
11934 3 : continue;
11935 : }
11936 :
11937 : auto coordSys =
11938 5 : proj_crs_get_coordinate_system(d->getPROJContext(), crs);
11939 5 : if (!coordSys)
11940 : {
11941 0 : proj_destroy(crs);
11942 0 : proj_destroy(conv);
11943 0 : continue;
11944 : }
11945 :
11946 5 : double dfConvFactor = 0.0;
11947 5 : proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
11948 : nullptr, nullptr, &dfConvFactor, nullptr,
11949 : nullptr, nullptr);
11950 5 : proj_destroy(coordSys);
11951 :
11952 6 : if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
11953 1 : (!EQUAL(unitsName, "meters") &&
11954 0 : std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
11955 : 1e-10))
11956 : {
11957 4 : proj_destroy(crs);
11958 4 : proj_destroy(conv);
11959 4 : continue;
11960 : }
11961 :
11962 1 : int idx_lat = proj_coordoperation_get_param_index(
11963 : d->getPROJContext(), conv,
11964 : EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
11965 1 : double valueLat = -1000;
11966 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
11967 : nullptr, nullptr, nullptr, &valueLat,
11968 : nullptr, nullptr, nullptr, nullptr,
11969 : nullptr, nullptr);
11970 1 : int idx_lon = proj_coordoperation_get_param_index(
11971 : d->getPROJContext(), conv,
11972 : EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
11973 1 : double valueLong = -1000;
11974 1 : proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
11975 : nullptr, nullptr, nullptr, &valueLong,
11976 : nullptr, nullptr, nullptr, nullptr,
11977 : nullptr, nullptr);
11978 1 : if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
11979 1 : std::fabs(latOfOrigin - valueLat) <= 1e-10)
11980 : {
11981 1 : Clear();
11982 1 : d->setPjCRS(crs);
11983 1 : proj_list_destroy(list);
11984 1 : proj_destroy(conv);
11985 1 : return OGRERR_NONE;
11986 : }
11987 :
11988 0 : proj_destroy(crs);
11989 0 : proj_destroy(conv);
11990 : }
11991 0 : proj_list_destroy(list);
11992 : }
11993 :
11994 0 : return OGRERR_FAILURE;
11995 : }
11996 :
11997 : /************************************************************************/
11998 : /* GetAxisMappingStrategy() */
11999 : /************************************************************************/
12000 :
12001 : /** \brief Return the data axis to CRS axis mapping strategy.
12002 : *
12003 : * <ul>
12004 : * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12005 : * lat/long order, the data will still be long/lat ordered. Similarly for
12006 : * a projected CRS with northing/easting order, the data will still be
12007 : * easting/northing ordered.
12008 : * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12009 : * the CRS axis.
12010 : * <li>OAMS_CUSTOM means that the data axis are customly defined with
12011 : * SetDataAxisToSRSAxisMapping()
12012 : * </ul>
12013 : * @return the data axis to CRS axis mapping strategy.
12014 : * @since GDAL 3.0
12015 : */
12016 37 : OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12017 : {
12018 37 : return d->m_axisMappingStrategy;
12019 : }
12020 :
12021 : /************************************************************************/
12022 : /* OSRGetAxisMappingStrategy() */
12023 : /************************************************************************/
12024 :
12025 : /** \brief Return the data axis to CRS axis mapping strategy.
12026 : *
12027 : * See OGRSpatialReference::GetAxisMappingStrategy()
12028 : * @since GDAL 3.0
12029 : */
12030 37 : OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12031 : {
12032 37 : VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12033 :
12034 37 : return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12035 : }
12036 :
12037 : /************************************************************************/
12038 : /* SetAxisMappingStrategy() */
12039 : /************************************************************************/
12040 :
12041 : /** \brief Set the data axis to CRS axis mapping strategy.
12042 : *
12043 : * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12044 : * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12045 : * later being the default value when the option is not set) to control the
12046 : * value of the data axis to CRS axis mapping strategy when a
12047 : * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12048 : * override this default value.
12049 : *
12050 : * See OGRSpatialReference::GetAxisMappingStrategy()
12051 : * @since GDAL 3.0
12052 : */
12053 79354 : void OGRSpatialReference::SetAxisMappingStrategy(
12054 : OSRAxisMappingStrategy strategy)
12055 : {
12056 79354 : d->m_axisMappingStrategy = strategy;
12057 79339 : d->refreshAxisMapping();
12058 79355 : }
12059 :
12060 : /************************************************************************/
12061 : /* OSRSetAxisMappingStrategy() */
12062 : /************************************************************************/
12063 :
12064 : /** \brief Set the data axis to CRS axis mapping strategy.
12065 : *
12066 : * See OGRSpatialReference::SetAxisMappingStrategy()
12067 : * @since GDAL 3.0
12068 : */
12069 818 : void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12070 : OSRAxisMappingStrategy strategy)
12071 : {
12072 818 : VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12073 :
12074 818 : OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12075 : }
12076 :
12077 : /************************************************************************/
12078 : /* GetDataAxisToSRSAxisMapping() */
12079 : /************************************************************************/
12080 :
12081 : /** \brief Return the data axis to SRS axis mapping.
12082 : *
12083 : * The number of elements of the vector will be the number of axis of the CRS.
12084 : * Values start at 1.
12085 : *
12086 : * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12087 : * for the first axis of the CRS.
12088 : *
12089 : * @since GDAL 3.0
12090 : */
12091 3806020 : const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12092 : {
12093 3806020 : return d->m_axisMapping;
12094 : }
12095 :
12096 : /************************************************************************/
12097 : /* OSRGetDataAxisToSRSAxisMapping() */
12098 : /************************************************************************/
12099 :
12100 : /** \brief Return the data axis to SRS axis mapping.
12101 : *
12102 : * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12103 : *
12104 : * @since GDAL 3.0
12105 : */
12106 175 : const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12107 : int *pnCount)
12108 : {
12109 175 : VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12110 175 : VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12111 :
12112 : const auto &v =
12113 175 : OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12114 175 : *pnCount = static_cast<int>(v.size());
12115 175 : return v.data();
12116 : }
12117 :
12118 : /************************************************************************/
12119 : /* SetDataAxisToSRSAxisMapping() */
12120 : /************************************************************************/
12121 :
12122 : /** \brief Set a custom data axis to CRS axis mapping.
12123 : *
12124 : * The number of elements of the mapping vector should be the number of axis
12125 : * of the CRS (as returned by GetAxesCount()) (although this method does not
12126 : * check that, beyond checking there are at least 2 elements, so that this
12127 : * method and setting the CRS can be done in any order).
12128 : * This is taken into account by OGRCoordinateTransformation to transform the
12129 : * order of coordinates to the order expected by the CRS before
12130 : * transformation, and back to the data order after transformation.
12131 : *
12132 : * The mapping[i] value (one based) represents the data axis number for the i(th)
12133 : * axis of the CRS. A negative value can also be used to ask for a sign
12134 : * reversal during coordinate transformation (to deal with northing vs southing,
12135 : * easting vs westing, heights vs depths).
12136 : *
12137 : * When used with OGRCoordinateTransformation,
12138 : * - the only valid values for mapping[0] (data axis number for the first axis
12139 : * of the CRS) are 1, 2, -1, -2.
12140 : * - the only valid values for mapping[1] (data axis number for the second axis
12141 : * of the CRS) are 1, 2, -1, -2.
12142 : * - the only valid values mapping[2] are 3 or -3.
12143 : * Note: this method does not validate the values of mapping[].
12144 : *
12145 : * mapping=[2,1] typically expresses the inversion of axis between the data
12146 : * axis and the CRS axis for a 2D CRS.
12147 : *
12148 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12149 : *
12150 : * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12151 : *
12152 : * @param mapping The new data axis to CRS axis mapping.
12153 : *
12154 : * @since GDAL 3.0
12155 : * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12156 : */
12157 6246 : OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12158 : const std::vector<int> &mapping)
12159 : {
12160 6246 : if (mapping.size() < 2)
12161 0 : return OGRERR_FAILURE;
12162 6246 : d->m_axisMappingStrategy = OAMS_CUSTOM;
12163 6246 : d->m_axisMapping = mapping;
12164 6246 : return OGRERR_NONE;
12165 : }
12166 :
12167 : /************************************************************************/
12168 : /* OSRSetDataAxisToSRSAxisMapping() */
12169 : /************************************************************************/
12170 :
12171 : /** \brief Set a custom data axis to CRS axis mapping.
12172 : *
12173 : * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12174 : *
12175 : * This is the same as the C++ method
12176 : * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12177 : *
12178 : * @since GDAL 3.1
12179 : */
12180 12 : OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12181 : int nMappingSize, const int *panMapping)
12182 : {
12183 12 : VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12184 12 : VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12185 : OGRERR_FAILURE);
12186 :
12187 12 : if (nMappingSize < 0)
12188 0 : return OGRERR_FAILURE;
12189 :
12190 24 : std::vector<int> mapping(nMappingSize);
12191 12 : if (nMappingSize)
12192 12 : memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12193 12 : return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12194 12 : mapping);
12195 : }
12196 :
12197 : /************************************************************************/
12198 : /* GetAreaOfUse() */
12199 : /************************************************************************/
12200 :
12201 : /** \brief Return the area of use of the CRS.
12202 : *
12203 : * This method is the same as the OSRGetAreaOfUse() function.
12204 : *
12205 : * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12206 : * longitude, expressed in degree. Might be NULL. If the returned value is
12207 : * -1000, the bounding box is unknown.
12208 : * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12209 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12210 : * the bounding box is unknown.
12211 : * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12212 : * longitude, expressed in degree. Might be NULL. If the returned value is
12213 : * -1000, the bounding box is unknown.
12214 : * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12215 : * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12216 : * the bounding box is unknown.
12217 : * @param ppszAreaName Pointer to a string to receive the name of the area of
12218 : * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12219 : * invalidated by further calls.
12220 : * @return true in case of success
12221 : * @since GDAL 3.0
12222 : */
12223 1 : bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12224 : double *pdfSouthLatitudeDeg,
12225 : double *pdfEastLongitudeDeg,
12226 : double *pdfNorthLatitudeDeg,
12227 : const char **ppszAreaName) const
12228 : {
12229 1 : d->refreshProjObj();
12230 1 : if (!d->m_pj_crs)
12231 : {
12232 0 : return false;
12233 : }
12234 1 : d->demoteFromBoundCRS();
12235 1 : const char *pszAreaName = nullptr;
12236 1 : int bSuccess = proj_get_area_of_use(
12237 1 : d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12238 : pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12239 : &pszAreaName);
12240 1 : d->undoDemoteFromBoundCRS();
12241 1 : d->m_osAreaName = pszAreaName ? pszAreaName : "";
12242 1 : if (ppszAreaName)
12243 1 : *ppszAreaName = d->m_osAreaName.c_str();
12244 1 : return CPL_TO_BOOL(bSuccess);
12245 : }
12246 :
12247 : /************************************************************************/
12248 : /* GetAreaOfUse() */
12249 : /************************************************************************/
12250 :
12251 : /** \brief Return the area of use of the CRS.
12252 : *
12253 : * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12254 : *
12255 : * @since GDAL 3.0
12256 : */
12257 1 : int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12258 : double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12259 : double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12260 : {
12261 1 : VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12262 :
12263 1 : return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12264 : pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12265 1 : pdfNorthLatitudeDeg, ppszAreaName);
12266 : }
12267 :
12268 : /************************************************************************/
12269 : /* OSRGetCRSInfoListFromDatabase() */
12270 : /************************************************************************/
12271 :
12272 : /** \brief Enumerate CRS objects from the database.
12273 : *
12274 : * The returned object is an array of OSRCRSInfo* pointers, whose last
12275 : * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12276 : *
12277 : * @param pszAuthName Authority name, used to restrict the search.
12278 : * Or NULL for all authorities.
12279 : * @param params Additional criteria. Must be set to NULL for now.
12280 : * @param pnOutResultCount Output parameter pointing to an integer to receive
12281 : * the size of the result list. Might be NULL
12282 : * @return an array of OSRCRSInfo* pointers to be freed with
12283 : * OSRDestroyCRSInfoList(), or NULL in case of error.
12284 : *
12285 : * @since GDAL 3.0
12286 : */
12287 : OSRCRSInfo **
12288 2 : OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12289 : CPL_UNUSED const OSRCRSListParameters *params,
12290 : int *pnOutResultCount)
12291 : {
12292 2 : int nResultCount = 0;
12293 2 : auto projList = proj_get_crs_info_list_from_database(
12294 : OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12295 2 : if (pnOutResultCount)
12296 2 : *pnOutResultCount = nResultCount;
12297 2 : if (!projList)
12298 : {
12299 0 : return nullptr;
12300 : }
12301 2 : auto res = new OSRCRSInfo *[nResultCount + 1];
12302 13220 : for (int i = 0; i < nResultCount; i++)
12303 : {
12304 13218 : res[i] = new OSRCRSInfo;
12305 26436 : res[i]->pszAuthName = projList[i]->auth_name
12306 13218 : ? CPLStrdup(projList[i]->auth_name)
12307 : : nullptr;
12308 13218 : res[i]->pszCode =
12309 13218 : projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12310 13218 : res[i]->pszName =
12311 13218 : projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12312 13218 : res[i]->eType = OSR_CRS_TYPE_OTHER;
12313 13218 : switch (projList[i]->type)
12314 : {
12315 1182 : case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12316 1182 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12317 1182 : break;
12318 442 : case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12319 442 : res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12320 442 : break;
12321 444 : case PJ_TYPE_GEOCENTRIC_CRS:
12322 444 : res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12323 444 : break;
12324 10154 : case PJ_TYPE_PROJECTED_CRS:
12325 10154 : res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12326 10154 : break;
12327 442 : case PJ_TYPE_VERTICAL_CRS:
12328 442 : res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12329 442 : break;
12330 554 : case PJ_TYPE_COMPOUND_CRS:
12331 554 : res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12332 554 : break;
12333 0 : default:
12334 0 : break;
12335 : }
12336 13218 : res[i]->bDeprecated = projList[i]->deprecated;
12337 13218 : res[i]->bBboxValid = projList[i]->bbox_valid;
12338 13218 : res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12339 13218 : res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12340 13218 : res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12341 13218 : res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12342 26436 : res[i]->pszAreaName = projList[i]->area_name
12343 13218 : ? CPLStrdup(projList[i]->area_name)
12344 : : nullptr;
12345 13218 : res[i]->pszProjectionMethod =
12346 13218 : projList[i]->projection_method_name
12347 13218 : ? CPLStrdup(projList[i]->projection_method_name)
12348 : : nullptr;
12349 : }
12350 2 : res[nResultCount] = nullptr;
12351 2 : proj_crs_info_list_destroy(projList);
12352 2 : return res;
12353 : }
12354 :
12355 : /************************************************************************/
12356 : /* OSRDestroyCRSInfoList() */
12357 : /************************************************************************/
12358 :
12359 : /** \brief Destroy the result returned by
12360 : * OSRGetCRSInfoListFromDatabase().
12361 : *
12362 : * @since GDAL 3.0
12363 : */
12364 2 : void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12365 : {
12366 2 : if (list)
12367 : {
12368 13220 : for (int i = 0; list[i] != nullptr; i++)
12369 : {
12370 13218 : CPLFree(list[i]->pszAuthName);
12371 13218 : CPLFree(list[i]->pszCode);
12372 13218 : CPLFree(list[i]->pszName);
12373 13218 : CPLFree(list[i]->pszAreaName);
12374 13218 : CPLFree(list[i]->pszProjectionMethod);
12375 13218 : delete list[i];
12376 : }
12377 2 : delete[] list;
12378 : }
12379 2 : }
12380 :
12381 : /************************************************************************/
12382 : /* UpdateCoordinateSystemFromGeogCRS() */
12383 : /************************************************************************/
12384 :
12385 : /*! @cond Doxygen_Suppress */
12386 : /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12387 : *
12388 : * @since GDAL 3.1
12389 : */
12390 1 : void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12391 : {
12392 1 : d->refreshProjObj();
12393 1 : if (!d->m_pj_crs)
12394 0 : return;
12395 1 : if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12396 0 : return;
12397 1 : if (GetAxesCount() == 3)
12398 0 : return;
12399 1 : auto ctxt = d->getPROJContext();
12400 1 : auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12401 1 : if (!baseCRS)
12402 0 : return;
12403 1 : auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12404 1 : if (!baseCRSCS)
12405 : {
12406 0 : proj_destroy(baseCRS);
12407 0 : return;
12408 : }
12409 1 : if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12410 : {
12411 0 : proj_destroy(baseCRSCS);
12412 0 : proj_destroy(baseCRS);
12413 0 : return;
12414 : }
12415 1 : auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12416 1 : if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12417 : {
12418 0 : proj_destroy(baseCRSCS);
12419 0 : proj_destroy(baseCRS);
12420 0 : proj_destroy(projCS);
12421 0 : return;
12422 : }
12423 :
12424 : PJ_AXIS_DESCRIPTION axis[3];
12425 4 : for (int i = 0; i < 3; i++)
12426 : {
12427 3 : const char *name = nullptr;
12428 3 : const char *abbreviation = nullptr;
12429 3 : const char *direction = nullptr;
12430 3 : double unit_conv_factor = 0;
12431 3 : const char *unit_name = nullptr;
12432 3 : proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
12433 : &abbreviation, &direction, &unit_conv_factor,
12434 : &unit_name, nullptr, nullptr);
12435 3 : axis[i].name = CPLStrdup(name);
12436 3 : axis[i].abbreviation = CPLStrdup(abbreviation);
12437 3 : axis[i].direction = CPLStrdup(direction);
12438 3 : axis[i].unit_name = CPLStrdup(unit_name);
12439 3 : axis[i].unit_conv_factor = unit_conv_factor;
12440 3 : axis[i].unit_type = PJ_UT_LINEAR;
12441 : }
12442 1 : proj_destroy(baseCRSCS);
12443 1 : proj_destroy(projCS);
12444 1 : auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
12445 4 : for (int i = 0; i < 3; i++)
12446 : {
12447 3 : CPLFree(axis[i].name);
12448 3 : CPLFree(axis[i].abbreviation);
12449 3 : CPLFree(axis[i].direction);
12450 3 : CPLFree(axis[i].unit_name);
12451 : }
12452 1 : if (!cs)
12453 : {
12454 0 : proj_destroy(baseCRS);
12455 0 : return;
12456 : }
12457 1 : auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
12458 1 : auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
12459 : conversion, cs);
12460 1 : proj_destroy(baseCRS);
12461 1 : proj_destroy(conversion);
12462 1 : proj_destroy(cs);
12463 1 : d->setPjCRS(crs);
12464 : }
12465 :
12466 : /*! @endcond */
12467 :
12468 : /************************************************************************/
12469 : /* PromoteTo3D() */
12470 : /************************************************************************/
12471 :
12472 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
12473 : *
12474 : * The new axis will be ellipsoidal height, oriented upwards, and with metre
12475 : * units.
12476 : *
12477 : * @param pszName New name for the CRS. If set to NULL, the previous name will
12478 : * be used.
12479 : * @return OGRERR_NONE if no error occurred.
12480 : * @since GDAL 3.1 and PROJ 6.3
12481 : */
12482 41 : OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
12483 : {
12484 41 : d->refreshProjObj();
12485 41 : if (!d->m_pj_crs)
12486 0 : return OGRERR_FAILURE;
12487 : auto newPj =
12488 41 : proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
12489 41 : if (!newPj)
12490 0 : return OGRERR_FAILURE;
12491 41 : d->setPjCRS(newPj);
12492 41 : return OGRERR_NONE;
12493 : }
12494 :
12495 : /************************************************************************/
12496 : /* OSRPromoteTo3D() */
12497 : /************************************************************************/
12498 :
12499 : /** \brief "Promotes" a 2D CRS to a 3D CRS one.
12500 : *
12501 : * See OGRSpatialReference::PromoteTo3D()
12502 : *
12503 : * @since GDAL 3.1 and PROJ 6.3
12504 : */
12505 3 : OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
12506 : {
12507 3 : VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
12508 :
12509 3 : return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
12510 : }
12511 :
12512 : /************************************************************************/
12513 : /* DemoteTo2D() */
12514 : /************************************************************************/
12515 :
12516 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
12517 : *
12518 : * @param pszName New name for the CRS. If set to NULL, the previous name will
12519 : * be used.
12520 : * @return OGRERR_NONE if no error occurred.
12521 : * @since GDAL 3.2 and PROJ 6.3
12522 : */
12523 45 : OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
12524 : {
12525 45 : d->refreshProjObj();
12526 45 : if (!d->m_pj_crs)
12527 0 : return OGRERR_FAILURE;
12528 : auto newPj =
12529 45 : proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
12530 45 : if (!newPj)
12531 0 : return OGRERR_FAILURE;
12532 45 : d->setPjCRS(newPj);
12533 45 : return OGRERR_NONE;
12534 : }
12535 :
12536 : /************************************************************************/
12537 : /* OSRDemoteTo2D() */
12538 : /************************************************************************/
12539 :
12540 : /** \brief "Demote" a 3D CRS to a 2D CRS one.
12541 : *
12542 : * See OGRSpatialReference::DemoteTo2D()
12543 : *
12544 : * @since GDAL 3.2 and PROJ 6.3
12545 : */
12546 1 : OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
12547 : {
12548 1 : VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
12549 :
12550 1 : return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
12551 : }
12552 :
12553 : /************************************************************************/
12554 : /* GetEPSGGeogCS() */
12555 : /************************************************************************/
12556 :
12557 : /** Try to establish what the EPSG code for this coordinate systems
12558 : * GEOGCS might be. Returns -1 if no reasonable guess can be made.
12559 : *
12560 : * @return EPSG code
12561 : */
12562 :
12563 331 : int OGRSpatialReference::GetEPSGGeogCS() const
12564 :
12565 : {
12566 : /* -------------------------------------------------------------------- */
12567 : /* Check axis order. */
12568 : /* -------------------------------------------------------------------- */
12569 662 : auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
12570 331 : if (!poGeogCRS)
12571 0 : return -1;
12572 :
12573 331 : bool ret = false;
12574 331 : poGeogCRS->d->demoteFromBoundCRS();
12575 331 : auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
12576 331 : poGeogCRS->d->m_pj_crs);
12577 331 : poGeogCRS->d->undoDemoteFromBoundCRS();
12578 331 : if (cs)
12579 : {
12580 331 : const char *pszDirection = nullptr;
12581 331 : if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
12582 : &pszDirection, nullptr, nullptr, nullptr,
12583 331 : nullptr))
12584 : {
12585 331 : if (EQUAL(pszDirection, "north"))
12586 : {
12587 133 : ret = true;
12588 : }
12589 : }
12590 :
12591 331 : proj_destroy(cs);
12592 : }
12593 331 : if (!ret)
12594 198 : return -1;
12595 :
12596 : /* -------------------------------------------------------------------- */
12597 : /* Do we already have it? */
12598 : /* -------------------------------------------------------------------- */
12599 133 : const char *pszAuthName = GetAuthorityName("GEOGCS");
12600 133 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
12601 59 : return atoi(GetAuthorityCode("GEOGCS"));
12602 :
12603 : /* -------------------------------------------------------------------- */
12604 : /* Get the datum and geogcs names. */
12605 : /* -------------------------------------------------------------------- */
12606 :
12607 74 : const char *pszGEOGCS = GetAttrValue("GEOGCS");
12608 74 : const char *pszDatum = GetAttrValue("DATUM");
12609 :
12610 : // We can only operate on coordinate systems with a geogcs.
12611 148 : OGRSpatialReference oSRSTmp;
12612 74 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
12613 : {
12614 : // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
12615 : // export to WKT1, so try to extract the geographic CRS through PROJ
12616 : // API with CopyGeogCSFrom() and get the nodes' values from it.
12617 1 : oSRSTmp.CopyGeogCSFrom(this);
12618 1 : pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
12619 1 : pszDatum = oSRSTmp.GetAttrValue("DATUM");
12620 1 : if (pszGEOGCS == nullptr || pszDatum == nullptr)
12621 : {
12622 0 : return -1;
12623 : }
12624 : }
12625 :
12626 : // Lookup geographic CRS name
12627 74 : const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
12628 74 : PJ_OBJ_LIST *list = proj_create_from_name(
12629 : d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
12630 74 : if (list)
12631 : {
12632 74 : const auto listSize = proj_list_get_count(list);
12633 74 : if (listSize == 1)
12634 : {
12635 49 : auto crs = proj_list_get(d->getPROJContext(), list, 0);
12636 49 : if (crs)
12637 : {
12638 49 : pszAuthName = proj_get_id_auth_name(crs, 0);
12639 49 : const char *pszCode = proj_get_id_code(crs, 0);
12640 49 : if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
12641 : {
12642 47 : const int nCode = atoi(pszCode);
12643 47 : proj_destroy(crs);
12644 47 : proj_list_destroy(list);
12645 47 : return nCode;
12646 : }
12647 2 : proj_destroy(crs);
12648 : }
12649 : }
12650 27 : proj_list_destroy(list);
12651 : }
12652 :
12653 : /* -------------------------------------------------------------------- */
12654 : /* Is this a "well known" geographic coordinate system? */
12655 : /* -------------------------------------------------------------------- */
12656 81 : const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
12657 27 : strstr(pszDatum, "WGS") ||
12658 27 : strstr(pszGEOGCS, "World Geodetic System") ||
12659 27 : strstr(pszGEOGCS, "World_Geodetic_System") ||
12660 81 : strstr(pszDatum, "World Geodetic System") ||
12661 27 : strstr(pszDatum, "World_Geodetic_System");
12662 :
12663 81 : const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
12664 27 : strstr(pszDatum, "NAD") ||
12665 27 : strstr(pszGEOGCS, "North American") ||
12666 27 : strstr(pszGEOGCS, "North_American") ||
12667 81 : strstr(pszDatum, "North American") ||
12668 27 : strstr(pszDatum, "North_American");
12669 :
12670 27 : if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
12671 0 : return 4326;
12672 :
12673 27 : if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
12674 0 : return 4322;
12675 :
12676 : // This is questionable as there are several 'flavors' of NAD83 that
12677 : // are not the same as 4269
12678 27 : if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
12679 0 : return 4269;
12680 :
12681 27 : if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
12682 0 : return 4267;
12683 :
12684 : /* -------------------------------------------------------------------- */
12685 : /* If we know the datum, associate the most likely GCS with */
12686 : /* it. */
12687 : /* -------------------------------------------------------------------- */
12688 27 : const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
12689 27 : pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
12690 27 : if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
12691 0 : GetPrimeMeridian() == 0.0)
12692 : {
12693 0 : const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
12694 :
12695 0 : if (nDatum >= 6000 && nDatum <= 6999)
12696 0 : return nDatum - 2000;
12697 : }
12698 :
12699 27 : return -1;
12700 : }
12701 :
12702 : /************************************************************************/
12703 : /* SetCoordinateEpoch() */
12704 : /************************************************************************/
12705 :
12706 : /** Set the coordinate epoch, as decimal year.
12707 : *
12708 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
12709 : * change with time. To be unambiguous the coordinates must always be qualified
12710 : * with the epoch at which they are valid. The coordinate epoch is not
12711 : * necessarily the epoch at which the observation was collected.
12712 : *
12713 : * Pedantically the coordinate epoch of an observation belongs to the
12714 : * observation, and not to the CRS, however it is often more practical to
12715 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
12716 : * CRS (see IsDynamic())
12717 : *
12718 : * This method is the same as the OSRSetCoordinateEpoch() function.
12719 : *
12720 : * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
12721 : * @since OGR 3.4
12722 : */
12723 :
12724 702 : void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
12725 : {
12726 702 : d->m_coordinateEpoch = dfCoordinateEpoch;
12727 702 : }
12728 :
12729 : /************************************************************************/
12730 : /* OSRSetCoordinateEpoch() */
12731 : /************************************************************************/
12732 :
12733 : /** \brief Set the coordinate epoch, as decimal year.
12734 : *
12735 : * See OGRSpatialReference::SetCoordinateEpoch()
12736 : *
12737 : * @since OGR 3.4
12738 : */
12739 29 : void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
12740 : {
12741 29 : VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
12742 :
12743 29 : return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
12744 29 : dfCoordinateEpoch);
12745 : }
12746 :
12747 : /************************************************************************/
12748 : /* GetCoordinateEpoch() */
12749 : /************************************************************************/
12750 :
12751 : /** Return the coordinate epoch, as decimal year.
12752 : *
12753 : * In a dynamic CRS, coordinates of a point on the surface of the Earth may
12754 : * change with time. To be unambiguous the coordinates must always be qualified
12755 : * with the epoch at which they are valid. The coordinate epoch is not
12756 : * necessarily the epoch at which the observation was collected.
12757 : *
12758 : * Pedantically the coordinate epoch of an observation belongs to the
12759 : * observation, and not to the CRS, however it is often more practical to
12760 : * bind it to the CRS. The coordinate epoch should be specified for dynamic
12761 : * CRS (see IsDynamic())
12762 : *
12763 : * This method is the same as the OSRGetCoordinateEpoch() function.
12764 : *
12765 : * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
12766 : * if not set, or relevant.
12767 : * @since OGR 3.4
12768 : */
12769 :
12770 9251 : double OGRSpatialReference::GetCoordinateEpoch() const
12771 : {
12772 9251 : return d->m_coordinateEpoch;
12773 : }
12774 :
12775 : /************************************************************************/
12776 : /* OSRGetCoordinateEpoch() */
12777 : /************************************************************************/
12778 :
12779 : /** \brief Get the coordinate epoch, as decimal year.
12780 : *
12781 : * See OGRSpatialReference::GetCoordinateEpoch()
12782 : *
12783 : * @since OGR 3.4
12784 : */
12785 582 : double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
12786 : {
12787 582 : VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
12788 :
12789 582 : return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
12790 : }
|