aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-05-14 15:11:02 +0200
committerEven Rouault <even.rouault@spatialys.com>2020-05-14 18:51:40 +0200
commit11012ebcc910c4d95d5acbff43f444715ae1ad32 (patch)
tree777757d7c6daf0e29900d4a49328a6cc78a7afbd
parent0c9cf39297a24d5e56aa488820a5ba3edaace90e (diff)
downloadPROJ-11012ebcc910c4d95d5acbff43f444715ae1ad32.tar.gz
PROJ-11012ebcc910c4d95d5acbff43f444715ae1ad32.zip
WKT/PROJJSON parsing: fix BoundCRS/PROJ4_GRIDS handling for vertical component when vertical CRS has non metre unit (fixes #2217)
-rw-r--r--src/iso19111/io.cpp137
-rw-r--r--test/unit/test_io.cpp26
-rw-r--r--test/unit/test_operation.cpp55
3 files changed, 164 insertions, 54 deletions
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 97f741be..7fddd324 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -3987,6 +3987,79 @@ WKTParser::Private::buildParametricDatum(const WKTNodeNNPtr &node) {
// ---------------------------------------------------------------------------
+static CRSNNPtr
+createBoundCRSSourceTransformationCRS(const crs::CRSPtr &sourceCRS,
+ const crs::CRSPtr &targetCRS) {
+ CRSPtr sourceTransformationCRS;
+ if (dynamic_cast<GeographicCRS *>(targetCRS.get())) {
+ GeographicCRSPtr sourceGeographicCRS =
+ sourceCRS->extractGeographicCRS();
+ sourceTransformationCRS = sourceGeographicCRS;
+ if (sourceGeographicCRS) {
+ if (sourceGeographicCRS->datum() != nullptr &&
+ sourceGeographicCRS->primeMeridian()
+ ->longitude()
+ .getSIValue() != 0.0) {
+ sourceTransformationCRS =
+ GeographicCRS::create(
+ util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY,
+ sourceGeographicCRS->nameStr() +
+ " (with Greenwich prime meridian)"),
+ datum::GeodeticReferenceFrame::create(
+ util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY,
+ sourceGeographicCRS->datum()->nameStr() +
+ " (with Greenwich prime meridian)"),
+ sourceGeographicCRS->datum()->ellipsoid(),
+ util::optional<std::string>(),
+ datum::PrimeMeridian::GREENWICH),
+ sourceGeographicCRS->coordinateSystem())
+ .as_nullable();
+ }
+ } else {
+ auto vertSourceCRS =
+ std::dynamic_pointer_cast<VerticalCRS>(sourceCRS);
+ if (!vertSourceCRS) {
+ throw ParsingException(
+ "Cannot find GeographicCRS or VerticalCRS in sourceCRS");
+ }
+ const auto &axis = vertSourceCRS->coordinateSystem()->axisList()[0];
+ if (axis->unit() == common::UnitOfMeasure::METRE &&
+ &(axis->direction()) == &AxisDirection::UP) {
+ sourceTransformationCRS = sourceCRS;
+ } else {
+ std::string sourceTransformationCRSName(
+ vertSourceCRS->nameStr());
+ if (ends_with(sourceTransformationCRSName, " (ftUS)")) {
+ sourceTransformationCRSName.resize(
+ sourceTransformationCRSName.size() - strlen(" (ftUS)"));
+ }
+ if (ends_with(sourceTransformationCRSName, " depth")) {
+ sourceTransformationCRSName.resize(
+ sourceTransformationCRSName.size() - strlen(" depth"));
+ }
+ if (!ends_with(sourceTransformationCRSName, " height")) {
+ sourceTransformationCRSName += " height";
+ }
+ sourceTransformationCRS =
+ VerticalCRS::create(
+ PropertyMap().set(IdentifiedObject::NAME_KEY,
+ sourceTransformationCRSName),
+ vertSourceCRS->datum(), vertSourceCRS->datumEnsemble(),
+ VerticalCS::createGravityRelatedHeight(
+ common::UnitOfMeasure::METRE))
+ .as_nullable();
+ }
+ }
+ } else {
+ sourceTransformationCRS = sourceCRS;
+ }
+ return NN_NO_CHECK(sourceTransformationCRS);
+}
+
+// ---------------------------------------------------------------------------
+
CRSNNPtr WKTParser::Private::buildVerticalCRS(const WKTNodeNNPtr &node) {
const auto *nodeP = node->GP();
auto &datumNode =
@@ -4111,16 +4184,18 @@ CRSNNPtr WKTParser::Private::buildVerticalCRS(const WKTNodeNNPtr &node) {
gridName != "g2012a_conus.gtx,g2012a_alaska.gtx,"
"g2012a_guam.gtx,g2012a_hawaii.gtx,"
"g2012a_puertorico.gtx,g2012a_samoa.gtx") {
- std::string transformationName(crs->nameStr());
- if (!ends_with(transformationName, " height")) {
- transformationName += " height";
- }
- transformationName += " to WGS84 ellipsoidal height";
+ auto sourceTransformationCRS =
+ createBoundCRSSourceTransformationCRS(
+ crs.as_nullable(),
+ GeographicCRS::EPSG_4979.as_nullable());
auto transformation = Transformation::
createGravityRelatedHeightToGeographic3D(
- PropertyMap().set(IdentifiedObject::NAME_KEY,
- transformationName),
- crs, GeographicCRS::EPSG_4979, nullptr, gridName,
+ PropertyMap().set(
+ IdentifiedObject::NAME_KEY,
+ sourceTransformationCRS->nameStr() +
+ " to WGS84 ellipsoidal height"),
+ sourceTransformationCRS, GeographicCRS::EPSG_4979,
+ nullptr, gridName,
std::vector<PositionalAccuracyNNPtr>());
return nn_static_pointer_cast<CRS>(BoundCRS::create(
crs, GeographicCRS::EPSG_4979, transformation));
@@ -4190,52 +4265,6 @@ CRSNNPtr WKTParser::Private::buildCompoundCRS(const WKTNodeNNPtr &node) {
// ---------------------------------------------------------------------------
-static CRSNNPtr
-createBoundCRSSourceTransformationCRS(const crs::CRSPtr &sourceCRS,
- const crs::CRSPtr &targetCRS) {
- CRSPtr sourceTransformationCRS;
- if (dynamic_cast<GeographicCRS *>(targetCRS.get())) {
- GeographicCRSPtr sourceGeographicCRS =
- sourceCRS->extractGeographicCRS();
- sourceTransformationCRS = sourceGeographicCRS;
- if (sourceGeographicCRS) {
- if (sourceGeographicCRS->datum() != nullptr &&
- sourceGeographicCRS->primeMeridian()
- ->longitude()
- .getSIValue() != 0.0) {
- sourceTransformationCRS =
- GeographicCRS::create(
- util::PropertyMap().set(
- common::IdentifiedObject::NAME_KEY,
- sourceGeographicCRS->nameStr() +
- " (with Greenwich prime meridian)"),
- datum::GeodeticReferenceFrame::create(
- util::PropertyMap().set(
- common::IdentifiedObject::NAME_KEY,
- sourceGeographicCRS->datum()->nameStr() +
- " (with Greenwich prime meridian)"),
- sourceGeographicCRS->datum()->ellipsoid(),
- util::optional<std::string>(),
- datum::PrimeMeridian::GREENWICH),
- sourceGeographicCRS->coordinateSystem())
- .as_nullable();
- }
- } else {
- sourceTransformationCRS =
- std::dynamic_pointer_cast<VerticalCRS>(sourceCRS);
- if (!sourceTransformationCRS) {
- throw ParsingException(
- "Cannot find GeographicCRS or VerticalCRS in sourceCRS");
- }
- }
- } else {
- sourceTransformationCRS = sourceCRS;
- }
- return NN_NO_CHECK(sourceTransformationCRS);
-}
-
-// ---------------------------------------------------------------------------
-
BoundCRSNNPtr WKTParser::Private::buildBoundCRS(const WKTNodeNNPtr &node) {
const auto *nodeP = node->GP();
auto &abridgedNode =
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 0570bb7e..e57cb003 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -3442,6 +3442,32 @@ TEST(wkt_parse, WKT1_VERT_DATUM_EXTENSION) {
// ---------------------------------------------------------------------------
+TEST(wkt_parse, WKT1_VERT_DATUM_EXTENSION_units_ftUS) {
+ auto wkt = "VERT_CS[\"NAVD88 height (ftUS)\","
+ " VERT_DATUM[\"North American Vertical Datum 1988\",2005,"
+ " EXTENSION[\"PROJ4_GRIDS\",\"foo.gtx\"],"
+ " AUTHORITY[\"EPSG\",\"5103\"]],"
+ " UNIT[\"US survey foot\",0.304800609601219,"
+ " AUTHORITY[\"EPSG\",\"9003\"]],"
+ " AXIS[\"Gravity-related height\",UP],"
+ " AUTHORITY[\"EPSG\",\"6360\"]]";
+
+ auto obj = WKTParser().createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+
+ EXPECT_EQ(crs->transformation()->nameStr(),
+ "NAVD88 height to WGS84 ellipsoidal height"); // no (ftUS)
+ auto sourceTransformationCRS = crs->transformation()->sourceCRS();
+ auto sourceTransformationVertCRS =
+ nn_dynamic_pointer_cast<VerticalCRS>(sourceTransformationCRS);
+ EXPECT_EQ(
+ sourceTransformationVertCRS->coordinateSystem()->axisList()[0]->unit(),
+ UnitOfMeasure::METRE);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(wkt_parse, WKT1_DATUM_EXTENSION) {
auto wkt =
"PROJCS[\"unnamed\",\n"
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 9b339525..d8944aa8 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -7136,6 +7136,61 @@ TEST(operation,
// ---------------------------------------------------------------------------
+TEST(operation,
+ compoundCRS_with_boundProjCRS_with_ftus_and_boundVerticalCRS_to_geogCRS) {
+
+ auto wkt =
+ "COMPD_CS[\"NAD_1983_StatePlane_Alabama_West_FIPS_0102_Feet + "
+ "NAVD88 height - Geoid12B (US Feet)\",\n"
+ " PROJCS[\"NAD_1983_StatePlane_Alabama_West_FIPS_0102_Feet\",\n"
+ " GEOGCS[\"NAD83\",\n"
+ " DATUM[\"North_American_Datum_1983\",\n"
+ " SPHEROID[\"GRS 1980\",6378137,298.257222101,\n"
+ " AUTHORITY[\"EPSG\",\"7019\"]],\n"
+ " TOWGS84[0,0,0,0,0,0,0],\n"
+ " AUTHORITY[\"EPSG\",\"6269\"]],\n"
+ " PRIMEM[\"Greenwich\",0],\n"
+ " UNIT[\"Degree\",0.0174532925199433]],\n"
+ " PROJECTION[\"Transverse_Mercator\"],\n"
+ " PARAMETER[\"latitude_of_origin\",30],\n"
+ " PARAMETER[\"central_meridian\",-87.5],\n"
+ " PARAMETER[\"scale_factor\",0.999933333333333],\n"
+ " PARAMETER[\"false_easting\",1968500],\n"
+ " PARAMETER[\"false_northing\",0],\n"
+ " UNIT[\"US survey foot\",0.304800609601219,\n"
+ " AUTHORITY[\"EPSG\",\"9003\"]],\n"
+ " AXIS[\"Easting\",EAST],\n"
+ " AXIS[\"Northing\",NORTH],\n"
+ " AUTHORITY[\"ESRI\",\"102630\"]],\n"
+ " VERT_CS[\"NAVD88 height (ftUS)\",\n"
+ " VERT_DATUM[\"North American Vertical Datum 1988\",2005,\n"
+ " EXTENSION[\"PROJ4_GRIDS\",\"foo.gtx\"],\n"
+ " AUTHORITY[\"EPSG\",\"5103\"]],\n"
+ " UNIT[\"US survey foot\",0.304800609601219,\n"
+ " AUTHORITY[\"EPSG\",\"9003\"]],\n"
+ " AXIS[\"Gravity-related height\",UP],\n"
+ " AUTHORITY[\"EPSG\",\"6360\"]]]";
+
+ auto obj = WKTParser().createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<BoundCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ auto op = CoordinateOperationFactory::create()->createOperation(
+ NN_NO_CHECK(crs), GeographicCRS::EPSG_4979);
+ ASSERT_TRUE(op != nullptr);
+
+ EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=us-ft +xy_out=m "
+ "+step +inv +proj=tmerc +lat_0=30 +lon_0=-87.5 "
+ "+k=0.999933333333333 +x_0=600000 +y_0=0 +ellps=GRS80 "
+ "+step +proj=unitconvert +z_in=us-ft +z_out=m "
+ "+step +proj=vgridshift +grids=foo.gtx +multiplier=1 "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg "
+ "+step +proj=axisswap +order=2,1");
+}
+
+// ---------------------------------------------------------------------------
+
TEST(operation, geocent_to_compoundCRS) {
auto objSrc = PROJStringParser().createFromPROJString(
"+proj=geocent +datum=WGS84 +units=m +type=crs");