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 <vector>
42
43 #include "cpl_atomic_ops.h"
44 #include "cpl_conv.h"
45 #include "cpl_csv.h"
46 #include "cpl_error.h"
47 #include "cpl_error_internal.h"
48 #include "cpl_http.h"
49 #include "cpl_multiproc.h"
50 #include "cpl_string.h"
51 #include "cpl_vsi.h"
52 #include "ogr_core.h"
53 #include "ogr_p.h"
54 #include "ogr_proj_p.h"
55 #include "ogr_srs_api.h"
56
57 #include "proj.h"
58 #include "proj_experimental.h"
59 #include "proj_constants.h"
60
61 // Exists since 8.0.1
62 #ifndef PROJ_AT_LEAST_VERSION
63 #define PROJ_COMPUTE_VERSION(maj,min,patch) ((maj)*10000+(min)*100+(patch))
64 #define PROJ_VERSION_NUMBER \
65 PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH)
66 #define PROJ_AT_LEAST_VERSION(maj,min,patch) \
67 (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj,min,patch))
68 #endif
69
70 CPL_CVSID("$Id: ogrspatialreference.cpp b0f0c489a4bc6b39aec821160e75af336a1e5505 2021-09-29 12:09:34 +0200 Even Rouault $")
71
72 #define STRINGIFY(s) #s
73 #define XSTRINGIFY(s) STRINGIFY(s)
74
75 struct OGRSpatialReference::Private
76 {
77 struct Listener: public OGR_SRSNode::Listener
78 {
79 OGRSpatialReference::Private* m_poObj = nullptr;
80
ListenerOGRSpatialReference::Private::Listener81 explicit Listener(OGRSpatialReference::Private* poObj): m_poObj(poObj) {}
82 Listener(const Listener&) = delete;
83 Listener& operator=(const Listener&) = delete;
84
notifyChangeOGRSpatialReference::Private::Listener85 void notifyChange(OGR_SRSNode*) override
86 {
87 m_poObj->nodesChanged();
88 }
89 };
90
91 PJ* m_pj_crs = nullptr;
92
93 // Temporary state used for object construction
94 PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
95 CPLString m_osPrimeMeridianName{};
96 CPLString m_osAngularUnits{};
97 CPLString m_osLinearUnits{};
98 CPLString m_osAxisName[3]{};
99
100 std::vector<std::string> m_wktImportWarnings{};
101 std::vector<std::string> m_wktImportErrors{};
102 CPLString m_osAreaName{};
103
104 bool m_bNodesChanged = false;
105 bool m_bNodesWKT2 = false;
106 OGR_SRSNode *m_poRoot = nullptr;
107
108 double dfFromGreenwich = 0.0;
109 double dfToMeter = 0.0;
110 double dfToDegrees = 0.0;
111 double m_dfAngularUnitToRadian = 0.0;
112
113 int nRefCount = 1;
114 int bNormInfoSet = FALSE;
115
116 PJ *m_pj_geod_base_crs_temp = nullptr;
117 PJ *m_pj_proj_crs_cs_temp = nullptr;
118
119 bool m_pj_crs_modified_during_demote = false;
120 PJ *m_pj_bound_crs_target = nullptr;
121 PJ *m_pj_bound_crs_co = nullptr;
122 PJ *m_pj_crs_backup = nullptr;
123 OGR_SRSNode *m_poRootBackup = nullptr;
124
125 bool m_bMorphToESRI = false;
126 bool m_bHasCenterLong = false;
127
128 std::shared_ptr<Listener> m_poListener{};
129
130 std::mutex m_mutex{};
131
132 OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
133 std::vector<int> m_axisMapping{1,2,3};
134
135 Private();
136 ~Private();
137 Private(const Private&) = delete;
138 Private& operator= (const Private&) = delete;
139
140 void clear();
141 void setPjCRS(PJ* pj_crsIn, bool doRefreshAxisMapping = true);
142 void setRoot(OGR_SRSNode* poRoot);
143 void refreshProjObj();
144 void nodesChanged();
145 void refreshRootFromProjObj();
146 void invalidateNodes();
147
148 void setMorphToESRI(bool b);
149
150 PJ *getGeodBaseCRS();
151 PJ *getProjCRSCoordSys();
152
153 const char *getProjCRSName();
154 OGRErr replaceConversionAndUnref(PJ* conv);
155
156 void demoteFromBoundCRS();
157 void undoDemoteFromBoundCRS();
158
getPROJContextOGRSpatialReference::Private159 PJ_CONTEXT *getPROJContext() { return OSRGetProjTLSContext(); }
160
161 const char *nullifyTargetKeyIfPossible(const char* pszTargetKey);
162
163 void refreshAxisMapping();
164 };
165
Private()166 OGRSpatialReference::Private::Private():
167 m_poListener(std::shared_ptr<Listener>(new Listener(this)))
168 {
169 }
170
~Private()171 OGRSpatialReference::Private::~Private()
172 {
173 // In case we destroy the object not in the thread that created it,
174 // we need to reassign the PROJ context. Having the context bundled inside
175 // PJ* deeply sucks...
176 auto ctxt = getPROJContext();
177
178 proj_assign_context( m_pj_crs, ctxt );
179 proj_destroy(m_pj_crs);
180
181 proj_assign_context( m_pj_geod_base_crs_temp, ctxt );
182 proj_destroy(m_pj_geod_base_crs_temp);
183
184 proj_assign_context( m_pj_proj_crs_cs_temp, ctxt );
185 proj_destroy(m_pj_proj_crs_cs_temp);
186
187 proj_assign_context( m_pj_bound_crs_target, ctxt );
188 proj_destroy(m_pj_bound_crs_target);
189
190 proj_assign_context( m_pj_bound_crs_co, ctxt );
191 proj_destroy(m_pj_bound_crs_co);
192
193 proj_assign_context( m_pj_crs_backup, ctxt );
194 proj_destroy(m_pj_crs_backup);
195
196 delete m_poRootBackup;
197 delete m_poRoot;
198 }
199
clear()200 void OGRSpatialReference::Private::clear()
201 {
202 proj_assign_context( m_pj_crs, getPROJContext() );
203 proj_destroy(m_pj_crs);
204 m_pj_crs = nullptr;
205
206 delete m_poRoot;
207 m_poRoot = nullptr;
208 m_bNodesChanged = false;
209
210 m_wktImportWarnings.clear();
211 m_wktImportErrors.clear();
212
213 m_pj_crs_modified_during_demote = false;
214 m_pjType = m_pj_crs ? proj_get_type(m_pj_crs) : PJ_TYPE_UNKNOWN;
215 m_osPrimeMeridianName.clear();
216 m_osAngularUnits.clear();
217 m_osLinearUnits.clear();
218
219 bNormInfoSet = FALSE;
220 dfFromGreenwich = 1.0;
221 dfToMeter = 1.0;
222 dfToDegrees = 1.0;
223 m_dfAngularUnitToRadian = 0.0;
224
225 m_bMorphToESRI = false;
226 m_bHasCenterLong = false;
227 }
228
setRoot(OGR_SRSNode * poRoot)229 void OGRSpatialReference::Private::setRoot(OGR_SRSNode* poRoot)
230 {
231 m_poRoot = poRoot;
232 if( m_poRoot )
233 {
234 m_poRoot->RegisterListener(m_poListener);
235 }
236 nodesChanged();
237 }
238
setPjCRS(PJ * pj_crsIn,bool doRefreshAxisMapping)239 void OGRSpatialReference::Private::setPjCRS(PJ* pj_crsIn,
240 bool doRefreshAxisMapping)
241 {
242 proj_assign_context( m_pj_crs, getPROJContext() );
243 proj_destroy(m_pj_crs);
244 m_pj_crs = pj_crsIn;
245 if( m_pj_crs )
246 {
247 m_pjType = proj_get_type(m_pj_crs);
248 }
249 if( m_pj_crs_backup )
250 {
251 m_pj_crs_modified_during_demote = true;
252 }
253 invalidateNodes();
254 if( doRefreshAxisMapping )
255 {
256 refreshAxisMapping();
257 }
258 }
259
260
refreshProjObj()261 void OGRSpatialReference::Private::refreshProjObj()
262 {
263 if( m_bNodesChanged && m_poRoot )
264 {
265 char* pszWKT = nullptr;
266 m_poRoot->exportToWkt(&pszWKT);
267 auto poRootBackup = m_poRoot;
268 m_poRoot = nullptr;
269 clear();
270 m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
271
272 const char* const options[] = { "STRICT=NO", nullptr };
273 PROJ_STRING_LIST warnings = nullptr;
274 PROJ_STRING_LIST errors = nullptr;
275 setPjCRS(proj_create_from_wkt(
276 getPROJContext(), pszWKT, options, &warnings, &errors));
277 for( auto iter = warnings; iter && *iter; ++iter ) {
278 m_wktImportWarnings.push_back(*iter);
279 }
280 for( auto iter = errors; iter && *iter; ++iter ) {
281 m_wktImportErrors.push_back(*iter);
282 }
283 proj_string_list_destroy(warnings);
284 proj_string_list_destroy(errors);
285
286 CPLFree(pszWKT);
287
288 m_poRoot = poRootBackup;
289 m_bNodesChanged = false;
290 }
291 }
292
refreshRootFromProjObj()293 void OGRSpatialReference::Private::refreshRootFromProjObj()
294 {
295 CPLAssert( m_poRoot == nullptr );
296
297 if( m_pj_crs )
298 {
299 CPLStringList aosOptions;
300 if( !m_bMorphToESRI )
301 {
302 aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
303 aosOptions.SetNameValue("MULTILINE", "NO");
304 }
305 aosOptions.SetNameValue("STRICT", "NO");
306
307 const char* pszWKT;
308 {
309 CPLErrorStateBackuper oErrorStateBackuper;
310 CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
311 pszWKT = proj_as_wkt(getPROJContext(),
312 m_pj_crs, m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
313 aosOptions.List());
314 m_bNodesWKT2 = false;
315 }
316 if( !m_bMorphToESRI && pszWKT == nullptr )
317 {
318 pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
319 aosOptions.List());
320 m_bNodesWKT2 = true;
321 }
322 if( pszWKT )
323 {
324 auto root = new OGR_SRSNode();
325 setRoot(root);
326 root->importFromWkt(&pszWKT);
327 m_bNodesChanged = false;
328 }
329 }
330 }
331
isNorthEastAxisOrder(PJ_CONTEXT * ctx,PJ * cs)332 static bool isNorthEastAxisOrder(PJ_CONTEXT* ctx, PJ* cs)
333 {
334 const char* pszName1 = nullptr;
335 const char* pszDirection1 = nullptr;
336 proj_cs_get_axis_info(
337 ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
338 nullptr, nullptr, nullptr, nullptr);
339 const char* pszName2 = nullptr;
340 const char* pszDirection2 = nullptr;
341 proj_cs_get_axis_info(
342 ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
343 nullptr, nullptr, nullptr, nullptr);
344 if( pszDirection1 && EQUAL(pszDirection1, "north") &&
345 pszDirection2 && EQUAL(pszDirection2, "east") )
346 {
347 return true;
348 }
349 if( pszDirection1 && pszDirection2 &&
350 ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
351 (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
352 pszName1 && STARTS_WITH_CI(pszName1, "northing") &&
353 pszName2 && STARTS_WITH_CI(pszName2, "easting") )
354 {
355 return true;
356 }
357 return false;
358 }
359
refreshAxisMapping()360 void OGRSpatialReference::Private::refreshAxisMapping()
361 {
362 if( !m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM )
363 return;
364
365 bool doUndoDemote = false;
366 if( m_pj_crs_backup == nullptr )
367 {
368 doUndoDemote = true;
369 demoteFromBoundCRS();
370 }
371 const auto ctxt = getPROJContext();
372 PJ* horizCRS = nullptr;
373 int axisCount = 0;
374 if( m_pjType == PJ_TYPE_VERTICAL_CRS )
375 {
376 axisCount = 1;
377 }
378 else if( m_pjType == PJ_TYPE_COMPOUND_CRS )
379 {
380 horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
381 if( horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS )
382 {
383 auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
384 if( baseCRS )
385 {
386 proj_destroy(horizCRS);
387 horizCRS = baseCRS;
388 }
389 }
390
391 auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
392 if( vertCRS )
393 {
394 if( proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS )
395 {
396 auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
397 if( baseCRS )
398 {
399 proj_destroy(vertCRS);
400 vertCRS = baseCRS;
401 }
402 }
403
404 auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
405 if( cs )
406 {
407 axisCount += proj_cs_get_axis_count(ctxt, cs);
408 proj_destroy(cs);
409 }
410 proj_destroy(vertCRS);
411 }
412 }
413 else
414 {
415 horizCRS = m_pj_crs;
416 }
417
418 bool bSwitchForGisFriendlyOrder = false;
419 if( horizCRS )
420 {
421 auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
422 if( cs )
423 {
424 int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
425 axisCount += nHorizCSAxisCount;
426 if( nHorizCSAxisCount >= 2 )
427 {
428 bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
429 }
430 proj_destroy(cs);
431 }
432 }
433 if( horizCRS != m_pj_crs )
434 {
435 proj_destroy(horizCRS);
436 }
437 if( doUndoDemote )
438 {
439 undoDemoteFromBoundCRS();
440 }
441
442 m_axisMapping.resize(axisCount);
443 if( m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
444 !bSwitchForGisFriendlyOrder )
445 {
446 for( int i = 0; i < axisCount; i++ )
447 {
448 m_axisMapping[i] = i + 1;
449 }
450 }
451 else
452 {
453 m_axisMapping[0] = 2;
454 m_axisMapping[1] = 1;
455 if( axisCount == 3 )
456 {
457 m_axisMapping[2] = 3;
458 }
459 }
460 }
461
nodesChanged()462 void OGRSpatialReference::Private::nodesChanged()
463 {
464 m_bNodesChanged = true;
465 }
466
invalidateNodes()467 void OGRSpatialReference::Private::invalidateNodes()
468 {
469 delete m_poRoot;
470 m_poRoot = nullptr;
471 m_bNodesChanged = false;
472 }
473
setMorphToESRI(bool b)474 void OGRSpatialReference::Private::setMorphToESRI(bool b)
475 {
476 invalidateNodes();
477 m_bMorphToESRI = b;
478 }
479
demoteFromBoundCRS()480 void OGRSpatialReference::Private::demoteFromBoundCRS()
481 {
482 CPLAssert(m_pj_bound_crs_target == nullptr);
483 CPLAssert(m_pj_bound_crs_co == nullptr);
484 CPLAssert(m_poRootBackup == nullptr);
485 CPLAssert(m_pj_crs_backup == nullptr);
486
487 m_pj_crs_modified_during_demote = false;
488
489 if( m_pjType == PJ_TYPE_BOUND_CRS ) {
490 auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
491 m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
492 m_pj_bound_crs_co = proj_crs_get_coordoperation(
493 getPROJContext(), m_pj_crs);
494
495 m_poRootBackup = m_poRoot;
496 m_poRoot = nullptr;
497 m_pj_crs_backup = m_pj_crs;
498 m_pj_crs = baseCRS;
499 m_pjType = proj_get_type(m_pj_crs);
500 }
501 }
502
undoDemoteFromBoundCRS()503 void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
504 {
505 if( m_pj_bound_crs_target )
506 {
507 CPLAssert(m_poRoot == nullptr);
508 CPLAssert(m_pj_crs);
509 if( !m_pj_crs_modified_during_demote )
510 {
511 proj_destroy(m_pj_crs);
512 m_pj_crs = m_pj_crs_backup;
513 m_pjType = proj_get_type(m_pj_crs);
514 m_poRoot = m_poRootBackup;
515 }
516 else
517 {
518 delete m_poRootBackup;
519 m_poRootBackup = nullptr;
520 proj_destroy(m_pj_crs_backup);
521 m_pj_crs_backup = nullptr;
522 setPjCRS(proj_crs_create_bound_crs(getPROJContext(),
523 m_pj_crs,
524 m_pj_bound_crs_target,
525 m_pj_bound_crs_co), false);
526 }
527 }
528
529 m_poRootBackup = nullptr;
530 m_pj_crs_backup = nullptr;
531 proj_destroy(m_pj_bound_crs_target);
532 m_pj_bound_crs_target = nullptr;
533 proj_destroy(m_pj_bound_crs_co);
534 m_pj_bound_crs_co = nullptr;
535 m_pj_crs_modified_during_demote = false;
536 }
537
nullifyTargetKeyIfPossible(const char * pszTargetKey)538 const char* OGRSpatialReference::Private::nullifyTargetKeyIfPossible(const char* pszTargetKey)
539 {
540 if( pszTargetKey )
541 {
542 demoteFromBoundCRS();
543 if( (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
544 m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
545 EQUAL(pszTargetKey, "GEOGCS") )
546 {
547 pszTargetKey = nullptr;
548 }
549 else if( m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
550 EQUAL(pszTargetKey, "GEOCCS") )
551 {
552 pszTargetKey = nullptr;
553 }
554 else if( m_pjType == PJ_TYPE_PROJECTED_CRS &&
555 EQUAL(pszTargetKey, "PROJCS") )
556 {
557 pszTargetKey = nullptr;
558 }
559 else if( m_pjType == PJ_TYPE_VERTICAL_CRS &&
560 EQUAL(pszTargetKey, "VERT_CS") )
561 {
562 pszTargetKey = nullptr;
563 }
564 undoDemoteFromBoundCRS();
565 }
566 return pszTargetKey;
567 }
568
getGeodBaseCRS()569 PJ *OGRSpatialReference::Private::getGeodBaseCRS()
570 {
571 if( m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
572 m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS ) {
573 return m_pj_crs;
574 }
575
576 auto ctxt = getPROJContext();
577 if( m_pjType == PJ_TYPE_PROJECTED_CRS ) {
578 proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
579 proj_destroy(m_pj_geod_base_crs_temp);
580 m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(
581 ctxt, m_pj_crs);
582 return m_pj_geod_base_crs_temp;
583 }
584
585 proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
586 proj_destroy(m_pj_geod_base_crs_temp);
587 auto cs = proj_create_ellipsoidal_2D_cs(
588 ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE, nullptr, 0);
589 m_pj_geod_base_crs_temp = proj_create_geographic_crs(
590 ctxt,
591 "WGS 84", "World Geodetic System 1984", "WGS 84", SRS_WGS84_SEMIMAJOR,
592 SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
593 SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
594 proj_destroy(cs);
595
596 return m_pj_geod_base_crs_temp;
597 }
598
getProjCRSCoordSys()599 PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
600 {
601 auto ctxt = getPROJContext();
602 if( m_pjType == PJ_TYPE_PROJECTED_CRS ) {
603 proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
604 proj_destroy(m_pj_proj_crs_cs_temp);
605 m_pj_proj_crs_cs_temp = proj_crs_get_coordinate_system(
606 getPROJContext(), m_pj_crs);
607 return m_pj_proj_crs_cs_temp;
608 }
609
610 proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
611 proj_destroy(m_pj_proj_crs_cs_temp);
612 m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
613 ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
614 return m_pj_proj_crs_cs_temp;
615 }
616
getProjCRSName()617 const char *OGRSpatialReference::Private::getProjCRSName()
618 {
619 if( m_pjType == PJ_TYPE_PROJECTED_CRS ) {
620 return proj_get_name(m_pj_crs);
621 }
622
623 return "unnamed";
624 }
625
replaceConversionAndUnref(PJ * conv)626 OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ* conv)
627 {
628 refreshProjObj();
629
630 demoteFromBoundCRS();
631
632 auto projCRS = proj_create_projected_crs(
633 getPROJContext(),
634 getProjCRSName(), getGeodBaseCRS(), conv, getProjCRSCoordSys());
635 proj_destroy(conv);
636
637 setPjCRS(projCRS);
638
639 undoDemoteFromBoundCRS();
640 return OGRERR_NONE;
641 }
642
643 /************************************************************************/
644 /* ToPointer() */
645 /************************************************************************/
646
ToPointer(OGRSpatialReferenceH hSRS)647 static inline OGRSpatialReference* ToPointer(OGRSpatialReferenceH hSRS)
648 {
649 return OGRSpatialReference::FromHandle(hSRS);
650 }
651
652 /************************************************************************/
653 /* ToHandle() */
654 /************************************************************************/
655
ToHandle(OGRSpatialReference * poSRS)656 static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference* poSRS)
657 {
658 return OGRSpatialReference::ToHandle(poSRS);
659 }
660
661 /************************************************************************/
662 /* OGRsnPrintDouble() */
663 /************************************************************************/
664
665 void OGRsnPrintDouble( char * pszStrBuf, size_t size, double dfValue );
666
OGRsnPrintDouble(char * pszStrBuf,size_t size,double dfValue)667 void OGRsnPrintDouble( char * pszStrBuf, size_t size, double dfValue )
668
669 {
670 CPLsnprintf( pszStrBuf, size, "%.16g", dfValue );
671
672 const size_t nLen = strlen(pszStrBuf);
673
674 // The following hack is intended to truncate some "precision" in cases
675 // that appear to be roundoff error.
676 if( nLen > 15
677 && (strcmp(pszStrBuf+nLen-6, "999999") == 0
678 || strcmp(pszStrBuf+nLen-6, "000001") == 0) )
679 {
680 CPLsnprintf( pszStrBuf, size, "%.15g", dfValue );
681 }
682
683 // Force to user periods regardless of locale.
684 if( strchr( pszStrBuf, ',' ) != nullptr )
685 {
686 char * const pszDelim = strchr( pszStrBuf, ',' );
687 *pszDelim = '.';
688 }
689 }
690
691 /************************************************************************/
692 /* OGRSpatialReference() */
693 /************************************************************************/
694
695 /**
696 * \brief Constructor.
697 *
698 * This constructor takes an optional string argument which if passed
699 * should be a WKT representation of an SRS. Passing this is equivalent
700 * to not passing it, and then calling importFromWkt() with the WKT string.
701 *
702 * Note that newly created objects are given a reference count of one.
703 *
704 * The C function OSRNewSpatialReference() does the same thing as this
705 * constructor.
706 *
707 * @param pszWKT well known text definition to which the object should
708 * be initialized, or NULL (the default).
709 */
710
OGRSpatialReference(const char * pszWKT)711 OGRSpatialReference::OGRSpatialReference( const char * pszWKT ) :
712 d(new Private())
713 {
714 if( pszWKT != nullptr )
715 importFromWkt( pszWKT );
716 }
717
718 /************************************************************************/
719 /* OSRNewSpatialReference() */
720 /************************************************************************/
721
722 /**
723 * \brief Constructor.
724 *
725 * This function is the same as OGRSpatialReference::OGRSpatialReference()
726 */
OSRNewSpatialReference(const char * pszWKT)727 OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference( const char *pszWKT )
728
729 {
730 OGRSpatialReference * poSRS = new OGRSpatialReference();
731
732 if( pszWKT != nullptr && strlen(pszWKT) > 0 )
733 {
734 if( poSRS->importFromWkt( pszWKT ) != OGRERR_NONE )
735 {
736 delete poSRS;
737 poSRS = nullptr;
738 }
739 }
740
741 return ToHandle( poSRS );
742 }
743
744 /************************************************************************/
745 /* OGRSpatialReference() */
746 /************************************************************************/
747
748 /** Simple copy constructor. See also Clone().
749 * @param oOther other spatial reference
750 */
OGRSpatialReference(const OGRSpatialReference & oOther)751 OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther) :
752 d(new Private())
753 {
754 *this = oOther;
755 }
756
757 /************************************************************************/
758 /* ~OGRSpatialReference() */
759 /************************************************************************/
760
761 /**
762 * \brief OGRSpatialReference destructor.
763 *
764 * The C function OSRDestroySpatialReference() does the same thing as this
765 * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
766 *
767 * @deprecated
768 */
769
~OGRSpatialReference()770 OGRSpatialReference::~OGRSpatialReference()
771
772 {
773 }
774
775 /************************************************************************/
776 /* DestroySpatialReference() */
777 /************************************************************************/
778
779 /**
780 * \brief OGRSpatialReference destructor.
781 *
782 * This static method will destroy a OGRSpatialReference. It is
783 * equivalent to calling delete on the object, but it ensures that the
784 * deallocation is properly executed within the OGR libraries heap on
785 * platforms where this can matter (win32).
786 *
787 * This function is the same as OSRDestroySpatialReference()
788 *
789 * @param poSRS the object to delete
790 *
791 * @since GDAL 1.7.0
792 */
793
DestroySpatialReference(OGRSpatialReference * poSRS)794 void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference* poSRS)
795 {
796 delete poSRS;
797 }
798
799 /************************************************************************/
800 /* OSRDestroySpatialReference() */
801 /************************************************************************/
802
803 /**
804 * \brief OGRSpatialReference destructor.
805 *
806 * This function is the same as OGRSpatialReference::~OGRSpatialReference()
807 * and OGRSpatialReference::DestroySpatialReference()
808 *
809 * @param hSRS the object to delete
810 */
OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)811 void CPL_STDCALL OSRDestroySpatialReference( OGRSpatialReferenceH hSRS )
812
813 {
814 delete ToPointer(hSRS);
815 }
816
817 /************************************************************************/
818 /* Clear() */
819 /************************************************************************/
820
821 /**
822 * \brief Wipe current definition.
823 *
824 * Returns OGRSpatialReference to a state with no definition, as it
825 * exists when first created. It does not affect reference counts.
826 */
827
Clear()828 void OGRSpatialReference::Clear()
829
830 {
831 d->clear();
832 }
833
834 /************************************************************************/
835 /* operator=() */
836 /************************************************************************/
837
838 /** Assignment operator.
839 * @param oSource SRS to assign to *this
840 * @return *this
841 */
842 OGRSpatialReference &
operator =(const OGRSpatialReference & oSource)843 OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
844
845 {
846 if( &oSource != this )
847 {
848 Clear();
849 #ifdef CPPCHECK
850 // Otherwise cppcheck would protest that nRefCount isn't modified
851 d->nRefCount = (d->nRefCount + 1) - 1;
852 #endif
853
854 oSource.d->refreshProjObj();
855 if( oSource.d->m_pj_crs )
856 d->setPjCRS(proj_clone(
857 d->getPROJContext(), oSource.d->m_pj_crs));
858 if( oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER )
859 SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
860 else if ( oSource.d->m_axisMappingStrategy == OAMS_CUSTOM )
861 SetDataAxisToSRSAxisMapping( oSource.d->m_axisMapping );
862 }
863
864 return *this;
865 }
866
867 /************************************************************************/
868 /* Reference() */
869 /************************************************************************/
870
871 /**
872 * \brief Increments the reference count by one.
873 *
874 * The reference count is used keep track of the number of OGRGeometry objects
875 * referencing this SRS.
876 *
877 * The method does the same thing as the C function OSRReference().
878 *
879 * @return the updated reference count.
880 */
881
Reference()882 int OGRSpatialReference::Reference()
883
884 {
885 return CPLAtomicInc(&d->nRefCount);
886 }
887
888 /************************************************************************/
889 /* OSRReference() */
890 /************************************************************************/
891
892 /**
893 * \brief Increments the reference count by one.
894 *
895 * This function is the same as OGRSpatialReference::Reference()
896 */
OSRReference(OGRSpatialReferenceH hSRS)897 int OSRReference( OGRSpatialReferenceH hSRS )
898
899 {
900 VALIDATE_POINTER1( hSRS, "OSRReference", 0 );
901
902 return ToPointer(hSRS)->Reference();
903 }
904
905 /************************************************************************/
906 /* Dereference() */
907 /************************************************************************/
908
909 /**
910 * \brief Decrements the reference count by one.
911 *
912 * The method does the same thing as the C function OSRDereference().
913 *
914 * @return the updated reference count.
915 */
916
Dereference()917 int OGRSpatialReference::Dereference()
918
919 {
920 if( d->nRefCount <= 0 )
921 CPLDebug( "OSR",
922 "Dereference() called on an object with refcount %d,"
923 "likely already destroyed!",
924 d->nRefCount );
925 return CPLAtomicDec(&d->nRefCount);
926 }
927
928 /************************************************************************/
929 /* OSRDereference() */
930 /************************************************************************/
931
932 /**
933 * \brief Decrements the reference count by one.
934 *
935 * This function is the same as OGRSpatialReference::Dereference()
936 */
OSRDereference(OGRSpatialReferenceH hSRS)937 int OSRDereference( OGRSpatialReferenceH hSRS )
938
939 {
940 VALIDATE_POINTER1( hSRS, "OSRDereference", 0 );
941
942 return ToPointer(hSRS)->Dereference();
943 }
944
945 /************************************************************************/
946 /* GetReferenceCount() */
947 /************************************************************************/
948
949 /**
950 * \brief Fetch current reference count.
951 *
952 * @return the current reference count.
953 */
GetReferenceCount() const954 int OGRSpatialReference::GetReferenceCount() const
955 {
956 return d->nRefCount;
957 }
958
959 /************************************************************************/
960 /* Release() */
961 /************************************************************************/
962
963 /**
964 * \brief Decrements the reference count by one, and destroy if zero.
965 *
966 * The method does the same thing as the C function OSRRelease().
967 */
968
Release()969 void OGRSpatialReference::Release()
970
971 {
972 if( Dereference() <= 0 )
973 delete this;
974 }
975
976 /************************************************************************/
977 /* OSRRelease() */
978 /************************************************************************/
979
980 /**
981 * \brief Decrements the reference count by one, and destroy if zero.
982 *
983 * This function is the same as OGRSpatialReference::Release()
984 */
OSRRelease(OGRSpatialReferenceH hSRS)985 void OSRRelease( OGRSpatialReferenceH hSRS )
986
987 {
988 VALIDATE_POINTER0( hSRS, "OSRRelease" );
989
990 ToPointer(hSRS)->Release();
991 }
992
GetRoot()993 OGR_SRSNode *OGRSpatialReference::GetRoot()
994 {
995 if( !d->m_poRoot )
996 {
997 d->refreshRootFromProjObj();
998 }
999 return d->m_poRoot;
1000 }
1001
GetRoot() const1002 const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1003 {
1004 if( !d->m_poRoot )
1005 {
1006 d->refreshRootFromProjObj();
1007 }
1008 return d->m_poRoot;
1009 }
1010
1011 /************************************************************************/
1012 /* SetRoot() */
1013 /************************************************************************/
1014
1015 /**
1016 * \brief Set the root SRS node.
1017 *
1018 * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1019 * as part of assigning the new root. Ownership of the passed OGR_SRSNode is
1020 * is assumed by the OGRSpatialReference.
1021 *
1022 * @param poNewRoot object to assign as root.
1023 */
1024
SetRoot(OGR_SRSNode * poNewRoot)1025 void OGRSpatialReference::SetRoot( OGR_SRSNode * poNewRoot )
1026
1027 {
1028 if( d->m_poRoot != poNewRoot )
1029 {
1030 delete d->m_poRoot;
1031 d->setRoot(poNewRoot);
1032 }
1033 }
1034
1035 /************************************************************************/
1036 /* GetAttrNode() */
1037 /************************************************************************/
1038
1039 /**
1040 * \brief Find named node in tree.
1041 *
1042 * This method does a pre-order traversal of the node tree searching for
1043 * a node with this exact value (case insensitive), and returns it. Leaf
1044 * nodes are not considered, under the assumption that they are just
1045 * attribute value nodes.
1046 *
1047 * If a node appears more than once in the tree (such as UNIT for instance),
1048 * the first encountered will be returned. Use GetNode() on a subtree to be
1049 * more specific.
1050 *
1051 * @param pszNodePath the name of the node to search for. May contain multiple
1052 * components such as "GEOGCS|UNIT".
1053 *
1054 * @return a pointer to the node found, or NULL if none.
1055 */
1056
GetAttrNode(const char * pszNodePath)1057 OGR_SRSNode *OGRSpatialReference::GetAttrNode( const char * pszNodePath )
1058
1059 {
1060 if( strchr(pszNodePath, '|') == nullptr )
1061 {
1062 // Fast path
1063 OGR_SRSNode *poNode = GetRoot();
1064 if( poNode )
1065 poNode = poNode->GetNode( pszNodePath );
1066 return poNode;
1067 }
1068
1069 char **papszPathTokens =
1070 CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1071
1072 if( CSLCount( papszPathTokens ) < 1 )
1073 {
1074 CSLDestroy(papszPathTokens);
1075 return nullptr;
1076 }
1077
1078 OGR_SRSNode *poNode = GetRoot();
1079 for( int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++ )
1080 {
1081 poNode = poNode->GetNode( papszPathTokens[i] );
1082 }
1083
1084 CSLDestroy( papszPathTokens );
1085
1086 return poNode;
1087 }
1088
1089 /**
1090 * \brief Find named node in tree.
1091 *
1092 * This method does a pre-order traversal of the node tree searching for
1093 * a node with this exact value (case insensitive), and returns it. Leaf
1094 * nodes are not considered, under the assumption that they are just
1095 * attribute value nodes.
1096 *
1097 * If a node appears more than once in the tree (such as UNIT for instance),
1098 * the first encountered will be returned. Use GetNode() on a subtree to be
1099 * more specific.
1100 *
1101 * @param pszNodePath the name of the node to search for. May contain multiple
1102 * components such as "GEOGCS|UNIT".
1103 *
1104 * @return a pointer to the node found, or NULL if none.
1105 */
1106
1107 const OGR_SRSNode *
GetAttrNode(const char * pszNodePath) const1108 OGRSpatialReference::GetAttrNode( const char * pszNodePath ) const
1109
1110 {
1111 OGR_SRSNode *poNode =
1112 const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1113
1114 return poNode;
1115 }
1116
1117 /************************************************************************/
1118 /* GetAttrValue() */
1119 /************************************************************************/
1120
1121 /**
1122 * \brief Fetch indicated attribute of named node.
1123 *
1124 * This method uses GetAttrNode() to find the named node, and then extracts
1125 * the value of the indicated child. Thus a call to GetAttrValue("UNIT",1)
1126 * would return the second child of the UNIT node, which is normally the
1127 * length of the linear unit in meters.
1128 *
1129 * This method does the same thing as the C function OSRGetAttrValue().
1130 *
1131 * @param pszNodeName the tree node to look for (case insensitive).
1132 * @param iAttr the child of the node to fetch (zero based).
1133 *
1134 * @return the requested value, or NULL if it fails for any reason.
1135 */
1136
GetAttrValue(const char * pszNodeName,int iAttr) const1137 const char *OGRSpatialReference::GetAttrValue( const char * pszNodeName,
1138 int iAttr ) const
1139
1140 {
1141 const OGR_SRSNode *poNode = GetAttrNode( pszNodeName );
1142 if( poNode == nullptr )
1143 {
1144 if( d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION") )
1145 {
1146 return GetAttrValue("METHOD", iAttr);
1147 }
1148 return nullptr;
1149 }
1150
1151 if( iAttr < 0 || iAttr >= poNode->GetChildCount() )
1152 return nullptr;
1153
1154 return poNode->GetChild(iAttr)->GetValue();
1155 }
1156
1157 /************************************************************************/
1158 /* OSRGetAttrValue() */
1159 /************************************************************************/
1160
1161 /**
1162 * \brief Fetch indicated attribute of named node.
1163 *
1164 * This function is the same as OGRSpatialReference::GetAttrValue()
1165 */
OSRGetAttrValue(OGRSpatialReferenceH hSRS,const char * pszKey,int iChild)1166 const char * CPL_STDCALL OSRGetAttrValue( OGRSpatialReferenceH hSRS,
1167 const char * pszKey, int iChild )
1168
1169 {
1170 VALIDATE_POINTER1( hSRS, "OSRGetAttrValue", nullptr );
1171
1172 return ToPointer(hSRS)->
1173 GetAttrValue( pszKey, iChild );
1174 }
1175
1176
1177 /************************************************************************/
1178 /* GetName() */
1179 /************************************************************************/
1180
1181 /**
1182 * \brief Return the CRS name.
1183 *
1184 * The returned value is only short lived and should not be used after other
1185 * calls to methods on this object.
1186 *
1187 * @since GDAL 3.0
1188 */
1189
GetName() const1190 const char* OGRSpatialReference::GetName() const
1191 {
1192 d->refreshProjObj();
1193 if( !d->m_pj_crs )
1194 return nullptr;
1195 return proj_get_name(d->m_pj_crs);
1196 }
1197
1198 /************************************************************************/
1199 /* OSRGetName() */
1200 /************************************************************************/
1201
1202 /**
1203 * \brief Return the CRS name.
1204 *
1205 * The returned value is only short lived and should not be used after other
1206 * calls to methods on this object.
1207 *
1208 * @since GDAL 3.0
1209 */
OSRGetName(OGRSpatialReferenceH hSRS)1210 const char* OSRGetName( OGRSpatialReferenceH hSRS )
1211
1212 {
1213 VALIDATE_POINTER1( hSRS, "OSRGetName", nullptr );
1214
1215 return ToPointer(hSRS)->GetName();
1216 }
1217
1218
1219 /************************************************************************/
1220 /* Clone() */
1221 /************************************************************************/
1222
1223 /**
1224 * \brief Make a duplicate of this OGRSpatialReference.
1225 *
1226 * This method is the same as the C function OSRClone().
1227 *
1228 * @return a new SRS, which becomes the responsibility of the caller.
1229 */
1230
Clone() const1231 OGRSpatialReference *OGRSpatialReference::Clone() const
1232
1233 {
1234 OGRSpatialReference *poNewRef = new OGRSpatialReference();
1235
1236 d->refreshProjObj();
1237 if( d->m_pj_crs != nullptr )
1238 poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1239 if( d->m_bHasCenterLong && d->m_poRoot )
1240 {
1241 poNewRef->d->setRoot(d->m_poRoot->Clone());
1242 }
1243 poNewRef->d->m_axisMapping = d->m_axisMapping;
1244 poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1245 return poNewRef;
1246 }
1247
1248 /************************************************************************/
1249 /* OSRClone() */
1250 /************************************************************************/
1251
1252 /**
1253 * \brief Make a duplicate of this OGRSpatialReference.
1254 *
1255 * This function is the same as OGRSpatialReference::Clone()
1256 */
OSRClone(OGRSpatialReferenceH hSRS)1257 OGRSpatialReferenceH CPL_STDCALL OSRClone( OGRSpatialReferenceH hSRS )
1258
1259 {
1260 VALIDATE_POINTER1( hSRS, "OSRClone", nullptr );
1261
1262 return ToHandle(
1263 ToPointer(hSRS)->Clone() );
1264 }
1265
1266 /************************************************************************/
1267 /* dumpReadable() */
1268 /************************************************************************/
1269
1270 /** Dump pretty wkt to stdout, mostly for debugging.
1271 */
dumpReadable()1272 void OGRSpatialReference::dumpReadable()
1273
1274 {
1275 char *pszPrettyWkt = nullptr;
1276
1277 const char* const apszOptions[] =
1278 { "FORMAT=WKT2", "MULTILINE=YES", nullptr };
1279 exportToWkt( &pszPrettyWkt, apszOptions );
1280 printf( "%s\n", pszPrettyWkt );/*ok*/
1281 CPLFree( pszPrettyWkt );
1282 }
1283
1284 /************************************************************************/
1285 /* exportToPrettyWkt() */
1286 /************************************************************************/
1287
1288 /**
1289 * Convert this SRS into a nicely formatted WKT 1 string for display to a person.
1290 *
1291 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1292 * for implementation details of WKT 1 in OGR.
1293 *
1294 * Note that the returned WKT string should be freed with
1295 * CPLFree() when no longer needed. It is the responsibility of the caller.
1296 *
1297 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1298 * option. Valid values are the one of the FORMAT option of
1299 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1300 *
1301 * This method is the same as the C function OSRExportToPrettyWkt().
1302 *
1303 * @param ppszResult the resulting string is returned in this pointer.
1304 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1305 * stripped off.
1306 *
1307 * @return OGRERR_NONE if successful.
1308 */
1309
exportToPrettyWkt(char ** ppszResult,int bSimplify) const1310 OGRErr OGRSpatialReference::exportToPrettyWkt( char ** ppszResult,
1311 int bSimplify ) const
1312
1313 {
1314 CPLStringList aosOptions;
1315 aosOptions.SetNameValue("MULTILINE", "YES");
1316 if( bSimplify )
1317 {
1318 aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1319 }
1320 return exportToWkt( ppszResult, aosOptions.List() );
1321 }
1322
1323 /************************************************************************/
1324 /* OSRExportToPrettyWkt() */
1325 /************************************************************************/
1326
1327 /**
1328 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1329 * person.
1330 *
1331 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1332 * option. Valid values are the one of the FORMAT option of
1333 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1334 *
1335 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1336 */
1337
OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,char ** ppszReturn,int bSimplify)1338 OGRErr CPL_STDCALL OSRExportToPrettyWkt( OGRSpatialReferenceH hSRS, char ** ppszReturn,
1339 int bSimplify)
1340
1341 {
1342 VALIDATE_POINTER1( hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE );
1343
1344 *ppszReturn = nullptr;
1345
1346 return ToPointer(hSRS)->
1347 exportToPrettyWkt( ppszReturn, bSimplify );
1348 }
1349
1350 /************************************************************************/
1351 /* exportToWkt() */
1352 /************************************************************************/
1353
1354 /**
1355 * \brief Convert this SRS into WKT 1 format.
1356 *
1357 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1358 * for implementation details of WKT 1 in OGR.
1359 *
1360 * Note that the returned WKT string should be freed with
1361 * CPLFree() when no longer needed. It is the responsibility of the caller.
1362 *
1363 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1364 * option. Valid values are the one of the FORMAT option of
1365 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1366 *
1367 * This method is the same as the C function OSRExportToWkt().
1368 *
1369 * @param ppszResult the resulting string is returned in this pointer.
1370 *
1371 * @return OGRERR_NONE if successful.
1372 */
1373
exportToWkt(char ** ppszResult) const1374 OGRErr OGRSpatialReference::exportToWkt( char ** ppszResult ) const
1375
1376 {
1377 return exportToWkt( ppszResult, nullptr );
1378 }
1379
1380 /************************************************************************/
1381 /* GDAL_proj_crs_create_bound_crs_to_WGS84() */
1382 /************************************************************************/
1383
GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT * ctx,PJ * pj,bool onlyIfEPSGCode,bool canModifyHorizPart)1384 static PJ* GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT* ctx, PJ* pj,
1385 bool onlyIfEPSGCode,
1386 bool canModifyHorizPart)
1387 {
1388 PJ* ret = nullptr;
1389 if( proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS )
1390 {
1391 auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1392 auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1393 if( horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS && vertCRS &&
1394 (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr) )
1395 {
1396 auto boundHoriz = canModifyHorizPart ?
1397 proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr) :
1398 proj_clone(ctx, horizCRS);
1399 auto boundVert = proj_crs_create_bound_crs_to_WGS84(
1400 ctx, vertCRS, nullptr);
1401 if( boundHoriz && boundVert )
1402 {
1403 ret = proj_create_compound_crs(
1404 ctx, proj_get_name(pj),
1405 boundHoriz,
1406 boundVert);
1407 }
1408 proj_destroy(boundHoriz);
1409 proj_destroy(boundVert);
1410 }
1411 proj_destroy(horizCRS);
1412 proj_destroy(vertCRS);
1413 }
1414 else if( proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1415 (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr) )
1416 {
1417 ret = proj_crs_create_bound_crs_to_WGS84(
1418 ctx, pj, nullptr);
1419 }
1420 return ret;
1421 }
1422
1423 /************************************************************************/
1424 /* exportToWkt() */
1425 /************************************************************************/
1426
1427 /**
1428 * Convert this SRS into a WKT string.
1429 *
1430 * Note that the returned WKT string should be freed with
1431 * CPLFree() when no longer needed. It is the responsibility of the caller.
1432 *
1433 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1434 * for implementation details of WKT 1 in OGR.
1435 *
1436 * @param ppszResult the resulting string is returned in this pointer.
1437 * @param papszOptions NULL terminated list of options, or NULL. Currently
1438 * supported options are
1439 * <ul>
1440 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1441 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1442 * If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1443 * node is returned.
1444 * If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1445 * node is returned.
1446 * WKT1 is an alias of WKT1_GDAL.
1447 * WKT2 will default to the latest revision implemented (currently WKT2_2018)
1448 * WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1449 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1450 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1451 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1452 * height (for example for use with LAS 1.4 WKT1).
1453 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1454 * </li>
1455 * </ul>
1456 *
1457 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1 configuration
1458 * option is set to YES, when exporting to WKT1_GDAL, this method will try
1459 * to add a TOWGS84[] node, if there's none attached yet to the SRS and if the SRS has a EPSG code.
1460 * See the AddGuessedTOWGS84() method for how this TOWGS84[] node may be added.
1461 *
1462 * @return OGRERR_NONE if successful.
1463 * @since GDAL 3.0
1464 */
1465
exportToWkt(char ** ppszResult,const char * const * papszOptions) const1466 OGRErr OGRSpatialReference::exportToWkt( char ** ppszResult,
1467 const char* const* papszOptions ) const
1468 {
1469 // In the past calling this method was thread-safe, even if we never
1470 // guaranteed it. Now proj_as_wkt() will cache the result internally,
1471 // so this is no longer thread-safe.
1472 std::lock_guard<std::mutex> oLock(d->m_mutex);
1473
1474 d->refreshProjObj();
1475 if( !d->m_pj_crs )
1476 {
1477 *ppszResult = CPLStrdup("");
1478 return OGRERR_FAILURE;
1479 }
1480
1481 if( d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI )
1482 {
1483 return d->m_poRoot->exportToWkt(ppszResult);
1484 }
1485
1486 auto ctxt = d->getPROJContext();
1487 auto wktFormat = PJ_WKT1_GDAL;
1488 const char* pszFormat = CSLFetchNameValueDef(papszOptions, "FORMAT",
1489 CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1490 if( EQUAL(pszFormat, "DEFAULT") )
1491 pszFormat = "";
1492
1493 if( EQUAL(pszFormat, "WKT1_ESRI" ) || d->m_bMorphToESRI )
1494 {
1495 wktFormat = PJ_WKT1_ESRI;
1496 }
1497 else if( EQUAL(pszFormat, "WKT1") ||
1498 EQUAL(pszFormat, "WKT1_GDAL") ||
1499 EQUAL(pszFormat, "WKT1_SIMPLE") ||
1500 EQUAL(pszFormat, "SFSQL") )
1501 {
1502 wktFormat = PJ_WKT1_GDAL;
1503 }
1504 else if( EQUAL(pszFormat, "WKT2_2015" ) )
1505 {
1506 wktFormat = PJ_WKT2_2015;
1507 }
1508 else if( EQUAL(pszFormat, "WKT2" ) ||
1509 EQUAL(pszFormat, "WKT2_2018" ) ||
1510 EQUAL(pszFormat, "WKT2_2019" ) )
1511 {
1512 wktFormat = PJ_WKT2_2018;
1513 }
1514 else if( pszFormat[0] == '\0' )
1515 {
1516 if( IsDerivedGeographic() )
1517 {
1518 wktFormat = PJ_WKT2_2018;
1519 }
1520 else if( (IsGeographic() || IsProjected()) &&
1521 !IsCompound() && GetAxesCount() == 3 )
1522 {
1523 wktFormat = PJ_WKT2_2018;
1524 }
1525 }
1526 else
1527 {
1528 CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1529 *ppszResult = CPLStrdup("");
1530 return OGRERR_FAILURE;
1531 }
1532
1533 CPLStringList aosOptions;
1534 if( wktFormat != PJ_WKT1_ESRI )
1535 {
1536 aosOptions.SetNameValue("OUTPUT_AXIS", "YES" );
1537 }
1538 aosOptions.SetNameValue("MULTILINE",
1539 CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1540
1541 const char* pszAllowEllpsHeightAsVertCS =
1542 CSLFetchNameValue(papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1543 if( pszAllowEllpsHeightAsVertCS )
1544 {
1545 aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1546 pszAllowEllpsHeightAsVertCS);
1547 }
1548
1549 PJ* boundCRS = nullptr;
1550 if( wktFormat == PJ_WKT1_GDAL &&
1551 CPLTestBool(
1552 CSLFetchNameValueDef(papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1553 CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))) )
1554 {
1555 boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1556 d->getPROJContext(), d->m_pj_crs, true, true);
1557 }
1558
1559 std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
1560 CPLInstallErrorHandlerAccumulator(aoErrors);
1561 const char* pszWKT = proj_as_wkt(
1562 ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1563 wktFormat, aosOptions.List());
1564 CPLUninstallErrorHandlerAccumulator();
1565 for( const auto& oError: aoErrors )
1566 {
1567 if( pszFormat[0] == '\0' &&
1568 (oError.msg.find("Unsupported conversion method") != std::string::npos ||
1569 oError.msg.find("can only be exported to WKT2") != std::string::npos) )
1570 {
1571 CPLErrorReset();
1572 // If we cannot export in the default mode (WKT1), retry with WKT2
1573 pszWKT = proj_as_wkt(
1574 ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1575 PJ_WKT2_2018, aosOptions.List());
1576 break;
1577 }
1578 CPLError( oError.type, oError.no, "%s", oError.msg.c_str() );
1579 }
1580
1581 if( !pszWKT )
1582 {
1583 *ppszResult = CPLStrdup("");
1584 proj_destroy(boundCRS);
1585 return OGRERR_FAILURE;
1586 }
1587
1588 if( EQUAL(pszFormat, "SFSQL" ) || EQUAL(pszFormat, "WKT1_SIMPLE") )
1589 {
1590 OGR_SRSNode oRoot;
1591 oRoot.importFromWkt(&pszWKT);
1592 oRoot.StripNodes( "AXIS" );
1593 if( EQUAL(pszFormat, "SFSQL" ) )
1594 {
1595 oRoot.StripNodes( "TOWGS84" );
1596 }
1597 oRoot.StripNodes( "AUTHORITY" );
1598 oRoot.StripNodes( "EXTENSION" );
1599 OGRErr eErr;
1600 if( CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")) )
1601 eErr = oRoot.exportToPrettyWkt( ppszResult, 1 );
1602 else
1603 eErr = oRoot.exportToWkt( ppszResult );
1604 proj_destroy(boundCRS);
1605 return eErr;
1606 }
1607
1608 *ppszResult = CPLStrdup( pszWKT );
1609 proj_destroy(boundCRS);
1610 return OGRERR_NONE;
1611 }
1612
1613 /************************************************************************/
1614 /* OSRExportToWkt() */
1615 /************************************************************************/
1616
1617 /**
1618 * \brief Convert this SRS into WKT 1 format.
1619 *
1620 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1621 * for implementation details of WKT in OGR.
1622 *
1623 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1624 * option. Valid values are the one of the FORMAT option of
1625 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1626 *
1627 * This function is the same as OGRSpatialReference::exportToWkt().
1628 */
1629
OSRExportToWkt(OGRSpatialReferenceH hSRS,char ** ppszReturn)1630 OGRErr CPL_STDCALL OSRExportToWkt( OGRSpatialReferenceH hSRS,
1631 char ** ppszReturn )
1632
1633 {
1634 VALIDATE_POINTER1( hSRS, "OSRExportToWkt", OGRERR_FAILURE );
1635
1636 *ppszReturn = nullptr;
1637
1638 return ToPointer(hSRS)->exportToWkt( ppszReturn );
1639 }
1640
1641 /************************************************************************/
1642 /* OSRExportToWktEx() */
1643 /************************************************************************/
1644
1645 /**
1646 * \brief Convert this SRS into WKT format.
1647 *
1648 * This function is the same as OGRSpatialReference::exportToWkt(char ** ppszResult,const char* const* papszOptions ) const
1649 *
1650 * @since GDAL 3.0
1651 */
1652
OSRExportToWktEx(OGRSpatialReferenceH hSRS,char ** ppszReturn,const char * const * papszOptions)1653 OGRErr OSRExportToWktEx( OGRSpatialReferenceH hSRS,
1654 char ** ppszReturn,
1655 const char* const* papszOptions )
1656 {
1657 VALIDATE_POINTER1( hSRS, "OSRExportToWktEx", OGRERR_FAILURE );
1658
1659 *ppszReturn = nullptr;
1660
1661 return ToPointer(hSRS)->exportToWkt( ppszReturn, papszOptions );
1662 }
1663
1664
1665 /************************************************************************/
1666 /* exportToPROJJSON() */
1667 /************************************************************************/
1668
1669 /**
1670 * Convert this SRS into a PROJJSON string.
1671 *
1672 * Note that the returned JSON string should be freed with
1673 * CPLFree() when no longer needed. It is the responsibility of the caller.
1674 *
1675 * @param ppszResult the resulting string is returned in this pointer.
1676 * @param papszOptions NULL terminated list of options, or NULL. Currently
1677 * supported options are
1678 * <ul>
1679 * <li>MULTILINE=YES/NO. Defaults to YES</li>
1680 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1681 * on).</li>
1682 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
1683 * disable it.</li>
1684 * </ul>
1685 *
1686 * @return OGRERR_NONE if successful.
1687 * @since GDAL 3.1 and PROJ 6.2
1688 */
1689
exportToPROJJSON(char ** ppszResult,CPL_UNUSED const char * const * papszOptions) const1690 OGRErr OGRSpatialReference::exportToPROJJSON( char ** ppszResult,
1691 CPL_UNUSED const char* const* papszOptions ) const
1692 {
1693 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 2
1694 d->refreshProjObj();
1695 if( !d->m_pj_crs )
1696 {
1697 *ppszResult = nullptr;
1698 return OGRERR_FAILURE;
1699 }
1700
1701 const char* pszPROJJSON = proj_as_projjson(
1702 d->getPROJContext(), d->m_pj_crs, papszOptions);
1703
1704 if( !pszPROJJSON )
1705 {
1706 *ppszResult = CPLStrdup("");
1707 return OGRERR_FAILURE;
1708 }
1709
1710 *ppszResult = CPLStrdup(pszPROJJSON);
1711 return OGRERR_NONE;
1712 #else
1713 CPLError(CE_Failure, CPLE_NotSupported,
1714 "exportToPROJJSON() requires PROJ 6.2 or later");
1715 *ppszResult = nullptr;
1716 return OGRERR_UNSUPPORTED_OPERATION;
1717 #endif
1718 }
1719
1720 /************************************************************************/
1721 /* OSRExportToPROJJSON() */
1722 /************************************************************************/
1723
1724 /**
1725 * \brief Convert this SRS into PROJJSON format.
1726 *
1727 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
1728 *
1729 * @since GDAL 3.1 and PROJ 6.2
1730 */
1731
OSRExportToPROJJSON(OGRSpatialReferenceH hSRS,char ** ppszReturn,const char * const * papszOptions)1732 OGRErr OSRExportToPROJJSON( OGRSpatialReferenceH hSRS,
1733 char ** ppszReturn,
1734 const char* const* papszOptions )
1735 {
1736 VALIDATE_POINTER1( hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE );
1737
1738 *ppszReturn = nullptr;
1739
1740 return ToPointer(hSRS)->exportToPROJJSON( ppszReturn, papszOptions );
1741 }
1742
1743 /************************************************************************/
1744 /* importFromWkt() */
1745 /************************************************************************/
1746
1747 /**
1748 * \brief Import from WKT string.
1749 *
1750 * This method will wipe the existing SRS definition, and
1751 * reassign it based on the contents of the passed WKT string. Only as
1752 * much of the input string as needed to construct this SRS is consumed from
1753 * the input string, and the input string pointer
1754 * is then updated to point to the remaining (unused) input.
1755 *
1756 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1757 * for implementation details of WKT in OGR.
1758 *
1759 * This method is the same as the C function OSRImportFromWkt().
1760 *
1761 * @param ppszInput Pointer to pointer to input. The pointer is updated to
1762 * point to remaining unused input text.
1763 *
1764 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
1765 * fails for any reason.
1766 * @since GDAL 2.3
1767 */
1768
importFromWkt(const char ** ppszInput)1769 OGRErr OGRSpatialReference::importFromWkt( const char ** ppszInput )
1770
1771 {
1772 if( !ppszInput || !*ppszInput )
1773 return OGRERR_FAILURE;
1774 if( strlen(*ppszInput) > 100 * 1000 &&
1775 CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")) )
1776 {
1777 CPLError(CE_Failure, CPLE_NotSupported,
1778 "Suspiciously large input for importFromWkt(). Rejecting it. "
1779 "You can remove this limitation by definition the "
1780 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
1781 return OGRERR_FAILURE;
1782 }
1783
1784 Clear();
1785
1786 bool canCache = false;
1787 auto tlsCache = OSRGetProjTLSCache();
1788 std::string osWkt;
1789 if( **ppszInput )
1790 {
1791 osWkt = *ppszInput;
1792 auto cachedObj = tlsCache->GetPJForWKT(osWkt);
1793 if( cachedObj )
1794 {
1795 d->setPjCRS(cachedObj);
1796 }
1797 else
1798 {
1799 const char* const options[] = { "STRICT=NO", nullptr };
1800 PROJ_STRING_LIST warnings = nullptr;
1801 PROJ_STRING_LIST errors = nullptr;
1802 d->setPjCRS(proj_create_from_wkt(
1803 d->getPROJContext(), *ppszInput, options, &warnings, &errors));
1804 for( auto iter = warnings; iter && *iter; ++iter ) {
1805 d->m_wktImportWarnings.push_back(*iter);
1806 }
1807 for( auto iter = errors; iter && *iter; ++iter ) {
1808 d->m_wktImportErrors.push_back(*iter);
1809 if( !d->m_pj_crs )
1810 {
1811 CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
1812 }
1813 }
1814 if( warnings == nullptr && errors == nullptr )
1815 {
1816 canCache = true;
1817 }
1818 proj_string_list_destroy(warnings);
1819 proj_string_list_destroy(errors);
1820 }
1821 }
1822 if( !d->m_pj_crs )
1823 return OGRERR_CORRUPT_DATA;
1824
1825 // Only accept CRS objects
1826 const auto type = d->m_pjType;
1827 if( type != PJ_TYPE_GEODETIC_CRS &&
1828 type != PJ_TYPE_GEOCENTRIC_CRS &&
1829 type != PJ_TYPE_GEOGRAPHIC_2D_CRS &&
1830 type != PJ_TYPE_GEOGRAPHIC_3D_CRS &&
1831 type != PJ_TYPE_VERTICAL_CRS &&
1832 type != PJ_TYPE_PROJECTED_CRS &&
1833 type != PJ_TYPE_COMPOUND_CRS &&
1834 type != PJ_TYPE_TEMPORAL_CRS &&
1835 type != PJ_TYPE_ENGINEERING_CRS &&
1836 type != PJ_TYPE_BOUND_CRS &&
1837 type != PJ_TYPE_OTHER_CRS )
1838 {
1839 Clear();
1840 return OGRERR_CORRUPT_DATA;
1841 }
1842
1843 if( canCache )
1844 {
1845 tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
1846 }
1847
1848 if( strstr(*ppszInput, "CENTER_LONG") ) {
1849 auto poRoot = new OGR_SRSNode();
1850 d->setRoot(poRoot);
1851 const char* pszTmp = *ppszInput;
1852 poRoot->importFromWkt(&pszTmp);
1853 d->m_bHasCenterLong = true;
1854 }
1855
1856 // TODO? we don't really update correctly since we assume that the
1857 // passed string is only WKT.
1858 *ppszInput += strlen(*ppszInput);
1859 return OGRERR_NONE;
1860
1861 #if no_longer_implemented_for_now
1862 /* -------------------------------------------------------------------- */
1863 /* The following seems to try and detect and unconsumed */
1864 /* VERTCS[] coordinate system definition (ESRI style) and to */
1865 /* import and attach it to the existing root. Likely we will */
1866 /* need to extend this somewhat to bring it into an acceptable */
1867 /* OGRSpatialReference organization at some point. */
1868 /* -------------------------------------------------------------------- */
1869 if( strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS") )
1870 {
1871 if( ((*ppszInput)[0]) == ',' )
1872 (*ppszInput)++;
1873 OGR_SRSNode *poNewChild = new OGR_SRSNode();
1874 poRoot->AddChild( poNewChild );
1875 return poNewChild->importFromWkt( ppszInput );
1876 }
1877 #endif
1878 }
1879
1880 /**
1881 * \brief Import from WKT string.
1882 *
1883 * This method will wipe the existing SRS definition, and
1884 * reassign it based on the contents of the passed WKT string. Only as
1885 * much of the input string as needed to construct this SRS is consumed from
1886 * the input string, and the input string pointer
1887 * is then updated to point to the remaining (unused) input.
1888 *
1889 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1890 * for implementation details of WKT in OGR.
1891 *
1892 * This method is the same as the C function OSRImportFromWkt().
1893 *
1894 * @param ppszInput Pointer to pointer to input. The pointer is updated to
1895 * point to remaining unused input text.
1896 *
1897 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
1898 * fails for any reason.
1899 * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const char*)
1900 */
1901
importFromWkt(char ** ppszInput)1902 OGRErr OGRSpatialReference::importFromWkt( char ** ppszInput )
1903
1904 {
1905 return importFromWkt( const_cast<const char**>(ppszInput) );
1906 }
1907
1908 /**
1909 * \brief Import from WKT string.
1910 *
1911 * This method will wipe the existing SRS definition, and
1912 * reassign it based on the contents of the passed WKT string. Only as
1913 * much of the input string as needed to construct this SRS is consumed from
1914 * the input string, and the input string pointer
1915 * is then updated to point to the remaining (unused) input.
1916 *
1917 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1918 * for implementation details of WKT in OGR.
1919 *
1920 * @param pszInput Input WKT
1921 *
1922 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
1923 * fails for any reason.
1924 * @since GDAL 2.3
1925 */
1926
importFromWkt(const char * pszInput)1927 OGRErr OGRSpatialReference::importFromWkt( const char* pszInput )
1928 {
1929 return importFromWkt(&pszInput);
1930 }
1931
1932 /************************************************************************/
1933 /* Validate() */
1934 /************************************************************************/
1935
1936 /**
1937 * \brief Validate CRS imported with importFromWkt() or with modified with
1938 * direct node manipulations. Otherwise the CRS should be always valid.
1939 *
1940 * This method attempts to verify that the spatial reference system is
1941 * well formed, and consists of known tokens. The validation is not
1942 * comprehensive.
1943 *
1944 * This method is the same as the C function OSRValidate().
1945 *
1946 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
1947 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
1948 * but contains non-standard PROJECTION[] values.
1949 */
1950
Validate() const1951 OGRErr OGRSpatialReference::Validate() const
1952
1953 {
1954 for( const auto& str: d->m_wktImportErrors )
1955 {
1956 CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
1957 }
1958 for( const auto& str: d->m_wktImportWarnings )
1959 {
1960 CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
1961 }
1962 if( !d->m_pj_crs || !d->m_wktImportErrors.empty() )
1963 {
1964 return OGRERR_CORRUPT_DATA;
1965 }
1966 if( !d->m_wktImportWarnings.empty() )
1967 {
1968 return OGRERR_UNSUPPORTED_SRS;
1969 }
1970 return OGRERR_NONE;
1971 }
1972
1973 /************************************************************************/
1974 /* OSRValidate() */
1975 /************************************************************************/
1976 /**
1977 * \brief Validate SRS tokens.
1978 *
1979 * This function is the same as the C++ method OGRSpatialReference::Validate().
1980 */
OSRValidate(OGRSpatialReferenceH hSRS)1981 OGRErr OSRValidate( OGRSpatialReferenceH hSRS )
1982
1983 {
1984 VALIDATE_POINTER1( hSRS, "OSRValidate", OGRERR_FAILURE );
1985
1986 return OGRSpatialReference::FromHandle(hSRS)->Validate();
1987 }
1988
1989 /************************************************************************/
1990 /* OSRImportFromWkt() */
1991 /************************************************************************/
1992
1993 /**
1994 * \brief Import from WKT string.
1995 *
1996 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System Issues</a> page
1997 * for implementation details of WKT in OGR.
1998 *
1999 * This function is the same as OGRSpatialReference::importFromWkt().
2000 */
2001
OSRImportFromWkt(OGRSpatialReferenceH hSRS,char ** ppszInput)2002 OGRErr OSRImportFromWkt( OGRSpatialReferenceH hSRS, char **ppszInput )
2003
2004 {
2005 VALIDATE_POINTER1( hSRS, "OSRImportFromWkt", OGRERR_FAILURE );
2006
2007 return ToPointer(hSRS)->importFromWkt(
2008 const_cast<const char**>(ppszInput) );
2009 }
2010
2011 /************************************************************************/
2012 /* SetNode() */
2013 /************************************************************************/
2014
2015 /**
2016 * \brief Set attribute value in spatial reference.
2017 *
2018 * Missing intermediate nodes in the path will be created if not already
2019 * in existence. If the attribute has no children one will be created and
2020 * assigned the value otherwise the zeroth child will be assigned the value.
2021 *
2022 * This method does the same as the C function OSRSetAttrValue().
2023 *
2024 * @param pszNodePath full path to attribute to be set. For instance
2025 * "PROJCS|GEOGCS|UNIT".
2026 *
2027 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2028 * This may be NULL if you just want to force creation of the intermediate
2029 * path.
2030 *
2031 * @return OGRERR_NONE on success.
2032 */
2033
SetNode(const char * pszNodePath,const char * pszNewNodeValue)2034 OGRErr OGRSpatialReference::SetNode( const char * pszNodePath,
2035 const char * pszNewNodeValue )
2036
2037 {
2038 char **papszPathTokens =
2039 CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2040
2041 if( CSLCount( papszPathTokens ) < 1 )
2042 {
2043 CSLDestroy(papszPathTokens);
2044 return OGRERR_FAILURE;
2045 }
2046
2047 if( GetRoot() == nullptr || !EQUAL(papszPathTokens[0], GetRoot()->GetValue()) )
2048 {
2049 if( EQUAL(papszPathTokens[0], "PROJCS") && CSLCount( papszPathTokens ) == 1 )
2050 {
2051 CSLDestroy(papszPathTokens);
2052 return SetProjCS(pszNewNodeValue);
2053 }
2054 else
2055 {
2056 SetRoot( new OGR_SRSNode( papszPathTokens[0] ) );
2057 }
2058 }
2059
2060 OGR_SRSNode *poNode = GetRoot();
2061 for( int i = 1; papszPathTokens[i] != nullptr; i++ )
2062 {
2063 int j = 0; // Used after for.
2064
2065 for( ; j < poNode->GetChildCount(); j++ )
2066 {
2067 if( EQUAL(poNode->GetChild( j )->GetValue(), papszPathTokens[i]) )
2068 {
2069 poNode = poNode->GetChild(j);
2070 j = -1;
2071 break;
2072 }
2073 }
2074
2075 if( j != -1 )
2076 {
2077 OGR_SRSNode *poNewNode = new OGR_SRSNode( papszPathTokens[i] );
2078 poNode->AddChild( poNewNode );
2079 poNode = poNewNode;
2080 }
2081 }
2082
2083 CSLDestroy( papszPathTokens );
2084
2085 if( pszNewNodeValue != nullptr )
2086 {
2087 if( poNode->GetChildCount() > 0 )
2088 poNode->GetChild(0)->SetValue( pszNewNodeValue );
2089 else
2090 poNode->AddChild( new OGR_SRSNode( pszNewNodeValue ) );
2091 }
2092 ;
2093 return OGRERR_NONE;
2094 }
2095
2096 /************************************************************************/
2097 /* OSRSetAttrValue() */
2098 /************************************************************************/
2099
2100 /**
2101 * \brief Set attribute value in spatial reference.
2102 *
2103 * This function is the same as OGRSpatialReference::SetNode()
2104 */
OSRSetAttrValue(OGRSpatialReferenceH hSRS,const char * pszPath,const char * pszValue)2105 OGRErr CPL_STDCALL OSRSetAttrValue( OGRSpatialReferenceH hSRS,
2106 const char * pszPath, const char * pszValue )
2107
2108 {
2109 VALIDATE_POINTER1( hSRS, "OSRSetAttrValue", OGRERR_FAILURE );
2110
2111 return ToPointer(hSRS)->
2112 SetNode( pszPath, pszValue );
2113 }
2114
2115 /************************************************************************/
2116 /* SetNode() */
2117 /************************************************************************/
2118
2119 /**
2120 * \brief Set attribute value in spatial reference.
2121 *
2122 * Missing intermediate nodes in the path will be created if not already
2123 * in existence. If the attribute has no children one will be created and
2124 * assigned the value otherwise the zeroth child will be assigned the value.
2125 *
2126 * This method does the same as the C function OSRSetAttrValue().
2127 *
2128 * @param pszNodePath full path to attribute to be set. For instance
2129 * "PROJCS|GEOGCS|UNIT".
2130 *
2131 * @param dfValue value to be assigned to node.
2132 *
2133 * @return OGRERR_NONE on success.
2134 */
2135
SetNode(const char * pszNodePath,double dfValue)2136 OGRErr OGRSpatialReference::SetNode( const char *pszNodePath,
2137 double dfValue )
2138
2139 {
2140 char szValue[64] = { '\0' };
2141
2142 if( std::abs(dfValue - static_cast<int>(dfValue)) == 0.0 )
2143 snprintf( szValue, sizeof(szValue), "%d", static_cast<int>(dfValue) );
2144 else
2145 OGRsnPrintDouble( szValue, sizeof(szValue), dfValue );
2146
2147 return SetNode( pszNodePath, szValue );
2148 }
2149
2150 /************************************************************************/
2151 /* SetAngularUnits() */
2152 /************************************************************************/
2153
2154 /**
2155 * \brief Set the angular units for the geographic coordinate system.
2156 *
2157 * This method creates a UNIT subnode with the specified values as a
2158 * child of the GEOGCS node.
2159 *
2160 * This method does the same as the C function OSRSetAngularUnits().
2161 *
2162 * @param pszUnitsName the units name to be used. Some preferred units
2163 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2164 *
2165 * @param dfInRadians the value to multiple by an angle in the indicated
2166 * units to transform to radians. Some standard conversion factors can
2167 * be found in ogr_srs_api.h.
2168 *
2169 * @return OGRERR_NONE on success.
2170 */
2171
SetAngularUnits(const char * pszUnitsName,double dfInRadians)2172 OGRErr OGRSpatialReference::SetAngularUnits( const char * pszUnitsName,
2173 double dfInRadians )
2174
2175 {
2176 d->bNormInfoSet = FALSE;
2177
2178 d->refreshProjObj();
2179 if( !d->m_pj_crs )
2180 return OGRERR_FAILURE;
2181 auto geodCRS = proj_crs_get_geodetic_crs(
2182 d->getPROJContext(), d->m_pj_crs);
2183 if( !geodCRS )
2184 return OGRERR_FAILURE;
2185 proj_destroy(geodCRS);
2186 d->demoteFromBoundCRS();
2187 d->setPjCRS(proj_crs_alter_cs_angular_unit(
2188 d->getPROJContext(), d->m_pj_crs,
2189 pszUnitsName, dfInRadians, nullptr, nullptr));
2190 d->undoDemoteFromBoundCRS();
2191
2192 d->m_osAngularUnits = pszUnitsName;
2193 d->m_dfAngularUnitToRadian = dfInRadians;
2194
2195 return OGRERR_NONE;
2196 }
2197
2198 /************************************************************************/
2199 /* OSRSetAngularUnits() */
2200 /************************************************************************/
2201
2202 /**
2203 * \brief Set the angular units for the geographic coordinate system.
2204 *
2205 * This function is the same as OGRSpatialReference::SetAngularUnits()
2206 */
OSRSetAngularUnits(OGRSpatialReferenceH hSRS,const char * pszUnits,double dfInRadians)2207 OGRErr OSRSetAngularUnits( OGRSpatialReferenceH hSRS,
2208 const char * pszUnits, double dfInRadians )
2209
2210 {
2211 VALIDATE_POINTER1( hSRS, "OSRSetAngularUnits", OGRERR_FAILURE );
2212
2213 return ToPointer(hSRS)->
2214 SetAngularUnits( pszUnits, dfInRadians );
2215 }
2216
2217 /************************************************************************/
2218 /* GetAngularUnits() */
2219 /************************************************************************/
2220
2221 /**
2222 * \brief Fetch angular geographic coordinate system units.
2223 *
2224 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2225 * will be assumed. This method only checks directly under the GEOGCS node
2226 * for units.
2227 *
2228 * This method does the same thing as the C function OSRGetAngularUnits().
2229 *
2230 * @param ppszName a pointer to be updated with the pointer to the units name.
2231 * The returned value remains internal to the OGRSpatialReference and should
2232 * not be freed, or modified. It may be invalidated on the next
2233 * OGRSpatialReference call.
2234 *
2235 * @return the value to multiply by angular distances to transform them to
2236 * radians.
2237 * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2238 */
2239
GetAngularUnits(const char ** ppszName) const2240 double OGRSpatialReference::GetAngularUnits( const char ** ppszName ) const
2241
2242 {
2243 d->refreshProjObj();
2244
2245 if( !d->m_osAngularUnits.empty() )
2246 {
2247 if( ppszName != nullptr )
2248 *ppszName = d->m_osAngularUnits.c_str();
2249 return d->m_dfAngularUnitToRadian;
2250 }
2251
2252 do
2253 {
2254 if( d->m_pj_crs == nullptr ||
2255 d->m_pjType == PJ_TYPE_ENGINEERING_CRS )
2256 {
2257 break;
2258 }
2259
2260 auto geodCRS = proj_crs_get_geodetic_crs(
2261 d->getPROJContext(), d->m_pj_crs);
2262 if( !geodCRS )
2263 {
2264 break;
2265 }
2266 auto coordSys = proj_crs_get_coordinate_system(
2267 d->getPROJContext(), geodCRS);
2268 proj_destroy(geodCRS);
2269 if( !coordSys )
2270 {
2271 break;
2272 }
2273 if( proj_cs_get_type(
2274 d->getPROJContext(), coordSys) != PJ_CS_TYPE_ELLIPSOIDAL )
2275 {
2276 proj_destroy(coordSys);
2277 break;
2278 }
2279
2280 double dfConvFactor = 0.0;
2281 const char* pszUnitName = nullptr;
2282 if( !proj_cs_get_axis_info(
2283 d->getPROJContext(), coordSys, 0, nullptr, nullptr, nullptr,
2284 &dfConvFactor, &pszUnitName, nullptr, nullptr) )
2285 {
2286 proj_destroy(coordSys);
2287 break;
2288 }
2289
2290 d->m_osAngularUnits = pszUnitName;
2291
2292 proj_destroy(coordSys);
2293 d->m_dfAngularUnitToRadian = dfConvFactor;
2294 }
2295 while(false);
2296
2297 if( d->m_osAngularUnits.empty() )
2298 {
2299 d->m_osAngularUnits = "degree";
2300 d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2301 }
2302
2303 if( ppszName != nullptr )
2304 *ppszName = d->m_osAngularUnits.c_str();
2305 return d->m_dfAngularUnitToRadian;
2306 }
2307
2308 /**
2309 * \brief Fetch angular geographic coordinate system units.
2310 *
2311 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2312 * will be assumed. This method only checks directly under the GEOGCS node
2313 * for units.
2314 *
2315 * This method does the same thing as the C function OSRGetAngularUnits().
2316 *
2317 * @param ppszName a pointer to be updated with the pointer to the units name.
2318 * The returned value remains internal to the OGRSpatialReference and should
2319 * not be freed, or modified. It may be invalidated on the next
2320 * OGRSpatialReference call.
2321 *
2322 * @return the value to multiply by angular distances to transform them to
2323 * radians.
2324 * @since GDAL 2.3.0
2325 */
2326
GetAngularUnits(char ** ppszName) const2327 double OGRSpatialReference::GetAngularUnits( char ** ppszName ) const
2328
2329 {
2330 return GetAngularUnits( const_cast<const char**>(ppszName) );
2331 }
2332
2333 /************************************************************************/
2334 /* OSRGetAngularUnits() */
2335 /************************************************************************/
2336
2337 /**
2338 * \brief Fetch angular geographic coordinate system units.
2339 *
2340 * This function is the same as OGRSpatialReference::GetAngularUnits()
2341 */
OSRGetAngularUnits(OGRSpatialReferenceH hSRS,char ** ppszName)2342 double OSRGetAngularUnits( OGRSpatialReferenceH hSRS, char ** ppszName )
2343
2344 {
2345 VALIDATE_POINTER1( hSRS, "OSRGetAngularUnits", 0 );
2346
2347 return ToPointer(hSRS)->
2348 GetAngularUnits( const_cast<const char**>(ppszName) );
2349 }
2350
2351 /************************************************************************/
2352 /* SetLinearUnitsAndUpdateParameters() */
2353 /************************************************************************/
2354
2355 /**
2356 * \brief Set the linear units for the projection.
2357 *
2358 * This method creates a UNIT subnode with the specified values as a
2359 * child of the PROJCS or LOCAL_CS node. It works the same as the
2360 * SetLinearUnits() method, but it also updates all existing linear
2361 * projection parameter values from the old units to the new units.
2362 *
2363 * @param pszName the units name to be used. Some preferred units
2364 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2365 * and SRS_UL_US_FOOT.
2366 *
2367 * @param dfInMeters the value to multiple by a length in the indicated
2368 * units to transform to meters. Some standard conversion factors can
2369 * be found in ogr_srs_api.h.
2370 *
2371 * @param pszUnitAuthority Unit authority name. Or nullptr
2372 *
2373 * @param pszUnitCode Unit code. Or nullptr
2374 *
2375 * @return OGRERR_NONE on success.
2376 */
2377
SetLinearUnitsAndUpdateParameters(const char * pszName,double dfInMeters,const char * pszUnitAuthority,const char * pszUnitCode)2378 OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2379 const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2380 const char *pszUnitCode )
2381
2382 {
2383 if( dfInMeters <= 0.0 )
2384 return OGRERR_FAILURE;
2385
2386 d->refreshProjObj();
2387 if( !d->m_pj_crs )
2388 return OGRERR_FAILURE;
2389
2390 d->demoteFromBoundCRS();
2391 if( d->m_pjType == PJ_TYPE_PROJECTED_CRS ) {
2392 d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2393 d->getPROJContext(),
2394 d->m_pj_crs, pszName, dfInMeters,
2395 pszUnitAuthority, pszUnitCode, true));
2396 }
2397 d->setPjCRS(proj_crs_alter_cs_linear_unit(
2398 d->getPROJContext(), d->m_pj_crs,
2399 pszName, dfInMeters, pszUnitAuthority, pszUnitCode));
2400 d->undoDemoteFromBoundCRS();
2401
2402 d->m_osLinearUnits = pszName;
2403 d->dfToMeter = dfInMeters;
2404
2405 return OGRERR_NONE;
2406 }
2407
2408 /************************************************************************/
2409 /* OSRSetLinearUnitsAndUpdateParameters() */
2410 /************************************************************************/
2411
2412 /**
2413 * \brief Set the linear units for the projection.
2414 *
2415 * This function is the same as
2416 * OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2417 */
OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,const char * pszUnits,double dfInMeters)2418 OGRErr OSRSetLinearUnitsAndUpdateParameters( OGRSpatialReferenceH hSRS,
2419 const char * pszUnits,
2420 double dfInMeters )
2421
2422 {
2423 VALIDATE_POINTER1( hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2424 OGRERR_FAILURE );
2425
2426 return ToPointer(hSRS)->
2427 SetLinearUnitsAndUpdateParameters( pszUnits, dfInMeters );
2428 }
2429
2430 /************************************************************************/
2431 /* SetLinearUnits() */
2432 /************************************************************************/
2433
2434 /**
2435 * \brief Set the linear units for the projection.
2436 *
2437 * This method creates a UNIT subnode with the specified values as a
2438 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2439 * Geographic 3D CRS the vertical axis units will be set.
2440 *
2441 * This method does the same as the C function OSRSetLinearUnits().
2442 *
2443 * @param pszUnitsName the units name to be used. Some preferred units
2444 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2445 * and SRS_UL_US_FOOT.
2446 *
2447 * @param dfInMeters the value to multiple by a length in the indicated
2448 * units to transform to meters. Some standard conversion factors can
2449 * be found in ogr_srs_api.h.
2450 *
2451 * @return OGRERR_NONE on success.
2452 */
2453
SetLinearUnits(const char * pszUnitsName,double dfInMeters)2454 OGRErr OGRSpatialReference::SetLinearUnits( const char * pszUnitsName,
2455 double dfInMeters )
2456
2457 {
2458 return SetTargetLinearUnits( nullptr, pszUnitsName, dfInMeters );
2459 }
2460
2461 /************************************************************************/
2462 /* OSRSetLinearUnits() */
2463 /************************************************************************/
2464
2465 /**
2466 * \brief Set the linear units for the projection.
2467 *
2468 * This function is the same as OGRSpatialReference::SetLinearUnits()
2469 */
OSRSetLinearUnits(OGRSpatialReferenceH hSRS,const char * pszUnits,double dfInMeters)2470 OGRErr OSRSetLinearUnits( OGRSpatialReferenceH hSRS,
2471 const char * pszUnits, double dfInMeters )
2472
2473 {
2474 VALIDATE_POINTER1( hSRS, "OSRSetLinearUnits", OGRERR_FAILURE );
2475
2476 return ToPointer(hSRS)->
2477 SetLinearUnits( pszUnits, dfInMeters );
2478 }
2479
2480 /************************************************************************/
2481 /* SetTargetLinearUnits() */
2482 /************************************************************************/
2483
2484 /**
2485 * \brief Set the linear units for the projection.
2486 *
2487 * This method creates a UNIT subnode with the specified values as a
2488 * child of the target node.
2489 *
2490 * This method does the same as the C function OSRSetTargetLinearUnits().
2491 *
2492 * @param pszTargetKey the keyword to set the linear units for.
2493 * i.e. "PROJCS" or "VERT_CS"
2494 *
2495 * @param pszUnitsName the units name to be used. Some preferred units
2496 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2497 * and SRS_UL_US_FOOT.
2498 *
2499 * @param dfInMeters the value to multiple by a length in the indicated
2500 * units to transform to meters. Some standard conversion factors can
2501 * be found in ogr_srs_api.h.
2502 *
2503 * @param pszUnitAuthority Unit authority name. Or nullptr
2504 *
2505 * @param pszUnitCode Unit code. Or nullptr
2506 *
2507 * @return OGRERR_NONE on success.
2508 *
2509 * @since OGR 1.9.0
2510 */
2511
SetTargetLinearUnits(const char * pszTargetKey,const char * pszUnitsName,double dfInMeters,const char * pszUnitAuthority,const char * pszUnitCode)2512 OGRErr OGRSpatialReference::SetTargetLinearUnits( const char *pszTargetKey,
2513 const char * pszUnitsName,
2514 double dfInMeters,
2515 const char *pszUnitAuthority,
2516 const char *pszUnitCode )
2517
2518 {
2519 if( dfInMeters <= 0.0 )
2520 return OGRERR_FAILURE;
2521
2522 d->refreshProjObj();
2523 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2524 if( pszTargetKey == nullptr )
2525 {
2526 if( !d->m_pj_crs )
2527 return OGRERR_FAILURE;
2528
2529 d->demoteFromBoundCRS();
2530 if( d->m_pjType == PJ_TYPE_PROJECTED_CRS ) {
2531 d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2532 d->getPROJContext(),
2533 d->m_pj_crs, pszUnitsName, dfInMeters,
2534 pszUnitAuthority, pszUnitCode, false));
2535 }
2536 d->setPjCRS(proj_crs_alter_cs_linear_unit(
2537 d->getPROJContext(), d->m_pj_crs,
2538 pszUnitsName, dfInMeters, pszUnitAuthority, pszUnitCode));
2539 d->undoDemoteFromBoundCRS();
2540
2541 d->m_osLinearUnits = pszUnitsName;
2542 d->dfToMeter = dfInMeters;
2543
2544 return OGRERR_NONE;
2545 }
2546
2547 OGR_SRSNode *poCS = GetAttrNode( pszTargetKey );
2548
2549 if( poCS == nullptr )
2550 return OGRERR_FAILURE;
2551
2552 char szValue[128] = { '\0' };
2553 if( dfInMeters < std::numeric_limits<int>::max() &&
2554 dfInMeters > std::numeric_limits<int>::min() &&
2555 dfInMeters == static_cast<int>(dfInMeters) )
2556 snprintf( szValue, sizeof(szValue),
2557 "%d", static_cast<int>(dfInMeters) );
2558 else
2559 OGRsnPrintDouble( szValue, sizeof(szValue), dfInMeters );
2560
2561 OGR_SRSNode *poUnits = nullptr;
2562 if( poCS->FindChild( "UNIT" ) >= 0 )
2563 {
2564 poUnits = poCS->GetChild( poCS->FindChild( "UNIT" ) );
2565 if( poUnits->GetChildCount() < 2 )
2566 return OGRERR_FAILURE;
2567 poUnits->GetChild(0)->SetValue( pszUnitsName );
2568 poUnits->GetChild(1)->SetValue( szValue );
2569 if( poUnits->FindChild( "AUTHORITY" ) != -1 )
2570 poUnits->DestroyChild( poUnits->FindChild( "AUTHORITY" ) );
2571 }
2572 else
2573 {
2574 poUnits = new OGR_SRSNode( "UNIT" );
2575 poUnits->AddChild( new OGR_SRSNode( pszUnitsName ) );
2576 poUnits->AddChild( new OGR_SRSNode( szValue ) );
2577
2578 poCS->AddChild( poUnits );
2579 }
2580
2581 return OGRERR_NONE;
2582 }
2583
2584 /************************************************************************/
2585 /* OSRSetLinearUnits() */
2586 /************************************************************************/
2587
2588 /**
2589 * \brief Set the linear units for the target node.
2590 *
2591 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
2592 *
2593 * @since OGR 1.9.0
2594 */
OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,const char * pszTargetKey,const char * pszUnits,double dfInMeters)2595 OGRErr OSRSetTargetLinearUnits( OGRSpatialReferenceH hSRS,
2596 const char *pszTargetKey,
2597 const char * pszUnits, double dfInMeters )
2598
2599 {
2600 VALIDATE_POINTER1( hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE );
2601
2602 return ToPointer(hSRS)->
2603 SetTargetLinearUnits( pszTargetKey, pszUnits, dfInMeters );
2604 }
2605
2606 /************************************************************************/
2607 /* GetLinearUnits() */
2608 /************************************************************************/
2609
2610 /**
2611 * \brief Fetch linear projection units.
2612 *
2613 * If no units are available, a value of "Meters" and 1.0 will be assumed.
2614 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
2615 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
2616 * axis units will be returned.
2617 *
2618 * This method does the same thing as the C function OSRGetLinearUnits()
2619 *
2620 * @param ppszName a pointer to be updated with the pointer to the units name.
2621 * The returned value remains internal to the OGRSpatialReference and should
2622 * not be freed, or modified. It may be invalidated on the next
2623 * OGRSpatialReference call.
2624 *
2625 * @return the value to multiply by linear distances to transform them to
2626 * meters.
2627 * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
2628 */
2629
GetLinearUnits(char ** ppszName) const2630 double OGRSpatialReference::GetLinearUnits( char ** ppszName ) const
2631
2632 {
2633 return GetTargetLinearUnits( nullptr, const_cast<const char**>(ppszName) );
2634 }
2635
2636 /**
2637 * \brief Fetch linear projection units.
2638 *
2639 * If no units are available, a value of "Meters" and 1.0 will be assumed.
2640 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
2641 * for units.
2642 *
2643 * This method does the same thing as the C function OSRGetLinearUnits()
2644 *
2645 * @param ppszName a pointer to be updated with the pointer to the units name.
2646 * The returned value remains internal to the OGRSpatialReference and should
2647 * not be freed, or modified. It may be invalidated on the next
2648 * OGRSpatialReference call.
2649 *
2650 * @return the value to multiply by linear distances to transform them to
2651 * meters.
2652 * @since GDAL 2.3.0
2653 */
2654
GetLinearUnits(const char ** ppszName) const2655 double OGRSpatialReference::GetLinearUnits( const char ** ppszName ) const
2656
2657 {
2658 return GetTargetLinearUnits( nullptr, ppszName );
2659 }
2660
2661 /************************************************************************/
2662 /* OSRGetLinearUnits() */
2663 /************************************************************************/
2664
2665 /**
2666 * \brief Fetch linear projection units.
2667 *
2668 * This function is the same as OGRSpatialReference::GetLinearUnits()
2669 */
OSRGetLinearUnits(OGRSpatialReferenceH hSRS,char ** ppszName)2670 double OSRGetLinearUnits( OGRSpatialReferenceH hSRS, char ** ppszName )
2671
2672 {
2673 VALIDATE_POINTER1( hSRS, "OSRGetLinearUnits", 0 );
2674
2675 return ToPointer(hSRS)->GetLinearUnits( const_cast<const char**>(ppszName) );
2676 }
2677
2678 /************************************************************************/
2679 /* GetTargetLinearUnits() */
2680 /************************************************************************/
2681
2682 /**
2683 * \brief Fetch linear units for target.
2684 *
2685 * If no units are available, a value of "Meters" and 1.0 will be assumed.
2686 *
2687 * This method does the same thing as the C function OSRGetTargetLinearUnits()
2688 *
2689 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
2690 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
2691 * GEOCCS, GEOGCS and VERT_CS are looked up)
2692 * @param ppszName a pointer to be updated with the pointer to the units name.
2693 * The returned value remains internal to the OGRSpatialReference and should not
2694 * be freed, or modified. It may be invalidated on the next
2695 * OGRSpatialReference call. ppszName can be set to NULL.
2696 *
2697 * @return the value to multiply by linear distances to transform them to
2698 * meters.
2699 *
2700 * @since OGR 1.9.0
2701 * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**) const.
2702 */
2703
GetTargetLinearUnits(const char * pszTargetKey,const char ** ppszName) const2704 double OGRSpatialReference::GetTargetLinearUnits( const char *pszTargetKey,
2705 const char ** ppszName ) const
2706
2707 {
2708 d->refreshProjObj();
2709
2710 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2711 if( pszTargetKey == nullptr )
2712 {
2713 // Use cached result if available
2714 if( !d->m_osLinearUnits.empty() )
2715 {
2716 if( ppszName )
2717 *ppszName = d->m_osLinearUnits.c_str();
2718 return d->dfToMeter;
2719 }
2720
2721 while( true )
2722 {
2723 if( d->m_pj_crs == nullptr )
2724 {
2725 break;
2726 }
2727
2728 d->demoteFromBoundCRS();
2729 PJ* coordSys = nullptr;
2730 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
2731 {
2732 auto subCRS = proj_crs_get_sub_crs(
2733 d->getPROJContext(), d->m_pj_crs, 1);
2734 if( subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS )
2735 {
2736 auto temp = proj_get_source_crs(
2737 d->getPROJContext(), subCRS);
2738 proj_destroy(subCRS);
2739 subCRS = temp;
2740 }
2741 if( subCRS && proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS )
2742 {
2743 coordSys = proj_crs_get_coordinate_system(
2744 d->getPROJContext(), subCRS);
2745 proj_destroy(subCRS);
2746 }
2747 else
2748 {
2749 proj_destroy(subCRS);
2750 d->undoDemoteFromBoundCRS();
2751 break;
2752 }
2753 }
2754 else
2755 {
2756 coordSys = proj_crs_get_coordinate_system(
2757 d->getPROJContext(), d->m_pj_crs);
2758 }
2759
2760 d->undoDemoteFromBoundCRS();
2761 if( !coordSys )
2762 {
2763 break;
2764 }
2765 auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
2766
2767 if( csType != PJ_CS_TYPE_CARTESIAN
2768 && csType != PJ_CS_TYPE_VERTICAL
2769 && csType != PJ_CS_TYPE_ELLIPSOIDAL
2770 && csType != PJ_CS_TYPE_SPHERICAL )
2771 {
2772 proj_destroy(coordSys);
2773 break;
2774 }
2775
2776 int axis = 0;
2777
2778 if ( csType == PJ_CS_TYPE_ELLIPSOIDAL
2779 || csType == PJ_CS_TYPE_SPHERICAL )
2780 {
2781 const int axisCount = proj_cs_get_axis_count(
2782 d->getPROJContext(), coordSys);
2783
2784 if( axisCount == 3 )
2785 {
2786 axis = 2;
2787 }
2788 else
2789 {
2790 proj_destroy(coordSys);
2791 break;
2792 }
2793 }
2794
2795 double dfConvFactor = 0.0;
2796 const char* pszUnitName = nullptr;
2797 if( !proj_cs_get_axis_info(
2798 d->getPROJContext(), coordSys, axis, nullptr, nullptr, nullptr,
2799 &dfConvFactor, &pszUnitName, nullptr, nullptr) )
2800 {
2801 proj_destroy(coordSys);
2802 break;
2803 }
2804
2805 d->m_osLinearUnits = pszUnitName;
2806 d->dfToMeter = dfConvFactor;
2807 if( ppszName )
2808 *ppszName = d->m_osLinearUnits.c_str();
2809
2810 proj_destroy(coordSys);
2811 return dfConvFactor;
2812 }
2813
2814 d->m_osLinearUnits = "unknown";
2815 d->dfToMeter = 1.0;
2816
2817 if( ppszName != nullptr )
2818 *ppszName = d->m_osLinearUnits.c_str();
2819 return 1.0;
2820 }
2821
2822
2823 const OGR_SRSNode *poCS = GetAttrNode( pszTargetKey );
2824
2825 if( ppszName != nullptr )
2826 *ppszName = "unknown";
2827
2828 if( poCS == nullptr )
2829 return 1.0;
2830
2831 for( int iChild = 0; iChild < poCS->GetChildCount(); iChild++ )
2832 {
2833 const OGR_SRSNode *poChild = poCS->GetChild(iChild);
2834
2835 if( EQUAL(poChild->GetValue(), "UNIT")
2836 && poChild->GetChildCount() >= 2 )
2837 {
2838 if( ppszName != nullptr )
2839 *ppszName = poChild->GetChild(0)->GetValue();
2840
2841 return CPLAtof( poChild->GetChild(1)->GetValue() );
2842 }
2843 }
2844
2845 return 1.0;
2846 }
2847
2848 /**
2849 * \brief Fetch linear units for target.
2850 *
2851 * If no units are available, a value of "Meters" and 1.0 will be assumed.
2852 *
2853 * This method does the same thing as the C function OSRGetTargetLinearUnits()
2854 *
2855 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
2856 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
2857 * GEOCCS and VERT_CS are looked up)
2858 * @param ppszName a pointer to be updated with the pointer to the units name.
2859 * The returned value remains internal to the OGRSpatialReference and should not
2860 * be freed, or modified. It may be invalidated on the next
2861 * OGRSpatialReference call. ppszName can be set to NULL.
2862 *
2863 * @return the value to multiply by linear distances to transform them to
2864 * meters.
2865 *
2866 * @since GDAL 2.3.0
2867 */
2868
GetTargetLinearUnits(const char * pszTargetKey,char ** ppszName) const2869 double OGRSpatialReference::GetTargetLinearUnits( const char *pszTargetKey,
2870 char ** ppszName ) const
2871
2872 {
2873 return GetTargetLinearUnits( pszTargetKey,
2874 const_cast<const char**>(ppszName) );
2875 }
2876
2877 /************************************************************************/
2878 /* OSRGetTargetLinearUnits() */
2879 /************************************************************************/
2880
2881 /**
2882 * \brief Fetch linear projection units.
2883 *
2884 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
2885 *
2886 * @since OGR 1.9.0
2887 */
OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,const char * pszTargetKey,char ** ppszName)2888 double OSRGetTargetLinearUnits( OGRSpatialReferenceH hSRS,
2889 const char *pszTargetKey,
2890 char ** ppszName )
2891
2892 {
2893 VALIDATE_POINTER1( hSRS, "OSRGetTargetLinearUnits", 0 );
2894
2895 return ToPointer(hSRS)->
2896 GetTargetLinearUnits( pszTargetKey, const_cast<const char**>(ppszName) );
2897 }
2898
2899 /************************************************************************/
2900 /* GetPrimeMeridian() */
2901 /************************************************************************/
2902
2903 /**
2904 * \brief Fetch prime meridian info.
2905 *
2906 * Returns the offset of the prime meridian from greenwich in degrees,
2907 * and the prime meridian name (if requested). If no PRIMEM value exists
2908 * in the coordinate system definition a value of "Greenwich" and an
2909 * offset of 0.0 is assumed.
2910 *
2911 * If the prime meridian name is returned, the pointer is to an internal
2912 * copy of the name. It should not be freed, altered or depended on after
2913 * the next OGR call.
2914 *
2915 * This method is the same as the C function OSRGetPrimeMeridian().
2916 *
2917 * @param ppszName return location for prime meridian name. If NULL, name
2918 * is not returned.
2919 *
2920 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
2921 * degrees.
2922 * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
2923 */
2924
GetPrimeMeridian(const char ** ppszName) const2925 double OGRSpatialReference::GetPrimeMeridian( const char **ppszName ) const
2926
2927 {
2928 d->refreshProjObj();
2929
2930 if( !d->m_osPrimeMeridianName.empty() )
2931 {
2932 if( ppszName != nullptr )
2933 *ppszName = d->m_osPrimeMeridianName.c_str();
2934 return d->dfFromGreenwich;
2935 }
2936
2937 while(true)
2938 {
2939 if( !d->m_pj_crs)
2940 break;
2941
2942 auto pm = proj_get_prime_meridian(
2943 d->getPROJContext(), d->m_pj_crs);
2944 if( !pm )
2945 break;
2946
2947 d->m_osPrimeMeridianName = proj_get_name(pm);
2948 if( ppszName )
2949 *ppszName = d->m_osPrimeMeridianName.c_str();
2950 double dfLongitude = 0.0;
2951 double dfConvFactor = 0.0;
2952 proj_prime_meridian_get_parameters(
2953 d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
2954 proj_destroy(pm);
2955 d->dfFromGreenwich = dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
2956 return d->dfFromGreenwich;
2957 }
2958
2959 d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
2960 d->dfFromGreenwich = 0.0;
2961 if( ppszName != nullptr )
2962 *ppszName = d->m_osPrimeMeridianName.c_str();
2963 return d->dfFromGreenwich;
2964 }
2965
2966 /**
2967 * \brief Fetch prime meridian info.
2968 *
2969 * Returns the offset of the prime meridian from greenwich in degrees,
2970 * and the prime meridian name (if requested). If no PRIMEM value exists
2971 * in the coordinate system definition a value of "Greenwich" and an
2972 * offset of 0.0 is assumed.
2973 *
2974 * If the prime meridian name is returned, the pointer is to an internal
2975 * copy of the name. It should not be freed, altered or depended on after
2976 * the next OGR call.
2977 *
2978 * This method is the same as the C function OSRGetPrimeMeridian().
2979 *
2980 * @param ppszName return location for prime meridian name. If NULL, name
2981 * is not returned.
2982 *
2983 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
2984 * degrees.
2985 * @since GDAL 2.3.0
2986 */
2987
GetPrimeMeridian(char ** ppszName) const2988 double OGRSpatialReference::GetPrimeMeridian( char **ppszName ) const
2989
2990 {
2991 return GetPrimeMeridian( const_cast<const char**>(ppszName) );
2992 }
2993
2994 /************************************************************************/
2995 /* OSRGetPrimeMeridian() */
2996 /************************************************************************/
2997
2998 /**
2999 * \brief Fetch prime meridian info.
3000 *
3001 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3002 */
OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS,char ** ppszName)3003 double OSRGetPrimeMeridian( OGRSpatialReferenceH hSRS, char **ppszName )
3004
3005 {
3006 VALIDATE_POINTER1( hSRS, "OSRGetPrimeMeridian", 0 );
3007
3008 return ToPointer(hSRS)->
3009 GetPrimeMeridian( const_cast<const char**>(ppszName) );
3010 }
3011
3012 /************************************************************************/
3013 /* SetGeogCS() */
3014 /************************************************************************/
3015
3016 /**
3017 * \brief Set geographic coordinate system.
3018 *
3019 * This method is used to set the datum, ellipsoid, prime meridian and
3020 * angular units for a geographic coordinate system. It can be used on its
3021 * own to establish a geographic spatial reference, or applied to a
3022 * projected coordinate system to establish the underlying geographic
3023 * coordinate system.
3024 *
3025 * This method does the same as the C function OSRSetGeogCS().
3026 *
3027 * @param pszGeogName user visible name for the geographic coordinate system
3028 * (not to serve as a key).
3029 *
3030 * @param pszDatumName key name for this datum. The OpenGIS specification
3031 * lists some known values, and otherwise EPSG datum names with a standard
3032 * transformation are considered legal keys.
3033 *
3034 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3035 *
3036 * @param dfSemiMajor the semi major axis of the spheroid.
3037 *
3038 * @param dfInvFlattening the inverse flattening for the spheroid.
3039 * This can be computed from the semi minor axis as
3040 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3041 *
3042 * @param pszPMName the name of the prime meridian (not to serve as a key)
3043 * If this is NULL a default value of "Greenwich" will be used.
3044 *
3045 * @param dfPMOffset the longitude of Greenwich relative to this prime
3046 * meridian. Always in Degrees
3047 *
3048 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3049 * standard names). If NULL a value of "degrees" will be assumed.
3050 *
3051 * @param dfConvertToRadians value to multiply angular units by to transform
3052 * them to radians. A value of SRS_UA_DEGREE_CONV will be used if
3053 * pszAngularUnits is NULL.
3054 *
3055 * @return OGRERR_NONE on success.
3056 */
3057
3058 OGRErr
SetGeogCS(const char * pszGeogName,const char * pszDatumName,const char * pszSpheroidName,double dfSemiMajor,double dfInvFlattening,const char * pszPMName,double dfPMOffset,const char * pszAngularUnits,double dfConvertToRadians)3059 OGRSpatialReference::SetGeogCS( const char * pszGeogName,
3060 const char * pszDatumName,
3061 const char * pszSpheroidName,
3062 double dfSemiMajor, double dfInvFlattening,
3063 const char * pszPMName, double dfPMOffset,
3064 const char * pszAngularUnits,
3065 double dfConvertToRadians )
3066
3067 {
3068 d->bNormInfoSet = FALSE;
3069 d->m_osAngularUnits.clear();
3070 d->m_dfAngularUnitToRadian = 0.0;
3071 d->m_osPrimeMeridianName.clear();
3072 d->dfFromGreenwich = 0.0;
3073
3074 /* -------------------------------------------------------------------- */
3075 /* For a geocentric coordinate system we want to set the datum */
3076 /* and ellipsoid based on the GEOGCS. Create the GEOGCS in a */
3077 /* temporary srs and use the copy method which has special */
3078 /* handling for GEOCCS. */
3079 /* -------------------------------------------------------------------- */
3080 if( IsGeocentric() )
3081 {
3082 OGRSpatialReference oGCS;
3083
3084 oGCS.SetGeogCS( pszGeogName, pszDatumName, pszSpheroidName,
3085 dfSemiMajor, dfInvFlattening,
3086 pszPMName, dfPMOffset,
3087 pszAngularUnits, dfConvertToRadians );
3088 return CopyGeogCSFrom( &oGCS );
3089 }
3090
3091 auto cs = proj_create_ellipsoidal_2D_cs(
3092 d->getPROJContext(),PJ_ELLPS2D_LATITUDE_LONGITUDE,
3093 pszAngularUnits, dfConvertToRadians);
3094 // Prime meridian expressed in Degree
3095 auto obj = proj_create_geographic_crs(
3096 d->getPROJContext(),pszGeogName, pszDatumName, pszSpheroidName,
3097 dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset,
3098 nullptr, 0.0, cs);
3099 proj_destroy(cs);
3100
3101 if( d->m_pj_crs == nullptr ||
3102 d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3103 d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS )
3104 {
3105 d->setPjCRS(obj);
3106 }
3107 else if( d->m_pjType == PJ_TYPE_PROJECTED_CRS )
3108 {
3109 d->setPjCRS(
3110 proj_crs_alter_geodetic_crs(
3111 d->getPROJContext(), d->m_pj_crs, obj));
3112 proj_destroy(obj);
3113 }
3114 else
3115 {
3116 proj_destroy(obj);
3117 }
3118
3119 return OGRERR_NONE;
3120 }
3121
3122 /************************************************************************/
3123 /* OSRSetGeogCS() */
3124 /************************************************************************/
3125
3126 /**
3127 * \brief Set geographic coordinate system.
3128 *
3129 * This function is the same as OGRSpatialReference::SetGeogCS()
3130 */
OSRSetGeogCS(OGRSpatialReferenceH hSRS,const char * pszGeogName,const char * pszDatumName,const char * pszSpheroidName,double dfSemiMajor,double dfInvFlattening,const char * pszPMName,double dfPMOffset,const char * pszAngularUnits,double dfConvertToRadians)3131 OGRErr OSRSetGeogCS( OGRSpatialReferenceH hSRS,
3132 const char * pszGeogName,
3133 const char * pszDatumName,
3134 const char * pszSpheroidName,
3135 double dfSemiMajor, double dfInvFlattening,
3136 const char * pszPMName, double dfPMOffset,
3137 const char * pszAngularUnits,
3138 double dfConvertToRadians )
3139
3140 {
3141 VALIDATE_POINTER1( hSRS, "OSRSetGeogCS", OGRERR_FAILURE );
3142
3143 return ToPointer(hSRS)->SetGeogCS(
3144 pszGeogName, pszDatumName,
3145 pszSpheroidName, dfSemiMajor, dfInvFlattening,
3146 pszPMName, dfPMOffset, pszAngularUnits, dfConvertToRadians );
3147 }
3148
3149 /************************************************************************/
3150 /* SetWellKnownGeogCS() */
3151 /************************************************************************/
3152
3153 /**
3154 * \brief Set a GeogCS based on well known name.
3155 *
3156 * This may be called on an empty OGRSpatialReference to make a geographic
3157 * coordinate system, or on something with an existing PROJCS node to
3158 * set the underlying geographic coordinate system of a projected coordinate
3159 * system.
3160 *
3161 * The following well known text values are currently supported:
3162 * <ul>
3163 * <li> "WGS84": same as "EPSG:4326" but has no dependence on EPSG data files.
3164 * <li> "WGS72": same as "EPSG:4322" but has no dependence on EPSG data files.
3165 * <li> "NAD27": same as "EPSG:4267" but has no dependence on EPSG data files.
3166 * <li> "NAD83": same as "EPSG:4269" but has no dependence on EPSG data files.
3167 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3168 * </ul>
3169 *
3170 * @param pszName name of well known geographic coordinate system.
3171 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3172 * recognised, the target object is already initialized, or an EPSG value
3173 * can't be successfully looked up.
3174 */
3175
SetWellKnownGeogCS(const char * pszName)3176 OGRErr OGRSpatialReference::SetWellKnownGeogCS( const char * pszName )
3177
3178 {
3179 /* -------------------------------------------------------------------- */
3180 /* Check for EPSG authority numbers. */
3181 /* -------------------------------------------------------------------- */
3182 if( STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:") )
3183 {
3184 OGRSpatialReference oSRS2;
3185 const OGRErr eErr = oSRS2.importFromEPSG( atoi(pszName+5) );
3186 if( eErr != OGRERR_NONE )
3187 return eErr;
3188
3189 if( !oSRS2.IsGeographic() )
3190 return OGRERR_FAILURE;
3191
3192 return CopyGeogCSFrom( &oSRS2 );
3193 }
3194
3195 /* -------------------------------------------------------------------- */
3196 /* Check for simple names. */
3197 /* -------------------------------------------------------------------- */
3198 const char *pszWKT = nullptr;
3199
3200 if( EQUAL(pszName, "WGS84") )
3201 {
3202 pszWKT = SRS_WKT_WGS84_LAT_LONG;
3203 }
3204 else if( EQUAL(pszName, "CRS84") ||
3205 EQUAL(pszName, "CRS:84") )
3206 {
3207 pszWKT = "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3208 }
3209 else if( EQUAL(pszName, "WGS72") )
3210 pszWKT =
3211 "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3212 "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3213 "AUTHORITY[\"EPSG\",\"6322\"]],"
3214 "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3215 "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3216 "AUTHORITY[\"EPSG\",\"4322\"]]";
3217
3218 else if( EQUAL(pszName, "NAD27") )
3219 pszWKT =
3220 "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3221 "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3222 "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3223 "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3224 "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3225 "AUTHORITY[\"EPSG\",\"4267\"]]";
3226
3227 else if( EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27") )
3228 pszWKT =
3229 "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3230 "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3231 "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3232 "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3233 "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3234
3235 else if( EQUAL(pszName, "NAD83") )
3236 pszWKT =
3237 "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3238 "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3239 "AUTHORITY[\"EPSG\",\"7019\"]],"
3240 "AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,"
3241 "AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,"
3242 "AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY[\"EPSG\",\"4269\"]]";
3243
3244 else if( EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83") )
3245 pszWKT =
3246 "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3247 "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3248 "AUTHORITY[\"EPSG\",\"7019\"]],"
3249 "AUTHORITY[\"EPSG\",\"6269\"]],PRIMEM[\"Greenwich\",0,"
3250 "AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,"
3251 "AUTHORITY[\"EPSG\",\"9122\"]],AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3252
3253 else
3254 return OGRERR_FAILURE;
3255
3256 /* -------------------------------------------------------------------- */
3257 /* Import the WKT */
3258 /* -------------------------------------------------------------------- */
3259 OGRSpatialReference oSRS2;
3260 const OGRErr eErr = oSRS2.importFromWkt( pszWKT );
3261 if( eErr != OGRERR_NONE )
3262 return eErr;
3263
3264 /* -------------------------------------------------------------------- */
3265 /* Copy over. */
3266 /* -------------------------------------------------------------------- */
3267 return CopyGeogCSFrom( &oSRS2 );
3268 }
3269
3270 /************************************************************************/
3271 /* OSRSetWellKnownGeogCS() */
3272 /************************************************************************/
3273
3274 /**
3275 * \brief Set a GeogCS based on well known name.
3276 *
3277 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3278 */
OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS,const char * pszName)3279 OGRErr OSRSetWellKnownGeogCS( OGRSpatialReferenceH hSRS, const char *pszName )
3280
3281 {
3282 VALIDATE_POINTER1( hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE );
3283
3284 return ToPointer(hSRS)->
3285 SetWellKnownGeogCS( pszName );
3286 }
3287
3288 /************************************************************************/
3289 /* CopyGeogCSFrom() */
3290 /************************************************************************/
3291
3292 /**
3293 * \brief Copy GEOGCS from another OGRSpatialReference.
3294 *
3295 * The GEOGCS information is copied into this OGRSpatialReference from another.
3296 * If this object has a PROJCS root already, the GEOGCS is installed within
3297 * it, otherwise it is installed as the root.
3298 *
3299 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3300 *
3301 * @return OGRERR_NONE on success or an error code.
3302 */
3303
CopyGeogCSFrom(const OGRSpatialReference * poSrcSRS)3304 OGRErr OGRSpatialReference::CopyGeogCSFrom(
3305 const OGRSpatialReference * poSrcSRS )
3306
3307 {
3308 d->bNormInfoSet = FALSE;
3309 d->m_osAngularUnits.clear();
3310 d->m_dfAngularUnitToRadian = 0.0;
3311 d->m_osPrimeMeridianName.clear();
3312 d->dfFromGreenwich = 0.0;
3313
3314 d->refreshProjObj();
3315 poSrcSRS->d->refreshProjObj();
3316 if( !poSrcSRS->d->m_pj_crs )
3317 {
3318 return OGRERR_FAILURE;
3319 }
3320 auto geodCRS = proj_crs_get_geodetic_crs(
3321 d->getPROJContext(),poSrcSRS->d->m_pj_crs);
3322 if( !geodCRS )
3323 {
3324 return OGRERR_FAILURE;
3325 }
3326
3327 /* -------------------------------------------------------------------- */
3328 /* Handle geocentric coordinate systems specially. We just */
3329 /* want to copy the DATUM. */
3330 /* -------------------------------------------------------------------- */
3331 if( d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS )
3332 {
3333 auto datum = proj_crs_get_datum(
3334 d->getPROJContext(), geodCRS);
3335 #if PROJ_VERSION_MAJOR > 7 || (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3336 if( datum == nullptr )
3337 {
3338 datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3339 }
3340 #endif
3341 if( datum == nullptr )
3342 {
3343 proj_destroy(geodCRS);
3344 return OGRERR_FAILURE;
3345 }
3346
3347 const char* pszUnitName = nullptr;
3348 double unitConvFactor = GetLinearUnits(&pszUnitName);
3349
3350 auto pj_crs = proj_create_geocentric_crs_from_datum(
3351 d->getPROJContext(),
3352 proj_get_name(d->m_pj_crs), datum, pszUnitName, unitConvFactor);
3353 proj_destroy(datum);
3354
3355 d->setPjCRS(pj_crs);
3356 }
3357
3358 else if( d->m_pjType == PJ_TYPE_PROJECTED_CRS )
3359 {
3360 auto pj_crs = proj_crs_alter_geodetic_crs(
3361 d->getPROJContext(), d->m_pj_crs, geodCRS);
3362 d->setPjCRS(pj_crs);
3363 }
3364
3365 else
3366 {
3367 d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3368 }
3369
3370 // Apply TOWGS84 of source CRS
3371 if( poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS )
3372 {
3373 auto target = proj_get_target_crs(
3374 d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3375 auto co = proj_crs_get_coordoperation(
3376 d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3377 d->setPjCRS(proj_crs_create_bound_crs(
3378 d->getPROJContext(), d->m_pj_crs, target, co));
3379 proj_destroy(target);
3380 proj_destroy(co);
3381 }
3382
3383 proj_destroy(geodCRS);
3384
3385 return OGRERR_NONE;
3386 }
3387
3388 /************************************************************************/
3389 /* OSRCopyGeogCSFrom() */
3390 /************************************************************************/
3391
3392 /**
3393 * \brief Copy GEOGCS from another OGRSpatialReference.
3394 *
3395 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3396 */
OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,const OGRSpatialReferenceH hSrcSRS)3397 OGRErr OSRCopyGeogCSFrom( OGRSpatialReferenceH hSRS,
3398 const OGRSpatialReferenceH hSrcSRS )
3399
3400 {
3401 VALIDATE_POINTER1( hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE );
3402 VALIDATE_POINTER1( hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE );
3403
3404 return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS) );
3405 }
3406
3407 /************************************************************************/
3408 /* SetFromUserInput() */
3409 /************************************************************************/
3410
3411 /**
3412 * \brief Set spatial reference from various text formats.
3413 *
3414 * This method will examine the provided input, and try to deduce the
3415 * format, and then use it to initialize the spatial reference system. It
3416 * may take the following forms:
3417 *
3418 * <ol>
3419 * <li> Well Known Text definition - passed on to importFromWkt().
3420 * <li> "EPSG:n" - number passed on to importFromEPSG().
3421 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3422 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3423 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3424 * <li> PROJ.4 definitions - passed on to importFromProj4().
3425 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3426 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3427 * WGS84 or WGS72.
3428 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3429 * <li> PROJJSON (PROJ >= 6.2)
3430 * </ol>
3431 *
3432 * It is expected that this method will be extended in the future to support
3433 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3434 * State Plane definitions.
3435 *
3436 * This method is intended to be flexible, but by its nature it is
3437 * imprecise as it must guess information about the format intended. When
3438 * possible applications should call the specific method appropriate if the
3439 * input is known to be in a particular format.
3440 *
3441 * This method does the same thing as the OSRSetFromUserInput() function.
3442 *
3443 * @param pszDefinition text definition to try to deduce SRS from.
3444 *
3445 * @return OGRERR_NONE on success, or an error code if the name isn't
3446 * recognised, the definition is corrupt, or an EPSG value can't be
3447 * successfully looked up.
3448 */
3449
SetFromUserInput(const char * pszDefinition)3450 OGRErr OGRSpatialReference::SetFromUserInput( const char * pszDefinition )
3451
3452 {
3453 if( STARTS_WITH_CI(pszDefinition, "ESRI::") )
3454 {
3455 pszDefinition += 6;
3456 }
3457
3458 /* -------------------------------------------------------------------- */
3459 /* Is it a recognised syntax? */
3460 /* -------------------------------------------------------------------- */
3461 const char* const wktKeywords[] = {
3462 // WKT1
3463 "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
3464 // WKT2"
3465 "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
3466 "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS",
3467 "ENGCRS", "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS"
3468 };
3469 for( const char* keyword: wktKeywords )
3470 {
3471 if( STARTS_WITH_CI(pszDefinition, keyword) )
3472 {
3473 return importFromWkt( pszDefinition );
3474 }
3475 }
3476
3477 if( STARTS_WITH_CI(pszDefinition, "EPSG:")
3478 || STARTS_WITH_CI(pszDefinition, "EPSGA:") )
3479 {
3480 OGRErr eStatus = OGRERR_NONE;
3481
3482 if( STARTS_WITH_CI(pszDefinition, "EPSG:") )
3483 eStatus = importFromEPSG( atoi(pszDefinition+5) );
3484
3485 else // if( STARTS_WITH_CI(pszDefinition, "EPSGA:") )
3486 eStatus = importFromEPSGA( atoi(pszDefinition+6) );
3487
3488 // Do we want to turn this into a compound definition
3489 // with a vertical datum?
3490 if( eStatus == OGRERR_NONE && strchr( pszDefinition, '+' ) != nullptr )
3491 {
3492 OGRSpatialReference oVertSRS;
3493
3494 eStatus = oVertSRS.importFromEPSG(
3495 atoi(strchr(pszDefinition, '+') + 1) );
3496 if( eStatus == OGRERR_NONE )
3497 {
3498 OGRSpatialReference oHorizSRS(*this);
3499
3500 Clear();
3501
3502 oHorizSRS.d->refreshProjObj();
3503 oVertSRS.d->refreshProjObj();
3504 if( !oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs )
3505 return OGRERR_FAILURE;
3506
3507 const char* pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
3508 const char* pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
3509
3510 CPLString osName = pszHorizName ? pszHorizName : "";
3511 osName += " + ";
3512 osName += pszVertName ? pszVertName : "";
3513
3514 SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
3515 }
3516 }
3517
3518 return eStatus;
3519 }
3520
3521 if( STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:")
3522 || STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:")
3523 || STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:")
3524 || STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:")
3525 || STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:"))
3526 return importFromURN( pszDefinition );
3527
3528 if( STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs")
3529 || STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs")
3530 || STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs")
3531 || STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs")
3532 || STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
3533 return importFromCRSURL( pszDefinition );
3534
3535 if( STARTS_WITH_CI(pszDefinition, "AUTO:") )
3536 return importFromWMSAUTO( pszDefinition );
3537
3538 // WMS/WCS OGC codes like OGC:CRS84.
3539 if( STARTS_WITH_CI(pszDefinition, "OGC:") )
3540 return SetWellKnownGeogCS( pszDefinition+4 );
3541
3542 if( STARTS_WITH_CI(pszDefinition, "CRS:") )
3543 return SetWellKnownGeogCS( pszDefinition );
3544
3545 if( STARTS_WITH_CI(pszDefinition, "DICT:")
3546 && strstr(pszDefinition, ",") )
3547 {
3548 char *pszFile = CPLStrdup(pszDefinition+5);
3549 char *pszCode = strstr(pszFile, ",") + 1;
3550
3551 pszCode[-1] = '\0';
3552
3553 OGRErr err = importFromDict( pszFile, pszCode );
3554 CPLFree( pszFile );
3555
3556 return err;
3557 }
3558
3559 if( EQUAL(pszDefinition, "NAD27")
3560 || EQUAL(pszDefinition,"NAD83")
3561 || EQUAL(pszDefinition,"WGS84")
3562 || EQUAL(pszDefinition,"WGS72") )
3563 {
3564 Clear();
3565 return SetWellKnownGeogCS( pszDefinition );
3566 }
3567
3568 // PROJJSON
3569 if( pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
3570 (strstr(pszDefinition, "GeodeticCRS") ||
3571 strstr(pszDefinition, "GeographicCRS") ||
3572 strstr(pszDefinition, "ProjectedCRS") ||
3573 strstr(pszDefinition, "VerticalCRS") ||
3574 strstr(pszDefinition, "BoundCRS") ||
3575 strstr(pszDefinition, "CompoundCRS")) )
3576 {
3577 auto obj = proj_create(d->getPROJContext(), pszDefinition);
3578 if( !obj )
3579 {
3580 return OGRERR_FAILURE;
3581 }
3582 Clear();
3583 d->setPjCRS(obj);
3584 return OGRERR_NONE;
3585 }
3586
3587 if( strstr(pszDefinition, "+proj") != nullptr
3588 || strstr(pszDefinition, "+init") != nullptr )
3589 return importFromProj4( pszDefinition );
3590
3591 if( STARTS_WITH_CI(pszDefinition, "http://") || STARTS_WITH_CI(pszDefinition, "https://") )
3592 {
3593 return importFromUrl (pszDefinition);
3594 }
3595
3596 if( EQUAL(pszDefinition, "osgb:BNG") )
3597 {
3598 return importFromEPSG(27700);
3599 }
3600
3601 // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
3602 const char* pszDot = strchr(pszDefinition, ':');
3603 if( pszDot )
3604 {
3605 CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
3606 auto authorities = proj_get_authorities_from_database(d->getPROJContext());
3607 if( authorities )
3608 {
3609 for( auto iter = authorities; *iter; ++iter )
3610 {
3611 if( *iter == osPrefix )
3612 {
3613 proj_string_list_destroy(authorities);
3614
3615 auto obj = proj_create_from_database(d->getPROJContext(),
3616 osPrefix, pszDot + 1, PJ_CATEGORY_CRS,
3617 false, nullptr);
3618 if( !obj )
3619 {
3620 return OGRERR_FAILURE;
3621 }
3622 Clear();
3623 d->setPjCRS(obj);
3624 return OGRERR_NONE;
3625 }
3626 }
3627 proj_string_list_destroy(authorities);
3628 }
3629 }
3630
3631 /* -------------------------------------------------------------------- */
3632 /* Try to open it as a file. */
3633 /* -------------------------------------------------------------------- */
3634 CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
3635 VSILFILE * const fp = VSIFOpenL( pszDefinition, "rt" );
3636 if( fp == nullptr )
3637 return OGRERR_CORRUPT_DATA;
3638
3639 const size_t nBufMax = 100000;
3640 char * const pszBuffer = static_cast<char *>( CPLMalloc(nBufMax) );
3641 const size_t nBytes = VSIFReadL( pszBuffer, 1, nBufMax-1, fp );
3642 VSIFCloseL( fp );
3643
3644 if( nBytes == nBufMax-1 )
3645 {
3646 CPLDebug( "OGR",
3647 "OGRSpatialReference::SetFromUserInput(%s), opened file "
3648 "but it is to large for our generous buffer. Is it really "
3649 "just a WKT definition?", pszDefinition );
3650 CPLFree( pszBuffer );
3651 return OGRERR_FAILURE;
3652 }
3653
3654 pszBuffer[nBytes] = '\0';
3655
3656 char *pszBufPtr = pszBuffer;
3657 while( pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n' )
3658 pszBufPtr++;
3659
3660 OGRErr err = OGRERR_NONE;
3661 if( pszBufPtr[0] == '<' )
3662 err = importFromXML( pszBufPtr );
3663 else if( (strstr(pszBuffer, "+proj") != nullptr
3664 || strstr(pszBuffer, "+init") != nullptr)
3665 && (strstr(pszBuffer, "EXTENSION") == nullptr
3666 && strstr(pszBuffer, "extension") == nullptr) )
3667 err = importFromProj4( pszBufPtr );
3668 else
3669 {
3670 if( STARTS_WITH_CI(pszBufPtr, "ESRI::") )
3671 {
3672 pszBufPtr += 6;
3673 }
3674
3675 // coverity[tainted_data]
3676 err = importFromWkt( pszBufPtr );
3677 }
3678
3679 CPLFree( pszBuffer );
3680
3681 return err;
3682 }
3683
3684 /************************************************************************/
3685 /* OSRSetFromUserInput() */
3686 /************************************************************************/
3687
3688 /**
3689 * \brief Set spatial reference from various text formats.
3690 *
3691 * This function is the same as OGRSpatialReference::SetFromUserInput()
3692 */
OSRSetFromUserInput(OGRSpatialReferenceH hSRS,const char * pszDef)3693 OGRErr CPL_STDCALL OSRSetFromUserInput( OGRSpatialReferenceH hSRS,
3694 const char *pszDef )
3695
3696 {
3697 VALIDATE_POINTER1( hSRS, "OSRSetFromUserInput", OGRERR_FAILURE );
3698
3699 return ToPointer(hSRS)->SetFromUserInput( pszDef );
3700 }
3701
3702 /************************************************************************/
3703 /* ImportFromUrl() */
3704 /************************************************************************/
3705
3706 /**
3707 * \brief Set spatial reference from a URL.
3708 *
3709 * This method will download the spatial reference at a given URL and
3710 * feed it into SetFromUserInput for you.
3711 *
3712 * This method does the same thing as the OSRImportFromUrl() function.
3713 *
3714 * @param pszUrl text definition to try to deduce SRS from.
3715 *
3716 * @return OGRERR_NONE on success, or an error code with the curl
3717 * error message if it is unable to download data.
3718 */
3719
importFromUrl(const char * pszUrl)3720 OGRErr OGRSpatialReference::importFromUrl( const char * pszUrl )
3721
3722 {
3723 if( !STARTS_WITH_CI(pszUrl, "http://") && !STARTS_WITH_CI(pszUrl, "https://") )
3724 {
3725 CPLError( CE_Failure, CPLE_AppDefined,
3726 "The given string is not recognized as a URL"
3727 "starting with 'http://' -- %s", pszUrl );
3728 return OGRERR_FAILURE;
3729 }
3730
3731 /* -------------------------------------------------------------------- */
3732 /* Fetch the result. */
3733 /* -------------------------------------------------------------------- */
3734 CPLErrorReset();
3735
3736 const char* pszHeaders = "HEADERS=Accept: application/x-ogcwkt";
3737 const char* pszTimeout = "TIMEOUT=10";
3738 char *apszOptions[] = {
3739 const_cast<char *>(pszHeaders),
3740 const_cast<char *>(pszTimeout),
3741 nullptr
3742 };
3743
3744 CPLHTTPResult *psResult = CPLHTTPFetch( pszUrl, apszOptions );
3745
3746 /* -------------------------------------------------------------------- */
3747 /* Try to handle errors. */
3748 /* -------------------------------------------------------------------- */
3749
3750 if( psResult == nullptr )
3751 return OGRERR_FAILURE;
3752 if( psResult->nDataLen == 0
3753 || CPLGetLastErrorNo() != 0 || psResult->pabyData == nullptr )
3754 {
3755 if( CPLGetLastErrorNo() == 0 )
3756 {
3757 CPLError( CE_Failure, CPLE_AppDefined,
3758 "No data was returned from the given URL" );
3759 }
3760 CPLHTTPDestroyResult( psResult );
3761 return OGRERR_FAILURE;
3762 }
3763
3764 if( psResult->nStatus != 0 )
3765 {
3766 CPLError( CE_Failure, CPLE_AppDefined,
3767 "Curl reports error: %d: %s",
3768 psResult->nStatus, psResult->pszErrBuf );
3769 CPLHTTPDestroyResult( psResult );
3770 return OGRERR_FAILURE;
3771 }
3772
3773 const char* pszData = reinterpret_cast<const char*>(psResult->pabyData);
3774 if( STARTS_WITH_CI(pszData, "http://") || STARTS_WITH_CI(pszData, "https://") )
3775 {
3776 CPLError( CE_Failure, CPLE_AppDefined,
3777 "The data that was downloaded also starts with 'http://' "
3778 "and cannot be passed into SetFromUserInput. Is this "
3779 "really a spatial reference definition? ");
3780 CPLHTTPDestroyResult( psResult );
3781 return OGRERR_FAILURE;
3782 }
3783 if( OGRERR_NONE != SetFromUserInput(pszData)) {
3784 CPLHTTPDestroyResult( psResult );
3785 return OGRERR_FAILURE;
3786 }
3787
3788 CPLHTTPDestroyResult( psResult );
3789 return OGRERR_NONE;
3790 }
3791
3792 /************************************************************************/
3793 /* OSRimportFromUrl() */
3794 /************************************************************************/
3795
3796 /**
3797 * \brief Set spatial reference from a URL.
3798 *
3799 * This function is the same as OGRSpatialReference::importFromUrl()
3800 */
OSRImportFromUrl(OGRSpatialReferenceH hSRS,const char * pszUrl)3801 OGRErr OSRImportFromUrl( OGRSpatialReferenceH hSRS, const char *pszUrl )
3802
3803 {
3804 VALIDATE_POINTER1( hSRS, "OSRImportFromUrl", OGRERR_FAILURE );
3805
3806 return
3807 ToPointer(hSRS)->importFromUrl( pszUrl );
3808 }
3809
3810 /************************************************************************/
3811 /* importFromURNPart() */
3812 /************************************************************************/
importFromURNPart(const char * pszAuthority,const char * pszCode,const char * pszURN)3813 OGRErr OGRSpatialReference::importFromURNPart(const char* pszAuthority,
3814 const char* pszCode,
3815 const char* pszURN)
3816 {
3817 #if PROJ_AT_LEAST_VERSION(8,1,0)
3818 (void)pszAuthority;
3819 (void)pszCode;
3820 (void)pszURN;
3821 return OGRERR_FAILURE;
3822 #else
3823 /* -------------------------------------------------------------------- */
3824 /* Is this an EPSG code? Note that we import it with EPSG */
3825 /* preferred axis ordering for geographic coordinate systems. */
3826 /* -------------------------------------------------------------------- */
3827 if( STARTS_WITH_CI(pszAuthority, "EPSG") )
3828 return importFromEPSGA( atoi(pszCode) );
3829
3830 /* -------------------------------------------------------------------- */
3831 /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
3832 /* -------------------------------------------------------------------- */
3833 if( STARTS_WITH_CI(pszAuthority, "IAU") )
3834 return importFromDict( "IAU2000.wkt", pszCode );
3835
3836 /* -------------------------------------------------------------------- */
3837 /* Is this an OGC code? */
3838 /* -------------------------------------------------------------------- */
3839 if( !STARTS_WITH_CI(pszAuthority, "OGC") )
3840 {
3841 CPLError( CE_Failure, CPLE_AppDefined,
3842 "URN %s has unrecognized authority.",
3843 pszURN );
3844 return OGRERR_FAILURE;
3845 }
3846
3847 if( STARTS_WITH_CI(pszCode, "CRS84") )
3848 return SetWellKnownGeogCS( pszCode );
3849 else if( STARTS_WITH_CI(pszCode, "CRS83") )
3850 return SetWellKnownGeogCS( pszCode );
3851 else if( STARTS_WITH_CI(pszCode, "CRS27") )
3852 return SetWellKnownGeogCS( pszCode );
3853 else if( STARTS_WITH_CI(pszCode, "84") ) // urn:ogc:def:crs:OGC:2:84
3854 return SetWellKnownGeogCS( "CRS84" );
3855
3856 /* -------------------------------------------------------------------- */
3857 /* Handle auto codes. We need to convert from format */
3858 /* AUTO42001:99:8888 to format AUTO:42001,99,8888. */
3859 /* -------------------------------------------------------------------- */
3860 else if( STARTS_WITH_CI(pszCode, "AUTO") )
3861 {
3862 char szWMSAuto[100] = { '\0' };
3863
3864 if( strlen(pszCode) > sizeof(szWMSAuto)-2 )
3865 return OGRERR_FAILURE;
3866
3867 snprintf( szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4 );
3868 for( int i = 5; szWMSAuto[i] != '\0'; i++ )
3869 {
3870 if( szWMSAuto[i] == ':' )
3871 szWMSAuto[i] = ',';
3872 }
3873
3874 return importFromWMSAUTO( szWMSAuto );
3875 }
3876
3877 /* -------------------------------------------------------------------- */
3878 /* Not a recognise OGC item. */
3879 /* -------------------------------------------------------------------- */
3880 CPLError( CE_Failure, CPLE_AppDefined,
3881 "URN %s value not supported.",
3882 pszURN );
3883
3884 return OGRERR_FAILURE;
3885 #endif
3886 }
3887
3888 /************************************************************************/
3889 /* importFromURN() */
3890 /* */
3891 /* See OGC recommendation paper 06-023r1 or later for details. */
3892 /************************************************************************/
3893
3894 /**
3895 * \brief Initialize from OGC URN.
3896 *
3897 * Initializes this spatial reference from a coordinate system defined
3898 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
3899 * paper 06-023r1. Currently EPSG and OGC authority values are supported,
3900 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
3901 *
3902 * This method is also support through SetFromUserInput() which can
3903 * normally be used for URNs.
3904 *
3905 * @param pszURN the urn string.
3906 *
3907 * @return OGRERR_NONE on success or an error code.
3908 */
3909
importFromURN(const char * pszURN)3910 OGRErr OGRSpatialReference::importFromURN( const char *pszURN )
3911
3912 {
3913 #if PROJ_AT_LEAST_VERSION(8,1,0)
3914 /* -------------------------------------------------------------------- */
3915 /* Is this an IAU code? Lets try for the IAU2000 dictionary. */
3916 /* -------------------------------------------------------------------- */
3917 const char* pszIAU = strstr(pszURN, "IAU");
3918 if( pszIAU )
3919 {
3920 const char* pszCode = strchr(pszIAU, ':');
3921 if( pszCode )
3922 {
3923 ++pszCode;
3924 if( *pszCode == ':' )
3925 ++pszCode;
3926 return importFromDict( "IAU2000.wkt", pszCode );
3927 }
3928 }
3929
3930 auto obj = proj_create(d->getPROJContext(), pszURN);
3931 if( !obj )
3932 {
3933 return OGRERR_FAILURE;
3934 }
3935 Clear();
3936 d->setPjCRS(obj);
3937 return OGRERR_NONE;
3938 #else
3939 const char *pszCur = nullptr;
3940
3941 if( STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:") )
3942 pszCur = pszURN + 16;
3943 else if( STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:") )
3944 pszCur = pszURN + 20;
3945 else if( STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:") )
3946 pszCur = pszURN + 18;
3947 else if( STARTS_WITH_CI(pszURN, "urn:opengis:crs:") )
3948 pszCur = pszURN + 16;
3949 else if( STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:") )
3950 pszCur = pszURN + 20;
3951 else
3952 {
3953 CPLError( CE_Failure, CPLE_AppDefined,
3954 "URN %s not a supported format.", pszURN );
3955 return OGRERR_FAILURE;
3956 }
3957
3958 /* -------------------------------------------------------------------- */
3959 /* Clear any existing definition. */
3960 /* -------------------------------------------------------------------- */
3961 Clear();
3962
3963 /* -------------------------------------------------------------------- */
3964 /* Find code (ignoring version) out of string like: */
3965 /* */
3966 /* authority:[version]:code */
3967 /* -------------------------------------------------------------------- */
3968 const char *pszAuthority = pszCur;
3969
3970 // skip authority
3971 while( *pszCur != ':' && *pszCur )
3972 pszCur++;
3973 if( *pszCur == ':' )
3974 pszCur++;
3975
3976 // skip version
3977 const char* pszBeforeVersion = pszCur;
3978 while( *pszCur != ':' && *pszCur )
3979 pszCur++;
3980 if( *pszCur == ':' )
3981 pszCur++;
3982 else
3983 // We come here in the case, the content to parse is authority:code
3984 // (instead of authority::code) which is probably illegal according to
3985 // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
3986 // for example in what is returned by GeoServer.
3987 pszCur = pszBeforeVersion;
3988
3989 const char *pszCode = pszCur;
3990
3991 const char* pszComma = strchr(pszCur, ',');
3992 if( pszComma == nullptr )
3993 return importFromURNPart(pszAuthority, pszCode, pszURN);
3994
3995 // There's a second part with the vertical SRS.
3996 pszCur = pszComma + 1;
3997 if( !STARTS_WITH(pszCur, "crs:") )
3998 {
3999 CPLError( CE_Failure, CPLE_AppDefined,
4000 "URN %s not a supported format.", pszURN );
4001 return OGRERR_FAILURE;
4002 }
4003
4004 pszCur += 4;
4005
4006 char* pszFirstCode = CPLStrdup(pszCode);
4007 pszFirstCode[pszComma - pszCode] = '\0';
4008 OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4009 CPLFree(pszFirstCode);
4010
4011 // Do we want to turn this into a compound definition
4012 // with a vertical datum?
4013 if( eStatus != OGRERR_NONE )
4014 return eStatus;
4015
4016 /* -------------------------------------------------------------------- */
4017 /* Find code (ignoring version) out of string like: */
4018 /* */
4019 /* authority:[version]:code */
4020 /* -------------------------------------------------------------------- */
4021 pszAuthority = pszCur;
4022
4023 // skip authority
4024 while( *pszCur != ':' && *pszCur )
4025 pszCur++;
4026 if( *pszCur == ':' )
4027 pszCur++;
4028
4029 // skip version
4030 pszBeforeVersion = pszCur;
4031 while( *pszCur != ':' && *pszCur )
4032 pszCur++;
4033 if( *pszCur == ':' )
4034 pszCur++;
4035 else
4036 pszCur = pszBeforeVersion;
4037
4038 pszCode = pszCur;
4039
4040 OGRSpatialReference oVertSRS;
4041 eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4042 if( eStatus == OGRERR_NONE )
4043 {
4044 OGRSpatialReference oHorizSRS(*this);
4045
4046 Clear();
4047
4048 oHorizSRS.d->refreshProjObj();
4049 oVertSRS.d->refreshProjObj();
4050 if( !oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs )
4051 return OGRERR_FAILURE;
4052
4053 const char* pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4054 const char* pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4055
4056 CPLString osName = pszHorizName ? pszHorizName : "";
4057 osName += " + ";
4058 osName += pszVertName ? pszVertName : "";
4059
4060 SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4061 }
4062
4063 return eStatus;
4064 #endif
4065 }
4066
4067 /************************************************************************/
4068 /* importFromCRSURL() */
4069 /* */
4070 /* See OGC Best Practice document 11-135 for details. */
4071 /************************************************************************/
4072
4073 /**
4074 * \brief Initialize from OGC URL.
4075 *
4076 * Initializes this spatial reference from a coordinate system defined
4077 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4078 * paper 11-135. Currently EPSG and OGC authority values are supported,
4079 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4080 *
4081 * This method is also supported through SetFromUserInput() which can
4082 * normally be used for URLs.
4083 *
4084 * @param pszURL the URL string.
4085 *
4086 * @return OGRERR_NONE on success or an error code.
4087 */
4088
importFromCRSURL(const char * pszURL)4089 OGRErr OGRSpatialReference::importFromCRSURL( const char *pszURL )
4090
4091 {
4092 #if PROJ_AT_LEAST_VERSION(8,1,0)
4093 auto obj = proj_create(d->getPROJContext(), pszURL);
4094 if( !obj )
4095 {
4096 return OGRERR_FAILURE;
4097 }
4098 Clear();
4099 d->setPjCRS(obj);
4100 return OGRERR_NONE;
4101 #else
4102 const char *pszCur = nullptr;
4103
4104 if( STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs") )
4105 pszCur = pszURL + 26;
4106 else if( STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs") )
4107 pszCur = pszURL + 27;
4108 else if( STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs") )
4109 pszCur = pszURL + 30;
4110 else if( STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs") )
4111 pszCur = pszURL + 31;
4112 else if( STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs") )
4113 pszCur = pszURL + 23;
4114 else
4115 {
4116 CPLError( CE_Failure, CPLE_AppDefined,
4117 "URL %s not a supported format.", pszURL );
4118 return OGRERR_FAILURE;
4119 }
4120
4121 if( *pszCur == '\0' )
4122 {
4123 CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4124 return OGRERR_FAILURE;
4125 }
4126
4127 /* -------------------------------------------------------------------- */
4128 /* Clear any existing definition. */
4129 /* -------------------------------------------------------------------- */
4130 Clear();
4131
4132 if( STARTS_WITH_CI(pszCur, "-compound?1=") )
4133 {
4134 /* -------------------------------------------------------------------- */
4135 /* It's a compound CRS, of the form: */
4136 /* */
4137 /* http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4138 /* -------------------------------------------------------------------- */
4139 pszCur += 12;
4140
4141 // Extract each component CRS URL.
4142 int iComponentUrl = 2;
4143
4144 CPLString osName = "";
4145 Clear();
4146
4147 while( iComponentUrl != -1 )
4148 {
4149 char searchStr[15] = {};
4150 snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4151
4152 const char* pszUrlEnd = strstr(pszCur, searchStr);
4153
4154 // Figure out the next component URL.
4155 char* pszComponentUrl = nullptr;
4156
4157 if( pszUrlEnd )
4158 {
4159 size_t nLen = pszUrlEnd - pszCur;
4160 pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4161 strncpy(pszComponentUrl, pszCur, nLen);
4162 pszComponentUrl[nLen] = '\0';
4163
4164 ++iComponentUrl;
4165 pszCur += nLen + strlen(searchStr);
4166 }
4167 else
4168 {
4169 if( iComponentUrl == 2 )
4170 {
4171 CPLError( CE_Failure, CPLE_AppDefined,
4172 "Compound CRS URLs must have at least two component CRSs." );
4173 return OGRERR_FAILURE;
4174 }
4175 else
4176 {
4177 pszComponentUrl = CPLStrdup(pszCur);
4178 // no more components
4179 iComponentUrl = -1;
4180 }
4181 }
4182
4183 OGRSpatialReference oComponentSRS;
4184 OGRErr eStatus = oComponentSRS.importFromCRSURL( pszComponentUrl );
4185
4186 CPLFree(pszComponentUrl);
4187 pszComponentUrl = nullptr;
4188
4189 if( eStatus == OGRERR_NONE )
4190 {
4191 if( osName.length() != 0 )
4192 {
4193 osName += " + ";
4194 }
4195 osName += oComponentSRS.GetRoot()->GetValue();
4196 SetNode( "COMPD_CS", osName );
4197 GetRoot()->AddChild( oComponentSRS.GetRoot()->Clone() );
4198 }
4199 else
4200 return eStatus;
4201 }
4202
4203 return OGRERR_NONE;
4204 }
4205
4206 /* -------------------------------------------------------------------- */
4207 /* It's a normal CRS URL, of the form: */
4208 /* */
4209 /* http://opengis.net/def/crs/AUTHORITY/VERSION/CODE */
4210 /* -------------------------------------------------------------------- */
4211 ++pszCur;
4212 const char *pszAuthority = pszCur;
4213
4214 // skip authority
4215 while( *pszCur != '/' && *pszCur )
4216 pszCur++;
4217 if( *pszCur == '/' )
4218 pszCur++;
4219
4220 // skip version
4221 while( *pszCur != '/' && *pszCur )
4222 pszCur++;
4223 if( *pszCur == '/' )
4224 pszCur++;
4225
4226 const char *pszCode = pszCur;
4227
4228 return importFromURNPart( pszAuthority, pszCode, pszURL );
4229 #endif
4230 }
4231
4232 /************************************************************************/
4233 /* importFromWMSAUTO() */
4234 /************************************************************************/
4235
4236 /**
4237 * \brief Initialize from WMSAUTO string.
4238 *
4239 * Note that the WMS 1.3 specification does not include the
4240 * units code, while apparently earlier specs do. We try to
4241 * guess around this.
4242 *
4243 * @param pszDefinition the WMSAUTO string
4244 *
4245 * @return OGRERR_NONE on success or an error code.
4246 */
importFromWMSAUTO(const char * pszDefinition)4247 OGRErr OGRSpatialReference::importFromWMSAUTO( const char * pszDefinition )
4248
4249 {
4250 #if PROJ_AT_LEAST_VERSION(8,1,0)
4251 auto obj = proj_create(d->getPROJContext(), pszDefinition);
4252 if( !obj )
4253 {
4254 return OGRERR_FAILURE;
4255 }
4256 Clear();
4257 d->setPjCRS(obj);
4258 return OGRERR_NONE;
4259 #else
4260 int nProjId, nUnitsId;
4261 double dfRefLong, dfRefLat = 0.0;
4262
4263 /* -------------------------------------------------------------------- */
4264 /* Tokenize */
4265 /* -------------------------------------------------------------------- */
4266 if( STARTS_WITH_CI(pszDefinition, "AUTO:") )
4267 pszDefinition += 5;
4268
4269 char **papszTokens =
4270 CSLTokenizeStringComplex( pszDefinition, ",", FALSE, TRUE );
4271
4272 if( CSLCount(papszTokens) == 4 )
4273 {
4274 nProjId = atoi(papszTokens[0]);
4275 nUnitsId = atoi(papszTokens[1]);
4276 dfRefLong = CPLAtof(papszTokens[2]);
4277 dfRefLat = CPLAtof(papszTokens[3]);
4278 }
4279 else if( CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005 )
4280 {
4281 nProjId = atoi(papszTokens[0]);
4282 nUnitsId = atoi(papszTokens[1]);
4283 dfRefLong = CPLAtof(papszTokens[2]);
4284 dfRefLat = 0.0;
4285 }
4286 else if( CSLCount(papszTokens) == 3 )
4287 {
4288 nProjId = atoi(papszTokens[0]);
4289 nUnitsId = 9001;
4290 dfRefLong = CPLAtof(papszTokens[1]);
4291 dfRefLat = CPLAtof(papszTokens[2]);
4292 }
4293 else if( CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005 )
4294 {
4295 nProjId = atoi(papszTokens[0]);
4296 nUnitsId = 9001;
4297 dfRefLong = CPLAtof(papszTokens[1]);
4298 }
4299 else
4300 {
4301 CSLDestroy( papszTokens );
4302 CPLError( CE_Failure, CPLE_AppDefined,
4303 "AUTO projection has wrong number of arguments, expected\n"
4304 "AUTO:proj_id,units_id,ref_long,ref_lat or"
4305 "AUTO:proj_id,ref_long,ref_lat" );
4306 return OGRERR_FAILURE;
4307 }
4308
4309 CSLDestroy( papszTokens );
4310 papszTokens = nullptr;
4311
4312 /* -------------------------------------------------------------------- */
4313 /* Build coordsys. */
4314 /* -------------------------------------------------------------------- */
4315 Clear();
4316
4317 /* -------------------------------------------------------------------- */
4318 /* Set WGS84. */
4319 /* -------------------------------------------------------------------- */
4320 SetWellKnownGeogCS( "WGS84" );
4321
4322 switch( nProjId )
4323 {
4324 case 42001: // Auto UTM
4325 SetUTM( static_cast<int>(floor( (dfRefLong + 180.0) / 6.0 )) + 1,
4326 dfRefLat >= 0.0 );
4327 break;
4328
4329 case 42002: // Auto TM (strangely very UTM-like).
4330 SetTM( 0, dfRefLong, 0.9996,
4331 500000.0, (dfRefLat >= 0.0) ? 0.0 : 10000000.0 );
4332 break;
4333
4334 case 42003: // Auto Orthographic.
4335 SetOrthographic( dfRefLat, dfRefLong, 0.0, 0.0 );
4336 break;
4337
4338 case 42004: // Auto Equirectangular
4339 SetEquirectangular( dfRefLat, dfRefLong, 0.0, 0.0 );
4340 break;
4341
4342 case 42005:
4343 SetMollweide( dfRefLong, 0.0, 0.0 );
4344 break;
4345
4346 default:
4347 CPLError( CE_Failure, CPLE_AppDefined,
4348 "Unsupported projection id in importFromWMSAUTO(): %d",
4349 nProjId );
4350 return OGRERR_FAILURE;
4351 }
4352
4353 /* -------------------------------------------------------------------- */
4354 /* Set units. */
4355 /* -------------------------------------------------------------------- */
4356
4357 switch( nUnitsId )
4358 {
4359 case 9001:
4360 SetTargetLinearUnits( nullptr, SRS_UL_METER, 1.0, "EPSG", "9001" );
4361 break;
4362
4363 case 9002:
4364 SetTargetLinearUnits( nullptr, "Foot", 0.3048, "EPSG", "9002" );
4365 break;
4366
4367 case 9003:
4368 SetTargetLinearUnits( nullptr, "US survey foot", CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003" );
4369 break;
4370
4371 default:
4372 CPLError( CE_Failure, CPLE_AppDefined,
4373 "Unsupported units code (%d).",
4374 nUnitsId );
4375 return OGRERR_FAILURE;
4376 break;
4377 }
4378
4379 return OGRERR_NONE;
4380 #endif
4381 }
4382
4383 /************************************************************************/
4384 /* GetSemiMajor() */
4385 /************************************************************************/
4386
4387 /**
4388 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
4389 *
4390 * This method does the same thing as the C function OSRGetSemiMajor().
4391 *
4392 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
4393 * can be found.
4394 *
4395 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
4396 */
4397
GetSemiMajor(OGRErr * pnErr) const4398 double OGRSpatialReference::GetSemiMajor( OGRErr * pnErr ) const
4399
4400 {
4401 if( pnErr != nullptr )
4402 *pnErr = OGRERR_FAILURE;
4403
4404 d->refreshProjObj();
4405 if( !d->m_pj_crs )
4406 return SRS_WGS84_SEMIMAJOR;
4407
4408 auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
4409 if( !ellps )
4410 return SRS_WGS84_SEMIMAJOR;
4411
4412 double dfSemiMajor = 0.0;
4413 proj_ellipsoid_get_parameters(
4414 d->getPROJContext(), ellps, &dfSemiMajor, nullptr, nullptr, nullptr);
4415 proj_destroy(ellps);
4416
4417 if( dfSemiMajor > 0 )
4418 {
4419 if( pnErr != nullptr )
4420 *pnErr = OGRERR_NONE;
4421 return dfSemiMajor;
4422 }
4423
4424 return SRS_WGS84_SEMIMAJOR;
4425 }
4426
4427 /************************************************************************/
4428 /* OSRGetSemiMajor() */
4429 /************************************************************************/
4430
4431 /**
4432 * \brief Get spheroid semi major axis.
4433 *
4434 * This function is the same as OGRSpatialReference::GetSemiMajor()
4435 */
OSRGetSemiMajor(OGRSpatialReferenceH hSRS,OGRErr * pnErr)4436 double OSRGetSemiMajor( OGRSpatialReferenceH hSRS, OGRErr *pnErr )
4437
4438 {
4439 VALIDATE_POINTER1( hSRS, "OSRGetSemiMajor", 0 );
4440
4441 return ToPointer(hSRS)->GetSemiMajor( pnErr );
4442 }
4443
4444 /************************************************************************/
4445 /* GetInvFlattening() */
4446 /************************************************************************/
4447
4448 /**
4449 * \brief Get spheroid inverse flattening.
4450 *
4451 * This method does the same thing as the C function OSRGetInvFlattening().
4452 *
4453 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
4454 * can be found.
4455 *
4456 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
4457 */
4458
GetInvFlattening(OGRErr * pnErr) const4459 double OGRSpatialReference::GetInvFlattening( OGRErr * pnErr ) const
4460
4461 {
4462 if( pnErr != nullptr )
4463 *pnErr = OGRERR_FAILURE;
4464
4465 d->refreshProjObj();
4466 if( !d->m_pj_crs )
4467 return SRS_WGS84_INVFLATTENING;
4468
4469 auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
4470 if( !ellps )
4471 return SRS_WGS84_INVFLATTENING;
4472
4473 double dfInvFlattening = -1.0;
4474 proj_ellipsoid_get_parameters(
4475 d->getPROJContext(), ellps, nullptr, nullptr, nullptr, &dfInvFlattening);
4476 proj_destroy(ellps);
4477
4478 if( dfInvFlattening >= 0.0 )
4479 {
4480 if( pnErr != nullptr )
4481 *pnErr = OGRERR_NONE;
4482 return dfInvFlattening;
4483 }
4484
4485 return SRS_WGS84_INVFLATTENING;
4486 }
4487
4488 /************************************************************************/
4489 /* OSRGetInvFlattening() */
4490 /************************************************************************/
4491
4492 /**
4493 * \brief Get spheroid inverse flattening.
4494 *
4495 * This function is the same as OGRSpatialReference::GetInvFlattening()
4496 */
OSRGetInvFlattening(OGRSpatialReferenceH hSRS,OGRErr * pnErr)4497 double OSRGetInvFlattening( OGRSpatialReferenceH hSRS, OGRErr *pnErr )
4498
4499 {
4500 VALIDATE_POINTER1( hSRS, "OSRGetInvFlattening", 0 );
4501
4502 return ToPointer(hSRS)->GetInvFlattening( pnErr );
4503 }
4504
4505 /************************************************************************/
4506 /* GetEccentricity() */
4507 /************************************************************************/
4508
4509 /**
4510 * \brief Get spheroid eccentricity
4511 *
4512 * @return eccentricity (or -1 in case of error)
4513 * @since GDAL 2.3
4514 */
4515
GetEccentricity() const4516 double OGRSpatialReference::GetEccentricity() const
4517
4518 {
4519 OGRErr eErr = OGRERR_NONE;
4520 const double dfInvFlattening = GetInvFlattening(&eErr);
4521 if( eErr != OGRERR_NONE )
4522 {
4523 return -1.0;
4524 }
4525 if( dfInvFlattening == 0.0 )
4526 return 0.0;
4527 if( dfInvFlattening < 0.5 )
4528 return -1.0;
4529 return sqrt(2.0 / dfInvFlattening -
4530 1.0 / (dfInvFlattening * dfInvFlattening));
4531 }
4532
4533 /************************************************************************/
4534 /* GetSquaredEccentricity() */
4535 /************************************************************************/
4536
4537 /**
4538 * \brief Get spheroid squared eccentricity
4539 *
4540 * @return squared eccentricity (or -1 in case of error)
4541 * @since GDAL 2.3
4542 */
4543
GetSquaredEccentricity() const4544 double OGRSpatialReference::GetSquaredEccentricity() const
4545
4546 {
4547 OGRErr eErr = OGRERR_NONE;
4548 const double dfInvFlattening = GetInvFlattening(&eErr);
4549 if( eErr != OGRERR_NONE )
4550 {
4551 return -1.0;
4552 }
4553 if( dfInvFlattening == 0.0 )
4554 return 0.0;
4555 if( dfInvFlattening < 0.5 )
4556 return -1.0;
4557 return 2.0 / dfInvFlattening -
4558 1.0 / (dfInvFlattening * dfInvFlattening);
4559 }
4560
4561 /************************************************************************/
4562 /* GetSemiMinor() */
4563 /************************************************************************/
4564
4565 /**
4566 * \brief Get spheroid semi minor axis.
4567 *
4568 * This method does the same thing as the C function OSRGetSemiMinor().
4569 *
4570 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
4571 * can be found.
4572 *
4573 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
4574 */
4575
GetSemiMinor(OGRErr * pnErr) const4576 double OGRSpatialReference::GetSemiMinor( OGRErr * pnErr ) const
4577
4578 {
4579 const double dfSemiMajor = GetSemiMajor( pnErr );
4580 const double dfInvFlattening = GetInvFlattening( pnErr );
4581
4582 return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
4583 }
4584
4585 /************************************************************************/
4586 /* OSRGetSemiMinor() */
4587 /************************************************************************/
4588
4589 /**
4590 * \brief Get spheroid semi minor axis.
4591 *
4592 * This function is the same as OGRSpatialReference::GetSemiMinor()
4593 */
OSRGetSemiMinor(OGRSpatialReferenceH hSRS,OGRErr * pnErr)4594 double OSRGetSemiMinor( OGRSpatialReferenceH hSRS, OGRErr *pnErr )
4595
4596 {
4597 VALIDATE_POINTER1( hSRS, "OSRGetSemiMinor", 0 );
4598
4599 return ToPointer(hSRS)->GetSemiMinor( pnErr );
4600 }
4601
4602 /************************************************************************/
4603 /* SetLocalCS() */
4604 /************************************************************************/
4605
4606 /**
4607 * \brief Set the user visible LOCAL_CS name.
4608 *
4609 * This method is the same as the C function OSRSetLocalCS().
4610 *
4611 * This method will ensure a LOCAL_CS node is created as the root,
4612 * and set the provided name on it. It must be used before SetLinearUnits().
4613 *
4614 * @param pszName the user visible name to assign. Not used as a key.
4615 *
4616 * @return OGRERR_NONE on success.
4617 */
4618
SetLocalCS(const char * pszName)4619 OGRErr OGRSpatialReference::SetLocalCS( const char * pszName )
4620
4621 {
4622 if( d->m_pjType == PJ_TYPE_UNKNOWN ||
4623 d->m_pjType == PJ_TYPE_ENGINEERING_CRS )
4624 {
4625 d->setPjCRS(proj_create_engineering_crs(
4626 d->getPROJContext(), pszName));
4627 }
4628 else
4629 {
4630 CPLDebug( "OGR",
4631 "OGRSpatialReference::SetLocalCS(%s) failed. "
4632 "It appears an incompatible object already exists.",
4633 pszName );
4634 return OGRERR_FAILURE;
4635 }
4636
4637 return OGRERR_NONE;
4638 }
4639
4640 /************************************************************************/
4641 /* OSRSetLocalCS() */
4642 /************************************************************************/
4643
4644 /**
4645 * \brief Set the user visible LOCAL_CS name.
4646 *
4647 * This function is the same as OGRSpatialReference::SetLocalCS()
4648 */
OSRSetLocalCS(OGRSpatialReferenceH hSRS,const char * pszName)4649 OGRErr OSRSetLocalCS( OGRSpatialReferenceH hSRS, const char * pszName )
4650
4651 {
4652 VALIDATE_POINTER1( hSRS, "OSRSetLocalCS", OGRERR_FAILURE );
4653
4654 return ToPointer(hSRS)->SetLocalCS( pszName );
4655 }
4656
4657 /************************************************************************/
4658 /* SetGeocCS() */
4659 /************************************************************************/
4660
4661 /**
4662 * \brief Set the user visible GEOCCS name.
4663 *
4664 * This method is the same as the C function OSRSetGeocCS().
4665
4666 * This method will ensure a GEOCCS node is created as the root,
4667 * and set the provided name on it. If used on a GEOGCS coordinate system,
4668 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
4669 * the GEOGCS.
4670 *
4671 * @param pszName the user visible name to assign. Not used as a key.
4672 *
4673 * @return OGRERR_NONE on success.
4674 *
4675 * @since OGR 1.9.0
4676 */
4677
SetGeocCS(const char * pszName)4678 OGRErr OGRSpatialReference::SetGeocCS( const char * pszName )
4679
4680 {
4681 OGRErr eErr = OGRERR_NONE;
4682 d->refreshProjObj();
4683 d->demoteFromBoundCRS();
4684 if( d->m_pjType == PJ_TYPE_UNKNOWN )
4685 {
4686 d->setPjCRS(proj_create_geocentric_crs(
4687 d->getPROJContext(),
4688 pszName, "World Geodetic System 1984", "WGS 84", SRS_WGS84_SEMIMAJOR,
4689 SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE,
4690 CPLAtof(SRS_UA_DEGREE_CONV),
4691 "Metre", 1.0));
4692 }
4693 else if( d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS ) {
4694 d->setPjCRS(proj_alter_name(
4695 d->getPROJContext(),d->m_pj_crs, pszName));
4696 }
4697 else if( d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
4698 d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS )
4699 {
4700 auto datum = proj_crs_get_datum(
4701 d->getPROJContext(), d->m_pj_crs);
4702 #if PROJ_VERSION_MAJOR > 7 || (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
4703 if( datum == nullptr )
4704 {
4705 datum = proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
4706 }
4707 #endif
4708 if( datum == nullptr )
4709 {
4710 d->undoDemoteFromBoundCRS();
4711 return OGRERR_FAILURE;
4712 }
4713
4714 auto pj_crs = proj_create_geocentric_crs_from_datum(
4715 d->getPROJContext(),
4716 proj_get_name(d->m_pj_crs), datum, nullptr, 0.0);
4717 d->setPjCRS(pj_crs);
4718
4719 proj_destroy(datum);
4720 }
4721 else
4722 {
4723 CPLDebug( "OGR",
4724 "OGRSpatialReference::SetGeocCS(%s) failed. "
4725 "It appears an incompatible object already exists.",
4726 pszName );
4727 eErr = OGRERR_FAILURE;
4728 }
4729 d->undoDemoteFromBoundCRS();
4730
4731 return eErr;
4732 }
4733
4734 /************************************************************************/
4735 /* OSRSetGeocCS() */
4736 /************************************************************************/
4737
4738 /**
4739 * \brief Set the user visible PROJCS name.
4740 *
4741 * This function is the same as OGRSpatialReference::SetGeocCS()
4742 *
4743 * @since OGR 1.9.0
4744 */
OSRSetGeocCS(OGRSpatialReferenceH hSRS,const char * pszName)4745 OGRErr OSRSetGeocCS( OGRSpatialReferenceH hSRS, const char * pszName )
4746
4747 {
4748 VALIDATE_POINTER1( hSRS, "OSRSetGeocCS", OGRERR_FAILURE );
4749
4750 return ToPointer(hSRS)->SetGeocCS( pszName );
4751 }
4752
4753 /************************************************************************/
4754 /* SetVertCS() */
4755 /************************************************************************/
4756
4757 /**
4758 * \brief Set the user visible VERT_CS name.
4759 *
4760 * This method is the same as the C function OSRSetVertCS().
4761
4762 * This method will ensure a VERT_CS node is created if needed. If the
4763 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
4764 * turned into a COMPD_CS.
4765 *
4766 * @param pszVertCSName the user visible name of the vertical coordinate
4767 * system. Not used as a key.
4768 *
4769 * @param pszVertDatumName the user visible name of the vertical datum. It
4770 * is helpful if this matches the EPSG name.
4771 *
4772 * @param nVertDatumType the OGC vertical datum type. Ignored
4773 *
4774 * @return OGRERR_NONE on success.
4775 *
4776 * @since OGR 1.9.0
4777 */
4778
SetVertCS(const char * pszVertCSName,const char * pszVertDatumName,int nVertDatumType)4779 OGRErr OGRSpatialReference::SetVertCS( const char * pszVertCSName,
4780 const char * pszVertDatumName,
4781 int nVertDatumType )
4782
4783 {
4784 CPL_IGNORE_RET_VAL(nVertDatumType);
4785
4786 d->refreshProjObj();
4787
4788 auto vertCRS =
4789 proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
4790 pszVertDatumName, nullptr, 0.0);
4791
4792 /* -------------------------------------------------------------------- */
4793 /* Handle the case where we want to make a compound coordinate */
4794 /* system. */
4795 /* -------------------------------------------------------------------- */
4796 if( IsProjected() || IsGeographic() )
4797 {
4798 auto compoundCRS = proj_create_compound_crs(d->getPROJContext(),
4799 nullptr,
4800 d->m_pj_crs,
4801 vertCRS);
4802 proj_destroy(vertCRS);
4803 d->setPjCRS(compoundCRS);
4804 }
4805 else
4806 {
4807 d->setPjCRS(vertCRS);
4808 }
4809 return OGRERR_NONE;
4810 }
4811
4812 /************************************************************************/
4813 /* OSRSetVertCS() */
4814 /************************************************************************/
4815
4816 /**
4817 * \brief Setup the vertical coordinate system.
4818 *
4819 * This function is the same as OGRSpatialReference::SetVertCS()
4820 *
4821 * @since OGR 1.9.0
4822 */
OSRSetVertCS(OGRSpatialReferenceH hSRS,const char * pszVertCSName,const char * pszVertDatumName,int nVertDatumType)4823 OGRErr OSRSetVertCS( OGRSpatialReferenceH hSRS,
4824 const char * pszVertCSName,
4825 const char * pszVertDatumName,
4826 int nVertDatumType )
4827
4828 {
4829 VALIDATE_POINTER1( hSRS, "OSRSetVertCS", OGRERR_FAILURE );
4830
4831 return ToPointer(hSRS)->
4832 SetVertCS( pszVertCSName, pszVertDatumName, nVertDatumType );
4833 }
4834
4835 /************************************************************************/
4836 /* SetCompoundCS() */
4837 /************************************************************************/
4838
4839 /**
4840 * \brief Setup a compound coordinate system.
4841 *
4842 * This method is the same as the C function OSRSetCompoundCS().
4843
4844 * This method is replace the current SRS with a COMPD_CS coordinate system
4845 * consisting of the passed in horizontal and vertical coordinate systems.
4846 *
4847 * @param pszName the name of the compound coordinate system.
4848 *
4849 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
4850 *
4851 * @param poVertSRS the vertical SRS (VERT_CS).
4852 *
4853 * @return OGRERR_NONE on success.
4854 */
4855
4856 OGRErr
SetCompoundCS(const char * pszName,const OGRSpatialReference * poHorizSRS,const OGRSpatialReference * poVertSRS)4857 OGRSpatialReference::SetCompoundCS( const char *pszName,
4858 const OGRSpatialReference *poHorizSRS,
4859 const OGRSpatialReference *poVertSRS )
4860
4861 {
4862 /* -------------------------------------------------------------------- */
4863 /* Verify these are legal horizontal and vertical coordinate */
4864 /* systems. */
4865 /* -------------------------------------------------------------------- */
4866 if( !poVertSRS->IsVertical() )
4867 {
4868 CPLError( CE_Failure, CPLE_AppDefined,
4869 "SetCompoundCS() fails, vertical component is not VERT_CS." );
4870 return OGRERR_FAILURE;
4871 }
4872 if( !poHorizSRS->IsProjected()
4873 && !poHorizSRS->IsGeographic() )
4874 {
4875 CPLError( CE_Failure, CPLE_AppDefined,
4876 "SetCompoundCS() fails, horizontal component is not PROJCS or GEOGCS." );
4877 return OGRERR_FAILURE;
4878 }
4879
4880 /* -------------------------------------------------------------------- */
4881 /* Replace with compound srs. */
4882 /* -------------------------------------------------------------------- */
4883 Clear();
4884
4885 auto compoundCRS = proj_create_compound_crs(
4886 d->getPROJContext(), pszName,
4887 poHorizSRS->d->m_pj_crs,
4888 poVertSRS->d->m_pj_crs);
4889 d->setPjCRS(compoundCRS);
4890
4891 return OGRERR_NONE;
4892 }
4893
4894 /************************************************************************/
4895 /* OSRSetCompoundCS() */
4896 /************************************************************************/
4897
4898 /**
4899 * \brief Setup a compound coordinate system.
4900 *
4901 * This function is the same as OGRSpatialReference::SetCompoundCS()
4902 */
OSRSetCompoundCS(OGRSpatialReferenceH hSRS,const char * pszName,OGRSpatialReferenceH hHorizSRS,OGRSpatialReferenceH hVertSRS)4903 OGRErr OSRSetCompoundCS( OGRSpatialReferenceH hSRS,
4904 const char *pszName,
4905 OGRSpatialReferenceH hHorizSRS,
4906 OGRSpatialReferenceH hVertSRS )
4907
4908 {
4909 VALIDATE_POINTER1( hSRS, "OSRSetCompoundCS", OGRERR_FAILURE );
4910 VALIDATE_POINTER1( hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE );
4911 VALIDATE_POINTER1( hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE );
4912
4913 return ToPointer(hSRS)->
4914 SetCompoundCS( pszName,
4915 ToPointer(hHorizSRS),
4916 ToPointer(hVertSRS) );
4917 }
4918
4919 /************************************************************************/
4920 /* SetProjCS() */
4921 /************************************************************************/
4922
4923 /**
4924 * \brief Set the user visible PROJCS name.
4925 *
4926 * This method is the same as the C function OSRSetProjCS().
4927 *
4928 * This method will ensure a PROJCS node is created as the root,
4929 * and set the provided name on it. If used on a GEOGCS coordinate system,
4930 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
4931 *
4932 * @param pszName the user visible name to assign. Not used as a key.
4933 *
4934 * @return OGRERR_NONE on success.
4935 */
4936
SetProjCS(const char * pszName)4937 OGRErr OGRSpatialReference::SetProjCS( const char * pszName )
4938
4939 {
4940 d->refreshProjObj();
4941 if( d->m_pjType == PJ_TYPE_PROJECTED_CRS ) {
4942 d->setPjCRS(proj_alter_name(
4943 d->getPROJContext(), d->m_pj_crs, pszName));
4944 } else {
4945 auto dummyConv = proj_create_conversion(d->getPROJContext(),
4946 nullptr, nullptr, nullptr,
4947 nullptr, nullptr, nullptr,
4948 0, nullptr);
4949 auto cs = proj_create_cartesian_2D_cs(
4950 d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
4951
4952 auto projCRS = proj_create_projected_crs(
4953 d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
4954 proj_destroy(dummyConv);
4955 proj_destroy(cs);
4956
4957 d->setPjCRS(projCRS);
4958 }
4959 return OGRERR_NONE;
4960 }
4961
4962 /************************************************************************/
4963 /* OSRSetProjCS() */
4964 /************************************************************************/
4965
4966 /**
4967 * \brief Set the user visible PROJCS name.
4968 *
4969 * This function is the same as OGRSpatialReference::SetProjCS()
4970 */
OSRSetProjCS(OGRSpatialReferenceH hSRS,const char * pszName)4971 OGRErr OSRSetProjCS( OGRSpatialReferenceH hSRS, const char * pszName )
4972
4973 {
4974 VALIDATE_POINTER1( hSRS, "OSRSetProjCS", OGRERR_FAILURE );
4975
4976 return ToPointer(hSRS)->SetProjCS( pszName );
4977 }
4978
4979 /************************************************************************/
4980 /* SetProjection() */
4981 /************************************************************************/
4982
4983 /**
4984 * \brief Set a projection name.
4985 *
4986 * This method is the same as the C function OSRSetProjection().
4987 *
4988 * @param pszProjection the projection name, which should be selected from
4989 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
4990 *
4991 * @return OGRERR_NONE on success.
4992 */
4993
SetProjection(const char * pszProjection)4994 OGRErr OGRSpatialReference::SetProjection( const char * pszProjection )
4995
4996 {
4997 OGR_SRSNode *poGeogCS = nullptr;
4998
4999 if( GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS") )
5000 {
5001 poGeogCS = d->m_poRoot;
5002 d->m_poRoot = nullptr;
5003 }
5004
5005 if( !GetAttrNode( "PROJCS" ) )
5006 {
5007 SetNode( "PROJCS", "unnamed" );
5008 }
5009
5010 const OGRErr eErr = SetNode( "PROJCS|PROJECTION", pszProjection );
5011 if( eErr != OGRERR_NONE )
5012 return eErr;
5013
5014 if( poGeogCS != nullptr )
5015 d->m_poRoot->InsertChild( poGeogCS, 1 );
5016
5017 return OGRERR_NONE;
5018 }
5019
5020 /************************************************************************/
5021 /* OSRSetProjection() */
5022 /************************************************************************/
5023
5024 /**
5025 * \brief Set a projection name.
5026 *
5027 * This function is the same as OGRSpatialReference::SetProjection()
5028 */
OSRSetProjection(OGRSpatialReferenceH hSRS,const char * pszProjection)5029 OGRErr OSRSetProjection( OGRSpatialReferenceH hSRS,
5030 const char * pszProjection )
5031
5032 {
5033 VALIDATE_POINTER1( hSRS, "OSRSetProjection", OGRERR_FAILURE );
5034
5035 return ToPointer(hSRS)->
5036 SetProjection( pszProjection );
5037 }
5038
5039 /************************************************************************/
5040 /* SetProjParm() */
5041 /************************************************************************/
5042
5043 /**
5044 * \brief Set a projection parameter value.
5045 *
5046 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5047 *
5048 * This method is the same as the C function OSRSetProjParm().
5049 *
5050 * Please check http://www.remotesensing.org/geotiff/proj_list pages for
5051 * legal parameter names for specific projections.
5052 *
5053 *
5054 * @param pszParamName the parameter name, which should be selected from
5055 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5056 *
5057 * @param dfValue value to assign.
5058 *
5059 * @return OGRERR_NONE on success.
5060 */
5061
SetProjParm(const char * pszParamName,double dfValue)5062 OGRErr OGRSpatialReference::SetProjParm( const char * pszParamName,
5063 double dfValue )
5064
5065 {
5066 OGR_SRSNode *poPROJCS = GetAttrNode( "PROJCS" );
5067
5068 if( poPROJCS == nullptr )
5069 return OGRERR_FAILURE;
5070
5071 char szValue[64] = { '\0' };
5072 OGRsnPrintDouble( szValue, sizeof(szValue), dfValue );
5073
5074 /* -------------------------------------------------------------------- */
5075 /* Try to find existing parameter with this name. */
5076 /* -------------------------------------------------------------------- */
5077 for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ )
5078 {
5079 OGR_SRSNode* poParam = poPROJCS->GetChild( iChild );
5080
5081 if( EQUAL(poParam->GetValue(), "PARAMETER")
5082 && poParam->GetChildCount() == 2
5083 && EQUAL(poParam->GetChild(0)->GetValue(), pszParamName) )
5084 {
5085 poParam->GetChild(1)->SetValue( szValue );
5086 return OGRERR_NONE;
5087 }
5088 }
5089
5090 /* -------------------------------------------------------------------- */
5091 /* Otherwise create a new parameter and append. */
5092 /* -------------------------------------------------------------------- */
5093 OGR_SRSNode* poParam = new OGR_SRSNode( "PARAMETER" );
5094 poParam->AddChild( new OGR_SRSNode( pszParamName ) );
5095 poParam->AddChild( new OGR_SRSNode( szValue ) );
5096
5097 poPROJCS->AddChild( poParam );
5098
5099 return OGRERR_NONE;
5100 }
5101
5102 /************************************************************************/
5103 /* OSRSetProjParm() */
5104 /************************************************************************/
5105
5106 /**
5107 * \brief Set a projection parameter value.
5108 *
5109 * This function is the same as OGRSpatialReference::SetProjParm()
5110 */
OSRSetProjParm(OGRSpatialReferenceH hSRS,const char * pszParamName,double dfValue)5111 OGRErr OSRSetProjParm( OGRSpatialReferenceH hSRS,
5112 const char * pszParamName, double dfValue )
5113
5114 {
5115 VALIDATE_POINTER1( hSRS, "OSRSetProjParm", OGRERR_FAILURE );
5116
5117 return ToPointer(hSRS)->
5118 SetProjParm( pszParamName, dfValue );
5119 }
5120
5121 /************************************************************************/
5122 /* FindProjParm() */
5123 /************************************************************************/
5124
5125 /**
5126 * \brief Return the child index of the named projection parameter on
5127 * its parent PROJCS node.
5128 *
5129 * @param pszParameter projection parameter to look for
5130 * @param poPROJCS projection CS node to look in. If NULL is passed,
5131 * the PROJCS node of the SpatialReference object will be searched.
5132 *
5133 * @return the child index of the named projection parameter. -1 on failure
5134 */
FindProjParm(const char * pszParameter,const OGR_SRSNode * poPROJCS) const5135 int OGRSpatialReference::FindProjParm( const char *pszParameter,
5136 const OGR_SRSNode *poPROJCS ) const
5137
5138 {
5139 if( poPROJCS == nullptr )
5140 poPROJCS = GetAttrNode( "PROJCS" );
5141
5142 if( poPROJCS == nullptr )
5143 return -1;
5144
5145 /* -------------------------------------------------------------------- */
5146 /* Search for requested parameter. */
5147 /* -------------------------------------------------------------------- */
5148 for( int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++ )
5149 {
5150 const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5151
5152 if( EQUAL(poParameter->GetValue(), "PARAMETER")
5153 && poParameter->GetChildCount() >= 2
5154 && EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5155 pszParameter) )
5156 {
5157 return iChild;
5158 }
5159 }
5160
5161 /* -------------------------------------------------------------------- */
5162 /* Try similar names, for selected parameters. */
5163 /* -------------------------------------------------------------------- */
5164 if( EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN) )
5165 {
5166 return FindProjParm( SRS_PP_LATITUDE_OF_CENTER, poPROJCS );
5167 }
5168
5169 if( EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN) )
5170 {
5171 int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS );
5172 if( iChild == -1 )
5173 iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS );
5174 return iChild;
5175 }
5176
5177 return -1;
5178 }
5179
5180 /************************************************************************/
5181 /* GetProjParm() */
5182 /************************************************************************/
5183
5184 /**
5185 * \brief Fetch a projection parameter value.
5186 *
5187 * NOTE: This code should be modified to translate non degree angles into
5188 * degrees based on the GEOGCS unit. This has not yet been done.
5189 *
5190 * This method is the same as the C function OSRGetProjParm().
5191 *
5192 * @param pszName the name of the parameter to fetch, from the set of
5193 * SRS_PP codes in ogr_srs_api.h.
5194 *
5195 * @param dfDefaultValue the value to return if this parameter doesn't exist.
5196 *
5197 * @param pnErr place to put error code on failure. Ignored if NULL.
5198 *
5199 * @return value of parameter.
5200 */
5201
GetProjParm(const char * pszName,double dfDefaultValue,OGRErr * pnErr) const5202 double OGRSpatialReference::GetProjParm( const char * pszName,
5203 double dfDefaultValue,
5204 OGRErr *pnErr ) const
5205
5206 {
5207 d->refreshProjObj();
5208 GetRoot(); // force update of d->m_bNodesWKT2
5209
5210 if( pnErr != nullptr )
5211 *pnErr = OGRERR_NONE;
5212
5213 /* -------------------------------------------------------------------- */
5214 /* Find the desired parameter. */
5215 /* -------------------------------------------------------------------- */
5216 const OGR_SRSNode *poPROJCS = GetAttrNode( d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS" );
5217 if( poPROJCS == nullptr )
5218 {
5219 if( pnErr != nullptr )
5220 *pnErr = OGRERR_FAILURE;
5221 return dfDefaultValue;
5222 }
5223
5224 const int iChild = FindProjParm( pszName, poPROJCS );
5225 if( iChild == -1 )
5226 {
5227 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
5228 if( IsProjected() && GetAxesCount() == 3 )
5229 {
5230 OGRSpatialReference* poSRSTmp = Clone();
5231 poSRSTmp->DemoteTo2D(nullptr);
5232 const double dfRet = poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
5233 delete poSRSTmp;
5234 return dfRet;
5235 }
5236 #endif
5237
5238 if( pnErr != nullptr )
5239 *pnErr = OGRERR_FAILURE;
5240 return dfDefaultValue;
5241 }
5242
5243 const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5244 return CPLAtof(poParameter->GetChild(1)->GetValue());
5245 }
5246
5247 /************************************************************************/
5248 /* OSRGetProjParm() */
5249 /************************************************************************/
5250
5251 /**
5252 * \brief Fetch a projection parameter value.
5253 *
5254 * This function is the same as OGRSpatialReference::GetProjParm()
5255 */
OSRGetProjParm(OGRSpatialReferenceH hSRS,const char * pszName,double dfDefaultValue,OGRErr * pnErr)5256 double OSRGetProjParm( OGRSpatialReferenceH hSRS, const char *pszName,
5257 double dfDefaultValue, OGRErr *pnErr )
5258
5259 {
5260 VALIDATE_POINTER1( hSRS, "OSRGetProjParm", 0 );
5261
5262 return ToPointer(hSRS)->
5263 GetProjParm(pszName, dfDefaultValue, pnErr);
5264 }
5265
5266 /************************************************************************/
5267 /* GetNormProjParm() */
5268 /************************************************************************/
5269
5270 /**
5271 * \brief Fetch a normalized projection parameter value.
5272 *
5273 * This method is the same as GetProjParm() except that the value of
5274 * the parameter is "normalized" into degrees or meters depending on
5275 * whether it is linear or angular.
5276 *
5277 * This method is the same as the C function OSRGetNormProjParm().
5278 *
5279 * @param pszName the name of the parameter to fetch, from the set of
5280 * SRS_PP codes in ogr_srs_api.h.
5281 *
5282 * @param dfDefaultValue the value to return if this parameter doesn't exist.
5283 *
5284 * @param pnErr place to put error code on failure. Ignored if NULL.
5285 *
5286 * @return value of parameter.
5287 */
5288
GetNormProjParm(const char * pszName,double dfDefaultValue,OGRErr * pnErr) const5289 double OGRSpatialReference::GetNormProjParm( const char * pszName,
5290 double dfDefaultValue,
5291 OGRErr *pnErr ) const
5292
5293 {
5294 GetNormInfo();
5295
5296 OGRErr nError = OGRERR_NONE;
5297 double dfRawResult = GetProjParm( pszName, dfDefaultValue, &nError );
5298 if( pnErr != nullptr )
5299 *pnErr = nError;
5300
5301 // If we got the default just return it unadjusted.
5302 if( nError != OGRERR_NONE )
5303 return dfRawResult;
5304
5305 if( d->dfToDegrees != 1.0 && IsAngularParameter(pszName) )
5306 dfRawResult *= d->dfToDegrees;
5307
5308 if( d->dfToMeter != 1.0 && IsLinearParameter( pszName ) )
5309 return dfRawResult * d->dfToMeter;
5310
5311 return dfRawResult;
5312 }
5313
5314 /************************************************************************/
5315 /* OSRGetNormProjParm() */
5316 /************************************************************************/
5317
5318 /**
5319 * \brief This function is the same as OGRSpatialReference::
5320 *
5321 * This function is the same as OGRSpatialReference::GetNormProjParm()
5322 */
OSRGetNormProjParm(OGRSpatialReferenceH hSRS,const char * pszName,double dfDefaultValue,OGRErr * pnErr)5323 double OSRGetNormProjParm( OGRSpatialReferenceH hSRS, const char *pszName,
5324 double dfDefaultValue, OGRErr *pnErr )
5325
5326 {
5327 VALIDATE_POINTER1( hSRS, "OSRGetNormProjParm", 0 );
5328
5329 return ToPointer(hSRS)->
5330 GetNormProjParm(pszName, dfDefaultValue, pnErr);
5331 }
5332
5333 /************************************************************************/
5334 /* SetNormProjParm() */
5335 /************************************************************************/
5336
5337 /**
5338 * \brief Set a projection parameter with a normalized value.
5339 *
5340 * This method is the same as SetProjParm() except that the value of
5341 * the parameter passed in is assumed to be in "normalized" form (decimal
5342 * degrees for angular values, meters for linear values. The values are
5343 * converted in a form suitable for the GEOGCS and linear units in effect.
5344 *
5345 * This method is the same as the C function OSRSetNormProjParm().
5346 *
5347 * @param pszName the parameter name, which should be selected from
5348 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5349 *
5350 * @param dfValue value to assign.
5351 *
5352 * @return OGRERR_NONE on success.
5353 */
5354
SetNormProjParm(const char * pszName,double dfValue)5355 OGRErr OGRSpatialReference::SetNormProjParm( const char * pszName,
5356 double dfValue )
5357
5358 {
5359 GetNormInfo();
5360
5361 if( d->dfToDegrees != 0.0 &&
5362 (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0)
5363 && IsAngularParameter(pszName) )
5364 {
5365 dfValue /= d->dfToDegrees;
5366 }
5367 else if( d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
5368 IsLinearParameter( pszName ) )
5369 dfValue /= d->dfToMeter;
5370
5371 return SetProjParm( pszName, dfValue );
5372 }
5373
5374 /************************************************************************/
5375 /* OSRSetNormProjParm() */
5376 /************************************************************************/
5377
5378 /**
5379 * \brief Set a projection parameter with a normalized value.
5380 *
5381 * This function is the same as OGRSpatialReference::SetNormProjParm()
5382 */
OSRSetNormProjParm(OGRSpatialReferenceH hSRS,const char * pszParamName,double dfValue)5383 OGRErr OSRSetNormProjParm( OGRSpatialReferenceH hSRS,
5384 const char * pszParamName, double dfValue )
5385
5386 {
5387 VALIDATE_POINTER1( hSRS, "OSRSetNormProjParm", OGRERR_FAILURE );
5388
5389 return ToPointer(hSRS)->
5390 SetNormProjParm( pszParamName, dfValue );
5391 }
5392
5393 /************************************************************************/
5394 /* SetTM() */
5395 /************************************************************************/
5396
SetTM(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5397 OGRErr OGRSpatialReference::SetTM( double dfCenterLat, double dfCenterLong,
5398 double dfScale,
5399 double dfFalseEasting,
5400 double dfFalseNorthing )
5401
5402 {
5403 return d->replaceConversionAndUnref(
5404 proj_create_conversion_transverse_mercator(d->getPROJContext(),
5405 dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing,
5406 nullptr, 0.0, nullptr, 0.0));
5407 }
5408
5409 /************************************************************************/
5410 /* OSRSetTM() */
5411 /************************************************************************/
5412
OSRSetTM(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5413 OGRErr OSRSetTM( OGRSpatialReferenceH hSRS,
5414 double dfCenterLat, double dfCenterLong,
5415 double dfScale,
5416 double dfFalseEasting,
5417 double dfFalseNorthing )
5418
5419 {
5420 VALIDATE_POINTER1( hSRS, "OSRSetTM", OGRERR_FAILURE );
5421
5422 return ToPointer(hSRS)->SetTM(
5423 dfCenterLat, dfCenterLong,
5424 dfScale,
5425 dfFalseEasting, dfFalseNorthing );
5426 }
5427
5428 /************************************************************************/
5429 /* SetTMVariant() */
5430 /************************************************************************/
5431
SetTMVariant(const char * pszVariantName,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5432 OGRErr OGRSpatialReference::SetTMVariant(
5433 const char *pszVariantName,
5434 double dfCenterLat, double dfCenterLong,
5435 double dfScale,
5436 double dfFalseEasting,
5437 double dfFalseNorthing )
5438
5439 {
5440 SetProjection( pszVariantName );
5441 SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat );
5442 SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong );
5443 SetNormProjParm( SRS_PP_SCALE_FACTOR, dfScale );
5444 SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
5445 SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
5446
5447 return OGRERR_NONE;
5448 }
5449
5450 /************************************************************************/
5451 /* OSRSetTMVariant() */
5452 /************************************************************************/
5453
OSRSetTMVariant(OGRSpatialReferenceH hSRS,const char * pszVariantName,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5454 OGRErr OSRSetTMVariant( OGRSpatialReferenceH hSRS,
5455 const char *pszVariantName,
5456 double dfCenterLat, double dfCenterLong,
5457 double dfScale,
5458 double dfFalseEasting,
5459 double dfFalseNorthing )
5460
5461 {
5462 VALIDATE_POINTER1( hSRS, "OSRSetTMVariant", OGRERR_FAILURE );
5463
5464 return ToPointer(hSRS)->SetTMVariant(
5465 pszVariantName,
5466 dfCenterLat, dfCenterLong,
5467 dfScale,
5468 dfFalseEasting, dfFalseNorthing );
5469 }
5470
5471 /************************************************************************/
5472 /* SetTMSO() */
5473 /************************************************************************/
5474
SetTMSO(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5475 OGRErr OGRSpatialReference::SetTMSO( double dfCenterLat, double dfCenterLong,
5476 double dfScale,
5477 double dfFalseEasting,
5478 double dfFalseNorthing )
5479
5480 {
5481 auto conv =
5482 proj_create_conversion_transverse_mercator_south_oriented(
5483 d->getPROJContext(),
5484 dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing,
5485 nullptr, 0.0, nullptr, 0.0);
5486
5487 const char* pszName = nullptr;
5488 double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
5489 CPLString osName = pszName ? pszName : "";
5490
5491 d->refreshProjObj();
5492
5493 d->demoteFromBoundCRS();
5494
5495 auto cs = proj_create_cartesian_2D_cs(
5496 d->getPROJContext(),
5497 PJ_CART2D_WESTING_SOUTHING,
5498 !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
5499 auto projCRS = proj_create_projected_crs(
5500 d->getPROJContext(),
5501 d->getProjCRSName(), d->getGeodBaseCRS(), conv, cs);
5502 proj_destroy(conv);
5503 proj_destroy(cs);
5504
5505 d->setPjCRS(projCRS);
5506
5507 d->undoDemoteFromBoundCRS();
5508
5509 return OGRERR_NONE;
5510 }
5511
5512 /************************************************************************/
5513 /* OSRSetTMSO() */
5514 /************************************************************************/
5515
OSRSetTMSO(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)5516 OGRErr OSRSetTMSO( OGRSpatialReferenceH hSRS,
5517 double dfCenterLat, double dfCenterLong,
5518 double dfScale,
5519 double dfFalseEasting,
5520 double dfFalseNorthing )
5521
5522 {
5523 VALIDATE_POINTER1( hSRS, "OSRSetTMSO", OGRERR_FAILURE );
5524
5525 return ToPointer(hSRS)->SetTMSO(
5526 dfCenterLat, dfCenterLong,
5527 dfScale,
5528 dfFalseEasting, dfFalseNorthing );
5529 }
5530
5531 /************************************************************************/
5532 /* SetTPED() */
5533 /************************************************************************/
5534
SetTPED(double dfLat1,double dfLong1,double dfLat2,double dfLong2,double dfFalseEasting,double dfFalseNorthing)5535 OGRErr OGRSpatialReference::SetTPED( double dfLat1, double dfLong1,
5536 double dfLat2, double dfLong2,
5537 double dfFalseEasting,
5538 double dfFalseNorthing )
5539
5540 {
5541 return d->replaceConversionAndUnref(
5542 proj_create_conversion_two_point_equidistant(d->getPROJContext(),
5543 dfLat1, dfLong1, dfLat2, dfLong2,
5544 dfFalseEasting, dfFalseNorthing,
5545 nullptr, 0.0, nullptr, 0.0));
5546 }
5547
5548 /************************************************************************/
5549 /* OSRSetTPED() */
5550 /************************************************************************/
5551
OSRSetTPED(OGRSpatialReferenceH hSRS,double dfLat1,double dfLong1,double dfLat2,double dfLong2,double dfFalseEasting,double dfFalseNorthing)5552 OGRErr OSRSetTPED( OGRSpatialReferenceH hSRS,
5553 double dfLat1, double dfLong1,
5554 double dfLat2, double dfLong2,
5555 double dfFalseEasting, double dfFalseNorthing )
5556
5557 {
5558 VALIDATE_POINTER1( hSRS, "OSRSetTPED", OGRERR_FAILURE );
5559
5560 return ToPointer(hSRS)->SetTPED(
5561 dfLat1, dfLong1, dfLat2, dfLong2,
5562 dfFalseEasting, dfFalseNorthing );
5563 }
5564
5565 /************************************************************************/
5566 /* SetTMG() */
5567 /************************************************************************/
5568
5569 OGRErr
SetTMG(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5570 OGRSpatialReference::SetTMG( double dfCenterLat, double dfCenterLong,
5571 double dfFalseEasting, double dfFalseNorthing )
5572
5573 {
5574 return d->replaceConversionAndUnref(
5575 proj_create_conversion_tunisia_mapping_grid(
5576 d->getPROJContext(),
5577 dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing,
5578 nullptr, 0.0, nullptr, 0.0));
5579 }
5580
5581 /************************************************************************/
5582 /* OSRSetTMG() */
5583 /************************************************************************/
5584
OSRSetTMG(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5585 OGRErr OSRSetTMG( OGRSpatialReferenceH hSRS,
5586 double dfCenterLat, double dfCenterLong,
5587 double dfFalseEasting,
5588 double dfFalseNorthing )
5589
5590 {
5591 VALIDATE_POINTER1( hSRS, "OSRSetTMG", OGRERR_FAILURE );
5592
5593 return ToPointer(hSRS)->SetTMG(
5594 dfCenterLat, dfCenterLong,
5595 dfFalseEasting, dfFalseNorthing );
5596 }
5597
5598 /************************************************************************/
5599 /* SetACEA() */
5600 /************************************************************************/
5601
SetACEA(double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5602 OGRErr OGRSpatialReference::SetACEA( double dfStdP1, double dfStdP2,
5603 double dfCenterLat, double dfCenterLong,
5604 double dfFalseEasting,
5605 double dfFalseNorthing )
5606
5607 {
5608 // Note different order of parameters. The one in PROJ is conformant with
5609 // EPSG
5610 return d->replaceConversionAndUnref(
5611 proj_create_conversion_albers_equal_area(
5612 d->getPROJContext(),
5613 dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
5614 dfFalseEasting, dfFalseNorthing,
5615 nullptr, 0.0, nullptr, 0.0));
5616 }
5617
5618 /************************************************************************/
5619 /* OSRSetACEA() */
5620 /************************************************************************/
5621
OSRSetACEA(OGRSpatialReferenceH hSRS,double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5622 OGRErr OSRSetACEA( OGRSpatialReferenceH hSRS,
5623 double dfStdP1, double dfStdP2,
5624 double dfCenterLat, double dfCenterLong,
5625 double dfFalseEasting,
5626 double dfFalseNorthing )
5627
5628 {
5629 VALIDATE_POINTER1( hSRS, "OSRSetACEA", OGRERR_FAILURE );
5630
5631 return ToPointer(hSRS)->SetACEA(
5632 dfStdP1, dfStdP2,
5633 dfCenterLat, dfCenterLong,
5634 dfFalseEasting, dfFalseNorthing );
5635 }
5636
5637 /************************************************************************/
5638 /* SetAE() */
5639 /************************************************************************/
5640
SetAE(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5641 OGRErr OGRSpatialReference::SetAE( double dfCenterLat, double dfCenterLong,
5642 double dfFalseEasting,
5643 double dfFalseNorthing )
5644
5645 {
5646 return d->replaceConversionAndUnref(
5647 proj_create_conversion_azimuthal_equidistant(
5648 d->getPROJContext(),
5649 dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing,
5650 nullptr, 0.0, nullptr, 0.0));
5651 }
5652
5653 /************************************************************************/
5654 /* OSRSetAE() */
5655 /************************************************************************/
5656
OSRSetAE(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5657 OGRErr OSRSetAE( OGRSpatialReferenceH hSRS,
5658 double dfCenterLat, double dfCenterLong,
5659 double dfFalseEasting,
5660 double dfFalseNorthing )
5661
5662 {
5663 VALIDATE_POINTER1( hSRS, "OSRSetACEA", OGRERR_FAILURE );
5664
5665 return ToPointer(hSRS)->SetAE(
5666 dfCenterLat, dfCenterLong,
5667 dfFalseEasting, dfFalseNorthing );
5668 }
5669
5670 /************************************************************************/
5671 /* SetBonne() */
5672 /************************************************************************/
5673
SetBonne(double dfStdP1,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5674 OGRErr OGRSpatialReference::SetBonne(
5675 double dfStdP1, double dfCentralMeridian,
5676 double dfFalseEasting, double dfFalseNorthing )
5677
5678 {
5679 return d->replaceConversionAndUnref(
5680 proj_create_conversion_bonne(
5681 d->getPROJContext(),
5682 dfStdP1, dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5683 nullptr, 0.0, nullptr, 0.0));
5684 }
5685
5686 /************************************************************************/
5687 /* OSRSetBonne() */
5688 /************************************************************************/
5689
OSRSetBonne(OGRSpatialReferenceH hSRS,double dfStdP1,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5690 OGRErr OSRSetBonne( OGRSpatialReferenceH hSRS,
5691 double dfStdP1, double dfCentralMeridian,
5692 double dfFalseEasting, double dfFalseNorthing )
5693
5694 {
5695 VALIDATE_POINTER1( hSRS, "OSRSetBonne", OGRERR_FAILURE );
5696
5697 return ToPointer(hSRS)->SetBonne(
5698 dfStdP1, dfCentralMeridian,
5699 dfFalseEasting, dfFalseNorthing );
5700 }
5701
5702 /************************************************************************/
5703 /* SetCEA() */
5704 /************************************************************************/
5705
SetCEA(double dfStdP1,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5706 OGRErr OGRSpatialReference::SetCEA( double dfStdP1, double dfCentralMeridian,
5707 double dfFalseEasting,
5708 double dfFalseNorthing )
5709
5710 {
5711 return d->replaceConversionAndUnref(
5712 proj_create_conversion_lambert_cylindrical_equal_area(
5713 d->getPROJContext(),
5714 dfStdP1, dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5715 nullptr, 0.0, nullptr, 0.0));
5716 }
5717
5718 /************************************************************************/
5719 /* OSRSetCEA() */
5720 /************************************************************************/
5721
OSRSetCEA(OGRSpatialReferenceH hSRS,double dfStdP1,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5722 OGRErr OSRSetCEA( OGRSpatialReferenceH hSRS,
5723 double dfStdP1, double dfCentralMeridian,
5724 double dfFalseEasting, double dfFalseNorthing )
5725
5726 {
5727 VALIDATE_POINTER1( hSRS, "OSRSetCEA", OGRERR_FAILURE );
5728
5729 return ToPointer(hSRS)->SetCEA(
5730 dfStdP1, dfCentralMeridian,
5731 dfFalseEasting, dfFalseNorthing );
5732 }
5733
5734 /************************************************************************/
5735 /* SetCS() */
5736 /************************************************************************/
5737
SetCS(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5738 OGRErr OGRSpatialReference::SetCS( double dfCenterLat, double dfCenterLong,
5739 double dfFalseEasting,
5740 double dfFalseNorthing )
5741
5742 {
5743 return d->replaceConversionAndUnref(
5744 proj_create_conversion_cassini_soldner(
5745 d->getPROJContext(),
5746 dfCenterLat, dfCenterLong, dfFalseEasting, dfFalseNorthing,
5747 nullptr, 0.0, nullptr, 0.0));
5748 }
5749
5750 /************************************************************************/
5751 /* OSRSetCS() */
5752 /************************************************************************/
5753
OSRSetCS(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5754 OGRErr OSRSetCS( OGRSpatialReferenceH hSRS,
5755 double dfCenterLat, double dfCenterLong,
5756 double dfFalseEasting,
5757 double dfFalseNorthing )
5758
5759 {
5760 VALIDATE_POINTER1( hSRS, "OSRSetCS", OGRERR_FAILURE );
5761
5762 return ToPointer(hSRS)->SetCS(
5763 dfCenterLat, dfCenterLong,
5764 dfFalseEasting, dfFalseNorthing );
5765 }
5766
5767 /************************************************************************/
5768 /* SetEC() */
5769 /************************************************************************/
5770
SetEC(double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5771 OGRErr OGRSpatialReference::SetEC( double dfStdP1, double dfStdP2,
5772 double dfCenterLat, double dfCenterLong,
5773 double dfFalseEasting,
5774 double dfFalseNorthing )
5775
5776 {
5777 // Note: different order of arguments
5778 return d->replaceConversionAndUnref(
5779 proj_create_conversion_equidistant_conic(
5780 d->getPROJContext(),
5781 dfCenterLat, dfCenterLong,
5782 dfStdP1, dfStdP2,
5783 dfFalseEasting, dfFalseNorthing,
5784 nullptr, 0.0, nullptr, 0.0));
5785 }
5786
5787 /************************************************************************/
5788 /* OSRSetEC() */
5789 /************************************************************************/
5790
OSRSetEC(OGRSpatialReferenceH hSRS,double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5791 OGRErr OSRSetEC( OGRSpatialReferenceH hSRS,
5792 double dfStdP1, double dfStdP2,
5793 double dfCenterLat, double dfCenterLong,
5794 double dfFalseEasting,
5795 double dfFalseNorthing )
5796
5797 {
5798 VALIDATE_POINTER1( hSRS, "OSRSetEC", OGRERR_FAILURE );
5799
5800 return ToPointer(hSRS)->SetEC(
5801 dfStdP1, dfStdP2,
5802 dfCenterLat, dfCenterLong,
5803 dfFalseEasting, dfFalseNorthing );
5804 }
5805
5806 /************************************************************************/
5807 /* SetEckert() */
5808 /************************************************************************/
5809
SetEckert(int nVariation,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5810 OGRErr OGRSpatialReference::SetEckert( int nVariation, // 1-6.
5811 double dfCentralMeridian,
5812 double dfFalseEasting,
5813 double dfFalseNorthing )
5814
5815 {
5816 PJ* conv;
5817 if( nVariation == 1 )
5818 {
5819 conv = proj_create_conversion_eckert_i(
5820 d->getPROJContext(),
5821 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5822 nullptr, 0.0, nullptr, 0.0);
5823 }
5824 else if( nVariation == 2 )
5825 {
5826 conv = proj_create_conversion_eckert_ii(
5827 d->getPROJContext(),
5828 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5829 nullptr, 0.0, nullptr, 0.0);
5830 }
5831 else if( nVariation == 3 )
5832 {
5833 conv = proj_create_conversion_eckert_iii(
5834 d->getPROJContext(),
5835 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5836 nullptr, 0.0, nullptr, 0.0);
5837 }
5838 else if( nVariation == 4 )
5839 {
5840 conv = proj_create_conversion_eckert_iv(
5841 d->getPROJContext(),
5842 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5843 nullptr, 0.0, nullptr, 0.0);
5844 }
5845 else if( nVariation == 5 )
5846 {
5847 conv = proj_create_conversion_eckert_v(
5848 d->getPROJContext(),
5849 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5850 nullptr, 0.0, nullptr, 0.0);
5851 }
5852 else if( nVariation == 6 )
5853 {
5854 conv = proj_create_conversion_eckert_vi(
5855 d->getPROJContext(),
5856 dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
5857 nullptr, 0.0, nullptr, 0.0);
5858 }
5859 else
5860 {
5861 CPLError( CE_Failure, CPLE_AppDefined,
5862 "Unsupported Eckert variation (%d).",
5863 nVariation );
5864 return OGRERR_UNSUPPORTED_SRS;
5865 }
5866
5867 return d->replaceConversionAndUnref(conv);
5868 }
5869
5870 /************************************************************************/
5871 /* OSRSetEckert() */
5872 /************************************************************************/
5873
OSRSetEckert(OGRSpatialReferenceH hSRS,int nVariation,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5874 OGRErr OSRSetEckert( OGRSpatialReferenceH hSRS,
5875 int nVariation,
5876 double dfCentralMeridian,
5877 double dfFalseEasting,
5878 double dfFalseNorthing )
5879
5880 {
5881 VALIDATE_POINTER1( hSRS, "OSRSetEckert", OGRERR_FAILURE );
5882
5883 return ToPointer(hSRS)->SetEckert(
5884 nVariation, dfCentralMeridian,
5885 dfFalseEasting, dfFalseNorthing );
5886 }
5887
5888 /************************************************************************/
5889 /* SetEckertIV() */
5890 /* */
5891 /* Deprecated */
5892 /************************************************************************/
5893
SetEckertIV(double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5894 OGRErr OGRSpatialReference::SetEckertIV( double dfCentralMeridian,
5895 double dfFalseEasting,
5896 double dfFalseNorthing )
5897
5898 {
5899 return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
5900 }
5901
5902 /************************************************************************/
5903 /* OSRSetEckertIV() */
5904 /************************************************************************/
5905
OSRSetEckertIV(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5906 OGRErr OSRSetEckertIV( OGRSpatialReferenceH hSRS,
5907 double dfCentralMeridian,
5908 double dfFalseEasting,
5909 double dfFalseNorthing )
5910
5911 {
5912 VALIDATE_POINTER1( hSRS, "OSRSetEckertIV", OGRERR_FAILURE );
5913
5914 return ToPointer(hSRS)->SetEckertIV(
5915 dfCentralMeridian,
5916 dfFalseEasting, dfFalseNorthing );
5917 }
5918
5919 /************************************************************************/
5920 /* SetEckertVI() */
5921 /* */
5922 /* Deprecated */
5923 /************************************************************************/
5924
SetEckertVI(double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5925 OGRErr OGRSpatialReference::SetEckertVI( double dfCentralMeridian,
5926 double dfFalseEasting,
5927 double dfFalseNorthing )
5928
5929 {
5930 return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
5931 }
5932
5933 /************************************************************************/
5934 /* OSRSetEckertVI() */
5935 /************************************************************************/
5936
OSRSetEckertVI(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)5937 OGRErr OSRSetEckertVI( OGRSpatialReferenceH hSRS,
5938 double dfCentralMeridian,
5939 double dfFalseEasting,
5940 double dfFalseNorthing )
5941
5942 {
5943 VALIDATE_POINTER1( hSRS, "OSRSetEckertVI", OGRERR_FAILURE );
5944
5945 return ToPointer(hSRS)->SetEckertVI(
5946 dfCentralMeridian,
5947 dfFalseEasting, dfFalseNorthing );
5948 }
5949
5950 /************************************************************************/
5951 /* SetEquirectangular() */
5952 /************************************************************************/
5953
SetEquirectangular(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5954 OGRErr OGRSpatialReference::SetEquirectangular(
5955 double dfCenterLat, double dfCenterLong,
5956 double dfFalseEasting,
5957 double dfFalseNorthing )
5958
5959 {
5960 if( dfCenterLat == 0.0 )
5961 {
5962 return d->replaceConversionAndUnref(
5963 proj_create_conversion_equidistant_cylindrical(
5964 d->getPROJContext(),
5965 0.0, dfCenterLong,
5966 dfFalseEasting, dfFalseNorthing,
5967 nullptr, 0.0, nullptr, 0.0));
5968 }
5969
5970 // Non-standard extension with non-zero latitude of origin
5971 SetProjection( SRS_PT_EQUIRECTANGULAR );
5972 SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat );
5973 SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong );
5974 SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
5975 SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
5976
5977 return OGRERR_NONE;
5978 }
5979
5980 /************************************************************************/
5981 /* OSRSetEquirectangular() */
5982 /************************************************************************/
5983
OSRSetEquirectangular(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)5984 OGRErr OSRSetEquirectangular( OGRSpatialReferenceH hSRS,
5985 double dfCenterLat, double dfCenterLong,
5986 double dfFalseEasting,
5987 double dfFalseNorthing )
5988
5989 {
5990 VALIDATE_POINTER1( hSRS, "OSRSetEquirectangular", OGRERR_FAILURE );
5991
5992 return ToPointer(hSRS)->SetEquirectangular(
5993 dfCenterLat, dfCenterLong,
5994 dfFalseEasting, dfFalseNorthing );
5995 }
5996
5997 /************************************************************************/
5998 /* SetEquirectangular2() */
5999 /* Generalized form */
6000 /************************************************************************/
6001
SetEquirectangular2(double dfCenterLat,double dfCenterLong,double dfStdParallel1,double dfFalseEasting,double dfFalseNorthing)6002 OGRErr OGRSpatialReference::SetEquirectangular2(
6003 double dfCenterLat, double dfCenterLong,
6004 double dfStdParallel1,
6005 double dfFalseEasting,
6006 double dfFalseNorthing )
6007
6008 {
6009 if( dfCenterLat == 0.0 )
6010 {
6011 return d->replaceConversionAndUnref(
6012 proj_create_conversion_equidistant_cylindrical(
6013 d->getPROJContext(),
6014 dfStdParallel1, dfCenterLong,
6015 dfFalseEasting, dfFalseNorthing,
6016 nullptr, 0.0, nullptr, 0.0));
6017 }
6018
6019 // Non-standard extension with non-zero latitude of origin
6020 SetProjection( SRS_PT_EQUIRECTANGULAR );
6021 SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat );
6022 SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong );
6023 SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1 );
6024 SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
6025 SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
6026
6027 return OGRERR_NONE;
6028 }
6029
6030 /************************************************************************/
6031 /* OSRSetEquirectangular2() */
6032 /************************************************************************/
6033
OSRSetEquirectangular2(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfStdParallel1,double dfFalseEasting,double dfFalseNorthing)6034 OGRErr OSRSetEquirectangular2( OGRSpatialReferenceH hSRS,
6035 double dfCenterLat, double dfCenterLong,
6036 double dfStdParallel1,
6037 double dfFalseEasting,
6038 double dfFalseNorthing )
6039
6040 {
6041 VALIDATE_POINTER1( hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE );
6042
6043 return ToPointer(hSRS)->SetEquirectangular2(
6044 dfCenterLat, dfCenterLong,
6045 dfStdParallel1,
6046 dfFalseEasting, dfFalseNorthing );
6047 }
6048
6049 /************************************************************************/
6050 /* SetGS() */
6051 /************************************************************************/
6052
SetGS(double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6053 OGRErr OGRSpatialReference::SetGS( double dfCentralMeridian,
6054 double dfFalseEasting,
6055 double dfFalseNorthing )
6056
6057 {
6058 return d->replaceConversionAndUnref(
6059 proj_create_conversion_gall(
6060 d->getPROJContext(),
6061 dfCentralMeridian,
6062 dfFalseEasting, dfFalseNorthing,
6063 nullptr, 0.0, nullptr, 0.0));
6064 }
6065
6066 /************************************************************************/
6067 /* OSRSetGS() */
6068 /************************************************************************/
6069
OSRSetGS(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6070 OGRErr OSRSetGS( OGRSpatialReferenceH hSRS,
6071 double dfCentralMeridian,
6072 double dfFalseEasting,
6073 double dfFalseNorthing )
6074
6075 {
6076 VALIDATE_POINTER1( hSRS, "OSRSetGS", OGRERR_FAILURE );
6077
6078 return ToPointer(hSRS)->SetGS(
6079 dfCentralMeridian,
6080 dfFalseEasting, dfFalseNorthing );
6081 }
6082
6083 /************************************************************************/
6084 /* SetGH() */
6085 /************************************************************************/
6086
SetGH(double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6087 OGRErr OGRSpatialReference::SetGH( double dfCentralMeridian,
6088 double dfFalseEasting,
6089 double dfFalseNorthing )
6090
6091 {
6092 return d->replaceConversionAndUnref(
6093 proj_create_conversion_goode_homolosine(
6094 d->getPROJContext(),
6095 dfCentralMeridian,
6096 dfFalseEasting, dfFalseNorthing,
6097 nullptr, 0.0, nullptr, 0.0));
6098 }
6099
6100 /************************************************************************/
6101 /* OSRSetGH() */
6102 /************************************************************************/
6103
OSRSetGH(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6104 OGRErr OSRSetGH( OGRSpatialReferenceH hSRS,
6105 double dfCentralMeridian,
6106 double dfFalseEasting,
6107 double dfFalseNorthing )
6108
6109 {
6110 VALIDATE_POINTER1( hSRS, "OSRSetGH", OGRERR_FAILURE );
6111
6112 return ToPointer(hSRS)->SetGH(
6113 dfCentralMeridian,
6114 dfFalseEasting, dfFalseNorthing );
6115 }
6116
6117 /************************************************************************/
6118 /* SetIGH() */
6119 /************************************************************************/
6120
SetIGH()6121 OGRErr OGRSpatialReference::SetIGH()
6122
6123 {
6124 return d->replaceConversionAndUnref(
6125 proj_create_conversion_interrupted_goode_homolosine(
6126 d->getPROJContext(),
6127 0.0, 0.0, 0.0,
6128 nullptr, 0.0, nullptr, 0.0));
6129 }
6130
6131 /************************************************************************/
6132 /* OSRSetIGH() */
6133 /************************************************************************/
6134
OSRSetIGH(OGRSpatialReferenceH hSRS)6135 OGRErr OSRSetIGH( OGRSpatialReferenceH hSRS )
6136
6137 {
6138 VALIDATE_POINTER1( hSRS, "OSRSetIGH", OGRERR_FAILURE );
6139
6140 return ToPointer(hSRS)->SetIGH();
6141 }
6142
6143 /************************************************************************/
6144 /* SetGEOS() */
6145 /************************************************************************/
6146
SetGEOS(double dfCentralMeridian,double dfSatelliteHeight,double dfFalseEasting,double dfFalseNorthing)6147 OGRErr OGRSpatialReference::SetGEOS( double dfCentralMeridian,
6148 double dfSatelliteHeight,
6149 double dfFalseEasting,
6150 double dfFalseNorthing )
6151
6152 {
6153 return d->replaceConversionAndUnref(
6154 proj_create_conversion_geostationary_satellite_sweep_y(
6155 d->getPROJContext(),
6156 dfCentralMeridian, dfSatelliteHeight,
6157 dfFalseEasting, dfFalseNorthing,
6158 nullptr, 0.0, nullptr, 0.0));
6159 }
6160
6161 /************************************************************************/
6162 /* OSRSetGEOS() */
6163 /************************************************************************/
6164
OSRSetGEOS(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfSatelliteHeight,double dfFalseEasting,double dfFalseNorthing)6165 OGRErr OSRSetGEOS( OGRSpatialReferenceH hSRS,
6166 double dfCentralMeridian,
6167 double dfSatelliteHeight,
6168 double dfFalseEasting,
6169 double dfFalseNorthing )
6170
6171 {
6172 VALIDATE_POINTER1( hSRS, "OSRSetGEOS", OGRERR_FAILURE );
6173
6174 return ToPointer(hSRS)->SetGEOS(
6175 dfCentralMeridian, dfSatelliteHeight,
6176 dfFalseEasting, dfFalseNorthing );
6177 }
6178
6179 /************************************************************************/
6180 /* SetGaussSchreiberTMercator() */
6181 /************************************************************************/
6182
SetGaussSchreiberTMercator(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6183 OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(
6184 double dfCenterLat, double dfCenterLong,
6185 double dfScale,
6186 double dfFalseEasting,
6187 double dfFalseNorthing )
6188
6189 {
6190 return d->replaceConversionAndUnref(
6191 proj_create_conversion_gauss_schreiber_transverse_mercator(
6192 d->getPROJContext(),
6193 dfCenterLat, dfCenterLong, dfScale,
6194 dfFalseEasting, dfFalseNorthing,
6195 nullptr, 0.0, nullptr, 0.0));
6196 }
6197
6198 /************************************************************************/
6199 /* OSRSetGaussSchreiberTMercator() */
6200 /************************************************************************/
6201
OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6202 OGRErr OSRSetGaussSchreiberTMercator( OGRSpatialReferenceH hSRS,
6203 double dfCenterLat, double dfCenterLong,
6204 double dfScale,
6205 double dfFalseEasting,
6206 double dfFalseNorthing )
6207
6208 {
6209 VALIDATE_POINTER1( hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE );
6210
6211 return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6212 dfCenterLat, dfCenterLong, dfScale,
6213 dfFalseEasting, dfFalseNorthing );
6214 }
6215
6216 /************************************************************************/
6217 /* SetGnomonic() */
6218 /************************************************************************/
6219
SetGnomonic(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6220 OGRErr OGRSpatialReference::SetGnomonic(
6221 double dfCenterLat, double dfCenterLong,
6222 double dfFalseEasting,
6223 double dfFalseNorthing )
6224
6225 {
6226 return d->replaceConversionAndUnref(
6227 proj_create_conversion_gnomonic(
6228 d->getPROJContext(),
6229 dfCenterLat, dfCenterLong,
6230 dfFalseEasting, dfFalseNorthing,
6231 nullptr, 0.0, nullptr, 0.0));
6232 }
6233
6234 /************************************************************************/
6235 /* OSRSetGnomonic() */
6236 /************************************************************************/
6237
OSRSetGnomonic(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6238 OGRErr OSRSetGnomonic( OGRSpatialReferenceH hSRS,
6239 double dfCenterLat, double dfCenterLong,
6240 double dfFalseEasting,
6241 double dfFalseNorthing )
6242
6243 {
6244 VALIDATE_POINTER1( hSRS, "OSRSetGnomonic", OGRERR_FAILURE );
6245
6246 return ToPointer(hSRS)->SetGnomonic(
6247 dfCenterLat, dfCenterLong,
6248 dfFalseEasting, dfFalseNorthing );
6249 }
6250
6251 /************************************************************************/
6252 /* SetHOMAC() */
6253 /************************************************************************/
6254
6255 /**
6256 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6257 * azimuth angle.
6258 *
6259 * This projection corresponds to EPSG projection method 9815, also
6260 * sometimes known as hotine oblique mercator (variant B).
6261 *
6262 * This method does the same thing as the C function OSRSetHOMAC().
6263 *
6264 * @param dfCenterLat Latitude of the projection origin.
6265 * @param dfCenterLong Longitude of the projection origin.
6266 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6267 * centerline.
6268 * @param dfRectToSkew Angle from Rectified to Skew Grid
6269 * @param dfScale Scale factor applies to the projection origin.
6270 * @param dfFalseEasting False easting.
6271 * @param dfFalseNorthing False northing.
6272 *
6273 * @return OGRERR_NONE on success.
6274 */
6275
SetHOMAC(double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfRectToSkew,double dfScale,double dfFalseEasting,double dfFalseNorthing)6276 OGRErr OGRSpatialReference::SetHOMAC( double dfCenterLat, double dfCenterLong,
6277 double dfAzimuth, double dfRectToSkew,
6278 double dfScale,
6279 double dfFalseEasting,
6280 double dfFalseNorthing )
6281
6282 {
6283 return d->replaceConversionAndUnref(
6284 proj_create_conversion_hotine_oblique_mercator_variant_b(
6285 d->getPROJContext(),
6286 dfCenterLat, dfCenterLong, dfAzimuth, dfRectToSkew, dfScale,
6287 dfFalseEasting, dfFalseNorthing,
6288 nullptr, 0.0, nullptr, 0.0));
6289 }
6290
6291 /************************************************************************/
6292 /* OSRSetHOMAC() */
6293 /************************************************************************/
6294
6295 /**
6296 * \brief Set an Oblique Mercator projection using azimuth angle.
6297 *
6298 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
6299 */
OSRSetHOMAC(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfRectToSkew,double dfScale,double dfFalseEasting,double dfFalseNorthing)6300 OGRErr OSRSetHOMAC( OGRSpatialReferenceH hSRS,
6301 double dfCenterLat, double dfCenterLong,
6302 double dfAzimuth, double dfRectToSkew,
6303 double dfScale,
6304 double dfFalseEasting,
6305 double dfFalseNorthing )
6306
6307 {
6308 VALIDATE_POINTER1( hSRS, "OSRSetHOMAC", OGRERR_FAILURE );
6309
6310 return ToPointer(hSRS)->SetHOMAC(
6311 dfCenterLat, dfCenterLong,
6312 dfAzimuth, dfRectToSkew,
6313 dfScale,
6314 dfFalseEasting, dfFalseNorthing );
6315 }
6316
6317 /************************************************************************/
6318 /* SetHOM() */
6319 /************************************************************************/
6320
6321 /**
6322 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
6323 *
6324 * This projection corresponds to EPSG projection method 9812, also
6325 * sometimes known as hotine oblique mercator (variant A)..
6326 *
6327 * This method does the same thing as the C function OSRSetHOM().
6328 *
6329 * @param dfCenterLat Latitude of the projection origin.
6330 * @param dfCenterLong Longitude of the projection origin.
6331 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6332 * centerline.
6333 * @param dfRectToSkew Angle from Rectified to Skew Grid
6334 * @param dfScale Scale factor applies to the projection origin.
6335 * @param dfFalseEasting False easting.
6336 * @param dfFalseNorthing False northing.
6337 *
6338 * @return OGRERR_NONE on success.
6339 */
6340
SetHOM(double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfRectToSkew,double dfScale,double dfFalseEasting,double dfFalseNorthing)6341 OGRErr OGRSpatialReference::SetHOM( double dfCenterLat, double dfCenterLong,
6342 double dfAzimuth, double dfRectToSkew,
6343 double dfScale,
6344 double dfFalseEasting,
6345 double dfFalseNorthing )
6346
6347 {
6348 return d->replaceConversionAndUnref(
6349 proj_create_conversion_hotine_oblique_mercator_variant_a(
6350 d->getPROJContext(),
6351 dfCenterLat, dfCenterLong, dfAzimuth, dfRectToSkew, dfScale,
6352 dfFalseEasting, dfFalseNorthing,
6353 nullptr, 0.0, nullptr, 0.0));
6354 }
6355
6356 /************************************************************************/
6357 /* OSRSetHOM() */
6358 /************************************************************************/
6359 /**
6360 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
6361 *
6362 * This is the same as the C++ method OGRSpatialReference::SetHOM()
6363 */
OSRSetHOM(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfRectToSkew,double dfScale,double dfFalseEasting,double dfFalseNorthing)6364 OGRErr OSRSetHOM( OGRSpatialReferenceH hSRS,
6365 double dfCenterLat, double dfCenterLong,
6366 double dfAzimuth, double dfRectToSkew,
6367 double dfScale,
6368 double dfFalseEasting,
6369 double dfFalseNorthing )
6370
6371 {
6372 VALIDATE_POINTER1( hSRS, "OSRSetHOM", OGRERR_FAILURE );
6373
6374 return ToPointer(hSRS)->SetHOM(
6375 dfCenterLat, dfCenterLong,
6376 dfAzimuth, dfRectToSkew,
6377 dfScale,
6378 dfFalseEasting, dfFalseNorthing );
6379 }
6380
6381 /************************************************************************/
6382 /* SetHOM2PNO() */
6383 /************************************************************************/
6384
6385 /**
6386 * \brief Set a Hotine Oblique Mercator projection using two points on projection
6387 * centerline.
6388 *
6389 * This method does the same thing as the C function OSRSetHOM2PNO().
6390 *
6391 * @param dfCenterLat Latitude of the projection origin.
6392 * @param dfLat1 Latitude of the first point on center line.
6393 * @param dfLong1 Longitude of the first point on center line.
6394 * @param dfLat2 Latitude of the second point on center line.
6395 * @param dfLong2 Longitude of the second point on center line.
6396 * @param dfScale Scale factor applies to the projection origin.
6397 * @param dfFalseEasting False easting.
6398 * @param dfFalseNorthing False northing.
6399 *
6400 * @return OGRERR_NONE on success.
6401 */
6402
SetHOM2PNO(double dfCenterLat,double dfLat1,double dfLong1,double dfLat2,double dfLong2,double dfScale,double dfFalseEasting,double dfFalseNorthing)6403 OGRErr OGRSpatialReference::SetHOM2PNO( double dfCenterLat,
6404 double dfLat1, double dfLong1,
6405 double dfLat2, double dfLong2,
6406 double dfScale,
6407 double dfFalseEasting,
6408 double dfFalseNorthing )
6409
6410 {
6411 return d->replaceConversionAndUnref(
6412 proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
6413 d->getPROJContext(),
6414 dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2, dfScale,
6415 dfFalseEasting, dfFalseNorthing,
6416 nullptr, 0.0, nullptr, 0.0));
6417 }
6418
6419 /************************************************************************/
6420 /* OSRSetHOM2PNO() */
6421 /************************************************************************/
6422 /**
6423 * \brief Set a Hotine Oblique Mercator projection using two points on
6424 * projection centerline.
6425 *
6426 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
6427 */
OSRSetHOM2PNO(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfLat1,double dfLong1,double dfLat2,double dfLong2,double dfScale,double dfFalseEasting,double dfFalseNorthing)6428 OGRErr OSRSetHOM2PNO( OGRSpatialReferenceH hSRS,
6429 double dfCenterLat,
6430 double dfLat1, double dfLong1,
6431 double dfLat2, double dfLong2,
6432 double dfScale,
6433 double dfFalseEasting, double dfFalseNorthing )
6434
6435 {
6436 VALIDATE_POINTER1( hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE );
6437
6438 return ToPointer(hSRS)->SetHOM2PNO(
6439 dfCenterLat,
6440 dfLat1, dfLong1,
6441 dfLat2, dfLong2,
6442 dfScale,
6443 dfFalseEasting, dfFalseNorthing );
6444 }
6445
6446 /************************************************************************/
6447 /* SetLOM() */
6448 /************************************************************************/
6449
6450 /**
6451 * \brief Set a Laborde Oblique Mercator projection.
6452 *
6453 * @param dfCenterLat Latitude of the projection origin.
6454 * @param dfCenterLong Longitude of the projection origin.
6455 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
6456 * centerline.
6457 * @param dfScale Scale factor on the initiali line
6458 * @param dfFalseEasting False easting.
6459 * @param dfFalseNorthing False northing.
6460 *
6461 * @return OGRERR_NONE on success.
6462 */
6463
SetLOM(double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfScale,double dfFalseEasting,double dfFalseNorthing)6464 OGRErr OGRSpatialReference::SetLOM( double dfCenterLat, double dfCenterLong,
6465 double dfAzimuth,
6466 double dfScale,
6467 double dfFalseEasting,
6468 double dfFalseNorthing )
6469
6470 {
6471 return d->replaceConversionAndUnref(
6472 proj_create_conversion_laborde_oblique_mercator(
6473 d->getPROJContext(),
6474 dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
6475 dfFalseEasting, dfFalseNorthing,
6476 nullptr, 0.0, nullptr, 0.0));
6477 }
6478
6479 /************************************************************************/
6480 /* SetIWMPolyconic() */
6481 /************************************************************************/
6482
SetIWMPolyconic(double dfLat1,double dfLat2,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6483 OGRErr OGRSpatialReference::SetIWMPolyconic(
6484 double dfLat1, double dfLat2,
6485 double dfCenterLong,
6486 double dfFalseEasting, double dfFalseNorthing )
6487
6488 {
6489 return d->replaceConversionAndUnref(
6490 proj_create_conversion_international_map_world_polyconic(
6491 d->getPROJContext(),
6492 dfCenterLong, dfLat1, dfLat2,
6493 dfFalseEasting, dfFalseNorthing,
6494 nullptr, 0.0, nullptr, 0.0));
6495 }
6496
6497 /************************************************************************/
6498 /* OSRSetIWMPolyconic() */
6499 /************************************************************************/
6500
OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS,double dfLat1,double dfLat2,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6501 OGRErr OSRSetIWMPolyconic( OGRSpatialReferenceH hSRS,
6502 double dfLat1, double dfLat2,
6503 double dfCenterLong,
6504 double dfFalseEasting, double dfFalseNorthing )
6505
6506 {
6507 VALIDATE_POINTER1( hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE );
6508
6509 return ToPointer(hSRS)->SetIWMPolyconic(
6510 dfLat1, dfLat2, dfCenterLong,
6511 dfFalseEasting, dfFalseNorthing );
6512 }
6513
6514 /************************************************************************/
6515 /* SetKrovak() */
6516 /************************************************************************/
6517
6518 /** Krovak east-north projection.
6519 *
6520 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
6521 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
6522 */
SetKrovak(double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfPseudoStdParallel1,double dfScale,double dfFalseEasting,double dfFalseNorthing)6523 OGRErr OGRSpatialReference::SetKrovak( double dfCenterLat, double dfCenterLong,
6524 double dfAzimuth,
6525 double dfPseudoStdParallel1,
6526 double dfScale,
6527 double dfFalseEasting,
6528 double dfFalseNorthing )
6529
6530 {
6531 return d->replaceConversionAndUnref(
6532 proj_create_conversion_krovak_north_oriented(
6533 d->getPROJContext(),
6534 dfCenterLat, dfCenterLong, dfAzimuth, dfPseudoStdParallel1, dfScale,
6535 dfFalseEasting, dfFalseNorthing,
6536 nullptr, 0.0, nullptr, 0.0));
6537
6538 }
6539
6540 /************************************************************************/
6541 /* OSRSetKrovak() */
6542 /************************************************************************/
6543
OSRSetKrovak(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfAzimuth,double dfPseudoStdParallel1,double dfScale,double dfFalseEasting,double dfFalseNorthing)6544 OGRErr OSRSetKrovak( OGRSpatialReferenceH hSRS,
6545 double dfCenterLat, double dfCenterLong,
6546 double dfAzimuth, double dfPseudoStdParallel1,
6547 double dfScale,
6548 double dfFalseEasting,
6549 double dfFalseNorthing )
6550
6551 {
6552 VALIDATE_POINTER1( hSRS, "OSRSetKrovak", OGRERR_FAILURE );
6553
6554 return ToPointer(hSRS)->SetKrovak(
6555 dfCenterLat, dfCenterLong,
6556 dfAzimuth, dfPseudoStdParallel1,
6557 dfScale,
6558 dfFalseEasting, dfFalseNorthing );
6559 }
6560
6561 /************************************************************************/
6562 /* SetLAEA() */
6563 /************************************************************************/
6564
SetLAEA(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6565 OGRErr OGRSpatialReference::SetLAEA( double dfCenterLat, double dfCenterLong,
6566 double dfFalseEasting,
6567 double dfFalseNorthing )
6568
6569 {
6570 auto conv =
6571 proj_create_conversion_lambert_azimuthal_equal_area(
6572 d->getPROJContext(),
6573 dfCenterLat, dfCenterLong,
6574 dfFalseEasting, dfFalseNorthing,
6575 nullptr, 0.0, nullptr, 0.0);
6576
6577 const char* pszName = nullptr;
6578 double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6579 CPLString osName = pszName ? pszName : "";
6580
6581 d->refreshProjObj();
6582
6583 d->demoteFromBoundCRS();
6584
6585 auto cs = proj_create_cartesian_2D_cs(
6586 d->getPROJContext(),
6587 std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0 ?
6588 PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH :
6589 std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0 ?
6590 PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH :
6591 PJ_CART2D_EASTING_NORTHING,
6592 !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6593 auto projCRS = proj_create_projected_crs(
6594 d->getPROJContext(),
6595 d->getProjCRSName(), d->getGeodBaseCRS(), conv, cs);
6596 proj_destroy(conv);
6597 proj_destroy(cs);
6598
6599 d->setPjCRS(projCRS);
6600
6601 d->undoDemoteFromBoundCRS();
6602
6603 return OGRERR_NONE;
6604 }
6605
6606 /************************************************************************/
6607 /* OSRSetLAEA() */
6608 /************************************************************************/
6609
OSRSetLAEA(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6610 OGRErr OSRSetLAEA( OGRSpatialReferenceH hSRS,
6611 double dfCenterLat, double dfCenterLong,
6612 double dfFalseEasting, double dfFalseNorthing )
6613
6614 {
6615 VALIDATE_POINTER1( hSRS, "OSRSetLAEA", OGRERR_FAILURE );
6616
6617 return ToPointer(hSRS)->SetLAEA(
6618 dfCenterLat, dfCenterLong,
6619 dfFalseEasting, dfFalseNorthing );
6620 }
6621
6622 /************************************************************************/
6623 /* SetLCC() */
6624 /************************************************************************/
6625
SetLCC(double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6626 OGRErr OGRSpatialReference::SetLCC( double dfStdP1, double dfStdP2,
6627 double dfCenterLat, double dfCenterLong,
6628 double dfFalseEasting,
6629 double dfFalseNorthing )
6630
6631 {
6632 return d->replaceConversionAndUnref(
6633 proj_create_conversion_lambert_conic_conformal_2sp(
6634 d->getPROJContext(),
6635 dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6636 dfFalseEasting, dfFalseNorthing,
6637 nullptr, 0, nullptr, 0));
6638 }
6639
6640 /************************************************************************/
6641 /* OSRSetLCC() */
6642 /************************************************************************/
6643
OSRSetLCC(OGRSpatialReferenceH hSRS,double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6644 OGRErr OSRSetLCC( OGRSpatialReferenceH hSRS,
6645 double dfStdP1, double dfStdP2,
6646 double dfCenterLat, double dfCenterLong,
6647 double dfFalseEasting, double dfFalseNorthing )
6648
6649 {
6650 VALIDATE_POINTER1( hSRS, "OSRSetLCC", OGRERR_FAILURE );
6651
6652 return ToPointer(hSRS)->SetLCC(
6653 dfStdP1, dfStdP2,
6654 dfCenterLat, dfCenterLong,
6655 dfFalseEasting, dfFalseNorthing );
6656 }
6657
6658 /************************************************************************/
6659 /* SetLCC1SP() */
6660 /************************************************************************/
6661
SetLCC1SP(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6662 OGRErr OGRSpatialReference::SetLCC1SP( double dfCenterLat, double dfCenterLong,
6663 double dfScale,
6664 double dfFalseEasting,
6665 double dfFalseNorthing )
6666
6667 {
6668 return d->replaceConversionAndUnref(
6669 proj_create_conversion_lambert_conic_conformal_1sp(
6670 d->getPROJContext(),
6671 dfCenterLat, dfCenterLong, dfScale,
6672 dfFalseEasting, dfFalseNorthing,
6673 nullptr, 0, nullptr, 0));
6674 }
6675
6676 /************************************************************************/
6677 /* OSRSetLCC1SP() */
6678 /************************************************************************/
6679
OSRSetLCC1SP(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6680 OGRErr OSRSetLCC1SP( OGRSpatialReferenceH hSRS,
6681 double dfCenterLat, double dfCenterLong,
6682 double dfScale,
6683 double dfFalseEasting, double dfFalseNorthing )
6684
6685 {
6686 VALIDATE_POINTER1( hSRS, "OSRSetLCC1SP", OGRERR_FAILURE );
6687
6688 return ToPointer(hSRS)->SetLCC1SP(
6689 dfCenterLat, dfCenterLong,
6690 dfScale,
6691 dfFalseEasting, dfFalseNorthing );
6692 }
6693
6694 /************************************************************************/
6695 /* SetLCCB() */
6696 /************************************************************************/
6697
SetLCCB(double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6698 OGRErr OGRSpatialReference::SetLCCB( double dfStdP1, double dfStdP2,
6699 double dfCenterLat, double dfCenterLong,
6700 double dfFalseEasting,
6701 double dfFalseNorthing )
6702
6703 {
6704 return d->replaceConversionAndUnref(
6705 proj_create_conversion_lambert_conic_conformal_2sp_belgium(
6706 d->getPROJContext(),
6707 dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6708 dfFalseEasting, dfFalseNorthing,
6709 nullptr, 0, nullptr, 0));
6710 }
6711
6712 /************************************************************************/
6713 /* OSRSetLCCB() */
6714 /************************************************************************/
6715
OSRSetLCCB(OGRSpatialReferenceH hSRS,double dfStdP1,double dfStdP2,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6716 OGRErr OSRSetLCCB( OGRSpatialReferenceH hSRS,
6717 double dfStdP1, double dfStdP2,
6718 double dfCenterLat, double dfCenterLong,
6719 double dfFalseEasting, double dfFalseNorthing )
6720
6721 {
6722 VALIDATE_POINTER1( hSRS, "OSRSetLCCB", OGRERR_FAILURE );
6723
6724 return ToPointer(hSRS)->SetLCCB(
6725 dfStdP1, dfStdP2,
6726 dfCenterLat, dfCenterLong,
6727 dfFalseEasting, dfFalseNorthing );
6728 }
6729
6730 /************************************************************************/
6731 /* SetMC() */
6732 /************************************************************************/
6733
SetMC(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6734 OGRErr OGRSpatialReference::SetMC( double dfCenterLat, double dfCenterLong,
6735 double dfFalseEasting,
6736 double dfFalseNorthing )
6737
6738 {
6739 (void)dfCenterLat; // ignored
6740
6741 return d->replaceConversionAndUnref(
6742 proj_create_conversion_miller_cylindrical(
6743 d->getPROJContext(),
6744 dfCenterLong,
6745 dfFalseEasting, dfFalseNorthing,
6746 nullptr, 0, nullptr, 0));
6747 }
6748
6749 /************************************************************************/
6750 /* OSRSetMC() */
6751 /************************************************************************/
6752
OSRSetMC(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6753 OGRErr OSRSetMC( OGRSpatialReferenceH hSRS,
6754 double dfCenterLat, double dfCenterLong,
6755 double dfFalseEasting, double dfFalseNorthing )
6756
6757 {
6758 VALIDATE_POINTER1( hSRS, "OSRSetMC", OGRERR_FAILURE );
6759
6760 return ToPointer(hSRS)->SetMC(
6761 dfCenterLat, dfCenterLong,
6762 dfFalseEasting, dfFalseNorthing );
6763 }
6764
6765 /************************************************************************/
6766 /* SetMercator() */
6767 /************************************************************************/
6768
SetMercator(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6769 OGRErr OGRSpatialReference::SetMercator( double dfCenterLat, double dfCenterLong,
6770 double dfScale,
6771 double dfFalseEasting,
6772 double dfFalseNorthing )
6773
6774 {
6775 if( dfCenterLat != 0.0 && dfScale == 1.0 )
6776 {
6777 // Not sure this is correct, but this is how it has been used
6778 // historically
6779 return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
6780 dfFalseNorthing);
6781 }
6782 return d->replaceConversionAndUnref(
6783 proj_create_conversion_mercator_variant_a(
6784 d->getPROJContext(),
6785 dfCenterLat, // should be zero
6786 dfCenterLong,
6787 dfScale,
6788 dfFalseEasting, dfFalseNorthing,
6789 nullptr, 0, nullptr, 0));
6790 }
6791
6792 /************************************************************************/
6793 /* OSRSetMercator() */
6794 /************************************************************************/
6795
OSRSetMercator(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)6796 OGRErr OSRSetMercator( OGRSpatialReferenceH hSRS,
6797 double dfCenterLat, double dfCenterLong,
6798 double dfScale,
6799 double dfFalseEasting, double dfFalseNorthing )
6800
6801 {
6802 VALIDATE_POINTER1( hSRS, "OSRSetMercator", OGRERR_FAILURE );
6803
6804 return ToPointer(hSRS)->SetMercator(
6805 dfCenterLat, dfCenterLong,
6806 dfScale,
6807 dfFalseEasting, dfFalseNorthing );
6808 }
6809
6810 /************************************************************************/
6811 /* SetMercator2SP() */
6812 /************************************************************************/
6813
SetMercator2SP(double dfStdP1,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6814 OGRErr OGRSpatialReference::SetMercator2SP(
6815 double dfStdP1,
6816 double dfCenterLat, double dfCenterLong,
6817 double dfFalseEasting,
6818 double dfFalseNorthing )
6819
6820 {
6821 if( dfCenterLat == 0.0 )
6822 {
6823 return d->replaceConversionAndUnref(
6824 proj_create_conversion_mercator_variant_b(
6825 d->getPROJContext(),
6826 dfStdP1,
6827 dfCenterLong,
6828 dfFalseEasting, dfFalseNorthing,
6829 nullptr, 0, nullptr, 0));
6830 }
6831
6832 SetProjection( SRS_PT_MERCATOR_2SP );
6833
6834 SetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, dfStdP1 );
6835 SetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat );
6836 SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCenterLong );
6837 SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
6838 SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
6839
6840 return OGRERR_NONE;
6841 }
6842
6843 /************************************************************************/
6844 /* OSRSetMercator2SP() */
6845 /************************************************************************/
6846
OSRSetMercator2SP(OGRSpatialReferenceH hSRS,double dfStdP1,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6847 OGRErr OSRSetMercator2SP( OGRSpatialReferenceH hSRS,
6848 double dfStdP1,
6849 double dfCenterLat, double dfCenterLong,
6850 double dfFalseEasting, double dfFalseNorthing )
6851
6852 {
6853 VALIDATE_POINTER1( hSRS, "OSRSetMercator2SP", OGRERR_FAILURE );
6854
6855 return ToPointer(hSRS)->SetMercator2SP(
6856 dfStdP1,
6857 dfCenterLat, dfCenterLong,
6858 dfFalseEasting, dfFalseNorthing );
6859 }
6860
6861 /************************************************************************/
6862 /* SetMollweide() */
6863 /************************************************************************/
6864
SetMollweide(double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6865 OGRErr OGRSpatialReference::SetMollweide( double dfCentralMeridian,
6866 double dfFalseEasting,
6867 double dfFalseNorthing )
6868
6869 {
6870 return d->replaceConversionAndUnref(
6871 proj_create_conversion_mollweide(
6872 d->getPROJContext(),
6873 dfCentralMeridian,
6874 dfFalseEasting, dfFalseNorthing,
6875 nullptr, 0, nullptr, 0));
6876 }
6877
6878 /************************************************************************/
6879 /* OSRSetMollweide() */
6880 /************************************************************************/
6881
OSRSetMollweide(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)6882 OGRErr OSRSetMollweide( OGRSpatialReferenceH hSRS,
6883 double dfCentralMeridian,
6884 double dfFalseEasting, double dfFalseNorthing )
6885
6886 {
6887 VALIDATE_POINTER1( hSRS, "OSRSetMollweide", OGRERR_FAILURE );
6888
6889 return ToPointer(hSRS)->SetMollweide(
6890 dfCentralMeridian,
6891 dfFalseEasting, dfFalseNorthing );
6892 }
6893
6894 /************************************************************************/
6895 /* SetNZMG() */
6896 /************************************************************************/
6897
SetNZMG(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6898 OGRErr OGRSpatialReference::SetNZMG( double dfCenterLat, double dfCenterLong,
6899 double dfFalseEasting,
6900 double dfFalseNorthing )
6901
6902 {
6903 return d->replaceConversionAndUnref(
6904 proj_create_conversion_new_zealand_mapping_grid(
6905 d->getPROJContext(),
6906 dfCenterLat, dfCenterLong,
6907 dfFalseEasting, dfFalseNorthing,
6908 nullptr, 0, nullptr, 0));
6909 }
6910
6911 /************************************************************************/
6912 /* OSRSetNZMG() */
6913 /************************************************************************/
6914
OSRSetNZMG(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6915 OGRErr OSRSetNZMG( OGRSpatialReferenceH hSRS,
6916 double dfCenterLat, double dfCenterLong,
6917 double dfFalseEasting, double dfFalseNorthing )
6918
6919 {
6920 VALIDATE_POINTER1( hSRS, "OSRSetNZMG", OGRERR_FAILURE );
6921
6922 return ToPointer(hSRS)->SetNZMG(
6923 dfCenterLat, dfCenterLong,
6924 dfFalseEasting, dfFalseNorthing );
6925 }
6926
6927 /************************************************************************/
6928 /* SetOS() */
6929 /************************************************************************/
6930
SetOS(double dfOriginLat,double dfCMeridian,double dfScale,double dfFalseEasting,double dfFalseNorthing)6931 OGRErr OGRSpatialReference::SetOS( double dfOriginLat, double dfCMeridian,
6932 double dfScale,
6933 double dfFalseEasting,
6934 double dfFalseNorthing )
6935
6936 {
6937 return d->replaceConversionAndUnref(
6938 proj_create_conversion_oblique_stereographic(
6939 d->getPROJContext(),
6940 dfOriginLat, dfCMeridian, dfScale,
6941 dfFalseEasting, dfFalseNorthing,
6942 nullptr, 0, nullptr, 0));
6943 }
6944
6945 /************************************************************************/
6946 /* OSRSetOS() */
6947 /************************************************************************/
6948
OSRSetOS(OGRSpatialReferenceH hSRS,double dfOriginLat,double dfCMeridian,double dfScale,double dfFalseEasting,double dfFalseNorthing)6949 OGRErr OSRSetOS( OGRSpatialReferenceH hSRS,
6950 double dfOriginLat, double dfCMeridian,
6951 double dfScale,
6952 double dfFalseEasting, double dfFalseNorthing )
6953
6954 {
6955 VALIDATE_POINTER1( hSRS, "OSRSetOS", OGRERR_FAILURE );
6956
6957 return ToPointer(hSRS)->SetOS(
6958 dfOriginLat, dfCMeridian,
6959 dfScale,
6960 dfFalseEasting, dfFalseNorthing );
6961 }
6962
6963 /************************************************************************/
6964 /* SetOrthographic() */
6965 /************************************************************************/
6966
SetOrthographic(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6967 OGRErr OGRSpatialReference::SetOrthographic(
6968 double dfCenterLat, double dfCenterLong,
6969 double dfFalseEasting, double dfFalseNorthing )
6970
6971 {
6972 return d->replaceConversionAndUnref(
6973 proj_create_conversion_orthographic(
6974 d->getPROJContext(),
6975 dfCenterLat, dfCenterLong,
6976 dfFalseEasting, dfFalseNorthing,
6977 nullptr, 0, nullptr, 0));
6978 }
6979
6980 /************************************************************************/
6981 /* OSRSetOrthographic() */
6982 /************************************************************************/
6983
OSRSetOrthographic(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)6984 OGRErr OSRSetOrthographic( OGRSpatialReferenceH hSRS,
6985 double dfCenterLat, double dfCenterLong,
6986 double dfFalseEasting, double dfFalseNorthing )
6987
6988 {
6989 VALIDATE_POINTER1( hSRS, "OSRSetOrthographic", OGRERR_FAILURE );
6990
6991 return ToPointer(hSRS)->SetOrthographic(
6992 dfCenterLat, dfCenterLong,
6993 dfFalseEasting, dfFalseNorthing );
6994 }
6995
6996 /************************************************************************/
6997 /* SetPolyconic() */
6998 /************************************************************************/
6999
SetPolyconic(double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7000 OGRErr OGRSpatialReference::SetPolyconic(
7001 double dfCenterLat, double dfCenterLong,
7002 double dfFalseEasting, double dfFalseNorthing )
7003
7004 {
7005 // note: it seems that by some definitions this should include a
7006 // scale_factor parameter.
7007 return d->replaceConversionAndUnref(
7008 proj_create_conversion_american_polyconic(
7009 d->getPROJContext(),
7010 dfCenterLat, dfCenterLong,
7011 dfFalseEasting, dfFalseNorthing,
7012 nullptr, 0, nullptr, 0));
7013 }
7014
7015 /************************************************************************/
7016 /* OSRSetPolyconic() */
7017 /************************************************************************/
7018
OSRSetPolyconic(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7019 OGRErr OSRSetPolyconic( OGRSpatialReferenceH hSRS,
7020 double dfCenterLat, double dfCenterLong,
7021 double dfFalseEasting, double dfFalseNorthing )
7022
7023 {
7024 VALIDATE_POINTER1( hSRS, "OSRSetPolyconic", OGRERR_FAILURE );
7025
7026 return ToPointer(hSRS)->SetPolyconic(
7027 dfCenterLat, dfCenterLong,
7028 dfFalseEasting, dfFalseNorthing );
7029 }
7030
7031 /************************************************************************/
7032 /* SetPS() */
7033 /************************************************************************/
7034
7035 /** Sets a Polar Stereographic projection.
7036 *
7037 * Two variants are possible:
7038 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7039 * interpretated as the latitude of origin, combined with the scale factor
7040 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7041 * is interpretated as the latitude of true scale. In that situation, dfScale
7042 * must be set to 1 (it is ignored in the projection parameters)
7043 */
SetPS(double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)7044 OGRErr OGRSpatialReference::SetPS(
7045 double dfCenterLat, double dfCenterLong,
7046 double dfScale,
7047 double dfFalseEasting, double dfFalseNorthing )
7048
7049 {
7050 PJ* conv;
7051 if( dfScale == 1.0 && std::abs(std::abs(dfCenterLat)-90) > 1e-8 )
7052 {
7053 conv = proj_create_conversion_polar_stereographic_variant_b(
7054 d->getPROJContext(),
7055 dfCenterLat, dfCenterLong,
7056 dfFalseEasting, dfFalseNorthing,
7057 nullptr, 0, nullptr, 0);
7058 }
7059 else
7060 {
7061 conv = proj_create_conversion_polar_stereographic_variant_a(
7062 d->getPROJContext(),
7063 dfCenterLat, dfCenterLong, dfScale,
7064 dfFalseEasting, dfFalseNorthing,
7065 nullptr, 0, nullptr, 0);
7066 }
7067
7068 const char* pszName = nullptr;
7069 double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7070 CPLString osName = pszName ? pszName : "";
7071
7072 d->refreshProjObj();
7073
7074 d->demoteFromBoundCRS();
7075
7076 auto cs = proj_create_cartesian_2D_cs(
7077 d->getPROJContext(),
7078 dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH :
7079 PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7080 !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7081 auto projCRS = proj_create_projected_crs(
7082 d->getPROJContext(),
7083 d->getProjCRSName(), d->getGeodBaseCRS(), conv, cs);
7084 proj_destroy(conv);
7085 proj_destroy(cs);
7086
7087 d->setPjCRS(projCRS);
7088
7089 d->undoDemoteFromBoundCRS();
7090
7091 return OGRERR_NONE;
7092 }
7093
7094 /************************************************************************/
7095 /* OSRSetPS() */
7096 /************************************************************************/
7097
OSRSetPS(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong,double dfScale,double dfFalseEasting,double dfFalseNorthing)7098 OGRErr OSRSetPS( OGRSpatialReferenceH hSRS,
7099 double dfCenterLat, double dfCenterLong,
7100 double dfScale,
7101 double dfFalseEasting, double dfFalseNorthing )
7102
7103 {
7104 VALIDATE_POINTER1( hSRS, "OSRSetPS", OGRERR_FAILURE );
7105
7106 return ToPointer(hSRS)->SetPS(
7107 dfCenterLat, dfCenterLong,
7108 dfScale,
7109 dfFalseEasting, dfFalseNorthing );
7110 }
7111
7112 /************************************************************************/
7113 /* SetRobinson() */
7114 /************************************************************************/
7115
SetRobinson(double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7116 OGRErr OGRSpatialReference::SetRobinson( double dfCenterLong,
7117 double dfFalseEasting,
7118 double dfFalseNorthing )
7119
7120 {
7121 return d->replaceConversionAndUnref(
7122 proj_create_conversion_robinson(
7123 d->getPROJContext(),
7124 dfCenterLong,
7125 dfFalseEasting, dfFalseNorthing,
7126 nullptr, 0, nullptr, 0));
7127 }
7128
7129 /************************************************************************/
7130 /* OSRSetRobinson() */
7131 /************************************************************************/
7132
OSRSetRobinson(OGRSpatialReferenceH hSRS,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7133 OGRErr OSRSetRobinson( OGRSpatialReferenceH hSRS,
7134 double dfCenterLong,
7135 double dfFalseEasting, double dfFalseNorthing )
7136
7137 {
7138 VALIDATE_POINTER1( hSRS, "OSRSetRobinson", OGRERR_FAILURE );
7139
7140 return ToPointer(hSRS)->SetRobinson(
7141 dfCenterLong,
7142 dfFalseEasting, dfFalseNorthing );
7143 }
7144
7145 /************************************************************************/
7146 /* SetSinusoidal() */
7147 /************************************************************************/
7148
SetSinusoidal(double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7149 OGRErr OGRSpatialReference::SetSinusoidal( double dfCenterLong,
7150 double dfFalseEasting,
7151 double dfFalseNorthing )
7152
7153 {
7154 return d->replaceConversionAndUnref(
7155 proj_create_conversion_sinusoidal(
7156 d->getPROJContext(),
7157 dfCenterLong,
7158 dfFalseEasting, dfFalseNorthing,
7159 nullptr, 0, nullptr, 0));
7160 }
7161
7162 /************************************************************************/
7163 /* OSRSetSinusoidal() */
7164 /************************************************************************/
7165
OSRSetSinusoidal(OGRSpatialReferenceH hSRS,double dfCenterLong,double dfFalseEasting,double dfFalseNorthing)7166 OGRErr OSRSetSinusoidal( OGRSpatialReferenceH hSRS,
7167 double dfCenterLong,
7168 double dfFalseEasting, double dfFalseNorthing )
7169
7170 {
7171 VALIDATE_POINTER1( hSRS, "OSRSetSinusoidal", OGRERR_FAILURE );
7172
7173 return ToPointer(hSRS)->SetSinusoidal(
7174 dfCenterLong,
7175 dfFalseEasting, dfFalseNorthing );
7176 }
7177
7178 /************************************************************************/
7179 /* SetStereographic() */
7180 /************************************************************************/
7181
SetStereographic(double dfOriginLat,double dfCMeridian,double dfScale,double dfFalseEasting,double dfFalseNorthing)7182 OGRErr OGRSpatialReference::SetStereographic(
7183 double dfOriginLat, double dfCMeridian,
7184 double dfScale,
7185 double dfFalseEasting,
7186 double dfFalseNorthing )
7187
7188 {
7189 return d->replaceConversionAndUnref(
7190 proj_create_conversion_stereographic(
7191 d->getPROJContext(),
7192 dfOriginLat, dfCMeridian, dfScale,
7193 dfFalseEasting, dfFalseNorthing,
7194 nullptr, 0, nullptr, 0));
7195 }
7196
7197 /************************************************************************/
7198 /* OSRSetStereographic() */
7199 /************************************************************************/
7200
OSRSetStereographic(OGRSpatialReferenceH hSRS,double dfOriginLat,double dfCMeridian,double dfScale,double dfFalseEasting,double dfFalseNorthing)7201 OGRErr OSRSetStereographic( OGRSpatialReferenceH hSRS,
7202 double dfOriginLat, double dfCMeridian,
7203 double dfScale,
7204 double dfFalseEasting, double dfFalseNorthing )
7205
7206 {
7207 VALIDATE_POINTER1( hSRS, "OSRSetStereographic", OGRERR_FAILURE );
7208
7209 return ToPointer(hSRS)->SetStereographic(
7210 dfOriginLat, dfCMeridian,
7211 dfScale,
7212 dfFalseEasting, dfFalseNorthing );
7213 }
7214
7215 /************************************************************************/
7216 /* SetSOC() */
7217 /* */
7218 /* NOTE: This definition isn't really used in practice any more */
7219 /* and should be considered deprecated. It seems that swiss */
7220 /* oblique mercator is now define as Hotine_Oblique_Mercator */
7221 /* with an azimuth of 90 and a rectified_grid_angle of 90. See */
7222 /* EPSG:2056 and Bug 423. */
7223 /************************************************************************/
7224
SetSOC(double dfLatitudeOfOrigin,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)7225 OGRErr OGRSpatialReference::SetSOC( double dfLatitudeOfOrigin,
7226 double dfCentralMeridian,
7227 double dfFalseEasting,
7228 double dfFalseNorthing )
7229
7230 {
7231 return d->replaceConversionAndUnref(
7232 proj_create_conversion_hotine_oblique_mercator_variant_b(
7233 d->getPROJContext(),
7234 dfLatitudeOfOrigin, dfCentralMeridian, 90.0, 90.0, 1.0,
7235 dfFalseEasting, dfFalseNorthing,
7236 nullptr, 0.0, nullptr, 0.0));
7237 #if 0
7238 SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7239 SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7240 SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7241 SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7242 SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7243
7244 return OGRERR_NONE;
7245 #endif
7246 }
7247
7248 /************************************************************************/
7249 /* OSRSetSOC() */
7250 /************************************************************************/
7251
OSRSetSOC(OGRSpatialReferenceH hSRS,double dfLatitudeOfOrigin,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)7252 OGRErr OSRSetSOC( OGRSpatialReferenceH hSRS,
7253 double dfLatitudeOfOrigin, double dfCentralMeridian,
7254 double dfFalseEasting, double dfFalseNorthing )
7255
7256 {
7257 VALIDATE_POINTER1( hSRS, "OSRSetSOC", OGRERR_FAILURE );
7258
7259 return ToPointer(hSRS)->SetSOC(
7260 dfLatitudeOfOrigin, dfCentralMeridian,
7261 dfFalseEasting, dfFalseNorthing );
7262 }
7263
7264 /************************************************************************/
7265 /* SetVDG() */
7266 /************************************************************************/
7267
SetVDG(double dfCMeridian,double dfFalseEasting,double dfFalseNorthing)7268 OGRErr OGRSpatialReference::SetVDG( double dfCMeridian,
7269 double dfFalseEasting,
7270 double dfFalseNorthing )
7271
7272 {
7273 return d->replaceConversionAndUnref(
7274 proj_create_conversion_van_der_grinten(
7275 d->getPROJContext(),
7276 dfCMeridian,
7277 dfFalseEasting, dfFalseNorthing,
7278 nullptr, 0, nullptr, 0));
7279 }
7280
7281 /************************************************************************/
7282 /* OSRSetVDG() */
7283 /************************************************************************/
7284
OSRSetVDG(OGRSpatialReferenceH hSRS,double dfCentralMeridian,double dfFalseEasting,double dfFalseNorthing)7285 OGRErr OSRSetVDG( OGRSpatialReferenceH hSRS,
7286 double dfCentralMeridian,
7287 double dfFalseEasting, double dfFalseNorthing )
7288
7289 {
7290 VALIDATE_POINTER1( hSRS, "OSRSetVDG", OGRERR_FAILURE );
7291
7292 return ToPointer(hSRS)->SetVDG(
7293 dfCentralMeridian,
7294 dfFalseEasting, dfFalseNorthing );
7295 }
7296
7297 /************************************************************************/
7298 /* SetUTM() */
7299 /************************************************************************/
7300
7301 /**
7302 * \brief Set UTM projection definition.
7303 *
7304 * This will generate a projection definition with the full set of
7305 * transverse mercator projection parameters for the given UTM zone.
7306 * If no PROJCS[] description is set yet, one will be set to look
7307 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7308 *
7309 * This method is the same as the C function OSRSetUTM().
7310 *
7311 * @param nZone UTM zone.
7312 *
7313 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7314 * hemisphere.
7315 *
7316 * @return OGRERR_NONE on success.
7317 */
7318
SetUTM(int nZone,int bNorth)7319 OGRErr OGRSpatialReference::SetUTM( int nZone, int bNorth )
7320
7321 {
7322 if( nZone < 0 || nZone > 60 )
7323 {
7324 CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7325 return OGRERR_FAILURE;
7326 }
7327
7328 return d->replaceConversionAndUnref(
7329 proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7330 }
7331
7332 /************************************************************************/
7333 /* OSRSetUTM() */
7334 /************************************************************************/
7335
7336 /**
7337 * \brief Set UTM projection definition.
7338 *
7339 * This is the same as the C++ method OGRSpatialReference::SetUTM()
7340 */
OSRSetUTM(OGRSpatialReferenceH hSRS,int nZone,int bNorth)7341 OGRErr OSRSetUTM( OGRSpatialReferenceH hSRS, int nZone, int bNorth )
7342
7343 {
7344 VALIDATE_POINTER1( hSRS, "OSRSetUTM", OGRERR_FAILURE );
7345
7346 return ToPointer(hSRS)->SetUTM( nZone, bNorth );
7347 }
7348
7349 /************************************************************************/
7350 /* GetUTMZone() */
7351 /* */
7352 /* Returns zero if it isn't UTM. */
7353 /************************************************************************/
7354
7355 /**
7356 * \brief Get utm zone information.
7357 *
7358 * This is the same as the C function OSRGetUTMZone().
7359 *
7360 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
7361 * zone which is negative in the southern hemisphere instead of having the
7362 * pbNorth flag used in the C and C++ interface.
7363 *
7364 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
7365 * FALSE if southern.
7366 *
7367 * @return UTM zone number or zero if this isn't a UTM definition.
7368 */
7369
GetUTMZone(int * pbNorth) const7370 int OGRSpatialReference::GetUTMZone( int * pbNorth ) const
7371
7372 {
7373 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
7374 if( IsProjected() && GetAxesCount() == 3 )
7375 {
7376 OGRSpatialReference* poSRSTmp = Clone();
7377 poSRSTmp->DemoteTo2D(nullptr);
7378 const int nZone = poSRSTmp->GetUTMZone(pbNorth);
7379 delete poSRSTmp;
7380 return nZone;
7381 }
7382 #endif
7383
7384 const char *pszProjection = GetAttrValue( "PROJECTION" );
7385
7386 if( pszProjection == nullptr
7387 || !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) )
7388 return 0;
7389
7390 if( GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 ) != 0.0 )
7391 return 0;
7392
7393 if( GetProjParm( SRS_PP_SCALE_FACTOR, 1.0 ) != 0.9996 )
7394 return 0;
7395
7396 if( fabs(GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 )-500000.0) > 0.001 )
7397 return 0;
7398
7399 const double dfFalseNorthing = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0);
7400
7401 if( dfFalseNorthing != 0.0
7402 && fabs(dfFalseNorthing-10000000.0) > 0.001 )
7403 return 0;
7404
7405 if( pbNorth != nullptr )
7406 *pbNorth = (dfFalseNorthing == 0);
7407
7408 const double dfCentralMeridian =
7409 GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0);
7410 const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
7411
7412 if( dfCentralMeridian < -177.00001 ||
7413 dfCentralMeridian > 177.000001 ||
7414 CPLIsNan(dfZone) ||
7415 std::abs(dfZone - static_cast<int>(dfZone) - 0.5 ) > 0.00001 )
7416 return 0;
7417
7418 return static_cast<int>(dfZone);
7419 }
7420
7421 /************************************************************************/
7422 /* OSRGetUTMZone() */
7423 /************************************************************************/
7424
7425 /**
7426 * \brief Get utm zone information.
7427 *
7428 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
7429 */
OSRGetUTMZone(OGRSpatialReferenceH hSRS,int * pbNorth)7430 int OSRGetUTMZone( OGRSpatialReferenceH hSRS, int *pbNorth )
7431
7432 {
7433 VALIDATE_POINTER1( hSRS, "OSRGetUTMZone", 0 );
7434
7435 return ToPointer(hSRS)->GetUTMZone( pbNorth );
7436 }
7437
7438 /************************************************************************/
7439 /* SetWagner() */
7440 /************************************************************************/
7441
SetWagner(int nVariation,double dfCenterLat,double dfFalseEasting,double dfFalseNorthing)7442 OGRErr OGRSpatialReference::SetWagner( int nVariation, // 1--7.
7443 double dfCenterLat,
7444 double dfFalseEasting,
7445 double dfFalseNorthing )
7446
7447 {
7448 PJ* conv;
7449 if( nVariation == 1 )
7450 {
7451 conv = proj_create_conversion_wagner_i(
7452 d->getPROJContext(),
7453 0.0, dfFalseEasting, dfFalseNorthing,
7454 nullptr, 0.0, nullptr, 0.0);
7455 }
7456 else if( nVariation == 2 )
7457 {
7458 conv = proj_create_conversion_wagner_ii(
7459 d->getPROJContext(),
7460 0.0, dfFalseEasting, dfFalseNorthing,
7461 nullptr, 0.0, nullptr, 0.0);
7462 }
7463 else if( nVariation == 3 )
7464 {
7465 conv = proj_create_conversion_wagner_iii(
7466 d->getPROJContext(),
7467 dfCenterLat, 0.0, dfFalseEasting, dfFalseNorthing,
7468 nullptr, 0.0, nullptr, 0.0);
7469 }
7470 else if( nVariation == 4 )
7471 {
7472 conv = proj_create_conversion_wagner_iv(
7473 d->getPROJContext(),
7474 0.0, dfFalseEasting, dfFalseNorthing,
7475 nullptr, 0.0, nullptr, 0.0);
7476 }
7477 else if( nVariation == 5 )
7478 {
7479 conv = proj_create_conversion_wagner_v(
7480 d->getPROJContext(),
7481 0.0, dfFalseEasting, dfFalseNorthing,
7482 nullptr, 0.0, nullptr, 0.0);
7483 }
7484 else if( nVariation == 6 )
7485 {
7486 conv = proj_create_conversion_wagner_vi(
7487 d->getPROJContext(),
7488 0.0, dfFalseEasting, dfFalseNorthing,
7489 nullptr, 0.0, nullptr, 0.0);
7490 }
7491 else if( nVariation == 7 )
7492 {
7493 conv = proj_create_conversion_wagner_vii(
7494 d->getPROJContext(),
7495 0.0, dfFalseEasting, dfFalseNorthing,
7496 nullptr, 0.0, nullptr, 0.0);
7497 }
7498 else
7499 {
7500 CPLError( CE_Failure, CPLE_AppDefined,
7501 "Unsupported Wagner variation (%d).", nVariation );
7502 return OGRERR_UNSUPPORTED_SRS;
7503 }
7504
7505 return d->replaceConversionAndUnref(conv);
7506 }
7507
7508 /************************************************************************/
7509 /* OSRSetWagner() */
7510 /************************************************************************/
7511
OSRSetWagner(OGRSpatialReferenceH hSRS,int nVariation,double dfCenterLat,double dfFalseEasting,double dfFalseNorthing)7512 OGRErr OSRSetWagner( OGRSpatialReferenceH hSRS,
7513 int nVariation, double dfCenterLat,
7514 double dfFalseEasting,
7515 double dfFalseNorthing )
7516
7517 {
7518 VALIDATE_POINTER1( hSRS, "OSRSetWagner", OGRERR_FAILURE );
7519
7520 return ToPointer(hSRS)->SetWagner(
7521 nVariation, dfCenterLat, dfFalseEasting, dfFalseNorthing );
7522 }
7523
7524 /************************************************************************/
7525 /* SetQSC() */
7526 /************************************************************************/
7527
SetQSC(double dfCenterLat,double dfCenterLong)7528 OGRErr OGRSpatialReference::SetQSC( double dfCenterLat, double dfCenterLong )
7529 {
7530 return d->replaceConversionAndUnref(
7531 proj_create_conversion_quadrilateralized_spherical_cube(
7532 d->getPROJContext(),
7533 dfCenterLat, dfCenterLong,
7534 0.0, 0.0,
7535 nullptr, 0, nullptr, 0));
7536 }
7537
7538 /************************************************************************/
7539 /* OSRSetQSC() */
7540 /************************************************************************/
7541
OSRSetQSC(OGRSpatialReferenceH hSRS,double dfCenterLat,double dfCenterLong)7542 OGRErr OSRSetQSC( OGRSpatialReferenceH hSRS,
7543 double dfCenterLat, double dfCenterLong )
7544
7545 {
7546 VALIDATE_POINTER1( hSRS, "OSRSetQSC", OGRERR_FAILURE );
7547
7548 return ToPointer(hSRS)->SetQSC(
7549 dfCenterLat, dfCenterLong );
7550 }
7551
7552 /************************************************************************/
7553 /* SetSCH() */
7554 /************************************************************************/
7555
SetSCH(double dfPegLat,double dfPegLong,double dfPegHeading,double dfPegHgt)7556 OGRErr OGRSpatialReference::SetSCH( double dfPegLat, double dfPegLong,
7557 double dfPegHeading, double dfPegHgt)
7558
7559 {
7560 return d->replaceConversionAndUnref(
7561 proj_create_conversion_spherical_cross_track_height(
7562 d->getPROJContext(),
7563 dfPegLat, dfPegLong,
7564 dfPegHeading, dfPegHgt,
7565 nullptr, 0, nullptr, 0));
7566 }
7567
7568 /************************************************************************/
7569 /* OSRSetSCH() */
7570 /************************************************************************/
7571
OSRSetSCH(OGRSpatialReferenceH hSRS,double dfPegLat,double dfPegLong,double dfPegHeading,double dfPegHgt)7572 OGRErr OSRSetSCH( OGRSpatialReferenceH hSRS,
7573 double dfPegLat, double dfPegLong,
7574 double dfPegHeading, double dfPegHgt)
7575
7576 {
7577 VALIDATE_POINTER1( hSRS, "OSRSetSCH", OGRERR_FAILURE );
7578
7579 return ToPointer(hSRS)->SetSCH(
7580 dfPegLat, dfPegLong, dfPegHeading, dfPegHgt );
7581 }
7582
7583
7584 /************************************************************************/
7585 /* SetVerticalPerspective() */
7586 /************************************************************************/
7587
SetVerticalPerspective(double dfTopoOriginLat,double dfTopoOriginLon,double dfTopoOriginHeight,double dfViewPointHeight,double dfFalseEasting,double dfFalseNorthing)7588 OGRErr OGRSpatialReference::SetVerticalPerspective( double dfTopoOriginLat,
7589 double dfTopoOriginLon,
7590 double dfTopoOriginHeight,
7591 double dfViewPointHeight,
7592 double dfFalseEasting,
7593 double dfFalseNorthing )
7594 {
7595 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
7596 return d->replaceConversionAndUnref(
7597 proj_create_conversion_vertical_perspective(
7598 d->getPROJContext(),
7599 dfTopoOriginLat, dfTopoOriginLon,
7600 dfTopoOriginHeight, dfViewPointHeight,
7601 dfFalseEasting, dfFalseNorthing,
7602 nullptr, 0, nullptr, 0));
7603 #else
7604 CPL_IGNORE_RET_VAL(dfTopoOriginHeight); // ignored by PROJ
7605
7606 OGRSpatialReference oSRS;
7607 CPLString oProj4String;
7608 oProj4String.Printf(
7609 "+proj=nsper +lat_0=%.18g +lon_0=%.18g +h=%.18g +x_0=%.18g +y_0=%.18g",
7610 dfTopoOriginLat, dfTopoOriginLon, dfViewPointHeight,
7611 dfFalseEasting, dfFalseNorthing);
7612 oSRS.SetFromUserInput(oProj4String);
7613 return d->replaceConversionAndUnref(
7614 proj_crs_get_coordoperation(d->getPROJContext(), oSRS.d->m_pj_crs));
7615 #endif
7616 }
7617
7618 /************************************************************************/
7619 /* OSRSetVerticalPerspective() */
7620 /************************************************************************/
7621
OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,double dfTopoOriginLat,double dfTopoOriginLon,double dfTopoOriginHeight,double dfViewPointHeight,double dfFalseEasting,double dfFalseNorthing)7622 OGRErr OSRSetVerticalPerspective( OGRSpatialReferenceH hSRS,
7623 double dfTopoOriginLat,
7624 double dfTopoOriginLon,
7625 double dfTopoOriginHeight,
7626 double dfViewPointHeight,
7627 double dfFalseEasting,
7628 double dfFalseNorthing )
7629
7630 {
7631 VALIDATE_POINTER1( hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE );
7632
7633 return ToPointer(hSRS)->SetVerticalPerspective(
7634 dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight,
7635 dfViewPointHeight, dfFalseEasting, dfFalseNorthing );
7636 }
7637
7638 /************************************************************************/
7639 /* SetDerivedGeogCRSWithPoleRotationGRIBConvention() */
7640 /************************************************************************/
7641
SetDerivedGeogCRSWithPoleRotationGRIBConvention(const char * pszCRSName,double dfSouthPoleLat,double dfSouthPoleLon,double dfAxisRotation)7642 OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
7643 const char* pszCRSName,
7644 double dfSouthPoleLat,
7645 double dfSouthPoleLon,
7646 double dfAxisRotation )
7647 {
7648 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
7649 d->refreshProjObj();
7650 if( !d->m_pj_crs )
7651 return OGRERR_FAILURE;
7652 if( d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS )
7653 return OGRERR_FAILURE;
7654 auto ctxt = d->getPROJContext();
7655 auto conv = proj_create_conversion_pole_rotation_grib_convention(
7656 ctxt,
7657 dfSouthPoleLat,
7658 dfSouthPoleLon,
7659 dfAxisRotation,
7660 nullptr, 0);
7661 auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
7662 d->setPjCRS(
7663 proj_create_derived_geographic_crs(
7664 ctxt,
7665 pszCRSName,
7666 d->m_pj_crs,
7667 conv,
7668 cs));
7669 proj_destroy(conv);
7670 proj_destroy(cs);
7671 return OGRERR_NONE;
7672 #else
7673 (void)pszCRSName;
7674 SetProjection( "Rotated_pole" );
7675 SetExtension(
7676 "PROJCS", "PROJ4",
7677 CPLSPrintf("+proj=ob_tran +lon_0=%.18g +o_proj=longlat +o_lon_p=%.18g "
7678 "+o_lat_p=%.18g +a=%.18g +b=%.18g +to_meter=0.0174532925199 +wktext",
7679 dfSouthPoleLon,
7680 dfAxisRotation == 0 ? 0 : -dfAxisRotation,
7681 dfSouthPoleLat == 0 ? 0 : -dfSouthPoleLat,
7682 GetSemiMajor(nullptr),
7683 GetSemiMinor(nullptr)));
7684 return OGRERR_NONE;
7685 #endif
7686 }
7687
7688 /************************************************************************/
7689 /* SetAuthority() */
7690 /************************************************************************/
7691
7692 /**
7693 * \brief Set the authority for a node.
7694 *
7695 * This method is the same as the C function OSRSetAuthority().
7696 *
7697 * @param pszTargetKey the partial or complete path to the node to
7698 * set an authority on. i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
7699 *
7700 * @param pszAuthority authority name, such as "EPSG".
7701 *
7702 * @param nCode code for value with this authority.
7703 *
7704 * @return OGRERR_NONE on success.
7705 */
7706
SetAuthority(const char * pszTargetKey,const char * pszAuthority,int nCode)7707 OGRErr OGRSpatialReference::SetAuthority( const char *pszTargetKey,
7708 const char * pszAuthority,
7709 int nCode )
7710
7711 {
7712 d->refreshProjObj();
7713 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
7714
7715 if( pszTargetKey == nullptr )
7716 {
7717 if( !d->m_pj_crs )
7718 return OGRERR_FAILURE;
7719 CPLString osCode;
7720 osCode.Printf("%d", nCode);
7721 d->demoteFromBoundCRS();
7722 d->setPjCRS(
7723 proj_alter_id(d->getPROJContext(), d->m_pj_crs,
7724 pszAuthority, osCode.c_str()));
7725 d->undoDemoteFromBoundCRS();
7726 return OGRERR_NONE;
7727 }
7728
7729 d->demoteFromBoundCRS();
7730 if( d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS") )
7731 {
7732 CPLString osCode;
7733 osCode.Printf("%d", nCode);
7734 auto newGeogCRS = proj_alter_id(d->getPROJContext(),
7735 d->getGeodBaseCRS(),
7736 pszAuthority, osCode.c_str());
7737
7738 auto conv = proj_crs_get_coordoperation(d->getPROJContext(),
7739 d->m_pj_crs);
7740
7741 auto projCRS = proj_create_projected_crs(
7742 d->getPROJContext(),
7743 d->getProjCRSName(), newGeogCRS, conv, d->getProjCRSCoordSys());
7744
7745 // Preserve existing id on the PROJCRS
7746 const char* pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
7747 const char* pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
7748 if( pszProjCRSAuthName && pszProjCRSCode )
7749 {
7750 auto projCRSWithId = proj_alter_id(d->getPROJContext(),
7751 projCRS, pszProjCRSAuthName, pszProjCRSCode);
7752 proj_destroy(projCRS);
7753 projCRS = projCRSWithId;
7754 }
7755
7756 proj_destroy(newGeogCRS);
7757 proj_destroy(conv);
7758
7759 d->setPjCRS(projCRS);
7760 d->undoDemoteFromBoundCRS();
7761 return OGRERR_NONE;
7762 }
7763 d->undoDemoteFromBoundCRS();
7764
7765 /* -------------------------------------------------------------------- */
7766 /* Find the node below which the authority should be put. */
7767 /* -------------------------------------------------------------------- */
7768 OGR_SRSNode *poNode = GetAttrNode( pszTargetKey );
7769
7770 if( poNode == nullptr )
7771 return OGRERR_FAILURE;
7772
7773 /* -------------------------------------------------------------------- */
7774 /* If there is an existing AUTHORITY child blow it away before */
7775 /* trying to set a new one. */
7776 /* -------------------------------------------------------------------- */
7777 int iOldChild = poNode->FindChild( "AUTHORITY" );
7778 if( iOldChild != -1 )
7779 poNode->DestroyChild( iOldChild );
7780
7781 /* -------------------------------------------------------------------- */
7782 /* Create a new authority node. */
7783 /* -------------------------------------------------------------------- */
7784 char szCode[32] = {};
7785
7786 snprintf( szCode, sizeof(szCode), "%d", nCode );
7787
7788 OGR_SRSNode *poAuthNode = new OGR_SRSNode( "AUTHORITY" );
7789 poAuthNode->AddChild( new OGR_SRSNode( pszAuthority ) );
7790 poAuthNode->AddChild( new OGR_SRSNode( szCode ) );
7791
7792 poNode->AddChild( poAuthNode );
7793
7794 return OGRERR_NONE;
7795 }
7796
7797 /************************************************************************/
7798 /* OSRSetAuthority() */
7799 /************************************************************************/
7800
7801 /**
7802 * \brief Set the authority for a node.
7803 *
7804 * This function is the same as OGRSpatialReference::SetAuthority().
7805 */
OSRSetAuthority(OGRSpatialReferenceH hSRS,const char * pszTargetKey,const char * pszAuthority,int nCode)7806 OGRErr OSRSetAuthority( OGRSpatialReferenceH hSRS,
7807 const char *pszTargetKey,
7808 const char * pszAuthority,
7809 int nCode )
7810
7811 {
7812 VALIDATE_POINTER1( hSRS, "OSRSetAuthority", OGRERR_FAILURE );
7813
7814 return ToPointer(hSRS)->SetAuthority( pszTargetKey,
7815 pszAuthority,
7816 nCode );
7817 }
7818
7819 /************************************************************************/
7820 /* GetAuthorityCode() */
7821 /************************************************************************/
7822
7823 /**
7824 * \brief Get the authority code for a node.
7825 *
7826 * This method is used to query an AUTHORITY[] node from within the
7827 * WKT tree, and fetch the code value.
7828 *
7829 * While in theory values may be non-numeric, for the EPSG authority all
7830 * code values should be integral.
7831 *
7832 * This method is the same as the C function OSRGetAuthorityCode().
7833 *
7834 * @param pszTargetKey the partial or complete path to the node to
7835 * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
7836 * search for an authority node on the root element.
7837 *
7838 * @return value code from authority node, or NULL on failure. The value
7839 * returned is internal and should not be freed or modified.
7840 */
7841
7842 const char *
GetAuthorityCode(const char * pszTargetKey) const7843 OGRSpatialReference::GetAuthorityCode( const char *pszTargetKey ) const
7844
7845 {
7846 d->refreshProjObj();
7847 const char* pszInputTargetKey = pszTargetKey;
7848 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
7849 if( pszTargetKey == nullptr )
7850 {
7851 if( !d->m_pj_crs )
7852 {
7853 return nullptr;
7854 }
7855 d->demoteFromBoundCRS();
7856 auto ret = proj_get_id_code(d->m_pj_crs, 0);
7857 if( ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS )
7858 {
7859 auto ctxt = d->getPROJContext();
7860 auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
7861 if( cs )
7862 {
7863 const int axisCount = proj_cs_get_axis_count(ctxt, cs);
7864 proj_destroy(cs);
7865 if( axisCount == 3 )
7866 {
7867 // This might come from a COMPD_CS with a VERT_DATUM type = 2002
7868 // in which case, using the WKT1 representation will enable
7869 // us to recover the EPSG code.
7870 pszTargetKey = pszInputTargetKey;
7871 }
7872 }
7873 }
7874 d->undoDemoteFromBoundCRS();
7875 if( ret != nullptr || pszTargetKey == nullptr )
7876 {
7877 return ret;
7878 }
7879 }
7880
7881 /* -------------------------------------------------------------------- */
7882 /* Find the node below which the authority should be put. */
7883 /* -------------------------------------------------------------------- */
7884 const OGR_SRSNode *poNode = GetAttrNode( pszTargetKey );
7885
7886 if( poNode == nullptr )
7887 return nullptr;
7888
7889 /* -------------------------------------------------------------------- */
7890 /* Fetch AUTHORITY child if there is one. */
7891 /* -------------------------------------------------------------------- */
7892 if( poNode->FindChild("AUTHORITY") == -1 )
7893 return nullptr;
7894
7895 poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
7896
7897 /* -------------------------------------------------------------------- */
7898 /* Create a new authority node. */
7899 /* -------------------------------------------------------------------- */
7900 if( poNode->GetChildCount() < 2 )
7901 return nullptr;
7902
7903 return poNode->GetChild(1)->GetValue();
7904 }
7905
7906 /************************************************************************/
7907 /* OSRGetAuthorityCode() */
7908 /************************************************************************/
7909
7910 /**
7911 * \brief Get the authority code for a node.
7912 *
7913 * This function is the same as OGRSpatialReference::GetAuthorityCode().
7914 */
OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,const char * pszTargetKey)7915 const char *OSRGetAuthorityCode( OGRSpatialReferenceH hSRS,
7916 const char *pszTargetKey )
7917
7918 {
7919 VALIDATE_POINTER1( hSRS, "OSRGetAuthorityCode", nullptr );
7920
7921 return ToPointer(hSRS)->
7922 GetAuthorityCode( pszTargetKey );
7923 }
7924
7925 /************************************************************************/
7926 /* GetAuthorityName() */
7927 /************************************************************************/
7928
7929 /**
7930 * \brief Get the authority name for a node.
7931 *
7932 * This method is used to query an AUTHORITY[] node from within the
7933 * WKT tree, and fetch the authority name value.
7934 *
7935 * The most common authority is "EPSG".
7936 *
7937 * This method is the same as the C function OSRGetAuthorityName().
7938 *
7939 * @param pszTargetKey the partial or complete path to the node to
7940 * get an authority from. i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
7941 * search for an authority node on the root element.
7942 *
7943 * @return value code from authority node, or NULL on failure. The value
7944 * returned is internal and should not be freed or modified.
7945 */
7946
7947 const char *
GetAuthorityName(const char * pszTargetKey) const7948 OGRSpatialReference::GetAuthorityName( const char *pszTargetKey ) const
7949
7950 {
7951 d->refreshProjObj();
7952 const char* pszInputTargetKey = pszTargetKey;
7953 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
7954 if( pszTargetKey == nullptr )
7955 {
7956 if( !d->m_pj_crs )
7957 {
7958 return nullptr;
7959 }
7960 d->demoteFromBoundCRS();
7961 auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
7962 if( ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS )
7963 {
7964 auto ctxt = d->getPROJContext();
7965 auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
7966 if( cs )
7967 {
7968 const int axisCount = proj_cs_get_axis_count(ctxt, cs);
7969 proj_destroy(cs);
7970 if( axisCount == 3 )
7971 {
7972 // This might come from a COMPD_CS with a VERT_DATUM type = 2002
7973 // in which case, using the WKT1 representation will enable
7974 // us to recover the EPSG code.
7975 pszTargetKey = pszInputTargetKey;
7976 }
7977 }
7978 }
7979 d->undoDemoteFromBoundCRS();
7980 if( ret != nullptr || pszTargetKey == nullptr )
7981 {
7982 return ret;
7983 }
7984 }
7985
7986 /* -------------------------------------------------------------------- */
7987 /* Find the node below which the authority should be put. */
7988 /* -------------------------------------------------------------------- */
7989 const OGR_SRSNode *poNode = GetAttrNode( pszTargetKey );
7990
7991 if( poNode == nullptr )
7992 return nullptr;
7993
7994 /* -------------------------------------------------------------------- */
7995 /* Fetch AUTHORITY child if there is one. */
7996 /* -------------------------------------------------------------------- */
7997 if( poNode->FindChild("AUTHORITY") == -1 )
7998 return nullptr;
7999
8000 poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8001
8002 /* -------------------------------------------------------------------- */
8003 /* Create a new authority node. */
8004 /* -------------------------------------------------------------------- */
8005 if( poNode->GetChildCount() < 2 )
8006 return nullptr;
8007
8008 return poNode->GetChild(0)->GetValue();
8009 }
8010
8011 /************************************************************************/
8012 /* OSRGetAuthorityName() */
8013 /************************************************************************/
8014
8015 /**
8016 * \brief Get the authority name for a node.
8017 *
8018 * This function is the same as OGRSpatialReference::GetAuthorityName().
8019 */
OSRGetAuthorityName(OGRSpatialReferenceH hSRS,const char * pszTargetKey)8020 const char *OSRGetAuthorityName( OGRSpatialReferenceH hSRS,
8021 const char *pszTargetKey )
8022
8023 {
8024 VALIDATE_POINTER1( hSRS, "OSRGetAuthorityName", nullptr );
8025
8026 return ToPointer(hSRS)->
8027 GetAuthorityName( pszTargetKey );
8028 }
8029
8030 /************************************************************************/
8031 /* StripVertical() */
8032 /************************************************************************/
8033
8034 /**
8035 * \brief Convert a compound cs into a horizontal CS.
8036 *
8037 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8038 * nodes are stripped resulting and only the horizontal coordinate system
8039 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8040 *
8041 * If this is not a compound coordinate system then nothing is changed.
8042 *
8043 * @since OGR 1.8.0
8044 */
8045
StripVertical()8046 OGRErr OGRSpatialReference::StripVertical()
8047
8048 {
8049 d->refreshProjObj();
8050 d->demoteFromBoundCRS();
8051 if( !d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS )
8052 {
8053 d->undoDemoteFromBoundCRS();
8054 return OGRERR_NONE;
8055 }
8056 auto horizCRS =
8057 proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8058 if( !horizCRS )
8059 {
8060 d->undoDemoteFromBoundCRS();
8061 return OGRERR_FAILURE;
8062 }
8063
8064 bool reuseExistingBoundCRS = false;
8065 if( d->m_pj_bound_crs_target )
8066 {
8067 auto type = proj_get_type(d->m_pj_bound_crs_target);
8068 reuseExistingBoundCRS =
8069 type == PJ_TYPE_GEOCENTRIC_CRS ||
8070 type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8071 type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8072 }
8073
8074 if( reuseExistingBoundCRS )
8075 {
8076 auto newBoundCRS =
8077 proj_crs_create_bound_crs(
8078 d->getPROJContext(), horizCRS,
8079 d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
8080 proj_destroy(horizCRS);
8081 d->undoDemoteFromBoundCRS();
8082 d->setPjCRS(newBoundCRS);
8083 }
8084 else
8085 {
8086 d->undoDemoteFromBoundCRS();
8087 d->setPjCRS(horizCRS);
8088 }
8089
8090 return OGRERR_NONE;
8091 }
8092
8093 /************************************************************************/
8094 /* StripTOWGS84IfKnownDatumAndAllowed() */
8095 /************************************************************************/
8096
8097 /**
8098 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8099 * and this is allowed by the user.
8100 *
8101 * The default behavior is to remove TOWGS84 information if the CRS has a
8102 * known horizontal datum. This can be disabled by setting the
8103 * OSR_STRIP_TOWGS84 configuration option to NO.
8104 *
8105 * @return true if TOWGS84 has been removed.
8106 * @since OGR 3.1.0
8107 */
8108
StripTOWGS84IfKnownDatumAndAllowed()8109 bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8110 {
8111 if( CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")) )
8112 {
8113 if( StripTOWGS84IfKnownDatum() )
8114 {
8115 CPLDebug("OSR", "TOWGS84 information has been removed. "
8116 "It can be kept by setting the OSR_STRIP_TOWGS84 "
8117 "configuration option to NO");
8118 return true;
8119 }
8120 }
8121 return false;
8122 }
8123
8124 /************************************************************************/
8125 /* StripTOWGS84IfKnownDatum() */
8126 /************************************************************************/
8127
8128 /**
8129 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8130 *
8131 * @return true if TOWGS84 has been removed.
8132 * @since OGR 3.1.0
8133 */
8134
StripTOWGS84IfKnownDatum()8135 bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8136
8137 {
8138 d->refreshProjObj();
8139 if( !d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS )
8140 {
8141 return false;
8142 }
8143 auto ctxt = d->getPROJContext();
8144 auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8145 if( proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS )
8146 {
8147 proj_destroy(baseCRS);
8148 return false;
8149 }
8150
8151 // Known base CRS code ? Return base CRS
8152 const char* pszCode = proj_get_id_code(baseCRS, 0);
8153 if( pszCode )
8154 {
8155 d->setPjCRS(baseCRS);
8156 return true;
8157 }
8158
8159 auto datum = proj_crs_get_datum(ctxt, baseCRS);
8160 #if PROJ_VERSION_MAJOR > 7 || (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8161 if( datum == nullptr )
8162 {
8163 datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8164 }
8165 #endif
8166 if( !datum )
8167 {
8168 proj_destroy(baseCRS);
8169 return false;
8170 }
8171
8172 // Known datum code ? Return base CRS
8173 pszCode = proj_get_id_code(datum, 0);
8174 if( pszCode )
8175 {
8176 proj_destroy(datum);
8177 d->setPjCRS(baseCRS);
8178 return true;
8179 }
8180
8181 const char* name = proj_get_name(datum);
8182 if( EQUAL(name, "unknown") )
8183 {
8184 proj_destroy(datum);
8185 proj_destroy(baseCRS);
8186 return false;
8187 }
8188 const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8189 PJ_OBJ_LIST* list = proj_create_from_name(ctxt, nullptr,
8190 name,
8191 &type, 1,
8192 false,
8193 1,
8194 nullptr);
8195
8196 bool knownDatumName = false;
8197 if( list )
8198 {
8199 if( proj_list_get_count(list) == 1 )
8200 {
8201 knownDatumName = true;
8202 }
8203 proj_list_destroy(list);
8204 }
8205
8206 proj_destroy(datum);
8207 if( knownDatumName )
8208 {
8209 d->setPjCRS(baseCRS);
8210 return true;
8211 }
8212 proj_destroy(baseCRS);
8213 return false;
8214 }
8215
8216 /************************************************************************/
8217 /* IsCompound() */
8218 /************************************************************************/
8219
8220 /**
8221 * \brief Check if coordinate system is compound.
8222 *
8223 * This method is the same as the C function OSRIsCompound().
8224 *
8225 * @return TRUE if this is rooted with a COMPD_CS node.
8226 */
8227
IsCompound() const8228 int OGRSpatialReference::IsCompound() const
8229
8230 {
8231 d->refreshProjObj();
8232 d->demoteFromBoundCRS();
8233 bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
8234 d->undoDemoteFromBoundCRS();
8235 return isCompound;
8236 }
8237
8238 /************************************************************************/
8239 /* OSRIsCompound() */
8240 /************************************************************************/
8241
8242 /**
8243 * \brief Check if the coordinate system is compound.
8244 *
8245 * This function is the same as OGRSpatialReference::IsCompound().
8246 */
OSRIsCompound(OGRSpatialReferenceH hSRS)8247 int OSRIsCompound( OGRSpatialReferenceH hSRS )
8248
8249 {
8250 VALIDATE_POINTER1( hSRS, "OSRIsCompound", 0 );
8251
8252 return ToPointer(hSRS)->IsCompound();
8253 }
8254
8255 /************************************************************************/
8256 /* IsProjected() */
8257 /************************************************************************/
8258
8259 /**
8260 * \brief Check if projected coordinate system.
8261 *
8262 * This method is the same as the C function OSRIsProjected().
8263 *
8264 * @return TRUE if this contains a PROJCS node indicating a it is a
8265 * projected coordinate system. Also if it is a CompoundCRS made of a
8266 * ProjectedCRS
8267 */
8268
IsProjected() const8269 int OGRSpatialReference::IsProjected() const
8270
8271 {
8272 d->refreshProjObj();
8273 d->demoteFromBoundCRS();
8274 bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
8275 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
8276 {
8277 auto horizCRS =
8278 proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8279 if( horizCRS )
8280 {
8281 auto horizCRSType = proj_get_type(horizCRS);
8282 isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
8283 if( horizCRSType == PJ_TYPE_BOUND_CRS )
8284 {
8285 auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
8286 if( base )
8287 {
8288 isProjected =
8289 proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
8290 proj_destroy(base);
8291 }
8292 }
8293 proj_destroy(horizCRS);
8294 }
8295 }
8296 d->undoDemoteFromBoundCRS();
8297 return isProjected;
8298 }
8299
8300 /************************************************************************/
8301 /* OSRIsProjected() */
8302 /************************************************************************/
8303 /**
8304 * \brief Check if projected coordinate system.
8305 *
8306 * This function is the same as OGRSpatialReference::IsProjected().
8307 */
OSRIsProjected(OGRSpatialReferenceH hSRS)8308 int OSRIsProjected( OGRSpatialReferenceH hSRS )
8309
8310 {
8311 VALIDATE_POINTER1( hSRS, "OSRIsProjected", 0 );
8312
8313 return ToPointer(hSRS)->IsProjected();
8314 }
8315
8316 /************************************************************************/
8317 /* IsGeocentric() */
8318 /************************************************************************/
8319
8320 /**
8321 * \brief Check if geocentric coordinate system.
8322 *
8323 * This method is the same as the C function OSRIsGeocentric().
8324 *
8325 * @return TRUE if this contains a GEOCCS node indicating a it is a
8326 * geocentric coordinate system.
8327 *
8328 * @since OGR 1.9.0
8329 */
8330
IsGeocentric() const8331 int OGRSpatialReference::IsGeocentric() const
8332
8333 {
8334 d->refreshProjObj();
8335 d->demoteFromBoundCRS();
8336 bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
8337 d->undoDemoteFromBoundCRS();
8338 return isGeocentric;
8339 }
8340
8341 /************************************************************************/
8342 /* OSRIsGeocentric() */
8343 /************************************************************************/
8344 /**
8345 * \brief Check if geocentric coordinate system.
8346 *
8347 * This function is the same as OGRSpatialReference::IsGeocentric().
8348 *
8349 * @since OGR 1.9.0
8350 */
OSRIsGeocentric(OGRSpatialReferenceH hSRS)8351 int OSRIsGeocentric( OGRSpatialReferenceH hSRS )
8352
8353 {
8354 VALIDATE_POINTER1( hSRS, "OSRIsGeocentric", 0 );
8355
8356 return ToPointer(hSRS)->IsGeocentric();
8357 }
8358
8359 /************************************************************************/
8360 /* IsEmpty() */
8361 /************************************************************************/
8362
8363 /**
8364 * \brief Return if the SRS is not set.
8365 */
8366
IsEmpty() const8367 bool OGRSpatialReference::IsEmpty() const
8368 {
8369 d->refreshProjObj();
8370 return d->m_pj_crs == nullptr;
8371 }
8372
8373 /************************************************************************/
8374 /* IsGeographic() */
8375 /************************************************************************/
8376
8377 /**
8378 * \brief Check if geographic coordinate system.
8379 *
8380 * This method is the same as the C function OSRIsGeographic().
8381 *
8382 * @return TRUE if this spatial reference is geographic ... that is the
8383 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
8384 * GeographicCRS
8385 */
8386
IsGeographic() const8387 int OGRSpatialReference::IsGeographic() const
8388
8389 {
8390 d->refreshProjObj();
8391 d->demoteFromBoundCRS();
8392 bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8393 d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8394 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
8395 {
8396 auto horizCRS =
8397 proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8398 if( horizCRS )
8399 {
8400 auto horizCRSType = proj_get_type(horizCRS);
8401 isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8402 horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8403 if( horizCRSType == PJ_TYPE_BOUND_CRS )
8404 {
8405 auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
8406 if( base )
8407 {
8408 horizCRSType = proj_get_type(base);
8409 isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8410 horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8411 proj_destroy(base);
8412 }
8413 }
8414 proj_destroy(horizCRS);
8415 }
8416 }
8417 d->undoDemoteFromBoundCRS();
8418 return isGeog;
8419 }
8420
8421 /************************************************************************/
8422 /* OSRIsGeographic() */
8423 /************************************************************************/
8424 /**
8425 * \brief Check if geographic coordinate system.
8426 *
8427 * This function is the same as OGRSpatialReference::IsGeographic().
8428 */
OSRIsGeographic(OGRSpatialReferenceH hSRS)8429 int OSRIsGeographic( OGRSpatialReferenceH hSRS )
8430
8431 {
8432 VALIDATE_POINTER1( hSRS, "OSRIsGeographic", 0 );
8433
8434 return ToPointer(hSRS)->IsGeographic();
8435 }
8436
8437
8438 /************************************************************************/
8439 /* IsDerivedGeographic() */
8440 /************************************************************************/
8441
8442 /**
8443 * \brief Check if the CRS is a derived geographic coordinate system.
8444 * (for example a rotated long/lat grid)
8445 *
8446 * This method is the same as the C function OSRIsDerivedGeographic().
8447 *
8448 * @since GDAL 3.1.0 and PROJ 6.3.0
8449 */
8450
IsDerivedGeographic() const8451 int OGRSpatialReference::IsDerivedGeographic() const
8452
8453 {
8454 d->refreshProjObj();
8455 d->demoteFromBoundCRS();
8456 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
8457 const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8458 d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8459 const bool isDerivedGeographic = isGeog &&
8460 proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
8461 d->undoDemoteFromBoundCRS();
8462 return isDerivedGeographic ? TRUE : FALSE;
8463 #else
8464 d->undoDemoteFromBoundCRS();
8465 return FALSE;
8466 #endif
8467 }
8468
8469 /************************************************************************/
8470 /* OSRIsDerivedGeographic() */
8471 /************************************************************************/
8472 /**
8473 * \brief Check if derived geographic coordinate system.
8474 * (for example a rotated long/lat grid)
8475 *
8476 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
8477 */
OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)8478 int OSRIsDerivedGeographic( OGRSpatialReferenceH hSRS )
8479
8480 {
8481 VALIDATE_POINTER1( hSRS, "OSRIsDerivedGeographic", 0 );
8482
8483 return ToPointer(hSRS)->IsDerivedGeographic();
8484 }
8485
8486 /************************************************************************/
8487 /* IsLocal() */
8488 /************************************************************************/
8489
8490 /**
8491 * \brief Check if local coordinate system.
8492 *
8493 * This method is the same as the C function OSRIsLocal().
8494 *
8495 * @return TRUE if this spatial reference is local ... that is the
8496 * root is a LOCAL_CS node.
8497 */
8498
IsLocal() const8499 int OGRSpatialReference::IsLocal() const
8500
8501 {
8502 d->refreshProjObj();
8503 return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
8504 }
8505
8506 /************************************************************************/
8507 /* OSRIsLocal() */
8508 /************************************************************************/
8509 /**
8510 * \brief Check if local coordinate system.
8511 *
8512 * This function is the same as OGRSpatialReference::IsLocal().
8513 */
OSRIsLocal(OGRSpatialReferenceH hSRS)8514 int OSRIsLocal( OGRSpatialReferenceH hSRS )
8515
8516 {
8517 VALIDATE_POINTER1( hSRS, "OSRIsLocal", 0 );
8518
8519 return ToPointer(hSRS)->IsLocal();
8520 }
8521
8522 /************************************************************************/
8523 /* IsVertical() */
8524 /************************************************************************/
8525
8526 /**
8527 * \brief Check if vertical coordinate system.
8528 *
8529 * This method is the same as the C function OSRIsVertical().
8530 *
8531 * @return TRUE if this contains a VERT_CS node indicating a it is a
8532 * vertical coordinate system. Also if it is a CompoundCRS made of a
8533 * VerticalCRS
8534 *
8535 * @since OGR 1.8.0
8536 */
8537
IsVertical() const8538 int OGRSpatialReference::IsVertical() const
8539
8540 {
8541 d->refreshProjObj();
8542 d->demoteFromBoundCRS();
8543 bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
8544 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
8545 {
8546 auto vertCRS =
8547 proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8548 if( vertCRS )
8549 {
8550 const auto vertCRSType = proj_get_type(vertCRS);
8551 isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
8552 if( vertCRSType == PJ_TYPE_BOUND_CRS )
8553 {
8554 auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
8555 if( base )
8556 {
8557 isVertical =
8558 proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
8559 proj_destroy(base);
8560 }
8561 }
8562 proj_destroy(vertCRS);
8563 }
8564 }
8565 d->undoDemoteFromBoundCRS();
8566 return isVertical;
8567 }
8568
8569 /************************************************************************/
8570 /* OSRIsVertical() */
8571 /************************************************************************/
8572 /**
8573 * \brief Check if vertical coordinate system.
8574 *
8575 * This function is the same as OGRSpatialReference::IsVertical().
8576 *
8577 * @since OGR 1.8.0
8578 */
OSRIsVertical(OGRSpatialReferenceH hSRS)8579 int OSRIsVertical( OGRSpatialReferenceH hSRS )
8580
8581 {
8582 VALIDATE_POINTER1( hSRS, "OSRIsVertical", 0 );
8583
8584 return ToPointer(hSRS)->IsVertical();
8585 }
8586
8587 /************************************************************************/
8588 /* CloneGeogCS() */
8589 /************************************************************************/
8590
8591 /**
8592 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference object.
8593 *
8594 * @return a new SRS, which becomes the responsibility of the caller.
8595 */
CloneGeogCS() const8596 OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
8597
8598 {
8599 d->refreshProjObj();
8600 if( d->m_pj_crs )
8601 {
8602 if( d->m_pjType == PJ_TYPE_ENGINEERING_CRS )
8603 return nullptr;
8604
8605 auto geodCRS = proj_crs_get_geodetic_crs(
8606 d->getPROJContext(), d->m_pj_crs);
8607 if( geodCRS )
8608 {
8609 OGRSpatialReference * poNewSRS = new OGRSpatialReference();
8610 if( d->m_pjType == PJ_TYPE_BOUND_CRS )
8611 {
8612 PJ* hub_crs = proj_get_target_crs(
8613 d->getPROJContext(), d->m_pj_crs);
8614 PJ* co = proj_crs_get_coordoperation(
8615 d->getPROJContext(), d->m_pj_crs);
8616 auto temp = proj_crs_create_bound_crs(
8617 d->getPROJContext(), geodCRS, hub_crs, co);
8618 proj_destroy(geodCRS);
8619 geodCRS = temp;
8620 proj_destroy(hub_crs);
8621 proj_destroy(co);
8622 }
8623
8624 /* -------------------------------------------------------------------- */
8625 /* We have to reconstruct the GEOGCS node for geocentric */
8626 /* coordinate systems. */
8627 /* -------------------------------------------------------------------- */
8628 if( proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS )
8629 {
8630 auto datum = proj_crs_get_datum(
8631 d->getPROJContext(), geodCRS);
8632 #if PROJ_VERSION_MAJOR > 7 || (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8633 if( datum == nullptr )
8634 {
8635 datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
8636 }
8637 #endif
8638 if( datum )
8639 {
8640 auto cs = proj_create_ellipsoidal_2D_cs(
8641 d->getPROJContext(),PJ_ELLPS2D_LATITUDE_LONGITUDE, nullptr, 0);
8642 auto temp = proj_create_geographic_crs_from_datum(
8643 d->getPROJContext(),"unnamed", datum, cs);
8644 proj_destroy(datum);
8645 proj_destroy(cs);
8646 proj_destroy(geodCRS);
8647 geodCRS = temp;
8648 }
8649 }
8650
8651 poNewSRS->d->setPjCRS(geodCRS);
8652 if( d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER )
8653 poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
8654 return poNewSRS;
8655 }
8656 }
8657 return nullptr;
8658 }
8659
8660 /************************************************************************/
8661 /* OSRCloneGeogCS() */
8662 /************************************************************************/
8663 /**
8664 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference object.
8665 *
8666 * This function is the same as OGRSpatialReference::CloneGeogCS().
8667 */
OSRCloneGeogCS(OGRSpatialReferenceH hSource)8668 OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS( OGRSpatialReferenceH hSource )
8669
8670 {
8671 VALIDATE_POINTER1( hSource, "OSRCloneGeogCS", nullptr );
8672
8673 return ToHandle(
8674 ToPointer(hSource)->CloneGeogCS() );
8675 }
8676
8677 /************************************************************************/
8678 /* IsSameGeogCS() */
8679 /************************************************************************/
8680
8681 /**
8682 * \brief Do the GeogCS'es match?
8683 *
8684 * This method is the same as the C function OSRIsSameGeogCS().
8685 *
8686 * @param poOther the SRS being compared against.
8687 *
8688 * @return TRUE if they are the same or FALSE otherwise.
8689 */
8690
IsSameGeogCS(const OGRSpatialReference * poOther) const8691 int OGRSpatialReference::IsSameGeogCS( const OGRSpatialReference *poOther ) const
8692
8693 {
8694 return IsSameGeogCS( poOther, nullptr );
8695 }
8696
8697 /**
8698 * \brief Do the GeogCS'es match?
8699 *
8700 * This method is the same as the C function OSRIsSameGeogCS().
8701 *
8702 * @param poOther the SRS being compared against.
8703 * @param papszOptions options. ignored
8704 *
8705 * @return TRUE if they are the same or FALSE otherwise.
8706 */
8707
IsSameGeogCS(const OGRSpatialReference * poOther,const char * const * papszOptions) const8708 int OGRSpatialReference::IsSameGeogCS( const OGRSpatialReference *poOther,
8709 const char* const * papszOptions ) const
8710
8711 {
8712 CPL_IGNORE_RET_VAL(papszOptions);
8713
8714 d->refreshProjObj();
8715 poOther->d->refreshProjObj();
8716 if( !d->m_pj_crs || !poOther->d->m_pj_crs )
8717 return FALSE;
8718 if( d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
8719 d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
8720 poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
8721 poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS )
8722 {
8723 return FALSE;
8724 }
8725
8726 auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(),
8727 d->m_pj_crs);
8728 auto otherGeodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(),
8729 poOther->d->m_pj_crs);
8730 if( !geodCRS || !otherGeodCRS )
8731 {
8732 proj_destroy(geodCRS);
8733 proj_destroy(otherGeodCRS);
8734 return FALSE;
8735 }
8736
8737 int ret = proj_is_equivalent_to(geodCRS, otherGeodCRS,
8738 PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
8739
8740 proj_destroy(geodCRS);
8741 proj_destroy(otherGeodCRS);
8742 return ret;
8743 }
8744
8745 /************************************************************************/
8746 /* OSRIsSameGeogCS() */
8747 /************************************************************************/
8748
8749 /**
8750 * \brief Do the GeogCS'es match?
8751 *
8752 * This function is the same as OGRSpatialReference::IsSameGeogCS().
8753 */
OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1,OGRSpatialReferenceH hSRS2)8754 int OSRIsSameGeogCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 )
8755
8756 {
8757 VALIDATE_POINTER1( hSRS1, "OSRIsSameGeogCS", 0 );
8758 VALIDATE_POINTER1( hSRS2, "OSRIsSameGeogCS", 0 );
8759
8760 return ToPointer(hSRS1)->IsSameGeogCS(
8761 ToPointer(hSRS2) );
8762 }
8763
8764 /************************************************************************/
8765 /* IsSameVertCS() */
8766 /************************************************************************/
8767
8768 /**
8769 * \brief Do the VertCS'es match?
8770 *
8771 * This method is the same as the C function OSRIsSameVertCS().
8772 *
8773 * @param poOther the SRS being compared against.
8774 *
8775 * @return TRUE if they are the same or FALSE otherwise.
8776 */
8777
IsSameVertCS(const OGRSpatialReference * poOther) const8778 int OGRSpatialReference::IsSameVertCS( const OGRSpatialReference *poOther ) const
8779
8780 {
8781 /* -------------------------------------------------------------------- */
8782 /* Does the datum name match? */
8783 /* -------------------------------------------------------------------- */
8784 const char *pszThisValue = this->GetAttrValue( "VERT_DATUM" );
8785 const char *pszOtherValue = poOther->GetAttrValue( "VERT_DATUM" );
8786
8787 if( pszThisValue == nullptr || pszOtherValue == nullptr
8788 || !EQUAL(pszThisValue, pszOtherValue) )
8789 return FALSE;
8790
8791 /* -------------------------------------------------------------------- */
8792 /* Do the units match? */
8793 /* -------------------------------------------------------------------- */
8794 pszThisValue = this->GetAttrValue( "VERT_CS|UNIT", 1 );
8795 if( pszThisValue == nullptr )
8796 pszThisValue = "1.0";
8797
8798 pszOtherValue = poOther->GetAttrValue( "VERT_CS|UNIT", 1 );
8799 if( pszOtherValue == nullptr )
8800 pszOtherValue = "1.0";
8801
8802 if( std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001 )
8803 return FALSE;
8804
8805 return TRUE;
8806 }
8807
8808 /************************************************************************/
8809 /* OSRIsSameVertCS() */
8810 /************************************************************************/
8811
8812 /**
8813 * \brief Do the VertCS'es match?
8814 *
8815 * This function is the same as OGRSpatialReference::IsSameVertCS().
8816 */
OSRIsSameVertCS(OGRSpatialReferenceH hSRS1,OGRSpatialReferenceH hSRS2)8817 int OSRIsSameVertCS( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 )
8818
8819 {
8820 VALIDATE_POINTER1( hSRS1, "OSRIsSameVertCS", 0 );
8821 VALIDATE_POINTER1( hSRS2, "OSRIsSameVertCS", 0 );
8822
8823 return ToPointer(hSRS1)->IsSameVertCS(
8824 ToPointer(hSRS2) );
8825 }
8826
8827 /************************************************************************/
8828 /* IsSame() */
8829 /************************************************************************/
8830
8831 /**
8832 * \brief Do these two spatial references describe the same system ?
8833 *
8834 * @param poOtherSRS the SRS being compared to.
8835 *
8836 * @return TRUE if equivalent or FALSE otherwise.
8837 */
8838
IsSame(const OGRSpatialReference * poOtherSRS) const8839 int OGRSpatialReference::IsSame( const OGRSpatialReference * poOtherSRS ) const
8840
8841 {
8842 return IsSame(poOtherSRS, nullptr);
8843 }
8844
8845
8846 /**
8847 * \brief Do these two spatial references describe the same system ?
8848 *
8849 * This also takes into account the data axis to CRS axis mapping by default
8850 *
8851 * @param poOtherSRS the SRS being compared to.
8852 * @param papszOptions options. NULL or NULL terminated list of options.
8853 * Currently supported options are:
8854 * <ul>
8855 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
8856 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
8857 * Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
8858 * </ul>
8859 *
8860 * @return TRUE if equivalent or FALSE otherwise.
8861 */
8862
IsSame(const OGRSpatialReference * poOtherSRS,const char * const * papszOptions) const8863 int OGRSpatialReference::IsSame( const OGRSpatialReference * poOtherSRS,
8864 const char* const * papszOptions ) const
8865
8866 {
8867 d->refreshProjObj();
8868 poOtherSRS->d->refreshProjObj();
8869 if( !d->m_pj_crs || !poOtherSRS->d->m_pj_crs )
8870 return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
8871 if( !CPLTestBool(CSLFetchNameValueDef(papszOptions,
8872 "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")) )
8873 {
8874 if( d->m_axisMapping != poOtherSRS->d->m_axisMapping )
8875 return false;
8876 }
8877
8878 bool reboundSelf = false;
8879 bool reboundOther = false;
8880 if( d->m_pjType == PJ_TYPE_BOUND_CRS &&
8881 poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS )
8882 {
8883 d->demoteFromBoundCRS();
8884 reboundSelf = true;
8885 }
8886 else if( d->m_pjType != PJ_TYPE_BOUND_CRS &&
8887 poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS )
8888 {
8889 poOtherSRS->d->demoteFromBoundCRS();
8890 reboundOther = true;
8891 }
8892
8893 PJ_COMPARISON_CRITERION criterion =
8894 PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
8895 const char* pszCriterion = CSLFetchNameValueDef(
8896 papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
8897 if( EQUAL(pszCriterion, "STRICT") )
8898 criterion = PJ_COMP_STRICT;
8899 else if( EQUAL(pszCriterion, "EQUIVALENT") )
8900 criterion = PJ_COMP_EQUIVALENT;
8901 else if( !EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS") )
8902 {
8903 CPLError(CE_Warning, CPLE_NotSupported,
8904 "Unsupported value for CRITERION: %s", pszCriterion);
8905 }
8906 int ret = proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs,
8907 criterion);
8908 if( reboundSelf )
8909 d->undoDemoteFromBoundCRS();
8910 if( reboundOther )
8911 poOtherSRS->d->undoDemoteFromBoundCRS();
8912
8913 return ret;
8914 }
8915
8916 /************************************************************************/
8917 /* OSRIsSame() */
8918 /************************************************************************/
8919
8920 /**
8921 * \brief Do these two spatial references describe the same system ?
8922 *
8923 * This function is the same as OGRSpatialReference::IsSame().
8924 */
OSRIsSame(OGRSpatialReferenceH hSRS1,OGRSpatialReferenceH hSRS2)8925 int OSRIsSame( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2 )
8926
8927 {
8928 VALIDATE_POINTER1( hSRS1, "OSRIsSame", 0 );
8929 VALIDATE_POINTER1( hSRS2, "OSRIsSame", 0 );
8930
8931 return ToPointer(hSRS1)->IsSame(
8932 ToPointer(hSRS2) );
8933 }
8934
8935 /************************************************************************/
8936 /* OSRIsSameEx() */
8937 /************************************************************************/
8938
8939 /**
8940 * \brief Do these two spatial references describe the same system ?
8941 *
8942 * This function is the same as OGRSpatialReference::IsSame().
8943 */
OSRIsSameEx(OGRSpatialReferenceH hSRS1,OGRSpatialReferenceH hSRS2,const char * const * papszOptions)8944 int OSRIsSameEx( OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
8945 const char* const* papszOptions )
8946 {
8947 VALIDATE_POINTER1( hSRS1, "OSRIsSame", 0 );
8948 VALIDATE_POINTER1( hSRS2, "OSRIsSame", 0 );
8949
8950 return ToPointer(hSRS1)->IsSame(
8951 ToPointer(hSRS2), papszOptions );
8952 }
8953
8954 /************************************************************************/
8955 /* convertToOtherProjection() */
8956 /************************************************************************/
8957
8958 /**
8959 * \brief Convert to another equivalent projection
8960 *
8961 * Currently implemented:
8962 * <ul>
8963 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
8964 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
8965 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
8966 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
8967 * </ul>
8968 *
8969 * @param pszTargetProjection target projection.
8970 * @param papszOptions lists of options. None supported currently.
8971 * @return a new SRS, or NULL in case of error.
8972 *
8973 * @since GDAL 2.3
8974 */
convertToOtherProjection(const char * pszTargetProjection,CPL_UNUSED const char * const * papszOptions) const8975 OGRSpatialReference* OGRSpatialReference::convertToOtherProjection(
8976 const char* pszTargetProjection,
8977 CPL_UNUSED const char* const* papszOptions ) const
8978 {
8979 if( pszTargetProjection == nullptr )
8980 return nullptr;
8981 int new_code;
8982 if( EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP) )
8983 {
8984 new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
8985 }
8986 else if( EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP) )
8987 {
8988 new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
8989 }
8990 else if( EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) )
8991 {
8992 new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
8993 }
8994 else if( EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) )
8995 {
8996 new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
8997 }
8998 else
8999 {
9000 return nullptr;
9001 }
9002
9003 d->refreshProjObj();
9004 d->demoteFromBoundCRS();
9005 OGRSpatialReference* poNewSRS = nullptr;
9006 if( d->m_pjType == PJ_TYPE_PROJECTED_CRS )
9007 {
9008 auto conv = proj_crs_get_coordoperation(
9009 d->getPROJContext(), d->m_pj_crs);
9010 auto new_conv = proj_convert_conversion_to_other_method(
9011 d->getPROJContext(), conv, new_code, nullptr);
9012 proj_destroy(conv);
9013 if( new_conv )
9014 {
9015 auto geodCRS = proj_crs_get_geodetic_crs(
9016 d->getPROJContext(), d->m_pj_crs);
9017 auto cs = proj_crs_get_coordinate_system(
9018 d->getPROJContext(), d->m_pj_crs);
9019 if( geodCRS && cs )
9020 {
9021 auto new_proj_crs = proj_create_projected_crs(
9022 d->getPROJContext(),
9023 proj_get_name(d->m_pj_crs),
9024 geodCRS, new_conv, cs);
9025 proj_destroy(new_conv);
9026 if( new_proj_crs )
9027 {
9028 poNewSRS = new OGRSpatialReference();
9029
9030 if( d->m_pj_bound_crs_target && d->m_pj_bound_crs_co )
9031 {
9032 auto boundCRS = proj_crs_create_bound_crs(
9033 d->getPROJContext(),
9034 new_proj_crs, d->m_pj_bound_crs_target,
9035 d->m_pj_bound_crs_co);
9036 if( boundCRS )
9037 {
9038 proj_destroy(new_proj_crs);
9039 new_proj_crs = boundCRS;
9040 }
9041 }
9042
9043 poNewSRS->d->setPjCRS(new_proj_crs);
9044 }
9045 }
9046 proj_destroy(geodCRS);
9047 proj_destroy(cs);
9048 }
9049 }
9050 d->undoDemoteFromBoundCRS();
9051 return poNewSRS;
9052 }
9053
9054
9055 /************************************************************************/
9056 /* OSRConvertToOtherProjection() */
9057 /************************************************************************/
9058
9059 /**
9060 * \brief Convert to another equivalent projection
9061 *
9062 * Currently implemented:
9063 * <ul>
9064 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9065 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9066 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9067 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9068 * </ul>
9069 *
9070 * @param hSRS source SRS
9071 * @param pszTargetProjection target projection.
9072 * @param papszOptions lists of options. None supported currently.
9073 * @return a new SRS, or NULL in case of error.
9074 *
9075 * @since GDAL 2.3
9076 */
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,const char * pszTargetProjection,const char * const * papszOptions)9077 OGRSpatialReferenceH OSRConvertToOtherProjection(
9078 OGRSpatialReferenceH hSRS,
9079 const char* pszTargetProjection,
9080 const char* const* papszOptions )
9081 {
9082 VALIDATE_POINTER1( hSRS, "OSRConvertToOtherProjection", nullptr );
9083 return ToHandle(
9084 ToPointer(hSRS)->
9085 convertToOtherProjection(pszTargetProjection, papszOptions));
9086 }
9087
9088 /************************************************************************/
9089 /* OSRFindMatches() */
9090 /************************************************************************/
9091
9092 /**
9093 * \brief Try to identify a match between the passed SRS and a related SRS
9094 * in a catalog.
9095 *
9096 * Matching may be partial, or may fail.
9097 * Returned entries will be sorted by decreasing match confidence (first
9098 * entry has the highest match confidence).
9099 *
9100 * The exact way matching is done may change in future versions. Starting with
9101 * GDAL 3.0, it relies on PROJ' proj_identify() function.
9102 *
9103 * This function is the same as OGRSpatialReference::FindMatches().
9104 *
9105 * @param hSRS SRS to match
9106 * @param papszOptions NULL terminated list of options or NULL
9107 * @param pnEntries Output parameter. Number of values in the returned array.
9108 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
9109 * will be allocated to an array of *pnEntries whose values between 0 and 100
9110 * indicate the confidence in the match. 100 is the highest confidence level.
9111 * The array must be freed with CPLFree().
9112 *
9113 * @return an array of SRS that match the passed SRS, or NULL. Must be freed with
9114 * OSRFreeSRSArray()
9115 *
9116 * @since GDAL 2.3
9117 */
OSRFindMatches(OGRSpatialReferenceH hSRS,char ** papszOptions,int * pnEntries,int ** ppanMatchConfidence)9118 OGRSpatialReferenceH* OSRFindMatches( OGRSpatialReferenceH hSRS,
9119 char** papszOptions,
9120 int* pnEntries,
9121 int** ppanMatchConfidence )
9122 {
9123 if( pnEntries )
9124 *pnEntries = 0;
9125 if( ppanMatchConfidence )
9126 *ppanMatchConfidence = nullptr;
9127 VALIDATE_POINTER1( hSRS, "OSRFindMatches", nullptr );
9128
9129 OGRSpatialReference* poSRS = ToPointer(hSRS);
9130 return poSRS->FindMatches(papszOptions, pnEntries,
9131 ppanMatchConfidence);
9132 }
9133
9134 /************************************************************************/
9135 /* OSRFreeSRSArray() */
9136 /************************************************************************/
9137
9138 /**
9139 * \brief Free return of OSRIdentifyMatches()
9140 *
9141 * @param pahSRS array of SRS (must be NULL terminated)
9142 * @since GDAL 2.3
9143 */
OSRFreeSRSArray(OGRSpatialReferenceH * pahSRS)9144 void OSRFreeSRSArray(OGRSpatialReferenceH* pahSRS)
9145 {
9146 if( pahSRS != nullptr )
9147 {
9148 for( int i = 0; pahSRS[i] != nullptr; ++i )
9149 {
9150 OSRRelease(pahSRS[i]);
9151 }
9152 CPLFree(pahSRS);
9153 }
9154 }
9155
9156 /************************************************************************/
9157 /* SetTOWGS84() */
9158 /************************************************************************/
9159
9160
9161 /**
9162 * \brief Set the Bursa-Wolf conversion to WGS84.
9163 *
9164 * This will create the TOWGS84 node as a child of the DATUM. It will fail
9165 * if there is no existing DATUM node. It will replace
9166 * an existing TOWGS84 node if there is one.
9167 *
9168 * The parameters have the same meaning as EPSG transformation 9606
9169 * (Position Vector 7-param. transformation).
9170 *
9171 * This method is the same as the C function OSRSetTOWGS84().
9172 *
9173 * @param dfDX X child in meters.
9174 * @param dfDY Y child in meters.
9175 * @param dfDZ Z child in meters.
9176 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
9177 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
9178 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
9179 * @param dfPPM scaling factor (parts per million).
9180 *
9181 * @return OGRERR_NONE on success.
9182 */
9183
SetTOWGS84(double dfDX,double dfDY,double dfDZ,double dfEX,double dfEY,double dfEZ,double dfPPM)9184 OGRErr OGRSpatialReference::SetTOWGS84( double dfDX, double dfDY, double dfDZ,
9185 double dfEX, double dfEY, double dfEZ,
9186 double dfPPM )
9187
9188 {
9189 d->refreshProjObj();
9190 if( d->m_pj_crs == nullptr )
9191 {
9192 return OGRERR_FAILURE;
9193 }
9194
9195 // Remove existing BoundCRS
9196 if( d->m_pjType == PJ_TYPE_BOUND_CRS ) {
9197 auto baseCRS = proj_get_source_crs(
9198 d->getPROJContext(), d->m_pj_crs);
9199 if( !baseCRS )
9200 return OGRERR_FAILURE;
9201 d->setPjCRS(baseCRS);
9202 }
9203
9204 PJ_PARAM_DESCRIPTION params[7];
9205
9206 params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
9207 params[0].auth_name = "EPSG";
9208 params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
9209 params[0].value = dfDX;
9210 params[0].unit_name = "metre";
9211 params[0].unit_conv_factor = 1.0;
9212 params[0].unit_type = PJ_UT_LINEAR;
9213
9214 params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
9215 params[1].auth_name = "EPSG";
9216 params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
9217 params[1].value = dfDY;
9218 params[1].unit_name = "metre";
9219 params[1].unit_conv_factor = 1.0;
9220 params[1].unit_type = PJ_UT_LINEAR;
9221
9222 params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
9223 params[2].auth_name = "EPSG";
9224 params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
9225 params[2].value = dfDZ;
9226 params[2].unit_name = "metre";
9227 params[2].unit_conv_factor = 1.0;
9228 params[2].unit_type = PJ_UT_LINEAR;
9229
9230 params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
9231 params[3].auth_name = "EPSG";
9232 params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
9233 params[3].value = dfEX;
9234 params[3].unit_name = "arc-second";
9235 params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
9236 params[3].unit_type = PJ_UT_ANGULAR;
9237
9238 params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
9239 params[4].auth_name = "EPSG";
9240 params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
9241 params[4].value = dfEY;
9242 params[4].unit_name = "arc-second";
9243 params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
9244 params[4].unit_type = PJ_UT_ANGULAR;
9245
9246 params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
9247 params[5].auth_name = "EPSG";
9248 params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
9249 params[5].value = dfEZ;
9250 params[5].unit_name = "arc-second";
9251 params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
9252 params[5].unit_type = PJ_UT_ANGULAR;
9253
9254 params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
9255 params[6].auth_name = "EPSG";
9256 params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
9257 params[6].value = dfPPM;
9258 params[6].unit_name = "parts per million";
9259 params[6].unit_conv_factor = 1e-6;
9260 params[6].unit_type = PJ_UT_SCALE;
9261
9262 auto sourceCRS =
9263 proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9264 if( !sourceCRS )
9265 {
9266 return OGRERR_FAILURE;
9267 }
9268
9269 const auto sourceType = proj_get_type(sourceCRS);
9270
9271 auto targetCRS = proj_create_from_database(
9272 d->getPROJContext(), "EPSG",
9273 sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ? "4326":
9274 sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979": "4978",
9275 PJ_CATEGORY_CRS, false, nullptr);
9276 if( !targetCRS )
9277 {
9278 proj_destroy(sourceCRS);
9279 return OGRERR_FAILURE;
9280 }
9281
9282 CPLString osMethodCode;
9283 osMethodCode.Printf("%d",
9284 sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ?
9285 EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D:
9286 sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ?
9287 EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D:
9288 EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
9289
9290 auto transf = proj_create_transformation(
9291 d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
9292 sourceCRS, targetCRS, nullptr,
9293 sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS ?
9294 EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D:
9295 sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ?
9296 EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D:
9297 EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
9298 "EPSG",
9299 osMethodCode.c_str(),
9300 7, params, -1);
9301 proj_destroy(sourceCRS);
9302 if( !transf )
9303 {
9304 proj_destroy(targetCRS);
9305 return OGRERR_FAILURE;
9306 }
9307
9308 auto newBoundCRS =
9309 proj_crs_create_bound_crs(
9310 d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
9311 proj_destroy(transf);
9312 proj_destroy(targetCRS);
9313 if( !newBoundCRS )
9314 {
9315 return OGRERR_FAILURE;
9316 }
9317
9318 d->setPjCRS(newBoundCRS);
9319 return OGRERR_NONE;
9320 }
9321
9322 /************************************************************************/
9323 /* OSRSetTOWGS84() */
9324 /************************************************************************/
9325
9326 /**
9327 * \brief Set the Bursa-Wolf conversion to WGS84.
9328 *
9329 * This function is the same as OGRSpatialReference::SetTOWGS84().
9330 */
OSRSetTOWGS84(OGRSpatialReferenceH hSRS,double dfDX,double dfDY,double dfDZ,double dfEX,double dfEY,double dfEZ,double dfPPM)9331 OGRErr OSRSetTOWGS84( OGRSpatialReferenceH hSRS,
9332 double dfDX, double dfDY, double dfDZ,
9333 double dfEX, double dfEY, double dfEZ,
9334 double dfPPM )
9335
9336 {
9337 VALIDATE_POINTER1( hSRS, "OSRSetTOWGS84", OGRERR_FAILURE );
9338
9339 return ToPointer(hSRS)->
9340 SetTOWGS84( dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ, dfPPM );
9341 }
9342
9343 /************************************************************************/
9344 /* GetTOWGS84() */
9345 /************************************************************************/
9346
9347 /**
9348 * \brief Fetch TOWGS84 parameters, if available.
9349 *
9350 * The parameters have the same meaning as EPSG transformation 9606
9351 * (Position Vector 7-param. transformation).
9352 *
9353 * @param padfCoeff array into which up to 7 coefficients are placed.
9354 * @param nCoeffCount size of padfCoeff - defaults to 7.
9355 *
9356 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
9357 * TOWGS84 node available.
9358 */
9359
GetTOWGS84(double * padfCoeff,int nCoeffCount) const9360 OGRErr OGRSpatialReference::GetTOWGS84( double * padfCoeff,
9361 int nCoeffCount ) const
9362
9363 {
9364 d->refreshProjObj();
9365 if( d->m_pjType != PJ_TYPE_BOUND_CRS )
9366 return OGRERR_FAILURE;
9367
9368 memset( padfCoeff, 0, sizeof(double) * nCoeffCount );
9369
9370 auto transf =
9371 proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
9372 int success = proj_coordoperation_get_towgs84_values(
9373 d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
9374 proj_destroy(transf);
9375
9376 return success ? OGRERR_NONE : OGRERR_FAILURE;
9377 }
9378
9379 /************************************************************************/
9380 /* OSRGetTOWGS84() */
9381 /************************************************************************/
9382
9383 /**
9384 * \brief Fetch TOWGS84 parameters, if available.
9385 *
9386 * This function is the same as OGRSpatialReference::GetTOWGS84().
9387 */
OSRGetTOWGS84(OGRSpatialReferenceH hSRS,double * padfCoeff,int nCoeffCount)9388 OGRErr OSRGetTOWGS84( OGRSpatialReferenceH hSRS,
9389 double * padfCoeff, int nCoeffCount )
9390
9391 {
9392 VALIDATE_POINTER1( hSRS, "OSRGetTOWGS84", OGRERR_FAILURE );
9393
9394 return ToPointer(hSRS)->
9395 GetTOWGS84( padfCoeff, nCoeffCount);
9396 }
9397
9398 /************************************************************************/
9399 /* IsAngularParameter() */
9400 /************************************************************************/
9401
9402 /** Is the passed projection parameter an angular one?
9403 *
9404 * @return TRUE or FALSE
9405 */
9406
IsAngularParameter(const char * pszParameterName)9407 int OGRSpatialReference::IsAngularParameter( const char *pszParameterName )
9408
9409 {
9410 if( STARTS_WITH_CI(pszParameterName, "long")
9411 || STARTS_WITH_CI(pszParameterName, "lati")
9412 || EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN)
9413 || STARTS_WITH_CI(pszParameterName, "standard_parallel")
9414 || EQUAL(pszParameterName, SRS_PP_AZIMUTH)
9415 || EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE) )
9416 return TRUE;
9417
9418 return FALSE;
9419 }
9420
9421 /************************************************************************/
9422 /* IsLongitudeParameter() */
9423 /************************************************************************/
9424
9425 /** Is the passed projection parameter an angular longitude
9426 * (relative to a prime meridian)?
9427 *
9428 * @return TRUE or FALSE
9429 */
9430
IsLongitudeParameter(const char * pszParameterName)9431 int OGRSpatialReference::IsLongitudeParameter( const char *pszParameterName )
9432
9433 {
9434 if( STARTS_WITH_CI(pszParameterName, "long")
9435 || EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) )
9436 return TRUE;
9437
9438 return FALSE;
9439 }
9440
9441 /************************************************************************/
9442 /* IsLinearParameter() */
9443 /************************************************************************/
9444
9445 /** Is the passed projection parameter an linear one measured in meters or
9446 * some similar linear measure.
9447 *
9448 * @return TRUE or FALSE
9449 */
IsLinearParameter(const char * pszParameterName)9450 int OGRSpatialReference::IsLinearParameter( const char *pszParameterName )
9451
9452 {
9453 if( STARTS_WITH_CI(pszParameterName, "false_")
9454 || EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT) )
9455 return TRUE;
9456
9457 return FALSE;
9458 }
9459
9460 /************************************************************************/
9461 /* GetNormInfo() */
9462 /************************************************************************/
9463
9464 /**
9465 * \brief Set the internal information for normalizing linear, and angular values.
9466 */
GetNormInfo() const9467 void OGRSpatialReference::GetNormInfo() const
9468
9469 {
9470 if( d->bNormInfoSet )
9471 return;
9472
9473 /* -------------------------------------------------------------------- */
9474 /* Initialize values. */
9475 /* -------------------------------------------------------------------- */
9476 d->bNormInfoSet = TRUE;
9477
9478 d->dfFromGreenwich = GetPrimeMeridian(nullptr);
9479 d->dfToMeter = GetLinearUnits(nullptr);
9480 d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
9481 if( fabs(d->dfToDegrees-1.0) < 0.000000001 )
9482 d->dfToDegrees = 1.0;
9483 }
9484
9485 /************************************************************************/
9486 /* GetExtension() */
9487 /************************************************************************/
9488
9489 /**
9490 * \brief Fetch extension value.
9491 *
9492 * Fetch the value of the named EXTENSION item for the identified
9493 * target node.
9494 *
9495 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
9496 * @param pszName the name of the extension being fetched.
9497 * @param pszDefault the value to return if the extension is not found.
9498 *
9499 * @return node value if successful or pszDefault on failure.
9500 */
9501
GetExtension(const char * pszTargetKey,const char * pszName,const char * pszDefault) const9502 const char *OGRSpatialReference::GetExtension( const char *pszTargetKey,
9503 const char *pszName,
9504 const char *pszDefault ) const
9505
9506 {
9507 /* -------------------------------------------------------------------- */
9508 /* Find the target node. */
9509 /* -------------------------------------------------------------------- */
9510 const OGR_SRSNode *poNode = pszTargetKey == nullptr
9511 ? GetRoot()
9512 : GetAttrNode( pszTargetKey );
9513
9514 if( poNode == nullptr )
9515 return nullptr;
9516
9517 /* -------------------------------------------------------------------- */
9518 /* Fetch matching EXTENSION if there is one. */
9519 /* -------------------------------------------------------------------- */
9520 for( int i = poNode->GetChildCount()-1; i >= 0; i-- )
9521 {
9522 const OGR_SRSNode *poChild = poNode->GetChild(i);
9523
9524 if( EQUAL(poChild->GetValue(), "EXTENSION")
9525 && poChild->GetChildCount() >= 2 )
9526 {
9527 if( EQUAL(poChild->GetChild(0)->GetValue(), pszName) )
9528 return poChild->GetChild(1)->GetValue();
9529 }
9530 }
9531
9532 return pszDefault;
9533 }
9534
9535 /************************************************************************/
9536 /* SetExtension() */
9537 /************************************************************************/
9538 /**
9539 * \brief Set extension value.
9540 *
9541 * Set the value of the named EXTENSION item for the identified
9542 * target node.
9543 *
9544 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
9545 * @param pszName the name of the extension being fetched.
9546 * @param pszValue the value to set
9547 *
9548 * @return OGRERR_NONE on success
9549 */
9550
SetExtension(const char * pszTargetKey,const char * pszName,const char * pszValue)9551 OGRErr OGRSpatialReference::SetExtension( const char *pszTargetKey,
9552 const char *pszName,
9553 const char *pszValue )
9554
9555 {
9556 /* -------------------------------------------------------------------- */
9557 /* Find the target node. */
9558 /* -------------------------------------------------------------------- */
9559 OGR_SRSNode *poNode = nullptr;
9560
9561 if( pszTargetKey == nullptr )
9562 poNode = GetRoot();
9563 else
9564 poNode = GetAttrNode(pszTargetKey);
9565
9566 if( poNode == nullptr )
9567 return OGRERR_FAILURE;
9568
9569 /* -------------------------------------------------------------------- */
9570 /* Fetch matching EXTENSION if there is one. */
9571 /* -------------------------------------------------------------------- */
9572 for( int i = poNode->GetChildCount()-1; i >= 0; i-- )
9573 {
9574 OGR_SRSNode *poChild = poNode->GetChild(i);
9575
9576 if( EQUAL(poChild->GetValue(), "EXTENSION")
9577 && poChild->GetChildCount() >= 2 )
9578 {
9579 if( EQUAL(poChild->GetChild(0)->GetValue(), pszName) )
9580 {
9581 poChild->GetChild(1)->SetValue( pszValue );
9582 return OGRERR_NONE;
9583 }
9584 }
9585 }
9586
9587 /* -------------------------------------------------------------------- */
9588 /* Create a new EXTENSION node. */
9589 /* -------------------------------------------------------------------- */
9590 OGR_SRSNode *poAuthNode = new OGR_SRSNode( "EXTENSION" );
9591 poAuthNode->AddChild( new OGR_SRSNode( pszName ) );
9592 poAuthNode->AddChild( new OGR_SRSNode( pszValue ) );
9593
9594 poNode->AddChild( poAuthNode );
9595
9596 return OGRERR_NONE;
9597 }
9598
9599 /************************************************************************/
9600 /* OSRCleanup() */
9601 /************************************************************************/
9602
9603 static void CleanupSRSWGS84Mutex();
9604
9605 /**
9606 * \brief Cleanup cached SRS related memory.
9607 *
9608 * This function will attempt to cleanup any cache spatial reference
9609 * related information, such as cached tables of coordinate systems.
9610 */
OSRCleanup(void)9611 void OSRCleanup( void )
9612
9613 {
9614 OGRCTDumpStatistics();
9615 CSVDeaccess( nullptr );
9616 CleanupSRSWGS84Mutex();
9617 OSRCTCleanCache();
9618 OSRCleanupTLSContext();
9619 }
9620
9621 /************************************************************************/
9622 /* GetAxesCount() */
9623 /************************************************************************/
9624
9625 /**
9626 * \brief Return the number of axis of the coordinate system of the CRS.
9627 *
9628 * @since GDAL 3.0
9629 */
GetAxesCount() const9630 int OGRSpatialReference::GetAxesCount() const
9631 {
9632 int axisCount = 0;
9633 d->refreshProjObj();
9634 if( d->m_pj_crs == nullptr )
9635 {
9636 return 0;
9637 }
9638 d->demoteFromBoundCRS();
9639 auto ctxt = d->getPROJContext();
9640 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
9641 {
9642 for( int i = 0; ; i++ )
9643 {
9644 auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
9645 if( !subCRS )
9646 break;
9647 if( proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS )
9648 {
9649 auto baseCRS = proj_get_source_crs(ctxt, subCRS);
9650 if( baseCRS )
9651 {
9652 proj_destroy(subCRS);
9653 subCRS = baseCRS;
9654 }
9655 }
9656 auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
9657 if( cs )
9658 {
9659 axisCount += proj_cs_get_axis_count(ctxt, cs);
9660 proj_destroy(cs);
9661 }
9662 proj_destroy(subCRS);
9663 }
9664 }
9665 else
9666 {
9667 auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
9668 if( cs )
9669 {
9670 axisCount = proj_cs_get_axis_count(ctxt, cs);
9671 proj_destroy(cs);
9672 }
9673 }
9674 d->undoDemoteFromBoundCRS();
9675 return axisCount;
9676 }
9677
9678 /************************************************************************/
9679 /* OSRGetAxesCount() */
9680 /************************************************************************/
9681
9682 /**
9683 * \brief Return the number of axis of the coordinate system of the CRS.
9684 *
9685 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxesCount()
9686 *
9687 * @since GDAL 3.1
9688 */
OSRGetAxesCount(OGRSpatialReferenceH hSRS)9689 int OSRGetAxesCount( OGRSpatialReferenceH hSRS )
9690
9691 {
9692 VALIDATE_POINTER1( hSRS, "OSRGetAxesCount", 0 );
9693
9694 return ToPointer(hSRS)->GetAxesCount();
9695 }
9696
9697 /************************************************************************/
9698 /* GetAxis() */
9699 /************************************************************************/
9700
9701 /**
9702 * \brief Fetch the orientation of one axis.
9703 *
9704 * Fetches the request axis (iAxis - zero based) from the
9705 * indicated portion of the coordinate system (pszTargetKey) which
9706 * should be either "GEOGCS" or "PROJCS".
9707 *
9708 * No CPLError is issued on routine failures (such as not finding the AXIS).
9709 *
9710 * This method is equivalent to the C function OSRGetAxis().
9711 *
9712 * @param pszTargetKey the coordinate system part to query ("PROJCS" or "GEOGCS").
9713 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
9714 * @param peOrientation location into which to place the fetch orientation, may be NULL.
9715 *
9716 * @return the name of the axis or NULL on failure.
9717 */
9718
9719 const char *
GetAxis(const char * pszTargetKey,int iAxis,OGRAxisOrientation * peOrientation) const9720 OGRSpatialReference::GetAxis( const char *pszTargetKey, int iAxis,
9721 OGRAxisOrientation *peOrientation ) const
9722
9723 {
9724 if( peOrientation != nullptr )
9725 *peOrientation = OAO_Other;
9726
9727 d->refreshProjObj();
9728 if( d->m_pj_crs == nullptr )
9729 {
9730 return nullptr;
9731 }
9732
9733 pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
9734 if( pszTargetKey == nullptr && iAxis <= 2 )
9735 {
9736 auto ctxt = d->getPROJContext();
9737
9738 int iAxisModified = iAxis;
9739
9740 d->demoteFromBoundCRS();
9741
9742 PJ* cs = nullptr;
9743 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
9744 {
9745 auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9746 if( horizCRS )
9747 {
9748 if( proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS )
9749 {
9750 auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
9751 if( baseCRS )
9752 {
9753 proj_destroy(horizCRS);
9754 horizCRS = baseCRS;
9755 }
9756 }
9757 cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
9758 proj_destroy(horizCRS);
9759 if( cs )
9760 {
9761 if( iAxisModified >= proj_cs_get_axis_count(ctxt, cs) )
9762 {
9763 iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
9764 proj_destroy(cs);
9765 cs = nullptr;
9766 }
9767 }
9768 }
9769
9770 if( cs == nullptr )
9771 {
9772 auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
9773 if( vertCRS )
9774 {
9775 if( proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS )
9776 {
9777 auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
9778 if( baseCRS )
9779 {
9780 proj_destroy(vertCRS);
9781 vertCRS = baseCRS;
9782 }
9783 }
9784
9785 cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
9786 proj_destroy(vertCRS);
9787 }
9788 }
9789 }
9790 else
9791 {
9792 cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
9793 }
9794
9795 if( cs )
9796 {
9797 const char* pszName = nullptr;
9798 const char* pszOrientation = nullptr;
9799 proj_cs_get_axis_info(
9800 ctxt, cs, iAxisModified, &pszName, nullptr, &pszOrientation,
9801 nullptr, nullptr, nullptr, nullptr);
9802 if( pszName && pszOrientation )
9803 {
9804 d->m_osAxisName[iAxis] = pszName;
9805 if( peOrientation )
9806 {
9807 if( EQUAL(pszOrientation, "NORTH") )
9808 *peOrientation = OAO_North;
9809 else if( EQUAL(pszOrientation, "EAST") )
9810 *peOrientation = OAO_East;
9811 else if( EQUAL(pszOrientation, "SOUTH") )
9812 *peOrientation = OAO_South;
9813 else if( EQUAL(pszOrientation, "WEST") )
9814 *peOrientation = OAO_West;
9815 else if( EQUAL(pszOrientation, "UP") )
9816 *peOrientation = OAO_Up;
9817 else if( EQUAL(pszOrientation, "DOWN") )
9818 *peOrientation = OAO_Down;
9819 }
9820 proj_destroy(cs);
9821 d->undoDemoteFromBoundCRS();
9822 return d->m_osAxisName[iAxis].c_str();
9823 }
9824 proj_destroy(cs);
9825 }
9826 d->undoDemoteFromBoundCRS();
9827 }
9828
9829 /* -------------------------------------------------------------------- */
9830 /* Find the target node. */
9831 /* -------------------------------------------------------------------- */
9832 const OGR_SRSNode *poNode = nullptr;
9833
9834 if( pszTargetKey == nullptr )
9835 poNode = GetRoot();
9836 else
9837 poNode = GetAttrNode(pszTargetKey);
9838
9839 if( poNode == nullptr )
9840 return nullptr;
9841
9842 /* -------------------------------------------------------------------- */
9843 /* Find desired child AXIS. */
9844 /* -------------------------------------------------------------------- */
9845 const OGR_SRSNode *poAxis = nullptr;
9846 const int nChildCount = poNode->GetChildCount();
9847
9848 for( int iChild = 0; iChild < nChildCount; iChild++ )
9849 {
9850 const OGR_SRSNode *poChild = poNode->GetChild( iChild );
9851
9852 if( !EQUAL(poChild->GetValue(), "AXIS") )
9853 continue;
9854
9855 if( iAxis == 0 )
9856 {
9857 poAxis = poChild;
9858 break;
9859 }
9860 iAxis--;
9861 }
9862
9863 if( poAxis == nullptr )
9864 return nullptr;
9865
9866 if( poAxis->GetChildCount() < 2 )
9867 return nullptr;
9868
9869 /* -------------------------------------------------------------------- */
9870 /* Extract name and orientation if possible. */
9871 /* -------------------------------------------------------------------- */
9872 if( peOrientation != nullptr )
9873 {
9874 const char *pszOrientation = poAxis->GetChild(1)->GetValue();
9875
9876 if( EQUAL(pszOrientation, "NORTH") )
9877 *peOrientation = OAO_North;
9878 else if( EQUAL(pszOrientation, "EAST") )
9879 *peOrientation = OAO_East;
9880 else if( EQUAL(pszOrientation, "SOUTH") )
9881 *peOrientation = OAO_South;
9882 else if( EQUAL(pszOrientation, "WEST") )
9883 *peOrientation = OAO_West;
9884 else if( EQUAL(pszOrientation, "UP") )
9885 *peOrientation = OAO_Up;
9886 else if( EQUAL(pszOrientation, "DOWN") )
9887 *peOrientation = OAO_Down;
9888 else if( EQUAL(pszOrientation, "OTHER") )
9889 *peOrientation = OAO_Other;
9890 else
9891 {
9892 CPLDebug( "OSR", "Unrecognized orientation value '%s'.",
9893 pszOrientation );
9894 }
9895 }
9896
9897 return poAxis->GetChild(0)->GetValue();
9898 }
9899
9900 /************************************************************************/
9901 /* OSRGetAxis() */
9902 /************************************************************************/
9903
9904 /**
9905 * \brief Fetch the orientation of one axis.
9906 *
9907 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
9908 */
OSRGetAxis(OGRSpatialReferenceH hSRS,const char * pszTargetKey,int iAxis,OGRAxisOrientation * peOrientation)9909 const char *OSRGetAxis( OGRSpatialReferenceH hSRS,
9910 const char *pszTargetKey, int iAxis,
9911 OGRAxisOrientation *peOrientation )
9912
9913 {
9914 VALIDATE_POINTER1( hSRS, "OSRGetAxis", nullptr );
9915
9916 return ToPointer(hSRS)->GetAxis( pszTargetKey, iAxis,
9917 peOrientation );
9918 }
9919
9920 /************************************************************************/
9921 /* OSRAxisEnumToName() */
9922 /************************************************************************/
9923
9924 /**
9925 * \brief Return the string representation for the OGRAxisOrientation enumeration.
9926 *
9927 * For example "NORTH" for OAO_North.
9928 *
9929 * @return an internal string
9930 */
OSRAxisEnumToName(OGRAxisOrientation eOrientation)9931 const char *OSRAxisEnumToName( OGRAxisOrientation eOrientation )
9932
9933 {
9934 if( eOrientation == OAO_North )
9935 return "NORTH";
9936 if( eOrientation == OAO_East )
9937 return "EAST";
9938 if( eOrientation == OAO_South )
9939 return "SOUTH";
9940 if( eOrientation == OAO_West )
9941 return "WEST";
9942 if( eOrientation == OAO_Up )
9943 return "UP";
9944 if( eOrientation == OAO_Down )
9945 return "DOWN";
9946 if( eOrientation == OAO_Other )
9947 return "OTHER";
9948
9949 return "UNKNOWN";
9950 }
9951
9952 /************************************************************************/
9953 /* SetAxes() */
9954 /************************************************************************/
9955
9956 /**
9957 * \brief Set the axes for a coordinate system.
9958 *
9959 * Set the names, and orientations of the axes for either a projected
9960 * (PROJCS) or geographic (GEOGCS) coordinate system.
9961 *
9962 * This method is equivalent to the C function OSRSetAxes().
9963 *
9964 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
9965 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
9966 * @param eXAxisOrientation normally OAO_East.
9967 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
9968 * @param eYAxisOrientation normally OAO_North.
9969 *
9970 * @return OGRERR_NONE on success or an error code.
9971 */
9972
9973 OGRErr
SetAxes(const char * pszTargetKey,const char * pszXAxisName,OGRAxisOrientation eXAxisOrientation,const char * pszYAxisName,OGRAxisOrientation eYAxisOrientation)9974 OGRSpatialReference::SetAxes( const char *pszTargetKey,
9975 const char *pszXAxisName,
9976 OGRAxisOrientation eXAxisOrientation,
9977 const char *pszYAxisName,
9978 OGRAxisOrientation eYAxisOrientation )
9979
9980 {
9981 /* -------------------------------------------------------------------- */
9982 /* Find the target node. */
9983 /* -------------------------------------------------------------------- */
9984 OGR_SRSNode *poNode = nullptr;
9985
9986 if( pszTargetKey == nullptr )
9987 poNode = GetRoot();
9988 else
9989 poNode = GetAttrNode( pszTargetKey );
9990
9991 if( poNode == nullptr )
9992 return OGRERR_FAILURE;
9993
9994 /* -------------------------------------------------------------------- */
9995 /* Strip any existing AXIS children. */
9996 /* -------------------------------------------------------------------- */
9997 while( poNode->FindChild( "AXIS" ) >= 0 )
9998 poNode->DestroyChild( poNode->FindChild( "AXIS" ) );
9999
10000 /* -------------------------------------------------------------------- */
10001 /* Insert desired axes */
10002 /* -------------------------------------------------------------------- */
10003 OGR_SRSNode *poAxis = new OGR_SRSNode( "AXIS" );
10004
10005 poAxis->AddChild( new OGR_SRSNode( pszXAxisName ) );
10006 poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(eXAxisOrientation) ));
10007
10008 poNode->AddChild( poAxis );
10009
10010 poAxis = new OGR_SRSNode( "AXIS" );
10011
10012 poAxis->AddChild( new OGR_SRSNode( pszYAxisName ) );
10013 poAxis->AddChild( new OGR_SRSNode( OSRAxisEnumToName(eYAxisOrientation) ));
10014
10015 poNode->AddChild( poAxis );
10016
10017 return OGRERR_NONE;
10018 }
10019
10020 /************************************************************************/
10021 /* OSRSetAxes() */
10022 /************************************************************************/
10023 /**
10024 * \brief Set the axes for a coordinate system.
10025 *
10026 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
10027 */
OSRSetAxes(OGRSpatialReferenceH hSRS,const char * pszTargetKey,const char * pszXAxisName,OGRAxisOrientation eXAxisOrientation,const char * pszYAxisName,OGRAxisOrientation eYAxisOrientation)10028 OGRErr OSRSetAxes( OGRSpatialReferenceH hSRS,
10029 const char *pszTargetKey,
10030 const char *pszXAxisName,
10031 OGRAxisOrientation eXAxisOrientation,
10032 const char *pszYAxisName,
10033 OGRAxisOrientation eYAxisOrientation )
10034 {
10035 VALIDATE_POINTER1( hSRS, "OSRSetAxes", OGRERR_FAILURE );
10036
10037 return ToPointer(hSRS)->SetAxes( pszTargetKey,
10038 pszXAxisName,
10039 eXAxisOrientation,
10040 pszYAxisName,
10041 eYAxisOrientation );
10042 }
10043
10044 #ifdef HAVE_MITAB
10045 char CPL_DLL *MITABSpatialRef2CoordSys( const OGRSpatialReference * );
10046 OGRSpatialReference CPL_DLL * MITABCoordSys2SpatialRef( const char * );
10047 #endif
10048
10049 /************************************************************************/
10050 /* OSRExportToMICoordSys() */
10051 /************************************************************************/
10052 /**
10053 * \brief Export coordinate system in Mapinfo style CoordSys format.
10054 *
10055 * This method is the equivalent of the C++ method OGRSpatialReference::exportToMICoordSys
10056 */
OSRExportToMICoordSys(OGRSpatialReferenceH hSRS,char ** ppszReturn)10057 OGRErr OSRExportToMICoordSys( OGRSpatialReferenceH hSRS, char ** ppszReturn )
10058
10059 {
10060 VALIDATE_POINTER1( hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE );
10061
10062 *ppszReturn = nullptr;
10063
10064 return ToPointer(hSRS)->exportToMICoordSys( ppszReturn );
10065 }
10066
10067 /************************************************************************/
10068 /* exportToMICoordSys() */
10069 /************************************************************************/
10070
10071 /**
10072 * \brief Export coordinate system in Mapinfo style CoordSys format.
10073 *
10074 * Note that the returned WKT string should be freed with
10075 * CPLFree() when no longer needed. It is the responsibility of the caller.
10076 *
10077 * This method is the same as the C function OSRExportToMICoordSys().
10078 *
10079 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
10080 * definition will be assigned.
10081 *
10082 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
10083 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
10084 */
10085
exportToMICoordSys(char ** ppszResult) const10086 OGRErr OGRSpatialReference::exportToMICoordSys( char **ppszResult ) const
10087
10088 {
10089 #ifdef HAVE_MITAB
10090 *ppszResult = MITABSpatialRef2CoordSys( this );
10091 if( *ppszResult != nullptr && strlen(*ppszResult) > 0 )
10092 return OGRERR_NONE;
10093
10094 return OGRERR_FAILURE;
10095 #else
10096 CPLError( CE_Failure, CPLE_NotSupported,
10097 "MITAB not available, CoordSys support disabled." );
10098
10099 return OGRERR_UNSUPPORTED_OPERATION;
10100 #endif
10101 }
10102
10103 /************************************************************************/
10104 /* OSRImportFromMICoordSys() */
10105 /************************************************************************/
10106 /**
10107 * \brief Import Mapinfo style CoordSys definition.
10108 *
10109 * This method is the equivalent of the C++ method OGRSpatialReference::importFromMICoordSys
10110 */
10111
OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,const char * pszCoordSys)10112 OGRErr OSRImportFromMICoordSys( OGRSpatialReferenceH hSRS,
10113 const char *pszCoordSys )
10114
10115 {
10116 VALIDATE_POINTER1( hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE );
10117
10118 return ToPointer(hSRS)->importFromMICoordSys( pszCoordSys );
10119 }
10120
10121 /************************************************************************/
10122 /* importFromMICoordSys() */
10123 /************************************************************************/
10124
10125 /**
10126 * \brief Import Mapinfo style CoordSys definition.
10127 *
10128 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys definition string.
10129 *
10130 * This method is the equivalent of the C function OSRImportFromMICoordSys().
10131 *
10132 * @param pszCoordSys Mapinfo style CoordSys definition string.
10133 *
10134 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
10135 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
10136 */
10137
importFromMICoordSys(const char * pszCoordSys)10138 OGRErr OGRSpatialReference::importFromMICoordSys( const char *pszCoordSys )
10139
10140 {
10141 #ifdef HAVE_MITAB
10142 OGRSpatialReference *poResult = MITABCoordSys2SpatialRef( pszCoordSys );
10143
10144 if( poResult == nullptr )
10145 return OGRERR_FAILURE;
10146
10147 *this = *poResult;
10148 delete poResult;
10149
10150 return OGRERR_NONE;
10151 #else
10152 CPLError( CE_Failure, CPLE_NotSupported,
10153 "MITAB not available, CoordSys support disabled." );
10154
10155 return OGRERR_UNSUPPORTED_OPERATION;
10156 #endif
10157 }
10158
10159 /************************************************************************/
10160 /* OSRCalcInvFlattening() */
10161 /************************************************************************/
10162
10163 /**
10164 * \brief Compute inverse flattening from semi-major and semi-minor axis
10165 *
10166 * @param dfSemiMajor Semi-major axis length.
10167 * @param dfSemiMinor Semi-minor axis length.
10168 *
10169 * @return inverse flattening, or 0 if both axis are equal.
10170 * @since GDAL 2.0
10171 */
10172
OSRCalcInvFlattening(double dfSemiMajor,double dfSemiMinor)10173 double OSRCalcInvFlattening( double dfSemiMajor, double dfSemiMinor )
10174 {
10175 if( fabs(dfSemiMajor-dfSemiMinor) < 1e-1 )
10176 return 0;
10177 if( dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor )
10178 {
10179 CPLError(CE_Failure, CPLE_IllegalArg,
10180 "OSRCalcInvFlattening(): Wrong input values");
10181 return 0;
10182 }
10183
10184 return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
10185 }
10186
10187 /************************************************************************/
10188 /* OSRCalcInvFlattening() */
10189 /************************************************************************/
10190
10191 /**
10192 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
10193 *
10194 * @param dfSemiMajor Semi-major axis length.
10195 * @param dfInvFlattening Inverse flattening or 0 for sphere.
10196 *
10197 * @return semi-minor axis
10198 * @since GDAL 2.0
10199 */
10200
OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,double dfInvFlattening)10201 double OSRCalcSemiMinorFromInvFlattening( double dfSemiMajor, double dfInvFlattening )
10202 {
10203 if( fabs(dfInvFlattening) < 0.000000000001 )
10204 return dfSemiMajor;
10205 if( dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0 )
10206 {
10207 CPLError(CE_Failure, CPLE_IllegalArg,
10208 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
10209 return dfSemiMajor;
10210 }
10211
10212 return dfSemiMajor * (1.0 - 1.0/dfInvFlattening);
10213 }
10214
10215 /************************************************************************/
10216 /* GetWGS84SRS() */
10217 /************************************************************************/
10218
10219 static OGRSpatialReference* poSRSWGS84 = nullptr;
10220 static CPLMutex* hMutex = nullptr;
10221
10222 /**
10223 * \brief Returns an instance of a SRS object with WGS84 WKT.
10224 *
10225 * Note: the instance will have SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
10226 *
10227 * The reference counter of the returned object is not increased by this operation.
10228 *
10229 * @return instance.
10230 * @since GDAL 2.0
10231 */
10232
GetWGS84SRS()10233 OGRSpatialReference* OGRSpatialReference::GetWGS84SRS()
10234 {
10235 CPLMutexHolderD(&hMutex);
10236 if( poSRSWGS84 == nullptr )
10237 {
10238 poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
10239 poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
10240 }
10241 return poSRSWGS84;
10242 }
10243
10244 /************************************************************************/
10245 /* CleanupSRSWGS84Mutex() */
10246 /************************************************************************/
10247
CleanupSRSWGS84Mutex()10248 static void CleanupSRSWGS84Mutex()
10249 {
10250 if( hMutex != nullptr )
10251 {
10252 poSRSWGS84->Release();
10253 poSRSWGS84 = nullptr;
10254 CPLDestroyMutex(hMutex);
10255 hMutex = nullptr;
10256 }
10257 }
10258
10259 /************************************************************************/
10260 /* OSRImportFromProj4() */
10261 /************************************************************************/
10262 /**
10263 * \brief Import PROJ coordinate string.
10264 *
10265 * This function is the same as OGRSpatialReference::importFromProj4().
10266 */
OSRImportFromProj4(OGRSpatialReferenceH hSRS,const char * pszProj4)10267 OGRErr OSRImportFromProj4( OGRSpatialReferenceH hSRS, const char *pszProj4 )
10268
10269 {
10270 VALIDATE_POINTER1( hSRS, "OSRImportFromProj4", OGRERR_FAILURE );
10271
10272 return OGRSpatialReference::FromHandle(hSRS)->
10273 importFromProj4( pszProj4 );
10274 }
10275
10276 /************************************************************************/
10277 /* importFromProj4() */
10278 /************************************************************************/
10279
10280 /**
10281 * \brief Import PROJ coordinate string.
10282 *
10283 * The OGRSpatialReference is initialized from the passed PROJs style
10284 * coordinate system string.
10285 *
10286 * Example:
10287 * pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
10288 *
10289 * It is also possible to import "+init=epsg:n" style definitions. Those are
10290 * a legacy syntax that should be avoided in the future. In particular they will
10291 * result in CRS objects whose axis order might not correspond to the official
10292 * EPSG axis order.
10293 *
10294 * This method is the equivalent of the C function OSRImportFromProj4().
10295 *
10296 * @param pszProj4 the PROJ style string.
10297 *
10298 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
10299 */
10300
importFromProj4(const char * pszProj4)10301 OGRErr OGRSpatialReference::importFromProj4( const char * pszProj4 )
10302
10303 {
10304 if( strlen(pszProj4) >= 10000 )
10305 {
10306 CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
10307 return OGRERR_CORRUPT_DATA;
10308 }
10309
10310 /* -------------------------------------------------------------------- */
10311 /* Clear any existing definition. */
10312 /* -------------------------------------------------------------------- */
10313 Clear();
10314
10315 CPLString osProj4(pszProj4);
10316 if( osProj4.find("type=crs") == std::string::npos )
10317 {
10318 osProj4 += " +type=crs";
10319 }
10320
10321 if( osProj4.find("+init=epsg:") != std::string::npos &&
10322 getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr )
10323 {
10324 static bool bHasWarned = false;
10325 if( !bHasWarned )
10326 {
10327 CPLError(CE_Warning, CPLE_AppDefined,
10328 "+init=epsg:XXXX syntax is deprecated. It might return "
10329 "a CRS with a non-EPSG compliant axis order.");
10330 bHasWarned = true;
10331 }
10332 }
10333 proj_context_use_proj4_init_rules(d->getPROJContext(), true);
10334 d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
10335 proj_context_use_proj4_init_rules(d->getPROJContext(), false);
10336 return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
10337 }
10338
10339 /************************************************************************/
10340 /* OSRExportToProj4() */
10341 /************************************************************************/
10342 /**
10343 * \brief Export coordinate system in PROJ.4 legacy format.
10344 *
10345 * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
10346 * PROJ >= 6 is significantly different from earlier versions. In particular
10347 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
10348 * will be missing most of the time. PROJ strings to encode CRS should be
10349 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation is the
10350 * recommended way.
10351 *
10352 * This function is the same as OGRSpatialReference::exportToProj4().
10353 */
OSRExportToProj4(OGRSpatialReferenceH hSRS,char ** ppszReturn)10354 OGRErr CPL_STDCALL OSRExportToProj4( OGRSpatialReferenceH hSRS,
10355 char ** ppszReturn )
10356
10357 {
10358 VALIDATE_POINTER1( hSRS, "OSRExportToProj4", OGRERR_FAILURE );
10359
10360 *ppszReturn = nullptr;
10361
10362 return OGRSpatialReference::FromHandle(hSRS)->exportToProj4( ppszReturn );
10363 }
10364
10365 /************************************************************************/
10366 /* exportToProj4() */
10367 /************************************************************************/
10368
10369 /**
10370 * \brief Export coordinate system in PROJ.4 legacy format.
10371 *
10372 * \warning Use of this function is discouraged. Its behavior in GDAL >= 3 /
10373 * PROJ >= 6 is significantly different from earlier versions. In particular
10374 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
10375 * will be missing most of the time. PROJ strings to encode CRS should be
10376 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT representation is the
10377 * recommended way.
10378 *
10379 * Converts the loaded coordinate reference system into PROJ format
10380 * to the extent possible. The string returned in ppszProj4 should be
10381 * deallocated by the caller with CPLFree() when no longer needed.
10382 *
10383 * LOCAL_CS coordinate systems are not translatable. An empty string
10384 * will be returned along with OGRERR_NONE.
10385 *
10386 * Special processing for Transverse Mercator:
10387 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
10388 * set to YES, the PROJ definition built from the SRS will use the +approx flag
10389 * for the tmerc and utm projection methods, rather than the more accurate method.
10390 *
10391 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
10392 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
10393 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be added.
10394 * This automatic addition may be disabled by setting the
10395 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
10396 *
10397 * This method is the equivalent of the C function OSRExportToProj4().
10398 *
10399 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
10400 * will be assigned.
10401 *
10402 * @return OGRERR_NONE on success or an error code on failure.
10403 */
10404
exportToProj4(char ** ppszProj4) const10405 OGRErr OGRSpatialReference::exportToProj4( char ** ppszProj4 ) const
10406
10407 {
10408 // In the past calling this method was thread-safe, even if we never
10409 // guaranteed it. Now proj_as_proj_string() will cache the result internally,
10410 // so this is no longer thread-safe.
10411 std::lock_guard<std::mutex> oLock(d->m_mutex);
10412
10413 d->refreshProjObj();
10414 if( d->m_pj_crs == nullptr ||
10415 d->m_pjType == PJ_TYPE_ENGINEERING_CRS )
10416 {
10417 *ppszProj4 = CPLStrdup("");
10418 return OGRERR_FAILURE;
10419 }
10420
10421 // OSR_USE_ETMERC is here just for legacy
10422 bool bForceApproxTMerc = false;
10423 const char* pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
10424 if( pszUseETMERC && pszUseETMERC[0] )
10425 {
10426 static bool bHasWarned = false;
10427 if( !bHasWarned )
10428 {
10429 CPLError(CE_Warning, CPLE_AppDefined,
10430 "OSR_USE_ETMERC is a legacy configuration option, which "
10431 "now has only effect when set to NO (YES is the default). "
10432 "Use OSR_USE_APPROX_TMERC=YES instead");
10433 bHasWarned = true;
10434 }
10435 bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
10436 }
10437 else
10438 {
10439 const char* pszUseApproxTMERC = CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
10440 if( pszUseApproxTMERC && pszUseApproxTMERC[0] )
10441 {
10442 bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
10443 }
10444 }
10445 const char* options[] = {
10446 bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr,
10447 nullptr
10448 };
10449
10450 const char* projString = proj_as_proj_string(d->getPROJContext(),
10451 d->m_pj_crs, PJ_PROJ_4, options);
10452
10453 PJ* boundCRS = nullptr;
10454 if( projString &&
10455 (strstr(projString, "+datum=") == nullptr ||
10456 d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
10457 CPLTestBool(
10458 CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")) )
10459 {
10460 boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
10461 d->getPROJContext(), d->m_pj_crs, true,
10462 strstr(projString, "+datum=") == nullptr);
10463 if( boundCRS )
10464 {
10465 projString = proj_as_proj_string(d->getPROJContext(),
10466 boundCRS, PJ_PROJ_4, options);
10467 }
10468 }
10469
10470 if( projString == nullptr )
10471 {
10472 *ppszProj4 = CPLStrdup("");
10473 proj_destroy(boundCRS);
10474 return OGRERR_FAILURE;
10475 }
10476 *ppszProj4 = CPLStrdup(projString);
10477 proj_destroy(boundCRS);
10478 char* pszTypeCrs = strstr(*ppszProj4, " +type=crs");
10479 if( pszTypeCrs )
10480 *pszTypeCrs = '\0';
10481 return OGRERR_NONE;
10482 }
10483
10484 /************************************************************************/
10485 /* morphToESRI() */
10486 /************************************************************************/
10487 /**
10488 * \brief Convert in place to ESRI WKT format.
10489 *
10490 * The value nodes of this coordinate system are modified in various manners
10491 * more closely map onto the ESRI concept of WKT format. This includes
10492 * renaming a variety of projections and arguments, and stripping out
10493 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
10494 *
10495 * \note Since GDAL 3.0, this function has only user-visible effects at
10496 * exportToWkt() time. It is recommended to use instead exportToWkt(char**, const char* const char*) const
10497 * with options having FORMAT=WKT1_ESRI.
10498 *
10499 * This does the same as the C function OSRMorphToESRI().
10500 *
10501 * @return OGRERR_NONE unless something goes badly wrong.
10502 * @deprecated
10503 */
10504
morphToESRI()10505 OGRErr OGRSpatialReference::morphToESRI()
10506
10507 {
10508 d->refreshProjObj();
10509 d->setMorphToESRI(true);
10510
10511 return OGRERR_NONE;
10512 }
10513
10514 /************************************************************************/
10515 /* OSRMorphToESRI() */
10516 /************************************************************************/
10517
10518 /**
10519 * \brief Convert in place to ESRI WKT format.
10520 *
10521 * This function is the same as the C++ method
10522 * OGRSpatialReference::morphToESRI().
10523 */
OSRMorphToESRI(OGRSpatialReferenceH hSRS)10524 OGRErr OSRMorphToESRI( OGRSpatialReferenceH hSRS )
10525
10526 {
10527 VALIDATE_POINTER1( hSRS, "OSRMorphToESRI", OGRERR_FAILURE );
10528
10529 return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
10530 }
10531
10532 /************************************************************************/
10533 /* morphFromESRI() */
10534 /************************************************************************/
10535
10536 /**
10537 * \brief Convert in place from ESRI WKT format.
10538 *
10539 * The value notes of this coordinate system are modified in various manners
10540 * to adhere more closely to the WKT standard. This mostly involves
10541 * translating a variety of ESRI names for projections, arguments and
10542 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
10543 * translation of EPSG to WKT for the CT specification.
10544 *
10545 * \note Since GDAL 3.0, this function is essentially a no-operation, since
10546 * morphing from ESRI is automatically done by importFromWkt(). Its only
10547 * effect is to undo the effect of a potential prior call to morphToESRI().
10548 *
10549 * This does the same as the C function OSRMorphFromESRI().
10550 *
10551 * @return OGRERR_NONE unless something goes badly wrong.
10552 * @deprecated
10553 */
10554
morphFromESRI()10555 OGRErr OGRSpatialReference::morphFromESRI()
10556
10557 {
10558 d->refreshProjObj();
10559 d->setMorphToESRI(false);
10560
10561 return OGRERR_NONE;
10562 }
10563
10564 /************************************************************************/
10565 /* OSRMorphFromESRI() */
10566 /************************************************************************/
10567
10568 /**
10569 * \brief Convert in place from ESRI WKT format.
10570 *
10571 * This function is the same as the C++ method
10572 * OGRSpatialReference::morphFromESRI().
10573 */
OSRMorphFromESRI(OGRSpatialReferenceH hSRS)10574 OGRErr OSRMorphFromESRI( OGRSpatialReferenceH hSRS )
10575
10576 {
10577 VALIDATE_POINTER1( hSRS, "OSRMorphFromESRI", OGRERR_FAILURE );
10578
10579 return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
10580 }
10581
10582 /************************************************************************/
10583 /* FindMatches() */
10584 /************************************************************************/
10585
10586 /**
10587 * \brief Try to identify a match between the passed SRS and a related SRS
10588 * in a catalog.
10589 *
10590 * Matching may be partial, or may fail.
10591 * Returned entries will be sorted by decreasing match confidence (first
10592 * entry has the highest match confidence).
10593 *
10594 * The exact way matching is done may change in future versions. Starting with
10595 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10596 *
10597 * This method is the same as OSRFindMatches().
10598 *
10599 * @param papszOptions NULL terminated list of options or NULL
10600 * @param pnEntries Output parameter. Number of values in the returned array.
10601 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10602 * will be allocated to an array of *pnEntries whose values between 0 and 100
10603 * indicate the confidence in the match. 100 is the highest confidence level.
10604 * The array must be freed with CPLFree().
10605 *
10606 * @return an array of SRS that match the passed SRS, or NULL. Must be freed with
10607 * OSRFreeSRSArray()
10608 *
10609 * @since GDAL 2.3
10610 */
FindMatches(char ** papszOptions,int * pnEntries,int ** ppanMatchConfidence) const10611 OGRSpatialReferenceH* OGRSpatialReference::FindMatches(
10612 char** papszOptions,
10613 int* pnEntries,
10614 int** ppanMatchConfidence ) const
10615 {
10616 CPL_IGNORE_RET_VAL(papszOptions);
10617
10618 if( pnEntries )
10619 *pnEntries = 0;
10620 if( ppanMatchConfidence )
10621 *ppanMatchConfidence = nullptr;
10622
10623 d->refreshProjObj();
10624 if( !d->m_pj_crs )
10625 return nullptr;
10626
10627 int* panConfidence = nullptr;
10628 auto list = proj_identify(d->getPROJContext(),
10629 d->m_pj_crs,
10630 nullptr,
10631 nullptr,
10632 &panConfidence);
10633 if( !list )
10634 return nullptr;
10635
10636 const int nMatches = proj_list_get_count(list);
10637
10638 if( pnEntries )
10639 *pnEntries = static_cast<int>(nMatches);
10640 OGRSpatialReferenceH* pahRet =
10641 static_cast<OGRSpatialReferenceH*>(
10642 CPLCalloc(sizeof(OGRSpatialReferenceH),
10643 nMatches + 1));
10644 if( ppanMatchConfidence )
10645 {
10646 *ppanMatchConfidence = static_cast<int*>(
10647 CPLMalloc(sizeof(int) * (nMatches + 1)));
10648 }
10649 for(int i=0; i<nMatches; i++)
10650 {
10651 PJ* obj = proj_list_get(d->getPROJContext(), list, i);
10652 CPLAssert(obj);
10653 OGRSpatialReference* poSRS = new OGRSpatialReference();
10654 poSRS->d->setPjCRS(obj);
10655 pahRet[i] = ToHandle(poSRS);
10656 if( ppanMatchConfidence )
10657 (*ppanMatchConfidence)[i] = panConfidence[i];
10658 }
10659 pahRet[ nMatches ] = nullptr;
10660 proj_list_destroy(list);
10661 proj_int_list_destroy(panConfidence);
10662
10663 return pahRet;
10664 }
10665
10666 /************************************************************************/
10667 /* importFromEPSGA() */
10668 /************************************************************************/
10669
10670 /**
10671 * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS code.
10672 *
10673 * This method will initialize the spatial reference based on the
10674 * passed in EPSG CRS code found in the PROJ database.
10675 *
10676 * Since GDAL 3.0, this method is identical to importFromEPSG().
10677 *
10678 * Before GDAL 3.0.3, this method try to attach a 3-parameter or 7-parameter
10679 * Helmert transformation to WGS84 when there is one and only one such method
10680 * available for the CRS.
10681 * This behavior might not always be desirable, so starting with GDAL 3.0.3,
10682 * this is no longer done. However the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG
10683 * configuration option can be set to YES to enable past behavior.
10684 * The AddGuessedTOWGS84() method can also be used for that purpose.
10685 *
10686 * This method is the same as the C function OSRImportFromEPSGA().
10687 *
10688 * @param nCode a CRS code.
10689 *
10690 * @return OGRERR_NONE on success, or an error code on failure.
10691 */
10692
importFromEPSGA(int nCode)10693 OGRErr OGRSpatialReference::importFromEPSGA( int nCode )
10694
10695 {
10696 Clear();
10697
10698 const bool bUseNonDeprecated = CPLTestBool(
10699 CPLGetConfigOption("OSR_USE_NON_DEPRECATED", "YES"));
10700 const bool bAddTOWGS84 = CPLTestBool(
10701 CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
10702 auto tlsCache = OSRGetProjTLSCache();
10703 if( tlsCache )
10704 {
10705 auto cachedObj = tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
10706 if( cachedObj )
10707 {
10708 d->setPjCRS(cachedObj);
10709 return OGRERR_NONE;
10710 }
10711 }
10712
10713 CPLString osCode;
10714 osCode.Printf("%d", nCode);
10715 auto obj = proj_create_from_database(d->getPROJContext(),
10716 "EPSG",
10717 osCode.c_str(),
10718 PJ_CATEGORY_CRS,
10719 true,
10720 nullptr);
10721 if( !obj )
10722 {
10723 return OGRERR_FAILURE;
10724 }
10725
10726 if( proj_is_deprecated(obj) ) {
10727 auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
10728 if( list && bUseNonDeprecated ) {
10729 const auto count = proj_list_get_count(list);
10730 if( count == 1 ) {
10731 auto nonDeprecated =
10732 proj_list_get(d->getPROJContext(), list, 0);
10733 if( nonDeprecated ) {
10734 proj_destroy(obj);
10735 obj = nonDeprecated;
10736 }
10737 }
10738 }
10739 proj_list_destroy(list);
10740 }
10741
10742 if( bAddTOWGS84 )
10743 {
10744 auto boundCRS = proj_crs_create_bound_crs_to_WGS84(
10745 d->getPROJContext(), obj, nullptr);
10746 if( boundCRS )
10747 {
10748 proj_destroy(obj);
10749 obj = boundCRS;
10750 }
10751 }
10752
10753 d->setPjCRS(obj);
10754
10755 if( tlsCache )
10756 {
10757 tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84, obj);
10758 }
10759
10760 return OGRERR_NONE;
10761 }
10762
10763 /************************************************************************/
10764 /* AddGuessedTOWGS84() */
10765 /************************************************************************/
10766
10767 /**
10768 * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
10769 * to WGS84.
10770 *
10771 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
10772 * to WGS84 when there is one and only one such method available for the CRS.
10773 * Note: this is more restrictive to how GDAL < 3 worked.
10774 *
10775 * This method is the same as the C function OSRAddGuessedTOWGS84().
10776 *
10777 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
10778 * already a transformation to WGS84 or none matching could be found).
10779 *
10780 * @since GDAL 3.0.3
10781 */
AddGuessedTOWGS84()10782 OGRErr OGRSpatialReference::AddGuessedTOWGS84()
10783 {
10784 d->refreshProjObj();
10785 if( !d->m_pj_crs )
10786 return OGRERR_FAILURE;
10787 auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
10788 d->getPROJContext(), d->m_pj_crs, false, true);
10789 if( !boundCRS )
10790 {
10791 return OGRERR_FAILURE;
10792 }
10793 d->setPjCRS(boundCRS);
10794 return OGRERR_NONE;
10795 }
10796
10797 /************************************************************************/
10798 /* OSRImportFromEPSGA() */
10799 /************************************************************************/
10800
10801 /**
10802 * \brief Try to add a a 3-parameter or 7-parameter Helmert transformation
10803 * to WGS84.
10804 *
10805 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
10806 *
10807 * @since GDAL 3.0.3
10808 */
10809
OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)10810 OGRErr OSRAddGuessedTOWGS84( OGRSpatialReferenceH hSRS)
10811
10812 {
10813 VALIDATE_POINTER1( hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE );
10814
10815 return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
10816 }
10817
10818 /************************************************************************/
10819 /* OSRImportFromEPSGA() */
10820 /************************************************************************/
10821
10822 /**
10823 * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS code.
10824 *
10825 * This function is the same as OGRSpatialReference::importFromEPSGA().
10826 */
10827
OSRImportFromEPSGA(OGRSpatialReferenceH hSRS,int nCode)10828 OGRErr CPL_STDCALL OSRImportFromEPSGA( OGRSpatialReferenceH hSRS, int nCode )
10829
10830 {
10831 VALIDATE_POINTER1( hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE );
10832
10833 return OGRSpatialReference::FromHandle(hSRS)->
10834 importFromEPSGA( nCode );
10835 }
10836
10837 /************************************************************************/
10838 /* importFromEPSG() */
10839 /************************************************************************/
10840
10841 /**
10842 * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS code.
10843 *
10844 * This method will initialize the spatial reference based on the
10845 * passed in EPSG CRS code found in the PROJ database.
10846 *
10847 * This method is the same as the C function OSRImportFromEPSG().
10848 *
10849 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
10850 * to WGS84 when there is one and only one such method available for the CRS.
10851 * This behavior might not always be desirable, so starting with GDAL 3.0.3,
10852 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option can be set to
10853 * NO to disable this behavior.
10854 *
10855 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
10856 *
10857 * @return OGRERR_NONE on success, or an error code on failure.
10858 */
10859
importFromEPSG(int nCode)10860 OGRErr OGRSpatialReference::importFromEPSG( int nCode )
10861
10862 {
10863 return importFromEPSGA( nCode );
10864 }
10865
10866 /************************************************************************/
10867 /* OSRImportFromEPSG() */
10868 /************************************************************************/
10869
10870 /**
10871 * \brief Initialize SRS based on EPSG geographic, projected or vertical CRS code.
10872 *
10873 * This function is the same as OGRSpatialReference::importFromEPSG().
10874 */
10875
OSRImportFromEPSG(OGRSpatialReferenceH hSRS,int nCode)10876 OGRErr CPL_STDCALL OSRImportFromEPSG( OGRSpatialReferenceH hSRS, int nCode )
10877
10878 {
10879 VALIDATE_POINTER1( hSRS, "OSRImportFromEPSG", OGRERR_FAILURE );
10880
10881 return OGRSpatialReference::FromHandle(hSRS)->
10882 importFromEPSG( nCode );
10883 }
10884
10885 /************************************************************************/
10886 /* EPSGTreatsAsLatLong() */
10887 /************************************************************************/
10888
10889 /**
10890 * \brief This method returns TRUE if EPSG feels this geographic coordinate
10891 * system should be treated as having lat/long coordinate ordering.
10892 *
10893 * Currently this returns TRUE for all geographic coordinate systems
10894 * with an EPSG code set, and axes set defining it as lat, long.
10895 *
10896 * \note Important change of behavior since GDAL 3.0. In previous versions,
10897 * geographic CRS imported with importFromEPSG() would cause this method to
10898 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
10899 * is now equivalent to importFromEPSGA().
10900 *
10901 * FALSE will be returned for all coordinate systems that are not geographic,
10902 * or that do not have an EPSG code set.
10903 *
10904 * This method is the same as the C function OSREPSGTreatsAsLatLong().
10905 *
10906 * @return TRUE or FALSE.
10907 */
10908
EPSGTreatsAsLatLong() const10909 int OGRSpatialReference::EPSGTreatsAsLatLong() const
10910
10911 {
10912 if( !IsGeographic() )
10913 return FALSE;
10914
10915 d->demoteFromBoundCRS();
10916 const char* pszAuth = proj_get_id_auth_name(d->m_pj_crs, 0);
10917 if( pszAuth == nullptr || !EQUAL(pszAuth, "EPSG") )
10918 {
10919 d->undoDemoteFromBoundCRS();
10920 return FALSE;
10921 }
10922
10923 bool ret = false;
10924 if ( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
10925 {
10926 auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(),
10927 d->m_pj_crs, 0);
10928 if ( horizCRS )
10929 {
10930 auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10931 horizCRS);
10932 if ( cs )
10933 {
10934 const char* pszDirection = nullptr;
10935 if( proj_cs_get_axis_info(
10936 d->getPROJContext(), cs, 0, nullptr, nullptr,
10937 &pszDirection, nullptr, nullptr, nullptr, nullptr) )
10938 {
10939 if( EQUAL(pszDirection, "north") )
10940 {
10941 ret = true;
10942 }
10943 }
10944
10945 proj_destroy(cs);
10946 }
10947
10948 proj_destroy(horizCRS);
10949 }
10950 }
10951 else
10952 {
10953 auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10954 d->m_pj_crs);
10955 if ( cs )
10956 {
10957 const char* pszDirection = nullptr;
10958 if( proj_cs_get_axis_info(
10959 d->getPROJContext(), cs, 0, nullptr, nullptr, &pszDirection,
10960 nullptr, nullptr, nullptr, nullptr) )
10961 {
10962 if( EQUAL(pszDirection, "north") )
10963 {
10964 ret = true;
10965 }
10966 }
10967
10968 proj_destroy(cs);
10969 }
10970 }
10971 d->undoDemoteFromBoundCRS();
10972
10973 return ret;
10974 }
10975
10976 /************************************************************************/
10977 /* OSREPSGTreatsAsLatLong() */
10978 /************************************************************************/
10979
10980 /**
10981 * \brief This function returns TRUE if EPSG feels this geographic coordinate
10982 * system should be treated as having lat/long coordinate ordering.
10983 *
10984 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
10985 */
10986
OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)10987 int OSREPSGTreatsAsLatLong( OGRSpatialReferenceH hSRS )
10988
10989 {
10990 VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE );
10991
10992 return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
10993 }
10994
10995 /************************************************************************/
10996 /* EPSGTreatsAsNorthingEasting() */
10997 /************************************************************************/
10998
10999 /**
11000 * \brief This method returns TRUE if EPSG feels this projected coordinate
11001 * system should be treated as having northing/easting coordinate ordering.
11002 *
11003 * Currently this returns TRUE for all projected coordinate systems
11004 * with an EPSG code set, and axes set defining it as northing, easting.
11005 *
11006 * \note Important change of behavior since GDAL 3.0. In previous versions,
11007 * projected CRS with northing, easting axis order imported with
11008 * importFromEPSG() would cause this method to
11009 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
11010 * is now equivalent to importFromEPSGA().
11011 *
11012 * FALSE will be returned for all coordinate systems that are not projected,
11013 * or that do not have an EPSG code set.
11014 *
11015 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
11016 *
11017 * @return TRUE or FALSE.
11018 *
11019 * @since OGR 1.10.0
11020 */
11021
EPSGTreatsAsNorthingEasting() const11022 int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
11023
11024 {
11025 if( !IsProjected() )
11026 return FALSE;
11027
11028 d->demoteFromBoundCRS();
11029 PJ* projCRS;
11030 const auto ctxt = d->getPROJContext();
11031 if( d->m_pjType == PJ_TYPE_COMPOUND_CRS )
11032 {
11033 projCRS = proj_crs_get_sub_crs(
11034 ctxt, d->m_pj_crs, 1);
11035 if( !projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS )
11036 {
11037 d->undoDemoteFromBoundCRS();
11038 proj_destroy(projCRS);
11039 return FALSE;
11040 }
11041 }
11042 else
11043 {
11044 projCRS = proj_clone(ctxt, d->m_pj_crs);
11045 }
11046 const char* pszAuth = proj_get_id_auth_name(projCRS, 0);
11047 if( pszAuth == nullptr || !EQUAL(pszAuth, "EPSG") )
11048 {
11049 d->undoDemoteFromBoundCRS();
11050 proj_destroy(projCRS);
11051 return FALSE;
11052 }
11053
11054 bool ret = false;
11055 auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
11056 proj_destroy(projCRS);
11057 d->undoDemoteFromBoundCRS();
11058
11059 if( cs )
11060 {
11061 ret = isNorthEastAxisOrder(ctxt, cs);
11062 proj_destroy(cs);
11063 }
11064
11065 return ret;
11066 }
11067
11068 /************************************************************************/
11069 /* OSREPSGTreatsAsNorthingEasting() */
11070 /************************************************************************/
11071
11072 /**
11073 * \brief This function returns TRUE if EPSG feels this projected coordinate
11074 * system should be treated as having northing/easting coordinate ordering.
11075 *
11076 * This function is the same as
11077 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
11078 *
11079 * @since OGR 1.10.0
11080 */
11081
OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)11082 int OSREPSGTreatsAsNorthingEasting( OGRSpatialReferenceH hSRS )
11083
11084 {
11085 VALIDATE_POINTER1( hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE );
11086
11087 return OGRSpatialReference::FromHandle(hSRS)->
11088 EPSGTreatsAsNorthingEasting();
11089 }
11090
11091 /************************************************************************/
11092 /* ImportFromESRIWisconsinWKT() */
11093 /* */
11094 /* Search a ESRI State Plane WKT and import it. */
11095 /************************************************************************/
11096
11097 // This is only used by the HFA driver and somewhat dubious we really need that
11098 // Coming from an old ESRI merge
11099
ImportFromESRIWisconsinWKT(const char * prjName,double centralMeridian,double latOfOrigin,const char * unitsName,const char * crsName)11100 OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(
11101 const char* prjName, double centralMeridian, double latOfOrigin,
11102 const char* unitsName, const char* crsName )
11103 {
11104 if( centralMeridian < -93 || centralMeridian > -87 )
11105 return OGRERR_FAILURE;
11106 if( latOfOrigin < 40 || latOfOrigin > 47 )
11107 return OGRERR_FAILURE;
11108
11109 // If the CS name is known.
11110 if( !prjName && !unitsName && crsName )
11111 {
11112 const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
11113 PJ_OBJ_LIST* list = proj_create_from_name(
11114 d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
11115 if( list )
11116 {
11117 if( proj_list_get_count(list) == 1 )
11118 {
11119 auto crs = proj_list_get(d->getPROJContext(), list, 0);
11120 if( crs )
11121 {
11122 Clear();
11123 d->setPjCRS(crs);
11124 proj_list_destroy(list);
11125 return OGRERR_NONE;
11126 }
11127 }
11128 proj_list_destroy(list);
11129 }
11130 return OGRERR_FAILURE;
11131 }
11132
11133 if( prjName == nullptr || unitsName == nullptr )
11134 {
11135 return OGRERR_FAILURE;
11136 }
11137
11138 const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
11139 PJ_OBJ_LIST* list = proj_create_from_name(
11140 d->getPROJContext(), "ESRI", "NAD_1983_HARN_WISCRS_", &type, 1, true,
11141 0, nullptr);
11142 if( list )
11143 {
11144 const auto listSize = proj_list_get_count(list);
11145 for( int i = 0; i < listSize; i++ )
11146 {
11147 auto crs = proj_list_get(d->getPROJContext(), list, i);
11148 if( !crs )
11149 {
11150 continue;
11151 }
11152
11153 auto conv = proj_crs_get_coordoperation(
11154 d->getPROJContext(), crs);
11155 if( !conv )
11156 {
11157 proj_destroy(crs);
11158 continue;
11159 }
11160 const char* pszMethodCode = nullptr;
11161 proj_coordoperation_get_method_info(
11162 d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
11163 const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
11164 if( !((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
11165 nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
11166 (EQUAL(prjName, "Lambert_Conformal_Conic") &&
11167 nMethodCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) )
11168 {
11169 proj_destroy(crs);
11170 proj_destroy(conv);
11171 continue;
11172 }
11173
11174 auto coordSys = proj_crs_get_coordinate_system(
11175 d->getPROJContext(), crs);
11176 if( !coordSys )
11177 {
11178 proj_destroy(crs);
11179 proj_destroy(conv);
11180 continue;
11181 }
11182
11183 double dfConvFactor = 0.0;
11184 proj_cs_get_axis_info(
11185 d->getPROJContext(), coordSys, 0, nullptr, nullptr, nullptr,
11186 &dfConvFactor, nullptr, nullptr, nullptr);
11187 proj_destroy(coordSys);
11188
11189 if( (EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
11190 (!EQUAL(unitsName, "meters") &&
11191 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) > 1e-10 ) )
11192 {
11193 proj_destroy(crs);
11194 proj_destroy(conv);
11195 continue;
11196 }
11197
11198 int idx_lat = proj_coordoperation_get_param_index(
11199 d->getPROJContext(), conv,
11200 EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
11201 double valueLat = -1000;
11202 proj_coordoperation_get_param(
11203 d->getPROJContext(), conv, idx_lat,
11204 nullptr, nullptr, nullptr,
11205 &valueLat, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
11206 int idx_lon = proj_coordoperation_get_param_index(
11207 d->getPROJContext(), conv,
11208 EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
11209 double valueLong = -1000;
11210 proj_coordoperation_get_param(
11211 d->getPROJContext(), conv, idx_lon,
11212 nullptr, nullptr, nullptr,
11213 &valueLong, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
11214 if( std::fabs(centralMeridian - valueLong) <= 1e-10 &&
11215 std::fabs(latOfOrigin - valueLat) <= 1e-10 )
11216 {
11217 Clear();
11218 d->setPjCRS(crs);
11219 proj_list_destroy(list);
11220 proj_destroy(conv);
11221 return OGRERR_NONE;
11222 }
11223
11224 proj_destroy(crs);
11225 proj_destroy(conv);
11226 }
11227 proj_list_destroy(list);
11228 }
11229
11230 return OGRERR_FAILURE;
11231 }
11232
11233 /************************************************************************/
11234 /* GetAxisMappingStrategy() */
11235 /************************************************************************/
11236
11237 /** \brief Return the data axis to CRS axis mapping strategy.
11238 *
11239 * <ul>
11240 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
11241 * lat/long order, the data will still be long/lat ordered. Similarly for
11242 * a projected CRS with northing/easting order, the data will still be
11243 * easting/northing ordered.
11244 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
11245 * the CRS axis.
11246 * <li>OAMS_CUSTOM means that the data axis are customly defined with
11247 * SetDataAxisToSRSAxisMapping()
11248 * </ul>
11249 * @return the the data axis to CRS axis mapping strategy.
11250 * @since GDAL 3.0
11251 */
GetAxisMappingStrategy() const11252 OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
11253 {
11254 return d->m_axisMappingStrategy;
11255 }
11256
11257 /************************************************************************/
11258 /* OSRGetAxisMappingStrategy() */
11259 /************************************************************************/
11260
11261 /** \brief Return the data axis to CRS axis mapping strategy.
11262 *
11263 * See OGRSpatialReference::GetAxisMappingStrategy()
11264 * @since GDAL 3.0
11265 */
OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)11266 OSRAxisMappingStrategy OSRGetAxisMappingStrategy( OGRSpatialReferenceH hSRS )
11267 {
11268 VALIDATE_POINTER1( hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM );
11269
11270 return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
11271 }
11272
11273 /************************************************************************/
11274 /* SetAxisMappingStrategy() */
11275 /************************************************************************/
11276
11277 /** \brief Set the data axis to CRS axis mapping strategy.
11278 *
11279 * See OGRSpatialReference::GetAxisMappingStrategy()
11280 * @since GDAL 3.0
11281 */
SetAxisMappingStrategy(OSRAxisMappingStrategy strategy)11282 void OGRSpatialReference::SetAxisMappingStrategy(OSRAxisMappingStrategy strategy)
11283 {
11284 d->m_axisMappingStrategy = strategy;
11285 d->refreshAxisMapping();
11286 }
11287
11288 /************************************************************************/
11289 /* OSRSetAxisMappingStrategy() */
11290 /************************************************************************/
11291
11292 /** \brief Set the data axis to CRS axis mapping strategy.
11293 *
11294 * See OGRSpatialReference::SetAxisMappingStrategy()
11295 * @since GDAL 3.0
11296 */
OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,OSRAxisMappingStrategy strategy)11297 void OSRSetAxisMappingStrategy( OGRSpatialReferenceH hSRS,
11298 OSRAxisMappingStrategy strategy )
11299 {
11300 VALIDATE_POINTER0( hSRS, "OSRSetAxisMappingStrategy" );
11301
11302 OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
11303 }
11304
11305 /************************************************************************/
11306 /* GetDataAxisToSRSAxisMapping() */
11307 /************************************************************************/
11308
11309 /** \brief Return the data axis to SRS axis mapping.
11310 *
11311 * The number of elements of the vector will be the number of axis of the CRS.
11312 * Values start at 1.
11313 *
11314 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
11315 * for the first axis of the CRS.
11316 *
11317 * @since GDAL 3.0
11318 */
GetDataAxisToSRSAxisMapping() const11319 const std::vector<int>& OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
11320 {
11321 return d->m_axisMapping;
11322 }
11323
11324 /************************************************************************/
11325 /* OSRGetDataAxisToSRSAxisMapping() */
11326 /************************************************************************/
11327
11328 /** \brief Return the data axis to SRS axis mapping.
11329 *
11330 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
11331 *
11332 * @since GDAL 3.0
11333 */
OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,int * pnCount)11334 const int *OSRGetDataAxisToSRSAxisMapping( OGRSpatialReferenceH hSRS, int* pnCount )
11335 {
11336 VALIDATE_POINTER1( hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr );
11337 VALIDATE_POINTER1( pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr );
11338
11339 const auto& v =
11340 OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
11341 *pnCount = static_cast<int>(v.size());
11342 return v.data();
11343 }
11344
11345 /************************************************************************/
11346 /* SetDataAxisToSRSAxisMapping() */
11347 /************************************************************************/
11348
11349 /** \brief Set a custom data axis to CRS axis mapping.
11350 *
11351 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
11352 *
11353 * See OGRSpatialReference::GetAxisMappingStrategy()
11354 * @since GDAL 3.0
11355 */
SetDataAxisToSRSAxisMapping(const std::vector<int> & mapping)11356 OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(const std::vector<int>& mapping)
11357 {
11358 if( mapping.size() < 2 )
11359 return OGRERR_FAILURE;
11360 d->m_axisMappingStrategy = OAMS_CUSTOM;
11361 d->m_axisMapping = mapping;
11362 return OGRERR_NONE;
11363 }
11364
11365 /************************************************************************/
11366 /* OSRSetDataAxisToSRSAxisMapping() */
11367 /************************************************************************/
11368
11369 /** \brief Set a custom data axis to CRS axis mapping.
11370 *s
11371 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
11372 *
11373 * See OGRSpatialReference::SetDataAxisToSRSAxisMapping()
11374 *
11375 * @since GDAL 3.1
11376 */
OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,int nMappingSize,const int * panMapping)11377 OGRErr OSRSetDataAxisToSRSAxisMapping( OGRSpatialReferenceH hSRS,
11378 int nMappingSize,
11379 const int* panMapping )
11380 {
11381 VALIDATE_POINTER1( hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE );
11382 VALIDATE_POINTER1( panMapping, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE );
11383
11384 if( nMappingSize < 0 )
11385 return OGRERR_FAILURE;
11386
11387 std::vector<int> mapping(nMappingSize);
11388 if( nMappingSize )
11389 memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
11390 return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(mapping);
11391 }
11392
11393 /************************************************************************/
11394 /* GetAreaOfUse() */
11395 /************************************************************************/
11396
11397 /** \brief Return the area of use of the CRS.
11398 *
11399 * This method is the same as the OSRGetAreaOfUse() function.
11400 *
11401 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
11402 * longitude, expressed in degree. Might be NULL. If the returned value is -1000,
11403 * the bounding box is unknown.
11404 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
11405 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
11406 * the bounding box is unknown.
11407 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
11408 * longitude, expressed in degree. Might be NULL. If the returned value is -1000,
11409 * the bounding box is unknown.
11410 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
11411 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
11412 * the bounding box is unknown.
11413 * @param ppszAreaName Pointer to a string to receive the name of the area of
11414 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be invalidated
11415 * by further calls.
11416 * @return true in case of success
11417 * @since GDAL 3.0
11418 */
GetAreaOfUse(double * pdfWestLongitudeDeg,double * pdfSouthLatitudeDeg,double * pdfEastLongitudeDeg,double * pdfNorthLatitudeDeg,const char ** ppszAreaName) const11419 bool OGRSpatialReference::GetAreaOfUse( double* pdfWestLongitudeDeg,
11420 double* pdfSouthLatitudeDeg,
11421 double* pdfEastLongitudeDeg,
11422 double* pdfNorthLatitudeDeg,
11423 const char **ppszAreaName ) const
11424 {
11425 d->refreshProjObj();
11426 if( !d->m_pj_crs )
11427 {
11428 return false;
11429 }
11430 d->demoteFromBoundCRS();
11431 const char* pszAreaName = nullptr;
11432 int bSuccess = proj_get_area_of_use(
11433 d->getPROJContext(),
11434 d->m_pj_crs,
11435 pdfWestLongitudeDeg,
11436 pdfSouthLatitudeDeg,
11437 pdfEastLongitudeDeg,
11438 pdfNorthLatitudeDeg,
11439 &pszAreaName);
11440 d->undoDemoteFromBoundCRS();
11441 d->m_osAreaName = pszAreaName ? pszAreaName : "";
11442 if( ppszAreaName )
11443 *ppszAreaName = d->m_osAreaName.c_str();
11444 return CPL_TO_BOOL(bSuccess);
11445 }
11446
11447 /************************************************************************/
11448 /* GetAreaOfUse() */
11449 /************************************************************************/
11450
11451 /** \brief Return the area of use of the CRS.
11452 *
11453 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
11454 *
11455 * @since GDAL 3.0
11456 */
OSRGetAreaOfUse(OGRSpatialReferenceH hSRS,double * pdfWestLongitudeDeg,double * pdfSouthLatitudeDeg,double * pdfEastLongitudeDeg,double * pdfNorthLatitudeDeg,const char ** ppszAreaName)11457 int OSRGetAreaOfUse( OGRSpatialReferenceH hSRS,
11458 double* pdfWestLongitudeDeg,
11459 double* pdfSouthLatitudeDeg,
11460 double* pdfEastLongitudeDeg,
11461 double* pdfNorthLatitudeDeg,
11462 const char **ppszAreaName )
11463 {
11464 VALIDATE_POINTER1( hSRS, "OSRGetAreaOfUse", FALSE );
11465
11466 return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
11467 pdfWestLongitudeDeg, pdfSouthLatitudeDeg,
11468 pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
11469 ppszAreaName);
11470 }
11471
11472 /************************************************************************/
11473 /* OSRGetCRSInfoListFromDatabase() */
11474 /************************************************************************/
11475
11476 /** \brief Enumerate CRS objects from the database.
11477 *
11478 * The returned object is an array of OSRCRSInfo* pointers, whose last
11479 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
11480 *
11481 * @param pszAuthName Authority name, used to restrict the search.
11482 * Or NULL for all authorities.
11483 * @param params Additional criteria. Must be set to NULL for now.
11484 * @param pnOutResultCount Output parameter pointing to an integer to receive
11485 * the size of the result list. Might be NULL
11486 * @return an array of OSRCRSInfo* pointers to be freed with
11487 * OSRDestroyCRSInfoList(), or NULL in case of error.
11488 *
11489 * @since GDAL 3.0
11490 */
OSRGetCRSInfoListFromDatabase(const char * pszAuthName,CPL_UNUSED const OSRCRSListParameters * params,int * pnOutResultCount)11491 OSRCRSInfo **OSRGetCRSInfoListFromDatabase(
11492 const char *pszAuthName,
11493 CPL_UNUSED const OSRCRSListParameters* params,
11494 int *pnOutResultCount)
11495 {
11496 int nResultCount = 0;
11497 auto projList = proj_get_crs_info_list_from_database(OSRGetProjTLSContext(),
11498 pszAuthName,
11499 nullptr,
11500 &nResultCount);
11501 if( pnOutResultCount )
11502 *pnOutResultCount = nResultCount;
11503 if( !projList )
11504 {
11505 return nullptr;
11506 }
11507 auto res = new OSRCRSInfo*[nResultCount + 1];
11508 for( int i = 0; i < nResultCount; i++ )
11509 {
11510 res[i] = new OSRCRSInfo;
11511 res[i]->pszAuthName = projList[i]->auth_name ?
11512 CPLStrdup(projList[i]->auth_name) : nullptr;
11513 res[i]->pszCode = projList[i]->code ?
11514 CPLStrdup(projList[i]->code) : nullptr;
11515 res[i]->pszName = projList[i]->name ?
11516 CPLStrdup(projList[i]->name) : nullptr;
11517 res[i]->eType = OSR_CRS_TYPE_OTHER;
11518 switch( projList[i]->type )
11519 {
11520 case PJ_TYPE_GEOGRAPHIC_2D_CRS:
11521 res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D; break;
11522 case PJ_TYPE_GEOGRAPHIC_3D_CRS:
11523 res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D; break;
11524 case PJ_TYPE_GEOCENTRIC_CRS:
11525 res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC; break;
11526 case PJ_TYPE_PROJECTED_CRS:
11527 res[i]->eType = OSR_CRS_TYPE_PROJECTED; break;
11528 case PJ_TYPE_VERTICAL_CRS:
11529 res[i]->eType = OSR_CRS_TYPE_VERTICAL; break;
11530 case PJ_TYPE_COMPOUND_CRS:
11531 res[i]->eType = OSR_CRS_TYPE_COMPOUND; break;
11532 default:
11533 break;
11534 }
11535 res[i]->bDeprecated = projList[i]->deprecated;
11536 res[i]->bBboxValid = projList[i]->bbox_valid;
11537 res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
11538 res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
11539 res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
11540 res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
11541 res[i]->pszAreaName = projList[i]->area_name ?
11542 CPLStrdup(projList[i]->area_name) : nullptr;
11543 res[i]->pszProjectionMethod = projList[i]->projection_method_name ?
11544 CPLStrdup(projList[i]->projection_method_name) : nullptr;
11545 }
11546 res[nResultCount] = nullptr;
11547 proj_crs_info_list_destroy(projList);
11548 return res;
11549 }
11550
11551 /************************************************************************/
11552 /* OSRDestroyCRSInfoList() */
11553 /************************************************************************/
11554
11555
11556 /** \brief Destroy the result returned by
11557 * OSRGetCRSInfoListFromDatabase().
11558 *
11559 * @since GDAL 3.0
11560 */
OSRDestroyCRSInfoList(OSRCRSInfo ** list)11561 void OSRDestroyCRSInfoList(OSRCRSInfo** list)
11562 {
11563 if (list)
11564 {
11565 for (int i = 0; list[i] != nullptr; i++)
11566 {
11567 CPLFree(list[i]->pszAuthName);
11568 CPLFree(list[i]->pszCode);
11569 CPLFree(list[i]->pszName);
11570 CPLFree(list[i]->pszAreaName);
11571 CPLFree(list[i]->pszProjectionMethod);
11572 delete list[i];
11573 }
11574 delete[] list;
11575 }
11576 }
11577
11578 /************************************************************************/
11579 /* UpdateCoordinateSystemFromGeogCRS() */
11580 /************************************************************************/
11581
11582 /*! @cond Doxygen_Suppress */
11583 /** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
11584 *
11585 * @since GDAL 3.1
11586 */
UpdateCoordinateSystemFromGeogCRS()11587 void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
11588 {
11589 d->refreshProjObj();
11590 if( !d->m_pj_crs )
11591 return;
11592 if( d->m_pjType != PJ_TYPE_PROJECTED_CRS )
11593 return;
11594 if( GetAxesCount() == 3 )
11595 return;
11596 auto ctxt = d->getPROJContext();
11597 auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
11598 if( !baseCRS )
11599 return;
11600 auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
11601 if( !baseCRSCS )
11602 {
11603 proj_destroy(baseCRS);
11604 return;
11605 }
11606 if( proj_cs_get_axis_count(ctxt, baseCRSCS) != 3 )
11607 {
11608 proj_destroy(baseCRSCS);
11609 proj_destroy(baseCRS);
11610 return;
11611 }
11612 auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11613 if( !projCS || proj_cs_get_axis_count(ctxt, projCS) != 2 )
11614 {
11615 proj_destroy(baseCRSCS);
11616 proj_destroy(baseCRS);
11617 proj_destroy(projCS);
11618 return;
11619 }
11620
11621 PJ_AXIS_DESCRIPTION axis[3];
11622 for( int i = 0; i < 3; i++ )
11623 {
11624 const char* name = nullptr;
11625 const char* abbreviation = nullptr;
11626 const char* direction = nullptr;
11627 double unit_conv_factor = 0;
11628 const char* unit_name = nullptr;
11629 proj_cs_get_axis_info(ctxt,
11630 i < 2 ? projCS : baseCRSCS,
11631 i,
11632 &name,
11633 &abbreviation,
11634 &direction,
11635 &unit_conv_factor,
11636 &unit_name, nullptr, nullptr);
11637 axis[i].name = CPLStrdup(name);
11638 axis[i].abbreviation = CPLStrdup(abbreviation);
11639 axis[i].direction = CPLStrdup(direction);
11640 axis[i].unit_name = CPLStrdup(unit_name);
11641 axis[i].unit_conv_factor = unit_conv_factor;
11642 axis[i].unit_type = PJ_UT_LINEAR;
11643 }
11644 proj_destroy(baseCRSCS);
11645 proj_destroy(projCS);
11646 auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
11647 for( int i = 0; i < 3; i++ )
11648 {
11649 CPLFree(axis[i].name);
11650 CPLFree(axis[i].abbreviation);
11651 CPLFree(axis[i].direction);
11652 CPLFree(axis[i].unit_name);
11653 }
11654 if( !cs )
11655 {
11656 proj_destroy(baseCRS);
11657 return;
11658 }
11659 auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
11660 auto crs = proj_create_projected_crs(ctxt,
11661 d->getProjCRSName(),
11662 baseCRS,
11663 conversion,
11664 cs);
11665 proj_destroy(baseCRS);
11666 proj_destroy(conversion);
11667 proj_destroy(cs);
11668 d->setPjCRS(crs);
11669 }
11670
11671 /*! @endcond */
11672
11673 /************************************************************************/
11674 /* PromoteTo3D() */
11675 /************************************************************************/
11676
11677 /** \brief "Promotes" a 2D CRS to a 3D CRS one.
11678 *
11679 * The new axis will be ellipsoidal height, oriented upwards, and with metre
11680 * units.
11681 *
11682 * @param pszName New name for the CRS. If set to NULL, the previous name will be used.
11683 * @return OGRERR_NONE if no error occurred.
11684 * @since GDAL 3.1 and PROJ 6.3
11685 */
PromoteTo3D(const char * pszName)11686 OGRErr OGRSpatialReference::PromoteTo3D(const char* pszName)
11687 {
11688 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
11689 d->refreshProjObj();
11690 if( !d->m_pj_crs )
11691 return OGRERR_FAILURE;
11692 auto newPj = proj_crs_promote_to_3D( d->getPROJContext(), pszName, d->m_pj_crs );
11693 if( !newPj )
11694 return OGRERR_FAILURE;
11695 d->setPjCRS(newPj);
11696 return OGRERR_NONE;
11697 #else
11698 CPL_IGNORE_RET_VAL(pszName);
11699 CPLError(CE_Failure, CPLE_NotSupported, "PROJ 6.3 required");
11700 return OGRERR_UNSUPPORTED_OPERATION;
11701 #endif
11702 }
11703
11704 /************************************************************************/
11705 /* OSRPromoteTo3D() */
11706 /************************************************************************/
11707
11708 /** \brief "Promotes" a 2D CRS to a 3D CRS one.
11709 *
11710 * See OGRSpatialReference::PromoteTo3D()
11711 *
11712 * @since GDAL 3.1 and PROJ 6.3
11713 */
OSRPromoteTo3D(OGRSpatialReferenceH hSRS,const char * pszName)11714 OGRErr OSRPromoteTo3D( OGRSpatialReferenceH hSRS, const char* pszName )
11715 {
11716 VALIDATE_POINTER1( hSRS, "OSRPromoteTo3D", OGRERR_FAILURE );
11717
11718 return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
11719 }
11720
11721 /************************************************************************/
11722 /* DemoteTo2D() */
11723 /************************************************************************/
11724
11725 /** \brief "Demote" a 3D CRS to a 2D CRS one.
11726 *
11727 * @param pszName New name for the CRS. If set to NULL, the previous name will be used.
11728 * @return OGRERR_NONE if no error occurred.
11729 * @since GDAL 3.2 and PROJ 6.3
11730 */
DemoteTo2D(const char * pszName)11731 OGRErr OGRSpatialReference::DemoteTo2D(const char* pszName)
11732 {
11733 #if PROJ_VERSION_MAJOR > 6 || PROJ_VERSION_MINOR >= 3
11734 d->refreshProjObj();
11735 if( !d->m_pj_crs )
11736 return OGRERR_FAILURE;
11737 auto newPj = proj_crs_demote_to_2D( d->getPROJContext(), pszName, d->m_pj_crs );
11738 if( !newPj )
11739 return OGRERR_FAILURE;
11740 d->setPjCRS(newPj);
11741 return OGRERR_NONE;
11742 #else
11743 CPL_IGNORE_RET_VAL(pszName);
11744 CPLError(CE_Failure, CPLE_NotSupported, "PROJ 6.3 required");
11745 return OGRERR_UNSUPPORTED_OPERATION;
11746 #endif
11747 }
11748
11749 /************************************************************************/
11750 /* OSRDemoteTo2D() */
11751 /************************************************************************/
11752
11753 /** \brief "Demote" a 3D CRS to a 2D CRS one.
11754 *
11755 * See OGRSpatialReference::DemoteTo2D()
11756 *
11757 * @since GDAL 3.2 and PROJ 6.3
11758 */
OSRDemoteTo2D(OGRSpatialReferenceH hSRS,const char * pszName)11759 OGRErr OSRDemoteTo2D( OGRSpatialReferenceH hSRS, const char* pszName )
11760 {
11761 VALIDATE_POINTER1( hSRS, "OSRDemoteTo2D", OGRERR_FAILURE );
11762
11763 return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
11764 }
11765
11766 /************************************************************************/
11767 /* GetEPSGGeogCS() */
11768 /************************************************************************/
11769
11770 /** Try to establish what the EPSG code for this coordinate systems
11771 * GEOGCS might be. Returns -1 if no reasonable guess can be made.
11772 *
11773 * @return EPSG code
11774 */
11775
GetEPSGGeogCS() const11776 int OGRSpatialReference::GetEPSGGeogCS() const
11777
11778 {
11779 /* -------------------------------------------------------------------- */
11780 /* Check axis order. */
11781 /* -------------------------------------------------------------------- */
11782 auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
11783 if( !poGeogCRS )
11784 return -1;
11785
11786 bool ret = false;
11787 poGeogCRS->d->demoteFromBoundCRS();
11788 auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
11789 poGeogCRS->d->m_pj_crs);
11790 poGeogCRS->d->undoDemoteFromBoundCRS();
11791 if( cs )
11792 {
11793 const char* pszDirection = nullptr;
11794 if( proj_cs_get_axis_info(
11795 d->getPROJContext(), cs, 0, nullptr, nullptr, &pszDirection,
11796 nullptr, nullptr, nullptr, nullptr) )
11797 {
11798 if( EQUAL(pszDirection, "north") )
11799 {
11800 ret = true;
11801 }
11802 }
11803
11804 proj_destroy(cs);
11805 }
11806 if( !ret )
11807 return -1;
11808
11809 /* -------------------------------------------------------------------- */
11810 /* Do we already have it? */
11811 /* -------------------------------------------------------------------- */
11812 const char *pszAuthName = GetAuthorityName( "GEOGCS" );
11813 if( pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") )
11814 return atoi(GetAuthorityCode( "GEOGCS" ));
11815
11816 /* -------------------------------------------------------------------- */
11817 /* Get the datum and geogcs names. */
11818 /* -------------------------------------------------------------------- */
11819
11820 const char *pszGEOGCS = GetAttrValue( "GEOGCS" );
11821 const char *pszDatum = GetAttrValue( "DATUM" );
11822
11823 // We can only operate on coordinate systems with a geogcs.
11824 OGRSpatialReference oSRSTmp;
11825 if( pszGEOGCS == nullptr || pszDatum == nullptr )
11826 {
11827 // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
11828 // export to WKT1, so try to extract the geographic CRS through PROJ
11829 // API with CopyGeogCSFrom() and get the nodes' values from it.
11830 oSRSTmp.CopyGeogCSFrom(this);
11831 pszGEOGCS = oSRSTmp.GetAttrValue( "GEOGCS" );
11832 pszDatum = oSRSTmp.GetAttrValue( "DATUM" );
11833 if( pszGEOGCS == nullptr || pszDatum == nullptr )
11834 {
11835 return -1;
11836 }
11837 }
11838
11839 // Lookup geographic CRS name
11840 const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
11841 PJ_OBJ_LIST* list = proj_create_from_name(d->getPROJContext(), nullptr,
11842 pszGEOGCS,
11843 &type, 1,
11844 false,
11845 1,
11846 nullptr);
11847 if( list )
11848 {
11849 const auto listSize = proj_list_get_count(list);
11850 if( listSize == 1 )
11851 {
11852 auto crs = proj_list_get(d->getPROJContext(), list, 0);
11853 if( crs )
11854 {
11855 pszAuthName = proj_get_id_auth_name(crs, 0);
11856 const char* pszCode = proj_get_id_code(crs, 0);
11857 if( pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG") )
11858 {
11859 const int nCode = atoi(pszCode);
11860 proj_destroy(crs);
11861 proj_list_destroy(list);
11862 return nCode;
11863 }
11864 proj_destroy(crs);
11865 }
11866 }
11867 proj_list_destroy(list);
11868 }
11869
11870 /* -------------------------------------------------------------------- */
11871 /* Is this a "well known" geographic coordinate system? */
11872 /* -------------------------------------------------------------------- */
11873 const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr
11874 || strstr(pszDatum, "WGS")
11875 || strstr(pszGEOGCS, "World Geodetic System")
11876 || strstr(pszGEOGCS, "World_Geodetic_System")
11877 || strstr(pszDatum, "World Geodetic System")
11878 || strstr(pszDatum, "World_Geodetic_System");
11879
11880 const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr
11881 || strstr(pszDatum, "NAD")
11882 || strstr(pszGEOGCS, "North American")
11883 || strstr(pszGEOGCS, "North_American")
11884 || strstr(pszDatum, "North American")
11885 || strstr(pszDatum, "North_American");
11886
11887 if( bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")) )
11888 return 4326;
11889
11890 if( bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")) )
11891 return 4322;
11892
11893 // This is questionable as there are several 'flavors' of NAD83 that
11894 // are not the same as 4269
11895 if( bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")) )
11896 return 4269;
11897
11898 if( bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")) )
11899 return 4267;
11900
11901 /* -------------------------------------------------------------------- */
11902 /* If we know the datum, associate the most likely GCS with */
11903 /* it. */
11904 /* -------------------------------------------------------------------- */
11905 const OGRSpatialReference& oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
11906 pszAuthName = oActiveObj.GetAuthorityName( "GEOGCS|DATUM" );
11907 if( pszAuthName != nullptr
11908 && EQUAL(pszAuthName, "epsg")
11909 && GetPrimeMeridian() == 0.0 )
11910 {
11911 const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
11912
11913 if( nDatum >= 6000 && nDatum <= 6999 )
11914 return nDatum - 2000;
11915 }
11916
11917 return -1;
11918 }
11919