aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-10-16 18:26:09 +0200
committerGitHub <noreply@github.com>2020-10-16 18:26:09 +0200
commit82b496fb32df0b6705159cd5c626aab20c8e9d39 (patch)
treeb6652073c9d66960e5c16d61055c53ffc3f8656f /src
parent93508fbec18e192646f2890e1ceb86de4cc9fd35 (diff)
parent686713479eb0b39feb5369b82647f96edf809b6c (diff)
downloadPROJ-82b496fb32df0b6705159cd5c626aab20c8e9d39.tar.gz
PROJ-82b496fb32df0b6705159cd5c626aab20c8e9d39.zip
Merge pull request #2370 from rouault/epsg10
Update to EPSG 10.003 and make code base robust to dealing with WKT CRS with DatumEnsemble
Diffstat (limited to 'src')
-rw-r--r--src/4D_api.cpp36
-rw-r--r--src/iso19111/c_api.cpp256
-rw-r--r--src/iso19111/coordinateoperation.cpp236
-rw-r--r--src/iso19111/crs.cpp249
-rw-r--r--src/iso19111/datum.cpp78
-rw-r--r--src/iso19111/factory.cpp598
-rw-r--r--src/proj.h17
-rw-r--r--src/proj_experimental.h4
8 files changed, 1026 insertions, 448 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 75fa6d04..3c6ab802 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -1069,28 +1069,18 @@ static PJ* create_operation_to_geog_crs(PJ_CONTEXT* ctx, const PJ* crs) {
geodetic_crs_type == PJ_TYPE_GEOGRAPHIC_3D_CRS )
{
auto datum = proj_crs_get_datum(ctx, geodetic_crs);
- if( datum )
- {
- auto cs = proj_create_ellipsoidal_2D_cs(
- ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, nullptr, 0);
- auto ellps = proj_get_ellipsoid(ctx, datum);
- proj_destroy(datum);
- double semi_major_metre = 0;
- double inv_flattening = 0;
- proj_ellipsoid_get_parameters(ctx, ellps, &semi_major_metre,
- nullptr, nullptr, &inv_flattening);
- auto temp = proj_create_geographic_crs(
- ctx, "unnamed crs", "unnamed datum",
- proj_get_name(ellps),
- semi_major_metre, inv_flattening,
- "Reference prime meridian", 0, nullptr, 0,
- cs);
- proj_destroy(ellps);
- proj_destroy(cs);
- proj_destroy(geodetic_crs);
- geodetic_crs = temp;
- geodetic_crs_type = proj_get_type(geodetic_crs);
- }
+ auto datum_ensemble = proj_crs_get_datum_ensemble(ctx, geodetic_crs);
+ auto cs = proj_create_ellipsoidal_2D_cs(
+ ctx, PJ_ELLPS2D_LONGITUDE_LATITUDE, nullptr, 0);
+ auto temp = proj_create_geographic_crs_from_datum(
+ ctx, "unnamed crs", datum ? datum : datum_ensemble,
+ cs);
+ proj_destroy(datum);
+ proj_destroy(datum_ensemble);
+ proj_destroy(cs);
+ proj_destroy(geodetic_crs);
+ geodetic_crs = temp;
+ geodetic_crs_type = proj_get_type(geodetic_crs);
}
if( geodetic_crs_type != PJ_TYPE_GEOGRAPHIC_2D_CRS )
{
@@ -1233,7 +1223,7 @@ std::vector<CoordOperation> pj_create_prepared_operations(PJ_CONTEXT *ctx,
&west_lon, &south_lat, &east_lon, &north_lat, &areaName) )
{
const bool isOffshore =
- areaName && strstr(areaName, "offshore");
+ areaName && strstr(areaName, "- offshore");
if( west_lon <= east_lon )
{
op = add_coord_op_to_list(i, op,
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 21a07729..cad76431 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -898,15 +898,23 @@ convertPJObjectTypeToObjectType(PJ_TYPE type, bool &valid) {
break;
case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
- case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
cppType = AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME;
break;
+ case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
+ cppType =
+ AuthorityFactory::ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME;
+ break;
+
case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
- case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
cppType = AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME;
break;
+ case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
+ cppType =
+ AuthorityFactory::ObjectType::DYNAMIC_VERTICAL_REFERENCE_FRAME;
+ break;
+
case PJ_TYPE_DATUM_ENSEMBLE:
cppType = AuthorityFactory::ObjectType::DATUM;
break;
@@ -3044,31 +3052,35 @@ PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
*
* @param ctx PROJ context, or NULL for default context
* @param crs_name Name of the GeographicCRS. Or NULL
- * @param datum Datum. Must not be NULL.
+ * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
+ * since 7.2). Must not be NULL.
* @param ellipsoidal_cs Coordinate system. Must not be NULL.
*
* @return Object of type GeographicCRS that must be unreferenced with
* proj_destroy(), or NULL in case of error.
*/
PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
- PJ *datum, PJ *ellipsoidal_cs) {
+ PJ *datum_or_datum_ensemble,
+ PJ *ellipsoidal_cs) {
SANITIZE_CTX(ctx);
- auto l_datum =
- std::dynamic_pointer_cast<GeodeticReferenceFrame>(datum->iso_obj);
- if (!l_datum) {
+ if (datum_or_datum_ensemble == nullptr) {
proj_log_error(ctx, __FUNCTION__,
- "datum is not a GeodeticReferenceFrame");
+ "Missing input datum_or_datum_ensemble");
return nullptr;
}
+ auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
+ datum_or_datum_ensemble->iso_obj);
+ auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
+ datum_or_datum_ensemble->iso_obj);
auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
if (!cs) {
return nullptr;
}
try {
auto geogCRS =
- GeographicCRS::create(createPropertyMapName(crs_name),
- NN_NO_CHECK(l_datum), NN_NO_CHECK(cs));
+ GeographicCRS::create(createPropertyMapName(crs_name), l_datum,
+ l_datum_ensemble, NN_NO_CHECK(cs));
return pj_obj_create(ctx, geogCRS);
} catch (const std::exception &e) {
proj_log_error(ctx, __FUNCTION__, e.what());
@@ -3141,7 +3153,8 @@ PJ *proj_create_geocentric_crs(
*
* @param ctx PROJ context, or NULL for default context
* @param crs_name Name of the GeographicCRS. Or NULL
- * @param datum Datum. Must not be NULL.
+ * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
+ * since 7.2). Must not be NULL.
* @param linear_units Name of the linear units. Or NULL for Metre
* @param linear_units_conv Conversion factor from the linear unit to metre. Or
* 0 for Metre if linear_units == NULL. Otherwise should be not NULL
@@ -3150,22 +3163,24 @@ PJ *proj_create_geocentric_crs(
* proj_destroy(), or NULL in case of error.
*/
PJ *proj_create_geocentric_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
- const PJ *datum,
+ const PJ *datum_or_datum_ensemble,
const char *linear_units,
double linear_units_conv) {
SANITIZE_CTX(ctx);
+ if (datum_or_datum_ensemble == nullptr) {
+ proj_log_error(ctx, __FUNCTION__,
+ "Missing input datum_or_datum_ensemble");
+ return nullptr;
+ }
+ auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
+ datum_or_datum_ensemble->iso_obj);
+ auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
+ datum_or_datum_ensemble->iso_obj);
try {
const UnitOfMeasure linearUnit(
createLinearUnit(linear_units, linear_units_conv));
- auto l_datum =
- std::dynamic_pointer_cast<GeodeticReferenceFrame>(datum->iso_obj);
- if (!l_datum) {
- proj_log_error(ctx, __FUNCTION__,
- "datum is not a GeodeticReferenceFrame");
- return nullptr;
- }
auto geodCRS = GeodeticCRS::create(
- createPropertyMapName(crs_name), NN_NO_CHECK(l_datum),
+ createPropertyMapName(crs_name), l_datum, l_datum_ensemble,
cs::CartesianCS::createGeocentric(linearUnit));
return pj_obj_create(ctx, geodCRS);
} catch (const std::exception &e) {
@@ -7981,6 +7996,207 @@ PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) {
// ---------------------------------------------------------------------------
+/** \brief Returns the datum ensemble of a SingleCRS.
+ *
+ * The returned object must be unreferenced with proj_destroy() after
+ * use.
+ * It should be used by at most one thread at a time.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param crs Object of type SingleCRS (must not be NULL)
+ * @return Object that must be unreferenced with proj_destroy(), or NULL
+ * in case of error (or if there is no datum ensemble)
+ *
+ * @since 7.2
+ */
+PJ *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs) {
+ SANITIZE_CTX(ctx);
+ if (!crs) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return nullptr;
+ }
+ auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
+ if (!l_crs) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
+ return nullptr;
+ }
+ const auto &datumEnsemble = l_crs->datumEnsemble();
+ if (!datumEnsemble) {
+ return nullptr;
+ }
+ return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the number of members of a datum ensemble.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
+ *
+ * @since 7.2
+ */
+int proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx,
+ const PJ *datum_ensemble) {
+ SANITIZE_CTX(ctx);
+ if (!datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return 0;
+ }
+ auto l_datum_ensemble =
+ dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
+ if (!l_datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
+ return 0;
+ }
+ return static_cast<int>(l_datum_ensemble->datums().size());
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the positional accuracy of the datum ensemble.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
+ * @return the accuracy, or -1 in case of error.
+ *
+ * @since 7.2
+ */
+double proj_datum_ensemble_get_accuracy(PJ_CONTEXT *ctx,
+ const PJ *datum_ensemble) {
+ SANITIZE_CTX(ctx);
+ if (!datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return -1;
+ }
+ auto l_datum_ensemble =
+ dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
+ if (!l_datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
+ return -1;
+ }
+ const auto &accuracy = l_datum_ensemble->positionalAccuracy();
+ try {
+ return c_locale_stod(accuracy->value());
+ } catch (const std::exception &) {
+ }
+ return -1;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns a member from a datum ensemble.
+ *
+ * The returned object must be unreferenced with proj_destroy() after
+ * use.
+ * It should be used by at most one thread at a time.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
+ * @param member_index Index of the datum member to extract (between 0 and
+ * proj_datum_ensemble_get_member_count()-1)
+ * @return Object that must be unreferenced with proj_destroy(), or NULL
+ * in case of error (or if there is no datum ensemble)
+ *
+ * @since 7.2
+ */
+PJ *proj_datum_ensemble_get_member(PJ_CONTEXT *ctx, const PJ *datum_ensemble,
+ int member_index) {
+ SANITIZE_CTX(ctx);
+ if (!datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return nullptr;
+ }
+ auto l_datum_ensemble =
+ dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
+ if (!l_datum_ensemble) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
+ return nullptr;
+ }
+ if (member_index < 0 ||
+ member_index >= static_cast<int>(l_datum_ensemble->datums().size())) {
+ proj_log_error(ctx, __FUNCTION__, "Invalid member_index");
+ return nullptr;
+ }
+ return pj_obj_create(ctx, l_datum_ensemble->datums()[member_index]);
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns a datum for a SingleCRS.
+ *
+ * If the SingleCRS has a datum, then this datum is returned.
+ * Otherwise, the SingleCRS has a datum ensemble, and this datum ensemble is
+ * returned as a regular datum instead of a datum ensemble.
+ *
+ * The returned object must be unreferenced with proj_destroy() after
+ * use.
+ * It should be used by at most one thread at a time.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param crs Object of type SingleCRS (must not be NULL)
+ * @return Object that must be unreferenced with proj_destroy(), or NULL
+ * in case of error (or if there is no datum)
+ *
+ * @since 7.2
+ */
+PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) {
+ SANITIZE_CTX(ctx);
+ if (!crs) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return nullptr;
+ }
+ auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
+ if (!l_crs) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
+ return nullptr;
+ }
+ const auto &datum = l_crs->datum();
+ if (datum) {
+ return pj_obj_create(ctx, NN_NO_CHECK(datum));
+ }
+ const auto &datumEnsemble = l_crs->datumEnsemble();
+ assert(datumEnsemble);
+ auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
+ return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the frame reference epoch of a dynamic geodetic or vertical
+ * reference frame.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param datum Object of type DynamicGeodeticReferenceFrame or
+ * DynamicVerticalReferenceFrame (must not be NULL)
+ * @return the frame reference epoch as decimal year, or -1 in case of error.
+ *
+ * @since 7.2
+ */
+double proj_dynamic_datum_get_frame_reference_epoch(PJ_CONTEXT *ctx,
+ const PJ *datum) {
+ SANITIZE_CTX(ctx);
+ if (!datum) {
+ proj_log_error(ctx, __FUNCTION__, "missing required input");
+ return -1;
+ }
+ auto dgrf = dynamic_cast<const DynamicGeodeticReferenceFrame *>(
+ datum->iso_obj.get());
+ auto dvrf = dynamic_cast<const DynamicVerticalReferenceFrame *>(
+ datum->iso_obj.get());
+ if (!dgrf && !dvrf) {
+ proj_log_error(ctx, __FUNCTION__, "Object is not a "
+ "DynamicGeodeticReferenceFrame or "
+ "DynamicVerticalReferenceFrame");
+ return -1;
+ }
+ const auto &frameReferenceEpoch =
+ dgrf ? dgrf->frameReferenceEpoch() : dvrf->frameReferenceEpoch();
+ return frameReferenceEpoch.value();
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Returns the coordinate system of a SingleCRS.
*
* The returned object must be unreferenced with proj_destroy() after
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 6fcf4d30..184ff435 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -11174,7 +11174,8 @@ struct CoordinateOperationFactory::Private {
static std::vector<CoordinateOperationNNPtr> createOperationsGeogToGeog(
std::vector<CoordinateOperationNNPtr> &res,
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
- const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst);
+ Private::Context &context, const crs::GeographicCRS *geogSrc,
+ const crs::GeographicCRS *geogDst);
static void createOperationsWithDatumPivot(
std::vector<CoordinateOperationNNPtr> &res,
@@ -12338,16 +12339,17 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate(
//! @cond Doxygen_Suppress
static TransformationNNPtr
createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS,
- const crs::CRSNNPtr &targetCRS) {
+ const crs::CRSNNPtr &targetCRS,
+ const io::DatabaseContextPtr &dbContext) {
const crs::GeographicCRS *geogSrc =
dynamic_cast<const crs::GeographicCRS *>(sourceCRS.get());
const crs::GeographicCRS *geogDst =
dynamic_cast<const crs::GeographicCRS *>(targetCRS.get());
- const bool isSameDatum =
- geogSrc && geogDst && geogSrc->datum() && geogDst->datum() &&
- geogSrc->datum()->_isEquivalentTo(
- geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT);
+ const bool isSameDatum = geogSrc && geogDst &&
+ geogSrc->datumNonNull(dbContext)->_isEquivalentTo(
+ geogDst->datumNonNull(dbContext).get(),
+ util::IComparable::Criterion::EQUIVALENT);
auto name = buildOpName(isSameDatum ? NULL_GEOGRAPHIC_OFFSET
: BALLPARK_GEOGRAPHIC_OFFSET,
@@ -12692,8 +12694,8 @@ static CoordinateOperationNNPtr createHorizVerticalHorizPROJBased(
std::vector<CoordinateOperationNNPtr>
CoordinateOperationFactory::Private::createOperationsGeogToGeog(
std::vector<CoordinateOperationNNPtr> &res, const crs::CRSNNPtr &sourceCRS,
- const crs::CRSNNPtr &targetCRS, const crs::GeographicCRS *geogSrc,
- const crs::GeographicCRS *geogDst) {
+ const crs::CRSNNPtr &targetCRS, Private::Context &context,
+ const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst) {
assert(sourceCRS.get() == geogSrc);
assert(targetCRS.get() == geogDst);
@@ -12723,10 +12725,13 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
std::string name(buildTransfName(geogSrc->nameStr(), geogDst->nameStr()));
- const bool sameDatum =
- geogSrc->datum() != nullptr && geogDst->datum() != nullptr &&
- geogSrc->datum()->_isEquivalentTo(
- geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT);
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable() : nullptr;
+
+ const bool sameDatum = geogSrc->datumNonNull(dbContext)->_isEquivalentTo(
+ geogDst->datumNonNull(dbContext).get(),
+ util::IComparable::Criterion::EQUIVALENT);
// Do the CRS differ by their axis order ?
bool axisReversal2D = false;
@@ -12821,7 +12826,7 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
datum, dstCS));
steps.emplace_back(
- createBallparkGeographicOffset(sourceCRS, interm_crs));
+ createBallparkGeographicOffset(sourceCRS, interm_crs, dbContext));
steps.emplace_back(Transformation::createLongitudeRotation(
util::PropertyMap()
@@ -12855,11 +12860,11 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog(
.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
metadata::Extent::WORLD),
sourceCRS, interm_crs, offset_pm));
- steps.emplace_back(
- createBallparkGeographicOffset(interm_crs, targetCRS));
+ steps.emplace_back(createBallparkGeographicOffset(
+ interm_crs, targetCRS, dbContext));
} else {
- steps.emplace_back(
- createBallparkGeographicOffset(sourceCRS, targetCRS));
+ steps.emplace_back(createBallparkGeographicOffset(
+ sourceCRS, targetCRS, dbContext));
}
}
@@ -13030,10 +13035,14 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
CreateOperationsWithDatumPivotAntiRecursion guard(context);
const auto &authFactory = context.context->getAuthorityFactory();
+ const auto &dbContext = authFactory->databaseContext();
+
const auto candidatesSrcGeod(findCandidateGeodCRSForDatum(
- authFactory, geodSrc, geodSrc->datum().get()));
+ authFactory, geodSrc,
+ geodSrc->datumNonNull(dbContext.as_nullable()).get()));
const auto candidatesDstGeod(findCandidateGeodCRSForDatum(
- authFactory, geodDst, geodDst->datum().get()));
+ authFactory, geodDst,
+ geodDst->datumNonNull(dbContext.as_nullable()).get()));
const bool sourceAndTargetAre3D =
geodSrc->coordinateSystem()->axisList().size() == 3 &&
@@ -13545,16 +13554,16 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase(
}
} else if (geodSrc && geodDst) {
- const auto &srcDatum = geodSrc->datum();
- const auto &dstDatum = geodDst->datum();
- sameGeodeticDatum =
- srcDatum != nullptr && dstDatum != nullptr &&
- srcDatum->_isEquivalentTo(dstDatum.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext = authFactory->databaseContext().as_nullable();
+
+ const auto srcDatum = geodSrc->datumNonNull(dbContext);
+ const auto dstDatum = geodDst->datumNonNull(dbContext);
+ sameGeodeticDatum = srcDatum->_isEquivalentTo(
+ dstDatum.get(), util::IComparable::Criterion::EQUIVALENT);
if (res.empty() && !sameGeodeticDatum &&
- !context.inCreateOperationsWithDatumPivotAntiRecursion &&
- srcDatum != nullptr && dstDatum != nullptr) {
+ !context.inCreateOperationsWithDatumPivotAntiRecursion) {
// If we still didn't find a transformation, and that the source
// and target are GeodeticCRS, then go through their underlying
// datum to find potential transformations between other
@@ -13860,8 +13869,10 @@ std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private::
};
AntiRecursionGuard guard(context);
const auto &authFactory = context.context->getAuthorityFactory();
- auto candidatesVert =
- findCandidateVertCRSForDatum(authFactory, vertDst->datum().get());
+ const auto dbContext = authFactory->databaseContext().as_nullable();
+
+ auto candidatesVert = findCandidateVertCRSForDatum(
+ authFactory, vertDst->datumNonNull(dbContext).get());
for (const auto &candidateVert : candidatesVert) {
auto resTmp = createOperations(sourceCRS, candidateVert, context);
if (!resTmp.empty()) {
@@ -13964,11 +13975,14 @@ void CoordinateOperationFactory::Private::
const crs::GeographicCRS *geogSrcIn, const crs::VerticalCRS *vertDstIn,
const crs::CRSNNPtr &targetCRSIn) {
if (res.empty() && geogSrcIn && vertDstIn &&
- geogSrcIn->coordinateSystem()->axisList().size() == 3 &&
- geogSrcIn->datum()) {
+ geogSrcIn->coordinateSystem()->axisList().size() == 3) {
const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable()
+ : nullptr;
const auto candidatesSrcGeod(findCandidateGeodCRSForDatum(
- authFactory, geogSrcIn, geogSrcIn->datum().get()));
+ authFactory, geogSrcIn,
+ geogSrcIn->datumNonNull(dbContext).get()));
for (const auto &candidate : candidatesSrcGeod) {
auto geogCandidate =
util::nn_dynamic_pointer_cast<crs::GeographicCRS>(
@@ -14030,7 +14044,8 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod(
auto geogDst = dynamic_cast<const crs::GeographicCRS *>(geodDst);
if (geogSrc && geogDst) {
- createOperationsGeogToGeog(res, sourceCRS, targetCRS, geogSrc, geogDst);
+ createOperationsGeogToGeog(res, sourceCRS, targetCRS, context, geogSrc,
+ geogDst);
return;
}
@@ -14038,14 +14053,23 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod(
const bool isSrcGeographic = geogSrc != nullptr;
const bool isTargetGeocentric = geodDst->isGeocentric();
const bool isTargetGeographic = geogDst != nullptr;
+
+ const auto IsSameDatum = [&context, &geodSrc, &geodDst]() {
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable()
+ : nullptr;
+
+ return geodSrc->datumNonNull(dbContext)->_isEquivalentTo(
+ geodDst->datumNonNull(dbContext).get(),
+ util::IComparable::Criterion::EQUIVALENT);
+ };
+
if (((isSrcGeocentric && isTargetGeographic) ||
- (isSrcGeographic && isTargetGeocentric)) &&
- geodSrc->datum() != nullptr && geodDst->datum() != nullptr) {
+ (isSrcGeographic && isTargetGeocentric))) {
// Same datum ?
- if (geodSrc->datum()->_isEquivalentTo(
- geodDst->datum().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (IsSameDatum()) {
res.emplace_back(
Conversion::createGeographicGeocentric(sourceCRS, targetCRS));
} else if (isSrcGeocentric && geogDst) {
@@ -14057,7 +14081,7 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod(
common::IdentifiedObject::NAME_KEY,
interm_crs_name),
geogDst),
- NN_NO_CHECK(geogDst->datum()),
+ geogDst->datum(), geogDst->datumEnsemble(),
NN_CHECK_ASSERT(
util::nn_dynamic_pointer_cast<cs::CartesianCS>(
geodSrc->coordinateSystem()))));
@@ -14082,10 +14106,7 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod(
if (isSrcGeocentric && isTargetGeocentric) {
if (sourceCRS->_isEquivalentTo(
targetCRS.get(), util::IComparable::Criterion::EQUIVALENT) ||
- (geodSrc->datum() != nullptr && geodDst->datum() != nullptr &&
- geodSrc->datum()->_isEquivalentTo(
- geodDst->datum().get(),
- util::IComparable::Criterion::EQUIVALENT))) {
+ IsSameDatum()) {
std::string name(NULL_GEOCENTRIC_TRANSLATION);
name += " from ";
name += sourceCRS->nameStr();
@@ -14151,14 +14172,19 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
const auto &hubSrc = boundSrc->hubCRS();
auto hubSrcGeog = dynamic_cast<const crs::GeographicCRS *>(hubSrc.get());
auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS();
- auto geogDstDatum = geogDst->datum();
+
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable() : nullptr;
+
+ const auto geogDstDatum = geogDst->datumNonNull(dbContext);
// If the underlying datum of the source is the same as the target, do
// not consider the boundCRS at all, but just its base
- if (geogCRSOfBaseOfBoundSrc && geogDstDatum) {
- auto geogCRSOfBaseOfBoundSrcDatum = geogCRSOfBaseOfBoundSrc->datum();
- if (geogCRSOfBaseOfBoundSrcDatum &&
- geogCRSOfBaseOfBoundSrcDatum->_isEquivalentTo(
+ if (geogCRSOfBaseOfBoundSrc) {
+ auto geogCRSOfBaseOfBoundSrcDatum =
+ geogCRSOfBaseOfBoundSrc->datumNonNull(dbContext);
+ if (geogCRSOfBaseOfBoundSrcDatum->_isEquivalentTo(
geogDstDatum.get(), util::IComparable::Criterion::EQUIVALENT)) {
res = createOperations(boundSrc->baseCRS(), targetCRS, context);
return;
@@ -14170,7 +14196,7 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
if (hubSrcGeog && geogCRSOfBaseOfBoundSrc &&
(hubSrcGeog->_isEquivalentTo(
geogDst, util::IComparable::Criterion::EQUIVALENT) ||
- hubSrcGeog->is2DPartOf3D(NN_NO_CHECK(geogDst)))) {
+ hubSrcGeog->is2DPartOf3D(NN_NO_CHECK(geogDst), dbContext))) {
triedBoundCrsToGeogCRSSameAsHubCRS = true;
CoordinateOperationPtr opIntermediate;
@@ -14223,9 +14249,8 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
}
}
// If the datum are equivalent, this is also fine
- } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() &&
- geogDstDatum &&
- hubSrcGeog->datum()->_isEquivalentTo(
+ } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog &&
+ hubSrcGeog->datumNonNull(dbContext)->_isEquivalentTo(
geogDstDatum.get(),
util::IComparable::Criterion::EQUIVALENT)) {
auto opsFirst = createOperations(
@@ -14268,12 +14293,11 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
// Case of "+proj=latlong +ellps=clrk66
// +nadgrids=ntv1_can.dat,conus"
// to "+proj=latlong +datum=NAD83"
- } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() &&
- geogDstDatum &&
+ } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog &&
geogCRSOfBaseOfBoundSrc->ellipsoid()->_isEquivalentTo(
datum::Ellipsoid::CLARKE_1866.get(),
util::IComparable::Criterion::EQUIVALENT) &&
- hubSrcGeog->datum()->_isEquivalentTo(
+ hubSrcGeog->datumNonNull(dbContext)->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6326.get(),
util::IComparable::Criterion::EQUIVALENT) &&
geogDstDatum->_isEquivalentTo(
@@ -14410,8 +14434,7 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
common::IdentifiedObject::NAME_KEY,
"Change of vertical unit"),
common::Scale(heightDepthReversal ? -factor : factor));
- auto dbContext = context.context->getAuthorityFactory()
- ->databaseContext();
+
conv->setCRSs(
hubSrc,
hubSrc->demoteTo2D(std::string(), dbContext)
@@ -14492,18 +14515,20 @@ void CoordinateOperationFactory::Private::createOperationsBoundToVert(
void CoordinateOperationFactory::Private::createOperationsVertToVert(
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
- Private::Context & /*context*/, const crs::VerticalCRS *vertSrc,
+ Private::Context &context, const crs::VerticalCRS *vertSrc,
const crs::VerticalCRS *vertDst,
std::vector<CoordinateOperationNNPtr> &res) {
ENTER_FUNCTION();
- const auto &srcDatum = vertSrc->datum();
- const auto &dstDatum = vertDst->datum();
- const bool equivalentVDatum =
- (srcDatum && dstDatum &&
- srcDatum->_isEquivalentTo(dstDatum.get(),
- util::IComparable::Criterion::EQUIVALENT));
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable() : nullptr;
+
+ const auto srcDatum = vertSrc->datumNonNull(dbContext);
+ const auto dstDatum = vertDst->datumNonNull(dbContext);
+ const bool equivalentVDatum = srcDatum->_isEquivalentTo(
+ dstDatum.get(), util::IComparable::Criterion::EQUIVALENT);
const auto &srcAxis = vertSrc->coordinateSystem()->axisList()[0];
const double convSrc = srcAxis->unit().conversionToSI();
@@ -14673,10 +14698,15 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound(
auto baseOfBoundDstAsVertCRS =
dynamic_cast<crs::VerticalCRS *>(boundDst->baseCRS().get());
if (baseOfBoundSrcAsVertCRS && baseOfBoundDstAsVertCRS) {
- const auto datumSrc = baseOfBoundSrcAsVertCRS->datum();
- const auto datumDst = baseOfBoundDstAsVertCRS->datum();
- if (datumSrc && datumDst &&
- datumSrc->nameStr() == datumDst->nameStr() &&
+
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable()
+ : nullptr;
+
+ const auto datumSrc = baseOfBoundSrcAsVertCRS->datumNonNull(dbContext);
+ const auto datumDst = baseOfBoundDstAsVertCRS->datumNonNull(dbContext);
+ if (datumSrc->nameStr() == datumDst->nameStr() &&
(datumSrc->nameStr() != "unknown" ||
boundSrc->transformation()->_isEquivalentTo(
boundDst->transformation().get(),
@@ -14946,7 +14976,7 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog(
if (!foundRegisteredTransformWithAllGridsAvailable && srcGeogCRS &&
!srcGeogCRS->_isEquivalentTo(
geogDst, util::IComparable::Criterion::EQUIVALENT) &&
- !srcGeogCRS->is2DPartOf3D(NN_NO_CHECK(geogDst))) {
+ !srcGeogCRS->is2DPartOf3D(NN_NO_CHECK(geogDst), dbContext)) {
auto verticalTransformsTmp = createOperations(
componentsSrc[1],
NN_NO_CHECK(srcGeogCRS)
@@ -15128,32 +15158,29 @@ void CoordinateOperationFactory::Private::createOperationsToGeod(
const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
Private::Context &context, const crs::GeodeticCRS *geodDst,
std::vector<CoordinateOperationNNPtr> &res) {
- auto datum = geodDst->datum();
- if (datum) {
- auto cs = cs::EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
- common::UnitOfMeasure::DEGREE, common::UnitOfMeasure::METRE);
- auto intermGeog3DCRS =
- util::nn_static_pointer_cast<crs::CRS>(crs::GeographicCRS::create(
- util::PropertyMap()
- .set(common::IdentifiedObject::NAME_KEY, geodDst->nameStr())
- .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
- metadata::Extent::WORLD),
- NN_NO_CHECK(datum), cs));
- auto sourceToGeog3DOps =
- createOperations(sourceCRS, intermGeog3DCRS, context);
- auto geog3DToTargetOps =
- createOperations(intermGeog3DCRS, targetCRS, context);
- if (!geog3DToTargetOps.empty()) {
- for (const auto &op : sourceToGeog3DOps) {
- auto newOp = op->shallowClone();
- setCRSs(newOp.get(), sourceCRS, intermGeog3DCRS);
- try {
- res.emplace_back(
- ConcatenatedOperation::createComputeMetadata(
- {newOp, geog3DToTargetOps.front()},
- disallowEmptyIntersection));
- } catch (const InvalidOperationEmptyIntersection &) {
- }
+
+ auto cs = cs::EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
+ common::UnitOfMeasure::DEGREE, common::UnitOfMeasure::METRE);
+ auto intermGeog3DCRS =
+ util::nn_static_pointer_cast<crs::CRS>(crs::GeographicCRS::create(
+ util::PropertyMap()
+ .set(common::IdentifiedObject::NAME_KEY, geodDst->nameStr())
+ .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
+ metadata::Extent::WORLD),
+ geodDst->datum(), geodDst->datumEnsemble(), cs));
+ auto sourceToGeog3DOps =
+ createOperations(sourceCRS, intermGeog3DCRS, context);
+ auto geog3DToTargetOps =
+ createOperations(intermGeog3DCRS, targetCRS, context);
+ if (!geog3DToTargetOps.empty()) {
+ for (const auto &op : sourceToGeog3DOps) {
+ auto newOp = op->shallowClone();
+ setCRSs(newOp.get(), sourceCRS, intermGeog3DCRS);
+ try {
+ res.emplace_back(ConcatenatedOperation::createComputeMetadata(
+ {newOp, geog3DToTargetOps.front()},
+ disallowEmptyIntersection));
+ } catch (const InvalidOperationEmptyIntersection &) {
}
}
}
@@ -15322,6 +15349,10 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound(
const crs::CompoundCRS *compoundDst,
std::vector<CoordinateOperationNNPtr> &res) {
+ const auto &authFactory = context.context->getAuthorityFactory();
+ const auto dbContext =
+ authFactory ? authFactory->databaseContext().as_nullable() : nullptr;
+
const auto &componentsDst = compoundDst->componentReferenceSystems();
if (!componentsDst.empty()) {
auto compDst0BoundCrs =
@@ -15333,13 +15364,11 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound(
dynamic_cast<crs::GeographicCRS *>(
compDst0BoundCrs->hubCRS().get());
if (boundSrcHubAsGeogCRS && compDst0BoundCrsHubAsGeogCRS) {
- const auto &boundSrcHubAsGeogCRSDatum =
- boundSrcHubAsGeogCRS->datum();
- const auto &compDst0BoundCrsHubAsGeogCRSDatum =
- compDst0BoundCrsHubAsGeogCRS->datum();
- if (boundSrcHubAsGeogCRSDatum &&
- compDst0BoundCrsHubAsGeogCRSDatum &&
- boundSrcHubAsGeogCRSDatum->_isEquivalentTo(
+ const auto boundSrcHubAsGeogCRSDatum =
+ boundSrcHubAsGeogCRS->datumNonNull(dbContext);
+ const auto compDst0BoundCrsHubAsGeogCRSDatum =
+ compDst0BoundCrsHubAsGeogCRS->datumNonNull(dbContext);
+ if (boundSrcHubAsGeogCRSDatum->_isEquivalentTo(
compDst0BoundCrsHubAsGeogCRSDatum.get())) {
auto cs = cs::EllipsoidalCS::
createLatitudeLongitudeEllipsoidalHeight(
@@ -15352,7 +15381,8 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound(
boundSrcHubAsGeogCRS->nameStr())
.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
metadata::Extent::WORLD),
- NN_NO_CHECK(boundSrcHubAsGeogCRSDatum), cs));
+ boundSrcHubAsGeogCRS->datum(),
+ boundSrcHubAsGeogCRS->datumEnsemble(), cs));
auto sourceToGeog3DOps =
createOperations(sourceCRS, intermGeog3DCRS, context);
auto geog3DToTargetOps =
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index e9082473..cea6476f 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -1022,7 +1022,8 @@ CRSNNPtr CRS::promoteTo3D(const std::string &newName,
if (firstResAxisList[2]->_isEquivalentTo(
verticalAxisIfNotAlreadyPresent.get(),
util::IComparable::Criterion::EQUIVALENT) &&
- geogCRS->is2DPartOf3D(NN_NO_CHECK(firstResGeog))) {
+ geogCRS->is2DPartOf3D(NN_NO_CHECK(firstResGeog),
+ dbContext)) {
return NN_NO_CHECK(
util::nn_dynamic_pointer_cast<CRS>(firstRes));
}
@@ -1202,6 +1203,18 @@ const datum::DatumEnsemblePtr &SingleCRS::datumEnsemble() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
+//! @cond Doxygen_Suppress
+/** \brief Return the real datum or a synthetized one if a datumEnsemble.
+ */
+const datum::DatumNNPtr
+SingleCRS::datumNonNull(const io::DatabaseContextPtr &dbContext) const {
+ return d->datum ? NN_NO_CHECK(d->datum)
+ : d->datumEnsemble->asDatum(dbContext);
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
/** \brief Return the cs::CoordinateSystem associated with the CRS.
*
* @return a CoordinateSystem.
@@ -1221,20 +1234,41 @@ bool SingleCRS::baseIsEquivalentTo(
!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,
- dbContext)) {
- return false;
+
+ if (criterion == util::IComparable::Criterion::STRICT) {
+ const auto &thisDatum = d->datum;
+ const auto &otherDatum = otherSingleCRS->d->datum;
+ if (thisDatum) {
+ if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion,
+ dbContext)) {
+ return false;
+ }
+ } else {
+ if (otherDatum) {
+ return false;
+ }
+ }
+
+ const auto &thisDatumEnsemble = d->datumEnsemble;
+ const auto &otherDatumEnsemble = otherSingleCRS->d->datumEnsemble;
+ if (thisDatumEnsemble) {
+ if (!thisDatumEnsemble->_isEquivalentTo(otherDatumEnsemble.get(),
+ criterion, dbContext)) {
+ return false;
+ }
+ } else {
+ if (otherDatumEnsemble) {
+ return false;
+ }
}
} else {
- if (otherDatum) {
+ if (!datumNonNull(dbContext)->_isEquivalentTo(
+ otherSingleCRS->datumNonNull(dbContext).get(), criterion,
+ dbContext)) {
return false;
}
}
- // TODO test DatumEnsemble
return d->coordinateSystem->_isEquivalentTo(
otherSingleCRS->d->coordinateSystem.get(), criterion,
dbContext) &&
@@ -1355,6 +1389,21 @@ const datum::GeodeticReferenceFramePtr &GeodeticCRS::datum() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+/** \brief Return the real datum or a synthetized one if a datumEnsemble.
+ */
+const datum::GeodeticReferenceFrameNNPtr
+GeodeticCRS::datumNonNull(const io::DatabaseContextPtr &dbContext) const {
+ return NN_NO_CHECK(
+ d->datum_
+ ? d->datum_
+ : util::nn_dynamic_pointer_cast<datum::GeodeticReferenceFrame>(
+ SingleCRS::getPrivate()->datumEnsemble->asDatum(dbContext)));
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
static datum::GeodeticReferenceFrame *oneDatum(const GeodeticCRS *crs) {
const auto &l_datumEnsemble = crs->datumEnsemble();
assert(l_datumEnsemble);
@@ -1718,8 +1767,8 @@ void GeodeticCRS::addDatumInfoToPROJString(
const auto &TOWGS84Params = formatter->getTOWGS84Parameters();
bool datumWritten = false;
const auto &nadgrids = formatter->getHDatumExtension();
- const auto &l_datum = datum();
- if (formatter->getCRSExport() && l_datum && TOWGS84Params.empty() &&
+ const auto l_datum = datumNonNull(formatter->databaseContext());
+ if (formatter->getCRSExport() && TOWGS84Params.empty() &&
nadgrids.empty()) {
if (l_datum->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6326.get(),
@@ -1967,11 +2016,12 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (authorityFactory) {
- const auto &thisDatum(datum());
+ const auto thisDatum(datumNonNull(dbContext));
- auto searchByDatum = [this, &authorityFactory, &res, &thisDatum,
- &geodetic_crs_type, crsCriterion, &dbContext]() {
- for (const auto &id : thisDatum->identifiers()) {
+ auto searchByDatumCode = [this, &authorityFactory, &res,
+ &geodetic_crs_type, crsCriterion, &dbContext](
+ const common::IdentifiedObjectNNPtr &l_datum) {
+ for (const auto &id : l_datum->identifiers()) {
try {
auto tempRes = authorityFactory->createGeodeticCRSFromDatum(
*id->codeSpace(), id->code(), geodetic_crs_type);
@@ -1986,10 +2036,10 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
};
- const auto &thisEllipsoid(ellipsoid());
auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum,
- &thisEllipsoid, &geodetic_crs_type,
- l_implicitCS, &dbContext]() {
+ &geodetic_crs_type, l_implicitCS,
+ &dbContext]() {
+ const auto &thisEllipsoid = thisDatum->ellipsoid();
const auto ellipsoids =
thisEllipsoid->identifiers().empty()
? authorityFactory->createEllipsoidFromExisting(
@@ -2003,9 +2053,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
*id->codeSpace(), id->code(),
geodetic_crs_type);
for (const auto &crs : tempRes) {
- const auto &crsDatum(crs->datum());
- if (crsDatum &&
- crsDatum->ellipsoid()->_isEquivalentTo(
+ const auto crsDatum(crs->datumNonNull(dbContext));
+ if (crsDatum->ellipsoid()->_isEquivalentTo(
ellps.get(),
util::IComparable::Criterion::EQUIVALENT,
dbContext) &&
@@ -2027,18 +2076,32 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
};
+ const auto searchByDatumOrEllipsoid = [&authorityFactory, &res,
+ &thisDatum, searchByDatumCode,
+ searchByEllipsoid]() {
+ if (!thisDatum->identifiers().empty()) {
+ searchByDatumCode(thisDatum);
+ } else {
+ auto candidateDatums = authorityFactory->createObjectsFromName(
+ thisDatum->nameStr(), {io::AuthorityFactory::ObjectType::
+ GEODETIC_REFERENCE_FRAME},
+ false);
+ const size_t sizeBefore = res.size();
+ for (const auto &candidateDatum : candidateDatums) {
+ searchByDatumCode(candidateDatum);
+ }
+ if (sizeBefore == res.size()) {
+ searchByEllipsoid();
+ }
+ }
+ };
+
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
ci_equal(thisName, "unnamed");
if (unsignificantName) {
- if (thisDatum) {
- if (!thisDatum->identifiers().empty()) {
- searchByDatum();
- } else {
- searchByEllipsoid();
- }
- }
+ searchByDatumOrEllipsoid();
} else if (hasCodeCompatibleOfAuthorityFactory(this,
authorityFactory)) {
// If the CRS has already an id, check in the database for the
@@ -2088,12 +2151,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
break;
}
}
- if (!gotAbove25Pct && thisDatum) {
- if (!thisDatum->identifiers().empty()) {
- searchByDatum();
- } else {
- searchByEllipsoid();
- }
+ if (!gotAbove25Pct) {
+ searchByDatumOrEllipsoid();
}
}
@@ -2120,22 +2179,20 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
// Then datum matching
- const auto &aDatum(a.first->datum());
- const auto &bDatum(b.first->datum());
- if (thisDatum && aDatum && bDatum) {
- const auto thisEquivADatum(thisDatum->_isEquivalentTo(
- aDatum.get(), util::IComparable::Criterion::EQUIVALENT,
- dbContext));
- const auto thisEquivBDatum(thisDatum->_isEquivalentTo(
- bDatum.get(), util::IComparable::Criterion::EQUIVALENT,
- dbContext));
-
- if (thisEquivADatum && !thisEquivBDatum) {
- return true;
- }
- if (!thisEquivADatum && thisEquivBDatum) {
- return false;
- }
+ const auto aDatum(a.first->datumNonNull(dbContext));
+ const auto bDatum(b.first->datumNonNull(dbContext));
+ const auto thisEquivADatum(thisDatum->_isEquivalentTo(
+ aDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
+ const auto thisEquivBDatum(thisDatum->_isEquivalentTo(
+ bDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
+
+ if (thisEquivADatum && !thisEquivBDatum) {
+ return true;
+ }
+ if (!thisEquivADatum && thisEquivBDatum) {
+ return false;
}
// Then coordinate system matching
@@ -2167,23 +2224,21 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
return false;
}
- if (aDatum && bDatum) {
- // Favor the CRS whole ellipsoid names matches the ellipsoid
- // name (WGS84...)
- const bool aEllpsNameEqCRSName =
- metadata::Identifier::isEquivalentName(
- aDatum->ellipsoid()->nameStr().c_str(),
- a.first->nameStr().c_str());
- const bool bEllpsNameEqCRSName =
- metadata::Identifier::isEquivalentName(
- bDatum->ellipsoid()->nameStr().c_str(),
- b.first->nameStr().c_str());
- if (aEllpsNameEqCRSName && !bEllpsNameEqCRSName) {
- return true;
- }
- if (bEllpsNameEqCRSName && !aEllpsNameEqCRSName) {
- return false;
- }
+ // Favor the CRS whole ellipsoid names matches the ellipsoid
+ // name (WGS84...)
+ const bool aEllpsNameEqCRSName =
+ metadata::Identifier::isEquivalentName(
+ aDatum->ellipsoid()->nameStr().c_str(),
+ a.first->nameStr().c_str());
+ const bool bEllpsNameEqCRSName =
+ metadata::Identifier::isEquivalentName(
+ bDatum->ellipsoid()->nameStr().c_str(),
+ b.first->nameStr().c_str());
+ if (aEllpsNameEqCRSName && !bEllpsNameEqCRSName) {
+ return true;
+ }
+ if (bEllpsNameEqCRSName && !aEllpsNameEqCRSName) {
+ return false;
}
// Arbitrary final sorting criterion
@@ -2331,7 +2386,8 @@ GeographicCRS::create(const util::PropertyMap &properties,
/** \brief Return whether the current GeographicCRS is the 2D part of the
* other 3D GeographicCRS.
*/
-bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other)
+bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other,
+ const io::DatabaseContextPtr &dbContext)
PROJ_PURE_DEFN {
const auto &axis = d->coordinateSystem_->axisList();
const auto &otherAxis = other->d->coordinateSystem_->axisList();
@@ -2349,13 +2405,10 @@ bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other)
util::IComparable::Criterion::EQUIVALENT))) {
return false;
}
- const auto &thisDatum = GeodeticCRS::getPrivate()->datum_;
- const auto &otherDatum = other->GeodeticCRS::getPrivate()->datum_;
- if (thisDatum && otherDatum) {
- return thisDatum->_isEquivalentTo(
- otherDatum.get(), util::IComparable::Criterion::EQUIVALENT);
- }
- return false;
+ const auto thisDatum = datumNonNull(dbContext);
+ const auto otherDatum = other->datumNonNull(dbContext);
+ return thisDatum->_isEquivalentTo(otherDatum.get(),
+ util::IComparable::Criterion::EQUIVALENT);
}
//! @endcond
@@ -2497,7 +2550,8 @@ GeographicCRS::demoteTo2D(const std::string &newName,
auto firstResAsGeogCRS =
util::nn_dynamic_pointer_cast<GeographicCRS>(firstRes);
if (firstResAsGeogCRS &&
- firstResAsGeogCRS->is2DPartOf3D(NN_NO_CHECK(this))) {
+ firstResAsGeogCRS->is2DPartOf3D(NN_NO_CHECK(this),
+ dbContext)) {
return NN_NO_CHECK(firstResAsGeogCRS);
}
}
@@ -2594,15 +2648,13 @@ void GeographicCRS::_exportToPROJString(
if (formatter->getLegacyCRSToCRSContext() &&
formatter->getHDatumExtension().empty() &&
formatter->getTOWGS84Parameters().empty()) {
- const auto &l_datum = datum();
- if (l_datum &&
- l_datum->_isEquivalentTo(
+ const auto l_datum = datumNonNull(formatter->databaseContext());
+ if (l_datum->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6326.get(),
util::IComparable::Criterion::EQUIVALENT)) {
done = true;
formatter->addParam("ellps", "WGS84");
- } else if (l_datum &&
- l_datum->_isEquivalentTo(
+ } else if (l_datum->_isEquivalentTo(
datum::GeodeticReferenceFrame::EPSG_6269.get(),
util::IComparable::Criterion::EQUIVALENT)) {
done = true;
@@ -2769,6 +2821,19 @@ const cs::VerticalCSNNPtr VerticalCRS::coordinateSystem() const {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+/** \brief Return the real datum or a synthetized one if a datumEnsemble.
+ */
+const datum::VerticalReferenceFrameNNPtr
+VerticalCRS::datumNonNull(const io::DatabaseContextPtr &dbContext) const {
+ return NN_NO_CHECK(
+ util::nn_dynamic_pointer_cast<datum::VerticalReferenceFrame>(
+ SingleCRS::datumNonNull(dbContext)));
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const {
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
formatter->startNode(isWKT2 ? io::WKTConstants::VERTCRS
@@ -3860,12 +3925,15 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
std::list<Pair> res;
const auto &thisName(nameStr());
+ io::DatabaseContextPtr dbContext =
+ authorityFactory ? authorityFactory->databaseContext().as_nullable()
+ : nullptr;
std::list<std::pair<GeodeticCRSNNPtr, int>> baseRes;
const auto &l_baseCRS(baseCRS());
- const auto l_datum = l_baseCRS->datum();
+ const auto l_datum = l_baseCRS->datumNonNull(dbContext);
const bool significantNameForDatum =
- l_datum != nullptr && !ci_starts_with(l_datum->nameStr(), "unknown") &&
+ !ci_starts_with(l_datum->nameStr(), "unknown") &&
l_datum->nameStr() != "unnamed";
const auto &ellipsoid = l_baseCRS->ellipsoid();
auto geogCRS = dynamic_cast<const GeographicCRS *>(l_baseCRS.get());
@@ -3900,9 +3968,6 @@ 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) &&
@@ -3992,10 +4057,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
util::IComparable::Criterion::EQUIVALENT,
dbContext)) {
if (!significantNameForDatum ||
- (crs->baseCRS()->datum() &&
- l_datum->_isEquivalentTo(
- crs->baseCRS()->datum().get(),
- util::IComparable::Criterion::EQUIVALENT))) {
+ l_datum->_isEquivalentTo(
+ crs->baseCRS()->datumNonNull(dbContext).get(),
+ util::IComparable::Criterion::EQUIVALENT)) {
res.emplace_back(crs, 70);
} else {
res.emplace_back(crs, 60);
@@ -4877,7 +4941,6 @@ BoundCRSNNPtr BoundCRS::createFromNadgrids(const CRSNNPtr &baseCRSIn,
? NN_NO_CHECK(std::static_pointer_cast<CRS>(sourceGeographicCRS))
: baseCRSIn;
if (sourceGeographicCRS != nullptr &&
- sourceGeographicCRS->datum() != nullptr &&
sourceGeographicCRS->primeMeridian()->longitude().getSIValue() != 0.0) {
transformationSourceCRS = GeographicCRS::create(
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
@@ -4886,9 +4949,9 @@ BoundCRSNNPtr BoundCRS::createFromNadgrids(const CRSNNPtr &baseCRSIn,
datum::GeodeticReferenceFrame::create(
util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY,
- sourceGeographicCRS->datum()->nameStr() +
+ sourceGeographicCRS->datumNonNull(nullptr)->nameStr() +
" (with Greenwich prime meridian)"),
- sourceGeographicCRS->datum()->ellipsoid(),
+ sourceGeographicCRS->datumNonNull(nullptr)->ellipsoid(),
util::optional<std::string>(), datum::PrimeMeridian::GREENWICH),
sourceGeographicCRS->coordinateSystem());
}
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index fbd4fd2d..7d42fd13 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -1581,8 +1581,7 @@ DatumEnsemble::DatumEnsemble(const std::vector<DatumNNPtr> &datumsIn,
#ifdef notdef
DatumEnsemble::DatumEnsemble(const DatumEnsemble &other)
- : common::IdentifiedObject(other),
- d(internal::make_unique<Private>(*other.d)) {}
+ : common::ObjectUsage(other), d(internal::make_unique<Private>(*other.d)) {}
#endif
// ---------------------------------------------------------------------------
@@ -1621,16 +1620,83 @@ DatumEnsemble::positionalAccuracy() const {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
+DatumNNPtr
+DatumEnsemble::asDatum(const io::DatabaseContextPtr &dbContext) const {
+
+ const auto &l_datums = datums();
+ auto *grf = dynamic_cast<const GeodeticReferenceFrame *>(l_datums[0].get());
+
+ const auto &l_identifiers = identifiers();
+ if (dbContext) {
+ if (!l_identifiers.empty()) {
+ const auto &id = l_identifiers[0];
+ try {
+ auto factory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), *(id->codeSpace()));
+ if (grf) {
+ return factory->createGeodeticDatum(id->code());
+ } else {
+ return factory->createVerticalDatum(id->code());
+ }
+ } catch (const std::exception &) {
+ }
+ }
+ }
+
+ std::string l_name(nameStr());
+ if (grf) {
+ // Remap to traditional datum names
+ if (l_name == "World Geodetic System 1984 ensemble") {
+ l_name = "World Geodetic System 1984";
+ } else if (l_name ==
+ "European Terrestrial Reference System 1989 ensemble") {
+ l_name = "European Terrestrial Reference System 1989";
+ }
+ }
+ auto props =
+ util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, l_name);
+ if (isDeprecated()) {
+ props.set(common::IdentifiedObject::DEPRECATED_KEY, true);
+ }
+ if (!l_identifiers.empty()) {
+ const auto &id = l_identifiers[0];
+ props.set(metadata::Identifier::CODESPACE_KEY, *(id->codeSpace()))
+ .set(metadata::Identifier::CODE_KEY, id->code());
+ }
+ const auto &l_usages = domains();
+ if (!l_usages.empty()) {
+
+ auto array(util::ArrayOfBaseObject::create());
+ for (const auto &usage : l_usages) {
+ array->add(usage);
+ }
+ props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY,
+ util::nn_static_pointer_cast<util::BaseObject>(array));
+ }
+ const auto anchor = util::optional<std::string>();
+
+ if (grf) {
+ return GeodeticReferenceFrame::create(props, grf->ellipsoid(), anchor,
+ grf->primeMeridian());
+ } else {
+ assert(dynamic_cast<VerticalReferenceFrame *>(l_datums[0].get()));
+ return datum::VerticalReferenceFrame::create(props, anchor);
+ }
+}
+//! @endcond
+
+// ---------------------------------------------------------------------------
+
+//! @cond Doxygen_Suppress
void DatumEnsemble::_exportToWKT(
io::WKTFormatter *formatter) const // throw(FormattingException)
{
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
if (!isWKT2 || !formatter->use2019Keywords()) {
- throw io::FormattingException(
- "DatumEnsemble can only be exported to WKT2:2019");
+ return asDatum(formatter->databaseContext())->_exportToWKT(formatter);
}
- auto l_datums = datums();
+ const auto &l_datums = datums();
assert(!l_datums.empty());
formatter->startNode(io::WKTConstants::ENSEMBLE, false);
@@ -1687,7 +1753,7 @@ void DatumEnsemble::_exportToJSON(
writer->Add(l_name);
}
- auto l_datums = datums();
+ const auto &l_datums = datums();
writer->AddObjKey("members");
{
auto membersContext(writer->MakeArrayContext(false));
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index a011f397..ef5c6e02 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -1064,6 +1064,18 @@ std::string DatabaseContext::getOldProjGridName(const std::string &gridName) {
// ---------------------------------------------------------------------------
+// FIXME: as we don't support datum ensemble yet, add it from name
+static std::string removeEnsembleSuffix(const std::string &name) {
+ if (name == "World Geodetic System 1984 ensemble") {
+ return "World Geodetic System 1984";
+ } else if (name == "European Terrestrial Reference System 1989 ensemble") {
+ return "European Terrestrial Reference System 1989";
+ }
+ return name;
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Gets the alias name from an official name.
*
* @param officialName Official name. Mandatory
@@ -1084,7 +1096,13 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
}
auto res = d->run(sql, {officialName});
if (res.empty()) {
- return std::string();
+ res = d->run(
+ "SELECT auth_name, code FROM alias_name WHERE table_name = ? AND "
+ "alt_name = ? AND source IN ('EPSG', 'PROJ')",
+ {tableName, officialName});
+ if (res.size() != 1) {
+ return std::string();
+ }
}
const auto &row = res.front();
res = d->run("SELECT alt_name FROM alias_name WHERE table_name = ? AND "
@@ -1131,8 +1149,14 @@ std::list<std::string> DatabaseContext::getAliases(
}
auto resSql = d->run(sql, {officialName});
if (resSql.empty()) {
- d->cacheAliasNames_.insert(key, res);
- return res;
+ resSql = d->run("SELECT auth_name, code FROM alias_name WHERE "
+ "table_name = ? AND "
+ "alt_name = ? AND source IN ('EPSG', 'PROJ')",
+ {tableName, officialName});
+ if (resSql.size() != 1) {
+ d->cacheAliasNames_.insert(key, res);
+ return res;
+ }
}
const auto &row = resSql.front();
resolvedAuthName = row[0];
@@ -1312,21 +1336,19 @@ struct AuthorityFactory::Private {
UnitOfMeasure createUnitOfMeasure(const std::string &auth_name,
const std::string &code);
- util::PropertyMap createProperties(const std::string &code,
- const std::string &name, bool deprecated,
- const metadata::ExtentPtr &extent);
+ util::PropertyMap
+ createProperties(const std::string &code, const std::string &name,
+ bool deprecated,
+ const std::vector<ObjectDomainNNPtr> &usages);
- util::PropertyMap createProperties(const std::string &code,
- const std::string &name, bool deprecated,
- const std::string &area_of_use_auth_name,
- const std::string &area_of_use_code);
+ util::PropertyMap
+ createPropertiesSearchUsages(const std::string &table_name,
+ const std::string &code,
+ const std::string &name, bool deprecated);
- util::PropertyMap createProperties(const std::string &code,
- const std::string &name, bool deprecated,
- const std::string &remarks,
- const std::string &scope,
- const std::string &area_of_use_auth_name,
- const std::string &area_of_use_code);
+ util::PropertyMap createPropertiesSearchUsages(
+ const std::string &table_name, const std::string &code,
+ const std::string &name, bool deprecated, const std::string &remarks);
SQLResultSet run(const std::string &sql,
const ListOfParams &parameters = ListOfParams());
@@ -1385,7 +1407,7 @@ AuthorityFactory::Private::createUnitOfMeasure(const std::string &auth_name,
util::PropertyMap AuthorityFactory::Private::createProperties(
const std::string &code, const std::string &name, bool deprecated,
- const metadata::ExtentPtr &extent) {
+ const std::vector<ObjectDomainNNPtr> &usages) {
auto props = util::PropertyMap()
.set(metadata::Identifier::CODESPACE_KEY, authority())
.set(metadata::Identifier::CODE_KEY, code)
@@ -1393,45 +1415,92 @@ util::PropertyMap AuthorityFactory::Private::createProperties(
if (deprecated) {
props.set(common::IdentifiedObject::DEPRECATED_KEY, true);
}
- if (extent) {
- props.set(
- common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY,
- NN_NO_CHECK(std::static_pointer_cast<util::BaseObject>(extent)));
+ if (!usages.empty()) {
+
+ auto array(util::ArrayOfBaseObject::create());
+ for (const auto &usage : usages) {
+ array->add(usage);
+ }
+ props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY,
+ util::nn_static_pointer_cast<util::BaseObject>(array));
}
return props;
}
// ---------------------------------------------------------------------------
-util::PropertyMap AuthorityFactory::Private::createProperties(
- const std::string &code, const std::string &name, bool deprecated,
- const std::string &area_of_use_auth_name,
- const std::string &area_of_use_code) {
- return createProperties(code, name, deprecated,
- area_of_use_auth_name.empty()
- ? nullptr
- : createFactory(area_of_use_auth_name)
- ->createExtent(area_of_use_code)
- .as_nullable());
+util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages(
+ const std::string &table_name, const std::string &code,
+ const std::string &name, bool deprecated) {
+
+ const std::string sql(
+ "SELECT extent.description, extent.south_lat, "
+ "extent.north_lat, extent.west_lon, extent.east_lon, "
+ "scope.scope, "
+ "(CASE WHEN scope.scope LIKE '%large scale%' THEN 0 ELSE 1 END) "
+ "AS score "
+ "FROM usage "
+ "JOIN extent ON usage.extent_auth_name = extent.auth_name AND "
+ "usage.extent_code = extent.code "
+ "JOIN scope ON usage.scope_auth_name = scope.auth_name AND "
+ "usage.scope_code = scope.code "
+ "WHERE object_table_name = ? AND object_auth_name = ? AND "
+ "object_code = ? "
+ "ORDER BY score, usage.auth_name, usage.code");
+ auto res = run(sql, {table_name, authority(), code});
+ std::vector<ObjectDomainNNPtr> usages;
+ for (const auto &row : res) {
+ try {
+ size_t idx = 0;
+ const auto &extent_description = row[idx++];
+ const auto &south_lat_str = row[idx++];
+ const auto &north_lat_str = row[idx++];
+ const auto &west_lon_str = row[idx++];
+ const auto &east_lon_str = row[idx++];
+ const auto &scope = row[idx];
+
+ util::optional<std::string> scopeOpt;
+ if (!scope.empty()) {
+ scopeOpt = scope;
+ }
+
+ metadata::ExtentPtr extent;
+ if (south_lat_str.empty()) {
+ extent = metadata::Extent::create(
+ util::optional<std::string>(extent_description),
+ {}, {}, {})
+ .as_nullable();
+ } else {
+ double south_lat = c_locale_stod(south_lat_str);
+ double north_lat = c_locale_stod(north_lat_str);
+ double west_lon = c_locale_stod(west_lon_str);
+ double east_lon = c_locale_stod(east_lon_str);
+ auto bbox = metadata::GeographicBoundingBox::create(
+ west_lon, south_lat, east_lon, north_lat);
+ extent = metadata::Extent::create(
+ util::optional<std::string>(extent_description),
+ std::vector<metadata::GeographicExtentNNPtr>{bbox},
+ std::vector<metadata::VerticalExtentNNPtr>(),
+ std::vector<metadata::TemporalExtentNNPtr>())
+ .as_nullable();
+ }
+
+ usages.emplace_back(ObjectDomain::create(scopeOpt, extent));
+ } catch (const std::exception &) {
+ }
+ }
+ return createProperties(code, name, deprecated, std::move(usages));
}
// ---------------------------------------------------------------------------
-util::PropertyMap AuthorityFactory::Private::createProperties(
- const std::string &code, const std::string &name, bool deprecated,
- const std::string &remarks, const std::string &scope,
- const std::string &area_of_use_auth_name,
- const std::string &area_of_use_code) {
- auto props = createProperties(code, name, deprecated,
- area_of_use_auth_name.empty()
- ? nullptr
- : createFactory(area_of_use_auth_name)
- ->createExtent(area_of_use_code)
- .as_nullable());
+util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages(
+ const std::string &table_name, const std::string &code,
+ const std::string &name, bool deprecated, const std::string &remarks) {
+ auto props =
+ createPropertiesSearchUsages(table_name, code, name, deprecated);
if (!remarks.empty())
props.set(common::IdentifiedObject::REMARKS_KEY, remarks);
- if (!scope.empty())
- props.set(common::ObjectUsage::SCOPE_KEY, scope);
return props;
}
@@ -1535,9 +1604,9 @@ AuthorityFactory::CRSInfo::CRSInfo()
util::BaseObjectNNPtr
AuthorityFactory::createObject(const std::string &code) const {
- auto res = d->runWithCodeParam(
- "SELECT table_name FROM object_view WHERE auth_name = ? AND code = ?",
- code);
+ auto res = d->runWithCodeParam("SELECT table_name, type FROM object_view "
+ "WHERE auth_name = ? AND code = ?",
+ code);
if (res.empty()) {
throw NoSuchAuthorityCodeException("not found", d->authority(), code);
}
@@ -1553,8 +1622,10 @@ AuthorityFactory::createObject(const std::string &code) const {
}
throw FactoryException(msg);
}
- const auto &table_name = res.front()[0];
- if (table_name == "area") {
+ const auto &first_row = res.front();
+ const auto &table_name = first_row[0];
+ const auto &type = first_row[1];
+ if (table_name == "extent") {
return util::nn_static_pointer_cast<util::BaseObject>(
createExtent(code));
}
@@ -1571,10 +1642,18 @@ AuthorityFactory::createObject(const std::string &code) const {
createEllipsoid(code));
}
if (table_name == "geodetic_datum") {
+ if (type == "ensemble") {
+ return util::nn_static_pointer_cast<util::BaseObject>(
+ createDatumEnsemble(code, table_name));
+ }
return util::nn_static_pointer_cast<util::BaseObject>(
createGeodeticDatum(code));
}
if (table_name == "vertical_datum") {
+ if (type == "ensemble") {
+ return util::nn_static_pointer_cast<util::BaseObject>(
+ createDatumEnsemble(code, table_name));
+ }
return util::nn_static_pointer_cast<util::BaseObject>(
createVerticalDatum(code));
}
@@ -1638,19 +1717,19 @@ AuthorityFactory::createExtent(const std::string &code) const {
return NN_NO_CHECK(extent);
}
}
- auto sql = "SELECT name, south_lat, north_lat, west_lon, east_lon, "
- "deprecated FROM area WHERE auth_name = ? AND code = ?";
+ auto sql = "SELECT description, south_lat, north_lat, west_lon, east_lon, "
+ "deprecated FROM extent WHERE auth_name = ? AND code = ?";
auto res = d->runWithCodeParam(sql, code);
if (res.empty()) {
- throw NoSuchAuthorityCodeException("area not found", d->authority(),
+ throw NoSuchAuthorityCodeException("extent not found", d->authority(),
code);
}
try {
const auto &row = res.front();
- const auto &name = row[0];
+ const auto &description = row[0];
if (row[1].empty()) {
auto extent = metadata::Extent::create(
- util::optional<std::string>(name), {}, {}, {});
+ util::optional<std::string>(description), {}, {}, {});
d->context()->d->cache(cacheKey, extent);
return extent;
}
@@ -1662,7 +1741,7 @@ AuthorityFactory::createExtent(const std::string &code) const {
west_lon, south_lat, east_lon, north_lat);
auto extent = metadata::Extent::create(
- util::optional<std::string>(name),
+ util::optional<std::string>(description),
std::vector<metadata::GeographicExtentNNPtr>{bbox},
std::vector<metadata::VerticalExtentNNPtr>(),
std::vector<metadata::TemporalExtentNNPtr>());
@@ -1670,7 +1749,7 @@ AuthorityFactory::createExtent(const std::string &code) const {
return extent;
} catch (const std::exception &ex) {
- throw buildFactoryException("area", code, ex);
+ throw buildFactoryException("extent", code, ex);
}
}
@@ -1816,7 +1895,7 @@ AuthorityFactory::createPrimeMeridian(const std::string &code) const {
normalizeMeasure(uom_code, longitude, normalized_uom_code);
auto uom = d->createUnitOfMeasure(uom_auth_name, normalized_uom_code);
- auto props = d->createProperties(code, name, deprecated, nullptr);
+ auto props = d->createProperties(code, name, deprecated, {});
auto pm = datum::PrimeMeridian::create(
props, common::Angle(normalized_value, uom));
d->context()->d->cache(cacheKey, pm);
@@ -1897,7 +1976,7 @@ AuthorityFactory::createEllipsoid(const std::string &code) const {
const auto &body = row[6];
const bool deprecated = row[7] == "1";
auto uom = d->createUnitOfMeasure(uom_auth_name, uom_code);
- auto props = d->createProperties(code, name, deprecated, nullptr);
+ auto props = d->createProperties(code, name, deprecated, {});
if (!inv_flattening_str.empty()) {
auto ellps = datum::Ellipsoid::createFlattenedSphere(
props, common::Length(semi_major_axis, uom),
@@ -1940,13 +2019,14 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const {
return NN_NO_CHECK(datum);
}
}
- auto res = d->runWithCodeParam(
- "SELECT name, ellipsoid_auth_name, ellipsoid_code, "
- "prime_meridian_auth_name, prime_meridian_code, area_of_use_auth_name, "
- "area_of_use_code, publication_date, deprecated FROM geodetic_datum "
- "WHERE "
- "auth_name = ? AND code = ?",
- code);
+ auto res =
+ d->runWithCodeParam("SELECT name, ellipsoid_auth_name, ellipsoid_code, "
+ "prime_meridian_auth_name, prime_meridian_code, "
+ "publication_date, frame_reference_epoch, "
+ "deprecated FROM geodetic_datum "
+ "WHERE "
+ "auth_name = ? AND code = ?",
+ code);
if (res.empty()) {
throw NoSuchAuthorityCodeException("geodetic datum not found",
d->authority(), code);
@@ -1958,22 +2038,29 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const {
const auto &ellipsoid_code = row[2];
const auto &prime_meridian_auth_name = row[3];
const auto &prime_meridian_code = row[4];
- const auto &area_of_use_auth_name = row[5];
- const auto &area_of_use_code = row[6];
- const auto &publication_date = row[7];
- const bool deprecated = row[8] == "1";
+ const auto &publication_date = row[5];
+ const auto &frame_reference_epoch = row[6];
+ const bool deprecated = row[7] == "1";
auto ellipsoid = d->createFactory(ellipsoid_auth_name)
->createEllipsoid(ellipsoid_code);
auto pm = d->createFactory(prime_meridian_auth_name)
->createPrimeMeridian(prime_meridian_code);
- auto props = d->createProperties(
- code, name, deprecated, area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages(
+ "geodetic_datum", code, removeEnsembleSuffix(name), deprecated);
auto anchor = util::optional<std::string>();
if (!publication_date.empty()) {
props.set("PUBLICATION_DATE", publication_date);
}
auto datum =
- datum::GeodeticReferenceFrame::create(props, ellipsoid, anchor, pm);
+ frame_reference_epoch.empty()
+ ? datum::GeodeticReferenceFrame::create(props, ellipsoid,
+ anchor, pm)
+ : util::nn_static_pointer_cast<datum::GeodeticReferenceFrame>(
+ datum::DynamicGeodeticReferenceFrame::create(
+ props, ellipsoid, anchor, pm,
+ common::Measure(c_locale_stod(frame_reference_epoch),
+ common::UnitOfMeasure::YEAR),
+ util::optional<std::string>()));
d->context()->d->cache(cacheKey, datum);
return datum;
} catch (const std::exception &ex) {
@@ -1993,10 +2080,11 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const {
datum::VerticalReferenceFrameNNPtr
AuthorityFactory::createVerticalDatum(const std::string &code) const {
- auto res = d->runWithCodeParam(
- "SELECT name, area_of_use_auth_name, area_of_use_code, deprecated FROM "
- "vertical_datum WHERE auth_name = ? AND code = ?",
- code);
+ auto res =
+ d->runWithCodeParam("SELECT name, publication_date, "
+ "frame_reference_epoch, deprecated FROM "
+ "vertical_datum WHERE auth_name = ? AND code = ?",
+ code);
if (res.empty()) {
throw NoSuchAuthorityCodeException("vertical datum not found",
d->authority(), code);
@@ -2004,13 +2092,24 @@ AuthorityFactory::createVerticalDatum(const std::string &code) const {
try {
const auto &row = res.front();
const auto &name = row[0];
- const auto &area_of_use_auth_name = row[1];
- const auto &area_of_use_code = row[2];
+ const auto &publication_date = row[1];
+ const auto &frame_reference_epoch = row[2];
const bool deprecated = row[3] == "1";
- auto props = d->createProperties(
- code, name, deprecated, area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages("vertical_datum", code,
+ name, deprecated);
+ if (!publication_date.empty()) {
+ props.set("PUBLICATION_DATE", publication_date);
+ }
auto anchor = util::optional<std::string>();
- return datum::VerticalReferenceFrame::create(props, anchor);
+ if (frame_reference_epoch.empty()) {
+ return datum::VerticalReferenceFrame::create(props, anchor);
+ } else {
+ return datum::DynamicVerticalReferenceFrame::create(
+ props, anchor, util::optional<datum::RealizationMethod>(),
+ common::Measure(c_locale_stod(frame_reference_epoch),
+ common::UnitOfMeasure::YEAR),
+ util::optional<std::string>());
+ }
} catch (const std::exception &ex) {
throw buildFactoryException("vertical reference frame", code, ex);
}
@@ -2018,6 +2117,62 @@ AuthorityFactory::createVerticalDatum(const std::string &code) const {
// ---------------------------------------------------------------------------
+/** \brief Returns a datum::DatumEnsemble from the specified code.
+ *
+ * @param code Object code allocated by authority.
+ * @param type "geodetic_datum", "vertical_datum" or empty string if unknown
+ * @return object.
+ * @throw NoSuchAuthorityCodeException
+ * @throw FactoryException
+ */
+
+datum::DatumEnsembleNNPtr
+AuthorityFactory::createDatumEnsemble(const std::string &code,
+ const std::string &type) const {
+ auto res = d->run(
+ "SELECT 'geodetic_datum', name, ensemble_accuracy, deprecated FROM "
+ "geodetic_datum WHERE "
+ "auth_name = ? AND code = ? AND ensemble_accuracy IS NOT NULL "
+ "UNION ALL "
+ "SELECT 'vertical_datum', name, ensemble_accuracy, deprecated FROM "
+ "vertical_datum WHERE "
+ "auth_name = ? AND code = ? AND ensemble_accuracy IS NOT NULL",
+ {d->authority(), code, d->authority(), code});
+ if (res.empty()) {
+ throw NoSuchAuthorityCodeException("datum ensemble not found",
+ d->authority(), code);
+ }
+ for (const auto &row : res) {
+ const std::string &gotType = row[0];
+ const std::string &name = row[1];
+ const std::string &ensembleAccuracy = row[2];
+ const bool deprecated = row[3] == "1";
+ if (type.empty() || type == gotType) {
+ auto resMembers =
+ d->run("SELECT member_auth_name, member_code FROM " + gotType +
+ "_ensemble_member WHERE "
+ "ensemble_auth_name = ? AND ensemble_code = ? "
+ "ORDER BY sequence",
+ {d->authority(), code});
+
+ std::vector<datum::DatumNNPtr> members;
+ for (const auto &memberRow : resMembers) {
+ members.push_back(
+ d->createFactory(memberRow[0])->createDatum(memberRow[1]));
+ }
+ auto props = d->createPropertiesSearchUsages(gotType, code, name,
+ deprecated);
+ return datum::DatumEnsemble::create(
+ props, std::move(members),
+ metadata::PositionalAccuracy::create(ensembleAccuracy));
+ }
+ }
+ throw NoSuchAuthorityCodeException("datum ensemble not found",
+ d->authority(), code);
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Returns a datum::Datum from the specified code.
*
* @param code Object code allocated by authority.
@@ -2105,7 +2260,13 @@ AuthorityFactory::createCoordinateSystem(const std::string &code) const {
const auto &orientation = row[2];
const auto &uom_auth_name = row[3];
const auto &uom_code = row[4];
- auto uom = d->createUnitOfMeasure(uom_auth_name, uom_code);
+ if (uom_auth_name.empty() && csType != "ordinal") {
+ throw FactoryException("no unit of measure for an axis is only "
+ "supported for ordinatal CS");
+ }
+ auto uom = uom_auth_name.empty()
+ ? common::UnitOfMeasure::NONE
+ : d->createUnitOfMeasure(uom_auth_name, uom_code);
auto props =
util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name);
const cs::AxisDirection *direction =
@@ -2176,6 +2337,9 @@ AuthorityFactory::createCoordinateSystem(const std::string &code) const {
}
throw FactoryException("invalid number of axis for VerticalCS");
}
+ if (csType == "ordinal") {
+ return cacheAndRet(cs::OrdinalCS::create(props, axisList));
+ }
throw FactoryException("unhandled coordinate system type: " + csType);
}
@@ -2216,18 +2380,16 @@ static crs::GeodeticCRSNNPtr
cloneWithProps(const crs::GeodeticCRSNNPtr &geodCRS,
const util::PropertyMap &props) {
auto cs = geodCRS->coordinateSystem();
- auto datum = geodCRS->datum();
- if (!datum) {
- return geodCRS;
- }
auto ellipsoidalCS = util::nn_dynamic_pointer_cast<cs::EllipsoidalCS>(cs);
if (ellipsoidalCS) {
- return crs::GeographicCRS::create(props, NN_NO_CHECK(datum),
+ return crs::GeographicCRS::create(props, geodCRS->datum(),
+ geodCRS->datumEnsemble(),
NN_NO_CHECK(ellipsoidalCS));
}
auto geocentricCS = util::nn_dynamic_pointer_cast<cs::CartesianCS>(cs);
if (geocentricCS) {
- return crs::GeodeticCRS::create(props, NN_NO_CHECK(datum),
+ return crs::GeodeticCRS::create(props, geodCRS->datum(),
+ geodCRS->datumEnsemble(),
NN_NO_CHECK(geocentricCS));
}
return geodCRS;
@@ -2250,7 +2412,7 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
}
std::string sql("SELECT name, type, coordinate_system_auth_name, "
"coordinate_system_code, datum_auth_name, datum_code, "
- "area_of_use_auth_name, area_of_use_code, text_definition, "
+ "text_definition, "
"deprecated FROM "
"geodetic_crs WHERE auth_name = ? AND code = ?");
if (geographicOnly) {
@@ -2270,13 +2432,11 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
const auto &cs_code = row[3];
const auto &datum_auth_name = row[4];
const auto &datum_code = row[5];
- const auto &area_of_use_auth_name = row[6];
- const auto &area_of_use_code = row[7];
- const auto &text_definition = row[8];
- const bool deprecated = row[9] == "1";
+ const auto &text_definition = row[6];
+ const bool deprecated = row[7] == "1";
- auto props = d->createProperties(
- code, name, deprecated, area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages("geodetic_crs", code, name,
+ deprecated);
if (!text_definition.empty()) {
DatabaseContext::Private::RecursionDetector detector(d->context());
@@ -2359,7 +2519,7 @@ AuthorityFactory::createVerticalCRS(const std::string &code) const {
auto res = d->runWithCodeParam(
"SELECT name, coordinate_system_auth_name, "
"coordinate_system_code, datum_auth_name, datum_code, "
- "area_of_use_auth_name, area_of_use_code, deprecated FROM "
+ "deprecated FROM "
"vertical_crs WHERE auth_name = ? AND code = ?",
code);
if (res.empty()) {
@@ -2373,16 +2533,14 @@ AuthorityFactory::createVerticalCRS(const std::string &code) const {
const auto &cs_code = row[2];
const auto &datum_auth_name = row[3];
const auto &datum_code = row[4];
- const auto &area_of_use_auth_name = row[5];
- const auto &area_of_use_code = row[6];
- const bool deprecated = row[7] == "1";
+ const bool deprecated = row[5] == "1";
auto cs =
d->createFactory(cs_auth_name)->createCoordinateSystem(cs_code);
auto datum =
d->createFactory(datum_auth_name)->createVerticalDatum(datum_code);
- auto props = d->createProperties(
- code, name, deprecated, area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages("vertical_crs", code, name,
+ deprecated);
auto verticalCS = util::nn_dynamic_pointer_cast<cs::VerticalCS>(cs);
if (verticalCS) {
@@ -2412,8 +2570,7 @@ operation::ConversionNNPtr
AuthorityFactory::createConversion(const std::string &code) const {
static const char *sql =
- "SELECT name, description, scope, "
- "area_of_use_auth_name, area_of_use_code, "
+ "SELECT name, description, "
"method_auth_name, method_code, method_name, "
"param1_auth_name, param1_code, param1_name, param1_value, "
@@ -2463,9 +2620,6 @@ AuthorityFactory::createConversion(const std::string &code) const {
size_t idx = 0;
const auto &name = row[idx++];
const auto &description = row[idx++];
- const auto &scope = row[idx++];
- const auto &area_of_use_auth_name = row[idx++];
- const auto &area_of_use_code = row[idx++];
const auto &method_auth_name = row[idx++];
const auto &method_code = row[idx++];
const auto &method_name = row[idx++];
@@ -2498,9 +2652,11 @@ AuthorityFactory::createConversion(const std::string &code) const {
}
const bool deprecated = row[base_param_idx + N_MAX_PARAMS * 6] == "1";
- auto propConversion =
- d->createProperties(code, name, deprecated, description, scope,
- area_of_use_auth_name, area_of_use_code);
+ auto propConversion = d->createPropertiesSearchUsages(
+ "conversion", code, name, deprecated);
+ if (!description.empty())
+ propConversion.set(common::IdentifiedObject::REMARKS_KEY,
+ description);
auto propMethod = util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY, method_name);
@@ -2556,7 +2712,7 @@ AuthorityFactory::Private::createProjectedCRSBegin(const std::string &code) {
"SELECT name, coordinate_system_auth_name, "
"coordinate_system_code, geodetic_crs_auth_name, geodetic_crs_code, "
"conversion_auth_name, conversion_code, "
- "area_of_use_auth_name, area_of_use_code, text_definition, "
+ "text_definition, "
"deprecated FROM projected_crs WHERE auth_name = ? AND code = ?",
code);
}
@@ -2581,13 +2737,11 @@ AuthorityFactory::Private::createProjectedCRSEnd(const std::string &code,
const auto &geodetic_crs_code = row[4];
const auto &conversion_auth_name = row[5];
const auto &conversion_code = row[6];
- const auto &area_of_use_auth_name = row[7];
- const auto &area_of_use_code = row[8];
- const auto &text_definition = row[9];
- const bool deprecated = row[10] == "1";
+ const auto &text_definition = row[7];
+ const bool deprecated = row[8] == "1";
- auto props = createProperties(code, name, deprecated,
- area_of_use_auth_name, area_of_use_code);
+ auto props = createPropertiesSearchUsages("projected_crs", code, name,
+ deprecated);
if (!text_definition.empty()) {
DatabaseContext::Private::RecursionDetector detector(context());
@@ -2670,12 +2824,12 @@ AuthorityFactory::Private::createProjectedCRSEnd(const std::string &code,
crs::CompoundCRSNNPtr
AuthorityFactory::createCompoundCRS(const std::string &code) const {
- auto res = d->runWithCodeParam(
- "SELECT name, horiz_crs_auth_name, horiz_crs_code, "
- "vertical_crs_auth_name, vertical_crs_code, "
- "area_of_use_auth_name, area_of_use_code, deprecated FROM "
- "compound_crs WHERE auth_name = ? AND code = ?",
- code);
+ auto res =
+ d->runWithCodeParam("SELECT name, horiz_crs_auth_name, horiz_crs_code, "
+ "vertical_crs_auth_name, vertical_crs_code, "
+ "deprecated FROM "
+ "compound_crs WHERE auth_name = ? AND code = ?",
+ code);
if (res.empty()) {
throw NoSuchAuthorityCodeException("compoundCRS not found",
d->authority(), code);
@@ -2687,9 +2841,7 @@ AuthorityFactory::createCompoundCRS(const std::string &code) const {
const auto &horiz_crs_code = row[2];
const auto &vertical_crs_auth_name = row[3];
const auto &vertical_crs_code = row[4];
- const auto &area_of_use_auth_name = row[5];
- const auto &area_of_use_code = row[6];
- const bool deprecated = row[7] == "1";
+ const bool deprecated = row[5] == "1";
auto horizCRS =
d->createFactory(horiz_crs_auth_name)
@@ -2697,8 +2849,8 @@ AuthorityFactory::createCompoundCRS(const std::string &code) const {
auto vertCRS = d->createFactory(vertical_crs_auth_name)
->createVerticalCRS(vertical_crs_code);
- auto props = d->createProperties(
- code, name, deprecated, area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages("compound_crs", code, name,
+ deprecated);
return crs::CompoundCRS::create(
props, std::vector<crs::CRSNNPtr>{horizCRS, vertCRS});
} catch (const std::exception &ex) {
@@ -2831,10 +2983,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
if (type == "helmert_transformation") {
auto res = d->runWithCodeParam(
- "SELECT name, description, scope, "
+ "SELECT name, description, "
"method_auth_name, method_code, method_name, "
"source_crs_auth_name, source_crs_code, target_crs_auth_name, "
- "target_crs_code, area_of_use_auth_name, area_of_use_code, "
+ "target_crs_code, "
"accuracy, tx, ty, tz, translation_uom_auth_name, "
"translation_uom_code, rx, ry, rz, rotation_uom_auth_name, "
"rotation_uom_code, scale_difference, "
@@ -2858,7 +3010,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
size_t idx = 0;
const auto &name = row[idx++];
const auto &description = row[idx++];
- const auto &scope = row[idx++];
const auto &method_auth_name = row[idx++];
const auto &method_code = row[idx++];
const auto &method_name = row[idx++];
@@ -2866,8 +3017,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
const auto &source_crs_code = row[idx++];
const auto &target_crs_auth_name = row[idx++];
const auto &target_crs_code = row[idx++];
- const auto &area_of_use_auth_name = row[idx++];
- const auto &area_of_use_code = row[idx++];
const auto &accuracy = row[idx++];
const auto &tx = row[idx++];
@@ -3046,9 +3195,8 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
values.emplace_back(createLength(pz, uom_pivot));
}
- auto props =
- d->createProperties(code, name, deprecated, description, scope,
- area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages(
+ type, code, name, deprecated, description);
if (!operation_version.empty()) {
props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY,
operation_version);
@@ -3076,10 +3224,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
if (type == "grid_transformation") {
auto res = d->runWithCodeParam(
- "SELECT name, description, scope, "
+ "SELECT name, description, "
"method_auth_name, method_code, method_name, "
"source_crs_auth_name, source_crs_code, target_crs_auth_name, "
- "target_crs_code, area_of_use_auth_name, area_of_use_code, "
+ "target_crs_code, "
"accuracy, grid_param_auth_name, grid_param_code, grid_param_name, "
"grid_name, "
"grid2_param_auth_name, grid2_param_code, grid2_param_name, "
@@ -3098,7 +3246,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
size_t idx = 0;
const auto &name = row[idx++];
const auto &description = row[idx++];
- const auto &scope = row[idx++];
const auto &method_auth_name = row[idx++];
const auto &method_code = row[idx++];
const auto &method_name = row[idx++];
@@ -3106,8 +3253,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
const auto &source_crs_code = row[idx++];
const auto &target_crs_auth_name = row[idx++];
const auto &target_crs_code = row[idx++];
- const auto &area_of_use_auth_name = row[idx++];
- const auto &area_of_use_code = row[idx++];
const auto &accuracy = row[idx++];
const auto &grid_param_auth_name = row[idx++];
const auto &grid_param_code = row[idx++];
@@ -3162,9 +3307,8 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
operation::ParameterValue::createFilename(grid2_name));
}
- auto props =
- d->createProperties(code, name, deprecated, description, scope,
- area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages(
+ type, code, name, deprecated, description);
if (!operation_version.empty()) {
props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY,
operation_version);
@@ -3197,10 +3341,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
std::ostringstream buffer;
buffer.imbue(std::locale::classic());
buffer
- << "SELECT name, description, scope, "
+ << "SELECT name, description, "
"method_auth_name, method_code, method_name, "
"source_crs_auth_name, source_crs_code, target_crs_auth_name, "
- "target_crs_code, area_of_use_auth_name, area_of_use_code, "
+ "target_crs_code, "
"accuracy";
constexpr int N_MAX_PARAMS = 7;
for (int i = 1; i <= N_MAX_PARAMS; ++i) {
@@ -3225,7 +3369,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
size_t idx = 0;
const auto &name = row[idx++];
const auto &description = row[idx++];
- const auto &scope = row[idx++];
const auto &method_auth_name = row[idx++];
const auto &method_code = row[idx++];
const auto &method_name = row[idx++];
@@ -3233,8 +3376,6 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
const auto &source_crs_code = row[idx++];
const auto &target_crs_auth_name = row[idx++];
const auto &target_crs_code = row[idx++];
- const auto &area_of_use_auth_name = row[idx++];
- const auto &area_of_use_code = row[idx++];
const auto &accuracy = row[idx++];
const size_t base_param_idx = idx;
@@ -3279,9 +3420,8 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
d->createFactory(target_crs_auth_name)
->createCoordinateReferenceSystem(target_crs_code);
- auto props =
- d->createProperties(code, name, deprecated, description, scope,
- area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages(
+ type, code, name, deprecated, description);
if (!operation_version.empty()) {
props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY,
operation_version);
@@ -3338,10 +3478,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
if (allowConcatenated && type == "concatenated_operation") {
auto res = d->runWithCodeParam(
- "SELECT name, description, scope, "
+ "SELECT name, description, "
"source_crs_auth_name, source_crs_code, "
"target_crs_auth_name, target_crs_code, "
- "area_of_use_auth_name, area_of_use_code, accuracy, "
+ "accuracy, "
"operation_version, deprecated FROM "
"concatenated_operation WHERE auth_name = ? AND code = ?",
code);
@@ -3362,13 +3502,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
size_t idx = 0;
const auto &name = row[idx++];
const auto &description = row[idx++];
- const auto &scope = row[idx++];
const auto &source_crs_auth_name = row[idx++];
const auto &source_crs_code = row[idx++];
const auto &target_crs_auth_name = row[idx++];
const auto &target_crs_code = row[idx++];
- const auto &area_of_use_auth_name = row[idx++];
- const auto &area_of_use_code = row[idx++];
const auto &accuracy = row[idx++];
const auto &operation_version = row[idx++];
const auto &deprecated_str = row[idx++];
@@ -3392,9 +3529,8 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation(
->createCoordinateReferenceSystem(target_crs_code),
operations);
- auto props =
- d->createProperties(code, name, deprecated, description, scope,
- area_of_use_auth_name, area_of_use_code);
+ auto props = d->createPropertiesSearchUsages(
+ type, code, name, deprecated, description);
if (!operation_version.empty()) {
props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY,
operation_version);
@@ -3602,11 +3738,17 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
sql = "SELECT source_crs_auth_name, source_crs_code, "
"target_crs_auth_name, target_crs_code, "
"cov.auth_name, cov.code, cov.table_name, "
- "area.south_lat, area.west_lon, area.north_lat, area.east_lon, "
+ "extent.south_lat, extent.west_lon, extent.north_lat, "
+ "extent.east_lon, "
"ss.replacement_auth_name, ss.replacement_code FROM "
- "coordinate_operation_view cov JOIN area "
- "ON cov.area_of_use_auth_name = area.auth_name AND "
- "cov.area_of_use_code = area.code "
+ "coordinate_operation_view cov "
+ "JOIN usage ON "
+ "usage.object_table_name = cov.table_name AND "
+ "usage.object_auth_name = cov.auth_name AND "
+ "usage.object_code = cov.code "
+ "JOIN extent "
+ "ON extent.auth_name = usage.extent_auth_name AND "
+ "extent.code = usage.extent_code "
"LEFT JOIN supersession ss ON "
"ss.superseded_table_name = cov.table_name AND "
"ss.superseded_auth_name = cov.auth_name AND "
@@ -3618,11 +3760,17 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes(
sql = "SELECT source_crs_auth_name, source_crs_code, "
"target_crs_auth_name, target_crs_code, "
"cov.auth_name, cov.code, cov.table_name, "
- "area.south_lat, area.west_lon, area.north_lat, area.east_lon "
+ "extent.south_lat, extent.west_lon, extent.north_lat, "
+ "extent.east_lon "
"FROM "
- "coordinate_operation_view cov JOIN area "
- "ON cov.area_of_use_auth_name = area.auth_name AND "
- "cov.area_of_use_code = area.code "
+ "coordinate_operation_view cov "
+ "JOIN usage ON "
+ "usage.object_table_name = cov.table_name AND "
+ "usage.object_auth_name = cov.auth_name AND "
+ "usage.object_code = cov.code "
+ "JOIN extent "
+ "ON extent.auth_name = usage.extent_auth_name AND "
+ "extent.code = usage.extent_code "
"WHERE ";
}
ListOfParams params;
@@ -3955,10 +4103,20 @@ AuthorityFactory::createFromCRSCodesWithIntermediates(
"ss2.same_source_target_crs = 1 ");
const std::string joinArea(
(discardSuperseded ? joinSupersession : std::string()) +
- "JOIN area a1 ON v1.area_of_use_auth_name = a1.auth_name "
- "AND v1.area_of_use_code = a1.code "
- "JOIN area a2 ON v2.area_of_use_auth_name = a2.auth_name "
- "AND v2.area_of_use_code = a2.code ");
+ "JOIN usage u1 ON "
+ "u1.object_table_name = v1.table_name AND "
+ "u1.object_auth_name = v1.auth_name AND "
+ "u1.object_code = v1.code "
+ "JOIN extent a1 "
+ "ON a1.auth_name = u1.extent_auth_name AND "
+ "a1.code = u1.extent_code "
+ "JOIN usage u2 ON "
+ "u2.object_table_name = v2.table_name AND "
+ "u2.object_auth_name = v2.auth_name AND "
+ "u2.object_code = v2.code "
+ "JOIN extent a2 "
+ "ON a2.auth_name = u2.extent_auth_name AND "
+ "a2.code = u2.extent_code ");
const std::string orderBy(
"ORDER BY (CASE WHEN accuracy1 is NULL THEN 1 ELSE 0 END) + "
"(CASE WHEN accuracy2 is NULL THEN 1 ELSE 0 END), "
@@ -4399,11 +4557,20 @@ AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates(
"AND g_v2s.code = v2.source_crs_code "
"AND g_v2t.auth_name = v2.target_crs_auth_name "
"AND g_v2t.code = v2.target_crs_code ");
- const std::string joinArea(
- "JOIN area a1 ON v1.area_of_use_auth_name = a1.auth_name "
- "AND v1.area_of_use_code = a1.code "
- "JOIN area a2 ON v2.area_of_use_auth_name = a2.auth_name "
- "AND v2.area_of_use_code = a2.code ");
+ const std::string joinArea("JOIN usage u1 ON "
+ "u1.object_table_name = v1.table_name AND "
+ "u1.object_auth_name = v1.auth_name AND "
+ "u1.object_code = v1.code "
+ "JOIN extent a1 "
+ "ON a1.auth_name = u1.extent_auth_name AND "
+ "a1.code = u1.extent_code "
+ "JOIN usage u2 ON "
+ "u2.object_table_name = v2.table_name AND "
+ "u2.object_auth_name = v2.auth_name AND "
+ "u2.object_code = v2.code "
+ "JOIN extent a2 "
+ "ON a2.auth_name = u2.extent_auth_name AND "
+ "a2.code = u2.extent_code ");
auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode,
targetCRSAuthName, targetCRSCode};
@@ -5094,9 +5261,17 @@ AuthorityFactory::getAuthorityCodes(const ObjectType &type,
case ObjectType::GEODETIC_REFERENCE_FRAME:
sql = "SELECT code FROM geodetic_datum WHERE ";
break;
+ case ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME:
+ sql = "SELECT code FROM geodetic_datum WHERE "
+ "frame_reference_epoch IS NOT NULL AND ";
+ break;
case ObjectType::VERTICAL_REFERENCE_FRAME:
sql = "SELECT code FROM vertical_datum WHERE ";
break;
+ case ObjectType::DYNAMIC_VERTICAL_REFERENCE_FRAME:
+ sql = "SELECT code FROM vertical_datum WHERE "
+ "frame_reference_epoch IS NOT NULL AND ";
+ break;
case ObjectType::CRS:
sql = "SELECT code FROM crs_view WHERE ";
break;
@@ -5206,13 +5381,23 @@ AuthorityFactory::getDescriptionText(const std::string &code) const {
* @throw FactoryException
*/
std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {
+
+ const auto getSqlArea = [](const std::string &table_name) {
+ return "JOIN usage u ON "
+ "u.object_table_name = '" +
+ table_name + "' AND "
+ "u.object_auth_name = c.auth_name AND "
+ "u.object_code = c.code "
+ "JOIN extent a "
+ "ON a.auth_name = u.extent_auth_name AND "
+ "a.code = u.extent_code ";
+ };
+
std::string sql = "SELECT c.auth_name, c.code, c.name, c.type, "
"c.deprecated, "
"a.west_lon, a.south_lat, a.east_lon, a.north_lat, "
- "a.name, NULL FROM geodetic_crs c "
- "JOIN area a ON "
- "c.area_of_use_auth_name = a.auth_name AND "
- "c.area_of_use_code = a.code";
+ "a.description, NULL FROM geodetic_crs c " +
+ getSqlArea("geodetic_crs");
ListOfParams params;
if (d->hasAuthorityRestriction()) {
sql += " WHERE c.auth_name = ?";
@@ -5222,10 +5407,9 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {
sql += "SELECT c.auth_name, c.code, c.name, 'projected', "
"c.deprecated, "
"a.west_lon, a.south_lat, a.east_lon, a.north_lat, "
- "a.name, cm.name AS conversion_method_name FROM projected_crs c "
- "JOIN area a ON "
- "c.area_of_use_auth_name = a.auth_name AND "
- "c.area_of_use_code = a.code "
+ "a.description, cm.name AS conversion_method_name FROM "
+ "projected_crs c " +
+ getSqlArea("projected_crs") +
"LEFT JOIN conversion_table conv ON "
"c.conversion_auth_name = conv.auth_name AND "
"c.conversion_code = conv.code "
@@ -5240,10 +5424,8 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {
sql += "SELECT c.auth_name, c.code, c.name, 'vertical', "
"c.deprecated, "
"a.west_lon, a.south_lat, a.east_lon, a.north_lat, "
- "a.name, NULL FROM vertical_crs c "
- "JOIN area a ON "
- "c.area_of_use_auth_name = a.auth_name AND "
- "c.area_of_use_code = a.code";
+ "a.description, NULL FROM vertical_crs c " +
+ getSqlArea("vertical_crs");
if (d->hasAuthorityRestriction()) {
sql += " WHERE c.auth_name = ?";
params.emplace_back(d->authority());
@@ -5252,10 +5434,8 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {
sql += "SELECT c.auth_name, c.code, c.name, 'compound', "
"c.deprecated, "
"a.west_lon, a.south_lat, a.east_lon, a.north_lat, "
- "a.name, NULL FROM compound_crs c "
- "JOIN area a ON "
- "c.area_of_use_auth_name = a.auth_name AND "
- "c.area_of_use_code = a.code";
+ "a.description, NULL FROM compound_crs c " +
+ getSqlArea("compound_crs");
if (d->hasAuthorityRestriction()) {
sql += " WHERE c.auth_name = ?";
params.emplace_back(d->authority());
@@ -5416,7 +5596,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias(
if (res.empty()) { // shouldn't happen normally
return std::string();
}
- return res.front()[0];
+ return removeEnsembleSuffix(res.front()[0]);
}
}
return std::string();
@@ -5466,7 +5646,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias(
outTableName = row[1];
outAuthName = row[2];
outCode = row[3];
- return row[0];
+ return removeEnsembleSuffix(row[0]);
}
}
@@ -5571,10 +5751,18 @@ AuthorityFactory::createObjectsFromNameEx(
res.emplace_back(
TableType("geodetic_datum", std::string()));
break;
+ case ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME:
+ res.emplace_back(
+ TableType("geodetic_datum", "frame_reference_epoch"));
+ break;
case ObjectType::VERTICAL_REFERENCE_FRAME:
res.emplace_back(
TableType("vertical_datum", std::string()));
break;
+ case ObjectType::DYNAMIC_VERTICAL_REFERENCE_FRAME:
+ res.emplace_back(
+ TableType("vertical_datum", "frame_reference_epoch"));
+ break;
case ObjectType::CRS:
res.emplace_back(TableType("geodetic_crs", std::string()));
res.emplace_back(TableType("projected_crs", std::string()));
@@ -5653,9 +5841,13 @@ AuthorityFactory::createObjectsFromNameEx(
sql += tableNameTypePair.first;
sql += " WHERE 1 = 1 ";
if (!tableNameTypePair.second.empty()) {
- sql += "AND type = '";
- sql += tableNameTypePair.second;
- sql += "' ";
+ if (tableNameTypePair.second == "frame_reference_epoch") {
+ sql += "AND frame_reference_epoch IS NOT NULL ";
+ } else {
+ sql += "AND type = '";
+ sql += tableNameTypePair.second;
+ sql += "' ";
+ }
}
if (deprecated) {
sql += "AND deprecated = 1 ";
@@ -5683,9 +5875,13 @@ AuthorityFactory::createObjectsFromNameEx(
sql += tableNameTypePair.first;
sql += "' ";
if (!tableNameTypePair.second.empty()) {
- sql += "AND ov.type = '";
- sql += tableNameTypePair.second;
- sql += "' ";
+ if (tableNameTypePair.second == "frame_reference_epoch") {
+ sql += "AND ov.frame_reference_epoch IS NOT NULL ";
+ } else {
+ sql += "AND ov.type = '";
+ sql += tableNameTypePair.second;
+ sql += "' ";
+ }
}
if (deprecated) {
sql += "AND ov.deprecated = 1 ";
@@ -5925,7 +6121,7 @@ std::list<std::pair<std::string, std::string>>
AuthorityFactory::listAreaOfUseFromName(const std::string &name,
bool approximateMatch) const {
std::string sql(
- "SELECT auth_name, code FROM area WHERE deprecated = 0 AND ");
+ "SELECT auth_name, code FROM extent WHERE deprecated = 0 AND ");
ListOfParams params;
if (d->hasAuthorityRestriction()) {
sql += " auth_name = ? AND ";
diff --git a/src/proj.h b/src/proj.h
index 512dcd84..b758663a 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -1249,6 +1249,23 @@ PJ PROJ_DLL *proj_crs_get_sub_crs(PJ_CONTEXT *ctx, const PJ *crs, int index);
PJ PROJ_DLL *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs);
+PJ PROJ_DLL *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs);
+
+PJ PROJ_DLL *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs);
+
+int PROJ_DLL proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx,
+ const PJ *datum_ensemble);
+
+double PROJ_DLL proj_datum_ensemble_get_accuracy(PJ_CONTEXT *ctx,
+ const PJ *datum_ensemble);
+
+PJ PROJ_DLL *proj_datum_ensemble_get_member(PJ_CONTEXT *ctx,
+ const PJ *datum_ensemble,
+ int member_index);
+
+double PROJ_DLL proj_dynamic_datum_get_frame_reference_epoch(PJ_CONTEXT *ctx,
+ const PJ *datum);
+
PJ PROJ_DLL *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs);
PJ_COORDINATE_SYSTEM_TYPE PROJ_DLL proj_cs_get_type(PJ_CONTEXT *ctx,
diff --git a/src/proj_experimental.h b/src/proj_experimental.h
index 50f1fb9f..05e49451 100644
--- a/src/proj_experimental.h
+++ b/src/proj_experimental.h
@@ -168,7 +168,7 @@ PJ PROJ_DLL *proj_create_geographic_crs(
PJ PROJ_DLL *proj_create_geographic_crs_from_datum(
PJ_CONTEXT *ctx,
const char *crs_name,
- PJ* datum,
+ PJ* datum_or_datum_ensemble,
PJ* ellipsoidal_cs);
PJ PROJ_DLL *proj_create_geocentric_crs(
@@ -187,7 +187,7 @@ PJ PROJ_DLL *proj_create_geocentric_crs(
PJ PROJ_DLL *proj_create_geocentric_crs_from_datum(
PJ_CONTEXT *ctx,
const char *crs_name,
- const PJ* datum,
+ const PJ* datum_or_datum_ensemble,
const char *linear_units,
double linear_units_conv);