aboutsummaryrefslogtreecommitdiff
path: root/src/iso19111
diff options
context:
space:
mode:
Diffstat (limited to 'src/iso19111')
-rw-r--r--src/iso19111/c_api.cpp112
-rw-r--r--src/iso19111/common.cpp33
-rw-r--r--src/iso19111/coordinateoperation.cpp133
-rw-r--r--src/iso19111/coordinatesystem.cpp27
-rw-r--r--src/iso19111/crs.cpp422
-rw-r--r--src/iso19111/datum.cpp151
-rw-r--r--src/iso19111/factory.cpp337
-rw-r--r--src/iso19111/io.cpp53
-rw-r--r--src/iso19111/metadata.cpp21
-rw-r--r--src/iso19111/util.cpp8
10 files changed, 882 insertions, 415 deletions
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 5df4c513..4c98af3d 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -1146,15 +1146,9 @@ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) {
// ---------------------------------------------------------------------------
-/** \brief Return whether two objects are equivalent.
- *
- * @param obj Object (must not be NULL)
- * @param other Other object (must not be NULL)
- * @param criterion Comparison criterion
- * @return TRUE if they are equivalent
- */
-int proj_is_equivalent_to(const PJ *obj, const PJ *other,
- PJ_COMPARISON_CRITERION criterion) {
+static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj,
+ const PJ *other,
+ PJ_COMPARISON_CRITERION criterion) {
assert(obj);
assert(other);
if (!obj->iso_obj) {
@@ -1176,7 +1170,50 @@ int proj_is_equivalent_to(const PJ *obj, const PJ *other,
return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
})(criterion);
- return obj->iso_obj->isEquivalentTo(other->iso_obj.get(), cppCriterion);
+ int res = obj->iso_obj->isEquivalentTo(
+ other->iso_obj.get(), cppCriterion,
+ ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx")
+ : nullptr);
+ if (ctx && ctx->cpp_context) {
+ ctx->cpp_context->autoCloseDbIfNeeded();
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return whether two objects are equivalent.
+ *
+ * Use proj_is_equivalent_to_with_ctx() to be able to use database information.
+ *
+ * @param obj Object (must not be NULL)
+ * @param other Other object (must not be NULL)
+ * @param criterion Comparison criterion
+ * @return TRUE if they are equivalent
+ */
+int proj_is_equivalent_to(const PJ *obj, const PJ *other,
+ PJ_COMPARISON_CRITERION criterion) {
+ return proj_is_equivalent_to_internal(nullptr, obj, other, criterion);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Return whether two objects are equivalent
+ *
+ * Possibly using database to check for name aliases.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param obj Object (must not be NULL)
+ * @param other Other object (must not be NULL)
+ * @param criterion Comparison criterion
+ * @return TRUE if they are equivalent
+ * @since 6.3
+ */
+int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj,
+ const PJ *other,
+ PJ_COMPARISON_CRITERION criterion) {
+ SANITIZE_CTX(ctx);
+ return proj_is_equivalent_to_internal(ctx, obj, other, criterion);
}
// ---------------------------------------------------------------------------
@@ -2159,6 +2196,10 @@ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) {
* The candidate CRSs are either hard-coded, or looked in the database when
* it is available.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -2727,16 +2768,19 @@ static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
if (metadata::Identifier::isEquivalentName(
datumName.c_str(), refDatum->nameStr().c_str())) {
datumName = refDatum->nameStr();
- }
- } else {
- std::string outTableName;
- std::string authNameFromAlias;
- std::string codeFromAlias;
- auto officialName = authFactory->getOfficialNameFromAlias(
- datumName, "geodetic_datum", std::string(), true,
- outTableName, authNameFromAlias, codeFromAlias);
- if (!officialName.empty()) {
- datumName = officialName;
+ } else if (refDatum->identifiers().size() == 1) {
+ const auto &id = refDatum->identifiers()[0];
+ const auto aliases =
+ authFactory->databaseContext()->getAliases(
+ *id->codeSpace(), id->code(), refDatum->nameStr(),
+ "geodetic_datum", std::string());
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(
+ datumName.c_str(), alias.c_str())) {
+ datumName = refDatum->nameStr();
+ break;
+ }
+ }
}
}
}
@@ -7783,6 +7827,34 @@ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
// ---------------------------------------------------------------------------
+/** \brief Returns a PJ* coordinate operation object which represents the
+ * inverse operation of the specified coordinate operation.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param obj Object of type CoordinateOperation (must not be NULL)
+ * @return a new PJ* object to free with proj_destroy() in case of success, or
+ * nullptr in case of error
+ * @since 6.3
+ */
+PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) {
+
+ SANITIZE_CTX(ctx);
+ auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
+ if (!co) {
+ proj_log_error(ctx, __FUNCTION__,
+ "Object is not a CoordinateOperation");
+ return nullptr;
+ }
+ try {
+ return pj_obj_create(ctx, co->inverse());
+ } catch (const std::exception &e) {
+ proj_log_debug(ctx, __FUNCTION__, e.what());
+ return nullptr;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Returns the number of steps of a concatenated operation.
*
* The input object must be a concatenated operation.
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
index 97900bda..f2e4de4c 100644
--- a/src/iso19111/common.cpp
+++ b/src/iso19111/common.cpp
@@ -901,19 +901,19 @@ void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const {
// ---------------------------------------------------------------------------
bool IdentifiedObject::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other);
if (!otherIdObj)
return false;
- return _isEquivalentTo(otherIdObj, criterion);
+ return _isEquivalentTo(otherIdObj, criterion, dbContext);
}
// ---------------------------------------------------------------------------
-bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj,
- util::IComparable::Criterion criterion)
- PROJ_PURE_DEFN {
+bool IdentifiedObject::_isEquivalentTo(
+ const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN {
if (criterion == util::IComparable::Criterion::STRICT) {
if (!ci_equal(nameStr(), otherIdObj->nameStr())) {
return false;
@@ -922,12 +922,19 @@ bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj,
} else {
if (!metadata::Identifier::isEquivalentName(
nameStr().c_str(), otherIdObj->nameStr().c_str())) {
- return false;
+ return hasEquivalentNameToUsingAlias(otherIdObj, dbContext);
}
}
return true;
}
+// ---------------------------------------------------------------------------
+
+bool IdentifiedObject::hasEquivalentNameToUsingAlias(
+ const IdentifiedObject *, const io::DatabaseContextPtr &) const {
+ return false;
+}
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -1092,8 +1099,8 @@ void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const {
//! @cond Doxygen_Suppress
bool ObjectDomain::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDomain = dynamic_cast<const ObjectDomain *>(other);
if (!otherDomain)
return false;
@@ -1106,7 +1113,7 @@ bool ObjectDomain::_isEquivalentTo(
return false;
return domainOfValidity().get() == nullptr ||
domainOfValidity()->_isEquivalentTo(
- otherDomain->domainOfValidity().get(), criterion);
+ otherDomain->domainOfValidity().get(), criterion, dbContext);
}
//! @endcond
@@ -1249,14 +1256,14 @@ void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const {
//! @cond Doxygen_Suppress
bool ObjectUsage::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other);
if (!otherObjUsage)
return false;
// TODO: incomplete
- return IdentifiedObject::_isEquivalentTo(other, criterion);
+ return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 7c0515c7..a1650046 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -1070,11 +1070,11 @@ void OperationMethod::_exportToJSON(
//! @cond Doxygen_Suppress
bool OperationMethod::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOM = dynamic_cast<const OperationMethod *>(other);
if (otherOM == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
// TODO test formula and formulaCitation
@@ -1086,7 +1086,8 @@ bool OperationMethod::_isEquivalentTo(
}
if (criterion == util::IComparable::Criterion::STRICT) {
for (size_t i = 0; i < paramsSize; i++) {
- if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion)) {
+ if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -1096,8 +1097,8 @@ bool OperationMethod::_isEquivalentTo(
bool found = false;
for (size_t j = 0; j < paramsSize; j++) {
if (candidateIndices[j] &&
- params[i]->_isEquivalentTo(otherParams[j].get(),
- criterion)) {
+ params[i]->_isEquivalentTo(otherParams[j].get(), criterion,
+ dbContext)) {
candidateIndices[j] = false;
found = true;
break;
@@ -1343,14 +1344,14 @@ bool OperationParameterValue::convertFromAbridged(
//! @cond Doxygen_Suppress
bool OperationParameterValue::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOPV = dynamic_cast<const OperationParameterValue *>(other);
if (otherOPV == nullptr) {
return false;
}
- if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(),
- criterion)) {
+ if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), criterion,
+ dbContext)) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
@@ -1358,7 +1359,7 @@ bool OperationParameterValue::_isEquivalentTo(
otherOPV->d->parameterValue.get(), criterion);
}
if (d->parameterValue->_isEquivalentTo(otherOPV->d->parameterValue.get(),
- criterion)) {
+ criterion, dbContext)) {
return true;
}
if (d->parameter->getEPSGCode() ==
@@ -1448,16 +1449,16 @@ OperationParameter::create(const util::PropertyMap &properties) {
//! @cond Doxygen_Suppress
bool OperationParameter::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOP = dynamic_cast<const OperationParameter *>(other);
if (otherOP == nullptr) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
- return IdentifiedObject::_isEquivalentTo(other, criterion);
+ return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
}
- if (IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ if (IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return true;
}
auto l_epsgCode = getEPSGCode();
@@ -1761,19 +1762,20 @@ static SingleOperationNNPtr createPROJBased(
//! @cond Doxygen_Suppress
bool SingleOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
- return _isEquivalentTo(other, criterion, false);
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
+ return _isEquivalentTo(other, criterion, dbContext, false);
}
bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext,
bool inOtherDirection) const {
auto otherSO = dynamic_cast<const SingleOperation *>(other);
if (otherSO == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
@@ -1783,7 +1785,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
bool equivalentMethods =
(criterion == util::IComparable::Criterion::EQUIVALENT &&
methodEPSGCode != 0 && methodEPSGCode == otherMethodEPSGCode) ||
- d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion);
+ d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion,
+ dbContext);
if (!equivalentMethods &&
criterion == util::IComparable::Criterion::EQUIVALENT) {
if ((methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA &&
@@ -1856,7 +1859,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) {
// Convert from 2SP to 1SP as the other direction has more
// degree of liberties.
- return otherSO->_isEquivalentTo(this, criterion);
+ return otherSO->_isEquivalentTo(this, criterion, dbContext);
} else if ((methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A &&
otherMethodEPSGCode ==
EPSG_CODE_METHOD_MERCATOR_VARIANT_B) ||
@@ -1872,7 +1875,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
auto eqConv =
conv->convertToOtherMethod(otherMethodEPSGCode);
if (eqConv) {
- return eqConv->_isEquivalentTo(other, criterion);
+ return eqConv->_isEquivalentTo(other, criterion,
+ dbContext);
}
}
}
@@ -1890,7 +1894,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
return false;
}
for (size_t i = 0; i < valuesSize; i++) {
- if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion)) {
+ if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -1911,7 +1916,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
bool sameNameDifferentValue = false;
for (size_t j = 0; j < otherValuesSize; j++) {
if (candidateIndices[j] &&
- values[i]->_isEquivalentTo(otherValues[j].get(), criterion)) {
+ values[i]->_isEquivalentTo(otherValues[j].get(), criterion,
+ dbContext)) {
candidateIndices[j] = false;
equivalent = true;
break;
@@ -1923,7 +1929,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
return false;
sameNameDifferentValue =
opParamvalue->parameter()->_isEquivalentTo(
- otherOpParamvalue->parameter().get(), criterion);
+ otherOpParamvalue->parameter().get(), criterion,
+ dbContext);
if (sameNameDifferentValue) {
candidateIndices[j] = false;
break;
@@ -1951,13 +1958,13 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
->parameterValue(
EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL)
.get(),
- criterion) &&
+ criterion, dbContext) &&
value_2nd->_isEquivalentTo(
otherSO
->parameterValue(
EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL)
.get(),
- criterion);
+ criterion, dbContext);
}
}
}
@@ -1989,7 +1996,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
// In the case the arguments don't perfectly match, try the reverse
// check.
if (equivalent && foundMissingArgs && !inOtherDirection) {
- return otherSO->_isEquivalentTo(this, criterion, true);
+ return otherSO->_isEquivalentTo(this, criterion, dbContext, true);
}
// Equivalent formulations of 2SP can have different parameters
@@ -2004,8 +2011,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
auto otherAs1SP = otherConv->convertToOtherMethod(
EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
if (thisAs1SP && otherAs1SP) {
- equivalent =
- thisAs1SP->_isEquivalentTo(otherAs1SP.get(), criterion);
+ equivalent = thisAs1SP->_isEquivalentTo(otherAs1SP.get(),
+ criterion, dbContext);
}
}
}
@@ -2415,9 +2422,9 @@ void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
-bool ParameterValue::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+bool ParameterValue::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &) const {
auto otherPV = dynamic_cast<const ParameterValue *>(other);
if (otherPV == nullptr) {
return false;
@@ -8763,7 +8770,8 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames(
}
}
- if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON) {
+ if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON ||
+ methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) {
auto fileParameter =
parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE,
EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE);
@@ -9437,6 +9445,19 @@ void Transformation::_exportToPROJString(
}
}
+ if (methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) {
+ auto fileParameter =
+ parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE,
+ EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE);
+ if (fileParameter &&
+ fileParameter->type() == ParameterValue::Type::FILENAME) {
+ formatter->addStep("vgridshift");
+ formatter->addParam("grids", fileParameter->valueFile());
+ formatter->addParam("multiplier", 1.0);
+ return;
+ }
+ }
+
if (isLongitudeRotation()) {
double offsetDeg =
parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET,
@@ -10189,10 +10210,12 @@ void ConcatenatedOperation::_exportToPROJString(
//! @cond Doxygen_Suppress
bool ConcatenatedOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCO = dynamic_cast<const ConcatenatedOperation *>(other);
- if (otherCO == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ if (otherCO == nullptr ||
+ (criterion == util::IComparable::Criterion::STRICT &&
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &steps = operations();
@@ -10201,7 +10224,8 @@ bool ConcatenatedOperation::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < steps.size(); i++) {
- if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion)) {
+ if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -11313,9 +11337,17 @@ struct FilterResults {
setOfSetOfGrids.end()) {
continue;
}
+
+ const bool sameNameOrEmptyName =
+ ((!curExtent && !lastExtent) ||
+ (curExtent && lastExtent &&
+ !curExtent->description()->empty() &&
+ *(curExtent->description()) ==
+ *(lastExtent->description())));
+
// If we have already found a operation without grids for
// that extent, no need to add any lower accuracy operation
- if (!lastHasGrids) {
+ if (!lastHasGrids && sameNameOrEmptyName) {
continue;
}
// If we had only operations involving grids, but one
@@ -12613,6 +12645,18 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
// Start in priority with candidates that have exactly the same name as
// the sourcCRS and targetCRS. Typically for the case of init=IGNF:XXXX
+
+ // Transformation from IGNF:NTFP to IGNF:RGF93G,
+ // using
+ // NTF geographiques Paris (gr) vers NTF GEOGRAPHIQUES GREENWICH (DMS) +
+ // NOUVELLE TRIANGULATION DE LA FRANCE (NTF) vers RGF93 (ETRS89)
+ // that is using ntf_r93.gsb, is horribly dependent
+ // of IGNF:RGF93G being returned before IGNF:RGF93GEO in candidatesDstGeod.
+ // If RGF93GEO is returned before then we go through WGS84 and use
+ // instead a Helmert transformation.
+ // The below logic is thus quite fragile, and attempts at changing it
+ // result in degraded results for other use cases...
+
for (const auto &candidateSrcGeod : candidatesSrcGeod) {
if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) {
for (const auto &candidateDstGeod : candidatesDstGeod) {
@@ -12666,7 +12710,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
#endif
createTransformations(candidateSrcGeod, candidateDstGeod,
opsFirst[0], isNullFirst);
- if (!res.empty() && hasResultSetOnlyResultsWithPROJStep(res)) {
+ if (!res.empty() && !hasResultSetOnlyResultsWithPROJStep(res)) {
return;
}
}
@@ -14870,14 +14914,15 @@ void InverseCoordinateOperation::_exportToPROJString(
// ---------------------------------------------------------------------------
bool InverseCoordinateOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherICO = dynamic_cast<const InverseCoordinateOperation *>(other);
if (otherICO == nullptr ||
- !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
- return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion);
+ return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp
index 6769b486..fc4b7492 100644
--- a/src/iso19111/coordinatesystem.cpp
+++ b/src/iso19111/coordinatesystem.cpp
@@ -429,8 +429,8 @@ void CoordinateSystemAxis::_exportToJSON(
//! @cond Doxygen_Suppress
bool CoordinateSystemAxis::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCSA = dynamic_cast<const CoordinateSystemAxis *>(other);
if (otherCSA == nullptr) {
return false;
@@ -441,7 +441,7 @@ bool CoordinateSystemAxis::_isEquivalentTo(
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
- if (!IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if (abbreviation() != otherCSA->abbreviation()) {
@@ -581,11 +581,13 @@ void CoordinateSystem::_exportToJSON(
writer.Add(getWKT2Type(true));
writer.AddObjKey("axis");
- auto axisContext(writer.MakeArrayContext(false));
- const auto &l_axisList = axisList();
- for (auto &axis : l_axisList) {
- formatter->setOmitTypeInImmediateChild();
- axis->_exportToJSON(formatter);
+ {
+ auto axisContext(writer.MakeArrayContext(false));
+ const auto &l_axisList = axisList();
+ for (auto &axis : l_axisList) {
+ formatter->setOmitTypeInImmediateChild();
+ axis->_exportToJSON(formatter);
+ }
}
if (formatter->outputId()) {
@@ -599,11 +601,11 @@ void CoordinateSystem::_exportToJSON(
//! @cond Doxygen_Suppress
bool CoordinateSystem::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCS = dynamic_cast<const CoordinateSystem *>(other);
if (otherCS == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
const auto &list = axisList();
@@ -615,7 +617,8 @@ bool CoordinateSystem::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < list.size(); i++) {
- if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion)) {
+ if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion,
+ dbContext)) {
return false;
}
}
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 49cc050f..d71d527e 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -49,6 +49,7 @@
#include <algorithm>
#include <cassert>
+#include <cmath>
#include <cstring>
#include <iostream>
#include <memory>
@@ -390,7 +391,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
if (boundCRS) {
if (boundCRS->hubCRS()->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return NN_NO_CHECK(boundCRS);
}
}
@@ -399,16 +400,16 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
auto geogCRS = extractGeographicCRS();
auto hubCRS = util::nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326);
if (geodCRS && !geogCRS) {
- if (geodCRS->_isEquivalentTo(
- GeographicCRS::EPSG_4978.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
return thisAsCRS;
}
hubCRS = util::nn_static_pointer_cast<CRS>(GeodeticCRS::EPSG_4978);
} else if (!geogCRS ||
geogCRS->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return thisAsCRS;
} else {
geodCRS = geogCRS;
@@ -595,12 +596,44 @@ CRSNNPtr CRS::alterId(const std::string &authName,
//! @cond Doxygen_Suppress
-static bool isAxisListNorthEast(
+static bool mustAxisOrderBeSwitchedForVisualizationInternal(
const std::vector<cs::CoordinateSystemAxisNNPtr> &axisList) {
const auto &dir0 = axisList[0]->direction();
const auto &dir1 = axisList[1]->direction();
- return (&dir0 == &cs::AxisDirection::NORTH &&
- &dir1 == &cs::AxisDirection::EAST);
+ if (&dir0 == &cs::AxisDirection::NORTH &&
+ &dir1 == &cs::AxisDirection::EAST) {
+ return true;
+ }
+
+ // Address EPSG:32661 "WGS 84 / UPS North (N,E)"
+ if (&dir0 == &cs::AxisDirection::SOUTH &&
+ &dir1 == &cs::AxisDirection::SOUTH) {
+ const auto &meridian0 = axisList[0]->meridian();
+ const auto &meridian1 = axisList[1]->meridian();
+ return meridian0 != nullptr && meridian1 != nullptr &&
+ std::abs(meridian0->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 180.0) < 1e-10 &&
+ std::abs(meridian1->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 90.0) < 1e-10;
+ }
+
+ // Address EPSG:32761 "WGS 84 / UPS South (N,E)"
+ if (&dir0 == &cs::AxisDirection::NORTH &&
+ &dir1 == &cs::AxisDirection::NORTH) {
+ const auto &meridian0 = axisList[0]->meridian();
+ const auto &meridian1 = axisList[1]->meridian();
+ return meridian0 != nullptr && meridian1 != nullptr &&
+ std::abs(meridian0->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 0.0) < 1e-10 &&
+ std::abs(meridian1->longitude().convertToUnit(
+ common::UnitOfMeasure::DEGREE) -
+ 90.0) < 1e-10;
+ }
+
+ return false;
}
// ---------------------------------------------------------------------------
@@ -616,12 +649,14 @@ bool CRS::mustAxisOrderBeSwitchedForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
- return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ geogCRS->coordinateSystem()->axisList());
}
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
- return isAxisListNorthEast(projCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ projCRS->coordinateSystem()->axisList());
}
return false;
@@ -655,7 +690,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs = axisList.size() == 2
? cs::EllipsoidalCS::create(util::PropertyMap(),
axisList[1], axisList[0])
@@ -670,7 +705,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
const auto &axisList = projCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs =
axisList.size() == 2
? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
@@ -695,6 +730,10 @@ CRSNNPtr CRS::normalizeForVisualization() const {
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -987,18 +1026,19 @@ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
bool SingleCRS::baseIsEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherSingleCRS = dynamic_cast<const SingleCRS *>(other);
if (otherSingleCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &thisDatum = d->datum;
const auto &otherDatum = otherSingleCRS->d->datum;
if (thisDatum) {
- if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion)) {
+ if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion,
+ dbContext)) {
return false;
}
} else {
@@ -1009,7 +1049,8 @@ bool SingleCRS::baseIsEquivalentTo(
// TODO test DatumEnsemble
return d->coordinateSystem->_isEquivalentTo(
- otherSingleCRS->d->coordinateSystem.get(), criterion) &&
+ otherSingleCRS->d->coordinateSystem.get(), criterion,
+ dbContext) &&
getExtensionProj4() == otherSingleCRS->getExtensionProj4();
}
@@ -1544,13 +1585,13 @@ getStandardCriterion(util::IComparable::Criterion criterion) {
//! @cond Doxygen_Suppress
bool GeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
const auto standardCriterion = getStandardCriterion(criterion);
auto otherGeodCRS = dynamic_cast<const GeodeticCRS *>(other);
// TODO test velocityModel
return otherGeodCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, standardCriterion);
+ SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext);
}
//! @endcond
@@ -1611,6 +1652,10 @@ static bool hasCodeCompatibleOfAuthorityFactory(
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match:
* <ul>
@@ -1651,6 +1696,9 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
std::list<Pair> res;
const auto &thisName(nameStr());
+ io::DatabaseContextPtr dbContext =
+ authorityFactory ? authorityFactory->databaseContext().as_nullable()
+ : nullptr;
const bool l_implicitCS = CRS::getPrivate()->implicitCS_;
const auto crsCriterion =
l_implicitCS
@@ -1664,7 +1712,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const bool nameEquivalent = metadata::Identifier::isEquivalentName(
thisName.c_str(), crs->nameStr().c_str());
const bool nameEqual = thisName == crs->nameStr();
- const bool isEq = _isEquivalentTo(crs.get(), crsCriterion);
+ const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext);
if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) {
res.emplace_back(util::nn_static_pointer_cast<GeodeticCRS>(crs),
nameEqual ? 100 : 90);
@@ -1699,13 +1747,14 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisDatum(datum());
auto searchByDatum = [this, &authorityFactory, &res, &thisDatum,
- &geodetic_crs_type, crsCriterion]() {
+ &geodetic_crs_type, crsCriterion, &dbContext]() {
for (const auto &id : thisDatum->identifiers()) {
try {
auto tempRes = authorityFactory->createGeodeticCRSFromDatum(
*id->codeSpace(), id->code(), geodetic_crs_type);
for (const auto &crs : tempRes) {
- if (_isEquivalentTo(crs.get(), crsCriterion)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion,
+ dbContext)) {
res.emplace_back(crs, 70);
}
}
@@ -1717,7 +1766,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisEllipsoid(ellipsoid());
auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum,
&thisEllipsoid, &geodetic_crs_type,
- l_implicitCS]() {
+ l_implicitCS, &dbContext]() {
const auto ellipsoids =
thisEllipsoid->identifiers().empty()
? authorityFactory->createEllipsoidFromExisting(
@@ -1735,15 +1784,17 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (crsDatum &&
crsDatum->ellipsoid()->_isEquivalentTo(
ellps.get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
crsDatum->primeMeridian()->_isEquivalentTo(
thisDatum->primeMeridian().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
(!l_implicitCS ||
coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::
- EQUIVALENT))) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext))) {
res.emplace_back(crs, 60);
}
}
@@ -1776,7 +1827,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
authorityFactory->databaseContext(),
*id->codeSpace())
->createGeodeticCRS(id->code());
- bool match = _isEquivalentTo(crs.get(), crsCriterion);
+ bool match =
+ _isEquivalentTo(crs.get(), crsCriterion, dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -1794,7 +1846,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
auto crs = util::nn_dynamic_pointer_cast<GeodeticCRS>(obj);
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
- if (_isEquivalentTo(crs.get(), crsCriterion)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -1824,8 +1876,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisCS(coordinateSystem());
// Sort results
- res.sort([&thisName, &thisDatum, &thisCS](const Pair &a,
- const Pair &b) {
+ res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a,
+ const Pair &b) {
// First consider confidence
if (a.second > b.second) {
return true;
@@ -1849,9 +1901,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &bDatum(b.first->datum());
if (thisDatum && aDatum && bDatum) {
const auto thisEquivADatum(thisDatum->_isEquivalentTo(
- aDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ aDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBDatum(thisDatum->_isEquivalentTo(
- bDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ bDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivADatum && !thisEquivBDatum) {
return true;
@@ -1865,9 +1919,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &aCS(a.first->coordinateSystem());
const auto &bCS(b.first->coordinateSystem());
const auto thisEquivACs(thisCS->_isEquivalentTo(
- aCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ aCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBCs(thisCS->_isEquivalentTo(
- bCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ bCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivACs && !thisEquivBCs) {
return true;
}
@@ -2084,14 +2140,14 @@ bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other)
//! @cond Doxygen_Suppress
bool GeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherGeogCRS = dynamic_cast<const GeographicCRS *>(other);
if (otherGeogCRS == nullptr) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
- if (GeodeticCRS::_isEquivalentTo(other, standardCriterion)) {
+ if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) {
return true;
}
if (criterion !=
@@ -2110,7 +2166,7 @@ bool GeographicCRS::_isEquivalentTo(
cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH
? cs::EllipsoidalCS::createLatitudeLongitude(unit)
: cs::EllipsoidalCS::createLongitudeLatitude(unit))
- ->GeodeticCRS::_isEquivalentTo(other, standardCriterion);
+ ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext);
}
return false;
}
@@ -2682,12 +2738,12 @@ VerticalCRS::create(const util::PropertyMap &properties,
//! @cond Doxygen_Suppress
bool VerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherVertCRS = dynamic_cast<const VerticalCRS *>(other);
// TODO test geoidModel and velocityModel
return otherVertCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
//! @endcond
@@ -2698,6 +2754,10 @@ bool VerticalCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match.
* 100% means that the name of the reference entry
@@ -2722,6 +2782,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -2733,12 +2795,11 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createVerticalCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -2756,8 +2817,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -2922,19 +2983,20 @@ DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
-bool DerivedCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+bool DerivedCRS::_isEquivalentTo(
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRS *>(other);
const auto standardCriterion = getStandardCriterion(criterion);
if (otherDerivedCRS == nullptr ||
- !SingleCRS::baseIsEquivalentTo(other, standardCriterion)) {
+ !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) {
return false;
}
return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->derivingConversion_->_isEquivalentTo(
- otherDerivedCRS->d->derivingConversion_.get(),
- standardCriterion);
+ otherDerivedCRS->d->derivingConversion_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -3367,11 +3429,11 @@ ProjectedCRS::create(const util::PropertyMap &properties,
// ---------------------------------------------------------------------------
bool ProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherProjCRS = dynamic_cast<const ProjectedCRS *>(other);
return otherProjCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -3394,23 +3456,33 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
bool axisSpecFound) const {
const auto &axisList = d->coordinateSystem()->axisList();
const auto &unit = axisList[0]->unit();
+ const auto *zUnit = axisList.size() == 3 ? &(axisList[2]->unit()) : nullptr;
if (!unit._isEquivalentTo(common::UnitOfMeasure::METRE,
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT) ||
+ (zUnit &&
+ !zUnit->_isEquivalentTo(common::UnitOfMeasure::METRE,
+ util::IComparable::Criterion::EQUIVALENT))) {
auto projUnit = unit.exportToPROJString();
const double toSI = unit.conversionToSI();
if (!formatter->getCRSExport()) {
formatter->addStep("unitconvert");
formatter->addParam("xy_in", "m");
- if (!formatter->omitZUnitConversion())
+ if (zUnit)
formatter->addParam("z_in", "m");
+
if (projUnit.empty()) {
formatter->addParam("xy_out", toSI);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_out", toSI);
} else {
formatter->addParam("xy_out", projUnit);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_out", projUnit);
+ }
+ if (zUnit) {
+ auto projZUnit = zUnit->exportToPROJString();
+ const double zToSI = zUnit->conversionToSI();
+ if (projZUnit.empty()) {
+ formatter->addParam("z_out", zToSI);
+ } else {
+ formatter->addParam("z_out", projZUnit);
+ }
}
} else {
if (projUnit.empty()) {
@@ -3479,6 +3551,10 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -3545,14 +3621,19 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &conv = derivingConversionRef();
const auto &cs = coordinateSystem();
+ io::DatabaseContextPtr dbContext =
+ authorityFactory ? authorityFactory->databaseContext().as_nullable()
+ : nullptr;
+
if (baseRes.size() == 1 && baseRes.front().second >= 70 &&
conv->isUTM(zone, north) &&
cs->_isEquivalentTo(
cs::CartesianCS::createEastingNorthing(common::UnitOfMeasure::METRE)
- .get())) {
+ .get(),
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
if (baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("WGS 84 / UTM zone ", zone, north));
res.emplace_back(
@@ -3566,7 +3647,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4267.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD27 / UTM zone ", zone, north));
res.emplace_back(
@@ -3581,7 +3662,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4269.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD83 / UTM zone ", zone, north));
res.emplace_back(
@@ -3613,9 +3694,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
*id->codeSpace())
->createProjectedCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -3636,9 +3717,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3650,10 +3731,12 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
l_baseCRS->_isEquivalentTo(
crs->baseCRS().get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) &&
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
objects.size() == 1) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3720,17 +3803,21 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (_isEquivalentTo(crs.get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext)) {
res.emplace_back(crs, unsignificantName ? 90 : 70);
} else if (ellipsoid->_isEquivalentTo(
crs->baseCRS()->ellipsoid().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, 70);
} else {
res.emplace_back(crs, 50);
@@ -3960,12 +4047,12 @@ void CompoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool CompoundCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCompoundCRS = dynamic_cast<const CompoundCRS *>(other);
if (otherCompoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &components = componentReferenceSystems();
@@ -3974,8 +4061,8 @@ bool CompoundCRS::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < components.size(); i++) {
- if (!components[i]->_isEquivalentTo(otherComponents[i].get(),
- criterion)) {
+ if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -3989,6 +4076,10 @@ bool CompoundCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match. The list is sorted by decreasing
* confidence.
@@ -4015,6 +4106,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -4028,12 +4121,11 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createCompoundCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -4054,8 +4146,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -4122,7 +4214,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
if (_isEquivalentTo(crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, unsignificantName ? 90 : 70);
} else {
res.emplace_back(crs, 25);
@@ -4494,20 +4587,22 @@ void BoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool BoundCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherBoundCRS = dynamic_cast<const BoundCRS *>(other);
if (otherBoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->transformation_->_isEquivalentTo(
- otherBoundCRS->d->transformation_.get(), standardCriterion);
+ otherBoundCRS->d->transformation_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -4518,10 +4613,36 @@ std::list<std::pair<CRSNNPtr, int>>
BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
typedef std::pair<CRSNNPtr, int> Pair;
std::list<Pair> res;
- if (authorityFactory &&
- d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (!authorityFactory)
+ return res;
+ std::list<Pair> resMatchOfTransfToWGS84;
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
+ if (d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
auto resTemp = d->baseCRS_->identify(authorityFactory);
+
+ std::string refTransfPROJString;
+ bool refTransfPROJStringValid = false;
+ auto refTransf = d->transformation_->normalizeForVisualization();
+ try {
+ refTransfPROJString = refTransf->exportToPROJString(
+ io::PROJStringFormatter::create().get());
+ refTransfPROJString = replaceAll(
+ refTransfPROJString,
+ " +rx=0 +ry=0 +rz=0 +s=0 +convention=position_vector", "");
+ refTransfPROJStringValid = true;
+ } catch (const std::exception &) {
+ }
+ bool refIsNullTransform = false;
+ if (isTOWGS84Compatible()) {
+ auto params = transformation()->getTOWGS84Parameters();
+ if (params == std::vector<double>{0, 0, 0, 0, 0, 0, 0}) {
+ refIsNullTransform = true;
+ }
+ }
+
for (const auto &pair : resTemp) {
const auto &candidateBaseCRS = pair.first;
auto projCRS =
@@ -4532,54 +4653,47 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (geodCRS) {
auto context = operation::CoordinateOperationContext::create(
authorityFactory, nullptr, 0.0);
+ context->setSpatialCriterion(
+ operation::CoordinateOperationContext::SpatialCriterion::
+ PARTIAL_INTERSECTION);
auto ops =
operation::CoordinateOperationFactory::create()
->createOperations(NN_NO_CHECK(geodCRS),
GeographicCRS::EPSG_4326, context);
- std::string refTransfPROJString;
- bool refTransfPROJStringValid = false;
- try {
- refTransfPROJString =
- d->transformation_->exportToPROJString(
- io::PROJStringFormatter::create().get());
- refTransfPROJStringValid = true;
- if (refTransfPROJString == "+proj=axisswap +order=2,1") {
- refTransfPROJString.clear();
- }
- } catch (const std::exception &) {
- }
+
bool foundOp = false;
for (const auto &op : ops) {
+ auto opNormalized = op->normalizeForVisualization();
std::string opTransfPROJString;
bool opTransfPROJStringValid = false;
if (op->nameStr().find("Ballpark geographic") == 0) {
- if (isTOWGS84Compatible()) {
- auto params =
- transformation()->getTOWGS84Parameters();
- if (params ==
- std::vector<double>{0, 0, 0, 0, 0, 0, 0}) {
- res.emplace_back(create(candidateBaseCRS,
- d->hubCRS_,
- transformation()),
- pair.second);
- foundOp = true;
- break;
- }
+ if (refIsNullTransform) {
+ res.emplace_back(create(candidateBaseCRS,
+ d->hubCRS_,
+ transformation()),
+ pair.second);
+ foundOp = true;
+ break;
}
continue;
}
try {
- opTransfPROJString = op->exportToPROJString(
+ opTransfPROJString = opNormalized->exportToPROJString(
io::PROJStringFormatter::create().get());
opTransfPROJStringValid = true;
+ opTransfPROJString = replaceAll(
+ opTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 "
+ "+convention=position_vector",
+ "");
} catch (const std::exception &) {
}
if ((refTransfPROJStringValid && opTransfPROJStringValid &&
refTransfPROJString == opTransfPROJString) ||
- op->_isEquivalentTo(
- d->transformation_.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
- res.emplace_back(
+ opNormalized->_isEquivalentTo(
+ refTransf.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
+ resMatchOfTransfToWGS84.emplace_back(
create(candidateBaseCRS, d->hubCRS_,
NN_NO_CHECK(util::nn_dynamic_pointer_cast<
operation::Transformation>(op))),
@@ -4596,7 +4710,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
}
}
- return res;
+ return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res;
}
// ---------------------------------------------------------------------------
@@ -4758,11 +4872,11 @@ void DerivedGeodeticCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeodeticCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -4908,11 +5022,11 @@ void DerivedGeographicCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeographicCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5055,11 +5169,11 @@ void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
bool DerivedProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedProjectedCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5185,11 +5299,11 @@ void TemporalCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool TemporalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTemporalCRS = dynamic_cast<const TemporalCRS *>(other);
return otherTemporalCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5323,11 +5437,11 @@ void EngineeringCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool EngineeringCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherEngineeringCRS = dynamic_cast<const EngineeringCRS *>(other);
return otherEngineeringCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5455,11 +5569,11 @@ void ParametricCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool ParametricCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherParametricCRS = dynamic_cast<const ParametricCRS *>(other);
return otherParametricCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5560,11 +5674,11 @@ void DerivedVerticalCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedVerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedVerticalCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5681,11 +5795,11 @@ void DerivedCRSTemplate<DerivedCRSTraits>::_exportToWKT(
template <class DerivedCRSTraits>
bool DerivedCRSTemplate<DerivedCRSTraits>::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRSTemplate *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index 321fe93f..d9d9c261 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -213,11 +213,13 @@ void Datum::setProperties(
// ---------------------------------------------------------------------------
-bool Datum::__isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+//! @cond Doxygen_Suppress
+bool Datum::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDatum = dynamic_cast<const Datum *>(other);
if (otherDatum == nullptr ||
- !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
@@ -248,12 +250,13 @@ bool Datum::__isEquivalentTo(const util::IComparable *other,
}
if (conventionalRS() && otherDatum->conventionalRS() &&
conventionalRS()->_isEquivalentTo(
- otherDatum->conventionalRS().get(), criterion)) {
+ otherDatum->conventionalRS().get(), criterion, dbContext)) {
return false;
}
}
return true;
}
+//! @endcond
// ---------------------------------------------------------------------------
@@ -450,11 +453,11 @@ void PrimeMeridian::_exportToPROJString(
//! @cond Doxygen_Suppress
bool PrimeMeridian::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherPM = dynamic_cast<const PrimeMeridian *>(other);
if (otherPM == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
// In MapInfo, the Paris prime meridian is returned as 2.3372291666667
@@ -984,11 +987,12 @@ EllipsoidNNPtr Ellipsoid::identify() const {
//! @cond Doxygen_Suppress
bool Ellipsoid::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherEllipsoid = dynamic_cast<const Ellipsoid *>(other);
if (otherEllipsoid == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !IdentifiedObject::_isEquivalentTo(other, criterion))) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
@@ -1197,7 +1201,8 @@ void GeodeticReferenceFrame::_exportToWKT(
io::WKTFormatter *formatter) const // throw(FormattingException)
{
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
- formatter->startNode(io::WKTConstants::DATUM, !identifiers().empty());
+ const auto &ids = identifiers();
+ formatter->startNode(io::WKTConstants::DATUM, !ids.empty());
auto l_name = nameStr();
if (l_name.empty()) {
l_name = "unnamed";
@@ -1232,10 +1237,35 @@ void GeodeticReferenceFrame::_exportToWKT(
}
}
}
- // Replace spaces by underscore, except if it is a special MapInfo
- // datum name
- } else if (!starts_with(l_name, "MIF ")) {
- l_name = io::WKTFormatter::morphNameToESRI(l_name);
+ } else {
+ // Replace spaces by underscore for datum names coming from EPSG
+ // so as to emulate GDAL < 3 importFromEPSG()
+ if (ids.size() == 1 && *(ids.front()->codeSpace()) == "EPSG") {
+ l_name = io::WKTFormatter::morphNameToESRI(l_name);
+ } else if (ids.empty()) {
+ const auto &dbContext = formatter->databaseContext();
+ if (dbContext) {
+ auto factory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), std::string());
+ // We use anonymous autority and approximate matching, so
+ // as to trigger the caching done in createObjectsFromName()
+ // in that case.
+ auto matches = factory->createObjectsFromName(
+ l_name, {io::AuthorityFactory::ObjectType::
+ GEODETIC_REFERENCE_FRAME},
+ true, 2);
+ if (matches.size() == 1) {
+ const auto &match = matches.front();
+ const auto &matchId = match->identifiers();
+ if (matchId.size() == 1 &&
+ *(matchId.front()->codeSpace()) == "EPSG" &&
+ metadata::Identifier::isEquivalentName(
+ l_name.c_str(), match->nameStr().c_str())) {
+ l_name = io::WKTFormatter::morphNameToESRI(l_name);
+ }
+ }
+ }
+ }
if (l_name == "World_Geodetic_System_1984") {
l_name = "WGS_1984";
}
@@ -1324,20 +1354,63 @@ void GeodeticReferenceFrame::_exportToJSON(
//! @cond Doxygen_Suppress
bool GeodeticReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherGRF = dynamic_cast<const GeodeticReferenceFrame *>(other);
- if (otherGRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherGRF == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return primeMeridian()->_isEquivalentTo(otherGRF->primeMeridian().get(),
- criterion) &&
- ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion);
+ criterion, dbContext) &&
+ ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion,
+ dbContext);
}
//! @endcond
// ---------------------------------------------------------------------------
+bool GeodeticReferenceFrame::hasEquivalentNameToUsingAlias(
+ const IdentifiedObject *other,
+ const io::DatabaseContextPtr &dbContext) const {
+ if (dbContext) {
+ if (!identifiers().empty()) {
+ const auto &id = identifiers().front();
+ auto aliases =
+ dbContext->getAliases(*(id->codeSpace()), id->code(), nameStr(),
+ "geodetic_datum", std::string());
+ const char *otherName = other->nameStr().c_str();
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(otherName,
+ alias.c_str())) {
+ return true;
+ }
+ }
+ return false;
+ } else if (!other->identifiers().empty()) {
+ auto otherGRF = dynamic_cast<const GeodeticReferenceFrame *>(other);
+ if (otherGRF) {
+ return otherGRF->hasEquivalentNameToUsingAlias(this, dbContext);
+ }
+ return false;
+ }
+
+ auto aliases =
+ dbContext->getAliases(std::string(), std::string(), nameStr(),
+ "geodetic_datum", std::string());
+ const char *otherName = other->nameStr().c_str();
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(otherName,
+ alias.c_str())) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
struct DynamicGeodeticReferenceFrame::Private {
common::Measure frameReferenceEpoch{};
@@ -1407,11 +1480,11 @@ DynamicGeodeticReferenceFrame::deformationModelName() const {
//! @cond Doxygen_Suppress
bool DynamicGeodeticReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDGRF = dynamic_cast<const DynamicGeodeticReferenceFrame *>(other);
if (otherDGRF == nullptr ||
- !GeodeticReferenceFrame::_isEquivalentTo(other, criterion)) {
+ !GeodeticReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return frameReferenceEpoch()._isEquivalentTo(
@@ -1842,10 +1915,11 @@ void VerticalReferenceFrame::_exportToJSON(
//! @cond Doxygen_Suppress
bool VerticalReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherVRF = dynamic_cast<const VerticalReferenceFrame *>(other);
- if (otherVRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherVRF == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if ((realizationMethod().has_value() ^
@@ -1932,11 +2006,11 @@ DynamicVerticalReferenceFrame::deformationModelName() const {
//! @cond Doxygen_Suppress
bool DynamicVerticalReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDGRF = dynamic_cast<const DynamicVerticalReferenceFrame *>(other);
if (otherDGRF == nullptr ||
- !VerticalReferenceFrame::_isEquivalentTo(other, criterion)) {
+ !VerticalReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return frameReferenceEpoch()._isEquivalentTo(
@@ -2131,10 +2205,11 @@ void TemporalDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool TemporalDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const TemporalDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return temporalOrigin().toString() ==
@@ -2223,10 +2298,11 @@ void EngineeringDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool EngineeringDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const EngineeringDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return true;
@@ -2308,10 +2384,11 @@ void ParametricDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool ParametricDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const ParametricDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return true;
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 96e62c16..7d15172e 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -264,6 +264,9 @@ struct DatabaseContext::Private {
std::map<std::string, std::vector<std::string>> cacheAllowedAuthorities_{};
+ lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{
+ CACHE_SIZE};
+
static void insertIntoCache(LRUCacheOfObjects &cache,
const std::string &code,
const util::BaseObjectPtr &obj);
@@ -1014,7 +1017,7 @@ bool DatabaseContext::isKnownName(const std::string &name,
/** \brief Gets the alias name from an official name.
*
- * @param officialName Official name.
+ * @param officialName Official name. Mandatory
* @param tableName Table name/category. Mandatory
* @param source Source of the alias. Mandatory
* @return Alias name (or empty if not found).
@@ -1046,6 +1049,63 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
// ---------------------------------------------------------------------------
+/** \brief Gets the alias names for an object.
+ *
+ * Either authName + code or officialName must be non empty.
+ *
+ * @param authName Authority.
+ * @param code Code.
+ * @param officialName Official name.
+ * @param tableName Table name/category. Mandatory
+ * @param source Source of the alias. May be empty.
+ * @return Aliases
+ */
+std::list<std::string> DatabaseContext::getAliases(
+ const std::string &authName, const std::string &code,
+ const std::string &officialName, const std::string &tableName,
+ const std::string &source) const {
+
+ std::list<std::string> res;
+ const auto key(authName + code + officialName + tableName + source);
+ if (d->cacheAliasNames_.tryGet(key, res)) {
+ return res;
+ }
+
+ std::string resolvedAuthName(authName);
+ std::string resolvedCode(code);
+ if (authName.empty() || code.empty()) {
+ std::string sql("SELECT auth_name, code FROM \"");
+ sql += replaceAll(tableName, "\"", "\"\"");
+ sql += "\" WHERE name = ?";
+ if (tableName == "geodetic_crs") {
+ sql += " AND type = " GEOG_2D_SINGLE_QUOTED;
+ }
+ auto resSql = d->run(sql, {officialName});
+ if (resSql.empty()) {
+ d->cacheAliasNames_.insert(key, res);
+ return res;
+ }
+ const auto &row = resSql.front();
+ resolvedAuthName = row[0];
+ resolvedCode = row[1];
+ }
+ std::string sql("SELECT alt_name FROM alias_name WHERE table_name = ? AND "
+ "auth_name = ? AND code = ?");
+ ListOfParams params{tableName, resolvedAuthName, resolvedCode};
+ if (!source.empty()) {
+ sql += " AND source = ?";
+ params.emplace_back(source);
+ }
+ auto resSql = d->run(sql, params);
+ for (const auto &row : resSql) {
+ res.emplace_back(row[0]);
+ }
+ d->cacheAliasNames_.insert(key, res);
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Return the 'text_definition' column of a table for an object
*
* @param tableName Table name/category.
@@ -1349,9 +1409,17 @@ AuthorityFactory::AuthorityFactory(const DatabaseContextNNPtr &context,
AuthorityFactoryNNPtr
AuthorityFactory::create(const DatabaseContextNNPtr &context,
const std::string &authorityName) {
-
- auto factory = AuthorityFactory::nn_make_shared<AuthorityFactory>(
- context, authorityName);
+ const auto getFactory = [&context, &authorityName]() {
+ for (const auto &knownName : {"EPSG", "ESRI", "PROJ"}) {
+ if (ci_equal(authorityName, knownName)) {
+ return AuthorityFactory::nn_make_shared<AuthorityFactory>(
+ context, knownName);
+ }
+ }
+ return AuthorityFactory::nn_make_shared<AuthorityFactory>(
+ context, authorityName);
+ };
+ auto factory = getFactory();
factory->d->setThis(factory);
return factory;
}
@@ -5105,13 +5173,12 @@ static void addToListString(std::string &out, const char *in) {
out += in;
}
-static void addToListStringWithOR(std::string &out, const char *in) {
+static void addToListStringWithOR(std::string &out, const std::string &in) {
if (!out.empty()) {
out += " OR ";
}
out += in;
}
-
//! @endcond
// ---------------------------------------------------------------------------
@@ -5148,6 +5215,7 @@ AuthorityFactory::createObjectsFromName(
}
std::string sql(
+ "SELECT table_name, auth_name, code, name, deprecated FROM ("
"SELECT table_name, auth_name, code, name, deprecated FROM object_view "
"WHERE ");
if (deprecated) {
@@ -5163,108 +5231,141 @@ AuthorityFactory::createObjectsFromName(
params.emplace_back(d->authority());
}
- if (allowedObjectTypes.empty()) {
- sql += "table_name IN ("
- "'prime_meridian','ellipsoid','geodetic_datum',"
- "'vertical_datum','geodetic_crs','projected_crs',"
- "'vertical_crs','compound_crs','conversion',"
- "'helmert_transformation','grid_transformation',"
- "'other_transformation','concatenated_operation'"
- ")";
- } else {
- std::string tableNameList;
- std::string otherConditions;
- for (const auto type : allowedObjectTypes) {
- switch (type) {
- case ObjectType::PRIME_MERIDIAN:
- addToListString(tableNameList, "'prime_meridian'");
- break;
- case ObjectType::ELLIPSOID:
- addToListString(tableNameList, "'ellipsoid'");
- break;
- case ObjectType::DATUM:
- addToListString(tableNameList,
- "'geodetic_datum','vertical_datum'");
- break;
- case ObjectType::GEODETIC_REFERENCE_FRAME:
- addToListString(tableNameList, "'geodetic_datum'");
- break;
- case ObjectType::VERTICAL_REFERENCE_FRAME:
- addToListString(tableNameList, "'vertical_datum'");
- break;
- case ObjectType::CRS:
- addToListString(tableNameList, "'geodetic_crs','projected_crs',"
- "'vertical_crs','compound_crs'");
- break;
- case ObjectType::GEODETIC_CRS:
- addToListString(tableNameList, "'geodetic_crs'");
- break;
- case ObjectType::GEOCENTRIC_CRS:
- addToListStringWithOR(otherConditions,
- "(table_name = " GEOCENTRIC_SINGLE_QUOTED
- " AND "
- "type = " GEOCENTRIC_SINGLE_QUOTED ")");
- break;
- case ObjectType::GEOGRAPHIC_CRS:
- addToListStringWithOR(otherConditions,
- "(table_name = 'geodetic_crs' AND "
- "type IN (" GEOG_2D_SINGLE_QUOTED
- "," GEOG_3D_SINGLE_QUOTED "))");
- break;
- case ObjectType::GEOGRAPHIC_2D_CRS:
- addToListStringWithOR(otherConditions,
- "(table_name = 'geodetic_crs' AND "
- "type = " GEOG_2D_SINGLE_QUOTED ")");
- break;
- case ObjectType::GEOGRAPHIC_3D_CRS:
- addToListStringWithOR(otherConditions,
- "(table_name = 'geodetic_crs' AND "
- "type = " GEOG_3D_SINGLE_QUOTED ")");
- break;
- case ObjectType::PROJECTED_CRS:
- addToListString(tableNameList, "'projected_crs'");
- break;
- case ObjectType::VERTICAL_CRS:
- addToListString(tableNameList, "'vertical_crs'");
- break;
- case ObjectType::COMPOUND_CRS:
- addToListString(tableNameList, "'compound_crs'");
- break;
- case ObjectType::COORDINATE_OPERATION:
- addToListString(tableNameList,
- "'conversion','helmert_transformation',"
- "'grid_transformation','other_transformation',"
- "'concatenated_operation'");
- break;
- case ObjectType::CONVERSION:
- addToListString(tableNameList, "'conversion'");
- break;
- case ObjectType::TRANSFORMATION:
- addToListString(tableNameList,
- "'helmert_transformation',"
- "'grid_transformation','other_transformation'");
- break;
- case ObjectType::CONCATENATED_OPERATION:
- addToListString(tableNameList, "'concatenated_operation'");
- break;
+ const auto getTableNameConstraint = [&allowedObjectTypes](
+ const std::string &colName) {
+ if (allowedObjectTypes.empty()) {
+ return colName + " IN ("
+ "'prime_meridian','ellipsoid','geodetic_datum',"
+ "'vertical_datum','geodetic_crs','projected_crs',"
+ "'vertical_crs','compound_crs','conversion',"
+ "'helmert_transformation','grid_transformation',"
+ "'other_transformation','concatenated_operation'"
+ ")";
+ } else {
+ std::string tableNameList;
+ std::string otherConditions;
+ for (const auto type : allowedObjectTypes) {
+ switch (type) {
+ case ObjectType::PRIME_MERIDIAN:
+ addToListString(tableNameList, "'prime_meridian'");
+ break;
+ case ObjectType::ELLIPSOID:
+ addToListString(tableNameList, "'ellipsoid'");
+ break;
+ case ObjectType::DATUM:
+ addToListString(tableNameList,
+ "'geodetic_datum','vertical_datum'");
+ break;
+ case ObjectType::GEODETIC_REFERENCE_FRAME:
+ addToListString(tableNameList, "'geodetic_datum'");
+ break;
+ case ObjectType::VERTICAL_REFERENCE_FRAME:
+ addToListString(tableNameList, "'vertical_datum'");
+ break;
+ case ObjectType::CRS:
+ addToListString(tableNameList,
+ "'geodetic_crs','projected_crs',"
+ "'vertical_crs','compound_crs'");
+ break;
+ case ObjectType::GEODETIC_CRS:
+ addToListString(tableNameList, "'geodetic_crs'");
+ break;
+ case ObjectType::GEOCENTRIC_CRS:
+ addToListStringWithOR(
+ otherConditions,
+ "(" + colName + " = " GEOCENTRIC_SINGLE_QUOTED " AND "
+ "type = " GEOCENTRIC_SINGLE_QUOTED ")");
+ break;
+ case ObjectType::GEOGRAPHIC_CRS:
+ addToListStringWithOR(otherConditions,
+ "(" + colName +
+ " = 'geodetic_crs' AND "
+ "type IN (" GEOG_2D_SINGLE_QUOTED
+ "," GEOG_3D_SINGLE_QUOTED "))");
+ break;
+ case ObjectType::GEOGRAPHIC_2D_CRS:
+ addToListStringWithOR(
+ otherConditions,
+ "(" + colName + " = 'geodetic_crs' AND "
+ "type = " GEOG_2D_SINGLE_QUOTED ")");
+ break;
+ case ObjectType::GEOGRAPHIC_3D_CRS:
+ addToListStringWithOR(
+ otherConditions,
+ "(" + colName + " = 'geodetic_crs' AND "
+ "type = " GEOG_3D_SINGLE_QUOTED ")");
+ break;
+ case ObjectType::PROJECTED_CRS:
+ addToListString(tableNameList, "'projected_crs'");
+ break;
+ case ObjectType::VERTICAL_CRS:
+ addToListString(tableNameList, "'vertical_crs'");
+ break;
+ case ObjectType::COMPOUND_CRS:
+ addToListString(tableNameList, "'compound_crs'");
+ break;
+ case ObjectType::COORDINATE_OPERATION:
+ addToListString(
+ tableNameList,
+ "'conversion','helmert_transformation',"
+ "'grid_transformation','other_transformation',"
+ "'concatenated_operation'");
+ break;
+ case ObjectType::CONVERSION:
+ addToListString(tableNameList, "'conversion'");
+ break;
+ case ObjectType::TRANSFORMATION:
+ addToListString(
+ tableNameList,
+ "'helmert_transformation',"
+ "'grid_transformation','other_transformation'");
+ break;
+ case ObjectType::CONCATENATED_OPERATION:
+ addToListString(tableNameList, "'concatenated_operation'");
+ break;
+ }
}
- }
- if (!tableNameList.empty()) {
- sql += "((table_name IN (";
- sql += tableNameList;
- sql += "))";
- if (!otherConditions.empty()) {
- sql += " OR ";
- sql += otherConditions;
+ std::string l_sql;
+ if (!tableNameList.empty()) {
+ l_sql = "((" + colName + " IN (";
+ l_sql += tableNameList;
+ l_sql += "))";
+ if (!otherConditions.empty()) {
+ l_sql += " OR ";
+ l_sql += otherConditions;
+ }
+ l_sql += ')';
+ } else if (!otherConditions.empty()) {
+ l_sql = "(";
+ l_sql += otherConditions;
+ l_sql += ')';
}
- sql += ')';
- } else if (!otherConditions.empty()) {
- sql += "(";
- sql += otherConditions;
- sql += ')';
+ return l_sql;
}
+ };
+
+ sql += getTableNameConstraint("table_name");
+
+ sql += " UNION SELECT ov.table_name AS table_name, "
+ "ov.auth_name AS auth_name, "
+ "ov.code AS code, a.alt_name AS name, "
+ "ov.deprecated AS deprecated FROM object_view ov "
+ "JOIN alias_name a ON ov.table_name = a.table_name AND "
+ "ov.auth_name = a.auth_name AND ov.code = a.code WHERE ";
+ if (deprecated) {
+ sql += "ov.deprecated = 1 AND ";
}
- sql += " ORDER BY deprecated, length(name), name";
+ if (!approximateMatch) {
+ sql += "a.alt_name LIKE ? AND ";
+ params.push_back(searchedNameWithoutDeprecated);
+ }
+ if (d->hasAuthorityRestriction()) {
+ sql += "ov.auth_name = ? AND ";
+ params.emplace_back(d->authority());
+ }
+ sql += getTableNameConstraint("ov.table_name");
+
+ sql += ") ORDER BY deprecated, length(name), name";
if (limitResultCount > 0 &&
limitResultCount <
static_cast<size_t>(std::numeric_limits<int>::max()) &&
@@ -5274,6 +5375,7 @@ AuthorityFactory::createObjectsFromName(
}
std::list<common::IdentifiedObjectNNPtr> res;
+ std::set<std::pair<std::string, std::string>> setIdentified;
// Querying geodetic datum is a super hot path when importing from WKT1
// so cache results.
@@ -5301,6 +5403,12 @@ AuthorityFactory::createObjectsFromName(
for (const auto &row : listOfRow) {
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
auto factory = d->createFactory(auth_name);
res.emplace_back(factory->createGeodeticDatum(code));
if (limitResultCount > 0 && res.size() == limitResultCount) {
@@ -5326,6 +5434,12 @@ AuthorityFactory::createObjectsFromName(
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
auto factory = d->createFactory(auth_name);
res.emplace_back(factory->createGeodeticDatum(code));
if (limitResultCount > 0 &&
@@ -5361,6 +5475,12 @@ AuthorityFactory::createObjectsFromName(
const auto &table_name = row[0];
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
const auto &deprecatedStr = row[4];
if (isFirst) {
firstIsDeprecated = deprecatedStr == "1";
@@ -5526,6 +5646,7 @@ std::list<crs::GeodeticCRSNNPtr> AuthorityFactory::createGeodeticCRSFromDatum(
sql += " AND type = ?";
params.emplace_back(geodetic_crs_type);
}
+ sql += " ORDER BY auth_name, code";
auto sqlRes = d->run(sql, params);
std::list<crs::GeodeticCRSNNPtr> res;
for (const auto &row : sqlRes) {
@@ -5607,7 +5728,9 @@ static std::string buildSqlLookForAuthNameCode(
std::set<std::string> authorities;
for (const auto &crs : list) {
- const auto &ids = crs.first->identifiers();
+ auto boundCRS = dynamic_cast<crs::BoundCRS *>(crs.first.get());
+ const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers()
+ : crs.first->identifiers();
if (!ids.empty()) {
authorities.insert(*(ids[0]->codeSpace()));
}
@@ -5626,7 +5749,9 @@ static std::string buildSqlLookForAuthNameCode(
params.emplace_back(auth_name);
bool firstGeodCRSForAuth = true;
for (const auto &crs : list) {
- const auto &ids = crs.first->identifiers();
+ auto boundCRS = dynamic_cast<crs::BoundCRS *>(crs.first.get());
+ const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers()
+ : crs.first->identifiers();
if (!ids.empty() && *(ids[0]->codeSpace()) == auth_name) {
if (!firstGeodCRSForAuth) {
sql += ',';
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 2aec5fac..c013ce24 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -2051,12 +2051,27 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
auto res = authFactory->createObjectsFromName(
name, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME},
true, 1);
- bool foundDatumName = false;
if (!res.empty()) {
+ bool foundDatumName = false;
const auto &refDatum = res.front();
if (metadata::Identifier::isEquivalentName(
name.c_str(), refDatum->nameStr().c_str())) {
foundDatumName = true;
+ } else if (refDatum->identifiers().size() == 1) {
+ const auto &id = refDatum->identifiers()[0];
+ const auto aliases =
+ authFactory->databaseContext()->getAliases(
+ *id->codeSpace(), id->code(), refDatum->nameStr(),
+ "geodetic_datum", std::string());
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(
+ name.c_str(), alias.c_str())) {
+ foundDatumName = true;
+ break;
+ }
+ }
+ }
+ if (foundDatumName) {
properties.set(IdentifiedObject::NAME_KEY,
refDatum->nameStr());
if (!properties.get(Identifier::CODESPACE_KEY) &&
@@ -2083,25 +2098,12 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
NN_NO_CHECK(dbContext_), *id->codeSpace());
auto dbDatum =
authFactory2->createGeodeticDatum(id->code());
- foundDatumName = true;
properties.set(IdentifiedObject::NAME_KEY,
dbDatum->nameStr());
} catch (const std::exception &) {
}
}
}
-
- if (!foundDatumName) {
- std::string outTableName;
- std::string authNameFromAlias;
- std::string codeFromAlias;
- auto officialName = authFactory->getOfficialNameFromAlias(
- name, "geodetic_datum", std::string(), true, outTableName,
- authNameFromAlias, codeFromAlias);
- if (!officialName.empty()) {
- properties.set(IdentifiedObject::NAME_KEY, officialName);
- }
- }
}
}
@@ -2683,7 +2685,9 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) {
auto cs = buildCS(csNode, node, angularUnit);
auto ellipsoidalCS = nn_dynamic_pointer_cast<EllipsoidalCS>(cs);
if (ellipsoidalCS) {
- assert(!ci_equal(nodeName, WKTConstants::GEOCCS));
+ if (ci_equal(nodeName, WKTConstants::GEOCCS)) {
+ throw ParsingException("ellipsoidal CS not expected in GEOCCS");
+ }
try {
auto crs = GeographicCRS::create(props, datum, datumEnsemble,
NN_NO_CHECK(ellipsoidalCS));
@@ -5669,13 +5673,18 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
DatabaseContextNNPtr dbContextNNPtr(NN_NO_CHECK(dbContext));
const auto &authName = tokens[0];
const auto &code = tokens[1];
- static const std::string epsg_lowercase("epsg");
- auto factory = AuthorityFactory::create(
- dbContextNNPtr,
- authName == epsg_lowercase ? Identifier::EPSG : authName);
+ auto factory = AuthorityFactory::create(dbContextNNPtr, authName);
try {
return factory->createCoordinateReferenceSystem(code);
} catch (...) {
+
+ // Convenience for well-known misused code
+ // See https://github.com/OSGeo/PROJ/issues/1730
+ if (ci_equal(authName, "EPSG") && code == "102100") {
+ factory = AuthorityFactory::create(dbContextNNPtr, "ESRI");
+ return factory->createCoordinateReferenceSystem(code);
+ }
+
const auto authorities = dbContextNNPtr->getAuthorities();
for (const auto &authCandidate : authorities) {
if (ci_equal(authCandidate, authName)) {
@@ -7157,6 +7166,12 @@ void PROJStringFormatter::stopInversion() {
// the current end of steps
for (auto iter = startIter; iter != d->steps_.end(); ++iter) {
iter->inverted = !iter->inverted;
+ for (auto &paramValue : iter->paramValues) {
+ if (paramValue.key == "omit_fwd")
+ paramValue.key = "omit_inv";
+ else if (paramValue.key == "omit_inv")
+ paramValue.key = "omit_fwd";
+ }
}
// And reverse the order of steps in that range as well.
std::reverse(startIter, d->steps_.end());
diff --git a/src/iso19111/metadata.cpp b/src/iso19111/metadata.cpp
index 41653b32..6266a86d 100644
--- a/src/iso19111/metadata.cpp
+++ b/src/iso19111/metadata.cpp
@@ -237,7 +237,8 @@ GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west,
//! @cond Doxygen_Suppress
bool GeographicBoundingBox::_isEquivalentTo(
- const util::IComparable *other, util::IComparable::Criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other);
if (!otherExtent)
return false;
@@ -502,7 +503,8 @@ VerticalExtent::create(double minimumIn, double maximumIn,
//! @cond Doxygen_Suppress
bool VerticalExtent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion) const {
+ util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const VerticalExtent *>(other);
if (!otherExtent)
return false;
@@ -587,7 +589,8 @@ TemporalExtentNNPtr TemporalExtent::create(const std::string &start,
//! @cond Doxygen_Suppress
bool TemporalExtent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion) const {
+ util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const TemporalExtent *>(other);
if (!otherExtent)
return false;
@@ -734,7 +737,8 @@ Extent::createFromBBOX(double west, double south, double east, double north,
//! @cond Doxygen_Suppress
bool Extent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherExtent = dynamic_cast<const Extent *>(other);
bool ret =
(otherExtent &&
@@ -749,15 +753,18 @@ bool Extent::_isEquivalentTo(const util::IComparable *other,
if (ret) {
for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) {
ret = d->geographicElements_[i]->_isEquivalentTo(
- otherExtent->d->geographicElements_[i].get(), criterion);
+ otherExtent->d->geographicElements_[i].get(), criterion,
+ dbContext);
}
for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) {
ret = d->verticalElements_[i]->_isEquivalentTo(
- otherExtent->d->verticalElements_[i].get(), criterion);
+ otherExtent->d->verticalElements_[i].get(), criterion,
+ dbContext);
}
for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) {
ret = d->temporalElements_[i]->_isEquivalentTo(
- otherExtent->d->temporalElements_[i].get(), criterion);
+ otherExtent->d->temporalElements_[i].get(), criterion,
+ dbContext);
}
}
return ret;
diff --git a/src/iso19111/util.cpp b/src/iso19111/util.cpp
index 25207d5c..2a6178e2 100644
--- a/src/iso19111/util.cpp
+++ b/src/iso19111/util.cpp
@@ -687,11 +687,13 @@ IComparable::~IComparable() = default;
/** \brief Returns whether an object is equivalent to another one.
* @param other other object to compare to
* @param criterion comparaison criterion.
+ * @param dbContext Database context, or nullptr.
* @return true if objects are equivalent.
*/
-bool IComparable::isEquivalentTo(const IComparable *other,
- Criterion criterion) const {
- return _isEquivalentTo(other, criterion);
+bool IComparable::isEquivalentTo(
+ const IComparable *other, Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
+ return _isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------