diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2018-12-06 16:23:07 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2018-12-06 16:31:25 +0100 |
| commit | a9ef3a229c6fef5ef8a05ba521a0237f2ffa6aa6 (patch) | |
| tree | 8b2cee3e3a7466ff914d705c3fb05cb126c83138 /src | |
| parent | 25fa18055f43d0881a1a2bf9990c99fecfcbdbb7 (diff) | |
| download | PROJ-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.cpp | 10 | ||||
| -rw-r--r-- | src/coordinateoperation.cpp | 100 | ||||
| -rw-r--r-- | src/crs.cpp | 135 | ||||
| -rw-r--r-- | src/factory.cpp | 62 |
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 "; } |
