Skip to content

Commit

Permalink
proj_create(): add support for compoundCRS and concatenatedOperation …
Browse files Browse the repository at this point in the history
…named from their components

Support following syntaxes:
- OGC URN combining references for compoundCRS:
  e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717"
- its GDAL shortcut: e.g. "EPSG:2393+5717"
- OGC URN combining references for concatenated operations:
  e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"
  • Loading branch information
rouault committed Apr 22, 2019
1 parent 1ebec58 commit 292807e
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 17 deletions.
17 changes: 11 additions & 6 deletions docs/source/apps/projinfo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ Synopsis
| {object_definition} | (-s {srs_def} -t {srs_def})
|
where {object_definition} or {object_definition} is a PROJ string, a
WKT string, an object name, AUTHORITY:CODE
(where AUTHORITY is the name of a CRS authority and CODE the code of a CRS
found in the proj.db database) or a OGC URN (such as "urn:ogc:def:crs:EPSG::4326",
"urn:ogc:def:coordinateOperation:EPSG::1671", "urn:ogc:def:ellipsoid:EPSG::7001"
or "urn:ogc:def:datum:EPSG::6326")
where {object_definition} or {srs_def} is

- a proj-string,
- a WKT string,
- an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
"urn:ogc:def:coordinateOperation:EPSG::1671"),
- 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"),
- a OGC URN combining references for concatenated operations
(e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618")

Description
***********
Expand Down
17 changes: 13 additions & 4 deletions docs/source/development/reference/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,17 @@ paragraph for more details.
.. c:function:: PJ* proj_create(PJ_CONTEXT *ctx, const char *definition)
Create a transformation object, or a CRS object, from a proj-string,
a WKT string, or object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
"urn:ogc:def:coordinateOperation:EPSG::1671").
Create a transformation object, or a CRS object, from:
- a proj-string,
- a WKT string,
- an object code (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
"urn:ogc:def:coordinateOperation:EPSG::1671"),
- 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"),
- a OGC URN combining references for concatenated operations
(e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618")
Example call:
Expand Down Expand Up @@ -110,7 +118,8 @@ paragraph for more details.
- the name of a CRS as found in the PROJ database, e.g "WGS84", "NAD27", etc.
- more generally any string accepted by :c:func:`proj_create`
- more generally any string accepted by :c:func:`proj_create` representing
a CRS
An "area of use" can be specified in area. When it is supplied, the more
accurate transformation between two given systems can be chosen.
Expand Down
8 changes: 4 additions & 4 deletions include/proj/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -993,16 +993,16 @@ class PROJ_GCC_DLL AuthorityFactory {

PROJ_INTERNAL std::list<crs::CompoundCRSNNPtr>
createCompoundCRSFromExisting(const crs::CompoundCRSNNPtr &crs) const;

PROJ_INTERNAL crs::CRSNNPtr
createCoordinateReferenceSystem(const std::string &code,
bool allowCompound) const;
//! @endcond

protected:
PROJ_INTERNAL AuthorityFactory(const DatabaseContextNNPtr &context,
const std::string &authorityName);

PROJ_INTERNAL crs::CRSNNPtr
createCoordinateReferenceSystem(const std::string &code,
bool allowCompound) const;

PROJ_INTERNAL crs::GeodeticCRSNNPtr
createGeodeticCRS(const std::string &code, bool geographicOnly) const;

Expand Down
5 changes: 5 additions & 0 deletions src/iso19111/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2484,6 +2484,8 @@ crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem(
return createCoordinateReferenceSystem(code, true);
}

//! @cond Doxygen_Suppress

crs::CRSNNPtr
AuthorityFactory::createCoordinateReferenceSystem(const std::string &code,
bool allowCompound) const {
Expand Down Expand Up @@ -2513,6 +2515,9 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code,
}
throw FactoryException("unhandled CRS type: " + type);
}

//! @endcond

// ---------------------------------------------------------------------------

//! @cond Doxygen_Suppress
Expand Down
102 changes: 99 additions & 3 deletions src/iso19111/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4397,15 +4397,97 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
const auto authorities = dbContextNNPtr->getAuthorities();
for (const auto &authCandidate : authorities) {
if (ci_equal(authCandidate, authName)) {
return AuthorityFactory::create(dbContextNNPtr,
authCandidate)
->createCoordinateReferenceSystem(code);
factory =
AuthorityFactory::create(dbContextNNPtr, authCandidate);
try {
return factory->createCoordinateReferenceSystem(code);
} catch (...) {
// EPSG:4326+3855
auto tokensCode = split(code, '+');
if (tokensCode.size() == 2) {
auto crs1(factory->createCoordinateReferenceSystem(
tokensCode[0], false));
auto crs2(factory->createCoordinateReferenceSystem(
tokensCode[1], false));
return CompoundCRS::create(
util::PropertyMap().set(
IdentifiedObject::NAME_KEY,
crs1->nameStr() + " + " + crs2->nameStr()),
{crs1, crs2});
}
throw;
}
}
}
throw;
}
}

// OGC 07-092r2: para 7.5.2
// URN combined references for compound coordinate reference systems
if (starts_with(text, "urn:ogc:def:crs,")) {
if (!dbContext) {
throw ParsingException("no database context specified");
}
auto tokensComma = split(text, ',');
std::vector<CRSNNPtr> components;
std::string name;
for (size_t i = 1; i < tokensComma.size(); i++) {
tokens = split(tokensComma[i], ':');
if (tokens.size() != 4) {
throw ParsingException(
concat("invalid crs component: ", tokensComma[i]));
}
const auto &type = tokens[0];
auto factory =
AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]);
const auto &code = tokens[3];
if (type == "crs") {
auto crs(factory->createCoordinateReferenceSystem(code, false));
components.emplace_back(crs);
if (!name.empty()) {
name += " + ";
}
name += crs->nameStr();
} else {
throw ParsingException(
concat("unexpected object type: ", type));
}
}
return CompoundCRS::create(
util::PropertyMap().set(IdentifiedObject::NAME_KEY, name),
components);
}

