aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/iso19111/io.cpp119
-rw-r--r--test/unit/test_io.cpp152
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);