diff options
45 files changed, 1382 insertions, 481 deletions
diff --git a/data/sql/commit.sql b/data/sql/commit.sql index 0468032d..4b17ea2b 100644 --- a/data/sql/commit.sql +++ b/data/sql/commit.sql @@ -36,6 +36,9 @@ FOR EACH ROW BEGIN lower(g1.grid_name) = lower(g2.grid_name) AND g1.auth_name = 'PROJ' AND g2.auth_name = 'EPSG'); + SELECT RAISE(ABORT, 'Arg! there is now a EPSG:102100 object. Hack in createFromUserInput() will no longer work') + WHERE EXISTS(SELECT 1 FROM crs_view WHERE auth_name == 'EPSG' AND code = '102100'); + -- check geoid_model table SELECT RAISE(ABORT, 'missing GEOID99 in geoid_model') WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID99'); diff --git a/data/sql/grid_alternatives.sql b/data/sql/grid_alternatives.sql index 1d06de9e..ee8760db 100644 --- a/data/sql/grid_alternatives.sql +++ b/data/sql/grid_alternatives.sql @@ -10,32 +10,32 @@ INSERT INTO grid_packages VALUES ('proj-datumgrid', 'Package with grids of general interest', - 'https://download.osgeo.org/proj/proj-datumgrid-latest.zip', + 'https://download.osgeo.org/proj/proj-datumgrid-1.8.zip', 1, 1); INSERT INTO grid_packages VALUES ('proj-datumgrid-north-america', 'Package with grids of interest for North-America', - 'https://download.osgeo.org/proj/proj-datumgrid-north-america-latest.zip', + 'https://download.osgeo.org/proj/proj-datumgrid-north-america-1.3.zip', 1, 1); INSERT INTO grid_packages VALUES ('proj-datumgrid-europe', 'Package with grids of interest for Europe', - 'https://download.osgeo.org/proj/proj-datumgrid-europe-latest.zip', + 'https://download.osgeo.org/proj/proj-datumgrid-europe-1.5.zip', 1, 1); INSERT INTO grid_packages VALUES ('proj-datumgrid-oceania', 'Package with grids of interest for Oceania', - 'https://download.osgeo.org/proj/proj-datumgrid-oceania-latest.zip', + 'https://download.osgeo.org/proj/proj-datumgrid-oceania-1.1.zip', 1, 1); -- not released yet at the time of writing INSERT INTO grid_packages VALUES ('proj-datumgrid-world', 'Package with grids of global extent (too large to be included in proj-datumgrid)', - 'https://download.osgeo.org/proj/proj-datumgrid-world-latest.zip', + 'https://download.osgeo.org/proj/proj-datumgrid-world-1.0.zip', 1, 1); @@ -1106,6 +1106,22 @@ INSERT INTO grid_alternatives(original_grid_name, 'proj-datumgrid-oceania', NULL, NULL, NULL, NULL); +INSERT INTO grid_alternatives(original_grid_name, + proj_grid_name, + proj_grid_format, + proj_method, + inverse_direction, + package_name, + url, direct_download, open_license, directory) + SELECT grid_name, + 'AUSGeoid98.gtx', + 'GTX', + 'vgridshift', + 1, + 'proj-datumgrid-oceania', + NULL, NULL, NULL, NULL FROM grid_transformation WHERE + grid_name LIKE '%DAT.htm' AND name LIKE 'GDA94 to AHD height%'; + -- Netherlands / RDNAP (non-free grids) INSERT INTO grid_alternatives(original_grid_name, @@ -1560,3 +1576,34 @@ INSERT INTO grid_alternatives(original_grid_name, 0, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + -- Iceland + +INSERT INTO grid_alternatives(original_grid_name, + proj_grid_name, + proj_grid_format, + proj_method, + inverse_direction, + package_name, + url, direct_download, open_license, directory) + VALUES ('ISN93_ISN2016.gsb', + 'ISN93_ISN2016.gsb', + 'NTv2', + 'hgridshift', + 0, + 'proj-datumgrid-europe', + NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, + proj_grid_name, + proj_grid_format, + proj_method, + inverse_direction, + package_name, + url, direct_download, open_license, directory) + VALUES ('ISN2004_ISN2016.gsb', + 'ISN2004_ISN2016.gsb', + 'NTv2', + 'hgridshift', + 0, + 'proj-datumgrid-europe', + NULL, NULL, NULL, NULL); diff --git a/data/sql/grid_transformation_custom.sql b/data/sql/grid_transformation_custom.sql index c7a58836..189b9e50 100644 --- a/data/sql/grid_transformation_custom.sql +++ b/data/sql/grid_transformation_custom.sql @@ -55,3 +55,38 @@ INSERT INTO "grid_transformation" VALUES( INSERT INTO "area" VALUES('PROJ','BWTA2017','Germany - Baden-Wurtemberg','Germany - Baden-Wurtemberg',47.5,49.83,7.49,10.51,0); -- Advertize a 0.8 accuracy slightly better than the 0.9 of BETA2007 for sort purposes INSERT INTO "grid_transformation" VALUES('PROJ','BWTA2017','DHDN to ETRS89 (BWTA2017)','DHDN to ETRS89 for Baden-Wurtemberg for ALKIS 2017. Using official BWTA2017 grid but this transformation entry has been created temporarily by PROJ. Accuracy indication not to be considered as authoritative','For applications requiring an accuracy of better than 1 metre.','EPSG','9615','NTv2','EPSG','4314','EPSG','4258','PROJ','BWTA2017',0.89,'EPSG','8656','Latitude and longitude difference file','BWTA2017.gsb',NULL,NULL,NULL,NULL,NULL,NULL,'-',0); + +-- Iceland + +INSERT INTO "grid_transformation" VALUES( + 'PROJ','EPSG_5323_TO_EPSG_8089','ISN2004 height to vertical datum ISH2004', + NULL,NULL, + 'PROJ','HEIGHT_TO_GEOGRAPHIC3D','GravityRelatedHeight to Geographic3D', + 'EPSG','5323', -- source CRS (ISN2004 height) + 'EPSG','8089', -- target CRS (ISH2004) + 'EPSG','1120', -- area of use: Iceland - onshore and offshore + NULL, + 'EPSG','8666','Geoid (height correction) model file','Icegeoid_ISN2004.gtx', + NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); + +INSERT INTO "grid_transformation" VALUES( + 'PROJ','EPSG_4945_TO_EPSG_8089','ISN93 height to vertical datum ISH2004', + NULL,NULL, + 'PROJ','HEIGHT_TO_GEOGRAPHIC3D','GravityRelatedHeight to Geographic3D', + 'EPSG','4945', -- source CRS (ISN93 height) + 'EPSG','8089', -- target CRS (ISH2004) + 'EPSG','1120', -- area of use: Iceland - onshore and offshore + NULL, + 'EPSG','8666','Geoid (height correction) model file','Icegeoid_ISN93.gtx', + NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); + +INSERT INTO "grid_transformation" VALUES( + 'PROJ','EPSG_8085_TO_EPSG_8089','ISN2016 height to vertical datum ISH2004', + NULL,NULL, + 'PROJ','HEIGHT_TO_GEOGRAPHIC3D','GravityRelatedHeight to Geographic3D', + 'EPSG','8085', -- source CRS (ISN2016 height) + 'EPSG','8089', -- target CRS (ISH2004) + 'EPSG','1120', -- area of use: Iceland - onshore and offshore + NULL, + 'EPSG','8666','Geoid (height correction) model file','Icegeoid_ISN2016.gtx', + NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
\ No newline at end of file diff --git a/include/proj/common.hpp b/include/proj/common.hpp index d2f71989..cedb94c9 100644 --- a/include/proj/common.hpp +++ b/include/proj/common.hpp @@ -337,15 +337,16 @@ class PROJ_GCC_DLL IdentifiedObject : public util::BaseObject, PROJ_INTERNAL void formatRemarks(io::JSONFormatter *formatter) const; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; - - PROJ_INTERNAL bool - _isEquivalentTo(const IdentifiedObject *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) PROJ_PURE_DECL; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; + + PROJ_INTERNAL bool _isEquivalentTo( + const IdentifiedObject *other, util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) PROJ_PURE_DECL; //! @endcond protected: @@ -357,6 +358,10 @@ class PROJ_GCC_DLL IdentifiedObject : public util::BaseObject, void setProperties(const util::PropertyMap &properties); // throw(InvalidValueTypeException) + virtual bool hasEquivalentNameToUsingAlias( + const IdentifiedObject *other, + const io::DatabaseContextPtr &dbContext) const; + private: PROJ_OPAQUE_PRIVATE_DATA IdentifiedObject &operator=(const IdentifiedObject &other) = delete; @@ -400,10 +405,11 @@ class PROJ_GCC_DLL ObjectDomain : public util::BaseObject, PROJ_INTERNAL void _exportToJSON( io::JSONFormatter *formatter) const; // throw(FormattingException) - bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -446,10 +452,11 @@ class PROJ_GCC_DLL ObjectUsage : public IdentifiedObject { PROJ_DLL static const std::string OBJECT_DOMAIN_KEY; //! @cond Doxygen_Suppress - bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp index 55bc28a0..81d82474 100644 --- a/include/proj/coordinateoperation.hpp +++ b/include/proj/coordinateoperation.hpp @@ -210,7 +210,8 @@ class PROJ_GCC_DLL GeneralOperationParameter : public common::IdentifiedObject { PROJ_INTERNAL bool _isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override = 0; + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override = 0; //! @endcond protected: @@ -251,10 +252,11 @@ class PROJ_GCC_DLL OperationParameter final : public GeneralOperationParameter { //! @endcond //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond // non-standard @@ -307,7 +309,8 @@ class PROJ_GCC_DLL GeneralParameterValue : public util::BaseObject, PROJ_INTERNAL bool _isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override = 0; + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override = 0; //! @endcond protected: @@ -390,10 +393,11 @@ class PROJ_GCC_DLL ParameterValue final : public util::BaseObject, PROJ_DLL bool booleanValue() PROJ_PURE_DECL; //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -450,10 +454,11 @@ class PROJ_GCC_DLL OperationParameterValue final PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -519,10 +524,11 @@ class PROJ_GCC_DLL OperationMethod : public common::IdentifiedObject, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -615,10 +621,11 @@ class PROJ_GCC_DLL SingleOperation : virtual public CoordinateOperation { PROJ_INTERNAL double parameterValueNumericAsSI(int epsg_code) const noexcept; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -637,6 +644,7 @@ class PROJ_GCC_DLL SingleOperation : virtual public CoordinateOperation { PROJ_INTERNAL bool _isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext, bool inOtherDirection) const; private: @@ -1681,10 +1689,11 @@ class PROJ_GCC_DLL ConcatenatedOperation final : public CoordinateOperation { _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) diff --git a/include/proj/coordinatesystem.hpp b/include/proj/coordinatesystem.hpp index 20c3383c..e1650168 100644 --- a/include/proj/coordinatesystem.hpp +++ b/include/proj/coordinatesystem.hpp @@ -193,7 +193,8 @@ class PROJ_GCC_DLL CoordinateSystemAxis final : public common::IdentifiedObject, _isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter, int order, bool disableAbbrev) const; @@ -260,10 +261,11 @@ class PROJ_GCC_DLL CoordinateSystem : public common::IdentifiedObject, PROJ_INTERNAL virtual std::string getWKT2Type(bool) const = 0; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp index 356c29d9..3b760099 100644 --- a/include/proj/crs.hpp +++ b/include/proj/crs.hpp @@ -189,7 +189,8 @@ class PROJ_GCC_DLL SingleCRS : public CRS { PROJ_INTERNAL bool baseIsEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const; + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const; private: PROJ_OPAQUE_PRIVATE_DATA @@ -284,10 +285,11 @@ class PROJ_GCC_DLL GeodeticCRS : virtual public SingleCRS, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond @@ -374,10 +376,11 @@ class PROJ_GCC_DLL GeographicCRS : public GeodeticCRS { PROJ_DLL bool is2DPartOf3D(util::nn<const GeographicCRS *> other) PROJ_PURE_DECL; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond @@ -464,10 +467,11 @@ class PROJ_GCC_DLL VerticalCRS : virtual public SingleCRS, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond @@ -538,10 +542,11 @@ class PROJ_GCC_DLL DerivedCRS : virtual public SingleCRS { io::WKTFormatter *formatter, const std::string &keyword, const std::string &baseKeyword) const; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL virtual const char *className() const = 0; @@ -625,10 +630,11 @@ class PROJ_GCC_DLL ProjectedCRS final : public DerivedCRS, PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -692,10 +698,11 @@ class PROJ_GCC_DLL TemporalCRS : virtual public SingleCRS { PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; private: PROJ_OPAQUE_PRIVATE_DATA @@ -751,10 +758,11 @@ class PROJ_GCC_DLL EngineeringCRS : virtual public SingleCRS { PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; INLINED_MAKE_SHARED @@ -811,10 +819,11 @@ class PROJ_GCC_DLL ParametricCRS : virtual public SingleCRS { PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; INLINED_MAKE_SHARED @@ -879,10 +888,11 @@ class PROJ_GCC_DLL CompoundCRS final : public CRS, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -965,10 +975,11 @@ class PROJ_GCC_DLL BoundCRS final : public CRS, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL BoundCRSNNPtr shallowCloneAsBoundCRS() const; PROJ_INTERNAL bool isTOWGS84Compatible() const; @@ -1045,10 +1056,11 @@ class PROJ_GCC_DLL DerivedGeodeticCRS final : public GeodeticCRS, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -1120,10 +1132,11 @@ class PROJ_GCC_DLL DerivedGeographicCRS final : public GeographicCRS, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -1188,10 +1201,11 @@ class PROJ_GCC_DLL DerivedProjectedCRS final : public DerivedCRS { PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL const char *className() const override { return "DerivedProjectedCRS"; @@ -1253,10 +1267,11 @@ class PROJ_GCC_DLL DerivedVerticalCRS final : public VerticalCRS, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL std::list<std::pair<CRSNNPtr, int>> _identify(const io::AuthorityFactoryPtr &authorityFactory) const override; @@ -1343,10 +1358,11 @@ class PROJ_GCC_DLL DerivedCRSTemplate final : public DerivedCRSTraits::BaseType, PROJ_INTERNAL CRSNNPtr _shallowClone() const override; - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL const char *className() const override; diff --git a/include/proj/datum.hpp b/include/proj/datum.hpp index e97f1695..6a0db1dc 100644 --- a/include/proj/datum.hpp +++ b/include/proj/datum.hpp @@ -72,14 +72,17 @@ class PROJ_GCC_DLL Datum : public common::ObjectUsage, PROJ_DLL const util::optional<common::DateTime> &publicationDate() const; PROJ_DLL const common::IdentifiedObjectPtr &conventionalRS() const; + //! @cond Doxygen_Suppress + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; + //! @endcond + protected: PROJ_INTERNAL Datum(); - PROJ_INTERNAL bool - __isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const; - #ifdef DOXYGEN_ENABLED std::string *anchorDefinition_; Date *publicationDate_; @@ -211,10 +214,11 @@ class PROJ_GCC_DLL PrimeMeridian final : public common::IdentifiedObject, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(io::FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL static std::string getPROJStringWellKnownName(const common::Angle &angle); @@ -317,10 +321,11 @@ class PROJ_GCC_DLL Ellipsoid final : public common::IdentifiedObject, PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(io::FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter) const override; // throw(FormattingException) @@ -419,10 +424,11 @@ class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum { PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -440,6 +446,10 @@ class PROJ_GCC_DLL GeodeticReferenceFrame : public Datum { PROJ_INTERNAL static const GeodeticReferenceFrameNNPtr createEPSG_6269(); PROJ_INTERNAL static const GeodeticReferenceFrameNNPtr createEPSG_6326(); + bool hasEquivalentNameToUsingAlias( + const IdentifiedObject *other, + const io::DatabaseContextPtr &dbContext) const override; + private: PROJ_OPAQUE_PRIVATE_DATA GeodeticReferenceFrame(const GeodeticReferenceFrame &other) = delete; @@ -484,10 +494,11 @@ class PROJ_GCC_DLL DynamicGeodeticReferenceFrame final const util::optional<std::string> &deformationModelNameIn); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) @@ -567,10 +578,11 @@ class PROJ_GCC_DLL VerticalReferenceFrame : public Datum { util::optional<RealizationMethod>()); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) @@ -630,10 +642,11 @@ class PROJ_GCC_DLL DynamicVerticalReferenceFrame final const util::optional<std::string> &deformationModelNameIn); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) const override; // throw(io::FormattingException) @@ -695,10 +708,11 @@ class PROJ_GCC_DLL TemporalDatum final : public Datum { PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -746,10 +760,11 @@ class PROJ_GCC_DLL EngineeringDatum final : public Datum { PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: @@ -793,10 +808,11 @@ class PROJ_GCC_DLL ParametricDatum final : public Datum { PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) const override; // throw(FormattingException) - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond protected: diff --git a/include/proj/internal/coordinateoperation_internal.hpp b/include/proj/internal/coordinateoperation_internal.hpp index 7ae2cd78..361261c1 100644 --- a/include/proj/internal/coordinateoperation_internal.hpp +++ b/include/proj/internal/coordinateoperation_internal.hpp @@ -118,10 +118,11 @@ class InverseCoordinateOperation : virtual public CoordinateOperation { void _exportToPROJString(io::PROJStringFormatter *formatter) const override; // throw(FormattingException) - bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; CoordinateOperationNNPtr inverse() const override; @@ -158,11 +159,13 @@ class InverseConversion : public Conversion, public InverseCoordinateOperation { InverseCoordinateOperation::_exportToPROJString(formatter); } - bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override { - return InverseCoordinateOperation::_isEquivalentTo(other, criterion); + bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override { + return InverseCoordinateOperation::_isEquivalentTo(other, criterion, + dbContext); } CoordinateOperationNNPtr inverse() const override { @@ -208,11 +211,13 @@ class InverseTransformation : public Transformation, Transformation::_exportToJSON(formatter); } - bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override { - return InverseCoordinateOperation::_isEquivalentTo(other, criterion); + bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override { + return InverseCoordinateOperation::_isEquivalentTo(other, criterion, + dbContext); } CoordinateOperationNNPtr inverse() const override { diff --git a/include/proj/io.hpp b/include/proj/io.hpp index be293eb9..37941901 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -842,6 +842,11 @@ class PROJ_GCC_DLL DatabaseContext { const std::string &tableName, const std::string &source) const; + PROJ_INTERNAL std::list<std::string> + getAliases(const std::string &authName, const std::string &code, + const std::string &officialName, const std::string &tableName, + const std::string &source) const; + PROJ_INTERNAL bool isKnownName(const std::string &name, const std::string &tableName) const; diff --git a/include/proj/metadata.hpp b/include/proj/metadata.hpp index 5d364840..bd78e6e3 100644 --- a/include/proj/metadata.hpp +++ b/include/proj/metadata.hpp @@ -108,7 +108,8 @@ class PROJ_GCC_DLL GeographicExtent : public util::BaseObject, PROJ_INTERNAL bool _isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override = 0; + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override = 0; //! @endcond /** \brief Returns whether this extent contains the other one. */ @@ -162,10 +163,11 @@ class PROJ_GCC_DLL GeographicBoundingBox : public GeographicExtent { create(double west, double south, double east, double north); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond PROJ_INTERNAL bool @@ -214,10 +216,11 @@ class PROJ_GCC_DLL TemporalExtent : public util::BaseObject, const std::string &stop); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond PROJ_DLL bool contains(const TemporalExtentNNPtr &other) const; @@ -263,10 +266,11 @@ class PROJ_GCC_DLL VerticalExtent : public util::BaseObject, const common::UnitOfMeasureNNPtr &unitIn); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond PROJ_DLL bool contains(const VerticalExtentNNPtr &other) const; @@ -323,10 +327,11 @@ class PROJ_GCC_DLL Extent : public util::BaseObject, public util::IComparable { util::optional<std::string>()); //! @cond Doxygen_Suppress - PROJ_INTERNAL bool - _isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion = - util::IComparable::Criterion::STRICT) const override; + PROJ_INTERNAL bool _isEquivalentTo( + const util::IComparable *other, + util::IComparable::Criterion criterion = + util::IComparable::Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const override; //! @endcond PROJ_DLL bool contains(const ExtentNNPtr &other) const; diff --git a/include/proj/util.hpp b/include/proj/util.hpp index e4d6c3e0..622709d1 100644 --- a/include/proj/util.hpp +++ b/include/proj/util.hpp @@ -166,6 +166,13 @@ namespace proj {} NS_PROJ_START +//! @cond Doxygen_Suppress +namespace io { +class DatabaseContext; +using DatabaseContextPtr = std::shared_ptr<DatabaseContext>; +} +//! @endcond + /** osgeo.proj.util namespace. * * \brief A set of base types from ISO 19103, \ref GeoAPI and other PROJ @@ -342,15 +349,18 @@ class PROJ_GCC_DLL IComparable { EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, }; - PROJ_DLL bool isEquivalentTo(const IComparable *other, - Criterion criterion = Criterion::STRICT) const; + PROJ_DLL bool + isEquivalentTo(const IComparable *other, + Criterion criterion = Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const; PROJ_PRIVATE : //! @cond Doxygen_Suppress PROJ_INTERNAL virtual bool - _isEquivalentTo(const IComparable *other, - Criterion criterion = Criterion::STRICT) const = 0; + _isEquivalentTo( + const IComparable *other, Criterion criterion = Criterion::STRICT, + const io::DatabaseContextPtr &dbContext = nullptr) const = 0; //! @endcond }; diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index e20f29b3..49d6e5d2 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -38,6 +38,7 @@ osgeo::proj::common::IdentifiedObject::alias() const osgeo::proj::common::IdentifiedObject::aliases() const osgeo::proj::common::IdentifiedObject::formatID(osgeo::proj::io::WKTFormatter*) const osgeo::proj::common::IdentifiedObject::getEPSGCode() const +osgeo::proj::common::IdentifiedObject::hasEquivalentNameToUsingAlias(osgeo::proj::common::IdentifiedObject const*, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::common::IdentifiedObject::~IdentifiedObject() osgeo::proj::common::IdentifiedObject::IdentifiedObject() osgeo::proj::common::IdentifiedObject::IdentifiedObject(osgeo::proj::common::IdentifiedObject const&) @@ -62,7 +63,7 @@ osgeo::proj::common::Measure::value() const osgeo::proj::common::ObjectDomain::create(osgeo::proj::util::optional<std::string> const&, std::shared_ptr<osgeo::proj::metadata::Extent> const&) osgeo::proj::common::ObjectDomain::domainOfValidity() const osgeo::proj::common::ObjectDomain::_exportToWKT(osgeo::proj::io::WKTFormatter*) const -osgeo::proj::common::ObjectDomain::_isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion) const +osgeo::proj::common::ObjectDomain::_isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::common::ObjectDomain::~ObjectDomain() osgeo::proj::common::ObjectDomain::ObjectDomain(osgeo::proj::common::ObjectDomain const&) osgeo::proj::common::ObjectDomain::ObjectDomain(osgeo::proj::util::optional<std::string> const&, std::shared_ptr<osgeo::proj::metadata::Extent> const&) @@ -70,7 +71,7 @@ osgeo::proj::common::ObjectDomain::scope() const osgeo::proj::common::ObjectUsage::baseExportToJSON(osgeo::proj::io::JSONFormatter*) const osgeo::proj::common::ObjectUsage::baseExportToWKT(osgeo::proj::io::WKTFormatter*) const osgeo::proj::common::ObjectUsage::domains() const -osgeo::proj::common::ObjectUsage::_isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion) const +osgeo::proj::common::ObjectUsage::_isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::common::ObjectUsage::~ObjectUsage() osgeo::proj::common::ObjectUsage::ObjectUsage() osgeo::proj::common::ObjectUsage::ObjectUsage(osgeo::proj::common::ObjectUsage const&) @@ -263,6 +264,7 @@ osgeo::proj::datum::EngineeringDatum::~EngineeringDatum() osgeo::proj::datum::GeodeticReferenceFrame::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::datum::Ellipsoid> > const&, osgeo::proj::util::optional<std::string> const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::datum::PrimeMeridian> > const&) osgeo::proj::datum::GeodeticReferenceFrame::ellipsoid() const osgeo::proj::datum::GeodeticReferenceFrame::~GeodeticReferenceFrame() +osgeo::proj::datum::GeodeticReferenceFrame::hasEquivalentNameToUsingAlias(osgeo::proj::common::IdentifiedObject const*, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::datum::GeodeticReferenceFrame::primeMeridian() const osgeo::proj::datum::ParametricDatum::create(osgeo::proj::util::PropertyMap const&, osgeo::proj::util::optional<std::string> const&) osgeo::proj::datum::ParametricDatum::~ParametricDatum() @@ -672,7 +674,7 @@ osgeo::proj::util::Exception::Exception(osgeo::proj::util::Exception const&) osgeo::proj::util::Exception::what() const osgeo::proj::util::GenericName::~GenericName() osgeo::proj::util::IComparable::~IComparable() -osgeo::proj::util::IComparable::isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion) const +osgeo::proj::util::IComparable::isEquivalentTo(osgeo::proj::util::IComparable const*, osgeo::proj::util::IComparable::Criterion, std::shared_ptr<osgeo::proj::io::DatabaseContext> const&) const osgeo::proj::util::InvalidValueTypeException::~InvalidValueTypeException() osgeo::proj::util::InvalidValueTypeException::InvalidValueTypeException(osgeo::proj::util::InvalidValueTypeException const&) osgeo::proj::util::LocalName::~LocalName() @@ -801,6 +803,7 @@ proj_context_use_proj4_init_rules proj_convert_conversion_to_other_method proj_coord proj_coord_error() +proj_coordoperation_create_inverse proj_coordoperation_get_accuracy proj_coordoperation_get_grid_used proj_coordoperation_get_grid_used_count @@ -960,6 +963,7 @@ proj_is_crs proj_is_deprecated proj_is_derived_crs proj_is_equivalent_to +proj_is_equivalent_to_with_ctx proj_list_angular_units proj_list_destroy proj_list_ellps diff --git a/src/apply_gridshift.cpp b/src/apply_gridshift.cpp index e6c7b2b1..39e7c3b5 100644 --- a/src/apply_gridshift.cpp +++ b/src/apply_gridshift.cpp @@ -115,7 +115,7 @@ int pj_apply_gridshift_2( PJ *defn, int inverse, /* Determine which grid is the correct given an input coordinate. */ /************************************************************************/ -static struct CTABLE* find_ctable(projCtx ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables) { +struct CTABLE* find_ctable(projCtx ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables) { int itable; /* keep trying till we find a table that works */ @@ -210,7 +210,7 @@ int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_coun ct = find_ctable(ctx, input, gridlist_count, gridlist); if( ct != nullptr ) { - output = nad_cvt( input, inverse, ct ); + output = nad_cvt( ctx, input, inverse, ct, gridlist_count, gridlist); if ( output.lam != HUGE_VAL && debug_count++ < 20 ) pj_log( ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id ); @@ -356,7 +356,7 @@ PJ_LP proj_hgrid_apply(PJ *P, PJ_LP lp, PJ_DIRECTION direction) { } inverse = direction == PJ_FWD ? 0 : 1; - out = nad_cvt(lp, inverse, ct); + out = nad_cvt(P->ctx, lp, inverse, ct, P->gridlist_count, P->gridlist); if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp index 7fe08023..09c8a81d 100644 --- a/src/apps/proj.cpp +++ b/src/apps/proj.cpp @@ -490,6 +490,14 @@ int main(int argc, char **argv) { exit(0); } + // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 + if( Proj->right == PJ_IO_UNITS_WHATEVER && Proj->descr && + strncmp(Proj->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + Proj->right = PJ_IO_UNITS_PROJECTED; + } + if (inverse) { if (!Proj->inv) emess(3,"inverse projection not available"); diff --git a/src/geodesic.h b/src/geodesic.h index 5d230531..e2265c89 100644 --- a/src/geodesic.h +++ b/src/geodesic.h @@ -158,7 +158,7 @@ GEODESIC_VERSION_PATCH) #if !defined(GEOD_DLL) -#if defined(_MSC_VER) +#if defined(_MSC_VER) && defined(PROJ_MSVC_DLL_EXPORT) #define GEOD_DLL __declspec(dllexport) #elif defined(__GNUC__) #define GEOD_DLL __attribute__ ((visibility("default"))) diff --git a/src/gridcatalog.cpp b/src/gridcatalog.cpp index 15d81dd7..9b94fef8 100644 --- a/src/gridcatalog.cpp +++ b/src/gridcatalog.cpp @@ -164,7 +164,7 @@ int pj_gc_apply_gridshift( PJ *defn, int inverse, return PJD_ERR_FAILED_TO_LOAD_GRID; } - output_after = nad_cvt( input, inverse, gi->ct ); + output_after = nad_cvt( defn->ctx, input, inverse, gi->ct, 0, nullptr ); if( output_after.lam == HUGE_VAL ) { if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) @@ -213,7 +213,7 @@ int pj_gc_apply_gridshift( PJ *defn, int inverse, return PJD_ERR_FAILED_TO_LOAD_GRID; } - output_before = nad_cvt( input, inverse, gi->ct ); + output_before = nad_cvt( defn->ctx, input, inverse, gi->ct, 0, nullptr ); if( output_before.lam == HUGE_VAL ) { if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 9db9e5b3..5a5e97f6 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -1142,15 +1142,9 @@ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) { // --------------------------------------------------------------------------- -/** \brief Return whether two objects are equivalent. - * - * @param obj Object (must not be NULL) - * @param other Other object (must not be NULL) - * @param criterion Comparison criterion - * @return TRUE if they are equivalent - */ -int proj_is_equivalent_to(const PJ *obj, const PJ *other, - PJ_COMPARISON_CRITERION criterion) { +static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj, + const PJ *other, + PJ_COMPARISON_CRITERION criterion) { assert(obj); assert(other); if (!obj->iso_obj) { @@ -1172,7 +1166,50 @@ int proj_is_equivalent_to(const PJ *obj, const PJ *other, return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS; })(criterion); - return obj->iso_obj->isEquivalentTo(other->iso_obj.get(), cppCriterion); + int res = obj->iso_obj->isEquivalentTo( + other->iso_obj.get(), cppCriterion, + ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx") + : nullptr); + if (ctx && ctx->cpp_context) { + ctx->cpp_context->autoCloseDbIfNeeded(); + } + return res; +} + +// --------------------------------------------------------------------------- + +/** \brief Return whether two objects are equivalent. + * + * Use proj_is_equivalent_to_with_ctx() to be able to use database information. + * + * @param obj Object (must not be NULL) + * @param other Other object (must not be NULL) + * @param criterion Comparison criterion + * @return TRUE if they are equivalent + */ +int proj_is_equivalent_to(const PJ *obj, const PJ *other, + PJ_COMPARISON_CRITERION criterion) { + return proj_is_equivalent_to_internal(nullptr, obj, other, criterion); +} + +// --------------------------------------------------------------------------- + +/** \brief Return whether two objects are equivalent + * + * Possibly using database to check for name aliases. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object (must not be NULL) + * @param other Other object (must not be NULL) + * @param criterion Comparison criterion + * @return TRUE if they are equivalent + * @since 6.3 + */ +int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj, + const PJ *other, + PJ_COMPARISON_CRITERION criterion) { + SANITIZE_CTX(ctx); + return proj_is_equivalent_to_internal(ctx, obj, other, criterion); } // --------------------------------------------------------------------------- @@ -7769,6 +7806,34 @@ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) { // --------------------------------------------------------------------------- +/** \brief Returns a PJ* coordinate operation object which represents the + * inverse operation of the specified coordinate operation. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object of type CoordinateOperation (must not be NULL) + * @return a new PJ* object to free with proj_destroy() in case of success, or + * nullptr in case of error + * @since 6.3 + */ +PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) { + + SANITIZE_CTX(ctx); + auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get()); + if (!co) { + proj_log_error(ctx, __FUNCTION__, + "Object is not a CoordinateOperation"); + return nullptr; + } + try { + return pj_obj_create(ctx, co->inverse()); + } catch (const std::exception &e) { + proj_log_debug(ctx, __FUNCTION__, e.what()); + return nullptr; + } +} + +// --------------------------------------------------------------------------- + /** \brief Returns the number of steps of a concatenated operation. * * The input object must be a concatenated operation. diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp index 97900bda..f2e4de4c 100644 --- a/src/iso19111/common.cpp +++ b/src/iso19111/common.cpp @@ -901,19 +901,19 @@ void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const { // --------------------------------------------------------------------------- bool IdentifiedObject::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other); if (!otherIdObj) return false; - return _isEquivalentTo(otherIdObj, criterion); + return _isEquivalentTo(otherIdObj, criterion, dbContext); } // --------------------------------------------------------------------------- -bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj, - util::IComparable::Criterion criterion) - PROJ_PURE_DEFN { +bool IdentifiedObject::_isEquivalentTo( + const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN { if (criterion == util::IComparable::Criterion::STRICT) { if (!ci_equal(nameStr(), otherIdObj->nameStr())) { return false; @@ -922,12 +922,19 @@ bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj, } else { if (!metadata::Identifier::isEquivalentName( nameStr().c_str(), otherIdObj->nameStr().c_str())) { - return false; + return hasEquivalentNameToUsingAlias(otherIdObj, dbContext); } } return true; } +// --------------------------------------------------------------------------- + +bool IdentifiedObject::hasEquivalentNameToUsingAlias( + const IdentifiedObject *, const io::DatabaseContextPtr &) const { + return false; +} + //! @endcond // --------------------------------------------------------------------------- @@ -1092,8 +1099,8 @@ void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const { //! @cond Doxygen_Suppress bool ObjectDomain::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDomain = dynamic_cast<const ObjectDomain *>(other); if (!otherDomain) return false; @@ -1106,7 +1113,7 @@ bool ObjectDomain::_isEquivalentTo( return false; return domainOfValidity().get() == nullptr || domainOfValidity()->_isEquivalentTo( - otherDomain->domainOfValidity().get(), criterion); + otherDomain->domainOfValidity().get(), criterion, dbContext); } //! @endcond @@ -1249,14 +1256,14 @@ void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const { //! @cond Doxygen_Suppress bool ObjectUsage::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other); if (!otherObjUsage) return false; // TODO: incomplete - return IdentifiedObject::_isEquivalentTo(other, criterion); + return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } //! @endcond diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 791bdcb0..62d72e65 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -1068,11 +1068,11 @@ void OperationMethod::_exportToJSON( //! @cond Doxygen_Suppress bool OperationMethod::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOM = dynamic_cast<const OperationMethod *>(other); if (otherOM == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // TODO test formula and formulaCitation @@ -1084,7 +1084,8 @@ bool OperationMethod::_isEquivalentTo( } if (criterion == util::IComparable::Criterion::STRICT) { for (size_t i = 0; i < paramsSize; i++) { - if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion)) { + if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion, + dbContext)) { return false; } } @@ -1094,8 +1095,8 @@ bool OperationMethod::_isEquivalentTo( bool found = false; for (size_t j = 0; j < paramsSize; j++) { if (candidateIndices[j] && - params[i]->_isEquivalentTo(otherParams[j].get(), - criterion)) { + params[i]->_isEquivalentTo(otherParams[j].get(), criterion, + dbContext)) { candidateIndices[j] = false; found = true; break; @@ -1341,14 +1342,14 @@ bool OperationParameterValue::convertFromAbridged( //! @cond Doxygen_Suppress bool OperationParameterValue::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOPV = dynamic_cast<const OperationParameterValue *>(other); if (otherOPV == nullptr) { return false; } - if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), - criterion)) { + if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), criterion, + dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { @@ -1356,7 +1357,7 @@ bool OperationParameterValue::_isEquivalentTo( otherOPV->d->parameterValue.get(), criterion); } if (d->parameterValue->_isEquivalentTo(otherOPV->d->parameterValue.get(), - criterion)) { + criterion, dbContext)) { return true; } if (d->parameter->getEPSGCode() == @@ -1446,16 +1447,16 @@ OperationParameter::create(const util::PropertyMap &properties) { //! @cond Doxygen_Suppress bool OperationParameter::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherOP = dynamic_cast<const OperationParameter *>(other); if (otherOP == nullptr) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { - return IdentifiedObject::_isEquivalentTo(other, criterion); + return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } - if (IdentifiedObject::_isEquivalentTo(other, criterion)) { + if (IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return true; } auto l_epsgCode = getEPSGCode(); @@ -1759,19 +1760,20 @@ static SingleOperationNNPtr createPROJBased( //! @cond Doxygen_Suppress bool SingleOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { - return _isEquivalentTo(other, criterion, false); + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { + return _isEquivalentTo(other, criterion, dbContext, false); } bool SingleOperation::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext, bool inOtherDirection) const { auto otherSO = dynamic_cast<const SingleOperation *>(other); if (otherSO == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } @@ -1781,7 +1783,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, bool equivalentMethods = (criterion == util::IComparable::Criterion::EQUIVALENT && methodEPSGCode != 0 && methodEPSGCode == otherMethodEPSGCode) || - d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion); + d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion, + dbContext); if (!equivalentMethods && criterion == util::IComparable::Criterion::EQUIVALENT) { if ((methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA && @@ -1854,7 +1857,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { // Convert from 2SP to 1SP as the other direction has more // degree of liberties. - return otherSO->_isEquivalentTo(this, criterion); + return otherSO->_isEquivalentTo(this, criterion, dbContext); } else if ((methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A && otherMethodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B) || @@ -1870,7 +1873,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, auto eqConv = conv->convertToOtherMethod(otherMethodEPSGCode); if (eqConv) { - return eqConv->_isEquivalentTo(other, criterion); + return eqConv->_isEquivalentTo(other, criterion, + dbContext); } } } @@ -1888,7 +1892,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, return false; } for (size_t i = 0; i < valuesSize; i++) { - if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion)) { + if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion, + dbContext)) { return false; } } @@ -1909,7 +1914,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, bool sameNameDifferentValue = false; for (size_t j = 0; j < otherValuesSize; j++) { if (candidateIndices[j] && - values[i]->_isEquivalentTo(otherValues[j].get(), criterion)) { + values[i]->_isEquivalentTo(otherValues[j].get(), criterion, + dbContext)) { candidateIndices[j] = false; equivalent = true; break; @@ -1921,7 +1927,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, return false; sameNameDifferentValue = opParamvalue->parameter()->_isEquivalentTo( - otherOpParamvalue->parameter().get(), criterion); + otherOpParamvalue->parameter().get(), criterion, + dbContext); if (sameNameDifferentValue) { candidateIndices[j] = false; break; @@ -1949,13 +1956,13 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) .get(), - criterion) && + criterion, dbContext) && value_2nd->_isEquivalentTo( otherSO ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL) .get(), - criterion); + criterion, dbContext); } } } @@ -1987,7 +1994,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, // In the case the arguments don't perfectly match, try the reverse // check. if (equivalent && foundMissingArgs && !inOtherDirection) { - return otherSO->_isEquivalentTo(this, criterion, true); + return otherSO->_isEquivalentTo(this, criterion, dbContext, true); } // Equivalent formulations of 2SP can have different parameters @@ -2002,8 +2009,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other, auto otherAs1SP = otherConv->convertToOtherMethod( EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); if (thisAs1SP && otherAs1SP) { - equivalent = - thisAs1SP->_isEquivalentTo(otherAs1SP.get(), criterion); + equivalent = thisAs1SP->_isEquivalentTo(otherAs1SP.get(), + criterion, dbContext); } } } @@ -2412,9 +2419,9 @@ void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -bool ParameterValue::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { +bool ParameterValue::_isEquivalentTo(const util::IComparable *other, + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &) const { auto otherPV = dynamic_cast<const ParameterValue *>(other); if (otherPV == nullptr) { return false; @@ -10186,10 +10193,12 @@ void ConcatenatedOperation::_exportToPROJString( //! @cond Doxygen_Suppress bool ConcatenatedOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCO = dynamic_cast<const ConcatenatedOperation *>(other); - if (otherCO == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion)) { + if (otherCO == nullptr || + (criterion == util::IComparable::Criterion::STRICT && + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &steps = operations(); @@ -10198,7 +10207,8 @@ bool ConcatenatedOperation::_isEquivalentTo( return false; } for (size_t i = 0; i < steps.size(); i++) { - if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion)) { + if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion, + dbContext)) { return false; } } @@ -11301,9 +11311,17 @@ struct FilterResults { setOfSetOfGrids.end()) { continue; } + + const bool sameNameOrEmptyName = + ((!curExtent && !lastExtent) || + (curExtent && lastExtent && + !curExtent->description()->empty() && + *(curExtent->description()) == + *(lastExtent->description()))); + // If we have already found a operation without grids for // that extent, no need to add any lower accuracy operation - if (!lastHasGrids) { + if (!lastHasGrids && sameNameOrEmptyName) { continue; } // If we had only operations involving grids, but one @@ -14831,14 +14849,15 @@ void InverseCoordinateOperation::_exportToPROJString( // --------------------------------------------------------------------------- bool InverseCoordinateOperation::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherICO = dynamic_cast<const InverseCoordinateOperation *>(other); if (otherICO == nullptr || - !ObjectUsage::_isEquivalentTo(other, criterion)) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } - return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion); + return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion, + dbContext); } // --------------------------------------------------------------------------- diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp index 6769b486..163f0f43 100644 --- a/src/iso19111/coordinatesystem.cpp +++ b/src/iso19111/coordinatesystem.cpp @@ -429,8 +429,8 @@ void CoordinateSystemAxis::_exportToJSON( //! @cond Doxygen_Suppress bool CoordinateSystemAxis::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCSA = dynamic_cast<const CoordinateSystemAxis *>(other); if (otherCSA == nullptr) { return false; @@ -441,7 +441,7 @@ bool CoordinateSystemAxis::_isEquivalentTo( return false; } if (criterion == util::IComparable::Criterion::STRICT) { - if (!IdentifiedObject::_isEquivalentTo(other, criterion)) { + if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (abbreviation() != otherCSA->abbreviation()) { @@ -599,11 +599,11 @@ void CoordinateSystem::_exportToJSON( //! @cond Doxygen_Suppress bool CoordinateSystem::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCS = dynamic_cast<const CoordinateSystem *>(other); if (otherCS == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } const auto &list = axisList(); @@ -615,7 +615,8 @@ bool CoordinateSystem::_isEquivalentTo( return false; } for (size_t i = 0; i < list.size(); i++) { - if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion)) { + if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion, + dbContext)) { return false; } } diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 49cc050f..cf90c388 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -49,6 +49,7 @@ #include <algorithm> #include <cassert> +#include <cmath> #include <cstring> #include <iostream> #include <memory> @@ -390,7 +391,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( if (boundCRS) { if (boundCRS->hubCRS()->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { return NN_NO_CHECK(boundCRS); } } @@ -399,16 +400,16 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( auto geogCRS = extractGeographicCRS(); auto hubCRS = util::nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326); if (geodCRS && !geogCRS) { - if (geodCRS->_isEquivalentTo( - GeographicCRS::EPSG_4978.get(), - util::IComparable::Criterion::EQUIVALENT)) { + if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(), + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { return thisAsCRS; } hubCRS = util::nn_static_pointer_cast<CRS>(GeodeticCRS::EPSG_4978); } else if (!geogCRS || geogCRS->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { return thisAsCRS; } else { geodCRS = geogCRS; @@ -595,12 +596,44 @@ CRSNNPtr CRS::alterId(const std::string &authName, //! @cond Doxygen_Suppress -static bool isAxisListNorthEast( +static bool mustAxisOrderBeSwitchedForVisualizationInternal( const std::vector<cs::CoordinateSystemAxisNNPtr> &axisList) { const auto &dir0 = axisList[0]->direction(); const auto &dir1 = axisList[1]->direction(); - return (&dir0 == &cs::AxisDirection::NORTH && - &dir1 == &cs::AxisDirection::EAST); + if (&dir0 == &cs::AxisDirection::NORTH && + &dir1 == &cs::AxisDirection::EAST) { + return true; + } + + // Address EPSG:32661 "WGS 84 / UPS North (N,E)" + if (&dir0 == &cs::AxisDirection::SOUTH && + &dir1 == &cs::AxisDirection::SOUTH) { + const auto &meridian0 = axisList[0]->meridian(); + const auto &meridian1 = axisList[1]->meridian(); + return meridian0 != nullptr && meridian1 != nullptr && + std::abs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 180.0) < 1e-10 && + std::abs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; + } + + // Address EPSG:32761 "WGS 84 / UPS South (N,E)" + if (&dir0 == &cs::AxisDirection::NORTH && + &dir1 == &cs::AxisDirection::NORTH) { + const auto &meridian0 = axisList[0]->meridian(); + const auto &meridian1 = axisList[1]->meridian(); + return meridian0 != nullptr && meridian1 != nullptr && + std::abs(meridian0->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 0.0) < 1e-10 && + std::abs(meridian1->longitude().convertToUnit( + common::UnitOfMeasure::DEGREE) - + 90.0) < 1e-10; + } + + return false; } // --------------------------------------------------------------------------- @@ -616,12 +649,14 @@ bool CRS::mustAxisOrderBeSwitchedForVisualization() const { const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this); if (geogCRS) { - return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList()); + return mustAxisOrderBeSwitchedForVisualizationInternal( + geogCRS->coordinateSystem()->axisList()); } const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this); if (projCRS) { - return isAxisListNorthEast(projCRS->coordinateSystem()->axisList()); + return mustAxisOrderBeSwitchedForVisualizationInternal( + projCRS->coordinateSystem()->axisList()); } return false; @@ -655,7 +690,7 @@ CRSNNPtr CRS::normalizeForVisualization() const { const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this); if (geogCRS) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); - if (isAxisListNorthEast(axisList)) { + if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1], axisList[0]) @@ -670,7 +705,7 @@ CRSNNPtr CRS::normalizeForVisualization() const { const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this); if (projCRS) { const auto &axisList = projCRS->coordinateSystem()->axisList(); - if (isAxisListNorthEast(axisList)) { + if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::CartesianCS::create(util::PropertyMap(), axisList[1], @@ -987,18 +1022,19 @@ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- bool SingleCRS::baseIsEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherSingleCRS = dynamic_cast<const SingleCRS *>(other); if (otherSingleCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &thisDatum = d->datum; const auto &otherDatum = otherSingleCRS->d->datum; if (thisDatum) { - if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion)) { + if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion, + dbContext)) { return false; } } else { @@ -1009,7 +1045,8 @@ bool SingleCRS::baseIsEquivalentTo( // TODO test DatumEnsemble return d->coordinateSystem->_isEquivalentTo( - otherSingleCRS->d->coordinateSystem.get(), criterion) && + otherSingleCRS->d->coordinateSystem.get(), criterion, + dbContext) && getExtensionProj4() == otherSingleCRS->getExtensionProj4(); } @@ -1544,13 +1581,13 @@ getStandardCriterion(util::IComparable::Criterion criterion) { //! @cond Doxygen_Suppress bool GeodeticCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { const auto standardCriterion = getStandardCriterion(criterion); auto otherGeodCRS = dynamic_cast<const GeodeticCRS *>(other); // TODO test velocityModel return otherGeodCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, standardCriterion); + SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext); } //! @endcond @@ -1651,6 +1688,9 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { std::list<Pair> res; const auto &thisName(nameStr()); + io::DatabaseContextPtr dbContext = + authorityFactory ? authorityFactory->databaseContext().as_nullable() + : nullptr; const bool l_implicitCS = CRS::getPrivate()->implicitCS_; const auto crsCriterion = l_implicitCS @@ -1664,7 +1704,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const bool nameEquivalent = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); const bool nameEqual = thisName == crs->nameStr(); - const bool isEq = _isEquivalentTo(crs.get(), crsCriterion); + const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext); if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) { res.emplace_back(util::nn_static_pointer_cast<GeodeticCRS>(crs), nameEqual ? 100 : 90); @@ -1699,13 +1739,14 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisDatum(datum()); auto searchByDatum = [this, &authorityFactory, &res, &thisDatum, - &geodetic_crs_type, crsCriterion]() { + &geodetic_crs_type, crsCriterion, &dbContext]() { for (const auto &id : thisDatum->identifiers()) { try { auto tempRes = authorityFactory->createGeodeticCRSFromDatum( *id->codeSpace(), id->code(), geodetic_crs_type); for (const auto &crs : tempRes) { - if (_isEquivalentTo(crs.get(), crsCriterion)) { + if (_isEquivalentTo(crs.get(), crsCriterion, + dbContext)) { res.emplace_back(crs, 70); } } @@ -1717,7 +1758,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisEllipsoid(ellipsoid()); auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum, &thisEllipsoid, &geodetic_crs_type, - l_implicitCS]() { + l_implicitCS, &dbContext]() { const auto ellipsoids = thisEllipsoid->identifiers().empty() ? authorityFactory->createEllipsoidFromExisting( @@ -1735,15 +1776,17 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (crsDatum && crsDatum->ellipsoid()->_isEquivalentTo( ellps.get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && crsDatum->primeMeridian()->_isEquivalentTo( thisDatum->primeMeridian().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && (!l_implicitCS || coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), - util::IComparable::Criterion:: - EQUIVALENT))) { + util::IComparable::Criterion::EQUIVALENT, + dbContext))) { res.emplace_back(crs, 60); } } @@ -1776,7 +1819,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { authorityFactory->databaseContext(), *id->codeSpace()) ->createGeodeticCRS(id->code()); - bool match = _isEquivalentTo(crs.get(), crsCriterion); + bool match = + _isEquivalentTo(crs.get(), crsCriterion, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -1794,7 +1838,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { auto crs = util::nn_dynamic_pointer_cast<GeodeticCRS>(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); - if (_isEquivalentTo(crs.get(), crsCriterion)) { + if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -1824,8 +1868,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisCS(coordinateSystem()); // Sort results - res.sort([&thisName, &thisDatum, &thisCS](const Pair &a, - const Pair &b) { + res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a, + const Pair &b) { // First consider confidence if (a.second > b.second) { return true; @@ -1849,9 +1893,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &bDatum(b.first->datum()); if (thisDatum && aDatum && bDatum) { const auto thisEquivADatum(thisDatum->_isEquivalentTo( - aDatum.get(), util::IComparable::Criterion::EQUIVALENT)); + aDatum.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); const auto thisEquivBDatum(thisDatum->_isEquivalentTo( - bDatum.get(), util::IComparable::Criterion::EQUIVALENT)); + bDatum.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); if (thisEquivADatum && !thisEquivBDatum) { return true; @@ -1865,9 +1911,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &aCS(a.first->coordinateSystem()); const auto &bCS(b.first->coordinateSystem()); const auto thisEquivACs(thisCS->_isEquivalentTo( - aCS.get(), util::IComparable::Criterion::EQUIVALENT)); + aCS.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); const auto thisEquivBCs(thisCS->_isEquivalentTo( - bCS.get(), util::IComparable::Criterion::EQUIVALENT)); + bCS.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)); if (thisEquivACs && !thisEquivBCs) { return true; } @@ -2084,14 +2132,14 @@ bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other) //! @cond Doxygen_Suppress bool GeographicCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherGeogCRS = dynamic_cast<const GeographicCRS *>(other); if (otherGeogCRS == nullptr) { return false; } const auto standardCriterion = getStandardCriterion(criterion); - if (GeodeticCRS::_isEquivalentTo(other, standardCriterion)) { + if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) { return true; } if (criterion != @@ -2110,7 +2158,7 @@ bool GeographicCRS::_isEquivalentTo( cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH ? cs::EllipsoidalCS::createLatitudeLongitude(unit) : cs::EllipsoidalCS::createLongitudeLatitude(unit)) - ->GeodeticCRS::_isEquivalentTo(other, standardCriterion); + ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext); } return false; } @@ -2682,12 +2730,12 @@ VerticalCRS::create(const util::PropertyMap &properties, //! @cond Doxygen_Suppress bool VerticalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherVertCRS = dynamic_cast<const VerticalCRS *>(other); // TODO test geoidModel and velocityModel return otherVertCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } //! @endcond @@ -2722,6 +2770,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisName(nameStr()); if (authorityFactory) { + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || @@ -2733,12 +2783,11 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( - authorityFactory->databaseContext(), - *id->codeSpace()) + dbContext, *id->codeSpace()) ->createVerticalCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT); + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -2756,8 +2805,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { assert(crs); auto crsNN = NN_NO_CHECK(crs); if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -2922,19 +2971,20 @@ DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- -bool DerivedCRS::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { +bool DerivedCRS::_isEquivalentTo( + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedCRS *>(other); const auto standardCriterion = getStandardCriterion(criterion); if (otherDerivedCRS == nullptr || - !SingleCRS::baseIsEquivalentTo(other, standardCriterion)) { + !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) { return false; } return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(), - criterion) && + criterion, dbContext) && d->derivingConversion_->_isEquivalentTo( - otherDerivedCRS->d->derivingConversion_.get(), - standardCriterion); + otherDerivedCRS->d->derivingConversion_.get(), standardCriterion, + dbContext); } // --------------------------------------------------------------------------- @@ -3367,11 +3417,11 @@ ProjectedCRS::create(const util::PropertyMap &properties, // --------------------------------------------------------------------------- bool ProjectedCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherProjCRS = dynamic_cast<const ProjectedCRS *>(other); return otherProjCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -3545,14 +3595,19 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &conv = derivingConversionRef(); const auto &cs = coordinateSystem(); + io::DatabaseContextPtr dbContext = + authorityFactory ? authorityFactory->databaseContext().as_nullable() + : nullptr; + if (baseRes.size() == 1 && baseRes.front().second >= 70 && conv->isUTM(zone, north) && cs->_isEquivalentTo( cs::CartesianCS::createEastingNorthing(common::UnitOfMeasure::METRE) - .get())) { + .get(), + util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("WGS 84 / UTM zone ", zone, north)); res.emplace_back( @@ -3566,7 +3621,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4267.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD27 / UTM zone ", zone, north)); res.emplace_back( @@ -3581,7 +3636,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4269.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD83 / UTM zone ", zone, north)); res.emplace_back( @@ -3613,9 +3668,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { *id->codeSpace()) ->createProjectedCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS); + crs.get(), util::IComparable::Criterion:: + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -3636,9 +3691,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) { + crs.get(), util::IComparable::Criterion:: + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -3650,10 +3705,12 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { l_baseCRS->_isEquivalentTo( crs->baseCRS().get(), util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) && + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && objects.size() == 1) { res.clear(); res.emplace_back(crsNN, 100); @@ -3720,17 +3777,21 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (_isEquivalentTo(crs.get(), util::IComparable::Criterion:: - EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) { + EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, + dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else if (ellipsoid->_isEquivalentTo( crs->baseCRS()->ellipsoid().get(), - util::IComparable::Criterion::EQUIVALENT) && + util::IComparable::Criterion::EQUIVALENT, + dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { res.emplace_back(crs, 70); } else { res.emplace_back(crs, 50); @@ -3960,12 +4021,12 @@ void CompoundCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool CompoundCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherCompoundCRS = dynamic_cast<const CompoundCRS *>(other); if (otherCompoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &components = componentReferenceSystems(); @@ -3974,8 +4035,8 @@ bool CompoundCRS::_isEquivalentTo( return false; } for (size_t i = 0; i < components.size(); i++) { - if (!components[i]->_isEquivalentTo(otherComponents[i].get(), - criterion)) { + if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion, + dbContext)) { return false; } } @@ -4015,6 +4076,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { const auto &thisName(nameStr()); if (authorityFactory) { + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || @@ -4028,12 +4091,11 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( - authorityFactory->databaseContext(), - *id->codeSpace()) + dbContext, *id->codeSpace()) ->createCompoundCRS(id->code()); bool match = _isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT); + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { @@ -4054,8 +4116,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( - crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + crs.get(), util::IComparable::Criterion::EQUIVALENT, + dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); @@ -4122,7 +4184,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { } if (_isEquivalentTo(crs.get(), - util::IComparable::Criterion::EQUIVALENT)) { + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else { res.emplace_back(crs, 25); @@ -4494,20 +4557,22 @@ void BoundCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool BoundCRS::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherBoundCRS = dynamic_cast<const BoundCRS *>(other); if (otherBoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !ObjectUsage::_isEquivalentTo(other, criterion))) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto standardCriterion = getStandardCriterion(criterion); return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(), - criterion) && + criterion, dbContext) && d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(), - criterion) && + criterion, dbContext) && d->transformation_->_isEquivalentTo( - otherBoundCRS->d->transformation_.get(), standardCriterion); + otherBoundCRS->d->transformation_.get(), standardCriterion, + dbContext); } // --------------------------------------------------------------------------- @@ -4518,10 +4583,36 @@ std::list<std::pair<CRSNNPtr, int>> BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair<CRSNNPtr, int> Pair; std::list<Pair> res; - if (authorityFactory && - d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), - util::IComparable::Criterion::EQUIVALENT)) { + if (!authorityFactory) + return res; + std::list<Pair> resMatchOfTransfToWGS84; + const io::DatabaseContextNNPtr &dbContext = + authorityFactory->databaseContext(); + if (d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { auto resTemp = d->baseCRS_->identify(authorityFactory); + + std::string refTransfPROJString; + bool refTransfPROJStringValid = false; + auto refTransf = d->transformation_->normalizeForVisualization(); + try { + refTransfPROJString = refTransf->exportToPROJString( + io::PROJStringFormatter::create().get()); + refTransfPROJString = replaceAll( + refTransfPROJString, + " +rx=0 +ry=0 +rz=0 +s=0 +convention=position_vector", ""); + refTransfPROJStringValid = true; + } catch (const std::exception &) { + } + bool refIsNullTransform = false; + if (isTOWGS84Compatible()) { + auto params = transformation()->getTOWGS84Parameters(); + if (params == std::vector<double>{0, 0, 0, 0, 0, 0, 0}) { + refIsNullTransform = true; + } + } + for (const auto &pair : resTemp) { const auto &candidateBaseCRS = pair.first; auto projCRS = @@ -4532,54 +4623,47 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { if (geodCRS) { auto context = operation::CoordinateOperationContext::create( authorityFactory, nullptr, 0.0); + context->setSpatialCriterion( + operation::CoordinateOperationContext::SpatialCriterion:: + PARTIAL_INTERSECTION); auto ops = operation::CoordinateOperationFactory::create() ->createOperations(NN_NO_CHECK(geodCRS), GeographicCRS::EPSG_4326, context); - std::string refTransfPROJString; - bool refTransfPROJStringValid = false; - try { - refTransfPROJString = - d->transformation_->exportToPROJString( - io::PROJStringFormatter::create().get()); - refTransfPROJStringValid = true; - if (refTransfPROJString == "+proj=axisswap +order=2,1") { - refTransfPROJString.clear(); - } - } catch (const std::exception &) { - } + bool foundOp = false; for (const auto &op : ops) { + auto opNormalized = op->normalizeForVisualization(); std::string opTransfPROJString; bool opTransfPROJStringValid = false; if (op->nameStr().find("Ballpark geographic") == 0) { - if (isTOWGS84Compatible()) { - auto params = - transformation()->getTOWGS84Parameters(); - if (params == - std::vector<double>{0, 0, 0, 0, 0, 0, 0}) { - res.emplace_back(create(candidateBaseCRS, - d->hubCRS_, - transformation()), - pair.second); - foundOp = true; - break; - } + if (refIsNullTransform) { + res.emplace_back(create(candidateBaseCRS, + d->hubCRS_, + transformation()), + pair.second); + foundOp = true; + break; } continue; } try { - opTransfPROJString = op->exportToPROJString( + opTransfPROJString = opNormalized->exportToPROJString( io::PROJStringFormatter::create().get()); opTransfPROJStringValid = true; + opTransfPROJString = replaceAll( + opTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 " + "+convention=position_vector", + ""); } catch (const std::exception &) { } if ((refTransfPROJStringValid && opTransfPROJStringValid && refTransfPROJString == opTransfPROJString) || - op->_isEquivalentTo( - d->transformation_.get(), - util::IComparable::Criterion::EQUIVALENT)) { - res.emplace_back( + opNormalized->_isEquivalentTo( + refTransf.get(), + util::IComparable::Criterion::EQUIVALENT, + dbContext)) { + resMatchOfTransfToWGS84.emplace_back( create(candidateBaseCRS, d->hubCRS_, NN_NO_CHECK(util::nn_dynamic_pointer_cast< operation::Transformation>(op))), @@ -4596,7 +4680,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { } } } - return res; + return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res; } // --------------------------------------------------------------------------- @@ -4758,11 +4842,11 @@ void DerivedGeodeticCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedGeodeticCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedGeodeticCRS *>(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -4908,11 +4992,11 @@ void DerivedGeographicCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedGeographicCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedGeographicCRS *>(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5055,11 +5139,11 @@ void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- bool DerivedProjectedCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedProjectedCRS *>(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5185,11 +5269,11 @@ void TemporalCRS::_exportToJSON( // --------------------------------------------------------------------------- bool TemporalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTemporalCRS = dynamic_cast<const TemporalCRS *>(other); return otherTemporalCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5323,11 +5407,11 @@ void EngineeringCRS::_exportToJSON( // --------------------------------------------------------------------------- bool EngineeringCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherEngineeringCRS = dynamic_cast<const EngineeringCRS *>(other); return otherEngineeringCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5455,11 +5539,11 @@ void ParametricCRS::_exportToJSON( // --------------------------------------------------------------------------- bool ParametricCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherParametricCRS = dynamic_cast<const ParametricCRS *>(other); return otherParametricCRS != nullptr && - SingleCRS::baseIsEquivalentTo(other, criterion); + SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5560,11 +5644,11 @@ void DerivedVerticalCRS::_exportToPROJString( // --------------------------------------------------------------------------- bool DerivedVerticalCRS::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedVerticalCRS *>(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- @@ -5681,11 +5765,11 @@ void DerivedCRSTemplate<DerivedCRSTraits>::_exportToWKT( template <class DerivedCRSTraits> bool DerivedCRSTemplate<DerivedCRSTraits>::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast<const DerivedCRSTemplate *>(other); return otherDerivedCRS != nullptr && - DerivedCRS::_isEquivalentTo(other, criterion); + DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } //! @endcond diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index 321fe93f..255f9822 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -213,11 +213,13 @@ void Datum::setProperties( // --------------------------------------------------------------------------- -bool Datum::__isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { +//! @cond Doxygen_Suppress +bool Datum::_isEquivalentTo(const util::IComparable *other, + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDatum = dynamic_cast<const Datum *>(other); if (otherDatum == nullptr || - !ObjectUsage::_isEquivalentTo(other, criterion)) { + !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { @@ -248,12 +250,13 @@ bool Datum::__isEquivalentTo(const util::IComparable *other, } if (conventionalRS() && otherDatum->conventionalRS() && conventionalRS()->_isEquivalentTo( - otherDatum->conventionalRS().get(), criterion)) { + otherDatum->conventionalRS().get(), criterion, dbContext)) { return false; } } return true; } +//! @endcond // --------------------------------------------------------------------------- @@ -450,11 +453,11 @@ void PrimeMeridian::_exportToPROJString( //! @cond Doxygen_Suppress bool PrimeMeridian::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherPM = dynamic_cast<const PrimeMeridian *>(other); if (otherPM == nullptr || - !IdentifiedObject::_isEquivalentTo(other, criterion)) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // In MapInfo, the Paris prime meridian is returned as 2.3372291666667 @@ -984,11 +987,12 @@ EllipsoidNNPtr Ellipsoid::identify() const { //! @cond Doxygen_Suppress bool Ellipsoid::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherEllipsoid = dynamic_cast<const Ellipsoid *>(other); if (otherEllipsoid == nullptr || (criterion == util::IComparable::Criterion::STRICT && - !IdentifiedObject::_isEquivalentTo(other, criterion))) { + !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext))) { return false; } @@ -1324,20 +1328,63 @@ void GeodeticReferenceFrame::_exportToJSON( //! @cond Doxygen_Suppress bool GeodeticReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherGRF = dynamic_cast<const GeodeticReferenceFrame *>(other); - if (otherGRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherGRF == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return primeMeridian()->_isEquivalentTo(otherGRF->primeMeridian().get(), - criterion) && - ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion); + criterion, dbContext) && + ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion, + dbContext); } //! @endcond // --------------------------------------------------------------------------- +bool GeodeticReferenceFrame::hasEquivalentNameToUsingAlias( + const IdentifiedObject *other, + const io::DatabaseContextPtr &dbContext) const { + if (dbContext) { + if (!identifiers().empty()) { + const auto &id = identifiers().front(); + auto aliases = + dbContext->getAliases(*(id->codeSpace()), id->code(), nameStr(), + "geodetic_datum", std::string()); + const char *otherName = other->nameStr().c_str(); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName(otherName, + alias.c_str())) { + return true; + } + } + return false; + } else if (!other->identifiers().empty()) { + auto otherGRF = dynamic_cast<const GeodeticReferenceFrame *>(other); + if (otherGRF) { + return otherGRF->hasEquivalentNameToUsingAlias(this, dbContext); + } + return false; + } + + auto aliases = + dbContext->getAliases(std::string(), std::string(), nameStr(), + "geodetic_datum", std::string()); + const char *otherName = other->nameStr().c_str(); + for (const auto &alias : aliases) { + if (metadata::Identifier::isEquivalentName(otherName, + alias.c_str())) { + return true; + } + } + } + return false; +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress struct DynamicGeodeticReferenceFrame::Private { common::Measure frameReferenceEpoch{}; @@ -1407,11 +1454,11 @@ DynamicGeodeticReferenceFrame::deformationModelName() const { //! @cond Doxygen_Suppress bool DynamicGeodeticReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast<const DynamicGeodeticReferenceFrame *>(other); if (otherDGRF == nullptr || - !GeodeticReferenceFrame::_isEquivalentTo(other, criterion)) { + !GeodeticReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( @@ -1842,10 +1889,11 @@ void VerticalReferenceFrame::_exportToJSON( //! @cond Doxygen_Suppress bool VerticalReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherVRF = dynamic_cast<const VerticalReferenceFrame *>(other); - if (otherVRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherVRF == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } if ((realizationMethod().has_value() ^ @@ -1932,11 +1980,11 @@ DynamicVerticalReferenceFrame::deformationModelName() const { //! @cond Doxygen_Suppress bool DynamicVerticalReferenceFrame::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast<const DynamicVerticalReferenceFrame *>(other); if (otherDGRF == nullptr || - !VerticalReferenceFrame::_isEquivalentTo(other, criterion)) { + !VerticalReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( @@ -2131,10 +2179,11 @@ void TemporalDatum::_exportToJSON( //! @cond Doxygen_Suppress bool TemporalDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast<const TemporalDatum *>(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return temporalOrigin().toString() == @@ -2223,10 +2272,11 @@ void EngineeringDatum::_exportToJSON( //! @cond Doxygen_Suppress bool EngineeringDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast<const EngineeringDatum *>(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; @@ -2308,10 +2358,11 @@ void ParametricDatum::_exportToJSON( //! @cond Doxygen_Suppress bool ParametricDatum::_isEquivalentTo( - const util::IComparable *other, - util::IComparable::Criterion criterion) const { + const util::IComparable *other, util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast<const ParametricDatum *>(other); - if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) { + if (otherTD == nullptr || + !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index f0ad157e..0bd44308 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -262,6 +262,9 @@ struct DatabaseContext::Private { std::map<std::string, std::vector<std::string>> cacheAllowedAuthorities_{}; + lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{ + CACHE_SIZE}; + static void insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj); @@ -1100,7 +1103,7 @@ bool DatabaseContext::isKnownName(const std::string &name, /** \brief Gets the alias name from an official name. * - * @param officialName Official name. + * @param officialName Official name. Mandatory * @param tableName Table name/category. Mandatory * @param source Source of the alias. Mandatory * @return Alias name (or empty if not found). @@ -1132,6 +1135,63 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName, // --------------------------------------------------------------------------- +/** \brief Gets the alias names for an object. + * + * Either authName + code or officialName must be non empty. + * + * @param authName Authority. + * @param code Code. + * @param officialName Official name. + * @param tableName Table name/category. Mandatory + * @param source Source of the alias. May be empty. + * @return Aliases + */ +std::list<std::string> DatabaseContext::getAliases( + const std::string &authName, const std::string &code, + const std::string &officialName, const std::string &tableName, + const std::string &source) const { + + std::list<std::string> res; + const auto key(authName + code + officialName + tableName + source); + if (d->cacheAliasNames_.tryGet(key, res)) { + return res; + } + + std::string resolvedAuthName(authName); + std::string resolvedCode(code); + if (authName.empty() || code.empty()) { + std::string sql("SELECT auth_name, code FROM \""); + sql += replaceAll(tableName, "\"", "\"\""); + sql += "\" WHERE name = ?"; + if (tableName == "geodetic_crs") { + sql += " AND type = " GEOG_2D_SINGLE_QUOTED; + } + auto resSql = d->run(sql, {officialName}); + if (resSql.empty()) { + d->cacheAliasNames_.insert(key, res); + return res; + } + const auto &row = resSql.front(); + resolvedAuthName = row[0]; + resolvedCode = row[1]; + } + std::string sql("SELECT alt_name FROM alias_name WHERE table_name = ? AND " + "auth_name = ? AND code = ?"); + ListOfParams params{tableName, resolvedAuthName, resolvedCode}; + if (!source.empty()) { + sql += " AND source = ?"; + params.emplace_back(source); + } + auto resSql = d->run(sql, params); + for (const auto &row : resSql) { + res.emplace_back(row[0]); + } + d->cacheAliasNames_.insert(key, res); + return res; +} + +// --------------------------------------------------------------------------- + /** \brief Return the 'text_definition' column of a table for an object * * @param tableName Table name/category. diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index e0e6152a..a67238d9 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -2683,7 +2683,9 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) { auto cs = buildCS(csNode, node, angularUnit); auto ellipsoidalCS = nn_dynamic_pointer_cast<EllipsoidalCS>(cs); if (ellipsoidalCS) { - assert(!ci_equal(nodeName, WKTConstants::GEOCCS)); + if (ci_equal(nodeName, WKTConstants::GEOCCS)) { + throw ParsingException("ellipsoidal CS not expected in GEOCCS"); + } try { auto crs = GeographicCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(ellipsoidalCS)); @@ -5673,6 +5675,14 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, try { return factory->createCoordinateReferenceSystem(code); } catch (...) { + + // Convenience for well-known misused code + // See https://github.com/OSGeo/PROJ/issues/1730 + if (ci_equal(authName, "EPSG") && code == "102100") { + factory = AuthorityFactory::create(dbContextNNPtr, "ESRI"); + return factory->createCoordinateReferenceSystem(code); + } + const auto authorities = dbContextNNPtr->getAuthorities(); for (const auto &authCandidate : authorities) { if (ci_equal(authCandidate, authName)) { @@ -7154,6 +7164,12 @@ void PROJStringFormatter::stopInversion() { // the current end of steps for (auto iter = startIter; iter != d->steps_.end(); ++iter) { iter->inverted = !iter->inverted; + for (auto ¶mValue : iter->paramValues) { + if (paramValue.key == "omit_fwd") + paramValue.key = "omit_inv"; + else if (paramValue.key == "omit_inv") + paramValue.key = "omit_fwd"; + } } // And reverse the order of steps in that range as well. std::reverse(startIter, d->steps_.end()); diff --git a/src/iso19111/metadata.cpp b/src/iso19111/metadata.cpp index 41653b32..6266a86d 100644 --- a/src/iso19111/metadata.cpp +++ b/src/iso19111/metadata.cpp @@ -237,7 +237,8 @@ GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west, //! @cond Doxygen_Suppress bool GeographicBoundingBox::_isEquivalentTo( - const util::IComparable *other, util::IComparable::Criterion) const { + const util::IComparable *other, util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other); if (!otherExtent) return false; @@ -502,7 +503,8 @@ VerticalExtent::create(double minimumIn, double maximumIn, //! @cond Doxygen_Suppress bool VerticalExtent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion) const { + util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast<const VerticalExtent *>(other); if (!otherExtent) return false; @@ -587,7 +589,8 @@ TemporalExtentNNPtr TemporalExtent::create(const std::string &start, //! @cond Doxygen_Suppress bool TemporalExtent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion) const { + util::IComparable::Criterion, + const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast<const TemporalExtent *>(other); if (!otherExtent) return false; @@ -734,7 +737,8 @@ Extent::createFromBBOX(double west, double south, double east, double north, //! @cond Doxygen_Suppress bool Extent::_isEquivalentTo(const util::IComparable *other, - util::IComparable::Criterion criterion) const { + util::IComparable::Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { auto otherExtent = dynamic_cast<const Extent *>(other); bool ret = (otherExtent && @@ -749,15 +753,18 @@ bool Extent::_isEquivalentTo(const util::IComparable *other, if (ret) { for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) { ret = d->geographicElements_[i]->_isEquivalentTo( - otherExtent->d->geographicElements_[i].get(), criterion); + otherExtent->d->geographicElements_[i].get(), criterion, + dbContext); } for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) { ret = d->verticalElements_[i]->_isEquivalentTo( - otherExtent->d->verticalElements_[i].get(), criterion); + otherExtent->d->verticalElements_[i].get(), criterion, + dbContext); } for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) { ret = d->temporalElements_[i]->_isEquivalentTo( - otherExtent->d->temporalElements_[i].get(), criterion); + otherExtent->d->temporalElements_[i].get(), criterion, + dbContext); } } return ret; diff --git a/src/iso19111/util.cpp b/src/iso19111/util.cpp index 25207d5c..2a6178e2 100644 --- a/src/iso19111/util.cpp +++ b/src/iso19111/util.cpp @@ -687,11 +687,13 @@ IComparable::~IComparable() = default; /** \brief Returns whether an object is equivalent to another one. * @param other other object to compare to * @param criterion comparaison criterion. + * @param dbContext Database context, or nullptr. * @return true if objects are equivalent. */ -bool IComparable::isEquivalentTo(const IComparable *other, - Criterion criterion) const { - return _isEquivalentTo(other, criterion); +bool IComparable::isEquivalentTo( + const IComparable *other, Criterion criterion, + const io::DatabaseContextPtr &dbContext) const { + return _isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index d1bc8836..bc27bafe 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -443,7 +443,7 @@ endif() include_directories(${SQLITE3_INCLUDE_DIR}) target_link_libraries(${PROJ_CORE_TARGET} ${SQLITE3_LIBRARY}) -if(MSVC) +if(MSVC AND BUILD_LIBPROJ_SHARED) target_compile_definitions(${PROJ_CORE_TARGET} PRIVATE PROJ_MSVC_DLL_EXPORT=1) endif() diff --git a/src/nad_cvt.cpp b/src/nad_cvt.cpp index 79441d0a..e8b8e9b7 100644 --- a/src/nad_cvt.cpp +++ b/src/nad_cvt.cpp @@ -7,10 +7,12 @@ #include "proj_internal.h" #include <math.h> +#include <limits> + #define MAX_ITERATIONS 10 #define TOL 1e-12 -PJ_LP nad_cvt(PJ_LP in, int inverse, struct CTABLE *ct) { +PJ_LP nad_cvt(projCtx ctx, PJ_LP in, int inverse, struct CTABLE *ct, int grid_count, PJ_GRIDINFO **tables) { PJ_LP t, tb,del, dif; int i = MAX_ITERATIONS; const double toltol = TOL*TOL; @@ -40,18 +42,37 @@ PJ_LP nad_cvt(PJ_LP in, int inverse, struct CTABLE *ct) { do { del = nad_intr(t, ct); - /* This case used to return failure, but I have - changed it to return the first order approximation - of the inverse shift. This avoids cases where the - grid shift *into* this grid came from another grid. - While we aren't returning optimally correct results - I feel a close result in this case is better than - no result. NFW - To demonstrate use -112.5839956 49.4914451 against - the NTv2 grid shift file from Canada. */ - if (del.lam == HUGE_VAL) + /* In case we are (or have switched) on the null grid, exit now */ + if( del.lam == 0 && del.phi == 0 ) break; + /* We can possibly go outside of the initial guessed grid, so try */ + /* to fetch a new grid into which iterate... */ + if (del.lam == HUGE_VAL) + { + if( grid_count == 0 ) + break; + PJ_LP lp; + lp.lam = t.lam + ct->ll.lam; + lp.phi = t.phi + ct->ll.phi; + auto newCt = find_ctable(ctx, lp, grid_count, tables); + if( newCt == nullptr || newCt == ct ) + break; + pj_log(ctx, PJ_LOG_DEBUG_MINOR, "Switching from grid %s to grid %s", + ct->id, + newCt->id); + ct = newCt; + t.lam = lp.lam - ct->ll.lam; + t.phi = lp.phi - ct->ll.phi; + tb = in; + tb.lam -= ct->ll.lam; + tb.phi -= ct->ll.phi; + tb.lam = adjlon (tb.lam - M_PI) + M_PI; + dif.lam = std::numeric_limits<double>::max(); + dif.phi = std::numeric_limits<double>::max(); + continue; + } + dif.lam = t.lam - del.lam - tb.lam; dif.phi = t.phi + del.phi - tb.phi; t.lam -= dif.lam; @@ -836,6 +836,10 @@ PJ_OBJ_LIST PROJ_DLL *proj_get_non_deprecated(PJ_CONTEXT *ctx, int PROJ_DLL proj_is_equivalent_to(const PJ *obj, const PJ *other, PJ_COMPARISON_CRITERION criterion); +int PROJ_DLL proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, + const PJ *obj, const PJ *other, + PJ_COMPARISON_CRITERION criterion); + int PROJ_DLL proj_is_crs(const PJ *obj); const char PROJ_DLL* proj_get_name(const PJ *obj); @@ -1091,6 +1095,9 @@ int PROJ_DLL proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx, int value_count, int emit_error_if_incompatible); +PJ PROJ_DLL *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj); + + int PROJ_DLL proj_concatoperation_get_step_count(PJ_CONTEXT *ctx, const PJ *concatoperation); diff --git a/src/proj_internal.h b/src/proj_internal.h index 3ca927a3..3e219682 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -863,8 +863,10 @@ int pj_deriv(PJ_LP, double, const PJ *, struct DERIVS *); int pj_factors(PJ_LP, const PJ *, double, struct FACTORS *); /* nadcon related protos */ +struct CTABLE* find_ctable(projCtx_t *ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables); + PJ_LP nad_intr(PJ_LP, struct CTABLE *); -PJ_LP nad_cvt(PJ_LP, int, struct CTABLE *); +PJ_LP nad_cvt(projCtx_t *ctx, PJ_LP in, int inverse, struct CTABLE *ct, int grid_count, PJ_GRIDINFO **tables); struct CTABLE *nad_init(projCtx_t *ctx, char *); struct CTABLE *nad_ctable_init( projCtx_t *ctx, struct projFileAPI_t* fid ); int nad_ctable_load( projCtx_t *ctx, struct CTABLE *, struct projFileAPI_t* fid ); diff --git a/src/transform.cpp b/src/transform.cpp index d111d835..781c0061 100644 --- a/src/transform.cpp +++ b/src/transform.cpp @@ -231,6 +231,14 @@ static int geographic_to_projected (PJ *P, long n, int dist, double *x, double * return 0; } + // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 + if( P->right == PJ_IO_UNITS_WHATEVER && P->descr && + strncmp(P->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + P->right = PJ_IO_UNITS_PROJECTED; + } + for( i = 0; i <n; i++ ) { PJ_XY projected_loc; @@ -342,6 +350,14 @@ static int projected_to_geographic (PJ *P, long n, int dist, double *x, double * return 0; } + // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 + if( P->right == PJ_IO_UNITS_WHATEVER && P->descr && + strncmp(P->descr, "General Oblique Transformation", + strlen("General Oblique Transformation")) == 0 ) + { + P->right = PJ_IO_UNITS_PROJECTED; + } + /* Fallback to the original PROJ.4 API 2d inversion - inv */ for( i = 0; i < n; i++ ) { PJ_XY projected_loc; diff --git a/test/cli/CMakeLists.txt b/test/cli/CMakeLists.txt index 4e1ab75a..feef5bf0 100644 --- a/test/cli/CMakeLists.txt +++ b/test/cli/CMakeLists.txt @@ -7,6 +7,7 @@ set(PROJINFO_BIN "projinfo") set(CCT_BIN "cct") proj_add_test_script_sh("test27" PROJ_BIN) proj_add_test_script_sh("test83" PROJ_BIN) +proj_add_test_script_sh("testproj" PROJ_BIN) proj_add_test_script_sh("testvarious" CS2CS_BIN) proj_add_test_script_sh("testdatumfile" CS2CS_BIN "connu") proj_add_test_script_sh("testIGNF" CS2CS_BIN "ntf_r93.gsb") diff --git a/test/cli/Makefile.am b/test/cli/Makefile.am index 758352c6..fb7169e4 100644 --- a/test/cli/Makefile.am +++ b/test/cli/Makefile.am @@ -10,6 +10,7 @@ CCTEXE = $(EXEPATH)/cct # PROJ.4 test scripts TEST27 = $(THIS_DIR)/test27 TEST83 = $(THIS_DIR)/test83 +TESTPROJ = $(THIS_DIR)/testproj TESTNTV2 = $(THIS_DIR)/testntv2 TESTVARIOUS = $(THIS_DIR)/testvarious TESTFLAKY = $(THIS_DIR)/testflaky @@ -24,6 +25,7 @@ EXTRA_DIST = pj_out27.dist pj_out83.dist td_out.dist \ testIGNF proj_outIGNF.dist \ testprojinfo testprojinfo_out.dist \ testcct testcct_out.dist \ + testproj testproj_out.dist \ CMakeLists.txt testprojinfo-check: @@ -35,6 +37,9 @@ test27-check: test83-check: $(TEST83) $(PROJEXE) +testproj-check: + $(TESTPROJ) $(PROJEXE) + testvarious-check: PROJ_LIB=$(PROJ_LIB) $(TESTVARIOUS) $(CS2CSEXE) @@ -56,4 +61,4 @@ testntv2-check: testcct-check: PROJ_LIB=$(PROJ_LIB) $(TESTCCT) $(CCTEXE) -check-local: testprojinfo-check test27-check test83-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check +check-local: testprojinfo-check test27-check test83-check testproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check diff --git a/test/cli/ntv2_out.dist b/test/cli/ntv2_out.dist index 9a97f9cf..650a69d8 100644 --- a/test/cli/ntv2_out.dist +++ b/test/cli/ntv2_out.dist @@ -9,3 +9,6 @@ Try with NTv2 and NTv1 together ... falls back to NTv1 99d00'00.000"W 65d00'00.000"N 0.0 99d0'1.5885"W 65d0'1.3482"N 0.000 111d00'00.000"W 46d00'00.000"N 0.0 111d0'3.1897"W 45d59'59.7489"N 0.000 111d00'00.000"W 47d30'00.000"N 0.0 111d0'2.7989"W 47d29'59.9896"N 0.000 +############################################################## +Switching between NTv2 subgrids +-112.5839956 49.4914451 0 -112.58307487 49.49145197 0.00000000 diff --git a/test/cli/testntv2 b/test/cli/testntv2 index 73371dbe..2a31304e 100755 --- a/test/cli/testntv2 +++ b/test/cli/testntv2 @@ -52,6 +52,14 @@ $EXE +proj=latlong +ellps=clrk66 +nadgrids=ntv2_0.gsb,ntv1_can.dat,conus \ 111d00'00.000"W 46d00'00.000"N 0.0 111d00'00.000"W 47d30'00.000"N 0.0 EOF + +echo "##############################################################" >> ${OUT} +echo Switching between NTv2 subgrids >> ${OUT} +# Initial guess is in ALraymnd, going to parent CAwest afterwards +$EXE +proj=latlong +datum=NAD83 +to +proj=latlong +ellps=clrk66 +nadgrids=ntv2_0.gsb -E -d 8 >>${OUT} <<EOF +-112.5839956 49.4914451 0 +EOF + # ############################################################################## # Done! diff --git a/test/cli/testproj b/test/cli/testproj new file mode 100755 index 00000000..8686224e --- /dev/null +++ b/test/cli/testproj @@ -0,0 +1,55 @@ +: +# Script to test proj exe +# +TEST_CLI_DIR=`dirname $0` +EXE=$1 + +usage() +{ + echo "Usage: ${0} <path to 'proj' program>" + echo + exit 1 +} + +if test -z "${EXE}"; then + EXE=../../src/cs2cs +fi + +if test ! -x ${EXE}; then + echo "*** ERROR: Can not find '${EXE}' program!" + exit 1 +fi + +if test -z "${PROJ_LIB}"; then + export PROJ_LIB="`dirname $0`/../../data" +fi + +echo "============================================" +echo "Running ${0} using ${EXE}:" +echo "============================================" + +OUT=testproj_out +# +echo "doing tests into file ${OUT}, please wait" +# +$EXE +ellps=WGS84 +proj=ob_tran +o_proj=latlon +o_lon_p=0.0 +o_lat_p=90.0 +lon_0=360.0 +to_meter=0.0174532925199433 +no_defs -E -f '%.3f' >${OUT} <<EOF +2 49 +EOF + +# +# do 'diff' with distribution results +echo "diff ${OUT} with testproj_out.dist" +diff -u -b ${OUT} ${TEST_CLI_DIR}/testproj_out.dist +if [ $? -ne 0 ] ; then + echo "" + echo "PROBLEMS HAVE OCCURRED" + echo "test file ${OUT} saved" + echo + exit 100 +else + echo "TEST OK" + echo "test file ${OUT} removed" + echo + /bin/rm -f ${OUT} + exit 0 +fi diff --git a/test/cli/testproj_out.dist b/test/cli/testproj_out.dist new file mode 100644 index 00000000..fad0a014 --- /dev/null +++ b/test/cli/testproj_out.dist @@ -0,0 +1 @@ +2 49 2.000 49.000 diff --git a/test/unit/pj_transform_test.cpp b/test/unit/pj_transform_test.cpp index b3a061b4..1f4473c1 100644 --- a/test/unit/pj_transform_test.cpp +++ b/test/unit/pj_transform_test.cpp @@ -614,4 +614,38 @@ TEST(proj_api_h, pj_set_finder) { pj_set_finder(nullptr); } +// --------------------------------------------------------------------------- + +TEST(pj_transform_test, ob_tran_to_meter_as_dest) { + auto src = pj_init_plus( + "+ellps=WGS84 +a=57.29577951308232 +proj=eqc +lon_0=0.0 +no_defs"); + auto dst = pj_init_plus("+ellps=WGS84 +proj=ob_tran +o_proj=latlon " + "+o_lon_p=0.0 +o_lat_p=90.0 +lon_0=360.0 " + "+to_meter=0.0174532925199433 +no_defs"); + double x = 2 * DEG_TO_RAD; + double y = 49 * DEG_TO_RAD; + EXPECT_EQ(pj_transform(src, dst, 1, 0, &x, &y, nullptr), 0); + EXPECT_NEAR(x, 2 * DEG_TO_RAD, 1e-12) << x / DEG_TO_RAD; + EXPECT_NEAR(y, 49 * DEG_TO_RAD, 1e-12) << y / DEG_TO_RAD; + pj_free(src); + pj_free(dst); +} + +// --------------------------------------------------------------------------- + +TEST(pj_transform_test, ob_tran_to_meter_as_srouce) { + auto src = pj_init_plus("+ellps=WGS84 +proj=ob_tran +o_proj=latlon " + "+o_lon_p=0.0 +o_lat_p=90.0 +lon_0=360.0 " + "+to_meter=0.0174532925199433 +no_defs"); + auto dst = pj_init_plus( + "+ellps=WGS84 +a=57.29577951308232 +proj=eqc +lon_0=0.0 +no_defs"); + double x = 2 * DEG_TO_RAD; + double y = 49 * DEG_TO_RAD; + EXPECT_EQ(pj_transform(src, dst, 1, 0, &x, &y, nullptr), 0); + EXPECT_NEAR(x, 2 * DEG_TO_RAD, 1e-12) << x / DEG_TO_RAD; + EXPECT_NEAR(y, 49 * DEG_TO_RAD, 1e-12) << y / DEG_TO_RAD; + pj_free(src); + pj_free(dst); +} + } // namespace diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index f87f6589..78e6bced 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -3486,6 +3486,36 @@ TEST_F(CApi, proj_normalize_for_visualization_on_crs) { // --------------------------------------------------------------------------- +TEST_F(CApi, proj_coordoperation_create_inverse) { + + auto P = proj_create( + m_ctxt, "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push " + "+v_3 +step +proj=cart +ellps=evrst30 +step +proj=helmert " + "+x=293 +y=836 +z=318 +rx=0.5 +ry=1.6 +rz=-2.8 +s=2.1 " + "+convention=position_vector +step +inv +proj=cart " + "+ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + auto Pinversed = proj_coordoperation_create_inverse(m_ctxt, P); + ObjectKeeper keeper_Pinversed(Pinversed); + ASSERT_NE(Pinversed, nullptr); + + auto projstr = proj_as_proj_string(m_ctxt, Pinversed, PJ_PROJ_5, nullptr); + ASSERT_NE(projstr, nullptr); + EXPECT_EQ(std::string(projstr), + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 " + "+step +proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=293 " + "+y=836 +z=318 +rx=0.5 +ry=1.6 +rz=-2.8 +s=2.1 " + "+convention=position_vector +step +inv +proj=cart " + "+ellps=evrst30 +step +proj=pop +v_3 +step +proj=unitconvert " + "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + TEST_F(CApi, proj_get_remarks) { auto co = proj_create_from_database(m_ctxt, "EPSG", "8048", PJ_CATEGORY_COORDINATE_OPERATION, false, @@ -4434,4 +4464,35 @@ TEST_F(CApi, proj_create_derived_geographic_crs) { "+o_lat_p=-2 +lon_0=3 +datum=WGS84 +no_defs " "+type=crs")); } + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_is_equivalent_to_with_ctx) { + auto from_epsg = proj_create_from_database(m_ctxt, "EPSG", "7844", + PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper_from_epsg(from_epsg); + ASSERT_NE(from_epsg, nullptr); + + auto wkt = "GEOGCRS[\"GDA2020\",\n" + " DATUM[\"GDA2020\",\n" + " ELLIPSOID[\"GRS_1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"Degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]]"; + auto from_wkt = + proj_create_from_wkt(m_ctxt, wkt, nullptr, nullptr, nullptr); + ObjectKeeper keeper_from_wkt(from_wkt); + EXPECT_NE(from_wkt, nullptr); + + EXPECT_TRUE(proj_is_equivalent_to_with_ctx(m_ctxt, from_epsg, from_wkt, + PJ_COMP_EQUIVALENT)); +} + } // namespace diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index e896853f..88a7813a 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -57,7 +57,8 @@ namespace { struct UnrelatedObject : public IComparable { UnrelatedObject() = default; - bool _isEquivalentTo(const IComparable *, Criterion) const override { + bool _isEquivalentTo(const IComparable *, Criterion, + const DatabaseContextPtr &) const override { assert(false); return false; } @@ -943,6 +944,16 @@ TEST(crs, EPSG_32661_projected_north_pole_north_east) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -964,6 +975,16 @@ TEST(crs, EPSG_5041_projected_north_pole_east_north) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -985,6 +1006,16 @@ TEST(crs, EPSG_32761_projected_south_pole_north_east) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=-90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -1006,6 +1037,16 @@ TEST(crs, EPSG_5042_projected_south_pole_east_north) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=-90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -1511,6 +1552,75 @@ TEST(crs, geodeticcrs_identify_db) { EXPECT_EQ(res.front().first->identifiers()[0]->code(), "4326"); EXPECT_EQ(res.front().second, 70); } + + { + // Identify by code, but datum name is an alias of the official one + auto wkt = "GEOGCRS[\"GDA2020\",\n" + " DATUM[\"GDA2020\",\n" + " ELLIPSOID[\"GRS_1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"Degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",7844]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto allFactory = AuthorityFactory::create(dbContext, std::string()); + auto res = crs->identify(allFactory); + ASSERT_EQ(res.size(), 1U); + ASSERT_TRUE(!res.front().first->identifiers().empty()); + EXPECT_EQ(*res.front().first->identifiers()[0]->codeSpace(), "EPSG"); + EXPECT_EQ(res.front().first->identifiers()[0]->code(), "7844"); + EXPECT_EQ(res.front().second, 100); + + EXPECT_TRUE(crs->_isEquivalentTo(res.front().first.get(), + IComparable::Criterion::EQUIVALENT, + dbContext)); + EXPECT_TRUE(res.front().first->_isEquivalentTo( + crs.get(), IComparable::Criterion::EQUIVALENT, dbContext)); + } + + { + // Identify by name, but datum name is an alias of the official one + auto wkt = "GEOGCRS[\"GDA2020\",\n" + " DATUM[\"GDA2020\",\n" + " ELLIPSOID[\"GRS_1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"Degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<GeodeticCRS>(obj); + ASSERT_TRUE(crs != nullptr); + + auto allFactory = AuthorityFactory::create(dbContext, std::string()); + auto res = crs->identify(allFactory); + ASSERT_EQ(res.size(), 1U); + ASSERT_TRUE(!res.front().first->identifiers().empty()); + EXPECT_EQ(*res.front().first->identifiers()[0]->codeSpace(), "EPSG"); + EXPECT_EQ(res.front().first->identifiers()[0]->code(), "7844"); + EXPECT_EQ(res.front().second, 100); + + EXPECT_TRUE(crs->_isEquivalentTo(res.front().first.get(), + IComparable::Criterion::EQUIVALENT, + dbContext)); + EXPECT_TRUE(res.front().first->_isEquivalentTo( + crs.get(), IComparable::Criterion::EQUIVALENT, dbContext)); + } } // --------------------------------------------------------------------------- @@ -3785,6 +3895,21 @@ TEST(crs, boundCRS_identify_db) { WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); EXPECT_TRUE(wkt.find("32122") != std::string::npos) << wkt; } + + { + // Identify from a PROJ string with +towgs84 + auto obj = PROJStringParser().createFromPROJString( + "+proj=utm +zone=48 +a=6377276.345 +b=6356075.41314024 " + "+towgs84=198,881,317,0,0,0,0 +units=m +no_defs +type=crs"); + auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + auto res = crs->identify(factoryEPSG); + ASSERT_EQ(res.size(), 1U); + auto boundCRS = dynamic_cast<const BoundCRS *>(res.front().first.get()); + ASSERT_TRUE(boundCRS != nullptr); + EXPECT_EQ(boundCRS->baseCRS()->getEPSGCode(), 3148); + EXPECT_EQ(res.front().second, 70); + } } // --------------------------------------------------------------------------- diff --git a/test/unit/test_datum.cpp b/test/unit/test_datum.cpp index 8a5d67cf..adf0ae4b 100644 --- a/test/unit/test_datum.cpp +++ b/test/unit/test_datum.cpp @@ -44,7 +44,8 @@ namespace { struct UnrelatedObject : public IComparable { UnrelatedObject() = default; - bool _isEquivalentTo(const IComparable *, Criterion) const override { + bool _isEquivalentTo(const IComparable *, Criterion, + const DatabaseContextPtr &) const override { assert(false); return false; } diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 38dfc2b4..4242a15c 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -5577,6 +5577,17 @@ TEST(wkt_parse, invalid_GEOCCS) { "NORTH],AXIS[\"longitude\",EAST]]"), ParsingException); + // ellipsoidal CS is invalid in a GEOCCS + EXPECT_THROW(WKTParser().createFromWKT( + "GEOCCS[\"WGS 84\",DATUM[\"World Geodetic System 1984\"," + "ELLIPSOID[\"WGS 84\",6378274,298.257223564," + "LENGTHUNIT[\"metre\",1]]]," + "CS[ellipsoidal,2],AXIS[\"geodetic latitude (Lat)\",north," + "ANGLEUNIT[\"degree\",0.0174532925199433]]," + "AXIS[\"geodetic longitude (Lon)\",east," + "ANGLEUNIT[\"degree\",0.0174532925199433]]]"), + ParsingException); + // 3 axis required EXPECT_THROW(WKTParser().createFromWKT( "GEOCCS[\"x\",DATUM[\"x\",SPHEROID[\"x\",1,0.5]],PRIMEM[" @@ -6856,6 +6867,30 @@ TEST(io, projstringformatter_optim_hgridshift_vgridshift_hgridshift_inv) { "+step +proj=pop +v_1 +v_2"); } + // Test omit_fwd->omit_inv when inversing the pipeline + { + auto fmt = PROJStringFormatter::create(); + fmt->startInversion(); + fmt->ingestPROJString("+proj=hgridshift +grids=foo +omit_fwd"); + fmt->stopInversion(); + + EXPECT_EQ(fmt->toString(), + "+proj=pipeline " + "+step +inv +proj=hgridshift +grids=foo +omit_inv"); + } + + // Test omit_inv->omit_fwd when inversing the pipeline + { + auto fmt = PROJStringFormatter::create(); + fmt->startInversion(); + fmt->ingestPROJString("+proj=hgridshift +grids=foo +omit_inv"); + fmt->stopInversion(); + + EXPECT_EQ(fmt->toString(), + "+proj=pipeline " + "+step +inv +proj=hgridshift +grids=foo +omit_fwd"); + } + // Variant with first hgridshift inverted, and second forward { auto fmt = PROJStringFormatter::create(); @@ -9324,6 +9359,20 @@ TEST(io, createFromUserInput) { // --------------------------------------------------------------------------- +TEST(io, createFromUserInput_hack_EPSG_102100) { + auto dbContext = DatabaseContext::create(); + auto obj = createFromUserInput("EPSG:102100", dbContext); + auto crs = nn_dynamic_pointer_cast<CRS>(obj); + ASSERT_TRUE(crs != nullptr); + const auto &ids = crs->identifiers(); + ASSERT_EQ(ids.size(), 1U); + // we do not lie on the real authority + EXPECT_EQ(*ids[0]->codeSpace(), "ESRI"); + EXPECT_EQ(ids[0]->code(), "102100"); +} + +// --------------------------------------------------------------------------- + TEST(io, guessDialect) { EXPECT_EQ(WKTParser().guessDialect("LOCAL_CS[\"foo\"]"), WKTParser::WKTGuessedDialect::WKT1_GDAL); diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 9cde8822..d503034c 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -65,7 +65,8 @@ namespace { struct UnrelatedObject : public IComparable { UnrelatedObject() = default; - bool _isEquivalentTo(const IComparable *, Criterion) const override { + bool _isEquivalentTo(const IComparable *, Criterion, + const DatabaseContextPtr &) const override { assert(false); return false; } @@ -4628,6 +4629,33 @@ TEST(operation, geogCRS_to_geogCRS_context_WGS84_G1674_to_WGS84_G1762) { // --------------------------------------------------------------------------- +TEST(operation, geogCRS_to_geogCRS_context_EPSG_4240_Indian1975_to_EPSG_4326) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + + auto list = CoordinateOperationFactory::create()->createOperations( + authFactory->createCoordinateReferenceSystem("4240"), // Indian 1975 + authFactory->createCoordinateReferenceSystem("4326"), ctxt); + ASSERT_EQ(list.size(), 3U); + + // Indian 1975 to WGS 84 (4), 3.0 m, Thailand - onshore + EXPECT_EQ(list[0]->getEPSGCode(), 1812); + + // The following is the one we want to see. It has a lesser accuracy than + // the above one and the same bbox, but the name of its area of use is + // slightly different + // Indian 1975 to WGS 84 (2), 5.0 m, Thailand - onshore and Gulf of Thailand + EXPECT_EQ(list[1]->getEPSGCode(), 1304); + + // Indian 1975 to WGS 84 (3), 1.0 m, Thailand - Bongkot field + EXPECT_EQ(list[2]->getEPSGCode(), 1537); +} + +// --------------------------------------------------------------------------- + TEST(operation, vertCRS_to_geogCRS_context) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); diff --git a/travis/osx/before_install.sh b/travis/osx/before_install.sh index 09a7a74f..5e2382f8 100755 --- a/travis/osx/before_install.sh +++ b/travis/osx/before_install.sh @@ -8,7 +8,7 @@ brew update brew install ccache brew install sqlite3 brew install doxygen -brew install md5sha1sum +#brew install md5sha1sum brew reinstall python brew reinstall wget |
