aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-04-03 19:41:23 +0200
committerEven Rouault <even.rouault@spatialys.com>2020-04-03 19:50:48 +0200
commitbb770de3b7cb04aad8116ed84a5815b7f6f7a82c (patch)
tree4f0ecbe08d2ab975966d65f85e4485e4f4050324
parent1a8933942cf92d0724cc168d04302d8b78c3814f (diff)
downloadPROJ-bb770de3b7cb04aad8116ed84a5815b7f6f7a82c.tar.gz
PROJ-bb770de3b7cb04aad8116ed84a5815b7f6f7a82c.zip
createFromUserInput(): allow compound CRS with the 2 parts given by names, e.g. 'WGS 84 + EGM96 height'
-rw-r--r--docs/source/apps/projinfo.rst3
-rw-r--r--src/iso19111/c_api.cpp4
-rw-r--r--src/iso19111/io.cpp107
-rw-r--r--test/unit/test_io.cpp30
4 files changed, 122 insertions, 22 deletions
diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst
index 91d09941..0e54c244 100644
--- a/docs/source/apps/projinfo.rst
+++ b/docs/source/apps/projinfo.rst
@@ -36,6 +36,8 @@ Synopsis
- a WKT string,
- an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
"urn:ogc:def:coordinateOperation:EPSG::1671"),
+ - an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as
+ uniqueness is not guaranteed, heuristics are applied to determine the appropriate best match.
- a OGC URN combining references for compound coordinate reference systems
(e.g "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" or custom abbreviated
syntax "EPSG:2393+5717"),
@@ -46,6 +48,7 @@ Synopsis
- a OGC URN combining references for concatenated operations
(e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618")
- a PROJJSON string. The jsonschema is at https://proj.org/schemas/v0.1/projjson.schema.json (*added in 6.2*)
+ - a compound CRS made from two object names separated with " + ". e.g. "WGS 84 + EGM96 height" (*added in 7.1*)
{object_reference} is a filename preceded by the '@' character. The
file referenced by the {object_reference} must contain a valid
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 17fbd079..5a3f0374 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -427,7 +427,9 @@ PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) {
/** \brief Instantiate an object from a WKT string, PROJ string, object code
* (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
- * "urn:ogc:def:coordinateOperation:EPSG::1671") or PROJJSON string.
+ * "urn:ogc:def:coordinateOperation:EPSG::1671"), a PROJJSON string, an object
+ * name (e.g "WGS 84") of a compound CRS build from object names
+ * (e.g "WGS 84 + EGM96 height")
*
* This function calls osgeo::proj::io::createFromUserInput()
*
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 8bfb4395..dfc1aa27 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -6025,32 +6025,20 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
if (dbContext) {
auto factory =
AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string());
- // First pass: exact match on CRS objects
- // Second pass: exact match on other objects
- // Third pass: approximate match on CRS objects
- // Fourth pass: approximate match on other objects
- constexpr size_t limitResultCount = 10;
- for (int pass = 0; pass <= 3; ++pass) {
- const bool approximateMatch = (pass >= 2);
+
+ const auto searchObject = [&factory](
+ const std::string &objectName, bool approximateMatch,
+ const std::vector<AuthorityFactory::ObjectType> &objectTypes,
+ bool &goOn) {
+ constexpr size_t limitResultCount = 10;
auto res = factory->createObjectsFromName(
- text,
- (pass == 0 || pass == 2)
- ? std::vector<
- AuthorityFactory::ObjectType>{AuthorityFactory::
- ObjectType::CRS}
- : std::vector<
- AuthorityFactory::
- ObjectType>{AuthorityFactory::ObjectType::
- ELLIPSOID,
- AuthorityFactory::ObjectType::DATUM,
- AuthorityFactory::ObjectType::
- COORDINATE_OPERATION},
- approximateMatch, limitResultCount);
+ objectName, objectTypes, approximateMatch, limitResultCount);
if (res.size() == 1) {
return res.front();
}
if (res.size() > 1) {
- if (pass == 0 || pass == 2) {
+ if (objectTypes.size() == 1 &&
+ objectTypes[0] == AuthorityFactory::ObjectType::CRS) {
for (size_t ndim = 2; ndim <= 3; ndim++) {
for (const auto &obj : res) {
auto crs =
@@ -6079,6 +6067,79 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
}
throw ParsingException(msg);
}
+ goOn = true;
+ throw ParsingException("dummy");
+ };
+
+ const auto searchCRS = [&searchObject](const std::string &objectName) {
+ bool goOn = false;
+ const auto objectTypes = std::vector<AuthorityFactory::ObjectType>{
+ AuthorityFactory::ObjectType::CRS};
+ try {
+ constexpr bool approximateMatch = false;
+ return searchObject(objectName, approximateMatch, objectTypes,
+ goOn);
+ } catch (const std::exception &) {
+ if (!goOn)
+ throw;
+ }
+ constexpr bool approximateMatch = true;
+ return searchObject(objectName, approximateMatch, objectTypes,
+ goOn);
+ };
+
+ // strings like "WGS 84 + EGM96 height"
+ CompoundCRSPtr compoundCRS;
+ try {
+ const auto tokensCompound = split(text, " + ");
+ if (tokensCompound.size() == 2) {
+ auto obj1 = searchCRS(tokensCompound[0]);
+ auto obj2 = searchCRS(tokensCompound[1]);
+ auto crs1 = util::nn_dynamic_pointer_cast<CRS>(obj1);
+ auto crs2 = util::nn_dynamic_pointer_cast<CRS>(obj2);
+ if (crs1 && crs2) {
+ compoundCRS =
+ CompoundCRS::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ crs1->nameStr() + " + " +
+ crs2->nameStr()),
+ {NN_NO_CHECK(crs1), NN_NO_CHECK(crs2)})
+ .as_nullable();
+ }
+ }
+ } catch (const std::exception &) {
+ }
+
+ // First pass: exact match on CRS objects
+ // Second pass: exact match on other objects
+ // Third pass: approximate match on CRS objects
+ // Fourth pass: approximate match on other objects
+ for (int pass = 0; pass <= 3; ++pass) {
+ const bool approximateMatch = (pass >= 2);
+ bool goOn = false;
+ try {
+ return searchObject(
+ text, approximateMatch,
+ (pass == 0 || pass == 2)
+ ? std::vector<
+ AuthorityFactory::ObjectType>{AuthorityFactory::
+ ObjectType::CRS}
+ : std::vector<
+ AuthorityFactory::
+ ObjectType>{AuthorityFactory::ObjectType::
+ ELLIPSOID,
+ AuthorityFactory::ObjectType::
+ DATUM,
+ AuthorityFactory::ObjectType::
+ COORDINATE_OPERATION},
+ goOn);
+ } catch (const std::exception &) {
+ if (!goOn)
+ throw;
+ }
+ if (compoundCRS) {
+ return NN_NO_CHECK(compoundCRS);
+ }
}
}
@@ -6114,6 +6175,8 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
* <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as
* uniqueness is not guaranteed, the function may apply heuristics to
* determine the appropriate best match.</li>
+ * <li>a compound CRS made from two object names separated with " + ".
+ * e.g. "WGS 84 + EGM96 height"</li>
* <li>PROJJSON string</li>
* </ul>
*
@@ -6163,6 +6226,8 @@ BaseObjectNNPtr createFromUserInput(const std::string &text,
* <li>an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as
* uniqueness is not guaranteed, the function may apply heuristics to
* determine the appropriate best match.</li>
+ * <li>a compound CRS made from two object names separated with " + ".
+ * e.g. "WGS 84 + EGM96 height"</li>
* <li>PROJJSON string</li>
* </ul>
*
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 9d6c2c43..6cdd3014 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -9548,6 +9548,36 @@ TEST(io, createFromUserInput) {
ParsingException);
EXPECT_NO_THROW(createFromUserInput("WGS84 UTM zone 31N", dbContext));
EXPECT_NO_THROW(createFromUserInput("ID74", dbContext));
+
+ {
+ // Exact match on each piece of the compound CRS
+ auto obj = createFromUserInput("WGS 84 + EGM96 height", dbContext);
+ auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->nameStr(), "WGS 84 + EGM96 height");
+ }
+
+ {
+ // Aproximate match on each piece of the compound CRS
+ auto obj = createFromUserInput("WGS84 + EGM96", dbContext);
+ auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->nameStr(), "WGS 84 + EGM96 height");
+ }
+
+ {
+ // Exact match of a CompoundCRS object
+ auto obj = createFromUserInput(
+ "WGS 84 / World Mercator + EGM2008 height", dbContext);
+ auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->identifiers().size(), 1U);
+ }
+
+ EXPECT_THROW(createFromUserInput("WGS 84 + foobar", dbContext),
+ ParsingException);
+ EXPECT_THROW(createFromUserInput("foobar + EGM96 height", dbContext),
+ ParsingException);
}
// ---------------------------------------------------------------------------