aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/crs.hpp2
-rw-r--r--include/proj/internal/Makefile.am1
-rw-r--r--include/proj/internal/crs_internal.hpp41
-rw-r--r--src/iso19111/crs.cpp150
-rw-r--r--src/iso19111/operation/concatenatedoperation.cpp36
-rw-r--r--test/unit/test_operation.cpp17
6 files changed, 190 insertions, 57 deletions
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index 5a8e75ae..ce950c47 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -142,6 +142,8 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage,
PROJ_INTERNAL bool mustAxisOrderBeSwitchedForVisualization() const;
+ PROJ_INTERNAL CRSNNPtr applyAxisOrderReversal(const char *nameSuffix) const;
+
PROJ_FOR_TEST CRSNNPtr normalizeForVisualization() const;
PROJ_INTERNAL CRSNNPtr allowNonConformantWKT1Export() const;
diff --git a/include/proj/internal/Makefile.am b/include/proj/internal/Makefile.am
index 59325667..25b71cc5 100644
--- a/include/proj/internal/Makefile.am
+++ b/include/proj/internal/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = vendor
noinst_HEADERS = \
+ crs_internal.hpp \
coordinatesystem_internal.hpp \
internal.hpp \
io_internal.hpp \
diff --git a/include/proj/internal/crs_internal.hpp b/include/proj/internal/crs_internal.hpp
new file mode 100644
index 00000000..9a68c8b0
--- /dev/null
+++ b/include/proj/internal/crs_internal.hpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: ISO19111:2019 implementation
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#ifndef FROM_PROJ_CPP
+#error This file should only be included from a PROJ cpp file
+#endif
+
+#ifndef CRS_INTERNAL_HH_INCLUDED
+#define CRS_INTERNAL_HH_INCLUDED
+
+#define NORMALIZED_AXIS_ORDER_SUFFIX_STR \
+ " (with axis order normalized for visualization)"
+
+#define AXIS_ORDER_REVERSED_SUFFIX_STR " (with axis order reversed)"
+
+#endif // CRS_INTERNAL_HH_INCLUDED
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index b7d57767..6d213bc6 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -43,6 +43,7 @@
#include "proj/util.hpp"
#include "proj/internal/coordinatesystem_internal.hpp"
+#include "proj/internal/crs_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"
@@ -887,51 +888,63 @@ void CRS::setProperties(
//! @cond Doxygen_Suppress
-CRSNNPtr CRS::normalizeForVisualization() const {
+// ---------------------------------------------------------------------------
- const auto createProperties = [this](const std::string &newName =
- std::string()) {
- auto props = util::PropertyMap().set(
- common::IdentifiedObject::NAME_KEY,
- !newName.empty()
- ? newName
- : nameStr() +
- " (with axis order normalized for visualization)");
- const auto &l_domains = domains();
- if (!l_domains.empty()) {
- auto array = util::ArrayOfBaseObject::create();
- for (const auto &domain : l_domains) {
- array->add(domain);
+CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const {
+
+ const auto createProperties =
+ [this, nameSuffix](const std::string &newNameIn = std::string()) {
+ std::string newName(newNameIn);
+ if (newName.empty()) {
+ newName = nameStr();
+ if (ends_with(newName, NORMALIZED_AXIS_ORDER_SUFFIX_STR)) {
+ newName.resize(newName.size() -
+ strlen(NORMALIZED_AXIS_ORDER_SUFFIX_STR));
+ } else if (ends_with(newName, AXIS_ORDER_REVERSED_SUFFIX_STR)) {
+ newName.resize(newName.size() -
+ strlen(AXIS_ORDER_REVERSED_SUFFIX_STR));
+ } else {
+ newName += nameSuffix;
+ }
}
- if (!array->empty()) {
- props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY, array);
+ auto props = util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY, newName);
+ const auto &l_domains = domains();
+ if (!l_domains.empty()) {
+ auto array = util::ArrayOfBaseObject::create();
+ for (const auto &domain : l_domains) {
+ array->add(domain);
+ }
+ if (!array->empty()) {
+ props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY, array);
+ }
}
- }
- const auto &l_identifiers = identifiers();
- const auto &l_remarks = remarks();
- if (l_identifiers.size() == 1) {
- std::string remarks("Axis order reversed compared to ");
- remarks += *(l_identifiers[0]->codeSpace());
- remarks += ':';
- remarks += l_identifiers[0]->code();
- if (!l_remarks.empty()) {
- remarks += ". ";
- remarks += l_remarks;
+ const auto &l_identifiers = identifiers();
+ const auto &l_remarks = remarks();
+ if (l_identifiers.size() == 1) {
+ std::string remarks("Axis order reversed compared to ");
+ if (!starts_with(l_remarks, remarks)) {
+ remarks += *(l_identifiers[0]->codeSpace());
+ remarks += ':';
+ remarks += l_identifiers[0]->code();
+ if (!l_remarks.empty()) {
+ remarks += ". ";
+ remarks += l_remarks;
+ }
+ props.set(common::IdentifiedObject::REMARKS_KEY, remarks);
+ }
+ } else if (!l_remarks.empty()) {
+ props.set(common::IdentifiedObject::REMARKS_KEY, l_remarks);
}
- props.set(common::IdentifiedObject::REMARKS_KEY, remarks);
- } else if (!l_remarks.empty()) {
- props.set(common::IdentifiedObject::REMARKS_KEY, l_remarks);
- }
- return props;
- };
+ return props;
+ };
const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
if (compoundCRS) {
const auto &comps = compoundCRS->componentReferenceSystems();
- if (!comps.empty() &&
- comps[0]->mustAxisOrderBeSwitchedForVisualization()) {
+ if (!comps.empty()) {
std::vector<CRSNNPtr> newComps;
- newComps.emplace_back(comps[0]->normalizeForVisualization());
+ newComps.emplace_back(comps[0]->applyAxisOrderReversal(nameSuffix));
std::string l_name = newComps.back()->nameStr();
for (size_t i = 1; i < comps.size(); i++) {
newComps.emplace_back(comps[i]);
@@ -946,16 +959,53 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
+ auto cs =
+ axisList.size() == 2
+ ? cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1],
+ axisList[0])
+ : cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1],
+ axisList[0], axisList[2]);
+ return util::nn_static_pointer_cast<CRS>(
+ GeographicCRS::create(createProperties(), geogCRS->datum(),
+ geogCRS->datumEnsemble(), cs));
+ }
+
+ const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
+ if (projCRS) {
+ const auto &axisList = projCRS->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<CRS>(
+ ProjectedCRS::create(createProperties(), projCRS->baseCRS(),
+ projCRS->derivingConversion(), cs));
+ }
+
+ throw util::UnsupportedOperationException(
+ "axis order reversal not supported on this type of CRS");
+}
+
+// ---------------------------------------------------------------------------
+
+CRSNNPtr CRS::normalizeForVisualization() const {
+
+ const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
+ if (compoundCRS) {
+ const auto &comps = compoundCRS->componentReferenceSystems();
+ if (!comps.empty() &&
+ comps[0]->mustAxisOrderBeSwitchedForVisualization()) {
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ }
+ }
+
+ const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
+ if (geogCRS) {
+ const auto &axisList = geogCRS->coordinateSystem()->axisList();
if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
- auto cs = axisList.size() == 2
- ? cs::EllipsoidalCS::create(util::PropertyMap(),
- axisList[1], axisList[0])
- : cs::EllipsoidalCS::create(util::PropertyMap(),
- axisList[1], axisList[0],
- axisList[2]);
- return util::nn_static_pointer_cast<CRS>(
- GeographicCRS::create(createProperties(), geogCRS->datum(),
- geogCRS->datumEnsemble(), cs));
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
}
}
@@ -963,15 +1013,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
if (projCRS) {
const auto &axisList = projCRS->coordinateSystem()->axisList();
if (mustAxisOrderBeSwitchedForVisualizationInternal(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<CRS>(
- ProjectedCRS::create(createProperties(), projCRS->baseCRS(),
- projCRS->derivingConversion(), cs));
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
}
}
diff --git a/src/iso19111/operation/concatenatedoperation.cpp b/src/iso19111/operation/concatenatedoperation.cpp
index 185ebb4a..7da561b4 100644
--- a/src/iso19111/operation/concatenatedoperation.cpp
+++ b/src/iso19111/operation/concatenatedoperation.cpp
@@ -37,6 +37,7 @@
#include "proj/metadata.hpp"
#include "proj/util.hpp"
+#include "proj/internal/crs_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"
@@ -245,15 +246,44 @@ void ConcatenatedOperation::fixStepsDirection(
return false;
};
+ // Apply axis order reversal operation on first operation if needed
+ // to set CRSs on it
+ if (operationsInOut.size() >= 1) {
+ auto &op = operationsInOut.front();
+ auto l_sourceCRS = op->sourceCRS();
+ auto l_targetCRS = op->targetCRS();
+ auto conv = dynamic_cast<const Conversion *>(op.get());
+ if (conv && !l_sourceCRS && !l_targetCRS &&
+ isAxisOrderReversal(conv->method()->getEPSGCode())) {
+ auto reversedCRS = concatOpSourceCRS->applyAxisOrderReversal(
+ NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ op->setCRSs(concatOpSourceCRS, reversedCRS, nullptr);
+ }
+ }
+
+ // Apply axis order reversal operation on last operation if needed
+ // to set CRSs on it
+ if (operationsInOut.size() >= 2) {
+ auto &op = operationsInOut.back();
+ auto l_sourceCRS = op->sourceCRS();
+ auto l_targetCRS = op->targetCRS();
+ auto conv = dynamic_cast<const Conversion *>(op.get());
+ if (conv && !l_sourceCRS && !l_targetCRS &&
+ isAxisOrderReversal(conv->method()->getEPSGCode())) {
+ auto reversedCRS = concatOpTargetCRS->applyAxisOrderReversal(
+ NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ op->setCRSs(reversedCRS, concatOpTargetCRS, nullptr);
+ }
+ }
+
for (size_t i = 0; i < operationsInOut.size(); ++i) {
auto &op = operationsInOut[i];
auto l_sourceCRS = op->sourceCRS();
auto l_targetCRS = op->targetCRS();
auto conv = dynamic_cast<const Conversion *>(op.get());
if (conv && i == 0 && !l_sourceCRS && !l_targetCRS) {
- auto derivedCRS =
- dynamic_cast<const crs::DerivedCRS *>(concatOpSourceCRS.get());
- if (derivedCRS) {
+ if (auto derivedCRS = dynamic_cast<const crs::DerivedCRS *>(
+ concatOpSourceCRS.get())) {
if (i + 1 < operationsInOut.size()) {
// use the sourceCRS of the next operation as our target CRS
l_targetCRS = operationsInOut[i + 1]->sourceCRS();
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index f76647e2..0e27aa9b 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5429,6 +5429,20 @@ TEST(operation, normalizeForVisualization) {
auto authFactory =
AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ const auto checkThroughWKTRoundtrip = [](const CoordinateOperationNNPtr
+ &opRef) {
+ auto wkt = opRef->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get());
+ auto objFromWkt = WKTParser().createFromWKT(wkt);
+ auto opFromWkt =
+ nn_dynamic_pointer_cast<CoordinateOperation>(objFromWkt);
+ ASSERT_TRUE(opFromWkt != nullptr);
+ EXPECT_TRUE(opRef->_isEquivalentTo(opFromWkt.get()));
+ EXPECT_EQ(
+ opFromWkt->exportToPROJString(PROJStringFormatter::create().get()),
+ opRef->exportToPROJString(PROJStringFormatter::create().get()));
+ };
+
// Source(geographic) must be inverted
{
auto src = authFactory->createCoordinateReferenceSystem("4326");
@@ -5443,6 +5457,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +proj=unitconvert +xy_in=deg +xy_out=rad "
"+step +proj=utm +zone=31 +ellps=WGS84");
+ checkThroughWKTRoundtrip(opNormalized);
}
// Target(geographic) must be inverted
@@ -5459,6 +5474,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +inv +proj=utm +zone=31 +ellps=WGS84 "
"+step +proj=unitconvert +xy_in=rad +xy_out=deg");
+ checkThroughWKTRoundtrip(opNormalized);
}
// Source(geographic) and target(projected) must be inverted
@@ -5475,6 +5491,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +proj=unitconvert +xy_in=deg +xy_out=rad "
"+step +proj=utm +zone=28 +ellps=GRS80");
+ checkThroughWKTRoundtrip(opNormalized);
}
// No inversion