diff options
| -rw-r--r-- | docs/source/apps/projinfo.rst | 72 | ||||
| -rw-r--r-- | include/proj/io.hpp | 8 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 2 | ||||
| -rw-r--r-- | src/apps/projinfo.cpp | 10 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 28 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 431 | ||||
| -rw-r--r-- | src/proj.h | 1 | ||||
| -rwxr-xr-x | test/cli/testprojinfo | 4 | ||||
| -rw-r--r-- | test/cli/testprojinfo_out.dist | 12 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 57 |
10 files changed, 437 insertions, 188 deletions
diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index c7a56689..34df3bca 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -25,6 +25,7 @@ Synopsis | [--show-superseded] [--hide-ballpark] [--accuracy {accuracy}] | [--allow-ellipsoidal-height-as-vertical-crs] | [--boundcrs-to-wgs84] + | [--authority name] | [--main-db-path path] [--aux-db-path path]* | [--identify] [--3d] | [--output-id AUTH:CODE] @@ -240,6 +241,16 @@ The following control parameters can appear in any order: geographic CRS, and if found, wraps those CRS into a BoundCRS object. This is mostly to be used for early-binding approaches. +.. option:: --authority name + + Specify the name of the authority into which to restrict looks up for + objects, when specifying an object by name or when coordinate operations are + computed. The default is to allow all authorities. + + When used with SQL output, this restricts the authorities to which intermediate + objects can belong to (the default is EPSG and PROJ). Note that the authority + of the :option:`--output-id` option will also be implicitly added. + .. option:: --main-db-path path Specify the name and path of the database to be used by projinfo. The @@ -451,11 +462,22 @@ Output: } } -4. Exporting the SQL statements to insert a new CRS in the database. +4. Exporting the SQL statements to insert a new CRS in an auxiliary database. .. code-block:: console - projinfo "+proj=merc +lat_ts=5 +datum=WGS84 +type=crs" --output-id HOBU:MY_CRS -o SQL -q + # Get the SQL statements for a custom CRS + projinfo "+proj=merc +lat_ts=5 +datum=WGS84 +type=crs +title=my_crs" --output-id HOBU:MY_CRS -o SQL -q > my_crs.sql + cat my_crs.sql + + # Initialize an auxiliary database with the schema of the reference database + echo ".schema" | sqlite3 /path/to/proj.db | sqlite3 aux.db + + # Append the content of the definition of HOBU:MY_CRS + sqlite3 aux.db < my_crs.db + + # Check that everything works OK + projinfo --aux-db-path aux.db HOBU:MY_CRS Output: @@ -465,9 +487,53 @@ Output: INSERT INTO usage VALUES('HOBU','USAGE_GEODETIC_CRS_MY_CRS','geodetic_crs','HOBU','GEODETIC_CRS_MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); INSERT INTO conversion VALUES('HOBU','CONVERSION_MY_CRS','unknown','','EPSG','9805','Mercator (variant B)','EPSG','8823','Latitude of 1st standard parallel',5,'EPSG','9122','EPSG','8802','Longitude of natural origin',0,'EPSG','9122','EPSG','8806','False easting',0,'EPSG','9001','EPSG','8807','False northing',0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); INSERT INTO usage VALUES('HOBU','USAGE_CONVERSION_MY_CRS','conversion','HOBU','CONVERSION_MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); - INSERT INTO projected_crs VALUES('HOBU','MY_CRS','unknown','','EPSG','4400','HOBU','GEODETIC_CRS_MY_CRS','HOBU','CONVERSION_MY_CRS',NULL,0); + INSERT INTO projected_crs VALUES('HOBU','MY_CRS','my_crs','','EPSG','4400','HOBU','GEODETIC_CRS_MY_CRS','HOBU','CONVERSION_MY_CRS',NULL,0); INSERT INTO usage VALUES('HOBU','USAGE_PROJECTED_CRS_MY_CRS','projected_crs','HOBU','MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); +:: + + PROJ.4 string: + +proj=merc +lat_ts=5 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs + + WKT2:2019 string: + PROJCRS["my_crs", + BASEGEOGCRS["unknown", + ENSEMBLE["World Geodetic System 1984 ensemble", + MEMBER["World Geodetic System 1984 (Transit)"], + MEMBER["World Geodetic System 1984 (G730)"], + MEMBER["World Geodetic System 1984 (G873)"], + MEMBER["World Geodetic System 1984 (G1150)"], + MEMBER["World Geodetic System 1984 (G1674)"], + MEMBER["World Geodetic System 1984 (G1762)"], + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]], + ENSEMBLEACCURACY[2.0]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + ID["HOBU","GEODETIC_CRS_MY_CRS"]], + CONVERSION["unknown", + METHOD["Mercator (variant B)", + ID["EPSG",9805]], + PARAMETER["Latitude of 1st standard parallel",5, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8823]], + PARAMETER["Longitude of natural origin",0, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8802]], + PARAMETER["False easting",0, + LENGTHUNIT["metre",1], + ID["EPSG",8806]], + PARAMETER["False northing",0, + LENGTHUNIT["metre",1], + ID["EPSG",8807]]], + CS[Cartesian,2], + AXIS["(E)",east, + ORDER[1], + LENGTHUNIT["metre",1]], + AXIS["(N)",north, + ORDER[2], + LENGTHUNIT["metre",1]], + ID["HOBU","MY_CRS"]] .. only:: man diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 5f0dd475..96a97142 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -844,10 +844,10 @@ class PROJ_GCC_DLL DatabaseContext { suggestsCodeFor(const common::IdentifiedObjectNNPtr &object, const std::string &authName, bool numericCode); - PROJ_DLL std::vector<std::string> - getInsertStatementsFor(const common::IdentifiedObjectNNPtr &object, - const std::string &authName, const std::string &code, - bool numericCode); + PROJ_DLL std::vector<std::string> getInsertStatementsFor( + const common::IdentifiedObjectNNPtr &object, + const std::string &authName, const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities = {"EPSG", "PROJ"}); PROJ_DLL void stopInsertStatementsSession(); diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 798f515e..aa148762 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -359,7 +359,7 @@ osgeo::proj::io::DatabaseContext::create(void*) osgeo::proj::io::DatabaseContext::~DatabaseContext() osgeo::proj::io::DatabaseContext::getAuthorities() const osgeo::proj::io::DatabaseContext::getDatabaseStructure() const -osgeo::proj::io::DatabaseContext::getInsertStatementsFor(dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::common::IdentifiedObject> > const&, std::string const&, std::string const&, bool) +osgeo::proj::io::DatabaseContext::getInsertStatementsFor(dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::common::IdentifiedObject> > const&, std::string const&, std::string const&, bool, std::vector<std::string, std::allocator<std::string> > const&) osgeo::proj::io::DatabaseContext::getMetadata(char const*) const osgeo::proj::io::DatabaseContext::getPath() const osgeo::proj::io::DatabaseContext::getSqliteHandle() const diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index 39d9666d..2334c293 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -76,6 +76,7 @@ struct OutputOptions { bool allowEllipsoidalHeightAsVerticalCRS = false; std::string outputAuthName{}; std::string outputCode{}; + std::vector<std::string> allowedAuthorities{}; }; } // anonymous namespace @@ -104,6 +105,7 @@ static void usage() { << " [--allow-ellipsoidal-height-as-vertical-crs]" << std::endl << " [--boundcrs-to-wgs84]" << std::endl + << " [--authority name]" << std::endl << " [--main-db-path path] [--aux-db-path path]*" << std::endl << " [--identify] [--3d]" << std::endl @@ -573,9 +575,14 @@ static void outputObject( std::cout << "SQL:" << std::endl; } dbContext->startInsertStatementsSession(); + auto allowedAuthorities(outputOpt.allowedAuthorities); + if (allowedAuthorities.empty()) { + allowedAuthorities.emplace_back("EPSG"); + allowedAuthorities.emplace_back("PROJ"); + } const auto statements = dbContext->getInsertStatementsFor( NN_NO_CHECK(identified), outputOpt.outputAuthName, - outputOpt.outputCode, false); + outputOpt.outputCode, false, allowedAuthorities); dbContext->stopInsertStatementsSession(); for (const auto &sql : statements) { std::cout << sql << std::endl; @@ -1118,6 +1125,7 @@ int main(int argc, char **argv) { } else if (arg == "--authority" && i + 1 < argc) { i++; authority = argv[i]; + outputOpt.allowedAuthorities = split(authority, ','); } else if (arg == "--identify") { identify = true; } else if (arg == "--show-superseded") { diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index b97e7300..a79b387e 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -8881,6 +8881,15 @@ void proj_string_destroy(char *str) { free(str); } * @param code Code with which the object will be inserted.Must not be NULL. * @param numeric_codes Whether intermediate objects that can be created should * use numeric codes (true), or may be alphanumeric (false) + * @param allowed_authorities NULL terminated list of authority names, or NULL. + * Authorities to which intermediate objects are + * allowed to refer to. "authority" will be + * implicitly added to it. Note that unit, + * coordinate systems, projection methods and + * parameters will in any case be allowed to refer + * to EPSG. + * If NULL, allowed_authorities defaults to + * {"EPSG", "PROJ", nullptr} * @param options NULL terminated list of options, or NULL. * No options are supported currently. * @@ -8888,12 +8897,10 @@ void proj_string_destroy(char *str) { free(str); } * proj_string_list_destroy()), or NULL in case of error. * @since 8.1 */ -PROJ_STRING_LIST proj_get_insert_statements(PJ_CONTEXT *ctx, - PJ_INSERT_SESSION *session, - const PJ *object, - const char *authority, - const char *code, int numeric_codes, - const char *const *options) { +PROJ_STRING_LIST proj_get_insert_statements( + PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session, const PJ *object, + const char *authority, const char *code, int numeric_codes, + const char *const *allowed_authorities, const char *const *options) { SANITIZE_CTX(ctx); (void)options; @@ -8939,9 +8946,16 @@ PROJ_STRING_LIST proj_get_insert_statements(PJ_CONTEXT *ctx, } auto dbContext = getDBcontext(ctx); + std::vector<std::string> allowedAuthorities{"EPSG", "PROJ"}; + if (allowed_authorities) { + allowedAuthorities.clear(); + for (auto iter = allowed_authorities; *iter; ++iter) { + allowedAuthorities.emplace_back(*iter); + } + } auto statements = dbContext->getInsertStatementsFor( NN_NO_CHECK(identifiedObject), authority, code, - numeric_codes != FALSE); + numeric_codes != FALSE, allowedAuthorities); return to_string_list(std::move(statements)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 36ef217d..d7050bce 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -49,6 +49,7 @@ #include "sqlite3_utils.hpp" +#include <algorithm> #include <cmath> #include <cstdlib> #include <cstring> @@ -347,51 +348,60 @@ struct DatabaseContext::Private { void appendSql(std::vector<std::string> &sqlStatements, const std::string &sql); - void identifyOrInsertUsages(const common::ObjectUsageNNPtr &obj, - const std::string &tableName, - const std::string &authName, - const std::string &code, - std::vector<std::string> &sqlStatements); + void + identifyOrInsertUsages(const common::ObjectUsageNNPtr &obj, + const std::string &tableName, + const std::string &authName, const std::string &code, + const std::vector<std::string> &allowedAuthorities, + std::vector<std::string> &sqlStatements); std::vector<std::string> getInsertStatementsFor(const datum::PrimeMeridianNNPtr &pm, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const datum::EllipsoidNNPtr &ellipsoid, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const datum::GeodeticReferenceFrameNNPtr &datum, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const crs::GeodeticCRSNNPtr &crs, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const crs::ProjectedCRSNNPtr &crs, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const datum::VerticalReferenceFrameNNPtr &datum, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const crs::VerticalCRSNNPtr &crs, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); std::vector<std::string> getInsertStatementsFor(const crs::CompoundCRSNNPtr &crs, const std::string &authName, const std::string &code, - bool numericCode); + bool numericCode, + const std::vector<std::string> &allowedAuthorities); #ifdef ENABLE_CUSTOM_LOCKLESS_VFS std::unique_ptr<SQLite3VFS> vfs_{}; @@ -1100,63 +1110,81 @@ void DatabaseContext::Private::appendSql( static void identifyFromNameOrCode( const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, - const common::IdentifiedObjectNNPtr &obj, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, const common::IdentifiedObjectNNPtr &obj, std::function<std::shared_ptr<util::IComparable>( const AuthorityFactoryNNPtr &authFactory, const std::string &)> instantiateFunc, AuthorityFactory::ObjectType objType, std::string &authName, std::string &code) { + + auto allowedAuthoritiesTmp(allowedAuthorities); + allowedAuthoritiesTmp.emplace_back(authNameParent); + for (const auto &id : obj->identifiers()) { try { - const auto tmpAuthFactory = - AuthorityFactory::create(dbContext, *(id->codeSpace())); - if (instantiateFunc(tmpAuthFactory, id->code()) - ->isEquivalentTo( - obj.get(), util::IComparable::Criterion::EQUIVALENT)) { - authName = *(id->codeSpace()); - code = id->code(); - return; + const auto idAuthName = *(id->codeSpace()); + if (std::find(allowedAuthoritiesTmp.begin(), + allowedAuthoritiesTmp.end(), + idAuthName) != allowedAuthoritiesTmp.end()) { + const auto factory = + AuthorityFactory::create(dbContext, idAuthName); + if (instantiateFunc(factory, id->code()) + ->isEquivalentTo( + obj.get(), + util::IComparable::Criterion::EQUIVALENT)) { + authName = idAuthName; + code = id->code(); + return; + } } } catch (const std::exception &) { } } - const auto candidates = allAuthFactory->createObjectsFromName( - obj->nameStr(), {objType}, false, 0); - for (const auto &candidate : candidates) { - const auto &ids = candidate->identifiers(); - if (!ids.empty() && - candidate->isEquivalentTo( - obj.get(), util::IComparable::Criterion::EQUIVALENT)) { - const auto &id = ids.front(); - authName = *(id->codeSpace()); - code = id->code(); + for (const auto &allowedAuthority : allowedAuthoritiesTmp) { + const auto factory = + AuthorityFactory::create(dbContext, allowedAuthority); + const auto candidates = + factory->createObjectsFromName(obj->nameStr(), {objType}, false, 0); + for (const auto &candidate : candidates) { + const auto &ids = candidate->identifiers(); + if (!ids.empty() && + candidate->isEquivalentTo( + obj.get(), util::IComparable::Criterion::EQUIVALENT)) { + const auto &id = ids.front(); + authName = *(id->codeSpace()); + code = id->code(); + return; + } } } } // --------------------------------------------------------------------------- -static void identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, - const datum::DatumEnsembleNNPtr &obj, - std::string &authName, std::string &code) { +static void +identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, + const datum::DatumEnsembleNNPtr &obj, + std::string &authName, std::string &code) { const auto instantiateFunc = [](const AuthorityFactoryNNPtr &authFactory, const std::string &lCode) { return util::nn_static_pointer_cast<util::IComparable>( authFactory->createDatumEnsemble(lCode, "geodetic_datum")); }; - identifyFromNameOrCode(dbContext, allAuthFactory, obj, instantiateFunc, - AuthorityFactory::ObjectType::DATUM_ENSEMBLE, - authName, code); + identifyFromNameOrCode( + dbContext, allowedAuthorities, authNameParent, obj, instantiateFunc, + AuthorityFactory::ObjectType::DATUM_ENSEMBLE, authName, code); } // --------------------------------------------------------------------------- static void identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, const datum::GeodeticReferenceFrameNNPtr &obj, std::string &authName, std::string &code) { const auto instantiateFunc = [](const AuthorityFactoryNNPtr &authFactory, @@ -1165,47 +1193,52 @@ identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, authFactory->createGeodeticDatum(lCode)); }; identifyFromNameOrCode( - dbContext, allAuthFactory, obj, instantiateFunc, + dbContext, allowedAuthorities, authNameParent, obj, instantiateFunc, AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME, authName, code); } // --------------------------------------------------------------------------- -static void identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, - const datum::EllipsoidNNPtr &obj, - std::string &authName, std::string &code) { +static void +identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, + const datum::EllipsoidNNPtr &obj, std::string &authName, + std::string &code) { const auto instantiateFunc = [](const AuthorityFactoryNNPtr &authFactory, const std::string &lCode) { return util::nn_static_pointer_cast<util::IComparable>( authFactory->createEllipsoid(lCode)); }; - identifyFromNameOrCode(dbContext, allAuthFactory, obj, instantiateFunc, - AuthorityFactory::ObjectType::ELLIPSOID, authName, - code); + identifyFromNameOrCode( + dbContext, allowedAuthorities, authNameParent, obj, instantiateFunc, + AuthorityFactory::ObjectType::ELLIPSOID, authName, code); } // --------------------------------------------------------------------------- -static void identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, - const datum::PrimeMeridianNNPtr &obj, - std::string &authName, std::string &code) { +static void +identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, + const datum::PrimeMeridianNNPtr &obj, + std::string &authName, std::string &code) { const auto instantiateFunc = [](const AuthorityFactoryNNPtr &authFactory, const std::string &lCode) { return util::nn_static_pointer_cast<util::IComparable>( authFactory->createPrimeMeridian(lCode)); }; - identifyFromNameOrCode(dbContext, allAuthFactory, obj, instantiateFunc, - AuthorityFactory::ObjectType::PRIME_MERIDIAN, - authName, code); + identifyFromNameOrCode( + dbContext, allowedAuthorities, authNameParent, obj, instantiateFunc, + AuthorityFactory::ObjectType::PRIME_MERIDIAN, authName, code); } // --------------------------------------------------------------------------- static void identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, - const AuthorityFactoryNNPtr &allAuthFactory, + const std::vector<std::string> &allowedAuthorities, + const std::string &authNameParent, const datum::VerticalReferenceFrameNNPtr &obj, std::string &authName, std::string &code) { const auto instantiateFunc = [](const AuthorityFactoryNNPtr &authFactory, @@ -1214,7 +1247,7 @@ identifyFromNameOrCode(const DatabaseContextNNPtr &dbContext, authFactory->createVerticalDatum(lCode)); }; identifyFromNameOrCode( - dbContext, allAuthFactory, obj, instantiateFunc, + dbContext, allowedAuthorities, authNameParent, obj, instantiateFunc, AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME, authName, code); } @@ -1292,7 +1325,7 @@ void DatabaseContext::Private::identify(const DatabaseContextNNPtr &dbContext, switch (obj.type()) { case common::UnitOfMeasure::Type::LINEAR: { if (convFactor == 1.0) { - authName = "EPSG"; + authName = metadata::Identifier::EPSG; code = "9001"; return; } @@ -1302,7 +1335,7 @@ void DatabaseContext::Private::identify(const DatabaseContextNNPtr &dbContext, constexpr double CONV_FACTOR_DEGREE = 1.74532925199432781271e-02; if (std::abs(convFactor - CONV_FACTOR_DEGREE) <= 1e-10 * CONV_FACTOR_DEGREE) { - authName = "EPSG"; + authName = metadata::Identifier::EPSG; code = "9102"; return; } @@ -1310,7 +1343,7 @@ void DatabaseContext::Private::identify(const DatabaseContextNNPtr &dbContext, } case common::UnitOfMeasure::Type::SCALE: { if (convFactor == 1.0) { - authName = "EPSG"; + authName = metadata::Identifier::EPSG; code = "9201"; return; } @@ -1390,7 +1423,7 @@ void DatabaseContext::Private::identify(const DatabaseContextNNPtr &dbContext, (axisList[0]->nameStr() == "Up" || axisList[0]->nameStr() == "Gravity-related height")) { // preferred coordinate system for gravity-related height - authName = "EPSG"; + authName = metadata::Identifier::EPSG; code = "6499"; return; } @@ -1416,16 +1449,16 @@ void DatabaseContext::Private::identify(const DatabaseContextNNPtr &dbContext, util::IComparable::Criterion::EQUIVALENT)) { authName = rowAuthName; code = rowCode; - if (authName == "EPSG" && code == "4400") { + if (authName == metadata::Identifier::EPSG && code == "4400") { // preferred coordinate system for cartesian // Easting, Northing return; } - if (authName == "EPSG" && code == "6422") { + if (authName == metadata::Identifier::EPSG && code == "6422") { // preferred coordinate system for geographic lat, lon return; } - if (authName == "EPSG" && code == "6423") { + if (authName == metadata::Identifier::EPSG && code == "6423") { // preferred coordinate system for geographic lat, lon, h return; } @@ -1487,9 +1520,25 @@ void DatabaseContext::Private::identifyOrInsert( // --------------------------------------------------------------------------- +static void +addAllowedAuthoritiesCond(const std::vector<std::string> &allowedAuthorities, + const std::string &authName, std::string &sql, + ListOfParams ¶ms) { + sql += "auth_name IN (?"; + params.emplace_back(authName); + for (const auto &allowedAuthority : allowedAuthorities) { + sql += ",?"; + params.emplace_back(allowedAuthority); + } + sql += ')'; +} + +// --------------------------------------------------------------------------- + void DatabaseContext::Private::identifyOrInsertUsages( const common::ObjectUsageNNPtr &obj, const std::string &tableName, const std::string &authName, const std::string &code, + const std::vector<std::string> &allowedAuthorities, std::vector<std::string> &sqlStatements) { std::string usageCode("USAGE_"); @@ -1517,13 +1566,16 @@ void DatabaseContext::Private::identifyOrInsertUsages( std::string scopeCode; const auto &scope = domain->scope(); if (scope.has_value()) { - const auto rows = - run("SELECT auth_name, code, " - "(CASE WHEN auth_name = 'EPSG' THEN 0 ELSE 1 END) " - "AS order_idx " - "FROM scope WHERE scope = ? AND deprecated = 0 " - "ORDER BY order_idx", - {*scope}); + std::string sql = + "SELECT auth_name, code, " + "(CASE WHEN auth_name = 'EPSG' THEN 0 ELSE 1 END) " + "AS order_idx " + "FROM scope WHERE scope = ? AND deprecated = 0 AND "; + ListOfParams params{*scope}; + addAllowedAuthoritiesCond(allowedAuthorities, authName, sql, + params); + sql += " ORDER BY order_idx, auth_name, code"; + const auto rows = run(sql, params); if (!rows.empty()) { const auto &row = rows.front(); scopeAuthName = row[0]; @@ -1531,10 +1583,10 @@ void DatabaseContext::Private::identifyOrInsertUsages( } else { scopeAuthName = authName; scopeCode = "SCOPE_" + tableName + "_" + code; - const auto sql = formatStatement( + const auto sqlToInsert = formatStatement( "INSERT INTO scope VALUES('%q','%q','%q',0);", scopeAuthName.c_str(), scopeCode.c_str(), scope->c_str()); - appendSql(sqlStatements, sql); + appendSql(sqlStatements, sqlToInsert); } } else { scopeAuthName = "PROJ"; @@ -1551,16 +1603,20 @@ void DatabaseContext::Private::identifyOrInsertUsages( dynamic_cast<const metadata::GeographicBoundingBox *>( geogElts.front().get()); if (bbox) { - const auto rows = run( + std::string sql = "SELECT auth_name, code, " "(CASE WHEN auth_name = 'EPSG' THEN 0 ELSE 1 END) " "AS order_idx " "FROM extent WHERE south_lat = ? AND north_lat = ? " "AND west_lon = ? AND east_lon = ? AND deprecated = 0 " - "ORDER BY order_idx", - {bbox->southBoundLatitude(), bbox->northBoundLatitude(), - bbox->westBoundLongitude(), - bbox->eastBoundLongitude()}); + "AND "; + ListOfParams params{ + bbox->southBoundLatitude(), bbox->northBoundLatitude(), + bbox->westBoundLongitude(), bbox->eastBoundLongitude()}; + addAllowedAuthoritiesCond(allowedAuthorities, authName, sql, + params); + sql += " ORDER BY order_idx, auth_name, code"; + const auto rows = run(sql, params); if (!rows.empty()) { const auto &row = rows.front(); extentAuthName = row[0]; @@ -1572,7 +1628,7 @@ void DatabaseContext::Private::identifyOrInsertUsages( if (description.empty()) { description = "unknown"; } - const auto sql = formatStatement( + const auto sqlToInsert = formatStatement( "INSERT INTO extent " "VALUES('%q','%q','%q','%q',%f,%f,%f,%f,0);", extentAuthName.c_str(), extentCode.c_str(), @@ -1581,7 +1637,7 @@ void DatabaseContext::Private::identifyOrInsertUsages( bbox->northBoundLatitude(), bbox->westBoundLongitude(), bbox->eastBoundLongitude()); - appendSql(sqlStatements, sql); + appendSql(sqlStatements, sqlToInsert); } } } @@ -1607,15 +1663,16 @@ void DatabaseContext::Private::identifyOrInsertUsages( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const datum::PrimeMeridianNNPtr &pm, const std::string &authName, - const std::string &code, bool /*numericCode*/) { + const std::string &code, bool /*numericCode*/, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); // Check if the object is already known under that code std::string pmAuthName; std::string pmCode; - identifyFromNameOrCode(self, allAuthFactory, pm, pmAuthName, pmCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, pm, pmAuthName, + pmCode); if (pmAuthName == authName && pmCode == code) { return {}; } @@ -1642,16 +1699,16 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const datum::EllipsoidNNPtr &ellipsoid, const std::string &authName, - const std::string &code, bool /*numericCode*/) { + const std::string &code, bool /*numericCode*/, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); // Check if the object is already known under that code std::string ellipsoidAuthName; std::string ellipsoidCode; - identifyFromNameOrCode(self, allAuthFactory, ellipsoid, ellipsoidAuthName, - ellipsoidCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, ellipsoid, + ellipsoidAuthName, ellipsoidCode); if (ellipsoidAuthName == authName && ellipsoidCode == code) { return {}; } @@ -1713,16 +1770,16 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const datum::GeodeticReferenceFrameNNPtr &datum, - const std::string &authName, const std::string &code, bool numericCode) { + const std::string &authName, const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); // Check if the object is already known under that code std::string datumAuthName; std::string datumCode; - identifyFromNameOrCode(self, allAuthFactory, datum, datumAuthName, - datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, datum, + datumAuthName, datumCode); if (datumAuthName == authName && datumCode == code) { return {}; } @@ -1733,7 +1790,7 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::string ellipsoidAuthName; std::string ellipsoidCode; const auto &ellipsoidOfDatum = datum->ellipsoid(); - identifyFromNameOrCode(self, allAuthFactory, ellipsoidOfDatum, + identifyFromNameOrCode(self, allowedAuthorities, authName, ellipsoidOfDatum, ellipsoidAuthName, ellipsoidCode); if (ellipsoidAuthName.empty()) { ellipsoidAuthName = authName; @@ -1744,14 +1801,16 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( ellipsoidCode = "ELLPS_" + code; } sqlStatements = self->getInsertStatementsFor( - ellipsoidOfDatum, ellipsoidAuthName, ellipsoidCode, numericCode); + ellipsoidOfDatum, ellipsoidAuthName, ellipsoidCode, numericCode, + allowedAuthorities); } // Find or insert prime meridian std::string pmAuthName; std::string pmCode; const auto &pmOfDatum = datum->primeMeridian(); - identifyFromNameOrCode(self, allAuthFactory, pmOfDatum, pmAuthName, pmCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, pmOfDatum, + pmAuthName, pmCode); if (pmAuthName.empty()) { pmAuthName = authName; if (numericCode) { @@ -1760,7 +1819,7 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( pmCode = "PM_" + code; } const auto sqlStatementsTmp = self->getInsertStatementsFor( - pmOfDatum, pmAuthName, pmCode, numericCode); + pmOfDatum, pmAuthName, pmCode, numericCode, allowedAuthorities); sqlStatements.insert(sqlStatements.end(), sqlStatementsTmp.begin(), sqlStatementsTmp.end()); } @@ -1776,7 +1835,7 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( appendSql(sqlStatements, sql); identifyOrInsertUsages(datum, "geodetic_datum", authName, code, - sqlStatements); + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -1785,10 +1844,10 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const crs::GeodeticCRSNNPtr &crs, const std::string &authName, - const std::string &code, bool numericCode) { + const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); std::vector<std::string> sqlStatements; @@ -1797,8 +1856,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::string datumCode; const auto &ensemble = crs->datumEnsemble(); if (ensemble) { - identifyFromNameOrCode(self, allAuthFactory, NN_NO_CHECK(ensemble), - datumAuthName, datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, + NN_NO_CHECK(ensemble), datumAuthName, datumCode); if (datumAuthName.empty()) { throw FactoryException( "Unhandled yet: insertion of new DatumEnsemble"); @@ -1807,8 +1866,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const auto &datum = crs->datum(); assert(datum); const auto datumNN = NN_NO_CHECK(datum); - identifyFromNameOrCode(self, allAuthFactory, datumNN, datumAuthName, - datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, datumNN, + datumAuthName, datumCode); if (datumAuthName.empty()) { datumAuthName = authName; if (numericCode) { @@ -1816,8 +1875,9 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( } else { datumCode = "GEODETIC_DATUM_" + code; } - sqlStatements = self->getInsertStatementsFor( - datumNN, datumAuthName, datumCode, numericCode); + sqlStatements = + self->getInsertStatementsFor(datumNN, datumAuthName, datumCode, + numericCode, allowedAuthorities); } } @@ -1847,7 +1907,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( datumAuthName.c_str(), datumCode.c_str()); appendSql(sqlStatements, sql); - identifyOrInsertUsages(crs, "geodetic_crs", authName, code, sqlStatements); + identifyOrInsertUsages(crs, "geodetic_crs", authName, code, + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -1855,10 +1916,10 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const crs::ProjectedCRSNNPtr &crs, const std::string &authName, - const std::string &code, bool numericCode) { + const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); std::vector<std::string> sqlStatements; @@ -1866,13 +1927,22 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const auto &baseCRS = crs->baseCRS(); std::string geodAuthName; std::string geodCode; - const auto candidates = baseCRS->identify(allAuthFactory); - for (const auto &candidate : candidates) { - if (candidate.second == 100) { - const auto &ids = candidate.first->identifiers(); - for (const auto &id : ids) { - geodAuthName = *(id->codeSpace()); - geodCode = id->code(); + + auto allowedAuthoritiesTmp(allowedAuthorities); + allowedAuthoritiesTmp.emplace_back(authName); + for (const auto &allowedAuthority : allowedAuthoritiesTmp) { + const auto factory = AuthorityFactory::create(self, allowedAuthority); + const auto candidates = baseCRS->identify(factory); + for (const auto &candidate : candidates) { + if (candidate.second == 100) { + const auto &ids = candidate.first->identifiers(); + for (const auto &id : ids) { + geodAuthName = *(id->codeSpace()); + geodCode = id->code(); + break; + } + } + if (!geodAuthName.empty()) { break; } } @@ -1880,8 +1950,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( if (geodAuthName.empty()) { geodAuthName = authName; geodCode = "GEODETIC_CRS_" + code; - sqlStatements = self->getInsertStatementsFor(baseCRS, geodAuthName, - geodCode, numericCode); + sqlStatements = self->getInsertStatementsFor( + baseCRS, geodAuthName, geodCode, numericCode, allowedAuthorities); } // Insert new record in conversion table @@ -1948,7 +2018,7 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( sql += ",0);"; appendSql(sqlStatements, sql); identifyOrInsertUsages(crs, "conversion", convAuthName, convCode, - sqlStatements); + allowedAuthorities, sqlStatements); } // Find or insert coordinate system @@ -1968,7 +2038,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( geodCode.c_str(), convAuthName.c_str(), convCode.c_str()); appendSql(sqlStatements, sql); - identifyOrInsertUsages(crs, "projected_crs", authName, code, sqlStatements); + identifyOrInsertUsages(crs, "projected_crs", authName, code, + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -1978,18 +2049,18 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const datum::VerticalReferenceFrameNNPtr &datum, const std::string &authName, const std::string &code, - bool /* numericCode */) { + bool /* numericCode */, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); std::vector<std::string> sqlStatements; // Check if the object is already known under that code std::string datumAuthName; std::string datumCode; - identifyFromNameOrCode(self, allAuthFactory, datum, datumAuthName, - datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, datum, + datumAuthName, datumCode); if (datumAuthName == authName && datumCode == code) { return {}; } @@ -2004,7 +2075,7 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( appendSql(sqlStatements, sql); identifyOrInsertUsages(datum, "vertical_datum", authName, code, - sqlStatements); + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -2013,10 +2084,10 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const crs::VerticalCRSNNPtr &crs, const std::string &authName, - const std::string &code, bool numericCode) { + const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); std::vector<std::string> sqlStatements; @@ -2025,8 +2096,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::string datumCode; const auto &ensemble = crs->datumEnsemble(); if (ensemble) { - identifyFromNameOrCode(self, allAuthFactory, NN_NO_CHECK(ensemble), - datumAuthName, datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, + NN_NO_CHECK(ensemble), datumAuthName, datumCode); if (datumAuthName.empty()) { throw FactoryException( "Unhandled yet: insertion of new DatumEnsemble"); @@ -2035,8 +2106,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const auto &datum = crs->datum(); assert(datum); const auto datumNN = NN_NO_CHECK(datum); - identifyFromNameOrCode(self, allAuthFactory, datumNN, datumAuthName, - datumCode); + identifyFromNameOrCode(self, allowedAuthorities, authName, datumNN, + datumAuthName, datumCode); if (datumAuthName.empty()) { datumAuthName = authName; if (numericCode) { @@ -2044,8 +2115,9 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( } else { datumCode = "VERTICAL_DATUM_" + code; } - sqlStatements = self->getInsertStatementsFor( - datumNN, datumAuthName, datumCode, numericCode); + sqlStatements = + self->getInsertStatementsFor(datumNN, datumAuthName, datumCode, + numericCode, allowedAuthorities); } } @@ -2066,7 +2138,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( datumAuthName.c_str(), datumCode.c_str()); appendSql(sqlStatements, sql); - identifyOrInsertUsages(crs, "vertical_crs", authName, code, sqlStatements); + identifyOrInsertUsages(crs, "vertical_crs", authName, code, + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -2075,10 +2148,10 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( const crs::CompoundCRSNNPtr &crs, const std::string &authName, - const std::string &code, bool numericCode) { + const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { const auto self = NN_NO_CHECK(self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); std::vector<std::string> sqlStatements; @@ -2089,20 +2162,33 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( throw FactoryException( "Cannot insert compound CRS with number of components != 2"); } + + auto allowedAuthoritiesTmp(allowedAuthorities); + allowedAuthoritiesTmp.emplace_back(authName); + for (const auto &component : components) { std::string compAuthName; std::string compCode; - const auto candidates = component->identify(allAuthFactory); - for (const auto &candidate : candidates) { - if (candidate.second == 100) { - const auto &ids = candidate.first->identifiers(); - for (const auto &id : ids) { - compAuthName = *(id->codeSpace()); - compCode = id->code(); + + for (const auto &allowedAuthority : allowedAuthoritiesTmp) { + const auto factory = + AuthorityFactory::create(self, allowedAuthority); + const auto candidates = component->identify(factory); + for (const auto &candidate : candidates) { + if (candidate.second == 100) { + const auto &ids = candidate.first->identifiers(); + for (const auto &id : ids) { + compAuthName = *(id->codeSpace()); + compCode = id->code(); + break; + } + } + if (!compAuthName.empty()) { break; } } } + if (compAuthName.empty()) { compAuthName = authName; if (numericCode) { @@ -2110,8 +2196,9 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( } else { compCode = "COMPONENT_" + code + '_' + toString(counter); } - const auto sqlStatementsTmp = self->getInsertStatementsFor( - component, compAuthName, compCode, numericCode); + const auto sqlStatementsTmp = + self->getInsertStatementsFor(component, compAuthName, compCode, + numericCode, allowedAuthorities); sqlStatements.insert(sqlStatements.end(), sqlStatementsTmp.begin(), sqlStatementsTmp.end()); } @@ -2132,7 +2219,8 @@ std::vector<std::string> DatabaseContext::Private::getInsertStatementsFor( componentsId[1].first.c_str(), componentsId[1].second.c_str()); appendSql(sqlStatements, sql); - identifyOrInsertUsages(crs, "compound_crs", authName, code, sqlStatements); + identifyOrInsertUsages(crs, "compound_crs", authName, code, + allowedAuthorities, sqlStatements); return sqlStatements; } @@ -2372,29 +2460,41 @@ DatabaseContext::suggestsCodeFor(const common::IdentifiedObjectNNPtr &object, * @param code Code with which the object will be inserted. * @param numericCode Whether intermediate objects that can be created should * use numeric codes (true), or may be alphanumeric (false) + * @param allowedAuthorities Authorities to which intermediate objects are + * allowed to refer to. authName will be implicitly + * added to it. Note that unit, coordinate + * systems, projection methods and parameters will in + * any case be allowed to refer to EPSG. * @throw FactoryException * @since 8.1 */ std::vector<std::string> DatabaseContext::getInsertStatementsFor( const common::IdentifiedObjectNNPtr &object, const std::string &authName, - const std::string &code, bool numericCode) { + const std::string &code, bool numericCode, + const std::vector<std::string> &allowedAuthorities) { if (d->memoryDbHandle_ == nullptr) { throw FactoryException( "startInsertStatementsSession() should be invoked first"); } - const auto self = NN_NO_CHECK(d->self_.lock()); - const auto allAuthFactory = AuthorityFactory::create(self, std::string()); const auto crs = util::nn_dynamic_pointer_cast<crs::CRS>(object); if (crs) { // Check if the object is already known under that code - const auto candidates = crs->identify(allAuthFactory); - for (const auto &candidate : candidates) { - if (candidate.second == 100) { - const auto &ids = candidate.first->identifiers(); - for (const auto &id : ids) { - if (*(id->codeSpace()) == authName && id->code() == code) { - return {}; + const auto self = NN_NO_CHECK(d->self_.lock()); + auto allowedAuthoritiesTmp(allowedAuthorities); + allowedAuthoritiesTmp.emplace_back(authName); + for (const auto &allowedAuthority : allowedAuthoritiesTmp) { + const auto factory = + AuthorityFactory::create(self, allowedAuthority); + const auto candidates = crs->identify(factory); + for (const auto &candidate : candidates) { + if (candidate.second == 100) { + const auto &ids = candidate.first->identifiers(); + for (const auto &id : ids) { + if (*(id->codeSpace()) == authName && + id->code() == code) { + return {}; + } } } } @@ -2404,57 +2504,57 @@ std::vector<std::string> DatabaseContext::getInsertStatementsFor( if (const auto pm = util::nn_dynamic_pointer_cast<datum::PrimeMeridian>(object)) { return d->getInsertStatementsFor(NN_NO_CHECK(pm), authName, code, - numericCode); + numericCode, allowedAuthorities); } else if (const auto ellipsoid = util::nn_dynamic_pointer_cast<datum::Ellipsoid>(object)) { return d->getInsertStatementsFor(NN_NO_CHECK(ellipsoid), authName, code, - numericCode); + numericCode, allowedAuthorities); } else if (const auto geodeticDatum = util::nn_dynamic_pointer_cast<datum::GeodeticReferenceFrame>( object)) { return d->getInsertStatementsFor(NN_NO_CHECK(geodeticDatum), authName, - code, numericCode); + code, numericCode, allowedAuthorities); } else if (const auto geodCRS = std::dynamic_pointer_cast<crs::GeodeticCRS>(crs)) { return d->getInsertStatementsFor(NN_NO_CHECK(geodCRS), authName, code, - numericCode); + numericCode, allowedAuthorities); } else if (const auto projCRS = std::dynamic_pointer_cast<crs::ProjectedCRS>(crs)) { return d->getInsertStatementsFor(NN_NO_CHECK(projCRS), authName, code, - numericCode); + numericCode, allowedAuthorities); } else if (const auto verticalDatum = util::nn_dynamic_pointer_cast<datum::VerticalReferenceFrame>( object)) { return d->getInsertStatementsFor(NN_NO_CHECK(verticalDatum), authName, - code, numericCode); + code, numericCode, allowedAuthorities); } else if (const auto vertCRS = std::dynamic_pointer_cast<crs::VerticalCRS>(crs)) { return d->getInsertStatementsFor(NN_NO_CHECK(vertCRS), authName, code, - numericCode); + numericCode, allowedAuthorities); } else if (const auto compoundCRS = std::dynamic_pointer_cast<crs::CompoundCRS>(crs)) { return d->getInsertStatementsFor(NN_NO_CHECK(compoundCRS), authName, - code, numericCode); + code, numericCode, allowedAuthorities); } else if (const auto boundCRS = std::dynamic_pointer_cast<crs::BoundCRS>(crs)) { return getInsertStatementsFor(boundCRS->baseCRS(), authName, code, - numericCode); + numericCode, allowedAuthorities); } else { @@ -3112,7 +3212,8 @@ AuthorityFactoryNNPtr AuthorityFactory::create(const DatabaseContextNNPtr &context, const std::string &authorityName) { const auto getFactory = [&context, &authorityName]() { - for (const auto &knownName : {"EPSG", "ESRI", "PROJ"}) { + for (const auto &knownName : + {metadata::Identifier::EPSG.c_str(), "ESRI", "PROJ"}) { if (ci_equal(authorityName, knownName)) { return AuthorityFactory::nn_make_shared<AuthorityFactory>( context, knownName); @@ -1195,6 +1195,7 @@ PROJ_STRING_LIST PROJ_DLL proj_get_insert_statements(PJ_CONTEXT *ctx, const char *authority, const char *code, int numeric_codes, + const char *const *allowed_authorities, const char *const *options); char PROJ_DLL *proj_suggests_code_for(PJ_CONTEXT *ctx, diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo index e300fc71..a91c307a 100755 --- a/test/cli/testprojinfo +++ b/test/cli/testprojinfo @@ -54,6 +54,10 @@ echo "Testing projinfo \"+proj=merc +lat_ts=5 +datum=WGS84 +type=crs\" --output- $EXE "+proj=merc +lat_ts=5 +datum=WGS84 +type=crs" --output-id HOBU:MY_CRS -o SQL -q >>${OUT} echo "" >>${OUT} +echo "Testing projinfo \"+proj=merc +lat_ts=5 +datum=WGS84 +type=crs\" --output-id HOBU:MY_CRS --authority HOBU -o SQL -q" >> ${OUT} +$EXE "+proj=merc +lat_ts=5 +datum=WGS84 +type=crs" --output-id HOBU:MY_CRS --authority HOBU -o SQL -q >>${OUT} +echo "" >>${OUT} + echo "Testing projinfo -s EPSG:4326 -t EPSG:32631 --single-line" >> ${OUT} $EXE -s EPSG:4326 -t EPSG:32631 --single-line >>${OUT} echo "" >>${OUT} diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index c5bae908..89c9dbcd 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -259,6 +259,18 @@ INSERT INTO usage VALUES('HOBU','USAGE_CONVERSION_MY_CRS','conversion','HOBU','C INSERT INTO projected_crs VALUES('HOBU','MY_CRS','unknown','','EPSG','4400','HOBU','GEODETIC_CRS_MY_CRS','HOBU','CONVERSION_MY_CRS',NULL,0); INSERT INTO usage VALUES('HOBU','USAGE_PROJECTED_CRS_MY_CRS','projected_crs','HOBU','MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); +Testing projinfo "+proj=merc +lat_ts=5 +datum=WGS84 +type=crs" --output-id HOBU:MY_CRS --authority HOBU -o SQL -q +INSERT INTO ellipsoid VALUES('HOBU','ELLPS_GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','WGS 84','','PROJ','EARTH',6378137,'EPSG','9001',298.257223563,NULL,0); +INSERT INTO prime_meridian VALUES('HOBU','PM_GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','Greenwich',0,'EPSG','9122',0); +INSERT INTO geodetic_datum VALUES('HOBU','GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','World Geodetic System 1984','','HOBU','ELLPS_GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','HOBU','PM_GEODETIC_DATUM_GEODETIC_CRS_MY_CRS',NULL,NULL,NULL,0); +INSERT INTO usage VALUES('HOBU','USAGE_GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','geodetic_datum','HOBU','GEODETIC_DATUM_GEODETIC_CRS_MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); +INSERT INTO geodetic_crs VALUES('HOBU','GEODETIC_CRS_MY_CRS','unknown','','geographic 2D','EPSG','6424','HOBU','GEODETIC_DATUM_GEODETIC_CRS_MY_CRS',NULL,0); +INSERT INTO usage VALUES('HOBU','USAGE_GEODETIC_CRS_MY_CRS','geodetic_crs','HOBU','GEODETIC_CRS_MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); +INSERT INTO conversion VALUES('HOBU','CONVERSION_MY_CRS','unknown','','EPSG','9805','Mercator (variant B)','EPSG','8823','Latitude of 1st standard parallel',5,'EPSG','9122','EPSG','8802','Longitude of natural origin',0,'EPSG','9122','EPSG','8806','False easting',0,'EPSG','9001','EPSG','8807','False northing',0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0); +INSERT INTO usage VALUES('HOBU','USAGE_CONVERSION_MY_CRS','conversion','HOBU','CONVERSION_MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); +INSERT INTO projected_crs VALUES('HOBU','MY_CRS','unknown','','EPSG','4400','HOBU','GEODETIC_CRS_MY_CRS','HOBU','CONVERSION_MY_CRS',NULL,0); +INSERT INTO usage VALUES('HOBU','USAGE_PROJECTED_CRS_MY_CRS','projected_crs','HOBU','MY_CRS','PROJ','EXTENT_UNKNOWN','PROJ','SCOPE_UNKNOWN'); + Testing projinfo -s EPSG:4326 -t EPSG:32631 --single-line Candidate operations found: 1 ------------------------------------- diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 120f8627..696e3615 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -5229,7 +5229,7 @@ TEST_F(CApi, proj_crs_is_derived) { // --------------------------------------------------------------------------- -TEST_F(CApi, proj_suggests_code_for) { +TEST_F(CApi, proj_get_insert_statements) { { auto session = proj_insert_object_session_create(nullptr); EXPECT_NE(session, nullptr); @@ -5274,16 +5274,59 @@ TEST_F(CApi, proj_suggests_code_for) { proj_string_destroy(code); } + const auto sizeOfStringList = [](const char *const *list) { + if (list == nullptr) + return -1; + int size = 0; + for (auto iter = list; *iter; ++iter) { + size += 1; + } + return size; + }; + // No session specified: we use a temporary session for (int i = 0; i < 2; i++) { - auto list = proj_get_insert_statements(m_ctxt, nullptr, crs, "HOBU", - "XXXX", false, nullptr); + auto list = proj_get_insert_statements( + m_ctxt, nullptr, crs, "HOBU", "XXXX", false, nullptr, nullptr); ASSERT_NE(list, nullptr); ASSERT_NE(list[0], nullptr); EXPECT_EQ(std::string(list[0]), "INSERT INTO geodetic_datum VALUES('HOBU'," "'GEODETIC_DATUM_XXXX','GDA2020','','EPSG','7019'," "'EPSG','8901',NULL,NULL,NULL,0);"); + EXPECT_EQ(sizeOfStringList(list), 4); + proj_string_list_destroy(list); + } + + // Pass an empty list of allowed authorities + // We cannot reuse the EPSG ellipsoid and prime meridian + { + const char *const allowed_authorities[] = {nullptr}; + auto list = + proj_get_insert_statements(m_ctxt, nullptr, crs, "HOBU", "XXXX", + false, allowed_authorities, nullptr); + EXPECT_EQ(sizeOfStringList(list), 6); + proj_string_list_destroy(list); + } + + // Allow only PROJ + // We cannot reuse the EPSG ellipsoid and prime meridian + { + const char *const allowed_authorities[] = {"PROJ", nullptr}; + auto list = + proj_get_insert_statements(m_ctxt, nullptr, crs, "HOBU", "XXXX", + false, allowed_authorities, nullptr); + EXPECT_EQ(sizeOfStringList(list), 6); + proj_string_list_destroy(list); + } + + // Allow EPSG + { + const char *const allowed_authorities[] = {"EPSG", nullptr}; + auto list = + proj_get_insert_statements(m_ctxt, nullptr, crs, "HOBU", "XXXX", + false, allowed_authorities, nullptr); + EXPECT_EQ(sizeOfStringList(list), 4); proj_string_list_destroy(list); } @@ -5291,8 +5334,8 @@ TEST_F(CApi, proj_suggests_code_for) { EXPECT_NE(session, nullptr); { - auto list = proj_get_insert_statements(m_ctxt, session, crs, "HOBU", - "XXXX", false, nullptr); + auto list = proj_get_insert_statements( + m_ctxt, session, crs, "HOBU", "XXXX", false, nullptr, nullptr); ASSERT_NE(list, nullptr); ASSERT_NE(list[0], nullptr); EXPECT_EQ(std::string(list[0]), @@ -5304,8 +5347,8 @@ TEST_F(CApi, proj_suggests_code_for) { // Object already inserted: return empty list { - auto list = proj_get_insert_statements(m_ctxt, session, crs, "HOBU", - "XXXX", false, nullptr); + auto list = proj_get_insert_statements( + m_ctxt, session, crs, "HOBU", "XXXX", false, nullptr, nullptr); ASSERT_NE(list, nullptr); ASSERT_EQ(list[0], nullptr); proj_string_list_destroy(list); |
