aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2018-12-06 16:23:07 +0100
committerEven Rouault <even.rouault@spatialys.com>2018-12-06 16:31:25 +0100
commita9ef3a229c6fef5ef8a05ba521a0237f2ffa6aa6 (patch)
tree8b2cee3e3a7466ff914d705c3fb05cb126c83138 /src
parent25fa18055f43d0881a1a2bf9990c99fecfcbdbb7 (diff)
downloadPROJ-a9ef3a229c6fef5ef8a05ba521a0237f2ffa6aa6.tar.gz
PROJ-a9ef3a229c6fef5ef8a05ba521a0237f2ffa6aa6.zip
Coordinate operation search: add a authority_to_authority_preference table to restrict and prioritize searches
Diffstat (limited to 'src')
-rw-r--r--src/c_api.cpp10
-rw-r--r--src/coordinateoperation.cpp100
-rw-r--r--src/crs.cpp135
-rw-r--r--src/factory.cpp62
4 files changed, 219 insertions, 88 deletions
diff --git a/src/c_api.cpp b/src/c_api.cpp
index fed91750..e1f34012 100644
--- a/src/c_api.cpp
+++ b/src/c_api.cpp
@@ -5354,9 +5354,17 @@ struct PJ_OPERATION_FACTORY_CONTEXT {
* The returned object must be unreferenced with
* proj_operation_factory_context_unref() after use.
*
+ * If authority is NULL or the empty string, then coordinate
+ * operations from any authority will be searched, with the restrictions set
+ * in the authority_to_authority_preference database table.
+ * If authority is set to "any", then coordinate
+ * operations from any authority will be searched
+ * If authority is a non-empty string different of "any",
+ * then coordinate operatiosn will be searched only in that authority namespace.
+ *
* @param ctx Context, or NULL for default context.
* @param authority Name of authority to which to restrict the search of
- * canidate operations. Or NULL to allow any authority.
+ * candidate operations.
* @return Object that must be unreferenced with
* proj_operation_factory_context_unref(), or NULL in
* case of error.
diff --git a/src/coordinateoperation.cpp b/src/coordinateoperation.cpp
index 04f9bc9a..d187f2cc 100644
--- a/src/coordinateoperation.cpp
+++ b/src/coordinateoperation.cpp
@@ -8937,6 +8937,14 @@ CoordinateOperationContext::getIntermediateCRS() const {
* If a non null authorityFactory is provided, the resulting context should
* not be used simultaneously by more than one thread.
*
+ * If authorityFactory->getAuthority() is the empty string, then coordinate
+ * operations from any authority will be searched, with the restrictions set
+ * in the authority_to_authority_preference database table.
+ * If authorityFactory->getAuthority() is set to "any", then coordinate
+ * operations from any authority will be searched
+ * If authorityFactory->getAuthority() is a non-empty string different of "any",
+ * then coordinate operatiosn will be searched only in that authority namespace.
+ *
* @param authorityFactory Authority factory, or null if no database lookup
* is allowed.
* Use io::authorityFactory::create(context, std::string()) to allow all
@@ -9663,6 +9671,8 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS,
const CoordinateOperationContextNNPtr &context) {
const auto &authFactory = context->getAuthorityFactory();
assert(authFactory);
+ const auto &authFactoryName = authFactory->getAuthority();
+
for (const auto &idSrc : sourceCRS->identifiers()) {
const auto &srcAuthName = *(idSrc->codeSpace());
const auto &srcCode = idSrc->code();
@@ -9671,17 +9681,39 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS,
const auto &targetAuthName = *(idTarget->codeSpace());
const auto &targetCode = idTarget->code();
if (!targetAuthName.empty()) {
- auto res =
- authFactory->createFromCoordinateReferenceSystemCodes(
- srcAuthName, srcCode, targetAuthName, targetCode,
- context->getUsePROJAlternativeGridNames(),
- context->getGridAvailabilityUse() ==
- CoordinateOperationContext::
- GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
- context->getDiscardSuperseded());
- if (!res.empty()) {
- return res;
+ std::vector<std::string> authorities;
+ if (authFactoryName == "any") {
+ authorities.emplace_back();
+ }
+ if (authFactoryName.empty()) {
+ authorities = authFactory->databaseContext()
+ ->getAllowedAuthorities(
+ srcAuthName, targetAuthName);
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ } else {
+ authorities.emplace_back(authFactoryName);
+ }
+ for (const auto &authority : authorities) {
+ const auto tmpAuthFactory =
+ io::AuthorityFactory::create(
+ authFactory->databaseContext(),
+ authority == "any" ? std::string() : authority);
+ auto res =
+ tmpAuthFactory
+ ->createFromCoordinateReferenceSystemCodes(
+ srcAuthName, srcCode, targetAuthName,
+ targetCode,
+ context->getUsePROJAlternativeGridNames(),
+ context->getGridAvailabilityUse() ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID,
+ context->getDiscardSuperseded());
+ if (!res.empty()) {
+ return res;
+ }
}
}
}
@@ -9706,6 +9738,8 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
const auto &authFactory = context->getAuthorityFactory();
assert(authFactory);
+ const auto &authFactoryName = authFactory->getAuthority();
+
for (const auto &idSrc : sourceCRS->identifiers()) {
const auto &srcAuthName = *(idSrc->codeSpace());
const auto &srcCode = idSrc->code();
@@ -9714,16 +9748,40 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
const auto &targetAuthName = *(idTarget->codeSpace());
const auto &targetCode = idTarget->code();
if (!targetAuthName.empty()) {
- auto res = authFactory->createFromCRSCodesWithIntermediates(
- srcAuthName, srcCode, targetAuthName, targetCode,
- context->getUsePROJAlternativeGridNames(),
- context->getGridAvailabilityUse() ==
- CoordinateOperationContext::GridAvailabilityUse::
- DISCARD_OPERATION_IF_MISSING_GRID,
- context->getDiscardSuperseded(),
- context->getIntermediateCRS());
- if (!res.empty()) {
- return res;
+ std::vector<std::string> authorities;
+ if (authFactoryName == "any") {
+ authorities.emplace_back();
+ }
+ if (authFactoryName.empty()) {
+ authorities = authFactory->databaseContext()
+ ->getAllowedAuthorities(
+ srcAuthName, targetAuthName);
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ } else {
+ authorities.emplace_back(authFactoryName);
+ }
+ for (const auto &authority : authorities) {
+ const auto tmpAuthFactory =
+ io::AuthorityFactory::create(
+ authFactory->databaseContext(),
+ authority == "any" ? std::string() : authority);
+
+ auto res =
+ tmpAuthFactory->createFromCRSCodesWithIntermediates(
+ srcAuthName, srcCode, targetAuthName,
+ targetCode,
+ context->getUsePROJAlternativeGridNames(),
+ context->getGridAvailabilityUse() ==
+ CoordinateOperationContext::
+ GridAvailabilityUse::
+ DISCARD_OPERATION_IF_MISSING_GRID,
+ context->getDiscardSuperseded(),
+ context->getIntermediateCRS());
+ if (!res.empty()) {
+ return res;
+ }
}
}
}
diff --git a/src/crs.cpp b/src/crs.cpp
index 546cfb0a..6212f561 100644
--- a/src/crs.cpp
+++ b/src/crs.cpp
@@ -409,74 +409,97 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
} else {
geodCRS = geogCRS;
}
- auto l_domains = domains();
+
+ if (!dbContext) {
+ return thisAsCRS;
+ }
+
+ const auto &l_domains = domains();
metadata::ExtentPtr extent;
if (!l_domains.empty()) {
extent = l_domains[0]->domainOfValidity();
}
- try {
- auto authFactory = dbContext
- ? io::AuthorityFactory::create(
- NN_NO_CHECK(dbContext), std::string())
- .as_nullable()
- : nullptr;
- auto ctxt = operation::CoordinateOperationContext::create(authFactory,
- extent, 0.0);
- // ctxt->setSpatialCriterion(
- // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
- auto list =
- operation::CoordinateOperationFactory::create()->createOperations(
- NN_NO_CHECK(geodCRS), hubCRS, ctxt);
- for (const auto &op : list) {
- auto transf =
- util::nn_dynamic_pointer_cast<operation::Transformation>(op);
- if (transf) {
- try {
- transf->getTOWGS84Parameters();
- } catch (const std::exception &) {
- continue;
- }
- return util::nn_static_pointer_cast<CRS>(
- BoundCRS::create(thisAsCRS, hubCRS, NN_NO_CHECK(transf)));
- } else {
- auto concatenated =
- dynamic_cast<const operation::ConcatenatedOperation *>(
- op.get());
- if (concatenated) {
- // Case for EPSG:4807 / "NTF (Paris)" that is made of a
- // longitude rotation followed by a Helmert
- // The prime meridian shift will be accounted elsewhere
- const auto &subops = concatenated->operations();
- if (subops.size() == 2) {
- auto firstOpIsTransformation =
- dynamic_cast<const operation::Transformation *>(
- subops[0].get());
- auto firstOpIsConversion =
- dynamic_cast<const operation::Conversion *>(
- subops[0].get());
- if ((firstOpIsTransformation &&
- firstOpIsTransformation->isLongitudeRotation()) ||
- (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) &&
- firstOpIsConversion)) {
- transf = util::nn_dynamic_pointer_cast<
- operation::Transformation>(subops[1]);
- if (transf) {
- try {
- transf->getTOWGS84Parameters();
- } catch (const std::exception &) {
- continue;
+ std::string crs_authority;
+ const auto &l_identifiers = identifiers();
+ // If the object has an authority, restrict the transformations to
+ // come from that codespace too. This avoids for example EPSG:4269
+ // (NAD83) to use a (dubious) ESRI transformation.
+ if (!l_identifiers.empty()) {
+ crs_authority = *(l_identifiers[0]->codeSpace());
+ }
+
+ auto authorities = dbContext->getAllowedAuthorities(crs_authority, "EPSG");
+ if (authorities.empty()) {
+ authorities.emplace_back();
+ }
+ for (const auto &authority : authorities) {
+ try {
+
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext),
+ authority == "any" ? std::string() : authority);
+ auto ctxt = operation::CoordinateOperationContext::create(
+ authFactory, extent, 0.0);
+ // ctxt->setSpatialCriterion(
+ // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION);
+ auto list =
+ operation::CoordinateOperationFactory::create()
+ ->createOperations(NN_NO_CHECK(geodCRS), hubCRS, ctxt);
+ for (const auto &op : list) {
+ auto transf =
+ util::nn_dynamic_pointer_cast<operation::Transformation>(
+ op);
+ if (transf && !starts_with(transf->nameStr(), "Null geo")) {
+ try {
+ transf->getTOWGS84Parameters();
+ } catch (const std::exception &) {
+ continue;
+ }
+ return util::nn_static_pointer_cast<CRS>(BoundCRS::create(
+ thisAsCRS, hubCRS, NN_NO_CHECK(transf)));
+ } else {
+ auto concatenated =
+ dynamic_cast<const operation::ConcatenatedOperation *>(
+ op.get());
+ if (concatenated) {
+ // Case for EPSG:4807 / "NTF (Paris)" that is made of a
+ // longitude rotation followed by a Helmert
+ // The prime meridian shift will be accounted elsewhere
+ const auto &subops = concatenated->operations();
+ if (subops.size() == 2) {
+ auto firstOpIsTransformation =
+ dynamic_cast<const operation::Transformation *>(
+ subops[0].get());
+ auto firstOpIsConversion =
+ dynamic_cast<const operation::Conversion *>(
+ subops[0].get());
+ if ((firstOpIsTransformation &&
+ firstOpIsTransformation
+ ->isLongitudeRotation()) ||
+ (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) &&
+ firstOpIsConversion)) {
+ transf = util::nn_dynamic_pointer_cast<
+ operation::Transformation>(subops[1]);
+ if (transf &&
+ !starts_with(transf->nameStr(),
+ "Null geo")) {
+ try {
+ transf->getTOWGS84Parameters();
+ } catch (const std::exception &) {
+ continue;
+ }
+ return util::nn_static_pointer_cast<CRS>(
+ BoundCRS::create(thisAsCRS, hubCRS,
+ NN_NO_CHECK(transf)));
}
- return util::nn_static_pointer_cast<CRS>(
- BoundCRS::create(thisAsCRS, hubCRS,
- NN_NO_CHECK(transf)));
}
}
}
}
}
+ } catch (const std::exception &) {
}
- } catch (const std::exception &) {
}
return thisAsCRS;
}
diff --git a/src/factory.cpp b/src/factory.cpp
index e24cee58..20701def 100644
--- a/src/factory.cpp
+++ b/src/factory.cpp
@@ -880,6 +880,44 @@ std::string DatabaseContext::getTextDefinition(const std::string &tableName,
return res[0][0];
}
+// ---------------------------------------------------------------------------
+
+/** \brief Return the allowed authorities when researching transformations
+ * between different authorities.
+ *
+ * @throw FactoryException
+ */
+std::vector<std::string> DatabaseContext::getAllowedAuthorities(
+ const std::string &sourceAuthName,
+ const std::string &targetAuthName) const {
+ auto res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = ? AND target_auth_name = ?",
+ {sourceAuthName, targetAuthName});
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = ? AND target_auth_name = 'any'",
+ {sourceAuthName});
+ }
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = 'any' AND target_auth_name = ?",
+ {targetAuthName});
+ }
+ if (res.empty()) {
+ res = d->run(
+ "SELECT allowed_authorities FROM authority_to_authority_preference "
+ "WHERE source_auth_name = 'any' AND target_auth_name = 'any'",
+ {});
+ }
+ if (res.empty()) {
+ return std::vector<std::string>();
+ }
+ return split(res[0][0], ',');
+}
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -947,6 +985,10 @@ struct AuthorityFactory::Private {
SQLResultSet runWithCodeParam(const char *sql, const std::string &code);
+ bool hasAuthorityRestriction() const {
+ return !authority_.empty() && authority_ != "any";
+ }
+
private:
DatabaseContextNNPtr context_;
std::string authority_;
@@ -3097,7 +3139,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
"= ? AND auth_name = ? AND code = ? AND deprecated != 1");
auto params = std::vector<SQLValues>{sourceCRSAuthName, sourceCRSCode,
targetCRSAuthName, targetCRSCode};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND conversion_auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -3134,7 +3176,7 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
}
params = {sourceCRSAuthName, sourceCRSCode, targetCRSAuthName,
targetCRSCode};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND cov.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -3361,7 +3403,7 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
"AND v1.deprecated = 0 AND v2.deprecated = 0 "
"AND intersects_bbox(south_lat1, west_lon1, north_lat1, east_lon1, "
"south_lat2, west_lon2, north_lat2, east_lon2) == 1 ");
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? ";
params.emplace_back(getAuthority());
params.emplace_back(getAuthority());
@@ -3852,7 +3894,7 @@ AuthorityFactory::createObjectsFromName(
sql += "name LIKE ? AND ";
params.push_back(searchedNameWithoutDeprecated);
}
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " auth_name = ? AND ";
params.emplace_back(getAuthority());
}
@@ -4152,7 +4194,7 @@ AuthorityFactory::listAreaOfUseFromName(const std::string &name,
std::string sql(
"SELECT auth_name, code FROM area WHERE deprecated = 0 AND ");
std::vector<SQLValues> params;
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " auth_name = ? AND ";
params.emplace_back(getAuthority());
}
@@ -4206,7 +4248,7 @@ std::list<crs::GeodeticCRSNNPtr> AuthorityFactory::createGeodeticCRSFromDatum(
"SELECT auth_name, code FROM geodetic_crs WHERE "
"datum_auth_name = ? AND datum_code = ? AND deprecated = 0");
std::vector<SQLValues> params{datum_auth_name, datum_code};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4242,7 +4284,7 @@ AuthorityFactory::createGeodeticCRSFromEllipsoid(
"geodetic_datum.deprecated = 0 AND "
"geodetic_crs.deprecated = 0");
std::vector<SQLValues> params{ellipsoid_auth_name, ellipsoid_code};
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND geodetic_crs.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4363,7 +4405,7 @@ AuthorityFactory::createProjectedCRSFromExisting(
sql += "conversion.method_auth_name = 'EPSG' AND "
"conversion.method_code = ?";
params.emplace_back(toString(methodEPSGCode));
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND projected_crs.auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4533,7 +4575,7 @@ AuthorityFactory::createProjectedCRSFromExisting(
params.emplace_back(patternVal);
}
sql += ")";
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
sql += " AND auth_name = ?";
params.emplace_back(getAuthority());
}
@@ -4597,7 +4639,7 @@ AuthorityFactory::createCompoundCRSFromExisting(
"vertical_crs_");
addAnd = true;
}
- if (!getAuthority().empty()) {
+ if (d->hasAuthorityRestriction()) {
if (addAnd) {
sql += " AND ";
}