diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2021-04-12 13:29:22 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-12 13:29:22 +0200 |
| commit | ff771264d7631356c2553e04b427ab3c7546d06d (patch) | |
| tree | a02305fcb019ba5013cec218209b0c09a473005a | |
| parent | 3bc25ac8a9deb868174a19392266dd2c870e3179 (diff) | |
| parent | d582ac8bae02b25e25685755173715ebf9349573 (diff) | |
| download | PROJ-ff771264d7631356c2553e04b427ab3c7546d06d.tar.gz PROJ-ff771264d7631356c2553e04b427ab3c7546d06d.zip | |
Merge pull request #2662 from rouault/celestial_body
Add proj_get_celestial_body_name() and …
| -rw-r--r-- | include/proj/io.hpp | 2 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 1 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 55 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 60 | ||||
| -rw-r--r-- | src/proj.h | 13 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 153 |
6 files changed, 264 insertions, 20 deletions
diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 96a97142..e4307d02 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -1089,6 +1089,8 @@ class PROJ_GCC_DLL AuthorityFactory { /** Name of the projection method for a projected CRS. Might be empty * even for projected CRS in some cases. */ std::string projectionMethodName; + /** Name of the celestial body of the CRS (e.g. "Earth") */ + std::string celestialBodyName; //! @cond Doxygen_Suppress CRSInfo(); diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index e1de53d0..ed49e0e5 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -959,6 +959,7 @@ proj_factors proj_geod proj_get_area_of_use proj_get_authorities_from_database +proj_get_celestial_body_name proj_get_codes_from_database proj_get_crs_info_list_from_database proj_get_crs_list_parameters_create diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index d8714594..c9eaf46a 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -2193,6 +2193,53 @@ PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) { // --------------------------------------------------------------------------- +/** \brief Get the name of the celestial body of this object. + * + * Object should be a CRS, Datum or Ellipsoid. + * + * @param ctx PROJ context, or NULL for default context + * @param obj Object of type CRS, Datum or Ellipsoid.(must not be NULL) + * @return the name of the celestial body, or NULL. + * @since 8.1 + */ +const char *proj_get_celestial_body_name(PJ_CONTEXT *ctx, const PJ *obj) { + SANITIZE_CTX(ctx); + const IdentifiedObject *ptr = obj->iso_obj.get(); + if (dynamic_cast<const CRS *>(ptr)) { + const auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__); + if (!geodCRS) { + // FIXME when vertical CRS can be non-EARTH... + return datum::Ellipsoid::EARTH.c_str(); + } + return geodCRS->ellipsoid()->celestialBody().c_str(); + } + const auto ensemble = dynamic_cast<const DatumEnsemble *>(ptr); + if (ensemble) { + ptr = ensemble->datums().front().get(); + // Go on + } + const auto geodetic_datum = + dynamic_cast<const GeodeticReferenceFrame *>(ptr); + if (geodetic_datum) { + return geodetic_datum->ellipsoid()->celestialBody().c_str(); + } + const auto vertical_datum = + dynamic_cast<const VerticalReferenceFrame *>(ptr); + if (vertical_datum) { + // FIXME when vertical CRS can be non-EARTH... + return datum::Ellipsoid::EARTH.c_str(); + } + const auto ellipsoid = dynamic_cast<const Ellipsoid *>(ptr); + if (ellipsoid) { + return ellipsoid->celestialBody().c_str(); + } + proj_log_error(ctx, __FUNCTION__, + "Object is not a CRS, Datum or Ellipsoid"); + return nullptr; +} + +// --------------------------------------------------------------------------- + /** \brief Get the horizontal datum from a CRS * * This function may return a Datum or DatumEnsemble object. @@ -2655,6 +2702,7 @@ PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() { ret->east_lon_degree = 0.0; ret->north_lat_degree = 0.0; ret->allow_deprecated = FALSE; + ret->celestial_body_name = nullptr; } return ret; } @@ -2792,6 +2840,10 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name, } } } + if (params && params->celestial_body_name && + params->celestial_body_name != info.celestialBodyName) { + continue; + } ret[i] = new PROJ_CRS_INFO; ret[i]->auth_name = pj_strdup(info.authName.c_str()); @@ -2809,6 +2861,8 @@ proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name, info.projectionMethodName.empty() ? nullptr : pj_strdup(info.projectionMethodName.c_str()); + ret[i]->celestial_body_name = + pj_strdup(info.celestialBodyName.c_str()); i++; } ret[i] = nullptr; @@ -2842,6 +2896,7 @@ void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) { free(list[i]->name); free(list[i]->area_name); free(list[i]->projection_method_name); + free(list[i]->celestial_body_name); delete list[i]; } delete[] list; diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 7037d642..19db111b 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -3544,7 +3544,8 @@ const DatabaseContextNNPtr &AuthorityFactory::databaseContext() const { AuthorityFactory::CRSInfo::CRSInfo() : authName{}, code{}, name{}, type{ObjectType::CRS}, deprecated{}, bbox_valid{}, west_lon_degree{}, south_lat_degree{}, east_lon_degree{}, - north_lat_degree{}, areaName{}, projectionMethodName{} {} + north_lat_degree{}, areaName{}, projectionMethodName{}, + celestialBodyName{} {} //! @endcond // --------------------------------------------------------------------------- @@ -7350,52 +7351,70 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { return sql; }; + const auto getJoinCelestialBody = [](const char *crs_alias) { + std::string sql("JOIN geodetic_datum gd ON gd.auth_name = "); + sql += crs_alias; + sql += ".datum_auth_name AND gd.code = "; + sql += crs_alias; + sql += ".datum_code " + "JOIN ellipsoid e ON e.auth_name = gd.ellipsoid_auth_name " + "AND e.code = gd.ellipsoid_code " + "JOIN celestial_body cb ON " + "cb.auth_name = e.celestial_body_auth_name " + "AND cb.code = e.celestial_body_code "; + return sql; + }; + std::string sql = "SELECT * FROM (" "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.description, NULL FROM geodetic_crs c " + - getSqlArea("geodetic_crs"); + "a.description, NULL, cb.name FROM geodetic_crs c "; + sql += getSqlArea("geodetic_crs"); + sql += getJoinCelestialBody("c"); ListOfParams params; if (d->hasAuthorityRestriction()) { - sql += " WHERE c.auth_name = ?"; + sql += "WHERE c.auth_name = ? "; params.emplace_back(d->authority()); } - sql += " UNION ALL "; - sql += "SELECT c.auth_name, c.code, c.name, 'projected', " + sql += "UNION ALL SELECT c.auth_name, c.code, c.name, 'projected', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " - "a.description, cm.name AS conversion_method_name FROM " - "projected_crs c "; - sql += getSqlArea("projected_crs"); - sql += "LEFT JOIN conversion_table conv ON " + "a.description, cm.name, cb.name AS conversion_method_name FROM " + "projected_crs c " + "LEFT JOIN conversion_table conv ON " "c.conversion_auth_name = conv.auth_name AND " "c.conversion_code = conv.code " "LEFT JOIN conversion_method cm ON " "conv.method_auth_name = cm.auth_name AND " - "conv.method_code = cm.code"; + "conv.method_code = cm.code " + "JOIN geodetic_crs gcrs ON " + "gcrs.auth_name = c.geodetic_crs_auth_name " + "AND gcrs.code = c.geodetic_crs_code "; + sql += getSqlArea("projected_crs"); + sql += getJoinCelestialBody("gcrs"); if (d->hasAuthorityRestriction()) { - sql += " WHERE c.auth_name = ?"; + sql += "WHERE c.auth_name = ? "; params.emplace_back(d->authority()); } - sql += " UNION ALL "; - sql += "SELECT c.auth_name, c.code, c.name, 'vertical', " + // FIXME: we can't handle non-EARTH vertical CRS for now + sql += "UNION ALL SELECT c.auth_name, c.code, c.name, 'vertical', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " - "a.description, NULL FROM vertical_crs c "; + "a.description, NULL, 'Earth' FROM vertical_crs c "; sql += getSqlArea("vertical_crs"); if (d->hasAuthorityRestriction()) { - sql += " WHERE c.auth_name = ?"; + sql += "WHERE c.auth_name = ? "; params.emplace_back(d->authority()); } - sql += " UNION ALL "; - sql += "SELECT c.auth_name, c.code, c.name, 'compound', " + // FIXME: we can't handle non-EARTH vertical CRS for now + sql += "UNION ALL SELECT c.auth_name, c.code, c.name, 'compound', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " - "a.description, NULL FROM compound_crs c "; + "a.description, NULL, 'Earth' FROM compound_crs c "; sql += getSqlArea("compound_crs"); if (d->hasAuthorityRestriction()) { - sql += " WHERE c.auth_name = ?"; + sql += "WHERE c.auth_name = ? "; params.emplace_back(d->authority()); } sql += ") r ORDER BY auth_name, code"; @@ -7432,6 +7451,7 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { } info.areaName = row[9]; info.projectionMethodName = row[10]; + info.celestialBodyName = row[11]; res.emplace_back(info); } return res; @@ -948,6 +948,11 @@ typedef struct /** Name of the projection method for a projected CRS. Might be NULL even *for projected CRS in some cases. */ char* projection_method_name; + + /** Name of the celestial body of the CRS (e.g. "Earth"). + * @since 8.1 + */ + char* celestial_body_name; } PROJ_CRS_INFO; /** \brief Structure describing optional parameters for proj_get_crs_list(); @@ -982,6 +987,12 @@ typedef struct /** Whether deprecated objects are allowed. Default to FALSE. */ int allow_deprecated; + + /** Celestial body of the CRS (e.g. "Earth"). The default value, NULL, + * means no restriction + * @since 8.1 + */ + const char* celestial_body_name; } PROJ_CRS_LIST_PARAMETERS; /** \brief Structure given description of a unit. @@ -1366,6 +1377,8 @@ int PROJ_DLL proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, int *out_is_semi_minor_computed, double *out_inv_flattening); +const char PROJ_DLL *proj_get_celestial_body_name(PJ_CONTEXT *ctx, const PJ *obj); + PJ PROJ_DLL *proj_get_prime_meridian(PJ_CONTEXT *ctx, const PJ *obj); diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index b624e063..ca41620e 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -1021,6 +1021,131 @@ TEST_F(CApi, proj_crs) { // --------------------------------------------------------------------------- +TEST_F(CApi, proj_get_celestial_body_name) { + + // Geographic CRS + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "4326", + PJ_CATEGORY_CRS, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Projected CRS + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "32631", + PJ_CATEGORY_CRS, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Vertical CRS + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "3855", + PJ_CATEGORY_CRS, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Compound CRS + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "9518", + PJ_CATEGORY_CRS, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Geodetic datum + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "6267", + PJ_CATEGORY_DATUM, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Datum ensemble + { + auto obj = proj_create_from_database( + m_ctxt, "EPSG", "6326", PJ_CATEGORY_DATUM_ENSEMBLE, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Vertical datum + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "1027", + PJ_CATEGORY_DATUM, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Ellipsoid + { + auto obj = proj_create_from_database( + m_ctxt, "EPSG", "7030", PJ_CATEGORY_ELLIPSOID, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Earth"); + } + + // Ellipsoid non-EARTH + { + auto obj = proj_create_from_database( + m_ctxt, "ESRI", "107903", PJ_CATEGORY_ELLIPSOID, false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_NE(celestial_body_name, nullptr); + EXPECT_EQ(std::string(celestial_body_name), "Moon"); + } + + // Coordinate operation -> error + { + auto obj = proj_create_from_database(m_ctxt, "EPSG", "1591", + PJ_CATEGORY_COORDINATE_OPERATION, + false, nullptr); + ASSERT_NE(obj, nullptr); + ObjectKeeper keeper(obj); + const char *celestial_body_name = + proj_get_celestial_body_name(m_ctxt, obj); + ASSERT_EQ(celestial_body_name, nullptr); + } +} + +// --------------------------------------------------------------------------- + TEST_F(CApi, proj_get_prime_meridian) { auto crs = proj_create_from_wkt( m_ctxt, @@ -3527,6 +3652,8 @@ TEST_F(CApi, proj_get_crs_info_list_from_database) { bool found3855 = false; bool found3901 = false; for (int i = 0; i < result_count; i++) { + // EPSG should only include Earth CRS, at least for now... + EXPECT_EQ(std::string(list[i]->celestial_body_name), "Earth"); auto code = std::string(list[i]->code); if (code == "4326") { found4326 = true; @@ -3700,6 +3827,32 @@ TEST_F(CApi, proj_get_crs_info_list_from_database) { proj_get_crs_list_parameters_destroy(params); proj_crs_info_list_destroy(list); } + + // Filter on celestial body + { + int result_count = 0; + auto params = proj_get_crs_list_parameters_create(); + params->celestial_body_name = "non existing"; + auto list = proj_get_crs_info_list_from_database(m_ctxt, nullptr, + params, &result_count); + ASSERT_NE(list, nullptr); + EXPECT_EQ(result_count, 0); + proj_get_crs_list_parameters_destroy(params); + proj_crs_info_list_destroy(list); + } + + // Filter on celestial body + { + int result_count = 0; + auto params = proj_get_crs_list_parameters_create(); + params->celestial_body_name = "Earth"; + auto list = proj_get_crs_info_list_from_database(m_ctxt, nullptr, + params, &result_count); + ASSERT_NE(list, nullptr); + EXPECT_GT(result_count, 0); + proj_get_crs_list_parameters_destroy(params); + proj_crs_info_list_destroy(list); + } } // --------------------------------------------------------------------------- |
