aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-11-02 23:10:44 +0100
committerGitHub <noreply@github.com>2019-11-02 23:10:44 +0100
commit5ce02144c3802703da57d753ac7572099f0e4551 (patch)
tree577a97540cb0c62b0024d940d691406d5c161b18 /src
parentc64d3fcb3a60f27631b80b6c7eebb800315ac8eb (diff)
parentd9064c3de62982c461a36ed9997eab56d1cb9f85 (diff)
downloadPROJ-5ce02144c3802703da57d753ac7572099f0e4551.tar.gz
PROJ-5ce02144c3802703da57d753ac7572099f0e4551.zip
Merge pull request #1709 from rouault/improve_extent_filtering
Better filtering based on extent and performance improvements
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/iso19111/coordinateoperation.cpp896
-rw-r--r--src/iso19111/crs.cpp3
-rw-r--r--src/iso19111/factory.cpp498
-rw-r--r--src/lib_proj.cmake1
-rw-r--r--src/tracing.cpp224
6 files changed, 1164 insertions, 462 deletions
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 9e3306ba..de5d56ea 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 {
@@ -464,6 +484,31 @@ static const metadata::ExtentPtr &getExtent(const crs::CRSNNPtr &crs) {
return nullExtent;
}
+static const metadata::ExtentPtr
+getExtentPossiblySynthetized(const crs::CRSNNPtr &crs, bool &approxOut) {
+ const auto &rawExtent(getExtent(crs));
+ approxOut = false;
+ if (rawExtent)
+ return rawExtent;
+ const auto compoundCRS = dynamic_cast<const crs::CompoundCRS *>(crs.get());
+ if (compoundCRS) {
+ // For a compoundCRS, take the intersection of the extent of its
+ // components.
+ const auto &components = compoundCRS->componentReferenceSystems();
+ metadata::ExtentPtr extent;
+ approxOut = true;
+ for (const auto &component : components) {
+ const auto &componentExtent(getExtent(component));
+ if (extent && componentExtent)
+ extent = extent->intersection(NN_NO_CHECK(componentExtent));
+ else if (componentExtent)
+ extent = componentExtent;
+ }
+ return extent;
+ }
+ return rawExtent;
+}
+
// ---------------------------------------------------------------------------
static metadata::ExtentPtr
@@ -10341,26 +10386,28 @@ CoordinateOperationContextNNPtr CoordinateOperationContext::create(
struct CoordinateOperationFactory::Private {
struct Context {
- // This is the source CRS and target CRS of the initial
+ // This is the extent of the source CRS and target CRS of the initial
// CoordinateOperationFactory::createOperations() public call, not
// necessarily the ones of intermediate
// CoordinateOperationFactory::Private::createOperations() calls.
// This is used to compare transformations area of use against the
// area of use of the source & target CRS.
- const crs::CRSNNPtr &sourceCRS;
- const crs::CRSNNPtr &targetCRS;
+ const metadata::ExtentPtr &extent1;
+ const metadata::ExtentPtr &extent2;
const CoordinateOperationContextNNPtr &context;
bool inCreateOperationsWithDatumPivotAntiRecursion = false;
bool inCreateOperationsThroughPreferredHub = false;
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 crs::CRSNNPtr &sourceCRSIn,
- const crs::CRSNNPtr &targetCRSIn,
+ Context(const metadata::ExtentPtr &extent1In,
+ const metadata::ExtentPtr &extent2In,
const CoordinateOperationContextNNPtr &contextIn)
- : sourceCRS(sourceCRSIn), targetCRS(targetCRSIn),
- context(contextIn) {}
+ : extent1(extent1In), extent2(extent2In), context(contextIn) {}
};
static std::vector<CoordinateOperationNNPtr>
@@ -10370,6 +10417,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,
@@ -10715,13 +10780,11 @@ struct FilterResults {
FilterResults(const std::vector<CoordinateOperationNNPtr> &sourceListIn,
const CoordinateOperationContextNNPtr &contextIn,
- const crs::CRSNNPtr &sourceCRSIn,
- const crs::CRSNNPtr &targetCRSIn,
+ const metadata::ExtentPtr &extent1In,
+ const metadata::ExtentPtr &extent2In,
bool forceStrictContainmentTest)
- : sourceList(sourceListIn), context(contextIn), sourceCRS(sourceCRSIn),
- targetCRS(targetCRSIn), sourceCRSExtent(getExtent(sourceCRS)),
- targetCRSExtent(getExtent(targetCRS)),
- areaOfInterest(context->getAreaOfInterest()),
+ : sourceList(sourceListIn), context(contextIn), extent1(extent1In),
+ extent2(extent2In), areaOfInterest(context->getAreaOfInterest()),
desiredAccuracy(context->getDesiredAccuracy()),
sourceAndTargetCRSExtentUse(
context->getSourceAndTargetCRSExtentUse()) {
@@ -10753,10 +10816,8 @@ struct FilterResults {
private:
const std::vector<CoordinateOperationNNPtr> &sourceList;
const CoordinateOperationContextNNPtr &context;
- const crs::CRSNNPtr &sourceCRS;
- const crs::CRSNNPtr &targetCRS;
- const metadata::ExtentPtr &sourceCRSExtent;
- const metadata::ExtentPtr &targetCRSExtent;
+ const metadata::ExtentPtr &extent1;
+ const metadata::ExtentPtr &extent2;
metadata::ExtentPtr areaOfInterest;
const double desiredAccuracy = context->getDesiredAccuracy();
const CoordinateOperationContext::SourceTargetCRSExtentUse
@@ -10774,24 +10835,23 @@ struct FilterResults {
if (sourceAndTargetCRSExtentUse ==
CoordinateOperationContext::SourceTargetCRSExtentUse::
INTERSECTION) {
- if (sourceCRSExtent && targetCRSExtent) {
- areaOfInterest = sourceCRSExtent->intersection(
- NN_NO_CHECK(targetCRSExtent));
+ if (extent1 && extent2) {
+ areaOfInterest =
+ extent1->intersection(NN_NO_CHECK(extent2));
}
} else if (sourceAndTargetCRSExtentUse ==
CoordinateOperationContext::SourceTargetCRSExtentUse::
SMALLEST) {
- if (sourceCRSExtent && targetCRSExtent) {
- if (getPseudoArea(sourceCRSExtent) <
- getPseudoArea(targetCRSExtent)) {
- areaOfInterest = sourceCRSExtent;
+ if (extent1 && extent2) {
+ if (getPseudoArea(extent1) < getPseudoArea(extent2)) {
+ areaOfInterest = extent1;
} else {
- areaOfInterest = targetCRSExtent;
+ areaOfInterest = extent2;
}
- } else if (sourceCRSExtent) {
- areaOfInterest = sourceCRSExtent;
+ } else if (extent1) {
+ areaOfInterest = extent1;
} else {
- areaOfInterest = targetCRSExtent;
+ areaOfInterest = extent2;
}
}
}
@@ -10849,13 +10909,11 @@ struct FilterResults {
if (!extent)
continue;
hasFoundOpWithExtent = true;
- bool extentContainsSource =
- !sourceCRSExtent ||
- extent->contains(NN_NO_CHECK(sourceCRSExtent));
- bool extentContainsTarget =
- !targetCRSExtent ||
- extent->contains(NN_NO_CHECK(targetCRSExtent));
- if (extentContainsSource && extentContainsTarget) {
+ bool extentContainsExtent1 =
+ !extent1 || extent->contains(NN_NO_CHECK(extent1));
+ bool extentContainsExtent2 =
+ !extent2 || extent->contains(NN_NO_CHECK(extent2));
+ if (extentContainsExtent1 && extentContainsExtent2) {
if (!op->hasBallparkTransformation()) {
hasOpThatContainsAreaOfInterest = true;
}
@@ -10863,19 +10921,17 @@ struct FilterResults {
if (spatialCriterion ==
CoordinateOperationContext::SpatialCriterion::
STRICT_CONTAINMENT) {
- if (!extentContainsSource || !extentContainsTarget) {
+ if (!extentContainsExtent1 || !extentContainsExtent2) {
continue;
}
} else if (spatialCriterion ==
CoordinateOperationContext::SpatialCriterion::
PARTIAL_INTERSECTION) {
- bool extentIntersectsSource =
- !sourceCRSExtent ||
- extent->intersects(NN_NO_CHECK(sourceCRSExtent));
- bool extentIntersectsTarget =
- targetCRSExtent &&
- extent->intersects(NN_NO_CHECK(targetCRSExtent));
- if (!extentIntersectsSource || !extentIntersectsTarget) {
+ bool extentIntersectsExtent1 =
+ !extent1 || extent->intersects(NN_NO_CHECK(extent1));
+ bool extentIntersectsExtent2 =
+ extent2 && extent->intersects(NN_NO_CHECK(extent2));
+ if (!extentIntersectsExtent1 || !extentIntersectsExtent2) {
continue;
}
}
@@ -10914,21 +10970,19 @@ struct FilterResults {
if (areaOfInterest) {
area = getPseudoArea(
extentOp->intersection(NN_NO_CHECK(areaOfInterest)));
- } else if (sourceCRSExtent && targetCRSExtent) {
- auto x =
- extentOp->intersection(NN_NO_CHECK(sourceCRSExtent));
- auto y =
- extentOp->intersection(NN_NO_CHECK(targetCRSExtent));
+ } else if (extent1 && extent2) {
+ auto x = extentOp->intersection(NN_NO_CHECK(extent1));
+ auto y = extentOp->intersection(NN_NO_CHECK(extent2));
area = getPseudoArea(x) + getPseudoArea(y) -
((x && y)
? getPseudoArea(x->intersection(NN_NO_CHECK(y)))
: 0.0);
- } else if (sourceCRSExtent) {
+ } else if (extent1) {
area = getPseudoArea(
- extentOp->intersection(NN_NO_CHECK(sourceCRSExtent)));
- } else if (targetCRSExtent) {
+ extentOp->intersection(NN_NO_CHECK(extent1)));
+ } else if (extent2) {
area = getPseudoArea(
- extentOp->intersection(NN_NO_CHECK(targetCRSExtent)));
+ extentOp->intersection(NN_NO_CHECK(extent2)));
} else {
area = getPseudoArea(extentOp);
}
@@ -11224,10 +11278,22 @@ struct FilterResults {
static std::vector<CoordinateOperationNNPtr>
filterAndSort(const std::vector<CoordinateOperationNNPtr> &sourceList,
const CoordinateOperationContextNNPtr &context,
- const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) {
- return FilterResults(sourceList, context, sourceCRS, targetCRS, false)
- .andSort()
- .getRes();
+ const metadata::ExtentPtr &extent1,
+ const metadata::ExtentPtr &extent2) {
+#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
@@ -11249,13 +11315,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();
@@ -11264,24 +11326,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 &&
@@ -11293,8 +11371,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;
}
}
}
@@ -11325,13 +11404,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);
@@ -11353,13 +11437,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;
}
}
}
@@ -11369,48 +11463,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;
@@ -11428,13 +11490,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;
}
}
}
@@ -11449,11 +11521,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;
@@ -11475,41 +11554,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;
}
}
}
@@ -12058,6 +12136,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);
@@ -12068,7 +12147,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);
@@ -12086,7 +12173,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()));
}
@@ -12106,35 +12193,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) {
@@ -12171,11 +12229,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 {
@@ -12194,10 +12251,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,
@@ -12275,7 +12332,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()) {
@@ -12288,7 +12345,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));
@@ -12301,13 +12358,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);
@@ -12327,8 +12382,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);
@@ -12336,13 +12391,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);
@@ -12401,11 +12454,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 {
@@ -12425,7 +12477,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());
@@ -12443,12 +12495,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);
@@ -12492,8 +12542,8 @@ createBallparkGeocentricTranslation(const crs::CRSNNPtr &sourceCRS,
bool CoordinateOperationFactory::Private::hasPerfectAccuracyResult(
const std::vector<CoordinateOperationNNPtr> &res, const Context &context) {
- auto resTmp = FilterResults(res, context.context, context.sourceCRS,
- context.targetCRS, true)
+ auto resTmp = FilterResults(res, context.context, context.extent1,
+ context.extent2, true)
.getRes();
for (const auto &op : resTmp) {
const double acc = getAccuracy(op);
@@ -12511,11 +12561,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;
@@ -12680,6 +12728,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 *>(
@@ -12730,79 +12780,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();
}
}
@@ -12864,6 +12910,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 {
@@ -12916,6 +12964,8 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private::
const crs::CRSNNPtr &targetCRS, // vertical CRS
Private::Context &context) {
+ ENTER_FUNCTION();
+
std::vector<CoordinateOperationNNPtr> res;
struct AntiRecursionGuard {
@@ -12932,22 +12982,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;
@@ -12988,7 +13030,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>(
@@ -12996,12 +13038,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;
}
}
@@ -13040,6 +13077,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(
@@ -13113,6 +13152,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.
@@ -13140,6 +13181,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();
@@ -13332,6 +13375,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();
@@ -13354,6 +13399,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 =
@@ -13408,6 +13455,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();
@@ -13459,6 +13508,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();
@@ -13665,6 +13716,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()) {
@@ -13776,9 +13829,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;
@@ -13791,6 +13852,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);
@@ -13805,6 +13871,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);
@@ -13814,6 +13885,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) {
@@ -14051,57 +14125,129 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound(
static crs::CRSNNPtr
getResolvedCRS(const crs::CRSNNPtr &crs,
- const CoordinateOperationContextNNPtr &context) {
+ const CoordinateOperationContextNNPtr &context,
+ metadata::ExtentPtr &extentOut) {
const auto &authFactory = context->getAuthorityFactory();
+ const auto &ids = crs->identifiers();
+ const auto &name = crs->nameStr();
+
+ bool approxExtent;
+ extentOut = getExtentPossiblySynthetized(crs, approxExtent);
+
+ // We try to "identify" the provided CRS with the ones of the database,
+ // but in a more restricted way that what identify() does.
+ // If we get a match from id in priority, and from name as a fallback, and
+ // that they are equivalent to the input CRS, then use the identified CRS.
+ // Even if they aren't equivalent, we update extentOut with the one of the
+ // identified CRS if our input one is absent/not reliable.
+
+ const auto tryToIdentifyByName = [&crs, &name, &authFactory, approxExtent,
+ &extentOut](
+ io::AuthorityFactory::ObjectType objectType) {
+ if (name != "unknown" && name != "unnamed") {
+ auto matches = authFactory->createObjectsFromName(
+ name, {objectType}, false, 2);
+ if (matches.size() == 1) {
+ const auto match =
+ util::nn_static_pointer_cast<crs::CRS>(matches.front());
+ if (approxExtent || !extentOut) {
+ extentOut = getExtent(match);
+ }
+ if (match->isEquivalentTo(
+ crs.get(), util::IComparable::Criterion::EQUIVALENT)) {
+ return match;
+ }
+ }
+ }
+ return crs;
+ };
+
+ auto geogCRS = dynamic_cast<crs::GeographicCRS *>(crs.get());
+ if (geogCRS && authFactory) {
+ if (!ids.empty()) {
+ const auto tmpAuthFactory = io::AuthorityFactory::create(
+ authFactory->databaseContext(), *ids.front()->codeSpace());
+ try {
+ auto resolvedCrs(
+ tmpAuthFactory->createGeographicCRS(ids.front()->code()));
+ if (approxExtent || !extentOut) {
+ extentOut = getExtent(resolvedCrs);
+ }
+ if (resolvedCrs->isEquivalentTo(
+ crs.get(), util::IComparable::Criterion::EQUIVALENT)) {
+ return util::nn_static_pointer_cast<crs::CRS>(resolvedCrs);
+ }
+ } catch (const std::exception &) {
+ }
+ } else {
+ return tryToIdentifyByName(
+ geogCRS->coordinateSystem()->axisList().size() == 2
+ ? io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS
+ : io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS);
+ }
+ }
auto projectedCrs = dynamic_cast<crs::ProjectedCRS *>(crs.get());
if (projectedCrs && authFactory) {
- const auto &ids = projectedCrs->identifiers();
- if (!ids.empty() && projectedCrs->baseCRS()->identifiers().empty()) {
+ if (!ids.empty()) {
const auto tmpAuthFactory = io::AuthorityFactory::create(
authFactory->databaseContext(), *ids.front()->codeSpace());
try {
auto resolvedCrs(
tmpAuthFactory->createProjectedCRS(ids.front()->code()));
+ if (approxExtent || !extentOut) {
+ extentOut = getExtent(resolvedCrs);
+ }
if (resolvedCrs->isEquivalentTo(
crs.get(), util::IComparable::Criterion::EQUIVALENT)) {
return util::nn_static_pointer_cast<crs::CRS>(resolvedCrs);
}
} catch (const std::exception &) {
}
+ } else {
+ return tryToIdentifyByName(
+ io::AuthorityFactory::ObjectType::PROJECTED_CRS);
}
}
auto compoundCrs = dynamic_cast<crs::CompoundCRS *>(crs.get());
- // If we get a CompoundCRS that has an EPSG code, but whose component CRS
- // lack one, typically from WKT2, this might be an issue to get proper
- // results in createOperations(), so import the CompoundCRS from the
- // registry, and if equivalent to the original one, then use the version
- // from the registry.
if (compoundCrs && authFactory) {
- const auto &ids = compoundCrs->identifiers();
if (!ids.empty()) {
- const auto &components = compoundCrs->componentReferenceSystems();
- bool hasMissingId = false;
- for (const auto &comp : components) {
- if (comp->identifiers().empty()) {
- hasMissingId = true;
- break;
+ const auto tmpAuthFactory = io::AuthorityFactory::create(
+ authFactory->databaseContext(), *ids.front()->codeSpace());
+ try {
+ auto resolvedCrs(
+ tmpAuthFactory->createCompoundCRS(ids.front()->code()));
+ if (approxExtent || !extentOut) {
+ extentOut = getExtent(resolvedCrs);
+ }
+ if (resolvedCrs->isEquivalentTo(
+ crs.get(), util::IComparable::Criterion::EQUIVALENT)) {
+ return util::nn_static_pointer_cast<crs::CRS>(resolvedCrs);
}
+ } catch (const std::exception &) {
}
- if (hasMissingId) {
- const auto tmpAuthFactory = io::AuthorityFactory::create(
- authFactory->databaseContext(), *ids.front()->codeSpace());
- try {
- auto resolvedCrs(
- tmpAuthFactory->createCompoundCRS(ids.front()->code()));
- if (resolvedCrs->isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
- return util::nn_static_pointer_cast<crs::CRS>(
- resolvedCrs);
- }
- } catch (const std::exception &) {
+ } else {
+ auto outCrs = tryToIdentifyByName(
+ io::AuthorityFactory::ObjectType::COMPOUND_CRS);
+ if (outCrs.get() != crs.get()) {
+ return outCrs;
+ }
+ if (approxExtent || !extentOut) {
+ // If we still did not get a reliable extent, then try to
+ // resolve the components of the compoundCRS, and take the
+ // intersection of their extent.
+ extentOut = metadata::ExtentPtr();
+ const auto &components =
+ compoundCrs->componentReferenceSystems();
+ for (const auto &component : components) {
+ metadata::ExtentPtr componentExtent;
+ getResolvedCRS(component, context, componentExtent);
+ if (extentOut && componentExtent)
+ extentOut = extentOut->intersection(
+ NN_NO_CHECK(componentExtent));
+ else if (componentExtent)
+ extentOut = componentExtent;
}
}
}
@@ -14136,6 +14282,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
@@ -14144,15 +14293,16 @@ CoordinateOperationFactory::createOperations(
auto l_sourceCRS = srcBoundCRS ? NN_NO_CHECK(srcBoundCRS) : sourceCRS;
auto l_targetCRS = targetBoundCRS ? NN_NO_CHECK(targetBoundCRS) : targetCRS;
- auto l_resolvedSourceCRS = getResolvedCRS(l_sourceCRS, context);
- auto l_resolvedTargetCRS = getResolvedCRS(l_targetCRS, context);
- Private::Context contextPrivate(l_resolvedSourceCRS, l_resolvedTargetCRS,
- context);
+ metadata::ExtentPtr sourceCRSExtent;
+ auto l_resolvedSourceCRS =
+ getResolvedCRS(l_sourceCRS, context, sourceCRSExtent);
+ metadata::ExtentPtr targetCRSExtent;
+ auto l_resolvedTargetCRS =
+ getResolvedCRS(l_targetCRS, context, targetCRSExtent);
+ Private::Context contextPrivate(sourceCRSExtent, targetCRSExtent, context);
if (context->getSourceAndTargetCRSExtentUse() ==
CoordinateOperationContext::SourceTargetCRSExtentUse::INTERSECTION) {
- auto sourceCRSExtent(getExtent(l_resolvedSourceCRS));
- auto targetCRSExtent(getExtent(l_resolvedTargetCRS));
if (sourceCRSExtent && targetCRSExtent &&
!sourceCRSExtent->intersects(NN_NO_CHECK(targetCRSExtent))) {
return std::vector<CoordinateOperationNNPtr>();
@@ -14162,7 +14312,7 @@ CoordinateOperationFactory::createOperations(
return filterAndSort(Private::createOperations(l_resolvedSourceCRS,
l_resolvedTargetCRS,
contextPrivate),
- context, l_resolvedSourceCRS, l_resolvedTargetCRS);
+ context, sourceCRSExtent, targetCRSExtent);
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index f16ed7cf..26725e24 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -993,7 +993,8 @@ bool SingleCRS::baseIsEquivalentTo(
// TODO test DatumEnsemble
return d->coordinateSystem->_isEquivalentTo(
- otherSingleCRS->d->coordinateSystem.get(), criterion);
+ otherSingleCRS->d->coordinateSystem.get(), criterion) &&
+ getExtensionProj4() == otherSingleCRS->getExtensionProj4();
}
// ---------------------------------------------------------------------------
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 &param : 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