Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

createFromUserInput(): support OGC URN to create projectedCRS, for example to instanciate a projected 3D CRS #1505

Merged
merged 1 commit into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions data/sql/customizations.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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');
4 changes: 4 additions & 0 deletions docs/source/apps/projinfo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
rouault marked this conversation as resolved.
Show resolved Hide resolved
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")

Expand Down
119 changes: 117 additions & 2 deletions src/iso19111/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
85 changes: 85 additions & 0 deletions test/unit/test_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,"
Expand Down