From a9b6f39494e6dab0ea02af9d82e7b3d570f5422f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 24 Sep 2020 22:41:59 +0200 Subject: Database: "minimal" update to EPSG v10.003 Content mostly unchanged since v9.9 This update is "minimal" in that it mostly reflects the removal of the 'area' table, replaced now by 'extent', 'scope' and 'usage' Other new aspects of EPSG v10 are left aside. --- src/iso19111/factory.cpp | 442 ++++++++++++++++++++++++++++------------------- 1 file changed, 262 insertions(+), 180 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index a011f397..f98e5cc8 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -1064,6 +1064,28 @@ std::string DatabaseContext::getOldProjGridName(const std::string &gridName) { // --------------------------------------------------------------------------- +// FIXME: as we don't support datum ensemble yet, remove it from name +static std::string addEnsembleSuffix(const std::string &name) { + if (name == "World Geodetic System 1984") { + return "World Geodetic System 1984 ensemble"; + } else if (name == "European Terrestrial Reference System 1989") { + return "European Terrestrial Reference System 1989 ensemble"; + } + return name; +} + +// 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 @@ -1082,7 +1104,7 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName, if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } - auto res = d->run(sql, {officialName}); + auto res = d->run(sql, {addEnsembleSuffix(officialName)}); if (res.empty()) { return std::string(); } @@ -1129,7 +1151,7 @@ std::list DatabaseContext::getAliases( if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } - auto resSql = d->run(sql, {officialName}); + auto resSql = d->run(sql, {addEnsembleSuffix(officialName)}); if (resSql.empty()) { d->cacheAliasNames_.insert(key, res); return res; @@ -1312,21 +1334,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 &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 ¶meters = ListOfParams()); @@ -1385,7 +1405,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 &usages) { auto props = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, authority()) .set(metadata::Identifier::CODE_KEY, code) @@ -1393,45 +1413,93 @@ 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(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(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.name, 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 usages; + for (const auto &row : res) { + try { + size_t idx = 0; + const auto &extent_name = row[idx++]; + idx++; /*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 scopeOpt; + if (!scope.empty()) { + scopeOpt = scope; + } + + metadata::ExtentPtr extent; + if (south_lat_str.empty()) { + extent = + metadata::Extent::create( + util::optional(extent_name), {}, {}, {}) + .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(extent_name), + std::vector{bbox}, + std::vector(), + std::vector()) + .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; } @@ -1554,7 +1622,7 @@ AuthorityFactory::createObject(const std::string &code) const { throw FactoryException(msg); } const auto &table_name = res.front()[0]; - if (table_name == "area") { + if (table_name == "extent") { return util::nn_static_pointer_cast( createExtent(code)); } @@ -1639,10 +1707,10 @@ AuthorityFactory::createExtent(const std::string &code) const { } } auto sql = "SELECT name, south_lat, north_lat, west_lon, east_lon, " - "deprecated FROM area WHERE auth_name = ? AND code = ?"; + "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 { @@ -1670,7 +1738,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 +1884,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 +1965,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 +2008,13 @@ 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, deprecated FROM geodetic_datum " + "WHERE " + "auth_name = ? AND code = ?", + code); if (res.empty()) { throw NoSuchAuthorityCodeException("geodetic datum not found", d->authority(), code); @@ -1958,16 +2026,14 @@ 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 bool deprecated = row[6] == "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(); if (!publication_date.empty()) { props.set("PUBLICATION_DATE", publication_date); @@ -1993,10 +2059,10 @@ 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, deprecated FROM " + "vertical_datum WHERE auth_name = ? AND code = ?", + code); if (res.empty()) { throw NoSuchAuthorityCodeException("vertical datum not found", d->authority(), code); @@ -2004,11 +2070,9 @@ 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 bool deprecated = row[3] == "1"; - auto props = d->createProperties( - code, name, deprecated, area_of_use_auth_name, area_of_use_code); + const bool deprecated = row[1] == "1"; + auto props = d->createPropertiesSearchUsages("vertical_datum", code, + name, deprecated); auto anchor = util::optional(); return datum::VerticalReferenceFrame::create(props, anchor); } catch (const std::exception &ex) { @@ -2105,7 +2169,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 +2246,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); } @@ -2250,7 +2323,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 +2343,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 +2430,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 +2444,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); if (verticalCS) { @@ -2412,8 +2481,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 +2531,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 +2563,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 +2623,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 +2648,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 +2735,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 +2752,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 +2760,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{horizCRS, vertCRS}); } catch (const std::exception &ex) { @@ -2831,10 +2894,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 +2921,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 +2928,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 +3106,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 +3135,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 +3157,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 +3164,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 +3218,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 +3252,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 +3280,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 +3287,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 +3331,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 +3389,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 +3413,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 +3440,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 +3649,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 +3671,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 +4014,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 +4468,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}; @@ -5206,13 +5284,23 @@ AuthorityFactory::getDescriptionText(const std::string &code) const { * @throw FactoryException */ std::list 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.name, NULL FROM geodetic_crs c " + + getSqlArea("geodetic_crs"); ListOfParams params; if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; @@ -5222,10 +5310,8 @@ std::list 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.name, 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 +5326,8 @@ std::list 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.name, NULL FROM vertical_crs c " + + getSqlArea("vertical_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); @@ -5252,10 +5336,8 @@ std::list 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.name, NULL FROM compound_crs c " + + getSqlArea("compound_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); @@ -5416,7 +5498,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 +5548,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( outTableName = row[1]; outAuthName = row[2]; outCode = row[3]; - return row[0]; + return removeEnsembleSuffix(row[0]); } } @@ -5521,7 +5603,7 @@ AuthorityFactory::createObjectsFromNameEx( const std::string &searchedName, const std::vector &allowedObjectTypes, bool approximateMatch, size_t limitResultCount) const { - std::string searchedNameWithoutDeprecated(searchedName); + std::string searchedNameWithoutDeprecated(addEnsembleSuffix(searchedName)); bool deprecated = false; if (ends_with(searchedNameWithoutDeprecated, " (deprecated)")) { deprecated = true; @@ -5925,7 +6007,7 @@ std::list> 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 "; -- cgit v1.2.3 From 2cfdd6f7fc683517a62147feace1b98b3f587abc Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 5 Oct 2020 14:56:41 +0200 Subject: Database: instanciate DynamicGeodeticReferenceFrame (things like ITRFxxx, WGS 84 (Gxxxx), etc.) when possible --- src/iso19111/factory.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index f98e5cc8..79419d73 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2011,7 +2011,8 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { auto res = d->runWithCodeParam("SELECT name, ellipsoid_auth_name, ellipsoid_code, " "prime_meridian_auth_name, prime_meridian_code, " - "publication_date, deprecated FROM geodetic_datum " + "publication_date, frame_reference_epoch, " + "deprecated FROM geodetic_datum " "WHERE " "auth_name = ? AND code = ?", code); @@ -2027,7 +2028,8 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { const auto &prime_meridian_auth_name = row[3]; const auto &prime_meridian_code = row[4]; const auto &publication_date = row[5]; - const bool deprecated = row[6] == "1"; + 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) @@ -2039,7 +2041,15 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { 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::DynamicGeodeticReferenceFrame::create( + props, ellipsoid, anchor, pm, + common::Measure(c_locale_stod(frame_reference_epoch), + common::UnitOfMeasure::YEAR), + util::optional())); d->context()->d->cache(cacheKey, datum); return datum; } catch (const std::exception &ex) { -- cgit v1.2.3 From 3ed9a613d8da9874f45d2a069686f1fa38c72ad9 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 5 Oct 2020 15:40:20 +0200 Subject: Database: use extended description for extent/area of use, as done by epsg.org WKT export --- src/4D_api.cpp | 2 +- src/iso19111/factory.cpp | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/4D_api.cpp b/src/4D_api.cpp index 75fa6d04..d7fe13e1 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -1233,7 +1233,7 @@ std::vector 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/factory.cpp b/src/iso19111/factory.cpp index 79419d73..6eb03ad4 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -1432,7 +1432,7 @@ util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages( const std::string &name, bool deprecated) { const std::string sql( - "SELECT extent.name, extent.description, extent.south_lat, " + "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) " @@ -1450,8 +1450,7 @@ util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages( for (const auto &row : res) { try { size_t idx = 0; - const auto &extent_name = row[idx++]; - idx++; /*const auto &extent_description = row[idx++];*/ + 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++]; @@ -1465,10 +1464,10 @@ util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages( metadata::ExtentPtr extent; if (south_lat_str.empty()) { - extent = - metadata::Extent::create( - util::optional(extent_name), {}, {}, {}) - .as_nullable(); + extent = metadata::Extent::create( + util::optional(extent_description), + {}, {}, {}) + .as_nullable(); } else { double south_lat = c_locale_stod(south_lat_str); double north_lat = c_locale_stod(north_lat_str); @@ -1477,7 +1476,7 @@ util::PropertyMap AuthorityFactory::Private::createPropertiesSearchUsages( auto bbox = metadata::GeographicBoundingBox::create( west_lon, south_lat, east_lon, north_lat); extent = metadata::Extent::create( - util::optional(extent_name), + util::optional(extent_description), std::vector{bbox}, std::vector(), std::vector()) @@ -1706,7 +1705,7 @@ AuthorityFactory::createExtent(const std::string &code) const { return NN_NO_CHECK(extent); } } - auto sql = "SELECT name, south_lat, north_lat, west_lon, east_lon, " + 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()) { @@ -1715,10 +1714,10 @@ AuthorityFactory::createExtent(const std::string &code) const { } 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(name), {}, {}, {}); + util::optional(description), {}, {}, {}); d->context()->d->cache(cacheKey, extent); return extent; } @@ -1730,7 +1729,7 @@ AuthorityFactory::createExtent(const std::string &code) const { west_lon, south_lat, east_lon, north_lat); auto extent = metadata::Extent::create( - util::optional(name), + util::optional(description), std::vector{bbox}, std::vector(), std::vector()); -- cgit v1.2.3 From ecf9cf0d975b3eb7630a314e8b32dd59658c3e2a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 17:31:13 +0200 Subject: getCRSInfoList(): make it use area description --- src/iso19111/factory.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 6eb03ad4..5d0ed69a 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -5308,7 +5308,7 @@ std::list AuthorityFactory::getCRSInfoList() const { 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 " + + "a.description, NULL FROM geodetic_crs c " + getSqlArea("geodetic_crs"); ListOfParams params; if (d->hasAuthorityRestriction()) { @@ -5319,7 +5319,8 @@ std::list 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 " + + "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 " @@ -5335,7 +5336,7 @@ std::list 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 " + + "a.description, NULL FROM vertical_crs c " + getSqlArea("vertical_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; @@ -5345,7 +5346,7 @@ std::list 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 " + + "a.description, NULL FROM compound_crs c " + getSqlArea("compound_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; -- cgit v1.2.3 From fece8a6c6e2e5eebeac6f9b4fc47ca830f2e3a3a Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Oct 2020 17:49:14 +0200 Subject: Database: add aliases for 'old' names of WGS84 and ETRS89 datums, and remove one hack --- src/iso19111/factory.cpp | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 5d0ed69a..46657431 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -1064,16 +1064,6 @@ std::string DatabaseContext::getOldProjGridName(const std::string &gridName) { // --------------------------------------------------------------------------- -// FIXME: as we don't support datum ensemble yet, remove it from name -static std::string addEnsembleSuffix(const std::string &name) { - if (name == "World Geodetic System 1984") { - return "World Geodetic System 1984 ensemble"; - } else if (name == "European Terrestrial Reference System 1989") { - return "European Terrestrial Reference System 1989 ensemble"; - } - return name; -} - // 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") { @@ -1104,9 +1094,15 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName, if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } - auto res = d->run(sql, {addEnsembleSuffix(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 " @@ -1151,10 +1147,16 @@ std::list DatabaseContext::getAliases( if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } - auto resSql = d->run(sql, {addEnsembleSuffix(officialName)}); + 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]; @@ -5613,7 +5615,7 @@ AuthorityFactory::createObjectsFromNameEx( const std::string &searchedName, const std::vector &allowedObjectTypes, bool approximateMatch, size_t limitResultCount) const { - std::string searchedNameWithoutDeprecated(addEnsembleSuffix(searchedName)); + std::string searchedNameWithoutDeprecated(searchedName); bool deprecated = false; if (ends_with(searchedNameWithoutDeprecated, " (deprecated)")) { deprecated = true; -- cgit v1.2.3 From b5369cc79eccc625419d37bb97a5361731fd8f0f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Oct 2020 22:15:31 +0200 Subject: Add a AuthorityFactory::createDatumEnsemble() method, and make it inherit from ObjectUsage as mandated by ISO 19111:2019 --- src/iso19111/datum.cpp | 3 +- src/iso19111/factory.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index 83f615e9..d94d729c 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -1574,8 +1574,7 @@ DatumEnsemble::DatumEnsemble(const std::vector &datumsIn, #ifdef notdef DatumEnsemble::DatumEnsemble(const DatumEnsemble &other) - : common::IdentifiedObject(other), - d(internal::make_unique(*other.d)) {} + : common::ObjectUsage(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 46657431..3895edb2 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -1604,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); } @@ -1622,7 +1622,9 @@ AuthorityFactory::createObject(const std::string &code) const { } throw FactoryException(msg); } - const auto &table_name = res.front()[0]; + 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( createExtent(code)); @@ -1640,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( + createDatumEnsemble(code, table_name)); + } return util::nn_static_pointer_cast( createGeodeticDatum(code)); } if (table_name == "vertical_datum") { + if (type == "ensemble") { + return util::nn_static_pointer_cast( + createDatumEnsemble(code, table_name)); + } return util::nn_static_pointer_cast( createVerticalDatum(code)); } @@ -2093,6 +2103,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 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. -- cgit v1.2.3 From 9dc3bf503b0455526a4d180930f8414621ea6187 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 6 Oct 2020 23:48:46 +0200 Subject: Add DatumEnsemble::asDatum() and use it in exportToWkt() to allow exporting DatumEnsemble to WKT < 2019. --- src/iso19111/datum.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index d94d729c..9ec9fb62 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -1612,17 +1612,84 @@ DatumEnsemble::positionalAccuracy() const { // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +DatumNNPtr +DatumEnsemble::asDatum(const io::DatabaseContextPtr &dbContext) const { + + const auto &l_datums = datums(); + auto *grf = dynamic_cast(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(array)); + } + const auto anchor = util::optional(); + + if (grf) { + return GeodeticReferenceFrame::create(props, grf->ellipsoid(), anchor, + grf->primeMeridian()); + } else { + assert(dynamic_cast(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); @@ -1679,7 +1746,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)); -- cgit v1.2.3 From 53672bdf7074e3737f6e6a53ee7373dcbccd6ea4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 7 Oct 2020 23:58:36 +0200 Subject: Make CRS identification work with CRS with DatumEnsemble --- src/iso19111/crs.cpp | 235 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 152 insertions(+), 83 deletions(-) (limited to 'src') diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index ecbd39e1..eb3918f7 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -1188,6 +1188,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. @@ -1207,20 +1219,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) && @@ -1340,6 +1373,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( + SingleCRS::getPrivate()->datumEnsemble->asDatum(dbContext))); +} +//! @endcond + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress static datum::GeodeticReferenceFrame *oneDatum(const GeodeticCRS *crs) { const auto &l_datumEnsemble = crs->datumEnsemble(); @@ -1704,8 +1752,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(), @@ -1953,11 +2001,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); @@ -1972,10 +2021,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( @@ -1989,9 +2038,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) && @@ -2013,18 +2061,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 @@ -2074,12 +2136,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { break; } } - if (!gotAbove25Pct && thisDatum) { - if (!thisDatum->identifiers().empty()) { - searchByDatum(); - } else { - searchByEllipsoid(); - } + if (!gotAbove25Pct) { + searchByDatumOrEllipsoid(); } } @@ -2106,22 +2164,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 @@ -2153,23 +2209,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 @@ -2341,6 +2395,12 @@ bool GeographicCRS::is2DPartOf3D(util::nn other) return thisDatum->_isEquivalentTo( otherDatum.get(), util::IComparable::Criterion::EQUIVALENT); } + const auto &thisDatumEnsemble = datumEnsemble(); + const auto &otherDatumEnsemble = other->datumEnsemble(); + if (thisDatumEnsemble && otherDatumEnsemble) { + return thisDatumEnsemble->_isEquivalentTo( + otherDatumEnsemble.get(), util::IComparable::Criterion::EQUIVALENT); + } return false; } @@ -2580,15 +2640,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; @@ -2754,6 +2812,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( + SingleCRS::datumNonNull(dbContext))); +} +//! @endcond + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; @@ -3846,12 +3917,15 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { std::list res; const auto &thisName(nameStr()); + io::DatabaseContextPtr dbContext = + authorityFactory ? authorityFactory->databaseContext().as_nullable() + : nullptr; std::list> 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(l_baseCRS.get()); @@ -3886,9 +3960,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) && @@ -3978,10 +4049,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); @@ -4863,7 +4933,6 @@ BoundCRSNNPtr BoundCRS::createFromNadgrids(const CRSNNPtr &baseCRSIn, ? NN_NO_CHECK(std::static_pointer_cast(sourceGeographicCRS)) : baseCRSIn; if (sourceGeographicCRS != nullptr && - sourceGeographicCRS->datum() != nullptr && sourceGeographicCRS->primeMeridian()->longitude().getSIValue() != 0.0) { transformationSourceCRS = GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, @@ -4872,9 +4941,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(), datum::PrimeMeridian::GREENWICH), sourceGeographicCRS->coordinateSystem()); } -- cgit v1.2.3 From 492c95ce77f4ebdab401c6fcc2d1dae5c6b5c401 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 00:51:00 +0200 Subject: Make createOperations() work with DatumEnsemble --- src/iso19111/coordinateoperation.cpp | 232 ++++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 101 deletions(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 6fcf4d30..30861be0 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -11174,7 +11174,8 @@ struct CoordinateOperationFactory::Private { static std::vector createOperationsGeogToGeog( std::vector &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 &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(sourceCRS.get()); const crs::GeographicCRS *geogDst = dynamic_cast(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 CoordinateOperationFactory::Private::createOperationsGeogToGeog( std::vector &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 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( @@ -14030,7 +14044,8 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( auto geogDst = dynamic_cast(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( 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(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; @@ -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 &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(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(), @@ -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 &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::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::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 &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( 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 = -- cgit v1.2.3 From 4049f4cc961f78d9313525e3eb82fbc5bb3fd879 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 01:09:54 +0200 Subject: createGeodeticCRS(): more DatumEnsemble compatibility in the case when creating from text_definition --- src/iso19111/factory.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 3895edb2..5f12273d 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2366,18 +2366,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); 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); 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; -- cgit v1.2.3 From f14782192083ab9ebb22bf94e3138722657dde9f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 15:48:08 +0200 Subject: Add C API to work with datum ensemble Add: - proj_crs_get_datum_ensemble() - proj_crs_get_datum_forced() - proj_datum_ensemble_get_member_count() - proj_datum_ensemble_get_accuracy() - proj_datum_ensemble_get_member() Make proj_create_geographic_crs_from_datum() and proj_create_geocentric_crs_from_datum() accept a datum ensemble. --- src/iso19111/c_api.cpp | 210 +++++++++++++++++++++++++++++++++++++++++++----- src/proj.h | 14 ++++ src/proj_experimental.h | 4 +- 3 files changed, 208 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 21a07729..fb0a3af7 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -3044,31 +3044,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(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( + datum_or_datum_ensemble->iso_obj); + auto l_datum_ensemble = std::dynamic_pointer_cast( + datum_or_datum_ensemble->iso_obj); auto cs = std::dynamic_pointer_cast(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 +3145,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 +3155,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( + datum_or_datum_ensemble->iso_obj); + auto l_datum_ensemble = std::dynamic_pointer_cast( + datum_or_datum_ensemble->iso_obj); try { const UnitOfMeasure linearUnit( createLinearUnit(linear_units, linear_units_conv)); - auto l_datum = - std::dynamic_pointer_cast(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 +7988,173 @@ 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(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(datum_ensemble->iso_obj.get()); + if (!l_datum_ensemble) { + proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble"); + return 0; + } + return static_cast(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(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(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(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(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 coordinate system of a SingleCRS. * * The returned object must be unreferenced with proj_destroy() after diff --git a/src/proj.h b/src/proj.h index 512dcd84..92966526 100644 --- a/src/proj.h +++ b/src/proj.h @@ -1249,6 +1249,20 @@ 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); + 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); -- cgit v1.2.3 From 67af1d5741da08d9546b907e0da2c21c54c61b27 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 15:49:30 +0200 Subject: Make proj_create_crs_to_crs() / cs2cs work with CRS with DatumEnsemble --- src/4D_api.cpp | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/4D_api.cpp b/src/4D_api.cpp index d7fe13e1..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 ) { -- cgit v1.2.3 From acd1cf3278a532b56f3f621e27a8895b3db3cb75 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 8 Oct 2020 16:10:55 +0200 Subject: Make GeographicCRS::is2DPartOf3D() work with DatumEnsemble --- src/iso19111/coordinateoperation.cpp | 4 ++-- src/iso19111/crs.cpp | 26 ++++++++++---------------- 2 files changed, 12 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 30861be0..184ff435 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -14196,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; @@ -14976,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) diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index eb3918f7..134f6006 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -1006,7 +1006,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(firstRes)); } @@ -2371,7 +2372,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 other) +bool GeographicCRS::is2DPartOf3D(util::nn other, + const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN { const auto &axis = d->coordinateSystem_->axisList(); const auto &otherAxis = other->d->coordinateSystem_->axisList(); @@ -2389,19 +2391,10 @@ bool GeographicCRS::is2DPartOf3D(util::nn 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); - } - const auto &thisDatumEnsemble = datumEnsemble(); - const auto &otherDatumEnsemble = other->datumEnsemble(); - if (thisDatumEnsemble && otherDatumEnsemble) { - return thisDatumEnsemble->_isEquivalentTo( - otherDatumEnsemble.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 @@ -2543,7 +2536,8 @@ GeographicCRS::demoteTo2D(const std::string &newName, auto firstResAsGeogCRS = util::nn_dynamic_pointer_cast(firstRes); if (firstResAsGeogCRS && - firstResAsGeogCRS->is2DPartOf3D(NN_NO_CHECK(this))) { + firstResAsGeogCRS->is2DPartOf3D(NN_NO_CHECK(this), + dbContext)) { return NN_NO_CHECK(firstResAsGeogCRS); } } -- cgit v1.2.3 From ad80dc88a70032e1d96812b574b2d4d22e223394 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 11 Oct 2020 19:50:09 +0200 Subject: Database: add a frame_reference_epoch column in vertical_datum to be able to handle dynamic vertical datums, and instanciate them properly from database --- src/iso19111/factory.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 5f12273d..473aa254 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2081,7 +2081,8 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { datum::VerticalReferenceFrameNNPtr AuthorityFactory::createVerticalDatum(const std::string &code) const { auto res = - d->runWithCodeParam("SELECT name, deprecated FROM " + d->runWithCodeParam("SELECT name, publication_date, " + "frame_reference_epoch, deprecated FROM " "vertical_datum WHERE auth_name = ? AND code = ?", code); if (res.empty()) { @@ -2091,11 +2092,23 @@ AuthorityFactory::createVerticalDatum(const std::string &code) const { try { const auto &row = res.front(); const auto &name = row[0]; - const bool deprecated = row[1] == "1"; + const auto &publication_date = row[1]; + const auto &frame_reference_epoch = row[2]; + const bool deprecated = row[3] == "1"; auto props = d->createPropertiesSearchUsages("vertical_datum", code, name, deprecated); + if (!publication_date.empty()) { + props.set("PUBLICATION_DATE", publication_date); + } auto anchor = util::optional(); - return datum::VerticalReferenceFrame::create(props, anchor); + return frame_reference_epoch.empty() + ? datum::VerticalReferenceFrame::create(props, anchor) + : datum::DynamicVerticalReferenceFrame::create( + props, anchor, + util::optional(), + common::Measure(c_locale_stod(frame_reference_epoch), + common::UnitOfMeasure::YEAR), + util::optional()); } catch (const std::exception &ex) { throw buildFactoryException("vertical reference frame", code, ex); } -- cgit v1.2.3 From d1a0d95da549f7d32bcd8be408afe1fca62a6fb2 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 11 Oct 2020 20:01:29 +0200 Subject: Database query: add AuthorityFactory::ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME and DYNAMIC_VERTICAL_REFERENCE_FRAME, and make corresponding C API work --- src/iso19111/c_api.cpp | 12 +++++++++-- src/iso19111/factory.cpp | 53 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index fb0a3af7..c152df88 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; diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 473aa254..ef5c6e02 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -2101,14 +2101,15 @@ AuthorityFactory::createVerticalDatum(const std::string &code) const { props.set("PUBLICATION_DATE", publication_date); } auto anchor = util::optional(); - return frame_reference_epoch.empty() - ? datum::VerticalReferenceFrame::create(props, anchor) - : datum::DynamicVerticalReferenceFrame::create( - props, anchor, - util::optional(), - common::Measure(c_locale_stod(frame_reference_epoch), - common::UnitOfMeasure::YEAR), - util::optional()); + if (frame_reference_epoch.empty()) { + return datum::VerticalReferenceFrame::create(props, anchor); + } else { + return datum::DynamicVerticalReferenceFrame::create( + props, anchor, util::optional(), + common::Measure(c_locale_stod(frame_reference_epoch), + common::UnitOfMeasure::YEAR), + util::optional()); + } } catch (const std::exception &ex) { throw buildFactoryException("vertical reference frame", code, ex); } @@ -5260,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; @@ -5742,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())); @@ -5824,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 "; @@ -5854,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 "; -- cgit v1.2.3 From 686713479eb0b39feb5369b82647f96edf809b6c Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sun, 11 Oct 2020 20:13:42 +0200 Subject: C API: add proj_dynamic_datum_get_frame_reference_epoch() --- src/iso19111/c_api.cpp | 34 ++++++++++++++++++++++++++++++++++ src/proj.h | 3 +++ 2 files changed, 37 insertions(+) (limited to 'src') diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index c152df88..cad76431 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -8163,6 +8163,40 @@ PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) { // --------------------------------------------------------------------------- +/** \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( + datum->iso_obj.get()); + auto dvrf = dynamic_cast( + 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/proj.h b/src/proj.h index 92966526..b758663a 100644 --- a/src/proj.h +++ b/src/proj.h @@ -1263,6 +1263,9 @@ 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, -- cgit v1.2.3