// OGC 07-092r2: para 7.5.3
// 7.5.3 URN combined references for concatenated operations
if (starts_with(text, "urn:ogc:def:coordinateOperation,")) {
if (!dbContext) {
throw ParsingException("no database context specified");
}
auto tokensComma = split(text, ',');
std::vector<CoordinateOperationNNPtr> components;
for (size_t i = 1; i < tokensComma.size(); i++) {
tokens = split(tokensComma[i], ':');
if (tokens.size() != 4) {
throw ParsingException(concat(
"invalid coordinateOperation component: ", tokensComma[i]));
}
const auto &type = tokens[0];
auto factory =
AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]);
const auto &code = tokens[3];
if (type == "coordinateOperation") {
auto op(factory->createCoordinateOperation(code, false));
components.emplace_back(op);
} else {
throw ParsingException(
concat("unexpected object type: ", type));
}
}
return ConcatenatedOperation::createComputeMetadata(components, true);
}

// urn:ogc:def:crs:EPSG::4326
if (tokens.size() == 7) {
if (!dbContext) {
Expand Down Expand Up @@ -4510,6 +4592,13 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
* "urn:ogc:def:coordinateOperation:EPSG::1671",
* "urn:ogc:def:ellipsoid:EPSG::7001"
* or "urn:ogc:def:datum:EPSG::6326"</li>
* <li> OGC URN combining references for compound coordinate reference systems
* e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717"
* We also accept a custom abbreviated syntax EPSG:2393+5717
* </li>
* <li> OGC URN combining references for concatenated operations
* e.g.
* "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li>
* <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>
Expand Down Expand Up @@ -4546,6 +4635,13 @@ BaseObjectNNPtr createFromUserInput(const std::string &text,
* "urn:ogc:def:coordinateOperation:EPSG::1671",
* "urn:ogc:def:ellipsoid:EPSG::7001"
* or "urn:ogc:def:datum:EPSG::6326"</li>
* <li> OGC URN combining references for compound coordinate reference systems
* e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717"
* We also accept a custom abbreviated syntax EPSG:2393+5717
* </li>
* <li> OGC URN combining references for concatenated operations
* e.g.
* "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"</li>
* <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>
Expand Down
58 changes: 58 additions & 0 deletions test/unit/test_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8775,6 +8775,39 @@ TEST(io, createFromUserInput) {
EXPECT_THROW(
createFromUserInput("urn:ogc:def:unhandled:EPSG::4326", dbContext),
ParsingException);
EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:non_existing_auth::4326",
dbContext),
NoSuchAuthorityCodeException);
EXPECT_THROW(createFromUserInput(
"urn:ogc:def:crs,crs:EPSG::2393,unhandled_type:EPSG::5717",
dbContext),
ParsingException);
EXPECT_THROW(createFromUserInput(
"urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::unexisting_code",
dbContext),
NoSuchAuthorityCodeException);
EXPECT_THROW(
createFromUserInput(
"urn:ogc:def:crs,crs:EPSG::2393::extra_element,crs:EPSG::EPSG",
dbContext),
ParsingException);
EXPECT_THROW(createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895,"
"unhandled_type:EPSG::1618",
dbContext),
ParsingException);
EXPECT_THROW(
createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895,"
"coordinateOperation:EPSG::unexisting_code",
dbContext),
NoSuchAuthorityCodeException);
EXPECT_THROW(
createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895::extra_element,"
"coordinateOperation:EPSG::1618",
dbContext),
ParsingException);

EXPECT_NO_THROW(createFromUserInput("+proj=longlat", nullptr));
EXPECT_NO_THROW(createFromUserInput("EPSG:4326", dbContext));
Expand All @@ -8789,6 +8822,31 @@ TEST(io, createFromUserInput) {
createFromUserInput("urn:ogc:def:meridian:EPSG::8901", dbContext));
EXPECT_NO_THROW(
createFromUserInput("urn:ogc:def:ellipsoid:EPSG::7030", dbContext));
{
auto obj = createFromUserInput("EPSG:2393+5717", dbContext);
auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(),
"KKJ / Finland Uniform Coordinate System + N60 height");
}
{
auto obj = createFromUserInput(
"urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717", dbContext);
auto crs = nn_dynamic_pointer_cast<CompoundCRS>(obj);
ASSERT_TRUE(crs != nullptr);
EXPECT_EQ(crs->nameStr(),
"KKJ / Finland Uniform Coordinate System + N60 height");
}
{
auto obj = createFromUserInput("urn:ogc:def:coordinateOperation,"
"coordinateOperation:EPSG::3895,"
"coordinateOperation:EPSG::1618",
dbContext);
auto concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(obj);
ASSERT_TRUE(concat != nullptr);
EXPECT_EQ(concat->nameStr(),
"MGI (Ferro) to MGI (1) + MGI to WGS 84 (3)");
}
EXPECT_NO_THROW(createFromUserInput(
"GEOGCRS[\"WGS 84\",\n"
" DATUM[\"World Geodetic System 1984\",\n"
Expand Down

0 comments on commit 292807e

Please sign in to comment.