aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2021-04-12 13:29:22 +0200
committerGitHub <noreply@github.com>2021-04-12 13:29:22 +0200
commitff771264d7631356c2553e04b427ab3c7546d06d (patch)
treea02305fcb019ba5013cec218209b0c09a473005a
parent3bc25ac8a9deb868174a19392266dd2c870e3179 (diff)
parentd582ac8bae02b25e25685755173715ebf9349573 (diff)
downloadPROJ-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.hpp2
-rw-r--r--scripts/reference_exported_symbols.txt1
-rw-r--r--src/iso19111/c_api.cpp55
-rw-r--r--src/iso19111/factory.cpp60
-rw-r--r--src/proj.h13
-rw-r--r--test/unit/test_c_api.cpp153
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;
diff --git a/src/proj.h b/src/proj.h
index ef6d5841..f32e26b7 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -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);
+ }
}
// ---------------------------------------------------------------------------