diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-02-20 18:04:35 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-02-20 20:32:17 +0100 |
| commit | 94578ea8ff38f4bc6b1f6f52b80ecf7359f5dfc2 (patch) | |
| tree | 1ef7f51339a7dc8c2e1040ddbf4d13aca6cc879a | |
| parent | 374cc258510428fa3bfb9d8ca61ad7ac83a00db1 (diff) | |
| download | PROJ-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.hpp | 3 | ||||
| -rw-r--r-- | include/proj/internal/coordinateoperation_internal.hpp | 3 | ||||
| -rw-r--r-- | src/4D_api.cpp | 4 | ||||
| -rw-r--r-- | src/apps/projinfo.cpp | 4 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 30 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 174 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 6 | ||||
| -rw-r--r-- | src/proj.h | 3 | ||||
| -rw-r--r-- | test/cli/testprojinfo_out.dist | 10 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 6 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 34 |
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(); @@ -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 " |
