diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2021-10-05 19:21:05 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-10-05 19:21:05 +0200 |
| commit | c50ba1b1a7ecde946544c03ab0951727dd87264d (patch) | |
| tree | 2be4d809980ba1450d6e8a2af29c4e9822c9f2d1 /src | |
| parent | 6b58adb4b7396b9c2aff2fa26aae2340d5a62d21 (diff) | |
| parent | 5d6bdadfca419c1d54d455e240743791e6cea44e (diff) | |
| download | PROJ-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.cpp | 90 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 19 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 128 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 205 | ||||
| -rw-r--r-- | src/iso19111/metadata.cpp | 25 |
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())); } |
