diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 0cd2f604b9..9d4ad47780 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -871,26 +871,32 @@ static bool mustAxisOrderBeSwitchedForVisualizationInternal( bool CRS::mustAxisOrderBeSwitchedForVisualization() const { - const CompoundCRS *compoundCRS = dynamic_cast(this); - if (compoundCRS) { + if (const CompoundCRS *compoundCRS = + dynamic_cast(this)) { const auto &comps = compoundCRS->componentReferenceSystems(); if (!comps.empty()) { return comps[0]->mustAxisOrderBeSwitchedForVisualization(); } } - const GeographicCRS *geogCRS = dynamic_cast(this); - if (geogCRS) { + if (const GeographicCRS *geogCRS = + dynamic_cast(this)) { return mustAxisOrderBeSwitchedForVisualizationInternal( geogCRS->coordinateSystem()->axisList()); } - const ProjectedCRS *projCRS = dynamic_cast(this); - if (projCRS) { + if (const ProjectedCRS *projCRS = + dynamic_cast(this)) { return mustAxisOrderBeSwitchedForVisualizationInternal( projCRS->coordinateSystem()->axisList()); } + if (const DerivedProjectedCRS *derivedProjCRS = + dynamic_cast(this)) { + return mustAxisOrderBeSwitchedForVisualizationInternal( + derivedProjCRS->coordinateSystem()->axisList()); + } + return false; } @@ -1009,8 +1015,8 @@ CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const { return props; }; - const CompoundCRS *compoundCRS = dynamic_cast(this); - if (compoundCRS) { + if (const CompoundCRS *compoundCRS = + dynamic_cast(this)) { const auto &comps = compoundCRS->componentReferenceSystems(); if (!comps.empty()) { std::vector newComps; @@ -1026,8 +1032,8 @@ CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const { } } - const GeographicCRS *geogCRS = dynamic_cast(this); - if (geogCRS) { + if (const GeographicCRS *geogCRS = + dynamic_cast(this)) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); auto cs = axisList.size() == 2 @@ -1040,8 +1046,8 @@ CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const { geogCRS->datumEnsemble(), cs)); } - const ProjectedCRS *projCRS = dynamic_cast(this); - if (projCRS) { + if (const ProjectedCRS *projCRS = + dynamic_cast(this)) { const auto &axisList = projCRS->coordinateSystem()->axisList(); auto cs = axisList.size() == 2 @@ -1054,6 +1060,20 @@ CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const { projCRS->derivingConversion(), cs)); } + if (const DerivedProjectedCRS *derivedProjCRS = + dynamic_cast(this)) { + const auto &axisList = derivedProjCRS->coordinateSystem()->axisList(); + auto cs = + axisList.size() == 2 + ? cs::CartesianCS::create(util::PropertyMap(), axisList[1], + axisList[0]) + : cs::CartesianCS::create(util::PropertyMap(), axisList[1], + axisList[0], axisList[2]); + return util::nn_static_pointer_cast(DerivedProjectedCRS::create( + createProperties(), derivedProjCRS->baseCRS(), + derivedProjCRS->derivingConversion(), cs)); + } + throw util::UnsupportedOperationException( "axis order reversal not supported on this type of CRS"); } @@ -1062,8 +1082,8 @@ CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const { CRSNNPtr CRS::normalizeForVisualization() const { - const CompoundCRS *compoundCRS = dynamic_cast(this); - if (compoundCRS) { + if (const CompoundCRS *compoundCRS = + dynamic_cast(this)) { const auto &comps = compoundCRS->componentReferenceSystems(); if (!comps.empty() && comps[0]->mustAxisOrderBeSwitchedForVisualization()) { @@ -1071,22 +1091,30 @@ CRSNNPtr CRS::normalizeForVisualization() const { } } - const GeographicCRS *geogCRS = dynamic_cast(this); - if (geogCRS) { + if (const GeographicCRS *geogCRS = + dynamic_cast(this)) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR); } } - const ProjectedCRS *projCRS = dynamic_cast(this); - if (projCRS) { + if (const ProjectedCRS *projCRS = + dynamic_cast(this)) { const auto &axisList = projCRS->coordinateSystem()->axisList(); if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR); } } + if (const DerivedProjectedCRS *derivedProjCRS = + dynamic_cast(this)) { + const auto &axisList = derivedProjCRS->coordinateSystem()->axisList(); + if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { + return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR); + } + } + return NN_NO_CHECK( std::static_pointer_cast(shared_from_this().as_nullable())); } diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index cdccddd03f..7868316ca3 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -4218,6 +4218,20 @@ static DerivedProjectedCRSNNPtr createDerivedProjectedCRS() { CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); } +static DerivedProjectedCRSNNPtr createDerivedProjectedCRSNorthingEasting() { + + auto derivingConversion = Conversion::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), + PropertyMap().set(IdentifiedObject::NAME_KEY, "PROJ unimplemented"), + std::vector{}, + std::vector{}); + + return DerivedProjectedCRS::create( + PropertyMap().set(IdentifiedObject::NAME_KEY, "derived projectedCRS"), + createProjected(), derivingConversion, + CartesianCS::createNorthingEasting(UnitOfMeasure::FOOT)); +} + // --------------------------------------------------------------------------- static DerivedVerticalCRSNNPtr createDerivedVerticalCRS() { @@ -6858,6 +6872,77 @@ TEST(crs, promoteTo3D_and_demoteTo2D) { // --------------------------------------------------------------------------- +TEST(crs, normalizeForVisualization_derivedprojected_operation) { + auto crs = createDerivedProjectedCRSNorthingEasting(); + + auto op = CoordinateOperationFactory::create()->createOperation( + GeographicCRS::EPSG_4326, crs); + + auto proj_string = + "+proj=pipeline +step +proj=axisswap +order=2,1 +step " + "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=utm +zone=31 " + "+ellps=WGS84 +step +proj=unimplemented +step +proj=unitconvert " + "+xy_in=m +xy_out=ft +step +proj=axisswap +order=2,1"; + + ASSERT_TRUE(op != nullptr); + EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), + proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step " + "+proj=utm +zone=31 +ellps=WGS84 +step +proj=unimplemented +step " + "+proj=unitconvert +xy_in=m +xy_out=ft"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); +} + +// --------------------------------------------------------------------------- + +TEST(crs, normalizeForVisualization_derivedprojected) { + + auto crs = createDerivedProjectedCRSNorthingEasting(); + + { + const auto &axisList = crs->coordinateSystem()->axisList(); + ASSERT_EQ(axisList.size(), 2U); + EXPECT_EQ(axisList[0]->direction(), + osgeo::proj::cs::AxisDirection::NORTH); + EXPECT_EQ(axisList[1]->direction(), + osgeo::proj::cs::AxisDirection::EAST); + } + + { + auto normalized = nn_dynamic_pointer_cast( + crs->normalizeForVisualization()); + const auto &normalizedAxisList = + normalized->coordinateSystem()->axisList(); + ASSERT_EQ(normalizedAxisList.size(), 2U); + EXPECT_EQ(normalizedAxisList[0]->direction(), + osgeo::proj::cs::AxisDirection::EAST); + EXPECT_EQ(normalizedAxisList[1]->direction(), + osgeo::proj::cs::AxisDirection::NORTH); + } + + { + auto normalized3D = nn_dynamic_pointer_cast( + crs->promoteTo3D(std::string(), nullptr) + ->normalizeForVisualization()); + const auto &normalized3DAxisList = + normalized3D->coordinateSystem()->axisList(); + ASSERT_EQ(normalized3DAxisList.size(), 3U); + EXPECT_EQ(normalized3DAxisList[0]->direction(), + osgeo::proj::cs::AxisDirection::EAST); + EXPECT_EQ(normalized3DAxisList[1]->direction(), + osgeo::proj::cs::AxisDirection::NORTH); + EXPECT_EQ(normalized3DAxisList[2]->direction(), + osgeo::proj::cs::AxisDirection::UP); + } +} + +// --------------------------------------------------------------------------- + TEST(crs, projected_normalizeForVisualization_do_not_mess_deriving_conversion) { auto authFactory =