aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2021-10-05 19:21:05 +0200
committerGitHub <noreply@github.com>2021-10-05 19:21:05 +0200
commitc50ba1b1a7ecde946544c03ab0951727dd87264d (patch)
tree2be4d809980ba1450d6e8a2af29c4e9822c9f2d1 /src
parent6b58adb4b7396b9c2aff2fa26aae2340d5a62d21 (diff)
parent5d6bdadfca419c1d54d455e240743791e6cea44e (diff)
downloadPROJ-c50ba1b1a7ecde946544c03ab0951727dd87264d.tar.gz
PROJ-c50ba1b1a7ecde946544c03ab0951727dd87264d.zip
Merge pull request #2876 from rouault/iau
Add IAU_2015 CRS definitions
Diffstat (limited to 'src')
-rw-r--r--src/apps/projinfo.cpp90
-rw-r--r--src/iso19111/c_api.cpp19
-rw-r--r--src/iso19111/factory.cpp128
-rw-r--r--src/iso19111/io.cpp205
-rw-r--r--src/iso19111/metadata.cpp25
5 files changed, 355 insertions, 112 deletions
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 10944984..add4119d 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);
}
@@ -968,7 +968,7 @@ int main(int argc, char **argv) {
double minimumAccuracy = -1;
bool outputAll = false;
bool dumpDbStructure = false;
- std::string listCRS;
+ std::string listCRSFilter;
bool listCRSSpecified = false;
for (int i = 1; i < argc; i++) {
@@ -1251,7 +1251,7 @@ int main(int argc, char **argv) {
listCRSSpecified = true;
if (i + 1 < argc && argv[i + 1][0] != '-') {
i++;
- listCRS = argv[i];
+ listCRSFilter = argv[i];
}
} else if (ci_equal(arg, "--searchpaths")) {
#ifdef _WIN32
@@ -1354,11 +1354,11 @@ int main(int argc, char **argv) {
if (listCRSSpecified) {
bool allow_deprecated = false;
std::set<AuthorityFactory::ObjectType> types;
- auto tokens = split(listCRS, ',');
- if (listCRS.empty()) {
+ auto tokens = split(listCRSFilter, ',');
+ if (listCRSFilter.empty()) {
tokens.clear();
}
- for (auto token : tokens) {
+ for (const auto &token : tokens) {
if (ci_equal(token, "allow_deprecated")) {
allow_deprecated = true;
} else if (ci_equal(token, "geodetic")) {
@@ -1397,44 +1397,50 @@ int main(int argc, char **argv) {
}
for (const auto &auth_name : allowedAuthorities) {
try {
- auto factory =
- AuthorityFactory::create(NN_NO_CHECK(dbContext), auth_name);
- const auto list = factory->getCRSInfoList();
- for (const auto &info : list) {
- if (!allow_deprecated && info.deprecated) {
- continue;
- }
- if (!types.empty() &&
- types.find(info.type) == types.end()) {
- continue;
- }
- if (bboxFilter) {
- if (!info.bbox_valid) {
+ auto actualAuthNames =
+ dbContext->getVersionedAuthoritiesFromName(auth_name);
+ if (actualAuthNames.empty())
+ actualAuthNames.push_back(auth_name);
+ for (const auto &actualAuthName : actualAuthNames) {
+ auto factory = AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), actualAuthName);
+ const auto list = factory->getCRSInfoList();
+ for (const auto &info : list) {
+ if (!allow_deprecated && info.deprecated) {
continue;
}
- auto crsExtent = Extent::createFromBBOX(
- info.west_lon_degree, info.south_lat_degree,
- info.east_lon_degree, info.north_lat_degree);
- if (spatialCriterion ==
- CoordinateOperationContext::SpatialCriterion::
- STRICT_CONTAINMENT) {
- if (!bboxFilter->contains(crsExtent)) {
+ if (!types.empty() &&
+ types.find(info.type) == types.end()) {
+ continue;
+ }
+ if (bboxFilter) {
+ if (!info.bbox_valid) {
continue;
}
- } else {
- if (!bboxFilter->intersects(crsExtent)) {
- continue;
+ auto crsExtent = Extent::createFromBBOX(
+ info.west_lon_degree, info.south_lat_degree,
+ info.east_lon_degree, info.north_lat_degree);
+ if (spatialCriterion ==
+ CoordinateOperationContext::SpatialCriterion::
+ STRICT_CONTAINMENT) {
+ if (!bboxFilter->contains(crsExtent)) {
+ continue;
+ }
+ } else {
+ if (!bboxFilter->intersects(crsExtent)) {
+ continue;
+ }
}
+ } else if (!area.empty() &&
+ tolower(info.areaName).find(areaLower) ==
+ std::string::npos) {
+ continue;
}
- } else if (!area.empty() &&
- tolower(info.areaName).find(areaLower) ==
- std::string::npos) {
- continue;
+ std::cout << info.authName << ":" << info.code << " \""
+ << info.name << "\""
+ << (info.deprecated ? " [deprecated]" : "")
+ << std::endl;
}
- std::cout << info.authName << ":" << info.code << " \""
- << info.name << "\""
- << (info.deprecated ? " [deprecated]" : "")
- << std::endl;
}
} catch (const std::exception &e) {
std::cerr << "ERROR: list-crs failed with: " << e.what()
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index a5e1bc81..9493452c 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -2766,10 +2766,19 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
PROJ_CRS_INFO **ret = nullptr;
int i = 0;
try {
- auto factory = AuthorityFactory::create(getDBcontext(ctx),
- auth_name ? auth_name : "");
- auto list = factory->getCRSInfoList();
- ret = new PROJ_CRS_INFO *[list.size() + 1];
+ auto dbContext = getDBcontext(ctx);
+ const std::string authName = auth_name ? auth_name : "";
+ auto actualAuthNames =
+ dbContext->getVersionedAuthoritiesFromName(authName);
+ if (actualAuthNames.empty())
+ actualAuthNames.push_back(authName);
+ std::list<AuthorityFactory::CRSInfo> concatList;
+ for (const auto &actualAuthName : actualAuthNames) {
+ auto factory = AuthorityFactory::create(dbContext, actualAuthName);
+ auto list = factory->getCRSInfoList();
+ concatList.splice(concatList.end(), std::move(list));
+ }
+ ret = new PROJ_CRS_INFO *[concatList.size() + 1];
GeographicBoundingBoxPtr bbox;
if (params && params->bbox_valid) {
bbox = GeographicBoundingBox::create(
@@ -2777,7 +2786,7 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
params->east_lon_degree, params->north_lat_degree)
.as_nullable();
}
- for (const auto &info : list) {
+ for (const auto &info : concatList) {
auto type = PJ_TYPE_CRS;
if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) {
type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index d5b0a22a..f08e32b9 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -104,6 +104,7 @@ namespace io {
#define GEOG_2D "geographic 2D"
#define GEOG_3D "geographic 3D"
#define GEOCENTRIC "geocentric"
+#define OTHER "other"
#define PROJECTED "projected"
#define VERTICAL "vertical"
#define COMPOUND "compound"
@@ -753,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;
@@ -794,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);
@@ -3557,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) {
@@ -4208,7 +4300,11 @@ AuthorityFactory::identifyBodyFromSemiMajorAxis(double semi_major_axis,
throw FactoryException("no match found");
}
if (res.size() > 1) {
- throw FactoryException("more than one match found");
+ for (const auto &row : res) {
+ if (row[0] != res.front()[0]) {
+ throw FactoryException("more than one match found");
+ }
+ }
}
return res.front()[0];
}
@@ -4716,6 +4812,17 @@ AuthorityFactory::createCoordinateSystem(const std::string &code) const {
}
throw FactoryException("invalid number of axis for CartesianCS");
}
+ if (csType == "spherical") {
+ if (axisList.size() == 2) {
+ return cacheAndRet(
+ cs::SphericalCS::create(props, axisList[0], axisList[1]));
+ }
+ if (axisList.size() == 3) {
+ return cacheAndRet(cs::SphericalCS::create(
+ props, axisList[0], axisList[1], axisList[2]));
+ }
+ throw FactoryException("invalid number of axis for SphericalCS");
+ }
if (csType == "vertical") {
if (axisList.size() == 1) {
return cacheAndRet(cs::VerticalCS::create(props, axisList[0]));
@@ -4797,8 +4904,7 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
}
std::string sql("SELECT name, type, coordinate_system_auth_name, "
"coordinate_system_code, datum_auth_name, datum_code, "
- "text_definition, "
- "deprecated FROM "
+ "text_definition, deprecated, description FROM "
"geodetic_crs WHERE auth_name = ? AND code = ?");
if (geographicOnly) {
sql += " AND type in (" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED
@@ -4819,9 +4925,10 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
const auto &datum_code = row[5];
const auto &text_definition = row[6];
const bool deprecated = row[7] == "1";
+ const auto &remarks = row[8];
auto props = d->createPropertiesSearchUsages("geodetic_crs", code, name,
- deprecated);
+ deprecated, remarks);
if (!text_definition.empty()) {
DatabaseContext::Private::RecursionDetector detector(d->context());
@@ -4869,6 +4976,7 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
d->context()->d->cache(cacheKey, crsRet);
return crsRet;
}
+
auto geocentricCS = util::nn_dynamic_pointer_cast<cs::CartesianCS>(cs);
if (type == GEOCENTRIC && geocentricCS) {
auto crsRet = crs::GeodeticCRS::create(props, datum, datumEnsemble,
@@ -4876,6 +4984,15 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
d->context()->d->cache(cacheKey, crsRet);
return crsRet;
}
+
+ auto sphericalCS = util::nn_dynamic_pointer_cast<cs::SphericalCS>(cs);
+ if (type == OTHER && sphericalCS) {
+ auto crsRet = crs::GeodeticCRS::create(props, datum, datumEnsemble,
+ NN_NO_CHECK(sphericalCS));
+ d->context()->d->cache(cacheKey, crsRet);
+ return crsRet;
+ }
+
throw FactoryException("unsupported (type, CS type) for geodeticCRS: " +
type + ", " + cs->getWKT2Type(true));
} catch (const std::exception &ex) {
@@ -5354,7 +5471,8 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code,
code);
}
const auto &type = res.front()[0];
- if (type == GEOG_2D || type == GEOG_3D || type == GEOCENTRIC) {
+ if (type == GEOG_2D || type == GEOG_3D || type == GEOCENTRIC ||
+ type == OTHER) {
return createGeodeticCRS(code);
}
if (type == VERTICAL) {
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index b317213a..1f193559 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) {
@@ -7799,6 +7868,32 @@ const std::string &PROJStringFormatter::toString() const {
continue;
}
+ // axisswap order=2,-1 followed by axisswap order=-2,1 is a no-op
+ if (curStep.name == "axisswap" && prevStep.name == "axisswap" &&
+ curStepParamCount == 1 && prevStepParamCount == 1 &&
+ !prevStep.inverted &&
+ prevStep.paramValues[0].equals("order", "2,-1") &&
+ !curStep.inverted &&
+ curStep.paramValues[0].equals("order", "-2,1")) {
+ deletePrevAndCurIter();
+ continue;
+ }
+
+ // axisswap order=2,-1 followed by axisswap order=1,-2 is
+ // equivalent to axisswap order=2,1
+ if (curStep.name == "axisswap" && prevStep.name == "axisswap" &&
+ curStepParamCount == 1 && prevStepParamCount == 1 &&
+ !prevStep.inverted &&
+ prevStep.paramValues[0].equals("order", "2,-1") &&
+ !curStep.inverted &&
+ curStep.paramValues[0].equals("order", "1,-2")) {
+ prevStep.inverted = false;
+ prevStep.paramValues[0] = Step::KeyValue("order", "2,1");
+ // Delete this iter
+ iterCur = steps.erase(iterCur);
+ continue;
+ }
+
// axisswap order=2,1 followed by axisswap order=2,-1 is
// equivalent to axisswap order=1,-2
// Same for axisswap order=-2,1 followed by axisswap order=2,1
@@ -10784,6 +10879,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()));
}