diff options
| author | Even Rouault <even.rouault@mines-paris.org> | 2019-04-23 21:54:29 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-04-23 21:54:29 +0200 |
| commit | 4c7364ba84b97fc87270386906af66522feea94f (patch) | |
| tree | 7b721bd91cc8a45d4e7b245c9db36a4fd0ef8688 | |
| parent | 8cfd9ca89c89b52a3c22d5ee7ecbe06791924a15 (diff) | |
| parent | 292807eee9e1194175b64cb5c0a9f0b432352abc (diff) | |
| download | PROJ-4c7364ba84b97fc87270386906af66522feea94f.tar.gz PROJ-4c7364ba84b97fc87270386906af66522feea94f.zip | |
Merge pull request #1441 from rouault/support_compound_crs_urn
proj_create(): add support for compoundCRS and concatenatedOperation named from their components
| -rw-r--r-- | docs/source/apps/projinfo.rst | 17 | ||||
| -rw-r--r-- | docs/source/development/reference/functions.rst | 17 | ||||
| -rw-r--r-- | include/proj/io.hpp | 8 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 5 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 102 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 58 |
6 files changed, 190 insertions, 17 deletions
diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index c8219838..955b9a18 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -29,12 +29,17 @@ Synopsis | {object_definition} | (-s {srs_def} -t {srs_def}) | - where {object_definition} or {object_definition} is a PROJ string, a - WKT string, an object name, AUTHORITY:CODE - (where AUTHORITY is the name of a CRS authority and CODE the code of a CRS - found in the proj.db database) or a OGC URN (such as "urn:ogc:def:crs:EPSG::4326", - "urn:ogc:def:coordinateOperation:EPSG::1671", "urn:ogc:def:ellipsoid:EPSG::7001" - or "urn:ogc:def:datum:EPSG::6326") + where {object_definition} or {srs_def} is + + - a proj-string, + - a WKT string, + - an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", + "urn:ogc:def:coordinateOperation:EPSG::1671"), + - a OGC URN combining references for compound coordinate reference systems + (e.g "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" or custom abbreviated + syntax "EPSG:2393+5717"), + - a OGC URN combining references for concatenated operations + (e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618") Description *********** diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index 00653ad9..aa177b31 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -29,9 +29,17 @@ paragraph for more details. .. c:function:: PJ* proj_create(PJ_CONTEXT *ctx, const char *definition) - Create a transformation object, or a CRS object, from a proj-string, - a WKT string, or object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", - "urn:ogc:def:coordinateOperation:EPSG::1671"). + Create a transformation object, or a CRS object, from: + + - a proj-string, + - a WKT string, + - an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", + "urn:ogc:def:coordinateOperation:EPSG::1671"), + - a OGC URN combining references for compound coordinate reference systems + (e.g "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" or custom abbreviated + syntax "EPSG:2393+5717"), + - a OGC URN combining references for concatenated operations + (e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618") Example call: @@ -110,7 +118,8 @@ paragraph for more details. - the name of a CRS as found in the PROJ database, e.g "WGS84", "NAD27", etc. - - more generally any string accepted by :c:func:`proj_create` + - more generally any string accepted by :c:func:`proj_create` representing + a CRS An "area of use" can be specified in area. When it is supplied, the more accurate transformation between two given systems can be chosen. diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 5386ca6c..a603533e 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -993,16 +993,16 @@ class PROJ_GCC_DLL AuthorityFactory { PROJ_INTERNAL std::list<crs::CompoundCRSNNPtr> createCompoundCRSFromExisting(const crs::CompoundCRSNNPtr &crs) const; + + PROJ_INTERNAL crs::CRSNNPtr + createCoordinateReferenceSystem(const std::string &code, + bool allowCompound) const; //! @endcond protected: PROJ_INTERNAL AuthorityFactory(const DatabaseContextNNPtr &context, const std::string &authorityName); - PROJ_INTERNAL crs::CRSNNPtr - createCoordinateReferenceSystem(const std::string &code, - bool allowCompound) const; - PROJ_INTERNAL crs::GeodeticCRSNNPtr createGeodeticCRS(const std::string &code, bool geographicOnly) const; diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index f24b3457..9f7467a2 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2484,6 +2484,8 @@ crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem( return createCoordinateReferenceSystem(code, true); } +//! @cond Doxygen_Suppress + crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, bool allowCompound) const { @@ -2513,6 +2515,9 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, } throw FactoryException("unhandled CRS type: " + type); } + +//! @endcond + // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 360d55a2..d8282bb0 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -4397,15 +4397,97 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, const auto authorities = dbContextNNPtr->getAuthorities(); for (const auto &authCandidate : authorities) { if (ci_equal(authCandidate, authName)) { - return AuthorityFactory::create(dbContextNNPtr, - authCandidate) - ->createCoordinateReferenceSystem(code); + factory = + AuthorityFactory::create(dbContextNNPtr, authCandidate); + try { + return factory->createCoordinateReferenceSystem(code); + } catch (...) { + // EPSG:4326+3855 + auto tokensCode = split(code, '+'); + if (tokensCode.size() == 2) { + auto crs1(factory->createCoordinateReferenceSystem( + tokensCode[0], false)); + auto crs2(factory->createCoordinateReferenceSystem( + tokensCode[1], false)); + return CompoundCRS::create( + util::PropertyMap().set( + IdentifiedObject::NAME_KEY, + crs1->nameStr() + " + " + crs2->nameStr()), + {crs1, crs2}); + } + throw; + } } } throw; } } + // OGC 07-092r2: para 7.5.2 + // URN combined references for compound coordinate reference systems + if (starts_with(text, "urn:ogc:def:crs,")) { + if (!dbContext) { + throw ParsingException("no database context specified"); + } + auto tokensComma = split(text, ','); + std::vector<CRSNNPtr> components; + std::string name; + for (size_t i = 1; i < tokensComma.size(); i++) { + tokens = split(tokensComma[i], ':'); + if (tokens.size() != 4) { + throw ParsingException( + concat("invalid crs component: ", tokensComma[i])); + } + const auto &type = tokens[0]; + auto factory = + AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); + const auto &code = tokens[3]; + if (type == "crs") { + auto crs(factory->createCoordinateReferenceSystem(code, false)); + components.emplace_back(crs); + if (!name.empty()) { + name += " + "; + } + name += crs->nameStr(); + } else { + throw ParsingException( + concat("unexpected object type: ", type)); + } + } + return CompoundCRS::create( + util::PropertyMap().set(IdentifiedObject::NAME_KEY, name), + components); + } + + // OGC 07-092r2: para 7.5.3 + // 7.5.3 URN combined references for concatenated operations + if (starts_with(text, "urn:ogc:def:coordinateOperation,")) { + if (!dbContext) { + throw ParsingException("no database context specified"); + } + auto tokensComma = split(text, ','); + std::vector<CoordinateOperationNNPtr> components; + for (size_t i = 1; i < tokensComma.size(); i++) { + tokens = split(tokensComma[i], ':'); + if (tokens.size() != 4) { + throw ParsingException(concat( + "invalid coordinateOperation component: ", tokensComma[i])); + } + const auto &type = tokens[0]; + auto factory = + AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); + const auto &code = tokens[3]; + if (type == "coordinateOperation") { + auto op(factory->createCoordinateOperation(code, false)); + components.emplace_back(op); + } else { + throw ParsingException( + concat("unexpected object type: ", type)); + } + } + return ConcatenatedOperation::createComputeMetadata(components, true); + } + // urn:ogc:def:crs:EPSG::4326 if (tokens.size() == 7) { if (!dbContext) { @@ -4510,6 +4592,13 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"</li> + * <li> OGC URN combining references for compound coordinate reference systems + * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" + * We also accept a custom abbreviated syntax EPSG:2393+5717 + * </li> + * <li> OGC URN combining references for concatenated operations + * e.g. + * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li> * <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.</li> @@ -4546,6 +4635,13 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"</li> + * <li> OGC URN combining references for compound coordinate reference systems + * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" + * We also accept a custom abbreviated syntax EPSG:2393+5717 + * </li> + * <li> OGC URN combining references for concatenated operations + * e.g. + * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li> * <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.</li> diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 30e0b427..df2481e6 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -8775,6 +8775,39 @@ TEST(io, createFromUserInput) { EXPECT_THROW( createFromUserInput("urn:ogc:def:unhandled:EPSG::4326", dbContext), ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:non_existing_auth::4326", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,unhandled_type:EPSG::5717", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393::extra_element,crs:EPSG::EPSG", + dbContext), + ParsingException); + EXPECT_THROW(createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "unhandled_type:EPSG::1618", + dbContext), + ParsingException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::unexisting_code", + dbContext), + NoSuchAuthorityCodeException); + EXPECT_THROW( + createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895::extra_element," + "coordinateOperation:EPSG::1618", + dbContext), + ParsingException); EXPECT_NO_THROW(createFromUserInput("+proj=longlat", nullptr)); EXPECT_NO_THROW(createFromUserInput("EPSG:4326", dbContext)); @@ -8789,6 +8822,31 @@ TEST(io, createFromUserInput) { createFromUserInput("urn:ogc:def:meridian:EPSG::8901", dbContext)); EXPECT_NO_THROW( createFromUserInput("urn:ogc:def:ellipsoid:EPSG::7030", dbContext)); + { + auto obj = createFromUserInput("EPSG:2393+5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput( + "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717", dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), + "KKJ / Finland Uniform Coordinate System + N60 height"); + } + { + auto obj = createFromUserInput("urn:ogc:def:coordinateOperation," + "coordinateOperation:EPSG::3895," + "coordinateOperation:EPSG::1618", + dbContext); + auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj); + ASSERT_TRUE(concat != nullptr); + EXPECT_EQ(concat->nameStr(), + "MGI (Ferro) to MGI (1) + MGI to WGS 84 (3)"); + } EXPECT_NO_THROW(createFromUserInput( "GEOGCRS[\"WGS 84\",\n" " DATUM[\"World Geodetic System 1984\",\n" |
