Skip to content

Commit

Permalink
Merge pull request #2696 from rouault/derived_vertical_crs_unit_heigh…
Browse files Browse the repository at this point in the history
…t_depth

Improvements related to DerivedVerticalCRS using Change Unit and Height/Depth reversal methods
  • Loading branch information
rouault authored Apr 24, 2021
2 parents 01a5c03 + 7898eab commit ea19621
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 11 deletions.
27 changes: 27 additions & 0 deletions src/iso19111/crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6506,6 +6506,33 @@ DerivedVerticalCRSNNPtr DerivedVerticalCRS::create(
void DerivedVerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const {
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
if (!isWKT2) {

bool useBaseMethod = true;
const DerivedVerticalCRS *dvcrs = this;
while (true) {
// If the derived vertical CRS is obtained through simple conversion
// methods that just do unit change or height/depth reversal, export
// it as a regular VerticalCRS
const int methodCode =
dvcrs->derivingConversionRef()->method()->getEPSGCode();
if (methodCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT ||
methodCode ==
EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT_NO_CONV_FACTOR ||
methodCode == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) {
dvcrs = dynamic_cast<DerivedVerticalCRS *>(baseCRS().get());
if (dvcrs == nullptr) {
break;
}
} else {
useBaseMethod = false;
break;
}
}
if (useBaseMethod) {
VerticalCRS::_exportToWKT(formatter);
return;
}

io::FormattingException::Throw(
"DerivedVerticalCRS can only be exported to WKT2");
}
Expand Down
69 changes: 58 additions & 11 deletions src/iso19111/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6491,12 +6491,13 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensOp[1]);
auto op = factoryOp->createCoordinateOperation(tokensOp[3], true);

std::string name(baseCRS->nameStr());
const auto &baseName = baseCRS->nameStr();
std::string name(baseName);
auto geogCRS =
util::nn_dynamic_pointer_cast<GeographicCRS>(baseCRS);
if (geogCRS &&
geogCRS->coordinateSystem()->axisList().size() == 3 &&
baseCRS->nameStr().find("3D") == std::string::npos) {
baseName.find("3D") == std::string::npos) {
name += " (3D)";
}
name += " / ";
Expand Down Expand Up @@ -6532,15 +6533,61 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
baseCRS)) {
return DerivedProjectedCRS::create(props, NN_NO_CHECK(pcrs),
convNN, cs);
} else if (dynamic_cast<VerticalCRS *>(baseCRS.get()) &&
dynamic_cast<VerticalCS *>(cs.get())) {
return DerivedVerticalCRS::create(
props,
NN_NO_CHECK(util::nn_dynamic_pointer_cast<VerticalCRS>(
baseCRS)),
convNN,
NN_NO_CHECK(
util::nn_dynamic_pointer_cast<VerticalCS>(cs)));
} else if (auto vertBaseCRS =
util::nn_dynamic_pointer_cast<VerticalCRS>(
baseCRS)) {
if (auto vertCS =
util::nn_dynamic_pointer_cast<VerticalCS>(cs)) {
const int methodCode = convNN->method()->getEPSGCode();
std::string newName(baseName);
std::string unitNameSuffix;
for (const char *suffix : {" (ft)", " (ftUS)"}) {
if (ends_with(newName, suffix)) {
unitNameSuffix = suffix;
newName.resize(newName.size() - strlen(suffix));
break;
}
}
bool newNameOk = false;
if (methodCode ==
EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT_NO_CONV_FACTOR ||
methodCode ==
EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT) {
const auto &unitName =
vertCS->axisList()[0]->unit().name();
if (unitName == UnitOfMeasure::METRE.name()) {
newNameOk = true;
} else if (unitName == UnitOfMeasure::FOOT.name()) {
newName += " (ft)";
newNameOk = true;
} else if (unitName ==
UnitOfMeasure::US_FOOT.name()) {
newName += " (ftUS)";
newNameOk = true;
}
} else if (methodCode ==
EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) {
if (ends_with(newName, " height")) {
newName.resize(newName.size() -
strlen(" height"));
newName += " depth";
newName += unitNameSuffix;
newNameOk = true;
} else if (ends_with(newName, " depth")) {
newName.resize(newName.size() -
strlen(" depth"));
newName += " height";
newName += unitNameSuffix;
newNameOk = true;
}
}
if (newNameOk) {
props.set(IdentifiedObject::NAME_KEY, newName);
}
return DerivedVerticalCRS::create(
props, NN_NO_CHECK(vertBaseCRS), convNN,
NN_NO_CHECK(vertCS));
}
}
}

