aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/sql/customizations_early.sql2
-rw-r--r--data/sql/proj_db_table_defs.sql11
-rw-r--r--include/proj/io.hpp13
-rw-r--r--src/apps/projinfo.cpp12
-rw-r--r--src/iso19111/factory.cpp91
-rw-r--r--src/iso19111/io.cpp179
-rw-r--r--src/iso19111/metadata.cpp25
-rw-r--r--test/unit/test_crs.cpp47
-rw-r--r--test/unit/test_io.cpp16
9 files changed, 329 insertions, 67 deletions
diff --git a/data/sql/customizations_early.sql b/data/sql/customizations_early.sql
index fceef235..08c44b37 100644
--- a/data/sql/customizations_early.sql
+++ b/data/sql/customizations_early.sql
@@ -5,7 +5,7 @@ INSERT INTO "scope" VALUES('PROJ','SCOPE_UNKNOWN','Not known.',0);
INSERT INTO celestial_body VALUES('PROJ', 'EARTH', 'Earth', 6378137.0);
-
+INSERT INTO versioned_auth_name_mapping VALUES ('IAU_2015', 'IAU', '2015', 1);
CREATE TRIGGER conversion_method_check_insert_trigger_orthographic
INSTEAD OF INSERT ON conversion
diff --git a/data/sql/proj_db_table_defs.sql b/data/sql/proj_db_table_defs.sql
index bf7afca0..20ce940e 100644
--- a/data/sql/proj_db_table_defs.sql
+++ b/data/sql/proj_db_table_defs.sql
@@ -1515,3 +1515,14 @@ CREATE TABLE authority_to_authority_preference(
allowed_authorities TEXT NOT NULL, -- for example 'PROJ,EPSG,any'
CONSTRAINT unique_authority_to_authority_preference UNIQUE (source_auth_name, target_auth_name)
);
+
+-- Map 'IAU_2015' to auth_name=IAU and version=2015
+CREATE TABLE versioned_auth_name_mapping(
+ versioned_auth_name TEXT NOT NULL PRIMARY KEY,
+ auth_name TEXT NOT NULL,
+ version TEXT NOT NULL,
+ priority INTEGER NOT NULL,
+ CONSTRAINT unique_auth_name_version UNIQUE (auth_name, version),
+ CONSTRAINT unique_auth_name_priority UNIQUE (auth_name, priority)
+);
+
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index 6b811b2d..eefec11e 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -512,6 +512,8 @@ class PROJ_GCC_DLL JSONFormatter {
PROJ_INTERNAL CPLJSonStreamingWriter *
writer() const;
+ PROJ_INTERNAL const DatabaseContextPtr &databaseContext() const;
+
struct ObjectContext {
JSONFormatter &m_formatter;
@@ -912,6 +914,17 @@ class PROJ_GCC_DLL DatabaseContext {
getTransformationsForGridName(const DatabaseContextNNPtr &databaseContext,
const std::string &gridName);
+ PROJ_INTERNAL bool
+ getAuthorityAndVersion(const std::string &versionedAuthName,
+ std::string &authNameOut, std::string &versionOut);
+
+ PROJ_INTERNAL bool getVersionedAuthority(const std::string &authName,
+ const std::string &version,
+ std::string &versionedAuthNameOut);
+
+ PROJ_INTERNAL std::vector<std::string>
+ getVersionedAuthoritiesFromName(const std::string &authName);
+
//! @endcond
protected:
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 10944984..5dfc4f72 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -495,8 +495,8 @@ static void outputObject(
if (!outputOpt.quiet) {
std::cout << "WKT2:2015 string:" << std::endl;
}
- auto formatter =
- WKTFormatter::create(WKTFormatter::Convention::WKT2_2015);
+ auto formatter = WKTFormatter::create(
+ WKTFormatter::Convention::WKT2_2015, dbContext);
formatter->setMultiLine(!outputOpt.singleLine);
formatter->setStrict(outputOpt.strict);
auto wkt = wktExportable->exportToWKT(formatter.get());
@@ -520,7 +520,7 @@ static void outputObject(
std::cout << "WKT2:2015_SIMPLIFIED string:" << std::endl;
}
auto formatter = WKTFormatter::create(
- WKTFormatter::Convention::WKT2_2015_SIMPLIFIED);
+ WKTFormatter::Convention::WKT2_2015_SIMPLIFIED, dbContext);
if (outputOpt.singleLine) {
formatter->setMultiLine(false);
}
@@ -545,8 +545,8 @@ static void outputObject(
if (!outputOpt.quiet) {
std::cout << "WKT2:2019 string:" << std::endl;
}
- auto formatter =
- WKTFormatter::create(WKTFormatter::Convention::WKT2_2019);
+ auto formatter = WKTFormatter::create(
+ WKTFormatter::Convention::WKT2_2019, dbContext);
if (outputOpt.singleLine) {
formatter->setMultiLine(false);
}
@@ -572,7 +572,7 @@ static void outputObject(
std::cout << "WKT2:2019_SIMPLIFIED string:" << std::endl;
}
auto formatter = WKTFormatter::create(
- WKTFormatter::Convention::WKT2_2019_SIMPLIFIED);
+ WKTFormatter::Convention::WKT2_2019_SIMPLIFIED, dbContext);
if (outputOpt.singleLine) {
formatter->setMultiLine(false);
}
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 94c2de2c..f08e32b9 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -754,6 +754,14 @@ struct DatabaseContext::Private {
// cppcheck-suppress functionStatic
void cache(const std::string &code, const GridInfoCache &info);
+ struct VersionedAuthName {
+ std::string versionedAuthName{};
+ std::string authName{};
+ std::string version{};
+ int priority = 0;
+ };
+ const std::vector<VersionedAuthName> &getCacheAuthNameWithVersion();
+
private:
friend class DatabaseContext;
@@ -795,6 +803,8 @@ struct DatabaseContext::Private {
lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{
CACHE_SIZE};
+ std::vector<VersionedAuthName> cacheAuthNameWithVersion_{};
+
static void insertIntoCache(LRUCacheOfObjects &cache,
const std::string &code,
const util::BaseObjectPtr &obj);
@@ -3558,6 +3568,87 @@ DatabaseContext::getNonDeprecated(const std::string &tableName,
// ---------------------------------------------------------------------------
+const std::vector<DatabaseContext::Private::VersionedAuthName> &
+DatabaseContext::Private::getCacheAuthNameWithVersion() {
+ if (cacheAuthNameWithVersion_.empty()) {
+ const auto sqlRes =
+ run("SELECT versioned_auth_name, auth_name, version, priority "
+ "FROM versioned_auth_name_mapping");
+ for (const auto &row : sqlRes) {
+ VersionedAuthName van;
+ van.versionedAuthName = row[0];
+ van.authName = row[1];
+ van.version = row[2];
+ van.priority = atoi(row[3].c_str());
+ cacheAuthNameWithVersion_.emplace_back(std::move(van));
+ }
+ }
+ return cacheAuthNameWithVersion_;
+}
+
+// ---------------------------------------------------------------------------
+
+// From IAU_2015 returns (IAU,2015)
+bool DatabaseContext::getAuthorityAndVersion(
+ const std::string &versionedAuthName, std::string &authNameOut,
+ std::string &versionOut) {
+
+ for (const auto &van : d->getCacheAuthNameWithVersion()) {
+ if (van.versionedAuthName == versionedAuthName) {
+ authNameOut = van.authName;
+ versionOut = van.version;
+ return true;
+ }
+ }
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+
+// From IAU and 2015, returns IAU_2015
+bool DatabaseContext::getVersionedAuthority(const std::string &authName,
+ const std::string &version,
+ std::string &versionedAuthNameOut) {
+
+ for (const auto &van : d->getCacheAuthNameWithVersion()) {
+ if (van.authName == authName && van.version == version) {
+ versionedAuthNameOut = van.versionedAuthName;
+ return true;
+ }
+ }
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+
+// From IAU returns IAU_latest, ... IAU_2015
+std::vector<std::string>
+DatabaseContext::getVersionedAuthoritiesFromName(const std::string &authName) {
+
+ typedef std::pair<std::string, int> VersionedAuthNamePriority;
+ std::vector<VersionedAuthNamePriority> tmp;
+ for (const auto &van : d->getCacheAuthNameWithVersion()) {
+ if (van.authName == authName) {
+ tmp.emplace_back(
+ VersionedAuthNamePriority(van.versionedAuthName, van.priority));
+ }
+ }
+ std::vector<std::string> res;
+ if (!tmp.empty()) {
+ // Sort by decreasing priority
+ std::sort(tmp.begin(), tmp.end(),
+ [](const VersionedAuthNamePriority &a,
+ const VersionedAuthNamePriority &b) {
+ return b.second > a.second;
+ });
+ for (const auto &pair : tmp)
+ res.emplace_back(pair.first);
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
std::vector<operation::CoordinateOperationNNPtr>
DatabaseContext::getTransformationsForGridName(
const DatabaseContextNNPtr &databaseContext, const std::string &gridName) {
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index b317213a..9e7f0580 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -1518,6 +1518,23 @@ IdentifierPtr WKTParser::Private::buildId(const WKTNodeNNPtr &node,
codeSpace = codeSpace.substr(strlen("INVERSE("));
codeSpace.resize(codeSpace.size() - 1);
}
+
+ std::string version;
+ if (nodeChildren.size() >= 3 &&
+ nodeChildren[2]->GP()->childrenSize() == 0) {
+ version = stripQuotes(nodeChildren[2]);
+ }
+
+ // IAU + 2015 -> IAU_2015
+ if (dbContext_ && !version.empty()) {
+ std::string codeSpaceOut;
+ if (dbContext_->getVersionedAuthority(codeSpace, version,
+ codeSpaceOut)) {
+ codeSpace = codeSpaceOut;
+ version.clear();
+ }
+ }
+
auto code = stripQuotes(nodeChildren[1]);
auto &citationNode = nodeP->lookForChild(WKTConstants::CITATION);
auto &uriNode = nodeP->lookForChild(WKTConstants::URI);
@@ -1542,9 +1559,7 @@ IdentifierPtr WKTParser::Private::buildId(const WKTNodeNNPtr &node,
stripQuotes(uriNodeP->children()[0]));
}
}
- if (nodeChildren.size() >= 3 &&
- nodeChildren[2]->GP()->childrenSize() == 0) {
- auto version = stripQuotes(nodeChildren[2]);
+ if (!version.empty()) {
propertiesId.set(Identifier::VERSION_KEY, version);
}
return Identifier::create(code, propertiesId);
@@ -5381,24 +5396,10 @@ IdentifierNNPtr JSONParser::buildId(const json &j, bool removeInverseOf) {
codeSpace = codeSpace.substr(strlen("INVERSE("));
codeSpace.resize(codeSpace.size() - 1);
}
- propertiesId.set(metadata::Identifier::CODESPACE_KEY, codeSpace);
- propertiesId.set(metadata::Identifier::AUTHORITY_KEY, codeSpace);
- if (!j.contains("code")) {
- throw ParsingException("Missing \"code\" key");
- }
- std::string code;
- auto codeJ = j["code"];
- if (codeJ.is_string()) {
- code = codeJ.get<std::string>();
- } else if (codeJ.is_number_integer()) {
- code = internal::toString(codeJ.get<int>());
- } else {
- throw ParsingException("Unexpected type for value of \"code\"");
- }
+ std::string version;
if (j.contains("version")) {
auto versionJ = j["version"];
- std::string version;
if (versionJ.is_string()) {
version = versionJ.get<std::string>();
} else if (versionJ.is_number()) {
@@ -5413,6 +5414,34 @@ IdentifierNNPtr JSONParser::buildId(const json &j, bool removeInverseOf) {
} else {
throw ParsingException("Unexpected type for value of \"version\"");
}
+ }
+
+ // IAU + 2015 -> IAU_2015
+ if (dbContext_ && !version.empty()) {
+ std::string codeSpaceOut;
+ if (dbContext_->getVersionedAuthority(codeSpace, version,
+ codeSpaceOut)) {
+ codeSpace = codeSpaceOut;
+ version.clear();
+ }
+ }
+
+ propertiesId.set(metadata::Identifier::CODESPACE_KEY, codeSpace);
+ propertiesId.set(metadata::Identifier::AUTHORITY_KEY, codeSpace);
+ if (!j.contains("code")) {
+ throw ParsingException("Missing \"code\" key");
+ }
+ std::string code;
+ auto codeJ = j["code"];
+ if (codeJ.is_string()) {
+ code = codeJ.get<std::string>();
+ } else if (codeJ.is_number_integer()) {
+ code = internal::toString(codeJ.get<int>());
+ } else {
+ throw ParsingException("Unexpected type for value of \"code\"");
+ }
+
+ if (!version.empty()) {
propertiesId.set(Identifier::VERSION_KEY, version);
}
@@ -6392,6 +6421,61 @@ static CRSNNPtr importFromWMSAUTO(const std::string &text) {
// ---------------------------------------------------------------------------
+static BaseObjectNNPtr createFromURNPart(const DatabaseContextPtr &dbContext,
+ const std::string &type,
+ const std::string &authName,
+ const std::string &version,
+ const std::string &code) {
+ if (!dbContext) {
+ throw ParsingException("no database context specified");
+ }
+ try {
+ auto factory =
+ AuthorityFactory::create(NN_NO_CHECK(dbContext), authName);
+ if (type == "crs") {
+ return factory->createCoordinateReferenceSystem(code);
+ }
+ if (type == "coordinateOperation") {
+ return factory->createCoordinateOperation(code, true);
+ }
+ if (type == "datum") {
+ return factory->createDatum(code);
+ }
+ if (type == "ensemble") {
+ return factory->createDatumEnsemble(code);
+ }
+ if (type == "ellipsoid") {
+ return factory->createEllipsoid(code);
+ }
+ if (type == "meridian") {
+ return factory->createPrimeMeridian(code);
+ }
+ throw ParsingException(concat("unhandled object type: ", type));
+ } catch (...) {
+ if (version.empty()) {
+ const auto authoritiesFromAuthName =
+ dbContext->getVersionedAuthoritiesFromName(authName);
+ for (const auto &authNameVersioned : authoritiesFromAuthName) {
+ try {
+ return createFromURNPart(dbContext, type, authNameVersioned,
+ std::string(), code);
+ } catch (...) {
+ }
+ }
+ throw;
+ }
+ std::string authNameWithVersion;
+ if (!dbContext->getVersionedAuthority(authName, version,
+ authNameWithVersion)) {
+ throw;
+ }
+ return createFromURNPart(dbContext, type, authNameWithVersion,
+ std::string(), code);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
static BaseObjectNNPtr createFromUserInput(const std::string &text,
const DatabaseContextPtr &dbContext,
bool usePROJ4InitRules,
@@ -6481,8 +6565,19 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
return factory->createCoordinateReferenceSystem(code);
}
- const auto authorities = dbContextNNPtr->getAuthorities();
- for (const auto &authCandidate : authorities) {
+ const auto authoritiesFromAuthName =
+ dbContextNNPtr->getVersionedAuthoritiesFromName(authName);
+ for (const auto &authNameVersioned : authoritiesFromAuthName) {
+ factory =
+ AuthorityFactory::create(dbContextNNPtr, authNameVersioned);
+ try {
+ return factory->createCoordinateReferenceSystem(code);
+ } catch (...) {
+ }
+ }
+
+ const auto allAuthorities = dbContextNNPtr->getAuthorities();
+ for (const auto &authCandidate : allAuthorities) {
if (ci_equal(authCandidate, authName)) {
factory =
AuthorityFactory::create(dbContextNNPtr, authCandidate);
@@ -6740,42 +6835,14 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
return ConcatenatedOperation::createComputeMetadata(components, true);
}
- const auto createFromURNPart =
- [&dbContext](const std::string &type, const std::string &authName,
- const std::string &code) -> BaseObjectNNPtr {
- if (!dbContext) {
- throw ParsingException("no database context specified");
- }
- auto factory =
- AuthorityFactory::create(NN_NO_CHECK(dbContext), authName);
- if (type == "crs") {
- return factory->createCoordinateReferenceSystem(code);
- }
- if (type == "coordinateOperation") {
- return factory->createCoordinateOperation(code, true);
- }
- if (type == "datum") {
- return factory->createDatum(code);
- }
- if (type == "ensemble") {
- return factory->createDatumEnsemble(code);
- }
- if (type == "ellipsoid") {
- return factory->createEllipsoid(code);
- }
- if (type == "meridian") {
- return factory->createPrimeMeridian(code);
- }
- throw ParsingException(concat("unhandled object type: ", type));
- };
-
// urn:ogc:def:crs:EPSG::4326
if (tokens.size() == 7 && tolower(tokens[0]) == "urn") {
const auto type = tokens[3] == "CRS" ? "crs" : tokens[3];
const auto &authName = tokens[4];
+ const auto &version = tokens[5];
const auto &code = tokens[6];
- return createFromURNPart(type, authName, code);
+ return createFromURNPart(dbContext, type, authName, version, code);
}
// urn:ogc:def:crs:OGC::AUTO42001:-117:33
@@ -6790,8 +6857,9 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
if (tokens.size() == 6 && tokens[0] == "urn" && tokens[2] != "def") {
const auto &type = tokens[2];
const auto &authName = tokens[3];
+ const auto &version = tokens[4];
const auto &code = tokens[5];
- return createFromURNPart(type, authName, code);
+ return createFromURNPart(dbContext, type, authName, version, code);
}
// Legacy urn:x-ogc:def:crs:EPSG:4326 (note the missing version)
@@ -6799,7 +6867,8 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
const auto &type = tokens[3];
const auto &authName = tokens[4];
const auto &code = tokens[5];
- return createFromURNPart(type, authName, code);
+ return createFromURNPart(dbContext, type, authName, std::string(),
+ code);
}
if (dbContext) {
@@ -10784,6 +10853,12 @@ CPLJSonStreamingWriter *JSONFormatter::writer() const { return &(d->writer_); }
// ---------------------------------------------------------------------------
+const DatabaseContextPtr &JSONFormatter::databaseContext() const {
+ return d->dbContext_;
+}
+
+// ---------------------------------------------------------------------------
+
bool JSONFormatter::outputId() const { return d->outputIdStack_.back(); }
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/metadata.cpp b/src/iso19111/metadata.cpp
index fc1b103f..6c168152 100644
--- a/src/iso19111/metadata.cpp
+++ b/src/iso19111/metadata.cpp
@@ -1058,7 +1058,12 @@ const optional<std::string> &Identifier::uri() PROJ_PURE_DEFN {
void Identifier::_exportToWKT(WKTFormatter *formatter) const {
const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
const std::string &l_code = code();
- const std::string &l_codeSpace = *codeSpace();
+ std::string l_codeSpace = *codeSpace();
+ std::string l_version = *version();
+ const auto &dbContext = formatter->databaseContext();
+ if (dbContext) {
+ dbContext->getAuthorityAndVersion(*codeSpace(), l_codeSpace, l_version);
+ }
if (!l_codeSpace.empty() && !l_code.empty()) {
if (isWKT2) {
formatter->startNode(WKTConstants::ID, false);
@@ -1069,8 +1074,7 @@ void Identifier::_exportToWKT(WKTFormatter *formatter) const {
} catch (const std::exception &) {
formatter->addQuotedString(l_code);
}
- if (version().has_value()) {
- auto l_version = *(version());
+ if (!l_version.empty()) {
try {
(void)c_locale_stod(l_version);
formatter->add(l_version);
@@ -1079,7 +1083,7 @@ void Identifier::_exportToWKT(WKTFormatter *formatter) const {
}
}
if (authority().has_value() &&
- *(authority()->title()) != l_codeSpace) {
+ *(authority()->title()) != *codeSpace()) {
formatter->startNode(WKTConstants::CITATION, false);
formatter->addQuotedString(*(authority()->title()));
formatter->endNode();
@@ -1103,7 +1107,12 @@ void Identifier::_exportToWKT(WKTFormatter *formatter) const {
void Identifier::_exportToJSON(JSONFormatter *formatter) const {
const std::string &l_code = code();
- const std::string &l_codeSpace = *codeSpace();
+ std::string l_codeSpace = *codeSpace();
+ std::string l_version = *version();
+ const auto &dbContext = formatter->databaseContext();
+ if (dbContext) {
+ dbContext->getAuthorityAndVersion(*codeSpace(), l_codeSpace, l_version);
+ }
if (!l_codeSpace.empty() && !l_code.empty()) {
auto writer = formatter->writer();
auto objContext(formatter->MakeObjectContext(nullptr, false));
@@ -1116,8 +1125,7 @@ void Identifier::_exportToJSON(JSONFormatter *formatter) const {
writer->Add(l_code);
}
- if (version().has_value()) {
- const auto l_version = *(version());
+ if (!l_version.empty()) {
writer->AddObjKey("version");
try {
const double dblVersion = c_locale_stod(l_version);
@@ -1132,7 +1140,8 @@ void Identifier::_exportToJSON(JSONFormatter *formatter) const {
writer->Add(l_version);
}
}
- if (authority().has_value() && *(authority()->title()) != l_codeSpace) {
+ if (authority().has_value() &&
+ *(authority()->title()) != *codeSpace()) {
writer->AddObjKey("authority_citation");
writer->Add(*(authority()->title()));
}
diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp
index 42e6018b..449da2bf 100644
--- a/test/unit/test_crs.cpp
+++ b/test/unit/test_crs.cpp
@@ -747,6 +747,53 @@ TEST(crs, EPSG_4268_geogcrs_deprecated_as_WKT1_GDAL) {
// ---------------------------------------------------------------------------
+TEST(crs, IAU_1000_as_WKT2) {
+ auto dbContext = DatabaseContext::create();
+ auto factory = AuthorityFactory::create(dbContext, "IAU_2015");
+ auto crs = factory->createCoordinateReferenceSystem("1000");
+ WKTFormatterNNPtr f(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2019, dbContext));
+ auto wkt = crs->exportToWKT(f.get());
+ // Check that IAU_2015 is split into a authority name and version
+ EXPECT_TRUE(wkt.find("ID[\"IAU\",1000,2015]") != std::string::npos) << wkt;
+
+ auto obj = createFromUserInput(wkt, dbContext);
+ auto crs2 = nn_dynamic_pointer_cast<CRS>(obj);
+ ASSERT_TRUE(crs2 != nullptr);
+ auto wkt2 = crs2->exportToWKT(f.get());
+ // Check that IAU_2015 is split into a authority name and version
+ EXPECT_TRUE(wkt2.find("ID[\"IAU\",1000,2015]") != std::string::npos)
+ << wkt2;
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(crs, IAU_1000_as_PROJJSON) {
+ auto dbContext = DatabaseContext::create();
+ auto factory = AuthorityFactory::create(dbContext, "IAU_2015");
+ auto crs = factory->createCoordinateReferenceSystem("1000");
+ auto projjson = crs->exportToJSON(JSONFormatter::create(dbContext).get());
+ // Check that IAU_2015 is split into a authority name and version
+ EXPECT_TRUE(projjson.find("\"authority\": \"IAU\",") != std::string::npos)
+ << projjson;
+ EXPECT_TRUE(projjson.find("\"code\": 1000,") != std::string::npos)
+ << projjson;
+ EXPECT_TRUE(projjson.find("\"version\": 2015") != std::string::npos)
+ << projjson;
+
+ auto obj = createFromUserInput(projjson, dbContext);
+ auto crs2 = nn_dynamic_pointer_cast<CRS>(obj);
+ ASSERT_TRUE(crs2 != nullptr);
+ WKTFormatterNNPtr f(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2019, dbContext));
+ auto wkt2 = crs2->exportToWKT(f.get());
+ // Check that IAU_2015 is split into a authority name and version
+ EXPECT_TRUE(wkt2.find("ID[\"IAU\",1000,2015]") != std::string::npos)
+ << wkt2;
+}
+
+// ---------------------------------------------------------------------------
+
TEST(crs, EPSG_2008_projcrs_deprecated_as_WKT1_GDAL) {
auto dbContext = DatabaseContext::create();
auto factory = AuthorityFactory::create(dbContext, "EPSG");
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index f9a54f3c..646a990a 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -10495,6 +10495,8 @@ TEST(io, createFromUserInput) {
EXPECT_NO_THROW(createFromUserInput("epsg:4326", dbContext));
EXPECT_NO_THROW(
createFromUserInput("urn:ogc:def:crs:EPSG::4326", dbContext));
+ EXPECT_NO_THROW(
+ createFromUserInput("urn:ogc:def:crs:EPSG:10:4326", dbContext));
EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:EPSG::4326", nullptr),
ParsingException);
EXPECT_NO_THROW(createFromUserInput(
@@ -10508,6 +10510,20 @@ TEST(io, createFromUserInput) {
EXPECT_NO_THROW(
createFromUserInput("urn:ogc:def:ellipsoid:EPSG::7030", dbContext));
+ EXPECT_NO_THROW(createFromUserInput("IAU:1000", dbContext));
+ EXPECT_NO_THROW(createFromUserInput("IAU_2015:1000", dbContext));
+ EXPECT_NO_THROW(
+ createFromUserInput("urn:ogc:def:crs:IAU::1000", dbContext));
+ EXPECT_NO_THROW(
+ createFromUserInput("urn:ogc:def:crs:IAU_2015::1000", dbContext));
+ EXPECT_NO_THROW(
+ createFromUserInput("urn:ogc:def:crs:IAU:2015:1000", dbContext));
+
+ EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:IAU_2015::xxxx", nullptr),
+ ParsingException);
+ EXPECT_THROW(createFromUserInput("urn:ogc:def:crs:IAU:xxxx:1000", nullptr),
+ ParsingException);
+
// Found as srsName in some GMLs...
EXPECT_NO_THROW(
createFromUserInput("URN:OGC:DEF:CRS:OGC:1.3:CRS84", dbContext));