aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-06-05 22:03:00 +0200
committerEven Rouault <even.rouault@spatialys.com>2019-06-06 15:02:58 +0200
commitcd28089a9984698b5557be1d9b868d30eaa3a893 (patch)
tree9d113dca2e00d8d8704adc6123d30c00593956de
parent0383f7829792f896d233acc1676377d2fffc3cdc (diff)
downloadPROJ-cd28089a9984698b5557be1d9b868d30eaa3a893.tar.gz
PROJ-cd28089a9984698b5557be1d9b868d30eaa3a893.zip
createFromUserInput(): support OGC URN to create projectedCRS, for example to instanciate a projected 3D CRS
-rw-r--r--data/sql/customizations.sql6
-rw-r--r--docs/source/apps/projinfo.rst4
-rw-r--r--src/iso19111/io.cpp119
-rw-r--r--test/unit/test_io.cpp85
4 files changed, 212 insertions, 2 deletions
diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql
index b9fcac31..5e038de4 100644
--- a/data/sql/customizations.sql
+++ b/data/sql/customizations.sql
@@ -59,3 +59,9 @@ INSERT INTO "ellipsoid" VALUES('PROJ','WGS60','WGS 60',NULL,'PROJ','EARTH',63781
-- Extra ellipsoids from IAU2000 dictionary (see https://github.com/USGS-Astrogeology/GDAL_scripts/blob/master/OGC_IAU2000_WKT_v2/naifcodes_radii_m_wAsteroids_IAU2000.csv)
INSERT INTO "ellipsoid" VALUES('PROJ','EARTH2000','Earth2000',NULL,'PROJ','EARTH',6378140.0,'EPSG','9001',NULL,6356750.0,0);
+
+-- Coordinate system ENh for ProjectedCRS 3D. Should be removed once EPSG has such a coordinate system
+INSERT INTO "coordinate_system" VALUES('PROJ','ENh','Cartesian',3);
+INSERT INTO "axis" VALUES('PROJ','1','Easting','E','east','PROJ','ENh',1,'EPSG','9001');
+INSERT INTO "axis" VALUES('PROJ','2','Northing','N','north','PROJ','ENh',2,'EPSG','9001');
+INSERT INTO "axis" VALUES('PROJ','3','Ellipsoidal height','h','up','PROJ','ENh',2,'EPSG','9001');
diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst
index 105b1e1b..dabcc0fe 100644
--- a/docs/source/apps/projinfo.rst
+++ b/docs/source/apps/projinfo.rst
@@ -38,6 +38,10 @@ Synopsis
- 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 references for projected or derived CRSs
+ e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)":
+ "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031"
+ (*added in 6.2*)
- a OGC URN combining references for concatenated operations
(e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618")
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 7329758a..7b8e270b 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -4449,13 +4449,118 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
}
}
- // 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, ',');
+ if (tokensComma.size() == 4 && starts_with(tokensComma[1], "crs:") &&
+ starts_with(tokensComma[2], "cs:") &&
+ starts_with(tokensComma[3], "coordinateOperation:")) {
+ // OGC 07-092r2: para 7.5.4
+ // URN combined references for projected or derived CRSs
+ const auto &crsPart = tokensComma[1];
+ const auto tokensCRS = split(crsPart, ':');
+ if (tokensCRS.size() != 4) {
+ throw ParsingException(
+ concat("invalid crs component: ", crsPart));
+ }
+ auto factoryCRS =
+ AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensCRS[1]);
+ auto baseCRS =
+ factoryCRS->createCoordinateReferenceSystem(tokensCRS[3], true);
+
+ const auto &csPart = tokensComma[2];
+ auto tokensCS = split(csPart, ':');
+ if (tokensCS.size() != 4) {
+ throw ParsingException(
+ concat("invalid cs component: ", csPart));
+ }
+ auto factoryCS =
+ AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensCS[1]);
+ auto cs = factoryCS->createCoordinateSystem(tokensCS[3]);
+
+ const auto &opPart = tokensComma[3];
+ auto tokensOp = split(opPart, ':');
+ if (tokensOp.size() != 4) {
+ throw ParsingException(
+ concat("invalid coordinateOperation component: ", opPart));
+ }
+ auto factoryOp =
+ AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensOp[1]);
+ auto op = factoryOp->createCoordinateOperation(tokensOp[3], true);
+
+ if (dynamic_cast<GeographicCRS *>(baseCRS.get()) &&
+ dynamic_cast<Conversion *>(op.get()) &&
+ dynamic_cast<CartesianCS *>(cs.get())) {
+ auto geogCRS = NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<GeographicCRS>(baseCRS));
+ auto name = op->nameStr() + " / " + baseCRS->nameStr();
+ if (geogCRS->coordinateSystem()->axisList().size() == 3 &&
+ baseCRS->nameStr().find("3D") == std::string::npos) {
+ name += " (3D)";
+ }
+ return ProjectedCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY, name),
+ geogCRS,
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(op)),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<CartesianCS>(cs)));
+ } else if (dynamic_cast<GeodeticCRS *>(baseCRS.get()) &&
+ !dynamic_cast<GeographicCRS *>(baseCRS.get()) &&
+ dynamic_cast<Conversion *>(op.get()) &&
+ dynamic_cast<CartesianCS *>(cs.get())) {
+ return DerivedGeodeticCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ op->nameStr() + " / " +
+ baseCRS->nameStr()),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<GeodeticCRS>(baseCRS)),
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(op)),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<CartesianCS>(cs)));
+ } else if (dynamic_cast<GeographicCRS *>(baseCRS.get()) &&
+ dynamic_cast<Conversion *>(op.get()) &&
+ dynamic_cast<EllipsoidalCS *>(cs.get())) {
+ return DerivedGeographicCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ op->nameStr() + " / " +
+ baseCRS->nameStr()),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<GeodeticCRS>(baseCRS)),
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(op)),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<EllipsoidalCS>(cs)));
+ } else if (dynamic_cast<ProjectedCRS *>(baseCRS.get()) &&
+ dynamic_cast<Conversion *>(op.get())) {
+ return DerivedProjectedCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ op->nameStr() + " / " +
+ baseCRS->nameStr()),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<ProjectedCRS>(baseCRS)),
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(op)),
+ cs);
+ } else if (dynamic_cast<VerticalCRS *>(baseCRS.get()) &&
+ dynamic_cast<Conversion *>(op.get()) &&
+ dynamic_cast<VerticalCS *>(cs.get())) {
+ return DerivedVerticalCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ op->nameStr() + " / " +
+ baseCRS->nameStr()),
+ NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<VerticalCRS>(baseCRS)),
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<Conversion>(op)),
+ NN_NO_CHECK(util::nn_dynamic_pointer_cast<VerticalCS>(cs)));
+ } else {
+ throw ParsingException("unsupported combination of baseCRS, CS "
+ "and coordinateOperation for a "
+ "DerivedCRS");
+ }
+ }
+
+ // OGC 07-092r2: para 7.5.2
+ // URN combined references for compound coordinate reference systems
std::vector<CRSNNPtr> components;
std::string name;
for (size_t i = 1; i < tokensComma.size(); i++) {
@@ -4622,6 +4727,11 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
* 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 references for projected or derived
+ * CRSs
+ * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)"
+ * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031"
+ * </li>
* <li> OGC URN combining references for concatenated operations
* e.g.
* "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li>
@@ -4665,6 +4775,11 @@ BaseObjectNNPtr createFromUserInput(const std::string &text,
* 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 references for projected or derived
+ * CRSs
+ * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)"
+ * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031"
+ * </li>
* <li> OGC URN combining references for concatenated operations
* e.g.
* "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li>
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 294e765d..cea59460 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -8874,6 +8874,91 @@ TEST(io, createFromUserInput) {
EXPECT_EQ(crs->nameStr(),
"KKJ / Finland Uniform Coordinate System + N60 height");
}
+
+ {
+ auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::4979,"
+ "cs:PROJ::ENh,"
+ "coordinateOperation:EPSG::16031",
+ dbContext);
+ auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->nameStr(), "UTM zone 31N / WGS 84 (3D)");
+ EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 4979);
+ EXPECT_EQ(crs->coordinateSystem()->axisList().size(), 3U);
+ EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
+ }
+
+ EXPECT_THROW(createFromUserInput(
+ "urn:ogc:def:crs,crs:EPSG::4979,"
+ "cs:PROJ::ENh,"
+ "coordinateOperation:EPSG::1024", // not a conversion
+ dbContext),
+ ParsingException);
+ EXPECT_THROW(createFromUserInput("urn:ogc:def:crs,crs:,"
+ "cs:PROJ::ENh,"
+ "coordinateOperation:EPSG::16031",
+ dbContext),
+ ParsingException);
+ EXPECT_THROW(createFromUserInput("urn:ogc:def:crs,crs:EPSG::4979,"
+ "cs:,"
+ "coordinateOperation:EPSG::16031",
+ dbContext),
+ ParsingException);
+ EXPECT_THROW(createFromUserInput("urn:ogc:def:crs,crs:EPSG::4979,"
+ "cs:PROJ::ENh,"
+ "coordinateOperation:",
+ dbContext),
+ ParsingException);
+
+ {
+ // Completely non-sensical from a geodesic point of view...
+ auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::4978,"
+ "cs:EPSG::6500,"
+ "coordinateOperation:EPSG::16031",
+ dbContext);
+ auto crs = nn_dynamic_pointer_cast<DerivedGeodeticCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 4978);
+ EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 6500);
+ EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
+ }
+ {
+ // Completely non-sensical from a geodesic point of view...
+ auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::4979,"
+ "cs:EPSG::6423,"
+ "coordinateOperation:EPSG::16031",
+ dbContext);
+ auto crs = nn_dynamic_pointer_cast<DerivedGeographicCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 4979);
+ EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 6423);
+ EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
+ }
+ {
+ // Completely non-sensical from a geodesic point of view...
+ auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::32631,"
+ "cs:EPSG::4400,"
+ "coordinateOperation:EPSG::16031",
+ dbContext);
+ auto crs = nn_dynamic_pointer_cast<DerivedProjectedCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 32631);
+ EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 4400);
+ EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
+ }
+ {
+ // Completely non-sensical from a geodesic point of view...
+ auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::3855,"
+ "cs:EPSG::6499,"
+ "coordinateOperation:EPSG::16031",
+ dbContext);
+ auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 3855);
+ EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 6499);
+ EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
+ }
+
{
auto obj = createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895,"