diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-11-02 03:18:32 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-11-02 14:54:20 +0100 |
| commit | d9064c3de62982c461a36ed9997eab56d1cb9f85 (patch) | |
| tree | 0689ff513a9de2c2cc9521ed447cded9ad9fd134 | |
| parent | 00608cc1c892a0d0522ffd08127a4494d4844e19 (diff) | |
| download | PROJ-d9064c3de62982c461a36ed9997eab56d1cb9f85.tar.gz PROJ-d9064c3de62982c461a36ed9997eab56d1cb9f85.zip | |
Add tracing framework, and improve createOperations() performance
| -rw-r--r-- | include/proj/internal/Makefile.am | 4 | ||||
| -rw-r--r-- | include/proj/internal/tracing.hpp | 85 | ||||
| -rw-r--r-- | include/proj/io.hpp | 10 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 4 | ||||
| -rwxr-xr-x | scripts/reformat_cpp.sh | 2 | ||||
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 629 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 498 | ||||
| -rw-r--r-- | src/lib_proj.cmake | 1 | ||||
| -rw-r--r-- | src/tracing.cpp | 224 |
10 files changed, 1084 insertions, 377 deletions
diff --git a/include/proj/internal/Makefile.am b/include/proj/internal/Makefile.am index f5bbeb7b..ba14c2a5 100644 --- a/include/proj/internal/Makefile.am +++ b/include/proj/internal/Makefile.am @@ -8,4 +8,6 @@ noinst_HEADERS = \ internal.hpp \ io_internal.hpp \ lru_cache.hpp \ - include_nlohmann_json.hpp + include_nlohmann_json.hpp \ + tracing.hpp + diff --git a/include/proj/internal/tracing.hpp b/include/proj/internal/tracing.hpp new file mode 100644 index 00000000..6b34aa39 --- /dev/null +++ b/include/proj/internal/tracing.hpp @@ -0,0 +1,85 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Tracing/profiling + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef TRACING_HH_INCLUDED +#define TRACING_HH_INCLUDED + +//! @cond Doxygen_Suppress + +#include <string> + +#include "proj/util.hpp" + +#ifdef ENABLE_TRACING + +NS_PROJ_START + +namespace tracing { + +void logTrace(const std::string &str, + const std::string &component = std::string()); + +class EnterBlock { + public: + EnterBlock(const std::string &msg); + ~EnterBlock(); + + private: + PROJ_OPAQUE_PRIVATE_DATA +}; + +#define TRACING_MERGE(a, b) a##b +#define TRACING_UNIQUE_NAME(a) TRACING_MERGE(unique_name_, a) + +#define ENTER_BLOCK(x) EnterBlock TRACING_UNIQUE_NAME(__LINE__)(x) +#define ENTER_FUNCTION() ENTER_BLOCK(__FUNCTION__ + std::string("()")) + +} // namespace tracing + +NS_PROJ_END + +using namespace NS_PROJ::tracing; + +#else // ENABLE_TRACING + +inline void logTrace(const std::string &, const std::string & = std::string()) { +} + +#define ENTER_BLOCK(x) \ + do { \ + } while (0); + +#define ENTER_FUNCTION() \ + do { \ + } while (0) + +#endif // ENABLE_TRACING + +//! @endcond + +#endif // TRACING_HH_INCLUDED diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 37b94c1e..f8ee1c97 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -1050,7 +1050,10 @@ class PROJ_GCC_DLL AuthorityFactory { const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, - bool discardSuperseded) const; + bool discardSuperseded, bool tryReverseOrder = false, + bool reportOnlyIntersectingTransformations = false, + const metadata::ExtentPtr &intersectingExtent1 = nullptr, + const metadata::ExtentPtr &intersectingExtent2 = nullptr) const; PROJ_DLL std::vector<operation::CoordinateOperationNNPtr> createFromCRSCodesWithIntermediates( @@ -1059,7 +1062,10 @@ class PROJ_GCC_DLL AuthorityFactory { bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded, const std::vector<std::pair<std::string, std::string>> - &intermediateCRSAuthCodes) const; + &intermediateCRSAuthCodes, + ObjectType allowedIntermediateObjectType = ObjectType::CRS, + const metadata::ExtentPtr &intersectingExtent1 = nullptr, + const metadata::ExtentPtr &intersectingExtent2 = nullptr) const; PROJ_DLL std::string getOfficialNameFromAlias( const std::string &aliasedName, const std::string &tableName, diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index 34e23b22..cb568566 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -299,8 +299,8 @@ osgeo::proj::io::AuthorityFactory::create(dropbox::oxygen::nn<std::shared_ptr<os osgeo::proj::io::AuthorityFactory::createEllipsoid(std::string const&) const osgeo::proj::io::AuthorityFactory::createExtent(std::string const&) const osgeo::proj::io::AuthorityFactory::createFromCoordinateReferenceSystemCodes(std::string const&, std::string const&) const -osgeo::proj::io::AuthorityFactory::createFromCoordinateReferenceSystemCodes(std::string const&, std::string const&, std::string const&, std::string const&, bool, bool, bool) const -osgeo::proj::io::AuthorityFactory::createFromCRSCodesWithIntermediates(std::string const&, std::string const&, std::string const&, std::string const&, bool, bool, bool, std::vector<std::pair<std::string, std::string>, std::allocator<std::pair<std::string, std::string> > > const&) const +osgeo::proj::io::AuthorityFactory::createFromCoordinateReferenceSystemCodes(std::string const&, std::string const&, std::string const&, std::string const&, bool, bool, bool, bool, bool, std::shared_ptr<osgeo::proj::metadata::Extent> const&, std::shared_ptr<osgeo::proj::metadata::Extent> const&) const +osgeo::proj::io::AuthorityFactory::createFromCRSCodesWithIntermediates(std::string const&, std::string const&, std::string const&, std::string const&, bool, bool, bool, std::vector<std::pair<std::string, std::string>, std::allocator<std::pair<std::string, std::string> > > const&, osgeo::proj::io::AuthorityFactory::ObjectType, std::shared_ptr<osgeo::proj::metadata::Extent> const&, std::shared_ptr<osgeo::proj::metadata::Extent> const&) const osgeo::proj::io::AuthorityFactory::createGeodeticCRS(std::string const&) const osgeo::proj::io::AuthorityFactory::createGeodeticDatum(std::string const&) const osgeo::proj::io::AuthorityFactory::createGeographicCRS(std::string const&) const diff --git a/scripts/reformat_cpp.sh b/scripts/reformat_cpp.sh index cc10b53e..a8002a6c 100755 --- a/scripts/reformat_cpp.sh +++ b/scripts/reformat_cpp.sh @@ -15,7 +15,7 @@ esac TOPDIR="$SCRIPT_DIR/.." -for i in "$TOPDIR"/include/proj/*.hpp "$TOPDIR"/include/proj/internal/*.hpp "$TOPDIR"/src/iso19111/*.cpp "$TOPDIR"/test/unit/*.cpp "$TOPDIR"/src/apps/projinfo.cpp; do +for i in "$TOPDIR"/include/proj/*.hpp "$TOPDIR"/include/proj/internal/*.hpp "$TOPDIR"/src/iso19111/*.cpp "$TOPDIR"/test/unit/*.cpp "$TOPDIR"/src/apps/projinfo.cpp "$TOPDIR"/src/tracing.cpp; do if ! echo "$i" | grep -q "lru_cache.hpp"; then "$SCRIPT_DIR"/reformat.sh "$i"; fi diff --git a/src/Makefile.am b/src/Makefile.am index 79ca75d7..0a209f9b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -210,7 +210,9 @@ libproj_la_SOURCES = \ wkt2_parser.h wkt2_parser.cpp \ wkt2_generated_parser.h wkt2_generated_parser.c \ \ - proj_json_streaming_writer.cpp + proj_json_streaming_writer.cpp \ + \ + tracing.cpp # The sed hack is to please MSVC diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 9796221d..d0a1761c 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -40,6 +40,7 @@ #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" +#include "proj/internal/tracing.hpp" // PROJ include order is sensitive // clang-format off @@ -56,11 +57,10 @@ #include <string> #include <vector> -// #define DEBUG +// #define TRACE_CREATE_OPERATIONS // #define DEBUG_SORT // #define DEBUG_CONCATENATED_OPERATION -#if defined(DEBUG) || defined(DEBUG_SORT) || \ - defined(DEBUG_CONCATENATED_OPERATION) +#if defined(DEBUG_SORT) || defined(DEBUG_CONCATENATED_OPERATION) #include <iostream> #endif @@ -135,6 +135,26 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom, // --------------------------------------------------------------------------- +#ifdef TRACE_CREATE_OPERATIONS + +//! @cond Doxygen_Suppress + +static std::string objectAsStr(const common::IdentifiedObject *obj) { + std::string ret(obj->nameStr()); + const auto &ids = obj->identifiers(); + if (!ids.empty()) { + ret += " ("; + ret += (*ids[0]->codeSpace()) + ":" + ids[0]->code(); + ret += ")"; + } + return ret; +} +//! @endcond + +#endif + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress class InvalidOperationEmptyIntersection : public InvalidOperation { @@ -10373,6 +10393,9 @@ struct CoordinateOperationFactory::Private { bool inCreateOperationsGeogToVertWithAlternativeGeog = false; bool inCreateOperationsGeogToVertWithIntermediateVert = false; bool skipHorizontalTransformation = false; + std::map<std::pair<io::AuthorityFactory::ObjectType, std::string>, + std::list<std::pair<std::string, std::string>>> + cacheNameToCRS{}; Context(const metadata::ExtentPtr &extent1In, const metadata::ExtentPtr &extent2In, @@ -10387,6 +10410,24 @@ struct CoordinateOperationFactory::Private { private: static constexpr bool allowEmptyIntersection = true; + static void + buildCRSIds(const crs::CRSNNPtr &crs, Private::Context &context, + std::list<std::pair<std::string, std::string>> &ids); + + static std::vector<CoordinateOperationNNPtr> + findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, + const crs::CRSNNPtr &targetCRS, + Private::Context &context); + + static std::vector<CoordinateOperationNNPtr> + findOpsInRegistryDirectTo(const crs::CRSNNPtr &targetCRS, + Private::Context &context); + + static std::vector<CoordinateOperationNNPtr> + findsOpsInRegistryWithIntermediate(const crs::CRSNNPtr &sourceCRS, + const crs::CRSNNPtr &targetCRS, + Private::Context &context); + static void createOperationsFromProj4Ext( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, @@ -11232,9 +11273,20 @@ filterAndSort(const std::vector<CoordinateOperationNNPtr> &sourceList, const CoordinateOperationContextNNPtr &context, const metadata::ExtentPtr &extent1, const metadata::ExtentPtr &extent2) { - return FilterResults(sourceList, context, extent1, extent2, false) - .andSort() - .getRes(); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_FUNCTION(); + logTrace("number of results before filter and sort: " + + toString(static_cast<int>(sourceList.size()))); +#endif + auto resFiltered = + FilterResults(sourceList, context, extent1, extent2, false) + .andSort() + .getRes(); +#ifdef TRACE_CREATE_OPERATIONS + logTrace("number of results after filter and sort: " + + toString(static_cast<int>(resFiltered.size()))); +#endif + return resFiltered; } //! @endcond @@ -11256,13 +11308,9 @@ applyInverse(const std::vector<CoordinateOperationNNPtr> &list) { //! @cond Doxygen_Suppress -static void buildCRSIds(const crs::CRSNNPtr &crs, - const CoordinateOperationContextNNPtr &context, - std::list<std::pair<std::string, std::string>> &ids) { - const auto &authFactory = context->getAuthorityFactory(); - assert(authFactory); - const auto &authFactoryName = authFactory->getAuthority(); - +void CoordinateOperationFactory::Private::buildCRSIds( + const crs::CRSNNPtr &crs, Private::Context &context, + std::list<std::pair<std::string, std::string>> &ids) { for (const auto &id : crs->identifiers()) { const auto &authName = *(id->codeSpace()); const auto &code = id->code(); @@ -11271,24 +11319,40 @@ static void buildCRSIds(const crs::CRSNNPtr &crs, } } if (ids.empty()) { - try { - const auto tmpAuthFactory = io::AuthorityFactory::create( - authFactory->databaseContext(), - (authFactoryName.empty() || authFactoryName == "any") - ? std::string() - : authFactoryName); - std::vector<io::AuthorityFactory::ObjectType> allowedObjects; - auto geogCRS = dynamic_cast<const crs::GeographicCRS *>(crs.get()); - if (geogCRS) { - allowedObjects.push_back( - geogCRS->coordinateSystem()->axisList().size() == 2 - ? io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS - : io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); - } else if (dynamic_cast<crs::ProjectedCRS *>(crs.get())) { - allowedObjects.push_back( - io::AuthorityFactory::ObjectType::PROJECTED_CRS); - } - if (!allowedObjects.empty()) { + std::vector<io::AuthorityFactory::ObjectType> allowedObjects; + auto geogCRS = dynamic_cast<const crs::GeographicCRS *>(crs.get()); + if (geogCRS) { + allowedObjects.push_back( + geogCRS->coordinateSystem()->axisList().size() == 2 + ? io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS + : io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); + } else if (dynamic_cast<crs::ProjectedCRS *>(crs.get())) { + allowedObjects.push_back( + io::AuthorityFactory::ObjectType::PROJECTED_CRS); + } else if (dynamic_cast<crs::VerticalCRS *>(crs.get())) { + allowedObjects.push_back( + io::AuthorityFactory::ObjectType::VERTICAL_CRS); + } + if (!allowedObjects.empty()) { + + const std::pair<io::AuthorityFactory::ObjectType, std::string> key( + allowedObjects[0], crs->nameStr()); + auto iter = context.cacheNameToCRS.find(key); + if (iter != context.cacheNameToCRS.end()) { + ids = iter->second; + return; + } + + const auto &authFactory = context.context->getAuthorityFactory(); + assert(authFactory); + const auto &authFactoryName = authFactory->getAuthority(); + try { + const auto tmpAuthFactory = io::AuthorityFactory::create( + authFactory->databaseContext(), + (authFactoryName.empty() || authFactoryName == "any") + ? std::string() + : authFactoryName); + auto matches = tmpAuthFactory->createObjectsFromName( crs->nameStr(), allowedObjects, false, 2); if (matches.size() == 1 && @@ -11300,8 +11364,9 @@ static void buildCRSIds(const crs::CRSNNPtr &crs, ids.emplace_back(*(tmpIds[0]->codeSpace()), tmpIds[0]->code()); } + } catch (const std::exception &) { } - } catch (const std::exception &) { + context.cacheNameToCRS[key] = ids; } } } @@ -11332,13 +11397,18 @@ getCandidateAuthorities(const io::AuthorityFactoryPtr &authFactory, // --------------------------------------------------------------------------- // Look in the authority registry for operations from sourceCRS to targetCRS -static std::vector<CoordinateOperationNNPtr> -findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, - const crs::CRSNNPtr &targetCRS, - const CoordinateOperationContextNNPtr &context) { - const auto &authFactory = context->getAuthorityFactory(); +std::vector<CoordinateOperationNNPtr> +CoordinateOperationFactory::Private::findOpsInRegistryDirect( + const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + Private::Context &context) { + const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("findOpsInRegistryDirect(" + objectAsStr(sourceCRS.get()) + + " --> " + objectAsStr(targetCRS.get()) + ")"); +#endif + std::list<std::pair<std::string, std::string>> sourceIds; std::list<std::pair<std::string, std::string>> targetIds; buildCRSIds(sourceCRS, context, sourceIds); @@ -11360,13 +11430,23 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, auto res = tmpAuthFactory->createFromCoordinateReferenceSystemCodes( srcAuthName, srcCode, targetAuthName, targetCode, - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == + context.context->getUsePROJAlternativeGridNames(), + context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded()); + context.context->getDiscardSuperseded(), true, false, + context.extent1, context.extent2); if (!res.empty()) { - return res; + auto resFiltered = + FilterResults(res, context.context, context.extent1, + context.extent2, false) + .getRes(); +#ifdef TRACE_CREATE_OPERATIONS + logTrace("filtering reduced from " + + toString(static_cast<int>(res.size())) + " to " + + toString(static_cast<int>(resFiltered.size()))); +#endif + return resFiltered; } } } @@ -11376,48 +11456,16 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, // --------------------------------------------------------------------------- -// Look in the authority registry for operations from sourceCRS -static std::vector<CoordinateOperationNNPtr> -findOpsInRegistryDirectFrom(const crs::CRSNNPtr &sourceCRS, - const CoordinateOperationContextNNPtr &context) { - const auto &authFactory = context->getAuthorityFactory(); - assert(authFactory); - - std::list<std::pair<std::string, std::string>> ids; - buildCRSIds(sourceCRS, context, ids); - - for (const auto &id : ids) { - const auto &srcAuthName = id.first; - const auto &srcCode = id.second; - - const auto authorities( - getCandidateAuthorities(authFactory, srcAuthName, srcAuthName)); - for (const auto &authority : authorities) { - const auto tmpAuthFactory = io::AuthorityFactory::create( - authFactory->databaseContext(), - authority == "any" ? std::string() : authority); - auto res = tmpAuthFactory->createFromCoordinateReferenceSystemCodes( - srcAuthName, srcCode, std::string(), std::string(), - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == - CoordinateOperationContext::GridAvailabilityUse:: - DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded()); - if (!res.empty()) { - return res; - } - } - } - return std::vector<CoordinateOperationNNPtr>(); -} - -// --------------------------------------------------------------------------- - // Look in the authority registry for operations to targetCRS -static std::vector<CoordinateOperationNNPtr> -findOpsInRegistryDirectTo(const crs::CRSNNPtr &targetCRS, - const CoordinateOperationContextNNPtr &context) { - const auto &authFactory = context->getAuthorityFactory(); +std::vector<CoordinateOperationNNPtr> +CoordinateOperationFactory::Private::findOpsInRegistryDirectTo( + const crs::CRSNNPtr &targetCRS, Private::Context &context) { +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("findOpsInRegistryDirectTo({any} -->" + + objectAsStr(targetCRS.get()) + ")"); +#endif + + const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); std::list<std::pair<std::string, std::string>> ids; @@ -11435,13 +11483,23 @@ findOpsInRegistryDirectTo(const crs::CRSNNPtr &targetCRS, authority == "any" ? std::string() : authority); auto res = tmpAuthFactory->createFromCoordinateReferenceSystemCodes( std::string(), std::string(), targetAuthName, targetCode, - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == + context.context->getUsePROJAlternativeGridNames(), + context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded()); + context.context->getDiscardSuperseded(), true, true, + context.extent1, context.extent2); if (!res.empty()) { - return res; + auto resFiltered = + FilterResults(res, context.context, context.extent1, + context.extent2, false) + .getRes(); +#ifdef TRACE_CREATE_OPERATIONS + logTrace("filtering reduced from " + + toString(static_cast<int>(res.size())) + " to " + + toString(static_cast<int>(resFiltered.size()))); +#endif + return resFiltered; } } } @@ -11456,11 +11514,18 @@ findOpsInRegistryDirectTo(const crs::CRSNNPtr &targetCRS, // Look in the authority registry for operations from sourceCRS to targetCRS // using an intermediate pivot -static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate( +std::vector<CoordinateOperationNNPtr> +CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, - const CoordinateOperationContextNNPtr &context) { + Private::Context &context) { - const auto &authFactory = context->getAuthorityFactory(); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("findsOpsInRegistryWithIntermediate(" + + objectAsStr(sourceCRS.get()) + " --> " + + objectAsStr(targetCRS.get()) + ")"); +#endif + + const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); std::list<std::pair<std::string, std::string>> sourceIds; @@ -11482,41 +11547,40 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate( authFactory->databaseContext(), authority == "any" ? std::string() : authority); + io::AuthorityFactory::ObjectType intermediateObjectType = + io::AuthorityFactory::ObjectType::CRS; + + // If doing GeogCRS --> GeogCRS, only use GeogCRS as + // intermediate CRS + // Avoid weird behaviour when doing NAD83 -> NAD83(2011) + // that would go through NAVD88 otherwise. + if (context.context->getIntermediateCRS().empty() && + dynamic_cast<const crs::GeographicCRS *>(sourceCRS.get()) && + dynamic_cast<const crs::GeographicCRS *>(targetCRS.get())) { + intermediateObjectType = + io::AuthorityFactory::ObjectType::GEOGRAPHIC_CRS; + } auto res = tmpAuthFactory->createFromCRSCodesWithIntermediates( srcAuthName, srcCode, targetAuthName, targetCode, - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == + context.context->getUsePROJAlternativeGridNames(), + context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded(), - context->getIntermediateCRS()); + context.context->getDiscardSuperseded(), + context.context->getIntermediateCRS(), + intermediateObjectType, context.extent1, context.extent2); if (!res.empty()) { - // If doing GeogCRS --> GeogCRS, only use GeogCRS as - // intermediate CRS - // Avoid weird behaviour when doing NAD83 -> NAD83(2011) - // that would go through NAVD88 otherwise. - if (context->getIntermediateCRS().empty() && - dynamic_cast<const crs::GeographicCRS *>( - sourceCRS.get()) && - dynamic_cast<const crs::GeographicCRS *>( - targetCRS.get())) { - std::vector<CoordinateOperationNNPtr> res2; - for (const auto &op : res) { - auto concatOp = - dynamic_cast<ConcatenatedOperation *>(op.get()); - if (concatOp && - dynamic_cast<const crs::GeographicCRS *>( - concatOp->operations() - .front() - ->targetCRS() - .get())) { - res2.emplace_back(op); - } - } - res = std::move(res2); - } - return res; + auto resFiltered = + FilterResults(res, context.context, context.extent1, + context.extent2, false) + .getRes(); +#ifdef TRACE_CREATE_OPERATIONS + logTrace("filtering reduced from " + + toString(static_cast<int>(res.size())) + " to " + + toString(static_cast<int>(resFiltered.size()))); +#endif + return resFiltered; } } } @@ -12065,6 +12129,7 @@ static bool hasIdentifiers(const CoordinateOperationNNPtr &op) { static std::vector<crs::CRSNNPtr> findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory, + const crs::GeodeticCRS *crs, const datum::GeodeticReferenceFrame *datum) { std::vector<crs::CRSNNPtr> candidates; assert(datum); @@ -12075,7 +12140,15 @@ findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory, const auto &authName = *(id->codeSpace()); const auto &code = id->code(); if (!authName.empty()) { - auto l_candidates = authFactory->createGeodeticCRSFromDatum( + const auto crsIds = crs->identifiers(); + const auto tmpFactory = + (crsIds.size() == 1 && + *(crsIds.front()->codeSpace()) == authName) + ? io::AuthorityFactory::create( + authFactory->databaseContext(), authName) + .as_nullable() + : authFactory; + auto l_candidates = tmpFactory->createGeodeticCRSFromDatum( authName, code, std::string()); for (const auto &candidate : l_candidates) { candidates.emplace_back(candidate); @@ -12093,7 +12166,7 @@ findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory, match.get(), util::IComparable::Criterion::EQUIVALENT) && !match->identifiers().empty()) { return findCandidateGeodCRSForDatum( - authFactory, + authFactory, crs, dynamic_cast<const datum::GeodeticReferenceFrame *>( match.get())); } @@ -12113,35 +12186,6 @@ static bool isNullTransformation(const std::string &name) { // --------------------------------------------------------------------------- -#ifdef DEBUG - -static int nCallLevel = 0; - -struct EnterDebugLevel { - EnterDebugLevel() { ++nCallLevel; } - ~EnterDebugLevel() { --nCallLevel; } -}; - -static void debugTrace(const std::string &str) { - for (int i = 1; i < nCallLevel; i++) - std::cerr << " "; - std::cerr << str << std::endl; -} - -static std::string objectAsStr(const common::IdentifiedObject *obj) { - std::string ret(obj->nameStr()); - const auto &ids = obj->identifiers(); - if (!ids.empty()) { - ret += " ("; - ret += (*ids[0]->codeSpace()) + ":" + ids[0]->code(); - ret += ")"; - } - return ret; -} -#endif - -// --------------------------------------------------------------------------- - void CoordinateOperationFactory::Private::setCRSs( CoordinateOperation *co, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { @@ -12178,11 +12222,10 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( const crs::CRSNNPtr &targetCRS, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, Private::Context &context) { -#ifdef DEBUG - EnterDebugLevel enterFunction; - debugTrace("createOperationsWithDatumPivot(" + - objectAsStr(sourceCRS.get()) + "," + - objectAsStr(targetCRS.get()) + ")"); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("createOperationsWithDatumPivot(" + + objectAsStr(sourceCRS.get()) + "," + + objectAsStr(targetCRS.get()) + ")"); #endif struct CreateOperationsWithDatumPivotAntiRecursion { @@ -12201,10 +12244,10 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( CreateOperationsWithDatumPivotAntiRecursion guard(context); const auto &authFactory = context.context->getAuthorityFactory(); - const auto candidatesSrcGeod( - findCandidateGeodCRSForDatum(authFactory, geodSrc->datum().get())); - const auto candidatesDstGeod( - findCandidateGeodCRSForDatum(authFactory, geodDst->datum().get())); + const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( + authFactory, geodSrc, geodSrc->datum().get())); + const auto candidatesDstGeod(findCandidateGeodCRSForDatum( + authFactory, geodDst, geodDst->datum().get())); auto createTransformations = [&](const crs::CRSNNPtr &candidateSrcGeod, const crs::CRSNNPtr &candidateDstGeod, @@ -12282,7 +12325,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( subOps.emplace_back(opSecondCloned); subOps.emplace_back(opsThird[0]); } -#ifdef DEBUG +#ifdef TRACE_CREATE_OPERATIONS std::string debugStr; for (const auto &op : subOps) { if (!debugStr.empty()) { @@ -12295,7 +12338,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( debugStr += objectAsStr(op->targetCRS().get()); debugStr += ")"; } - debugTrace("transformation " + debugStr); + logTrace("transformation " + debugStr); #endif res.emplace_back(ConcatenatedOperation::createComputeMetadata( subOps, !allowEmptyIntersection)); @@ -12308,13 +12351,11 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) { for (const auto &candidateDstGeod : candidatesDstGeod) { if (candidateDstGeod->nameStr() == targetCRS->nameStr()) { -#ifdef DEBUG - EnterDebugLevel loopLevel; - debugTrace("try " + objectAsStr(sourceCRS.get()) + "->" + - objectAsStr(candidateSrcGeod.get()) + "->" + - objectAsStr(candidateDstGeod.get()) + "->" + - objectAsStr(targetCRS.get()) + ")"); - EnterDebugLevel loopLevel2; +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(candidateSrcGeod.get()) + "->" + + objectAsStr(candidateDstGeod.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); #endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); @@ -12334,8 +12375,8 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( } for (const auto &candidateSrcGeod : candidatesSrcGeod) { -#ifdef DEBUG - EnterDebugLevel loopLevel; +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK(""); #endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); @@ -12343,13 +12384,11 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); for (const auto &candidateDstGeod : candidatesDstGeod) { -#ifdef DEBUG - EnterDebugLevel loopLevel2; - debugTrace("try " + objectAsStr(sourceCRS.get()) + "->" + - objectAsStr(candidateSrcGeod.get()) + "->" + - objectAsStr(candidateDstGeod.get()) + "->" + - objectAsStr(targetCRS.get()) + ")"); - EnterDebugLevel loopLevel3; +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(candidateSrcGeod.get()) + "->" + + objectAsStr(candidateDstGeod.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); #endif createTransformations(candidateSrcGeod, candidateDstGeod, opsFirst[0], isNullFirst); @@ -12408,11 +12447,10 @@ void CoordinateOperationFactory::Private::createOperationsThroughPreferredHub( } assert(!dstPreferredHubs.empty()); -#ifdef DEBUG - EnterDebugLevel enterFunction; - debugTrace("createOperationsThroughPreferredHub(" + - objectAsStr(sourceCRS.get()) + "," + - objectAsStr(targetCRS.get()) + ")"); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("createOperationsThroughPreferredHub(" + + objectAsStr(sourceCRS.get()) + " --> " + + objectAsStr(targetCRS.get()) + ")"); #endif struct AntiRecursionGuard { @@ -12432,7 +12470,7 @@ void CoordinateOperationFactory::Private::createOperationsThroughPreferredHub( std::vector<crs::CRSNNPtr> candidatesIntermCRS; for (const auto &datumHub : dstPreferredHubs) { auto candidates = - findCandidateGeodCRSForDatum(authFactory, datumHub.get()); + findCandidateGeodCRSForDatum(authFactory, geodDst, datumHub.get()); bool addedGeog2D = false; for (const auto &intermCRS : candidates) { auto geogCRS = dynamic_cast<crs::GeographicCRS *>(intermCRS.get()); @@ -12450,12 +12488,10 @@ void CoordinateOperationFactory::Private::createOperationsThroughPreferredHub( } for (const auto &intermCRS : candidatesIntermCRS) { -#ifdef DEBUG - EnterDebugLevel loopLevel; - debugTrace("try " + objectAsStr(sourceCRS.get()) + "->" + - objectAsStr(intermCRS.get()) + "->" + - objectAsStr(targetCRS.get()) + ")"); - EnterDebugLevel loopLevel2; +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(intermCRS.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); #endif const auto opsFirst = createOperations(sourceCRS, intermCRS, context); const auto opsLast = createOperations(intermCRS, targetCRS, context); @@ -12518,11 +12554,9 @@ CoordinateOperationFactory::Private::createOperations( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context) { -#ifdef DEBUG - EnterDebugLevel enterFunction; - auto debugStr("createOperations(" + objectAsStr(sourceCRS.get()) + "," + - objectAsStr(targetCRS.get()) + ")"); - debugTrace(debugStr); +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("createOperations(" + objectAsStr(sourceCRS.get()) + " --> " + + objectAsStr(targetCRS.get()) + ")"); #endif std::vector<CoordinateOperationNNPtr> res; @@ -12687,6 +12721,8 @@ void CoordinateOperationFactory::Private::createOperationsFromProj4Ext( const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + auto sourceProjExportable = dynamic_cast<const io::IPROJStringExportable *>( boundSrc ? boundSrc : sourceCRS.get()); auto targetProjExportable = dynamic_cast<const io::IPROJStringExportable *>( @@ -12737,79 +12773,75 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( const crs::VerticalCRS *vertDst, std::vector<CoordinateOperationNNPtr> &res) { - bool doFilterAndCheckPerfectOp = true; - res = findOpsInRegistryDirect(sourceCRS, targetCRS, context.context); - if (!sourceCRS->_isEquivalentTo(targetCRS.get())) { - auto resFromInverse = applyInverse( - findOpsInRegistryDirect(targetCRS, sourceCRS, context.context)); - res.insert(res.end(), resFromInverse.begin(), resFromInverse.end()); + ENTER_FUNCTION(); - // If we get at least a result with perfect accuracy, do not - // bother generating synthetic transforms. - if (hasPerfectAccuracyResult(res, context)) { - return true; - } + res = findOpsInRegistryDirect(sourceCRS, targetCRS, context); - doFilterAndCheckPerfectOp = false; - - bool sameGeodeticDatum = false; - - if (vertSrc || vertDst) { - createOperationsFromDatabaseWithVertCRS(sourceCRS, targetCRS, - context, geogSrc, geogDst, - vertSrc, vertDst, res); - } else if (geodSrc && geodDst) { - - const auto &srcDatum = geodSrc->datum(); - const auto &dstDatum = geodDst->datum(); - sameGeodeticDatum = - srcDatum != nullptr && dstDatum != nullptr && - srcDatum->_isEquivalentTo( - dstDatum.get(), util::IComparable::Criterion::EQUIVALENT); - - if (res.empty() && !sameGeodeticDatum && - !context.inCreateOperationsWithDatumPivotAntiRecursion && - srcDatum != nullptr && dstDatum != nullptr) { - // If we still didn't find a transformation, and that the source - // and target are GeodeticCRS, then go through their underlying - // datum to find potential transformations between other - // GeodeticRSs - // that are made of those datum - // The typical example is if transforming between two - // GeographicCRS, - // but transformations are only available between their - // corresponding geocentric CRS. - createOperationsWithDatumPivot(res, sourceCRS, targetCRS, - geodSrc, geodDst, context); - doFilterAndCheckPerfectOp = !res.empty(); - } - } - - // NAD27 to NAD83 has tens of results already. No need to look - // for a pivot - if (!sameGeodeticDatum && - ((res.empty() && - context.context->getAllowUseIntermediateCRS() == - CoordinateOperationContext::IntermediateCRSUse:: - IF_NO_DIRECT_TRANSFORMATION) || - context.context->getAllowUseIntermediateCRS() == - CoordinateOperationContext::IntermediateCRSUse::ALWAYS || - getenv("PROJ_FORCE_SEARCH_PIVOT"))) { - auto resWithIntermediate = findsOpsInRegistryWithIntermediate( - sourceCRS, targetCRS, context.context); - res.insert(res.end(), resWithIntermediate.begin(), - resWithIntermediate.end()); + // If we get at least a result with perfect accuracy, do not + // bother generating synthetic transforms. + if (hasPerfectAccuracyResult(res, context)) { + return true; + } + + bool doFilterAndCheckPerfectOp = false; + + bool sameGeodeticDatum = false; + + if (vertSrc || vertDst) { + createOperationsFromDatabaseWithVertCRS(sourceCRS, targetCRS, context, + geogSrc, geogDst, vertSrc, + vertDst, res); + } else if (geodSrc && geodDst) { + + const auto &srcDatum = geodSrc->datum(); + const auto &dstDatum = geodDst->datum(); + sameGeodeticDatum = + srcDatum != nullptr && dstDatum != nullptr && + srcDatum->_isEquivalentTo(dstDatum.get(), + util::IComparable::Criterion::EQUIVALENT); + + if (res.empty() && !sameGeodeticDatum && + !context.inCreateOperationsWithDatumPivotAntiRecursion && + srcDatum != nullptr && dstDatum != nullptr) { + // If we still didn't find a transformation, and that the source + // and target are GeodeticCRS, then go through their underlying + // datum to find potential transformations between other + // GeodeticRSs + // that are made of those datum + // The typical example is if transforming between two + // GeographicCRS, + // but transformations are only available between their + // corresponding geocentric CRS. + createOperationsWithDatumPivot(res, sourceCRS, targetCRS, geodSrc, + geodDst, context); doFilterAndCheckPerfectOp = !res.empty(); + } + } - // If transforming from/to WGS84 (Gxxxx), try through 'neutral' - // WGS84 - if (res.empty() && geodSrc && geodDst && - !context.inCreateOperationsThroughPreferredHub && - !context.inCreateOperationsWithDatumPivotAntiRecursion) { - createOperationsThroughPreferredHub(res, sourceCRS, targetCRS, - geodSrc, geodDst, context); - doFilterAndCheckPerfectOp = !res.empty(); - } + // NAD27 to NAD83 has tens of results already. No need to look + // for a pivot + if (!sameGeodeticDatum && + ((res.empty() && + context.context->getAllowUseIntermediateCRS() == + CoordinateOperationContext::IntermediateCRSUse:: + IF_NO_DIRECT_TRANSFORMATION) || + context.context->getAllowUseIntermediateCRS() == + CoordinateOperationContext::IntermediateCRSUse::ALWAYS || + getenv("PROJ_FORCE_SEARCH_PIVOT"))) { + auto resWithIntermediate = + findsOpsInRegistryWithIntermediate(sourceCRS, targetCRS, context); + res.insert(res.end(), resWithIntermediate.begin(), + resWithIntermediate.end()); + doFilterAndCheckPerfectOp = !res.empty(); + + // If transforming from/to WGS84 (Gxxxx), try through 'neutral' + // WGS84 + if (res.empty() && geodSrc && geodDst && + !context.inCreateOperationsThroughPreferredHub && + !context.inCreateOperationsWithDatumPivotAntiRecursion) { + createOperationsThroughPreferredHub(res, sourceCRS, targetCRS, + geodSrc, geodDst, context); + doFilterAndCheckPerfectOp = !res.empty(); } } @@ -12871,6 +12903,8 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private:: const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Private::Context &context) { + ENTER_FUNCTION(); + std::vector<CoordinateOperationNNPtr> res; struct AntiRecursionGuard { @@ -12923,6 +12957,8 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private:: const crs::CRSNNPtr &targetCRS, // vertical CRS Private::Context &context) { + ENTER_FUNCTION(); + std::vector<CoordinateOperationNNPtr> res; struct AntiRecursionGuard { @@ -12939,22 +12975,14 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private:: }; AntiRecursionGuard guard(context); - for (int i = 0; i < 2; i++) { - - // Generally EPSG has operations from GeogCrs to VertCRS - auto ops = - i == 0 ? findOpsInRegistryDirectTo(targetCRS, context.context) - : findOpsInRegistryDirectFrom(targetCRS, context.context); + // Generally EPSG has operations from GeogCrs to VertCRS + auto ops = findOpsInRegistryDirectTo(targetCRS, context); - for (const auto &op : ops) { - const auto tmpCRS = i == 0 ? op->sourceCRS() : op->targetCRS(); - if (tmpCRS && - dynamic_cast<const crs::GeographicCRS *>(tmpCRS.get())) { - res.emplace_back(i == 0 ? op : op->inverse()); - } + for (const auto &op : ops) { + const auto tmpCRS = op->sourceCRS(); + if (tmpCRS && dynamic_cast<const crs::GeographicCRS *>(tmpCRS.get())) { + res.emplace_back(op); } - if (!res.empty()) - break; } return res; @@ -12995,7 +13023,7 @@ void CoordinateOperationFactory::Private:: geogSrcIn->datum()) { const auto &authFactory = context.context->getAuthorityFactory(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( - authFactory, geogSrcIn->datum().get())); + authFactory, geogSrcIn, geogSrcIn->datum().get())); for (const auto &candidate : candidatesSrcGeod) { auto geogCandidate = util::nn_dynamic_pointer_cast<crs::GeographicCRS>( @@ -13003,12 +13031,7 @@ void CoordinateOperationFactory::Private:: if (geogCandidate && geogCandidate->coordinateSystem()->axisList().size() == 2) { res = findOpsInRegistryDirect(NN_NO_CHECK(geogCandidate), - targetCRSIn, context.context); - if (res.empty()) { - res = applyInverse(findOpsInRegistryDirect( - targetCRSIn, NN_NO_CHECK(geogCandidate), - context.context)); - } + targetCRSIn, context); break; } } @@ -13047,6 +13070,8 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( const crs::GeodeticCRS *geodDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + if (geodSrc->ellipsoid()->celestialBody() != geodDst->ellipsoid()->celestialBody()) { throw util::UnsupportedOperationException( @@ -13120,6 +13145,8 @@ void CoordinateOperationFactory::Private::createOperationsDerivedTo( Private::Context &context, const crs::DerivedCRS *derivedSrc, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + auto opFirst = derivedSrc->derivingConversion()->inverse(); // Small optimization if the targetCRS is the baseCRS of the source // derivedCRS. @@ -13147,6 +13174,8 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( const crs::GeographicCRS *geogDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + const auto &hubSrc = boundSrc->hubCRS(); auto hubSrcGeog = dynamic_cast<const crs::GeographicCRS *>(hubSrc.get()); auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS(); @@ -13339,6 +13368,8 @@ void CoordinateOperationFactory::Private::createOperationsBoundToVert( const crs::VerticalCRS *vertDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + auto baseSrcVert = dynamic_cast<const crs::VerticalCRS *>(boundSrc->baseCRS().get()); const auto &hubSrc = boundSrc->hubCRS(); @@ -13361,6 +13392,8 @@ void CoordinateOperationFactory::Private::createOperationsVertToVert( const crs::VerticalCRS *vertDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + const auto &srcDatum = vertSrc->datum(); const auto &dstDatum = vertDst->datum(); const bool equivalentVDatum = @@ -13415,6 +13448,8 @@ void CoordinateOperationFactory::Private::createOperationsVertToGeog( const crs::GeographicCRS *geogDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + if (vertSrc->identifiers().empty()) { const auto &vertSrcName = vertSrc->nameStr(); const auto &authFactory = context.context->getAuthorityFactory(); @@ -13466,6 +13501,8 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( Private::Context &context, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + const auto &hubSrc = boundSrc->hubCRS(); auto hubSrcGeog = dynamic_cast<const crs::GeographicCRS *>(hubSrc.get()); const auto &hubDst = boundDst->hubCRS(); @@ -13672,6 +13709,8 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( const crs::GeographicCRS *geogDst, std::vector<CoordinateOperationNNPtr> &res) { + ENTER_FUNCTION(); + const auto &authFactory = context.context->getAuthorityFactory(); const auto &componentsSrc = compoundSrc->componentReferenceSystems(); if (!componentsSrc.empty()) { @@ -13783,9 +13822,17 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( cacheHorizToInterpAndInterpToTarget; for (const auto &verticalTransform : verticalTransforms) { +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("Considering vertical transform " + + objectAsStr(verticalTransform.get())); +#endif crs::GeographicCRSPtr interpolationGeogCRS = getInterpolationGeogCRS(verticalTransform, dbContext); if (interpolationGeogCRS) { +#ifdef TRACE_CREATE_OPERATIONS + logTrace("Using " + objectAsStr(interpolationGeogCRS.get()) + + " as interpolation CRS"); +#endif std::vector<CoordinateOperationNNPtr> srcToInterpOps; std::vector<CoordinateOperationNNPtr> interpToTargetOps; @@ -13798,6 +13845,11 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( if (!key.empty()) { auto iter = cacheHorizToInterpAndInterpToTarget.find(key); if (iter == cacheHorizToInterpAndInterpToTarget.end()) { +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("looking for horizontal transformation " + "from source to interpCRS and interpCRS to " + "target"); +#endif srcToInterpOps = createOperations( componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS), context); @@ -13812,6 +13864,11 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( interpToTargetOps = iter->second.second; } } else { +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("looking for horizontal transformation " + "from source to interpCRS and interpCRS to " + "target"); +#endif srcToInterpOps = createOperations( componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS), context); @@ -13821,6 +13878,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( context); } +#ifdef TRACE_CREATE_OPERATIONS + ENTER_BLOCK("creating HorizVerticalHorizPROJBased operations"); +#endif for (const auto &srcToInterp : srcToInterpOps) { for (const auto &interpToTarget : interpToTargetOps) { @@ -14215,6 +14275,9 @@ CoordinateOperationFactory::createOperations( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const CoordinateOperationContextNNPtr &context) const { +#ifdef TRACE_CREATE_OPERATIONS + ENTER_FUNCTION(); +#endif // Look if we are called on CRS that have a link to a 'canonical' // BoundCRS // If so, use that one as input diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 44e44dbe..307c3a07 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -43,6 +43,7 @@ #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj/internal/lru_cache.hpp" +#include "proj/internal/tracing.hpp" #include <cmath> #include <cstdlib> @@ -186,6 +187,12 @@ struct DatabaseContext::Private { void cache(const std::string &code, const datum::GeodeticReferenceFrameNNPtr &datum); + datum::EllipsoidPtr + // cppcheck-suppress functionStatic + getEllipsoidFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const datum::EllipsoidNNPtr &ellipsoid); + datum::PrimeMeridianPtr // cppcheck-suppress functionStatic getPrimeMeridianFromCache(const std::string &code); @@ -244,6 +251,7 @@ struct DatabaseContext::Private { static constexpr size_t CACHE_SIZE = 128; LRUCacheOfObjects cacheUOM_{CACHE_SIZE}; LRUCacheOfObjects cacheCRS_{CACHE_SIZE}; + LRUCacheOfObjects cacheEllipsoid_{CACHE_SIZE}; LRUCacheOfObjects cacheGeodeticDatum_{CACHE_SIZE}; LRUCacheOfObjects cachePrimeMeridian_{CACHE_SIZE}; LRUCacheOfObjects cacheCS_{CACHE_SIZE}; @@ -252,6 +260,8 @@ struct DatabaseContext::Private { cacheCRSToCrsCoordOp_{CACHE_SIZE}; lru11::Cache<std::string, GridInfoCache> cacheGridInfo_{CACHE_SIZE}; + std::map<std::string, std::vector<std::string>> cacheAllowedAuthorities_{}; + static void insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj); @@ -408,6 +418,22 @@ void DatabaseContext::Private::cache( // --------------------------------------------------------------------------- +datum::EllipsoidPtr +DatabaseContext::Private::getEllipsoidFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheEllipsoid_, code, obj); + return std::static_pointer_cast<datum::Ellipsoid>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const datum::EllipsoidNNPtr &ellps) { + insertIntoCache(cacheEllipsoid_, code, ellps.as_nullable()); +} + +// --------------------------------------------------------------------------- + datum::PrimeMeridianPtr DatabaseContext::Private::getPrimeMeridianFromCache(const std::string &code) { util::BaseObjectPtr obj; @@ -830,6 +856,25 @@ SQLResultSet DatabaseContext::Private::run(const std::string &sql, nBindField++; } +#ifdef TRACE_DATABASE + size_t nPos = 0; + std::string sqlSubst(sql); + for (const auto ¶m : parameters) { + nPos = sqlSubst.find('?', nPos); + assert(nPos != std::string::npos); + std::string strValue; + if (param.type() == SQLValues::Type::STRING) { + strValue = '\'' + param.stringValue() + '\''; + } else { + strValue = toString(param.doubleValue()); + } + sqlSubst = + sqlSubst.substr(0, nPos) + strValue + sqlSubst.substr(nPos + 1); + nPos += strValue.size(); + } + logTrace(sqlSubst, "DATABASE"); +#endif + SQLResultSet result; const int column_count = sqlite3_column_count(stmt); while (true) { @@ -1118,32 +1163,42 @@ std::string DatabaseContext::getTextDefinition(const std::string &tableName, std::vector<std::string> DatabaseContext::getAllowedAuthorities( const std::string &sourceAuthName, const std::string &targetAuthName) const { - auto res = d->run( + + const auto key(sourceAuthName + targetAuthName); + auto hit = d->cacheAllowedAuthorities_.find(key); + if (hit != d->cacheAllowedAuthorities_.end()) { + return hit->second; + } + + auto sqlRes = 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( + if (sqlRes.empty()) { + sqlRes = 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( + if (sqlRes.empty()) { + sqlRes = 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( + if (sqlRes.empty()) { + sqlRes = d->run( "SELECT allowed_authorities FROM authority_to_authority_preference " "WHERE source_auth_name = 'any' AND target_auth_name = 'any'", {}); } - if (res.empty()) { + if (sqlRes.empty()) { + d->cacheAllowedAuthorities_[key] = std::vector<std::string>(); return std::vector<std::string>(); } - return split(res.front()[0], ','); + auto res = split(sqlRes.front()[0], ','); + d->cacheAllowedAuthorities_[key] = res; + return res; } // --------------------------------------------------------------------------- @@ -1209,8 +1264,8 @@ struct AuthorityFactory::Private { return AuthorityFactory::create(context_, auth_name); } - bool rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op, - bool discardIfMissingGrid); + bool + rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op); UnitOfMeasure createUnitOfMeasure(const std::string &auth_name, const std::string &code); @@ -1337,12 +1392,10 @@ util::PropertyMap AuthorityFactory::Private::createProperties( // --------------------------------------------------------------------------- bool AuthorityFactory::Private::rejectOpDueToMissingGrid( - const operation::CoordinateOperationNNPtr &op, bool discardIfMissingGrid) { - if (discardIfMissingGrid) { - for (const auto &gridDesc : op->gridsNeeded(context())) { - if (!gridDesc.available) { - return true; - } + const operation::CoordinateOperationNNPtr &op) { + for (const auto &gridDesc : op->gridsNeeded(context())) { + if (!gridDesc.available) { + return true; } } return false; @@ -1542,7 +1595,7 @@ AuthorityFactory::createExtent(const std::string &code) const { if (row[1].empty()) { auto extent = metadata::Extent::create( util::optional<std::string>(name), {}, {}, {}); - d->context()->d->cache(code, extent); + d->context()->d->cache(cacheKey, extent); return extent; } double south_lat = c_locale_stod(row[1]); @@ -1557,7 +1610,7 @@ AuthorityFactory::createExtent(const std::string &code) const { std::vector<metadata::GeographicExtentNNPtr>{bbox}, std::vector<metadata::VerticalExtentNNPtr>(), std::vector<metadata::TemporalExtentNNPtr>()); - d->context()->d->cache(code, extent); + d->context()->d->cache(cacheKey, extent); return extent; } catch (const std::exception &ex) { @@ -1755,6 +1808,13 @@ AuthorityFactory::identifyBodyFromSemiMajorAxis(double semi_major_axis, datum::EllipsoidNNPtr AuthorityFactory::createEllipsoid(const std::string &code) const { + const auto cacheKey(d->authority() + code); + { + auto ellps = d->context()->d->getEllipsoidFromCache(cacheKey); + if (ellps) { + return NN_NO_CHECK(ellps); + } + } auto res = d->runWithCodeParam( "SELECT ellipsoid.name, ellipsoid.semi_major_axis, " "ellipsoid.uom_auth_name, ellipsoid.uom_code, " @@ -1783,16 +1843,22 @@ AuthorityFactory::createEllipsoid(const std::string &code) const { auto uom = d->createUnitOfMeasure(uom_auth_name, uom_code); auto props = d->createProperties(code, name, deprecated, nullptr); if (!inv_flattening_str.empty()) { - return datum::Ellipsoid::createFlattenedSphere( + auto ellps = datum::Ellipsoid::createFlattenedSphere( props, common::Length(semi_major_axis, uom), common::Scale(c_locale_stod(inv_flattening_str)), body); + d->context()->d->cache(cacheKey, ellps); + return ellps; } else if (semi_major_axis_str == semi_minor_axis_str) { - return datum::Ellipsoid::createSphere( + auto ellps = datum::Ellipsoid::createSphere( props, common::Length(semi_major_axis, uom), body); + d->context()->d->cache(cacheKey, ellps); + return ellps; } else { - return datum::Ellipsoid::createTwoAxis( + auto ellps = datum::Ellipsoid::createTwoAxis( props, common::Length(semi_major_axis, uom), common::Length(c_locale_stod(semi_minor_axis_str), uom), body); + d->context()->d->cache(cacheKey, ellps); + return ellps; } } catch (const std::exception &ex) { throw buildFactoryException("ellipsoid", code, ex); @@ -2112,10 +2178,14 @@ crs::GeodeticCRSNNPtr AuthorityFactory::createGeodeticCRS(const std::string &code, bool geographicOnly) const { const auto cacheKey(d->authority() + code); - auto crs = std::dynamic_pointer_cast<crs::GeodeticCRS>( - d->context()->d->getCRSFromCache(cacheKey)); + auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { - return NN_NO_CHECK(crs); + auto geogCRS = std::dynamic_pointer_cast<crs::GeodeticCRS>(crs); + if (geogCRS) { + return NN_NO_CHECK(geogCRS); + } + throw NoSuchAuthorityCodeException("geodeticCRS not found", + d->authority(), code); } std::string sql("SELECT name, type, coordinate_system_auth_name, " "coordinate_system_code, datum_auth_name, datum_code, " @@ -2153,7 +2223,9 @@ AuthorityFactory::createGeodeticCRS(const std::string &code, pj_add_type_crs_if_needed(text_definition), d->context()); auto geodCRS = util::nn_dynamic_pointer_cast<crs::GeodeticCRS>(obj); if (geodCRS) { - return cloneWithProps(NN_NO_CHECK(geodCRS), props); + auto crsRet = cloneWithProps(NN_NO_CHECK(geodCRS), props); + d->context()->d->cache(cacheKey, crsRet); + return crsRet; } auto boundCRS = dynamic_cast<const crs::BoundCRS *>(obj.get()); @@ -2213,6 +2285,16 @@ AuthorityFactory::createGeodeticCRS(const std::string &code, crs::VerticalCRSNNPtr AuthorityFactory::createVerticalCRS(const std::string &code) const { + const auto cacheKey(d->authority() + code); + auto crs = d->context()->d->getCRSFromCache(cacheKey); + if (crs) { + auto projCRS = std::dynamic_pointer_cast<crs::VerticalCRS>(crs); + if (projCRS) { + return NN_NO_CHECK(projCRS); + } + throw NoSuchAuthorityCodeException("verticalCRS not found", + d->authority(), code); + } auto res = d->runWithCodeParam( "SELECT name, coordinate_system_auth_name, " "coordinate_system_code, datum_auth_name, datum_code, " @@ -2243,8 +2325,10 @@ AuthorityFactory::createVerticalCRS(const std::string &code) const { auto verticalCS = util::nn_dynamic_pointer_cast<cs::VerticalCS>(cs); if (verticalCS) { - return crs::VerticalCRS::create(props, datum, - NN_NO_CHECK(verticalCS)); + auto crsRet = + crs::VerticalCRS::create(props, datum, NN_NO_CHECK(verticalCS)); + d->context()->d->cache(cacheKey, crsRet); + return crsRet; } throw FactoryException("unsupported CS type for verticalCRS: " + cs->getWKT2Type(true)); @@ -2380,6 +2464,16 @@ AuthorityFactory::createConversion(const std::string &code) const { crs::ProjectedCRSNNPtr AuthorityFactory::createProjectedCRS(const std::string &code) const { + const auto cacheKey(d->authority() + code); + auto crs = d->context()->d->getCRSFromCache(cacheKey); + if (crs) { + auto projCRS = std::dynamic_pointer_cast<crs::ProjectedCRS>(crs); + if (projCRS) { + return NN_NO_CHECK(projCRS); + } + throw NoSuchAuthorityCodeException("projectedCRS not found", + d->authority(), code); + } auto res = d->runWithCodeParam( "SELECT name, coordinate_system_auth_name, " "coordinate_system_code, geodetic_crs_auth_name, geodetic_crs_code, " @@ -2422,9 +2516,11 @@ AuthorityFactory::createProjectedCRS(const std::string &code) const { common::IdentifiedObject::NAME_KEY, name), conv->method(), conv->parameterValues()) : conv; - return crs::ProjectedCRS::create(props, projCRS->baseCRS(), - newConv, - projCRS->coordinateSystem()); + auto crsRet = crs::ProjectedCRS::create( + props, projCRS->baseCRS(), newConv, + projCRS->coordinateSystem()); + d->context()->d->cache(cacheKey, crsRet); + return crsRet; } auto boundCRS = dynamic_cast<const crs::BoundCRS *>(obj.get()); @@ -2464,8 +2560,10 @@ AuthorityFactory::createProjectedCRS(const std::string &code) const { auto cartesianCS = util::nn_dynamic_pointer_cast<cs::CartesianCS>(cs); if (cartesianCS) { - return crs::ProjectedCRS::create(props, baseCRS, conv, - NN_NO_CHECK(cartesianCS)); + auto crsRet = crs::ProjectedCRS::create(props, baseCRS, conv, + NN_NO_CHECK(cartesianCS)); + d->context()->d->cache(cacheKey, crsRet); + return crsRet; } throw FactoryException("unsupported CS type for projectedCRS: " + cs->getWKT2Type(true)); @@ -3309,6 +3407,14 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( * missing grids should be removed from the result set. * @param discardSuperseded Whether cordinate operations that are superseded * (but not deprecated) should be removed from the result set. + * @param tryReverseOrder whether to search in the reverse order too (and thus + * inverse results found that way) + * @param reportOnlyIntersectingTransformations if intersectingExtent1 and + * intersectingExtent2 should be honored in a strict way. + * @param intersectingExtent1 Optional extent that the resulting operations + * must intersect. + * @param intersectingExtent2 Optional extent that the resulting operations + * must intersect. * @return list of coordinate operations * @throw NoSuchAuthorityCodeException * @throw FactoryException @@ -3319,7 +3425,10 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, - bool discardSuperseded) const { + bool discardSuperseded, bool tryReverseOrder, + bool reportOnlyIntersectingTransformations, + const metadata::ExtentPtr &intersectingExtent1, + const metadata::ExtentPtr &intersectingExtent2) const { auto cacheKey(d->authority()); cacheKey += sourceCRSAuthName.empty() ? "{empty}" : sourceCRSAuthName; @@ -3329,6 +3438,24 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( cacheKey += (usePROJAlternativeGridNames ? '1' : '0'); cacheKey += (discardIfMissingGrid ? '1' : '0'); cacheKey += (discardSuperseded ? '1' : '0'); + cacheKey += (tryReverseOrder ? '1' : '0'); + cacheKey += (reportOnlyIntersectingTransformations ? '1' : '0'); + for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { + if (extent) { + const auto &geogExtent = extent->geographicElements(); + if (geogExtent.size() == 1) { + auto bbox = + dynamic_cast<const metadata::GeographicBoundingBox *>( + geogExtent[0].get()); + if (bbox) { + cacheKey += toString(bbox->southBoundLatitude()); + cacheKey += toString(bbox->westBoundLongitude()); + cacheKey += toString(bbox->northBoundLatitude()); + cacheKey += toString(bbox->eastBoundLongitude()); + } + } + } + } std::vector<operation::CoordinateOperationNNPtr> list; @@ -3337,31 +3464,35 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( } if (!targetCRSAuthName.empty()) { - // Look-up first for conversion which is the most precise. - std::string sql("SELECT conversion_auth_name, " - "geodetic_crs_auth_name, geodetic_crs_code FROM " - "projected_crs WHERE auth_name = ? AND code = ?"); - auto params = ListOfParams{targetCRSAuthName, targetCRSCode}; - auto res = d->run(sql, params); - if (!res.empty()) { - const auto &row = res.front(); - bool ok = row[1] == sourceCRSAuthName && row[2] == sourceCRSCode; - if (ok && d->hasAuthorityRestriction()) { - ok = row[0] == d->authority(); - } - if (ok) { - auto targetCRS = d->createFactory(targetCRSAuthName) - ->createProjectedCRS(targetCRSCode); + auto targetFactory = d->createFactory(targetCRSAuthName); + try { + auto targetCRS = targetFactory->createProjectedCRS(targetCRSCode); + const auto &baseIds = targetCRS->baseCRS()->identifiers(); + if (sourceCRSAuthName.empty() || + (!baseIds.empty() && + *(baseIds.front()->codeSpace()) == sourceCRSAuthName && + baseIds.front()->code() == sourceCRSCode)) { + bool ok = true; auto conv = targetCRS->derivingConversion(); - list.emplace_back(conv); - d->context()->d->cache(cacheKey, list); - return list; + if (d->hasAuthorityRestriction()) { + ok = *(conv->identifiers().front()->codeSpace()) == + d->authority(); + } + if (ok) { + list.emplace_back(conv); + d->context()->d->cache(cacheKey, list); + return list; + } } + } catch (const std::exception &) { } } std::string sql; if (discardSuperseded) { - sql = "SELECT cov.auth_name, cov.code, cov.table_name, " + sql = "SELECT source_crs_auth_name, source_crs_code, " + "target_crs_auth_name, target_crs_code, " + "cov.auth_name, cov.code, cov.table_name, " + "area.south_lat, area.west_lon, area.north_lat, area.east_lon, " "ss.replacement_auth_name, ss.replacement_code FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " @@ -3373,22 +3504,65 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( "ss.superseded_table_name = ss.replacement_table_name " "WHERE "; } else { - sql = "SELECT cov.auth_name, cov.code, cov.table_name FROM " + sql = "SELECT source_crs_auth_name, source_crs_code, " + "target_crs_auth_name, target_crs_code, " + "cov.auth_name, cov.code, cov.table_name, " + "area.south_lat, area.west_lon, area.north_lat, area.east_lon " + "FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " "cov.area_of_use_code = area.code " "WHERE "; } ListOfParams params; - if (!sourceCRSAuthName.empty()) { - sql += "source_crs_auth_name = ? AND source_crs_code = ? AND "; - params.emplace_back(sourceCRSAuthName); - params.emplace_back(sourceCRSCode); - } - if (!targetCRSAuthName.empty()) { - sql += "target_crs_auth_name = ? AND target_crs_code = ? AND "; - params.emplace_back(targetCRSAuthName); - params.emplace_back(targetCRSCode); + if (!sourceCRSAuthName.empty() && !targetCRSAuthName.empty()) { + if (tryReverseOrder) { + sql += "((source_crs_auth_name = ? AND source_crs_code = ? AND " + "target_crs_auth_name = ? AND target_crs_code = ?) OR " + "(source_crs_auth_name = ? AND source_crs_code = ? AND " + "target_crs_auth_name = ? AND target_crs_code = ?)) AND "; + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + } else { + sql += "source_crs_auth_name = ? AND source_crs_code = ? AND " + "target_crs_auth_name = ? AND target_crs_code = ? AND "; + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + } + } else if (!sourceCRSAuthName.empty()) { + if (tryReverseOrder) { + sql += "((source_crs_auth_name = ? AND source_crs_code = ?) OR " + "(target_crs_auth_name = ? AND target_crs_code = ?)) AND "; + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + } else { + sql += "source_crs_auth_name = ? AND source_crs_code = ? AND "; + params.emplace_back(sourceCRSAuthName); + params.emplace_back(sourceCRSCode); + } + } else if (!targetCRSAuthName.empty()) { + if (tryReverseOrder) { + sql += "((source_crs_auth_name = ? AND source_crs_code = ?) OR " + "(target_crs_auth_name = ? AND target_crs_code = ?)) AND "; + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + } else { + sql += "target_crs_auth_name = ? AND target_crs_code = ? AND "; + params.emplace_back(targetCRSAuthName); + params.emplace_back(targetCRSCode); + } } sql += "cov.deprecated = 0"; if (d->hasAuthorityRestriction()) { @@ -3402,16 +3576,25 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( std::set<std::pair<std::string, std::string>> setTransf; if (discardSuperseded) { for (const auto &row : res) { - const auto &auth_name = row[0]; - const auto &code = row[1]; + const auto &auth_name = row[4]; + const auto &code = row[5]; setTransf.insert( std::pair<std::string, std::string>(auth_name, code)); } } + + // Do a pass to determine if there are transformations that intersect + // intersectingExtent1 & intersectingExtent2 + std::vector<bool> intersectingTransformations; + intersectingTransformations.resize(res.size()); + bool hasIntersectingTransformations = false; + size_t i = 0; for (const auto &row : res) { + size_t thisI = i; + ++i; if (discardSuperseded) { - const auto &replacement_auth_name = row[3]; - const auto &replacement_code = row[4]; + const auto &replacement_auth_name = row[11]; + const auto &replacement_code = row[12]; if (!replacement_auth_name.empty() && setTransf.find(std::pair<std::string, std::string>( replacement_auth_name, replacement_code)) != @@ -3422,12 +3605,77 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( } } - const auto &auth_name = row[0]; - const auto &code = row[1]; - const auto &table_name = row[2]; + bool intersecting = true; + try { + double south_lat = c_locale_stod(row[7]); + double west_lon = c_locale_stod(row[8]); + double north_lat = c_locale_stod(row[9]); + double east_lon = c_locale_stod(row[10]); + auto transf_extent = metadata::Extent::createFromBBOX( + west_lon, south_lat, east_lon, north_lat); + + for (const auto &extent : + {intersectingExtent1, intersectingExtent2}) { + if (extent) { + if (!transf_extent->intersects(NN_NO_CHECK(extent))) { + intersecting = false; + break; + } + } + } + } catch (const std::exception &) { + } + + intersectingTransformations[thisI] = intersecting; + if (intersecting) + hasIntersectingTransformations = true; + } + + // If there are intersecting transformations, then only report those ones + // If there are no intersecting transformations, report all of them + // This is for the "projinfo -s EPSG:32631 -t EPSG:2171" use case where we + // still want to be able to use the Pulkovo datum shift if EPSG:32631 + // coordinates are used + i = 0; + for (const auto &row : res) { + size_t thisI = i; + ++i; + if ((hasIntersectingTransformations || + reportOnlyIntersectingTransformations) && + !intersectingTransformations[thisI]) { + continue; + } + if (discardSuperseded) { + const auto &replacement_auth_name = row[11]; + const auto &replacement_code = row[12]; + if (!replacement_auth_name.empty() && + setTransf.find(std::pair<std::string, std::string>( + replacement_auth_name, replacement_code)) != + setTransf.end()) { + // Skip transformations that are superseded by others that got + // returned in the result set. + continue; + } + } + + const auto &source_crs_auth_name = row[0]; + const auto &source_crs_code = row[1]; + const auto &target_crs_auth_name = row[2]; + const auto &target_crs_code = row[3]; + const auto &auth_name = row[4]; + const auto &code = row[5]; + const auto &table_name = row[6]; auto op = d->createFactory(auth_name)->createCoordinateOperation( code, true, usePROJAlternativeGridNames, table_name); - if (!d->rejectOpDueToMissingGrid(op, discardIfMissingGrid)) { + if (tryReverseOrder && + (!sourceCRSAuthName.empty() + ? (source_crs_auth_name != sourceCRSAuthName || + source_crs_code != sourceCRSCode) + : (target_crs_auth_name != targetCRSAuthName || + target_crs_code != targetCRSCode))) { + op = op->inverse(); + } + if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) { list.emplace_back(op); } } @@ -3524,6 +3772,13 @@ static bool useIrrelevantPivot(const operation::CoordinateOperationNNPtr &op, * used as potential intermediate CRS. If the list is empty, the database will * be used to find common CRS in operations involving both the source and * target CRS. + * @param allowedIntermediateObjectType Restrict the type of the intermediate + * object considered. + * Only ObjectType::CRS and ObjectType::GEOGRAPHIC_CRS supported currently + * @param intersectingExtent1 Optional extent that the resulting operations + * must intersect. + * @param intersectingExtent2 Optional extent that the resulting operations + * must intersect. * @return list of coordinate operations * @throw NoSuchAuthorityCodeException * @throw FactoryException @@ -3536,7 +3791,10 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded, const std::vector<std::pair<std::string, std::string>> - &intermediateCRSAuthCodes) const { + &intermediateCRSAuthCodes, + ObjectType allowedIntermediateObjectType, + const metadata::ExtentPtr &intersectingExtent1, + const metadata::ExtentPtr &intersectingExtent2) const { std::vector<operation::CoordinateOperationNNPtr> listTmp; @@ -3617,18 +3875,55 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( joinArea + "WHERE v1.source_crs_auth_name = ? AND v1.source_crs_code = ? " "AND v2.target_crs_auth_name = ? AND v2.target_crs_code = ? "); + if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { + sql += "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " + "x.auth_name = v1.target_crs_auth_name AND " + "x.code = v1.target_crs_code AND " + "x.type IN ('geographic 2D', 'geographic 3D')) "; + } auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode}; - std::string additionalWhere( "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 "); + "south_lat2, west_lon2, north_lat2, east_lon2) = 1 "); if (d->hasAuthorityRestriction()) { additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? "; params.emplace_back(d->authority()); params.emplace_back(d->authority()); } + for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { + if (extent) { + const auto &geogExtent = extent->geographicElements(); + if (geogExtent.size() == 1) { + auto bbox = + dynamic_cast<const metadata::GeographicBoundingBox *>( + geogExtent[0].get()); + if (bbox) { + const double south_lat = bbox->southBoundLatitude(); + const double west_lon = bbox->westBoundLongitude(); + const double north_lat = bbox->northBoundLatitude(); + const double east_lon = bbox->eastBoundLongitude(); + if (south_lat != -90.0 || west_lon != -180.0 || + north_lat != 90.0 || east_lon != 180.0) { + additionalWhere += + "AND intersects_bbox(south_lat1, " + "west_lon1, north_lat1, east_lon1, ?, ?, ?, ?) AND " + "intersects_bbox(south_lat2, west_lon2, " + "north_lat2, east_lon2, ?, ?, ?, ?) "; + params.emplace_back(south_lat); + params.emplace_back(west_lon); + params.emplace_back(north_lat); + params.emplace_back(east_lon); + params.emplace_back(south_lat); + params.emplace_back(west_lon); + params.emplace_back(north_lat); + params.emplace_back(east_lon); + } + } + } + } + } std::string intermediateWhere = buildIntermediateWhere(intermediateCRSAuthCodes, "target", "source"); for (const auto &pair : intermediateCRSAuthCodes) { @@ -3721,6 +4016,12 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( joinArea + "WHERE v1.source_crs_auth_name = ? AND v1.source_crs_code = ? " "AND v2.source_crs_auth_name = ? AND v2.source_crs_code = ? "; + if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { + sql += "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " + "x.auth_name = v1.target_crs_auth_name AND " + "x.code = v1.target_crs_code AND " + "x.type IN ('geographic 2D', 'geographic 3D')) "; + } intermediateWhere = buildIntermediateWhere(intermediateCRSAuthCodes, "target", "target"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); @@ -3762,6 +4063,12 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( joinArea + "WHERE v1.target_crs_auth_name = ? AND v1.target_crs_code = ? " "AND v2.target_crs_auth_name = ? AND v2.target_crs_code = ? "; + if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { + sql += "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " + "x.auth_name = v1.source_crs_auth_name AND " + "x.code = v1.source_crs_code AND " + "x.type IN ('geographic 2D', 'geographic 3D')) "; + } intermediateWhere = buildIntermediateWhere(intermediateCRSAuthCodes, "source", "source"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); @@ -3803,6 +4110,12 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( joinArea + "WHERE v1.target_crs_auth_name = ? AND v1.target_crs_code = ? " "AND v2.source_crs_auth_name = ? AND v2.source_crs_code = ? "; + if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { + sql += "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " + "x.auth_name = v1.source_crs_auth_name AND " + "x.code = v1.source_crs_code AND " + "x.type IN ('geographic 2D', 'geographic 3D')) "; + } intermediateWhere = buildIntermediateWhere(intermediateCRSAuthCodes, "source", "target"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); @@ -3840,7 +4153,7 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( std::vector<operation::CoordinateOperationNNPtr> list; for (const auto &op : listTmp) { - if (!d->rejectOpDueToMissingGrid(op, discardIfMissingGrid)) { + if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) { list.emplace_back(op); } } @@ -4239,15 +4552,18 @@ AuthorityFactory::createObjectsFromName( } std::string sql( - "SELECT table_name, auth_name, code, name FROM object_view WHERE " - "deprecated = ? AND "); - ListOfParams params{deprecated ? 1.0 : 0.0}; + "SELECT table_name, auth_name, code, name, deprecated FROM object_view " + "WHERE "); + if (deprecated) { + sql += "deprecated = 1 AND "; + } + ListOfParams params; if (!approximateMatch) { sql += "name LIKE ? AND "; params.push_back(searchedNameWithoutDeprecated); } if (d->hasAuthorityRestriction()) { - sql += " auth_name = ? AND "; + sql += "auth_name = ? AND "; params.emplace_back(d->authority()); } @@ -4352,7 +4668,7 @@ AuthorityFactory::createObjectsFromName( sql += ')'; } } - sql += " ORDER BY length(name), name"; + sql += " ORDER BY deprecated, length(name), name"; if (limitResultCount > 0 && limitResultCount < static_cast<size_t>(std::numeric_limits<int>::max()) && @@ -4374,9 +4690,13 @@ AuthorityFactory::createObjectsFromName( auto sqlRes = d->run(sql, params); for (const auto &row : sqlRes) { const auto &name = row[3]; + const auto &deprecatedStr = row[4]; const auto canonicalizedName( metadata::Identifier::canonicalizeName(name)); - mapCanonicalizeGRFName[canonicalizedName].push_back(row); + auto &v = mapCanonicalizeGRFName[canonicalizedName]; + if (deprecatedStr == "0" || v.empty() || v.front()[4] == "1") { + v.push_back(row); + } } } auto iter = mapCanonicalizeGRFName.find(canonicalizedSearchedName); @@ -4424,6 +4744,8 @@ AuthorityFactory::createObjectsFromName( } } else { auto sqlRes = d->run(sql, params); + bool isFirst = true; + bool firstIsDeprecated = false; for (const auto &row : sqlRes) { const auto &name = row[3]; if (approximateMatch) { @@ -4443,6 +4765,14 @@ AuthorityFactory::createObjectsFromName( const auto &table_name = row[0]; const auto &auth_name = row[1]; const auto &code = row[2]; + const auto &deprecatedStr = row[4]; + if (isFirst) { + firstIsDeprecated = deprecatedStr == "1"; + isFirst = false; + } + if (deprecatedStr == "1" && !res.empty() && !firstIsDeprecated) { + break; + } auto factory = d->createFactory(auth_name); if (table_name == "prime_meridian") { res.emplace_back(factory->createPrimeMeridian(code)); @@ -4477,12 +4807,6 @@ AuthorityFactory::createObjectsFromName( } } - if (res.empty() && !deprecated) { - return createObjectsFromName(searchedName + " (deprecated)", - allowedObjectTypes, approximateMatch, - limitResultCount); - } - auto sortLambda = [](const common::IdentifiedObjectNNPtr &a, const common::IdentifiedObjectNNPtr &b) { const auto &aName = a->nameStr(); diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index b16032d2..c6a6e111 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -284,6 +284,7 @@ set(SRC_LIBPROJ_CORE wkt_parser.hpp zpoly1.cpp proj_json_streaming_writer.cpp + tracing.cpp ${CMAKE_CURRENT_BINARY_DIR}/proj_config.h ) diff --git a/src/tracing.cpp b/src/tracing.cpp new file mode 100644 index 00000000..4c7a8515 --- /dev/null +++ b/src/tracing.cpp @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * Project: PROJ + * Purpose: Tracing/profiling + * Author: Even Rouault <even dot rouault at spatialys dot com> + * + ****************************************************************************** + * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef ENABLE_TRACING + +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif + +#include <stdlib.h> + +#include "proj/internal/internal.hpp" +#include "proj/internal/tracing.hpp" + +//! @cond Doxygen_Suppress + +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <sys/timeb.h> +#else +#include <sys/time.h> /* for gettimeofday() */ +#define CPLTimeVal timeval +#define CPLGettimeofday(t, u) gettimeofday(t, u) +#endif + +NS_PROJ_START + +using namespace internal; + +namespace tracing { + +#if defined(_WIN32) && !defined(__CYGWIN__) +struct CPLTimeVal { + time_t tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +// --------------------------------------------------------------------------- + +static void CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/) { + struct _timeb theTime; + + _ftime(&theTime); + tp->tv_sec = static_cast<time_t>(theTime.time); + tp->tv_usec = theTime.millitm * 1000; +} +#endif + +struct Singleton { + FILE *f = nullptr; + int callLevel = 0; + int minDelayMicroSec = 10 * 1000; // 10 millisec + long long startTimeStamp = 0; + std::string componentsWhiteList{}; + std::string componentsBlackList{}; + + Singleton(); + ~Singleton(); + + Singleton(const Singleton &) = delete; + Singleton &operator=(const Singleton &) = delete; + + void logTraceRaw(const std::string &str); +}; + +// --------------------------------------------------------------------------- + +Singleton::Singleton() { + const char *traceFile = getenv("PROJ_TRACE_FILE"); + if (traceFile) + f = fopen(traceFile, "wb"); + if (!f) + f = stderr; + + const char *minDelay = getenv("PROJ_TRACE_MIN_DELAY"); + if (minDelay) { + minDelayMicroSec = atoi(minDelay); + } + + const char *whiteList = getenv("PROJ_TRACE_WHITE_LIST"); + if (whiteList) { + componentsWhiteList = whiteList; + } + + const char *blackList = getenv("PROJ_TRACE_BLACK_LIST"); + if (blackList) { + componentsBlackList = blackList; + } + + CPLTimeVal ts; + CPLGettimeofday(&ts, nullptr); + startTimeStamp = static_cast<long long>(ts.tv_sec) * 1000000 + ts.tv_usec; + + logTraceRaw("<log>"); + ++callLevel; +} + +// --------------------------------------------------------------------------- + +Singleton::~Singleton() { + --callLevel; + logTraceRaw("</log>"); + fflush(f); + + if (f != stderr) + fclose(f); +} + +// --------------------------------------------------------------------------- + +static Singleton &getSingleton() { + static Singleton singleton; + return singleton; +} + +// --------------------------------------------------------------------------- + +void Singleton::logTraceRaw(const std::string &str) { + CPLTimeVal ts; + CPLGettimeofday(&ts, nullptr); + const auto ts_usec = + static_cast<long long>(ts.tv_sec) * 1000000 + ts.tv_usec; + fprintf(f, "<!-- %03d.%06d --> ", + static_cast<int>((ts_usec - startTimeStamp) / 1000000), + static_cast<int>((ts_usec - startTimeStamp) % 1000000)); + for (int i = 0; i < callLevel; i++) + fprintf(f, " "); + fprintf(f, "%s\n", str.c_str()); + fflush(f); +} + +// --------------------------------------------------------------------------- + +void logTrace(const std::string &str, const std::string &component) { + auto &singleton = getSingleton(); + if (!singleton.componentsWhiteList.empty() && + (component.empty() || + singleton.componentsWhiteList.find(component) == std::string::npos)) { + return; + } + if (!singleton.componentsBlackList.empty() && !component.empty() && + singleton.componentsBlackList.find(component) != std::string::npos) { + return; + } + std::string rawStr("<trace"); + if (!component.empty()) { + rawStr += " component='" + component + '\''; + } + rawStr += '>'; + rawStr += str; + rawStr += "</trace>"; + singleton.logTraceRaw(rawStr); +} + +// --------------------------------------------------------------------------- + +struct EnterBlock::Private { + std::string msg_{}; + CPLTimeVal startTimeStamp_{}; +}; + +// --------------------------------------------------------------------------- + +EnterBlock::EnterBlock(const std::string &msg) : d(new Private()) { + auto &singleton = getSingleton(); + d->msg_ = msg; + CPLGettimeofday(&d->startTimeStamp_, nullptr); + singleton.logTraceRaw("<block_level_" + toString(singleton.callLevel) + + ">"); + ++singleton.callLevel; + singleton.logTraceRaw("<enter>" + d->msg_ + "</enter>"); +} + +// --------------------------------------------------------------------------- + +EnterBlock::~EnterBlock() { + auto &singleton = getSingleton(); + CPLTimeVal endTimeStamp; + CPLGettimeofday(&endTimeStamp, nullptr); + int delayMicroSec = static_cast<int>( + (endTimeStamp.tv_usec - d->startTimeStamp_.tv_usec) + + 1000000 * (endTimeStamp.tv_sec - d->startTimeStamp_.tv_sec)); + std::string lengthStr; + if (delayMicroSec >= singleton.minDelayMicroSec) { + lengthStr = " length='" + toString(delayMicroSec / 1000) + "." + + toString((delayMicroSec % 1000) / 100) + " msec'"; + } + singleton.logTraceRaw("<leave" + lengthStr + ">" + d->msg_ + "</leave>"); + --singleton.callLevel; + singleton.logTraceRaw("</block_level_" + toString(singleton.callLevel) + + ">"); +} + +} // namespace tracing + +NS_PROJ_END + +//! @endcond + +#endif // ENABLE_TRACING |
