diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2021-04-10 16:59:08 +0200 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2021-04-10 19:17:39 +0200 |
| commit | 21f0af0f8b56ab76b93aaf6ce962580ec7978fcb (patch) | |
| tree | f77790ea35031c0a55e376dac5c33962a40dbf8b | |
| parent | 84a679954b1fa4c41c7bdbac87013be78b64bea4 (diff) | |
| download | PROJ-21f0af0f8b56ab76b93aaf6ce962580ec7978fcb.tar.gz PROJ-21f0af0f8b56ab76b93aaf6ce962580ec7978fcb.zip | |
createFromUserInput(): add support for OGC URLs
e.g:
http://www.opengis.net/def/crs/EPSG/0/4326
http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/def/crs/EPSG/0/4326&2=http://www.opengis.net/def/crs/EPSG/0/3855
| -rw-r--r-- | src/iso19111/io.cpp | 82 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 78 |
2 files changed, 160 insertions, 0 deletions
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 184d992f..2d31fd59 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -6126,6 +6126,80 @@ EllipsoidNNPtr JSONParser::buildEllipsoid(const json &j) { // --------------------------------------------------------------------------- +// import a CRS encoded as OGC Best Practice document 11-135. + +static const char *const crsURLPrefixes[] = { + "http://opengis.net/def/crs", "https://opengis.net/def/crs", + "http://www.opengis.net/def/crs", "https://www.opengis.net/def/crs", + "www.opengis.net/def/crs", +}; + +static bool isCRSURL(const std::string &text) { + for (const auto crsURLPrefix : crsURLPrefixes) { + if (starts_with(text, crsURLPrefix)) { + return true; + } + } + return false; +} + +static CRSNNPtr importFromCRSURL(const std::string &text, + const DatabaseContextNNPtr &dbContext) { + // e.g http://www.opengis.net/def/crs/EPSG/0/4326 + std::vector<std::string> parts; + for (const auto crsURLPrefix : crsURLPrefixes) { + if (starts_with(text, crsURLPrefix)) { + parts = split(text.substr(strlen(crsURLPrefix)), '/'); + break; + } + } + + // e.g + // "http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/def/crs/EPSG/0/4326&2=http://www.opengis.net/def/crs/EPSG/0/3855" + if (!parts.empty() && starts_with(parts[0], "-compound?")) { + parts = split(text.substr(text.find('?') + 1), '&'); + std::map<int, std::string> mapParts; + for (const auto &part : parts) { + const auto queryParam = split(part, '='); + if (queryParam.size() != 2) { + throw ParsingException("invalid OGC CRS URL"); + } + try { + mapParts[std::stoi(queryParam[0])] = queryParam[1]; + } catch (const std::exception &) { + throw ParsingException("invalid OGC CRS URL"); + } + } + std::vector<CRSNNPtr> components; + std::string name; + for (size_t i = 1; i <= mapParts.size(); ++i) { + const auto iter = mapParts.find(static_cast<int>(i)); + if (iter == mapParts.end()) { + throw ParsingException("invalid OGC CRS URL"); + } + components.emplace_back(importFromCRSURL(iter->second, dbContext)); + if (!name.empty()) { + name += " + "; + } + name += components.back()->nameStr(); + } + return CompoundCRS::create( + util::PropertyMap().set(IdentifiedObject::NAME_KEY, name), + components); + } + + if (parts.size() < 4) { + throw ParsingException("invalid OGC CRS URL"); + } + + const auto &auth_name = parts[1]; + const auto &code = parts[3]; + auto factoryCRS = AuthorityFactory::create(dbContext, auth_name); + return factoryCRS->createCoordinateReferenceSystem(code, true); +} + +// --------------------------------------------------------------------------- + static BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules, @@ -6187,6 +6261,10 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, .createFromPROJString(text); } + if (isCRSURL(text) && dbContext) { + return importFromCRSURL(text, NN_NO_CHECK(dbContext)); + } + auto tokens = split(text, ':'); if (tokens.size() == 2) { if (!dbContext) { @@ -6590,6 +6668,10 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, * <li> OGC URN combining references for concatenated operations * e.g. * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li> + * <li>OGC URL for a single CRS. e.g. + * "http://www.opengis.net/def/crs/EPSG/0/4326</li> <li>OGC URL for a compound + * CRS. e.g + * "http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/def/crs/EPSG/0/4326&2=http://www.opengis.net/def/crs/EPSG/0/3855"</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 9d627ce9..708b3874 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -10637,6 +10637,84 @@ TEST(io, createFromUserInput) { // --------------------------------------------------------------------------- +TEST(io, createFromUserInput_ogc_crs_url) { + auto dbContext = DatabaseContext::create(); + + { + auto obj = createFromUserInput( + "http://www.opengis.net/def/crs/EPSG/0/4326", dbContext); + auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj); + ASSERT_TRUE(crs != nullptr); + } + + EXPECT_THROW( + createFromUserInput("http://www.opengis.net/def/crs", dbContext), + ParsingException); + + EXPECT_THROW( + createFromUserInput("http://www.opengis.net/def/crs/EPSG/0", dbContext), + ParsingException); + + EXPECT_THROW(createFromUserInput( + "http://www.opengis.net/def/crs/EPSG/0/XXXX", dbContext), + NoSuchAuthorityCodeException); + + { + auto obj = createFromUserInput( + "http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/" + "def/crs/EPSG/0/4326&2=http://www.opengis.net/def/crs/EPSG/0/3855", + dbContext); + auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), "WGS 84 + EGM2008 height"); + } + + // No part + EXPECT_THROW(createFromUserInput("http://www.opengis.net/def/crs-compound?", + dbContext), + ParsingException); + + // Just one part + EXPECT_THROW( + createFromUserInput("http://www.opengis.net/def/crs-compound?1=http://" + "www.opengis.net/def/crs/EPSG/0/4326", + dbContext), + InvalidCompoundCRSException); + + // Invalid compound CRS + EXPECT_THROW( + createFromUserInput( + "http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/" + "def/crs/EPSG/0/4326&2=http://www.opengis.net/def/crs/EPSG/0/4326", + dbContext), + InvalidCompoundCRSException); + + // Missing 2= + EXPECT_THROW( + createFromUserInput( + "http://www.opengis.net/def/crs-compound?1=http://www.opengis.net/" + "def/crs/EPSG/0/4326&3=http://www.opengis.net/def/crs/EPSG/0/3855", + dbContext), + ParsingException); + + // Invalid query parameter + EXPECT_THROW( + createFromUserInput("http://www.opengis.net/def/crs-compound?1=http://" + "www.opengis.net/def/crs/EPSG/0/4326&bla", + dbContext), + ParsingException); + + // Invalid query parameter + EXPECT_THROW( + createFromUserInput("http://www.opengis.net/def/crs-compound?1=http://" + "www.opengis.net/def/crs/EPSG/0/4326&two=http://" + "www.opengis.net/def/crs/EPSG/0/3855", + dbContext), + ParsingException); +} + +// --------------------------------------------------------------------------- + TEST(io, createFromUserInput_hack_EPSG_102100) { auto dbContext = DatabaseContext::create(); auto obj = createFromUserInput("EPSG:102100", dbContext); |
