diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2021-04-10 18:35:15 +0200 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2021-04-10 19:17:40 +0200 |
| commit | b6c12e6968f38770177347f368ee2e75211d4d28 (patch) | |
| tree | 6b0ace7c2b354b05ea905b2d323a2493bedcb23d | |
| parent | efef14e274a028b08dbdc5b021d0b8189a63a39c (diff) | |
| download | PROJ-b6c12e6968f38770177347f368ee2e75211d4d28.tar.gz PROJ-b6c12e6968f38770177347f368ee2e75211d4d28.zip | |
createFromUserInput(): add support for WMS AUTO: syntax
| -rw-r--r-- | src/iso19111/io.cpp | 119 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 152 |
2 files changed, 271 insertions, 0 deletions
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 598f0c4a..bd29c310 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -6200,6 +6200,114 @@ static CRSNNPtr importFromCRSURL(const std::string &text, // --------------------------------------------------------------------------- +/* Import a CRS encoded as WMSAUTO string. + * + * Note that the WMS 1.3 specification does not include the + * units code, while apparently earlier specs do. We try to + * guess around this. + * + * (code derived from GDAL's importFromWMSAUTO()) + */ + +static CRSNNPtr importFromWMSAUTO(const std::string &text) { + + int nUnitsId = 9001; + double dfRefLong; + double dfRefLat = 0.0; + + assert(ci_starts_with(text, "AUTO:")); + const auto parts = split(text.substr(strlen("AUTO:")), ','); + + try { + constexpr int AUTO_MOLLWEIDE = 42005; + if (parts.size() == 4) { + nUnitsId = std::stoi(parts[1]); + dfRefLong = c_locale_stod(parts[2]); + dfRefLat = c_locale_stod(parts[3]); + } else if (parts.size() == 3 && std::stoi(parts[0]) == AUTO_MOLLWEIDE) { + nUnitsId = std::stoi(parts[1]); + dfRefLong = c_locale_stod(parts[2]); + } else if (parts.size() == 3) { + dfRefLong = c_locale_stod(parts[1]); + dfRefLat = c_locale_stod(parts[2]); + } else if (parts.size() == 2 && std::stoi(parts[0]) == AUTO_MOLLWEIDE) { + dfRefLong = c_locale_stod(parts[1]); + } else { + throw ParsingException("invalid WMS AUTO CRS definition"); + } + + const auto getConversion = [=]() { + const int nProjId = std::stoi(parts[0]); + switch (nProjId) { + case 42001: // Auto UTM + if (!(dfRefLong >= -180 && dfRefLong < 180)) { + throw ParsingException("invalid WMS AUTO CRS definition: " + "invalid longitude"); + } + return Conversion::createUTM( + util::PropertyMap(), + static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1, + dfRefLat >= 0.0); + + case 42002: // Auto TM (strangely very UTM-like). + return Conversion::createTransverseMercator( + util::PropertyMap(), common::Angle(0), + common::Angle(dfRefLong), common::Scale(0.9996), + common::Length(500000), + common::Length((dfRefLat >= 0.0) ? 0.0 : 10000000.0)); + + case 42003: // Auto Orthographic. + return Conversion::createOrthographic( + util::PropertyMap(), common::Angle(dfRefLat), + common::Angle(dfRefLong), common::Length(0), + common::Length(0)); + + case 42004: // Auto Equirectangular + return Conversion::createEquidistantCylindrical( + util::PropertyMap(), common::Angle(dfRefLat), + common::Angle(dfRefLong), common::Length(0), + common::Length(0)); + + case 42005: // MSVC 2015 thinks that AUTO_MOLLWEIDE is not constant + return Conversion::createMollweide( + util::PropertyMap(), common::Angle(dfRefLong), + common::Length(0), common::Length(0)); + + default: + throw ParsingException("invalid WMS AUTO CRS definition: " + "unsupported projection id"); + } + }; + + const auto getUnits = [=]() { + switch (nUnitsId) { + case 9001: + return UnitOfMeasure::METRE; + + case 9002: + return UnitOfMeasure::FOOT; + + case 9003: + return UnitOfMeasure::US_FOOT; + + default: + throw ParsingException("invalid WMS AUTO CRS definition: " + "unsupported units code"); + } + }; + + return crs::ProjectedCRS::create( + util::PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), + crs::GeographicCRS::EPSG_4326, getConversion(), + cs::CartesianCS::createEastingNorthing(getUnits())); + + } catch (const std::exception &) { + throw ParsingException("invalid WMS AUTO CRS definition"); + } +} + +// --------------------------------------------------------------------------- + static BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules, @@ -6265,6 +6373,10 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, return importFromCRSURL(text, NN_NO_CHECK(dbContext)); } + if (ci_starts_with(text, "AUTO:")) { + return importFromWMSAUTO(text); + } + auto tokens = split(text, ':'); if (tokens.size() == 2) { if (!dbContext) { @@ -6522,6 +6634,13 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, return createFromURNPart(type, authName, code); } + // urn:ogc:def:crs:OGC::AUTO42001:-117:33 + if (tokens.size() > 7 && tokens[0] == "urn" && tokens[4] == "OGC" && + ci_starts_with(tokens[6], "AUTO")) { + const auto textAUTO = text.substr(text.find(":AUTO") + 5); + return importFromWMSAUTO("AUTO:" + replaceAll(textAUTO, ":", ",")); + } + // Legacy urn:opengis:crs:EPSG:0:4326 (note the missing def: compared to // above) if (tokens.size() == 6 && tokens[0] == "urn" && tokens[2] != "def") { diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index b6fa3d0e..780db16d 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -10733,6 +10733,158 @@ TEST(io, createFromUserInput_ogc_crs_url) { // --------------------------------------------------------------------------- +TEST(io, createFromUserInput_OGC_AUTO) { + + // UTM north + { + auto obj = createFromUserInput("AUTO:42001,-117,33", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "UTM zone 11N"); + EXPECT_EQ( + crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +type=crs"); + } + + // UTM south + { + auto obj = createFromUserInput("AUTO:42001,-117,-33", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "UTM zone 11S"); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +south +datum=WGS84 +units=m +no_defs " + "+type=crs"); + } + + // Explicit unit: metre + { + auto obj = createFromUserInput("AUTO:42001,9001,-117,33", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "UTM zone 11N"); + EXPECT_EQ( + crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +type=crs"); + } + + // Explicit unit: foot + { + auto obj = createFromUserInput("AUTO:42001,9002,-117,33", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "UTM zone 11N"); + EXPECT_EQ( + crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +datum=WGS84 +units=ft +no_defs +type=crs"); + } + + // Explicit unit: US survery foot + { + auto obj = createFromUserInput("AUTO:42001,9003,-117,33", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "UTM zone 11N"); + EXPECT_EQ( + crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +datum=WGS84 +units=us-ft +no_defs +type=crs"); + } + + // Explicit unit: invalid + EXPECT_THROW(createFromUserInput("AUTO:42001,0,-117,33", nullptr), + ParsingException); + + // Invalid longitude + EXPECT_THROW(createFromUserInput("AUTO:42001,-180.01,33", nullptr), + ParsingException); + EXPECT_NO_THROW(createFromUserInput("AUTO:42001,-180,33", nullptr)); + EXPECT_THROW(createFromUserInput("AUTO:42001,180,33", nullptr), + ParsingException); + EXPECT_NO_THROW(createFromUserInput("AUTO:42001,179.999,33", nullptr)); + + // Too short + EXPECT_THROW(createFromUserInput("AUTO:42001", nullptr), ParsingException); + + // TMerc / north + { + auto obj = createFromUserInput("AUTO:42002,1,2", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "Transverse Mercator"); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=tmerc +lat_0=0 +lon_0=1 +k=0.9996 +x_0=500000 +y_0=0 " + "+datum=WGS84 +units=m +no_defs +type=crs"); + } + + // TMerc / south + { + auto obj = createFromUserInput("AUTO:42002,1,-2", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->derivingConversion()->nameStr(), "Transverse Mercator"); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=tmerc +lat_0=0 +lon_0=1 +k=0.9996 +x_0=500000 " + "+y_0=10000000 +datum=WGS84 +units=m +no_defs +type=crs"); + } + + // Orthographic + { + auto obj = createFromUserInput("AUTO:42003,1,2", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=ortho +lat_0=2 +lon_0=1 +x_0=0 +y_0=0 +datum=WGS84 " + "+units=m +no_defs +type=crs"); + } + + // Equirectangular + { + auto obj = createFromUserInput("AUTO:42004,1,0", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=1 +x_0=0 +y_0=0 " + "+datum=WGS84 +units=m +no_defs +type=crs"); + } + + // Mollweide + { + auto obj = createFromUserInput("AUTO:42005,1", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=moll +lon_0=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m " + "+no_defs +type=crs"); + } + + // Mollweide with explicit unit + { + auto obj = createFromUserInput("AUTO:42005,9001,1", nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=moll +lon_0=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m " + "+no_defs +type=crs"); + } + + // Invalid method id + EXPECT_THROW(createFromUserInput("AUTO:42999,1,0", nullptr), + ParsingException); + + // As urn:ogc:def:crs:OGC::AUTOxxxx:.... + { + auto obj = createFromUserInput("urn:ogc:def:crs:OGC::AUTO42001:-117:33", + nullptr); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ( + crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +type=crs"); + } +} + +// --------------------------------------------------------------------------- + TEST(io, createFromUserInput_hack_EPSG_102100) { auto dbContext = DatabaseContext::create(); auto obj = createFromUserInput("EPSG:102100", dbContext); |
