aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/coordinateoperation.hpp4
-rw-r--r--src/apps/projinfo.cpp10
-rw-r--r--src/iso19111/c_api.cpp21
-rw-r--r--src/iso19111/coordinateoperation.cpp55
-rw-r--r--src/iso19111/factory.cpp3
-rw-r--r--src/proj.h5
-rw-r--r--test/unit/test_c_api.cpp57
-rw-r--r--test/unit/test_operation.cpp4
8 files changed, 133 insertions, 26 deletions
diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp
index c7459416..686a0c73 100644
--- a/include/proj/coordinateoperation.hpp
+++ b/include/proj/coordinateoperation.hpp
@@ -1759,6 +1759,10 @@ class PROJ_GCC_DLL CoordinateOperationContext {
PROJ_DLL void setDesiredAccuracy(double accuracy);
+ PROJ_DLL void setAllowBallparkTransformations(bool allow);
+
+ PROJ_DLL bool getAllowBallparkTransformations() const;
+
/** Specify how source and target CRS extent should be used to restrict
* candidate operations (only taken into account if no explicit area of
* interest is specified. */
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 10ce346f..85db14e2 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -705,6 +705,7 @@ static void outputOperations(
ctxt->setIntermediateCRS(pivots);
ctxt->setUsePROJAlternativeGridNames(usePROJGridAlternatives);
ctxt->setDiscardSuperseded(!showSuperseded);
+ ctxt->setAllowBallparkTransformations(outputOpt.ballparkAllowed);
list = CoordinateOperationFactory::create()->createOperations(
nnSourceCRS, nnTargetCRS, ctxt);
if (!spatialCriterionExplicitlySpecified &&
@@ -726,15 +727,6 @@ static void outputOperations(
<< std::endl;
std::exit(1);
}
- if (!outputOpt.ballparkAllowed) {
- std::vector<CoordinateOperationNNPtr> listNew;
- for (const auto &op : list) {
- if (!op->hasBallparkTransformation()) {
- listNew.emplace_back(op);
- }
- }
- list = std::move(listNew);
- }
if (outputOpt.quiet && !list.empty()) {
outputObject(dbContext, list[0], allowUseIntermediateCRS, outputOpt);
return;
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 5a3f0374..6b726f34 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -7518,6 +7518,27 @@ void PROJ_DLL proj_operation_factory_context_set_discard_superseded(
// ---------------------------------------------------------------------------
+/** \brief Set whether ballpark transformations are allowed.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param factory_ctx Operation factory context. must not be NULL
+ * @param allow set to TRUE to allow ballpark transformations.
+ * @since 7.1
+ */
+void PROJ_DLL proj_operation_factory_context_set_allow_ballpark_transformations(
+ PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int allow) {
+ SANITIZE_CTX(ctx);
+ assert(factory_ctx);
+ try {
+ factory_ctx->operationContext->setAllowBallparkTransformations(allow !=
+ 0);
+ } catch (const std::exception &e) {
+ proj_log_error(ctx, __FUNCTION__, e.what());
+ }
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
/** \brief Opaque object representing a set of operation results. */
struct PJ_OPERATION_LIST : PJ_OBJ_LIST {
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 822d09ac..8c47050d 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -10464,6 +10464,7 @@ struct CoordinateOperationContext::Private {
std::vector<std::pair<std::string, std::string>>
intermediateCRSAuthCodes_{};
bool discardSuperseded_ = true;
+ bool allowBallpark_ = true;
};
//! @endcond
@@ -10518,6 +10519,20 @@ void CoordinateOperationContext::setDesiredAccuracy(double accuracy) {
// ---------------------------------------------------------------------------
+/** \brief Return whether ballpark transformations are allowed */
+bool CoordinateOperationContext::getAllowBallparkTransformations() const {
+ return d->allowBallpark_;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Set whether ballpark transformations are allowed */
+void CoordinateOperationContext::setAllowBallparkTransformations(bool allow) {
+ d->allowBallpark_ = allow;
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Set how source and target CRS extent should be used
* when considering if a transformation can be used (only takes effect if
* no area of interest is explicitly defined).
@@ -11187,7 +11202,7 @@ struct FilterResults {
const CoordinateOperationContext::SourceTargetCRSExtentUse
sourceAndTargetCRSExtentUse;
- bool hasOpThatContainsAreaOfInterest = false;
+ bool hasOpThatContainsAreaOfInterestAndNoGrid = false;
std::vector<CoordinateOperationNNPtr> res{};
// ----------------------------------------------------------------------
@@ -11233,6 +11248,7 @@ struct FilterResults {
STRICT_CONTAINMENT
: context->getSpatialCriterion();
bool hasFoundOpWithExtent = false;
+ const bool allowBallpark = context->getAllowBallparkTransformations();
for (const auto &op : sourceList) {
if (desiredAccuracy != 0) {
const double accuracy = getAccuracy(op);
@@ -11240,6 +11256,9 @@ struct FilterResults {
continue;
}
}
+ if (!allowBallpark && op->hasBallparkTransformation()) {
+ continue;
+ }
if (areaOfInterest) {
bool emptyIntersection = false;
auto extent = getExtent(op, true, emptyIntersection);
@@ -11248,9 +11267,11 @@ struct FilterResults {
hasFoundOpWithExtent = true;
bool extentContains =
extent->contains(NN_NO_CHECK(areaOfInterest));
- if (extentContains) {
- if (!op->hasBallparkTransformation()) {
- hasOpThatContainsAreaOfInterest = true;
+ if (!hasOpThatContainsAreaOfInterestAndNoGrid &&
+ extentContains) {
+ if (!op->hasBallparkTransformation() &&
+ op->gridsNeeded(nullptr, true).empty()) {
+ hasOpThatContainsAreaOfInterestAndNoGrid = true;
}
}
if (spatialCriterion ==
@@ -11277,9 +11298,11 @@ struct FilterResults {
!extent1 || extent->contains(NN_NO_CHECK(extent1));
bool extentContainsExtent2 =
!extent2 || extent->contains(NN_NO_CHECK(extent2));
- if (extentContainsExtent1 && extentContainsExtent2) {
- if (!op->hasBallparkTransformation()) {
- hasOpThatContainsAreaOfInterest = true;
+ if (!hasOpThatContainsAreaOfInterestAndNoGrid &&
+ extentContainsExtent1 && extentContainsExtent2) {
+ if (!op->hasBallparkTransformation() &&
+ op->gridsNeeded(nullptr, true).empty()) {
+ hasOpThatContainsAreaOfInterestAndNoGrid = true;
}
}
if (spatialCriterion ==
@@ -11313,6 +11336,9 @@ struct FilterResults {
continue;
}
}
+ if (!allowBallpark && op->hasBallparkTransformation()) {
+ continue;
+ }
res.emplace_back(op);
}
}
@@ -11446,12 +11472,13 @@ struct FilterResults {
// If we have more than one result, and than the last result is the
// default "Ballpark geographic offset" or "Ballpark geocentric
- // translation"
- // operations we have synthetized, and that at least one operation
- // has the desired area of interest, remove it as
- // all previous results are necessarily better
- if (hasOpThatContainsAreaOfInterest && res.size() > 1) {
- const std::string &name = res.back()->nameStr();
+ // translation" operations we have synthetized, and that at least one
+ // operation has the desired area of interest and does not require the
+ // use of grids, remove it as all previous results are necessarily
+ // better
+ if (hasOpThatContainsAreaOfInterestAndNoGrid && res.size() > 1) {
+ const auto &opLast = res.back();
+ const std::string &name = opLast->nameStr();
if (name.find(BALLPARK_GEOGRAPHIC_OFFSET) != std::string::npos ||
name.find(NULL_GEOGRAPHIC_OFFSET) != std::string::npos ||
name.find(NULL_GEOCENTRIC_TRANSLATION) != std::string::npos ||
@@ -14772,7 +14799,7 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound(
}
}
- // If we didn't find a non-ballbark transformation between
+ // If we didn't find a non-ballpark transformation between
// the 2 vertical CRS, then try through intermediate geographic CRS
// For example
// WGS 84 + EGM96 --> ETRS89 + Belfast height where
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 32ed6abb..aa3cd6cf 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -926,7 +926,8 @@ bool DatabaseContext::lookForGridAlternative(const std::string &officialName,
bool &inverse) const {
auto res = d->run(
"SELECT proj_grid_name, proj_grid_format, inverse_direction FROM "
- "grid_alternatives WHERE original_grid_name = ?",
+ "grid_alternatives WHERE original_grid_name = ? AND "
+ "proj_grid_name <> ''",
{officialName});
if (res.empty()) {
return false;
diff --git a/src/proj.h b/src/proj.h
index 857fb862..167d4de2 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -1202,6 +1202,11 @@ void PROJ_DLL proj_operation_factory_context_set_discard_superseded(
PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
int discard);
+void PROJ_DLL proj_operation_factory_context_set_allow_ballpark_transformations(
+ PJ_CONTEXT *ctx,
+ PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
+ int allow);
+
/* ------------------------------------------------------------------------- */
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index da95e523..9e477ef3 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -1600,6 +1600,63 @@ TEST_F(CApi, proj_create_operations_with_pivot) {
// ---------------------------------------------------------------------------
+TEST_F(CApi, proj_create_operations_allow_ballpark_transformations) {
+ auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr);
+ ASSERT_NE(ctxt, nullptr);
+ ContextKeeper keeper_ctxt(ctxt);
+
+ auto source_crs = proj_create_from_database(
+ m_ctxt, "EPSG", "4267", PJ_CATEGORY_CRS, false, nullptr); // NAD27
+ ASSERT_NE(source_crs, nullptr);
+ ObjectKeeper keeper_source_crs(source_crs);
+
+ auto target_crs = proj_create_from_database(
+ m_ctxt, "EPSG", "4258", PJ_CATEGORY_CRS, false, nullptr); // ETRS89
+ ASSERT_NE(target_crs, nullptr);
+ ObjectKeeper keeper_target_crs(target_crs);
+
+ proj_operation_factory_context_set_spatial_criterion(
+ m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION);
+
+ proj_operation_factory_context_set_grid_availability_use(
+ m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED);
+
+ // Default: allowed implictly
+ {
+ auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt);
+ ASSERT_NE(res, nullptr);
+ ObjListKeeper keeper_res(res);
+
+ EXPECT_EQ(proj_list_get_count(res), 1);
+ }
+
+ // Allow explictly
+ {
+ proj_operation_factory_context_set_allow_ballpark_transformations(
+ m_ctxt, ctxt, true);
+
+ auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt);
+ ASSERT_NE(res, nullptr);
+ ObjListKeeper keeper_res(res);
+
+ EXPECT_EQ(proj_list_get_count(res), 1);
+ }
+
+ // Disallow
+ {
+ proj_operation_factory_context_set_allow_ballpark_transformations(
+ m_ctxt, ctxt, false);
+
+ auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt);
+ ASSERT_NE(res, nullptr);
+ ObjListKeeper keeper_res(res);
+
+ EXPECT_EQ(proj_list_get_count(res), 0);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
TEST_F(CApi, proj_context_set_database_path_null) {
EXPECT_TRUE(
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 99e05dd1..854f813c 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5109,7 +5109,7 @@ TEST(operation, geogCRS_to_geogCRS_init_IGNF_to_init_IGNF_context) {
auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
auto list = CoordinateOperationFactory::create()->createOperations(
NN_CHECK_ASSERT(sourceCRS), NN_CHECK_ASSERT(targetCRS), ctxt);
- ASSERT_EQ(list.size(), 1U);
+ ASSERT_EQ(list.size(), 2U);
EXPECT_EQ(list[0]->nameStr(),
"NOUVELLE TRIANGULATION DE LA FRANCE (NTF) vers RGF93 (ETRS89)");
@@ -6552,7 +6552,7 @@ TEST(operation, ETRS89_3D_to_proj_string_with_geoidgrids_nadgrids) {
auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
auto list = CoordinateOperationFactory::create()->createOperations(
src, NN_NO_CHECK(dst), ctxt);
- ASSERT_EQ(list.size(), 1U);
+ ASSERT_EQ(list.size(), 2U);
EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline "
"+step +proj=axisswap +order=2,1 "