aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2022-02-14 20:40:25 +0100
committerGitHub <noreply@github.com>2022-02-14 20:40:25 +0100
commitd4deb549982a16c0b7b324739af781d2779f2324 (patch)
tree0ac30dd48e217392fcf1b3937e025adaa81b9730
parentf67c82912fde55a58d565697263306c840b3efd4 (diff)
parentdf9e2fe519d97139a8264057d96b034788e8e8e7 (diff)
downloadPROJ-d4deb549982a16c0b7b324739af781d2779f2324.tar.gz
PROJ-d4deb549982a16c0b7b324739af781d2779f2324.zip
Merge pull request #3055 from rouault/over_fix
Better deal with importing strings like '+init=epsg:XXXX +over' (refs MapServer/MapServer#6478)
-rw-r--r--include/proj/crs.hpp2
-rw-r--r--src/iso19111/crs.cpp42
-rw-r--r--src/iso19111/io.cpp10
-rw-r--r--src/iso19111/operation/conversion.cpp6
-rw-r--r--src/iso19111/operation/coordinateoperationfactory.cpp3
-rw-r--r--test/unit/test_c_api.cpp38
-rw-r--r--test/unit/test_io.cpp4
7 files changed, 91 insertions, 14 deletions
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index ce950c47..593bfc4b 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -158,6 +158,8 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage,
PROJ_INTERNAL bool hasImplicitCS() const;
+ PROJ_INTERNAL bool hasOver() const;
+
PROJ_INTERNAL static CRSNNPtr
getResolvedCRS(const CRSNNPtr &crs,
const io::AuthorityFactoryPtr &authFactory,
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 2ea8393c..4750896a 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -98,20 +98,36 @@ struct CRS::Private {
BoundCRSPtr canonicalBoundCRS_{};
std::string extensionProj4_{};
bool implicitCS_ = false;
+ bool over_ = false;
bool allowNonConformantWKT1Export_ = false;
// for what was initially a COMPD_CS with a VERT_CS with a datum type ==
// ellipsoidal height / 2002
CompoundCRSPtr originalCompoundCRS_{};
- void setImplicitCS(const util::PropertyMap &properties) {
- const auto pVal = properties.get("IMPLICIT_CS");
- if (pVal) {
- if (const auto genVal =
- dynamic_cast<const util::BoxedValue *>(pVal->get())) {
- if (genVal->type() == util::BoxedValue::Type::BOOLEAN &&
- genVal->booleanValue()) {
- implicitCS_ = true;
+ void setNonStandardProperties(const util::PropertyMap &properties) {
+ {
+ const auto pVal = properties.get("IMPLICIT_CS");
+ if (pVal) {
+ if (const auto genVal =
+ dynamic_cast<const util::BoxedValue *>(pVal->get())) {
+ if (genVal->type() == util::BoxedValue::Type::BOOLEAN &&
+ genVal->booleanValue()) {
+ implicitCS_ = true;
+ }
+ }
+ }
+ }
+
+ {
+ const auto pVal = properties.get("OVER");
+ if (pVal) {
+ if (const auto genVal =
+ dynamic_cast<const util::BoxedValue *>(pVal->get())) {
+ if (genVal->type() == util::BoxedValue::Type::BOOLEAN &&
+ genVal->booleanValue()) {
+ over_ = true;
+ }
}
}
}
@@ -142,6 +158,9 @@ CRS::~CRS() = default;
* (e.g from ESRI WKT) */
bool CRS::hasImplicitCS() const { return d->implicitCS_; }
+/** \brief Return whether the CRS has a +over flag */
+bool CRS::hasOver() const { return d->over_; }
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -2854,7 +2873,7 @@ GeographicCRS::create(const util::PropertyMap &properties,
GeographicCRS::nn_make_shared<GeographicCRS>(datum, datumEnsemble, cs));
crs->assignSelf(crs);
crs->setProperties(properties);
- crs->CRS::getPrivate()->setImplicitCS(properties);
+ crs->CRS::getPrivate()->setNonStandardProperties(properties);
return crs;
}
@@ -3122,6 +3141,9 @@ void GeographicCRS::_exportToPROJString(
if (!formatter->getCRSExport()) {
addAngularUnitConvertAndAxisSwap(formatter);
}
+ if (hasOver()) {
+ formatter->addParam("over");
+ }
}
//! @endcond
@@ -4298,7 +4320,7 @@ ProjectedCRS::create(const util::PropertyMap &properties,
crs->assignSelf(crs);
crs->setProperties(properties);
crs->setDerivingConversionCRS();
- crs->CRS::getPrivate()->setImplicitCS(properties);
+ crs->CRS::getPrivate()->setNonStandardProperties(properties);
return crs;
}
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index a7fedb91..f047ef3e 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -10489,9 +10489,12 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
auto crs = dynamic_cast<CRS *>(obj.get());
bool hasSignificantParamValues = false;
+ bool hasOver = false;
for (const auto &kv : d->steps_[0].paramValues) {
- if (!((kv.key == "type" && kv.value == "crs") ||
- kv.key == "wktext" || kv.key == "no_defs")) {
+ if (kv.key == "over") {
+ hasOver = true;
+ } else if (!((kv.key == "type" && kv.value == "crs") ||
+ kv.key == "wktext" || kv.key == "no_defs")) {
hasSignificantParamValues = true;
break;
}
@@ -10502,6 +10505,9 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
properties.set(IdentifiedObject::NAME_KEY,
d->title_.empty() ? crs->nameStr()
: d->title_);
+ if (hasOver) {
+ properties.set("OVER", true);
+ }
const auto &extent = getExtent(crs);
if (extent) {
properties.set(
diff --git a/src/iso19111/operation/conversion.cpp b/src/iso19111/operation/conversion.cpp
index 21052a07..39c21439 100644
--- a/src/iso19111/operation/conversion.cpp
+++ b/src/iso19111/operation/conversion.cpp
@@ -3320,6 +3320,9 @@ static bool createPROJ4WebMercator(const Conversion *conv,
formatter->addParam("k", 1.0);
formatter->addParam("units", units);
formatter->addParam("nadgrids", "@null");
+ if (targetProjCRS && targetProjCRS->hasOver()) {
+ formatter->addParam("over");
+ }
formatter->addParam("wktext");
formatter->addParam("no_defs");
return true;
@@ -4095,6 +4098,9 @@ void Conversion::_exportToPROJString(
formatter->pushOmitZUnitConversion();
projCRS->addUnitConvertAndAxisSwap(formatter, bAxisSpecFound);
formatter->popOmitZUnitConversion();
+ if (projCRS->hasOver()) {
+ formatter->addParam("over");
+ }
}
auto derivedGeographicCRS =
diff --git a/src/iso19111/operation/coordinateoperationfactory.cpp b/src/iso19111/operation/coordinateoperationfactory.cpp
index e13d4071..6b7d4d20 100644
--- a/src/iso19111/operation/coordinateoperationfactory.cpp
+++ b/src/iso19111/operation/coordinateoperationfactory.cpp
@@ -5645,6 +5645,9 @@ namespace crs {
crs::CRSNNPtr CRS::getResolvedCRS(const crs::CRSNNPtr &crs,
const io::AuthorityFactoryPtr &authFactory,
metadata::ExtentPtr &extentOut) {
+ if (crs->hasOver()) {
+ return crs;
+ }
const auto &ids = crs->identifiers();
const auto &name = crs->nameStr();
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index 3e6ef1ab..ecbb2999 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -5392,6 +5392,44 @@ TEST_F(CApi, use_proj4_init_rules) {
PJ_CONTEXT *ctx = proj_context_create();
proj_context_use_proj4_init_rules(ctx, true);
ASSERT_TRUE(proj_context_get_use_proj4_init_rules(ctx, true));
+
+ {
+ // Test +over
+ auto crs = proj_create(ctx, "+init=epsg:28992 +over");
+ ObjectKeeper keeper_crs(crs);
+ ASSERT_NE(crs, nullptr);
+
+ auto datum = proj_crs_get_datum(ctx, crs);
+ ASSERT_NE(datum, nullptr);
+ ObjectKeeper keeper_datum(datum);
+
+ auto datum_name = proj_get_name(datum);
+ ASSERT_TRUE(datum_name != nullptr);
+ EXPECT_EQ(datum_name, std::string("Amersfoort"));
+
+ auto proj_5 = proj_as_proj_string(ctx, crs, PJ_PROJ_5, nullptr);
+ ASSERT_NE(proj_5, nullptr);
+ EXPECT_EQ(std::string(proj_5),
+ "+proj=sterea +lat_0=52.1561605555556 "
+ "+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 "
+ "+y_0=463000 +ellps=bessel +units=m +over "
+ "+no_defs +type=crs");
+ }
+
+ {
+ // Test +over on epsg:3857
+ auto crs = proj_create(ctx, "+init=epsg:3857 +over");
+ ObjectKeeper keeper_crs(crs);
+ ASSERT_NE(crs, nullptr);
+
+ auto proj_5 = proj_as_proj_string(ctx, crs, PJ_PROJ_5, nullptr);
+ ASSERT_NE(proj_5, nullptr);
+ EXPECT_EQ(std::string(proj_5),
+ "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 "
+ "+y_0=0 +k=1 +units=m +nadgrids=@null +over +wktext "
+ "+no_defs +type=crs");
+ }
+
proj_context_use_proj4_init_rules(ctx, false);
ASSERT_TRUE(!proj_context_get_use_proj4_init_rules(ctx, true));
proj_context_destroy(ctx);
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 1c7fc812..33f4c731 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -10531,13 +10531,13 @@ TEST(io, projparse_init) {
}
{
- auto obj = createFromUserInput("+title=mytitle +over +init=epsg:4326",
+ auto obj = createFromUserInput("+title=mytitle +init=epsg:4326 +over",
dbContext, true);
auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(), "mytitle");
EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
- "+proj=longlat +over +datum=WGS84 +no_defs +type=crs");
+ "+proj=longlat +datum=WGS84 +over +no_defs +type=crs");
}
{