Expand Down
26 changes: 26 additions & 0 deletions test/unit/test_crs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5620,6 +5620,32 @@ TEST(crs, DerivedVerticalCRS_WKT1) {

// ---------------------------------------------------------------------------

TEST(crs, DerivedVerticalCRS_WKT1_when_simple_derivation) {

auto derivingConversion =
Conversion::createChangeVerticalUnit(PropertyMap().set(
IdentifiedObject::NAME_KEY, "Vertical Axis Unit Conversion"));

auto crs = DerivedVerticalCRS::create(
PropertyMap().set(IdentifiedObject::NAME_KEY, "Derived vertCRS"),
createVerticalCRS(), derivingConversion,
VerticalCS::createGravityRelatedHeight(UnitOfMeasure::FOOT));

auto expected = "VERT_CS[\"Derived vertCRS\",\n"
" VERT_DATUM[\"Ordnance Datum Newlyn\",2005,\n"
" AUTHORITY[\"EPSG\",\"5101\"]],\n"
" UNIT[\"foot\",0.3048,\n"
" AUTHORITY[\"EPSG\",\"9002\"]],\n"
" AXIS[\"Gravity-related height\",UP]]";

EXPECT_EQ(
crs->exportToWKT(
WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
expected);
}

// ---------------------------------------------------------------------------

static DerivedEngineeringCRSNNPtr createDerivedEngineeringCRS() {

auto derivingConversion = Conversion::create(
Expand Down
61 changes: 61 additions & 0 deletions test/unit/test_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10585,6 +10585,7 @@ TEST(io, createFromUserInput) {
EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 4400);
EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 16031);
}

{
// DerivedVerticalCRS based on "NAVD88 height", using a foot UP axis,
// and EPSG:7813 "Vertical Axis Unit Conversion" conversion
Expand All @@ -10594,11 +10595,71 @@ TEST(io, createFromUserInput) {
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 height (ft)");
EXPECT_EQ(crs->baseCRS()->getEPSGCode(), 5703);
EXPECT_EQ(crs->coordinateSystem()->getEPSGCode(), 1030);
EXPECT_EQ(crs->derivingConversion()->getEPSGCode(), 7813);
}

{
// DerivedVerticalCRS based on "NAVD88 height", using a ftUS UP axis,
// and EPSG:7813 "Vertical Axis Unit Conversion" conversion
auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::5703,"
"cs:EPSG::6497,"
"coordinateOperation:EPSG::7813",
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 height (ftUS)");
}

{
// DerivedVerticalCRS based on "NAVD88 height (ftUS)", using a metre UP
// axis, and EPSG:7813 "Vertical Axis Unit Conversion" conversion
auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::6360,"
"cs:EPSG::6499,"
"coordinateOperation:EPSG::7813",
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 height");
}
{
// DerivedVerticalCRS based on "NAVD88 height", using a metre DOWN axis,
// and EPSG:7812 "Height / Depth reversal" conversion
auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::5703,"
"cs:EPSG::6498,"
"coordinateOperation:EPSG::7812",
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 depth");
}

{
// DerivedVerticalCRS based on "NAVD88 height (ftUS)", using a ftUS DOWN
// axis, and EPSG:7812 "Height / Depth reversal" conversion
auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::6360,"
"cs:EPSG::1043,"
"coordinateOperation:EPSG::7812",
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 depth (ftUS)");
}

{
// DerivedVerticalCRS based on "NAVD88 depth (ftUS)", using a ftUS UP
// axis, and EPSG:7812 "Height / Depth reversal" conversion
auto obj = createFromUserInput("urn:ogc:def:crs,crs:EPSG::6358,"
"cs:EPSG::6497,"
"coordinateOperation:EPSG::7812",
dbContext);
auto crs = nn_dynamic_pointer_cast<DerivedVerticalCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "NAVD88 height (ftUS)");
}

{
auto obj = createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895,"
Expand Down

0 comments on commit ea19621

Please sign in to comment.