aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-02-20 18:04:35 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-02-20 20:32:17 +0100
commit94578ea8ff38f4bc6b1f6f52b80ecf7359f5dfc2 (patch)
tree1ef7f51339a7dc8c2e1040ddbf4d13aca6cc879a
parent374cc258510428fa3bfb9d8ca61ad7ac83a00db1 (diff)
downloadPROJ-94578ea8ff38f4bc6b1f6f52b80ecf7359f5dfc2.tar.gz
PROJ-94578ea8ff38f4bc6b1f6f52b80ecf7359f5dfc2.zip
CoordinateOperation: add a hasBallparkTransformation() method that can be used to know if it includes a very approximative transformation term
-rw-r--r--include/proj/coordinateoperation.hpp3
-rw-r--r--include/proj/internal/coordinateoperation_internal.hpp3
-rw-r--r--src/4D_api.cpp4
-rw-r--r--src/apps/projinfo.cpp4
-rw-r--r--src/iso19111/c_api.cpp30
-rw-r--r--src/iso19111/coordinateoperation.cpp174
-rw-r--r--src/iso19111/crs.cpp6
-rw-r--r--src/proj.h3
-rw-r--r--test/cli/testprojinfo_out.dist10
-rw-r--r--test/unit/test_c_api.cpp6
-rw-r--r--test/unit/test_operation.cpp34
11 files changed, 186 insertions, 91 deletions
diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp
index 2f7c9bbf..a0883cae 100644
--- a/include/proj/coordinateoperation.hpp
+++ b/include/proj/coordinateoperation.hpp
@@ -148,6 +148,8 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage,
PROJ_DLL bool
isPROJInstanciable(const io::DatabaseContextPtr &databaseContext) const;
+ PROJ_DLL bool hasBallparkTransformation() const;
+
protected:
PROJ_INTERNAL CoordinateOperation();
PROJ_INTERNAL CoordinateOperation(const CoordinateOperation &other);
@@ -167,6 +169,7 @@ class PROJ_GCC_DLL CoordinateOperation : public common::ObjectUsage,
PROJ_INTERNAL
void setAccuracies(
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies);
+ PROJ_INTERNAL void setHasBallparkTransformation(bool b);
private:
PROJ_OPAQUE_PRIVATE_DATA
diff --git a/include/proj/internal/coordinateoperation_internal.hpp b/include/proj/internal/coordinateoperation_internal.hpp
index 8428b8bf..65b786b2 100644
--- a/include/proj/internal/coordinateoperation_internal.hpp
+++ b/include/proj/internal/coordinateoperation_internal.hpp
@@ -246,7 +246,8 @@ class PROJBasedOperation : public SingleOperation {
create(const util::PropertyMap &properties,
const io::IPROJStringExportableNNPtr &projExportable, bool inverse,
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
- const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies);
+ const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies,
+ bool hasRoughTransformation);
std::set<GridDescription>
gridsNeeded(const io::DatabaseContextPtr &databaseContext) const override;
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 1b3374f3..4f13f238 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -1102,8 +1102,8 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char
double north_lat = 0.0;
const char* name = proj_get_name(op);
- if( name && (strstr(name, "Null geographic offset") ||
- strstr(name, "Null geocentric translation")) )
+ if( name && (strstr(name, "Ballpark geographic offset") ||
+ strstr(name, "Ballpark geocentric translation")) )
{
// Skip default transformations
}
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 9472d99e..55a8d622 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -508,6 +508,10 @@ static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
std::cout << "unknown domain of validity";
}
+ if (op->hasBallparkTransformation()) {
+ std::cout << ", has ballpark transformation";
+ }
+
std::cout << std::endl;
}
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index b3f200fe..e731615c 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -5754,6 +5754,36 @@ int proj_coordoperation_is_instanciable(PJ_CONTEXT *ctx,
// ---------------------------------------------------------------------------
+/** \brief Return whether a coordinate operation has a "ballpark"
+ * transformation,
+ * that is a very approximate one, due to lack of more accurate transformations.
+ *
+ * Typically a null geographic offset between two horizontal datum, or a
+ * null vertical offset (or limited to unit changes) between two vertical
+ * datum. Errors of several tens to one hundred meters might be expected,
+ * compared to more accurate transformations.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param coordoperation Objet of type CoordinateOperation or derived classes
+ * (must not be NULL)
+ * @return TRUE or FALSE.
+ */
+
+int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
+ const PJ *coordoperation) {
+ assert(coordoperation);
+ auto op = dynamic_cast<const CoordinateOperation *>(
+ coordoperation->iso_obj.get());
+ if (!op) {
+ proj_log_error(ctx, __FUNCTION__,
+ "Object is not a CoordinateOperation");
+ return 0;
+ }
+ return op->hasBallparkTransformation();
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Return the number of parameters of a SingleOperation
*
* @param ctx PROJ context, or NULL for default context
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index ede6b72e..65588923 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -104,14 +104,16 @@ constexpr double UTM_NORTH_FALSE_NORTHING = 0.0;
constexpr double UTM_SOUTH_FALSE_NORTHING = 10000000.0;
static const std::string INVERSE_OF = "Inverse of ";
-static const char *NULL_GEOCENTRIC_TRANSLATION = "Null geocentric translation";
-static const char *NULL_GEOGRAPHIC_OFFSET = "Null geographic offset";
-static const char *APPROXIMATE_TRANSFORMATION_PREFIX =
- " (approximate transformation";
-static const char *APPROXIMATE_TRANSFORMATION = " (approximate transformation)";
-static const char *APPROXIMATE_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT =
- " (approximate transformation, without ellipsoid height to vertical height "
- "correction)";
+static const char *BALLPARK_GEOCENTRIC_TRANSLATION =
+ "Ballpark geocentric translation";
+static const char *BALLPARK_GEOGRAPHIC_OFFSET = "Ballpark geographic offset";
+static const char *BALLPARK_VERTICAL_TRANSFORMATION_PREFIX =
+ " (ballpark vertical transformation";
+static const char *BALLPARK_VERTICAL_TRANSFORMATION =
+ " (ballpark vertical transformation)";
+static const char *BALLPARK_VERTICAL_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT =
+ " (ballpark vertical transformation, without ellipsoid height to vertical "
+ "height correction)";
//! @endcond
//! @cond Doxygen_Suppress
@@ -519,6 +521,7 @@ struct CoordinateOperation::Private {
crs::CRSPtr interpolationCRS_{};
util::optional<common::DataEpoch> sourceCoordinateEpoch_{};
util::optional<common::DataEpoch> targetCoordinateEpoch_{};
+ bool hasBallparkTransformation_ = false;
// do not set this for a ProjectedCRS.definingConversion
struct CRSStrongRef {
@@ -729,6 +732,27 @@ bool CoordinateOperation::isPROJInstanciable(
// ---------------------------------------------------------------------------
+/** \brief Return whether a coordinate operation has a "ballpark"
+ * transformation,
+ * that is a very approximate one, due to lack of more accurate transformations.
+ *
+ * Typically a null geographic offset between two horizontal datum, or a
+ * null vertical offset (or limited to unit changes) between two vertical
+ * datum. Errors of several tens to one hundred meters might be expected,
+ * compared to more accurate transformations.
+ */
+bool CoordinateOperation::hasBallparkTransformation() const {
+ return d->hasBallparkTransformation_;
+}
+
+// ---------------------------------------------------------------------------
+
+void CoordinateOperation::setHasBallparkTransformation(bool b) {
+ d->hasBallparkTransformation_ = b;
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
struct OperationMethod::Private {
util::optional<std::string> formula_{};
@@ -1529,11 +1553,12 @@ static SingleOperationNNPtr createPROJBased(
const util::PropertyMap &properties,
const io::IPROJStringExportableNNPtr &projExportable,
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
- const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies =
- std::vector<metadata::PositionalAccuracyNNPtr>()) {
+ const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies,
+ bool hasBallparkTransformation) {
return util::nn_static_pointer_cast<SingleOperation>(
PROJBasedOperation::create(properties, projExportable, false, sourceCRS,
- targetCRS, accuracies));
+ targetCRS, accuracies,
+ hasBallparkTransformation));
}
//! @endcond
@@ -6149,6 +6174,11 @@ TransformationNNPtr Transformation::create(
accuracies);
conv->assignSelf(conv);
conv->setProperties(properties);
+ std::string name;
+ if (properties.getStringValue(common::IdentifiedObject::NAME_KEY, name) &&
+ ci_find(name, "ballpark") != std::string::npos) {
+ conv->setHasBallparkTransformation(true);
+ }
return conv;
}
@@ -7155,10 +7185,10 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom,
// Forge a name for the inverse, either from the forward name, or
// from the source and target CRS names
const char *opType;
- if (starts_with(forwardName, NULL_GEOCENTRIC_TRANSLATION)) {
- opType = NULL_GEOCENTRIC_TRANSLATION;
- } else if (starts_with(forwardName, NULL_GEOGRAPHIC_OFFSET)) {
- opType = NULL_GEOGRAPHIC_OFFSET;
+ if (starts_with(forwardName, BALLPARK_GEOCENTRIC_TRANSLATION)) {
+ opType = BALLPARK_GEOCENTRIC_TRANSLATION;
+ } else if (starts_with(forwardName, BALLPARK_GEOGRAPHIC_OFFSET)) {
+ opType = BALLPARK_GEOGRAPHIC_OFFSET;
} else if (dynamic_cast<const Transformation *>(op) ||
starts_with(forwardName, "Transformation from ")) {
opType = "Transformation";
@@ -9188,7 +9218,9 @@ CoordinateOperationNNPtr ConcatenatedOperation::createComputeMetadata(
}
std::vector<CoordinateOperationNNPtr> flattenOps;
+ bool hasBallparkTransformation = false;
for (const auto &subOp : operationsIn) {
+ hasBallparkTransformation |= subOp->hasBallparkTransformation();
auto subOpConcat =
dynamic_cast<const ConcatenatedOperation *>(subOp.get());
if (subOpConcat) {
@@ -9228,6 +9260,7 @@ CoordinateOperationNNPtr ConcatenatedOperation::createComputeMetadata(
}
auto op = create(properties, flattenOps, accuracies);
+ op->setHasBallparkTransformation(hasBallparkTransformation);
op->d->computedName_ = true;
return op;
}
@@ -10012,13 +10045,7 @@ struct FilterResults {
bool extentContains =
extent->contains(NN_NO_CHECK(areaOfInterest));
if (extentContains) {
- const auto &name = op->nameStr();
- if (name.find(NULL_GEOGRAPHIC_OFFSET) ==
- std::string::npos &&
- name.find(NULL_GEOCENTRIC_TRANSLATION) ==
- std::string::npos &&
- name.find(APPROXIMATE_TRANSFORMATION_PREFIX) ==
- std::string::npos) {
+ if (!op->hasBallparkTransformation()) {
hasOpThatContainsAreaOfInterest = true;
}
}
@@ -10049,13 +10076,7 @@ struct FilterResults {
!targetCRSExtent ||
extent->contains(NN_NO_CHECK(targetCRSExtent));
if (extentContainsSource && extentContainsTarget) {
- const auto &name = op->nameStr();
- if (name.find(NULL_GEOGRAPHIC_OFFSET) ==
- std::string::npos &&
- name.find(NULL_GEOCENTRIC_TRANSLATION) ==
- std::string::npos &&
- name.find(APPROXIMATE_TRANSFORMATION_PREFIX) ==
- std::string::npos) {
+ if (!op->hasBallparkTransformation()) {
hasOpThatContainsAreaOfInterest = true;
}
}
@@ -10156,12 +10177,12 @@ struct FilterResults {
const auto stepCount = getStepCount(op);
const bool isApprox =
- op->nameStr().find(APPROXIMATE_TRANSFORMATION_PREFIX) !=
+ op->nameStr().find(BALLPARK_VERTICAL_TRANSFORMATION_PREFIX) !=
std::string::npos;
const bool isNullTransformation =
- op->nameStr().find(NULL_GEOGRAPHIC_OFFSET) !=
+ op->nameStr().find(BALLPARK_GEOGRAPHIC_OFFSET) !=
std::string::npos ||
- op->nameStr().find(NULL_GEOCENTRIC_TRANSLATION) !=
+ op->nameStr().find(BALLPARK_GEOCENTRIC_TRANSLATION) !=
std::string::npos;
map[op.get()] = PrecomputedOpCharacteristics(
area, getAccuracy(op), hasGrids, gridsAvailable, gridsKnown,
@@ -10177,14 +10198,16 @@ struct FilterResults {
void removeSyntheticNullTransforms() {
// If we have more than one result, and than the last result is the
- // default "Null geographic offset" or "Null geocentric translation"
+ // 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();
- if (name.find(NULL_GEOGRAPHIC_OFFSET) != std::string::npos ||
- name.find(NULL_GEOCENTRIC_TRANSLATION) != std::string::npos) {
+ if (name.find(BALLPARK_GEOGRAPHIC_OFFSET) != std::string::npos ||
+ name.find(BALLPARK_GEOCENTRIC_TRANSLATION) !=
+ std::string::npos) {
std::vector<CoordinateOperationNNPtr> resTemp;
for (size_t i = 0; i < res.size() - 1; i++) {
resTemp.emplace_back(res[i]);
@@ -10597,9 +10620,9 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate(
//! @cond Doxygen_Suppress
static TransformationNNPtr
-createNullGeographicOffset(const crs::CRSNNPtr &sourceCRS,
- const crs::CRSNNPtr &targetCRS) {
- std::string name(NULL_GEOGRAPHIC_OFFSET);
+createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS,
+ const crs::CRSNNPtr &targetCRS) {
+ std::string name(BALLPARK_GEOGRAPHIC_OFFSET);
name += " from ";
name += sourceCRS->nameStr();
name += " to ";
@@ -10790,7 +10813,7 @@ createGeodToGeodPROJBased(const crs::CRSNNPtr &geodSrc,
auto properties = util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY,
buildTransfName(geodSrc->nameStr(), geodDst->nameStr()));
- return createPROJBased(properties, exportable, geodSrc, geodDst);
+ return createPROJBased(properties, exportable, geodSrc, geodDst, {}, false);
}
// ---------------------------------------------------------------------------
@@ -10827,7 +10850,9 @@ static CoordinateOperationNNPtr createHorizVerticalPROJBased(
}
return createPROJBased(properties, exportable, sourceCRS, targetCRS,
- accuracies);
+ accuracies,
+ horizTransform->hasBallparkTransformation() ||
+ verticalTransform->hasBallparkTransformation());
}
// ---------------------------------------------------------------------------
@@ -10852,6 +10877,10 @@ static CoordinateOperationNNPtr createHorizVerticalHorizPROJBased(
: std::vector<CoordinateOperationNNPtr>{opSrcCRSToGeogCRS,
verticalTransform,
opGeogCRStoDstCRS};
+ bool hasBallparkTransformation = false;
+ for (const auto &op : ops) {
+ hasBallparkTransformation |= op->hasBallparkTransformation();
+ }
auto extent = getExtent(ops, true, dummy);
auto properties = util::PropertyMap();
properties.set(common::IdentifiedObject::NAME_KEY,
@@ -10870,7 +10899,7 @@ static CoordinateOperationNNPtr createHorizVerticalHorizPROJBased(
}
return createPROJBased(properties, exportable, sourceCRS, targetCRS,
- accuracies);
+ accuracies, hasBallparkTransformation);
}
//! @endcond
@@ -10926,6 +10955,11 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
std::string name(buildTransfName(geogSrc->nameStr(), geogDst->nameStr()));
+ const bool sameDatum =
+ geogSrc->datum() != nullptr && geogDst->datum() != nullptr &&
+ geogSrc->datum()->_isEquivalentTo(
+ geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT);
+
// Do they differ by vertical units ?
if (vconvSrc != vconvDst &&
geogSrc->ellipsoid()->_isEquivalentTo(
@@ -10941,18 +10975,19 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
name),
common::Scale(factor));
conv->setCRSs(sourceCRS, targetCRS, nullptr);
+ conv->setHasBallparkTransformation(!sameDatum);
res.push_back(conv);
return res;
} else {
- res.emplace_back(createGeodToGeodPROJBased(sourceCRS, targetCRS));
+ auto op = createGeodToGeodPROJBased(sourceCRS, targetCRS);
+ op->setHasBallparkTransformation(!sameDatum);
+ res.emplace_back(op);
return res;
}
}
// Do the CRS differ only by their axis order ?
- if (geogSrc->datum() != nullptr && geogDst->datum() != nullptr &&
- geogSrc->datum()->_isEquivalentTo(
- geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT) &&
+ if (sameDatum &&
!srcCS->_isEquivalentTo(dstCS.get(),
util::IComparable::Criterion::EQUIVALENT)) {
auto srcOrder = srcCS->axisOrder();
@@ -11013,7 +11048,8 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
metadata::Extent::WORLD),
datum, dstCS));
- steps.emplace_back(createNullGeographicOffset(sourceCRS, interm_crs));
+ steps.emplace_back(
+ createBallparkGeographicOffset(sourceCRS, interm_crs));
steps.emplace_back(Transformation::createLongitudeRotation(
util::PropertyMap()
@@ -11048,15 +11084,17 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
metadata::Extent::WORLD),
sourceCRS, interm_crs, offset_pm));
steps.emplace_back(
- createNullGeographicOffset(interm_crs, targetCRS));
+ createBallparkGeographicOffset(interm_crs, targetCRS));
} else {
steps.emplace_back(
- createNullGeographicOffset(sourceCRS, targetCRS));
+ createBallparkGeographicOffset(sourceCRS, targetCRS));
}
}
- res.emplace_back(ConcatenatedOperation::createComputeMetadata(
- steps, !allowEmptyIntersection));
+ auto op = ConcatenatedOperation::createComputeMetadata(
+ steps, !allowEmptyIntersection);
+ op->setHasBallparkTransformation(!sameDatum);
+ res.emplace_back(op);
return res;
}
@@ -11108,8 +11146,8 @@ findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory,
static bool isNullTransformation(const std::string &name) {
- return starts_with(name, NULL_GEOCENTRIC_TRANSLATION) ||
- starts_with(name, NULL_GEOGRAPHIC_OFFSET);
+ return starts_with(name, BALLPARK_GEOCENTRIC_TRANSLATION) ||
+ starts_with(name, BALLPARK_GEOGRAPHIC_OFFSET);
}
// ---------------------------------------------------------------------------
@@ -11227,9 +11265,9 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
// ---------------------------------------------------------------------------
static CoordinateOperationNNPtr
-createNullGeocentricTranslation(const crs::CRSNNPtr &sourceCRS,
- const crs::CRSNNPtr &targetCRS) {
- std::string name(NULL_GEOCENTRIC_TRANSLATION);
+createBallparkGeocentricTranslation(const crs::CRSNNPtr &sourceCRS,
+ const crs::CRSNNPtr &targetCRS) {
+ std::string name(BALLPARK_GEOCENTRIC_TRANSLATION);
name += " from ";
name += sourceCRS->nameStr();
name += " to ";
@@ -11451,7 +11489,7 @@ CoordinateOperationFactory::Private::createOperations(
util::nn_dynamic_pointer_cast<cs::CartesianCS>(
geodSrc->coordinateSystem()))));
auto opFirst =
- createNullGeocentricTranslation(sourceCRS, interm_crs);
+ createBallparkGeocentricTranslation(sourceCRS, interm_crs);
auto opSecond =
createGeographicGeocentric(interm_crs, targetCRS);
res.emplace_back(ConcatenatedOperation::createComputeMetadata(
@@ -11466,7 +11504,7 @@ CoordinateOperationFactory::Private::createOperations(
if (isSrcGeocentric && isTargetGeocentric) {
res.emplace_back(
- createNullGeocentricTranslation(sourceCRS, targetCRS));
+ createBallparkGeocentricTranslation(sourceCRS, targetCRS));
return res;
}
@@ -11681,11 +11719,12 @@ CoordinateOperationFactory::Private::createOperations(
const double factor = convSrc / convDst;
auto name = buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr());
if (!equivalentVDatum) {
- name += APPROXIMATE_TRANSFORMATION;
+ name += BALLPARK_VERTICAL_TRANSFORMATION;
auto conv = Transformation::createChangeVerticalUnit(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
name),
sourceCRS, targetCRS, common::Scale(factor), {});
+ conv->setHasBallparkTransformation(true);
res.push_back(conv);
} else if (convSrc != convDst) {
auto conv = Conversion::createChangeVerticalUnit(
@@ -11714,8 +11753,9 @@ CoordinateOperationFactory::Private::createOperations(
util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY,
buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr()) +
- APPROXIMATE_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT),
+ BALLPARK_VERTICAL_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT),
sourceCRS, targetCRS, common::Scale(factor), {});
+ conv->setHasBallparkTransformation(true);
res.push_back(conv);
return res;
}
@@ -12022,7 +12062,8 @@ PROJBasedOperationNNPtr PROJBasedOperation::create(
const util::PropertyMap &properties,
const io::IPROJStringExportableNNPtr &projExportable, bool inverse,
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
- const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
+ const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies,
+ bool hasBallparkTransformation) {
auto formatter = io::PROJStringFormatter::create();
if (inverse) {
@@ -12048,6 +12089,7 @@ PROJBasedOperationNNPtr PROJBasedOperation::create(
op->setAccuracies(accuracies);
op->projStringExportable_ = projExportable.as_nullable();
op->inverse_ = inverse;
+ op->setHasBallparkTransformation(hasBallparkTransformation);
return op;
}
@@ -12061,7 +12103,7 @@ CoordinateOperationNNPtr PROJBasedOperation::inverse() const {
createPropertiesForInverse(this, false, false),
NN_NO_CHECK(projStringExportable_), !inverse_,
NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
- coordinateOperationAccuracies()));
+ coordinateOperationAccuracies(), hasBallparkTransformation()));
}
auto formatter = io::PROJStringFormatter::create();
@@ -12074,11 +12116,11 @@ CoordinateOperationNNPtr PROJBasedOperation::inverse() const {
}
formatter->stopInversion();
- return util::nn_static_pointer_cast<CoordinateOperation>(
- PROJBasedOperation::create(
- createPropertiesForInverse(this, false, false),
- formatter->toString(), targetCRS(), sourceCRS(),
- coordinateOperationAccuracies()));
+ auto op = PROJBasedOperation::create(
+ createPropertiesForInverse(this, false, false), formatter->toString(),
+ targetCRS(), sourceCRS(), coordinateOperationAccuracies());
+ op->setHasBallparkTransformation(hasBallparkTransformation());
+ return util::nn_static_pointer_cast<CoordinateOperation>(op);
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 01a588e3..aabb15a1 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -453,7 +453,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
auto transf =
util::nn_dynamic_pointer_cast<operation::Transformation>(
op);
- if (transf && !starts_with(transf->nameStr(), "Null geo")) {
+ if (transf && !starts_with(transf->nameStr(), "Ballpark geo")) {
try {
transf->getTOWGS84Parameters();
} catch (const std::exception &) {
@@ -486,7 +486,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
operation::Transformation>(subops[1]);
if (transf &&
!starts_with(transf->nameStr(),
- "Null geo")) {
+ "Ballpark geo")) {
try {
transf->getTOWGS84Parameters();
} catch (const std::exception &) {
@@ -3855,7 +3855,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
for (const auto &op : ops) {
std::string opTransfPROJString;
bool opTransfPROJStringValid = false;
- if (op->nameStr().find("Null geographic") == 0) {
+ if (op->nameStr().find("Ballpark geographic") == 0) {
if (isTOWGS84Compatible()) {
auto params =
transformation()->getTOWGS84Parameters();
diff --git a/src/proj.h b/src/proj.h
index af22c341..edbbd798 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -1002,6 +1002,9 @@ int PROJ_DLL proj_coordoperation_get_method_info(PJ_CONTEXT *ctx,
int PROJ_DLL proj_coordoperation_is_instanciable(PJ_CONTEXT *ctx,
const PJ *coordoperation);
+int PROJ_DLL proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
+ const PJ *coordoperation);
+
int PROJ_DLL proj_coordoperation_get_param_count(PJ_CONTEXT *ctx,
const PJ *coordoperation);
diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist
index 0ce6150d..674e9631 100644
--- a/test/cli/testprojinfo_out.dist
+++ b/test/cli/testprojinfo_out.dist
@@ -172,13 +172,13 @@ Note: using '--spatial-test intersects' would bring more results (7)
-------------------------------------
Operation n°1:
-unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World
+unknown id, Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation
PROJ string:
WKT2_2018 string:
-COORDINATEOPERATION["Null geographic offset from NAD27 to NAD83",
+COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
SOURCECRS[
GEOGCRS["NAD27",
DATUM["North American Datum 1927",
@@ -238,7 +238,7 @@ DERIVED_FROM(EPSG):1241, NAD27 to NAD83 (1), 0.15 m, USA - CONUS including EEZ
DERIVED_FROM(EPSG):1243, NAD27 to NAD83 (2), 0.5 m, USA - Alaska including EEZ
EPSG:1462, NAD27 to NAD83 (5), 1.0 m, Canada - Quebec
EPSG:1573, NAD27 to NAD83 (6), 1.5 m, Canada - Quebec
-unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World
+unknown id, Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation
Testing projinfo -s NAD27 -t NAD83 --grid-check none --spatial-test intersects
Candidate operations found: 7
@@ -531,13 +531,13 @@ COORDINATEOPERATION["NAD27 to NAD83 (6)",
-------------------------------------
Operation n°7:
-unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World
+unknown id, Ballpark geographic offset from NAD27 to NAD83, unknown accuracy, World, has ballpark transformation
PROJ string:
WKT2_2018 string:
-COORDINATEOPERATION["Null geographic offset from NAD27 to NAD83",
+COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
SOURCECRS[
GEOGCRS["NAD27",
DATUM["North American Datum 1927",
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index 69472a58..7682c072 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -1345,6 +1345,7 @@ TEST_F(CApi, proj_create_operations) {
auto op = proj_list_get(m_ctxt, res, 0);
ASSERT_NE(op, nullptr);
ObjectKeeper keeper_op(op);
+ EXPECT_FALSE(proj_coordoperation_has_ballpark_transformation(m_ctxt, op));
EXPECT_EQ(proj_get_name(op), std::string("NAD27 to NAD83 (3)"));
}
@@ -1401,8 +1402,9 @@ TEST_F(CApi, proj_create_operations_with_pivot) {
ASSERT_NE(op, nullptr);
ObjectKeeper keeper_op(op);
- EXPECT_EQ(proj_get_name(op),
- std::string("Null geographic offset from WGS 84 to JGD2011"));
+ EXPECT_EQ(
+ proj_get_name(op),
+ std::string("Ballpark geographic offset from WGS 84 to JGD2011"));
}
// Restrict pivot to Tokyo CRS
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index c0cb0b95..06c230b0 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -4199,7 +4199,7 @@ TEST(operation, geogCRS_to_geogCRS_context_default) {
EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m
EXPECT_EQ(list[1]->getEPSGCode(), 1644); // Poland - 1m
EXPECT_EQ(list[2]->nameStr(),
- "Null geographic offset from Pulkovo 1942(58) to ETRS89");
+ "Ballpark geographic offset from Pulkovo 1942(58) to ETRS89");
EXPECT_EQ(
list[0]->exportToPROJString(PROJStringFormatter::create().get()),
@@ -4507,7 +4507,8 @@ TEST(operation, geogCRS_to_geogCRS_noop) {
auto op = CoordinateOperationFactory::create()->createOperation(
GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4326);
ASSERT_TRUE(op != nullptr);
- EXPECT_EQ(op->nameStr(), "Null geographic offset from WGS 84 to WGS 84");
+ EXPECT_EQ(op->nameStr(),
+ "Ballpark geographic offset from WGS 84 to WGS 84");
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "");
EXPECT_EQ(op->inverse()->nameStr(), op->nameStr());
}
@@ -4768,9 +4769,10 @@ TEST(operation, geocentricCRS_to_geogCRS_different_datum) {
auto op = CoordinateOperationFactory::create()->createOperation(
createGeocentricDatumWGS84(), GeographicCRS::EPSG_4269);
ASSERT_TRUE(op != nullptr);
- EXPECT_EQ(op->nameStr(), "Null geocentric translation from WGS 84 to NAD83 "
- "(geocentric) + Conversion from NAD83 "
- "(geocentric) to NAD83");
+ EXPECT_EQ(op->nameStr(),
+ "Ballpark geocentric translation from WGS 84 to NAD83 "
+ "(geocentric) + Conversion from NAD83 "
+ "(geocentric) to NAD83");
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +inv +proj=cart +ellps=GRS80 +step "
"+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap "
@@ -4785,7 +4787,7 @@ TEST(operation, geogCRS_to_geocentricCRS_different_datum) {
GeographicCRS::EPSG_4269, createGeocentricDatumWGS84());
ASSERT_TRUE(op != nullptr);
EXPECT_EQ(op->nameStr(), "Conversion from NAD83 to NAD83 (geocentric) + "
- "Null geocentric translation from NAD83 "
+ "Ballpark geocentric translation from NAD83 "
"(geocentric) to WGS 84");
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +proj=axisswap +order=2,1 +step "
@@ -4801,7 +4803,7 @@ TEST(operation, geocentricCRS_to_geocentricCRS_noop) {
createGeocentricDatumWGS84(), createGeocentricDatumWGS84());
ASSERT_TRUE(op != nullptr);
EXPECT_EQ(op->nameStr(),
- "Null geocentric translation from WGS 84 to WGS 84");
+ "Ballpark geocentric translation from WGS 84 to WGS 84");
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), "");
EXPECT_EQ(op->inverse()->nameStr(), op->nameStr());
}
@@ -6128,6 +6130,7 @@ TEST(operation, compoundCRS_to_compoundCRS_context) {
authFactory->createCoordinateReferenceSystem("5500"), ctxt);
// 152 or 155 depending if the VERTCON grids are there
ASSERT_GE(list.size(), 152U);
+ EXPECT_FALSE(list[0]->hasBallparkTransformation());
EXPECT_EQ(list[0]->nameStr(), "NGVD29 height (ftUS) to NAVD88 height (3) + "
"NAD27 to WGS 84 (79) + Inverse of "
"NAD83(NSRS2007) to WGS 84 (1)");
@@ -6149,12 +6152,13 @@ TEST(operation, compoundCRS_to_compoundCRS_context) {
"+xy_out=rad +z_out=m") == 0)
<< list[i]->nameStr();
if (list[i]->nameStr().find("Transformation from NGVD29 height (ftUS) "
- "to NAVD88 height (approximate "
+ "to NAVD88 height (ballpark vertical "
"transformation)") == 0) {
+ EXPECT_TRUE(list[i]->hasBallparkTransformation());
EXPECT_EQ(list[i]->nameStr(),
"Transformation from NGVD29 height (ftUS) to NAVD88 "
- "height (approximate transformation) + NAD27 to WGS 84 "
- "(79) + Inverse of NAD83(NSRS2007) to WGS 84 (1)");
+ "height (ballpark vertical transformation) + NAD27 to "
+ "WGS 84 (79) + Inverse of NAD83(NSRS2007) to WGS 84 (1)");
EXPECT_EQ(projString,
"+proj=pipeline +step +proj=axisswap +order=2,1 +step "
"+proj=unitconvert +xy_in=deg +z_in=us-ft +xy_out=rad "
@@ -6254,6 +6258,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D) {
auto op = CoordinateOperationFactory::create()->createOperation(
NN_CHECK_ASSERT(compoundcrs_ft), NN_CHECK_ASSERT(geogcrs_m));
ASSERT_TRUE(op != nullptr);
+ EXPECT_TRUE(op->hasBallparkTransformation());
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +inv +proj=merc +lon_0=0 +k=1 +x_0=0 "
"+y_0=0 +ellps=WGS84 +step +proj=unitconvert +xy_in=rad "
@@ -6264,6 +6269,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D) {
auto op = CoordinateOperationFactory::create()->createOperation(
NN_CHECK_ASSERT(geogcrs_m), NN_CHECK_ASSERT(compoundcrs_ft));
ASSERT_TRUE(op != nullptr);
+ EXPECT_TRUE(op->hasBallparkTransformation());
EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +proj=unitconvert +xy_in=deg +z_in=m "
"+xy_out=rad +z_out=ft +step +proj=merc +lon_0=0 +k=1 +x_0=0 "
@@ -6287,9 +6293,10 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) {
authFactory->createCoordinateReferenceSystem("4979"), // WGS 84
ctxt);
ASSERT_GE(list.size(), 1U);
+ EXPECT_TRUE(list[0]->hasBallparkTransformation());
EXPECT_EQ(list[0]->nameStr(),
"NAD27 to WGS 84 (79) + Transformation from NGVD29 height "
- "(ftUS) to WGS 84 (approximate transformation, without "
+ "(ftUS) to WGS 84 (ballpark vertical transformation, without "
"ellipsoid height to vertical height correction)");
EXPECT_EQ(list[0]->exportToPROJString(
PROJStringFormatter::create(
@@ -6314,9 +6321,10 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) {
authFactory->createCoordinateReferenceSystem("4979"), // WGS 84
ctxt);
ASSERT_GE(list.size(), 1U);
+ EXPECT_TRUE(list[0]->hasBallparkTransformation());
EXPECT_EQ(list[0]->nameStr(),
"NAD83(NSRS2007) to WGS 84 (1) + Transformation from NAVD88 "
- "height to WGS 84 (approximate transformation, without "
+ "height to WGS 84 (ballpark vertical transformation, without "
"ellipsoid height to vertical height correction)");
EXPECT_EQ(list[0]->exportToPROJString(
PROJStringFormatter::create(
@@ -6343,6 +6351,7 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) {
ctxt);
ASSERT_EQ(list.size(), 2U);
+ EXPECT_FALSE(list[0]->hasBallparkTransformation());
EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 "
"+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 "
@@ -6350,6 +6359,7 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) {
"+grids=ntf_r93.gsb +step +proj=unitconvert +xy_in=rad "
"+xy_out=deg +step +proj=axisswap +order=2,1");
+ EXPECT_FALSE(list[1]->hasBallparkTransformation());
EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=pipeline +step +inv +proj=lcc +lat_1=49.5 +lat_0=49.5 "
"+lon_0=0 +k_0=0.99987734 +x_0=600000 +y_0=200000 "