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 &gt;= 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 &gt;= 3 /
10346  * PROJ &gt;= 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 &gt;= 3 /
10373  * PROJ &gt;= 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