aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/proj/io.hpp32
-rw-r--r--src/iso19111/c_api.cpp212
-rw-r--r--src/iso19111/factory.cpp161
-rw-r--r--src/proj.h81
-rw-r--r--test/unit/test_c_api.cpp206
-rw-r--r--test/unit/test_factory.cpp99
6 files changed, 768 insertions, 23 deletions
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index 4120d707..aedf0457 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -888,6 +888,38 @@ class PROJ_GCC_DLL AuthorityFactory {
PROJ_DLL std::string getDescriptionText(const std::string &code) const;
+ /** CRS information */
+ struct CRSInfo {
+ /** Authority name */
+ std::string authName{};
+ /** Code */
+ std::string code{};
+ /** Name */
+ std::string name{};
+ /** Type */
+ ObjectType type{ObjectType::CRS};
+ /** Whether the object is deprecated */
+ bool deprecated{};
+ /** Whereas the west_lon_degree, south_lat_degree, east_lon_degree and
+ * north_lat_degree fields are valid. */
+ bool bbox_valid{};
+ /** Western-most longitude of the area of use, in degrees. */
+ double west_lon_degree{};
+ /** Southern-most latitude of the area of use, in degrees. */
+ double south_lat_degree{};
+ /** Eastern-most longitude of the area of use, in degrees. */
+ double east_lon_degree{};
+ /** Northern-most latitude of the area of use, in degrees. */
+ double north_lat_degree{};
+ /** Name of the area of use. */
+ std::string areaName{};
+ /** Name of the projection method for a projected CRS. Might be empty
+ * even for projected CRS in some cases. */
+ std::string projectionMethodName{};
+ };
+
+ PROJ_DLL std::list<CRSInfo> getCRSInfoList() const;
+
// non-standard
PROJ_DLL static AuthorityFactoryNNPtr
create(const DatabaseContextNNPtr &context,
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index de11f181..5982da47 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -34,6 +34,7 @@
#include <cstdarg>
#include <cstring>
#include <map>
+#include <new>
#include <utility>
#include <vector>
@@ -1895,6 +1896,8 @@ PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) {
*
* @return a NULL terminated list of NUL-terminated strings that must be
* freed with proj_string_list_destroy(), or NULL in case of error.
+ *
+ * @see proj_get_crs_info_list_from_database()
*/
PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx,
const char *auth_name,
@@ -1932,6 +1935,215 @@ void proj_string_list_destroy(PROJ_STRING_LIST list) {
// ---------------------------------------------------------------------------
+/** \brief Instanciate a default set of parameters to be used by
+ * proj_get_crs_list().
+ *
+ * @return a new object to free with proj_get_crs_list_parameters_destroy() */
+PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() {
+ auto ret = new (std::nothrow) PROJ_CRS_LIST_PARAMETERS();
+ if (ret) {
+ ret->types = nullptr;
+ ret->typesCount = 0;
+ ret->crs_area_of_use_contains_bbox = TRUE;
+ ret->bbox_valid = FALSE;
+ ret->west_lon_degree = 0.0;
+ ret->south_lat_degree = 0.0;
+ ret->east_lon_degree = 0.0;
+ ret->north_lat_degree = 0.0;
+ ret->allow_deprecated = FALSE;
+ }
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Destroy an object returned by proj_get_crs_list_parameters_create()
+ */
+void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) {
+ delete params;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Enumerate CRS objects from the database, taking into account various
+ * criteria.
+ *
+ * The returned object is an array of PROJ_CRS_INFO* pointers, whose last
+ * entry is NULL. This array should be freed with proj_crs_list_destroy()
+ *
+ * When no filter parameters are set, this is functionnaly equivalent to
+ * proj_get_crs_info_list_from_database(), instanciating a PJ* object for each
+ * of the proj_create_from_database() and retrieving information with the
+ * various getters. However this function will be much faster.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param auth_name Authority name, used to restrict the search.
+ * Or NULL for all authorities.
+ * @param params Additional criteria, or NULL. If not-NULL, params SHOULD
+ * have been allocated by proj_get_crs_list_parameters_create(), as the
+ * PROJ_CRS_LIST_PARAMETERS structure might grow over time.
+ * @param out_result_count Output parameter pointing to an integer to receive
+ * the size of the result list. Might be NULL
+ * @return an array of PROJ_CRS_INFO* pointers to be freed with
+ * proj_crs_list_destroy(), or NULL in case of error.
+ */
+PROJ_CRS_INFO **
+proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
+ const PROJ_CRS_LIST_PARAMETERS *params,
+ int *out_result_count) {
+ SANITIZE_CTX(ctx);
+ PROJ_CRS_INFO **ret = nullptr;
+ int i = 0;
+ try {
+ auto factory = AuthorityFactory::create(getDBcontext(ctx),
+ auth_name ? auth_name : "");
+ auto list = factory->getCRSInfoList();
+ ret = new PROJ_CRS_INFO *[list.size() + 1];
+ GeographicBoundingBoxPtr bbox;
+ if (params && params->bbox_valid) {
+ bbox = GeographicBoundingBox::create(
+ params->west_lon_degree, params->south_lat_degree,
+ params->east_lon_degree, params->north_lat_degree)
+ .as_nullable();
+ }
+ for (const auto &info : list) {
+ auto type = PJ_TYPE_CRS;
+ if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) {
+ type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
+ } else if (info.type ==
+ AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS) {
+ type = PJ_TYPE_GEOGRAPHIC_3D_CRS;
+ } else if (info.type ==
+ AuthorityFactory::ObjectType::GEOCENTRIC_CRS) {
+ type = PJ_TYPE_GEOCENTRIC_CRS;
+ } else if (info.type ==
+ AuthorityFactory::ObjectType::PROJECTED_CRS) {
+ type = PJ_TYPE_PROJECTED_CRS;
+ } else if (info.type ==
+ AuthorityFactory::ObjectType::VERTICAL_CRS) {
+ type = PJ_TYPE_VERTICAL_CRS;
+ } else if (info.type ==
+ AuthorityFactory::ObjectType::COMPOUND_CRS) {
+ type = PJ_TYPE_COMPOUND_CRS;
+ }
+ if (params && params->typesCount) {
+ bool typeValid = false;
+ for (size_t j = 0; j < params->typesCount; j++) {
+ if (params->types[j] == type) {
+ typeValid = true;
+ break;
+ } else if (params->types[j] == PJ_TYPE_GEOGRAPHIC_CRS &&
+ (type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
+ type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
+ typeValid = true;
+ break;
+ } else if (params->types[j] == PJ_TYPE_GEODETIC_CRS &&
+ (type == PJ_TYPE_GEOCENTRIC_CRS ||
+ type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
+ type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
+ typeValid = true;
+ break;
+ }
+ }
+ if (!typeValid) {
+ continue;
+ }
+ }
+ if (params && !params->allow_deprecated && info.deprecated) {
+ continue;
+ }
+ if (params && params->bbox_valid) {
+ if (!info.bbox_valid) {
+ continue;
+ }
+ if (info.west_lon_degree <= info.east_lon_degree &&
+ params->west_lon_degree <= params->east_lon_degree) {
+ if (params->crs_area_of_use_contains_bbox) {
+ if (params->west_lon_degree < info.west_lon_degree ||
+ params->east_lon_degree > info.east_lon_degree ||
+ params->south_lat_degree < info.south_lat_degree ||
+ params->north_lat_degree > info.north_lat_degree) {
+ continue;
+ }
+ } else {
+ if (info.east_lon_degree < params->west_lon_degree ||
+ info.west_lon_degree > params->east_lon_degree ||
+ info.north_lat_degree < params->south_lat_degree ||
+ info.south_lat_degree > params->north_lat_degree) {
+ continue;
+ }
+ }
+ } else {
+ auto crsExtent = GeographicBoundingBox::create(
+ info.west_lon_degree, info.south_lat_degree,
+ info.east_lon_degree, info.north_lat_degree);
+ if (params->crs_area_of_use_contains_bbox) {
+ if (!crsExtent->contains(NN_NO_CHECK(bbox))) {
+ continue;
+ }
+ } else {
+ if (!bbox->intersects(crsExtent)) {
+ continue;
+ }
+ }
+ }
+ }
+
+ ret[i] = new PROJ_CRS_INFO;
+ ret[i]->auth_name = pj_strdup(info.authName.c_str());
+ ret[i]->code = pj_strdup(info.code.c_str());
+ ret[i]->name = pj_strdup(info.name.c_str());
+ ret[i]->type = type;
+ ret[i]->deprecated = info.deprecated;
+ ret[i]->bbox_valid = info.bbox_valid;
+ ret[i]->west_lon_degree = info.west_lon_degree;
+ ret[i]->south_lat_degree = info.south_lat_degree;
+ ret[i]->east_lon_degree = info.east_lon_degree;
+ ret[i]->north_lat_degree = info.north_lat_degree;
+ ret[i]->area_name = pj_strdup(info.areaName.c_str());
+ ret[i]->projection_method_name =
+ info.projectionMethodName.empty()
+ ? nullptr
+ : pj_strdup(info.projectionMethodName.c_str());
+ i++;
+ }
+ ret[i] = nullptr;
+ if (out_result_count)
+ *out_result_count = i;
+ return ret;
+ } catch (const std::exception &e) {
+ proj_log_error(ctx, __FUNCTION__, e.what());
+ if (ret) {
+ ret[i + 1] = nullptr;
+ proj_crs_list_destroy(ret);
+ }
+ if (out_result_count)
+ *out_result_count = 0;
+ }
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Destroy the result returned by
+ * proj_get_crs_info_list_from_database().
+ */
+void proj_crs_list_destroy(PROJ_CRS_INFO **list) {
+ if (list) {
+ for (int i = 0; list[i] != nullptr; i++) {
+ pj_dalloc(list[i]->auth_name);
+ pj_dalloc(list[i]->code);
+ pj_dalloc(list[i]->name);
+ pj_dalloc(list[i]->area_name);
+ pj_dalloc(list[i]->projection_method_name);
+ delete list[i];
+ }
+ delete[] list;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS),
* or the Transformation from the baseCRS to the hubCRS of a BoundCRS
*
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index a3047f8b..81abcdf1 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -77,9 +77,17 @@ namespace io {
//! @cond Doxygen_Suppress
-#define GEOG_2D "'geographic 2D'"
-#define GEOG_3D "'geographic 3D'"
-#define GEOCENTRIC "'geocentric'"
+// CRS subtypes
+#define GEOG_2D "geographic 2D"
+#define GEOG_3D "geographic 3D"
+#define GEOCENTRIC "geocentric"
+#define PROJECTED "projected"
+#define VERTICAL "vertical"
+#define COMPOUND "compound"
+
+#define GEOG_2D_SINGLE_QUOTED "'geographic 2D'"
+#define GEOG_3D_SINGLE_QUOTED "'geographic 3D'"
+#define GEOCENTRIC_SINGLE_QUOTED "'geocentric'"
// ---------------------------------------------------------------------------
@@ -1051,7 +1059,7 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
sql += replaceAll(tableName, "\"", "\"\"");
sql += "\" WHERE name = ?";
if (tableName == "geodetic_crs") {
- sql += " AND type = " GEOG_2D;
+ sql += " AND type = " GEOG_2D_SINGLE_QUOTED;
}
auto res = d->run(sql, {officialName});
if (res.empty()) {
@@ -2067,7 +2075,8 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
"deprecated FROM "
"geodetic_crs WHERE auth_name = ? AND code = ?");
if (geographicOnly) {
- sql += " AND type in (" GEOG_2D "," GEOG_3D ")";
+ sql += " AND type in (" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED
+ ")";
}
auto res = d->runWithCodeParam(sql, code);
if (res.empty()) {
@@ -2124,15 +2133,14 @@ AuthorityFactory::createGeodeticCRS(const std::string &code,
auto ellipsoidalCS =
util::nn_dynamic_pointer_cast<cs::EllipsoidalCS>(cs);
- if ((type == "geographic 2D" || type == "geographic 3D") &&
- ellipsoidalCS) {
+ if ((type == GEOG_2D || type == GEOG_3D) && ellipsoidalCS) {
auto crsRet = crs::GeographicCRS::create(
props, datum, NN_NO_CHECK(ellipsoidalCS));
d->context()->d->cache(cacheKey, crsRet);
return crsRet;
}
auto geocentricCS = util::nn_dynamic_pointer_cast<cs::CartesianCS>(cs);
- if (type == "geocentric" && geocentricCS) {
+ if (type == GEOCENTRIC && geocentricCS) {
auto crsRet = crs::GeodeticCRS::create(props, datum,
NN_NO_CHECK(geocentricCS));
d->context()->d->cache(cacheKey, crsRet);
@@ -2477,17 +2485,16 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code,
code);
}
const auto &type = res.front()[0];
- if (type == "geographic 2D" || type == "geographic 3D" ||
- type == "geocentric") {
+ if (type == GEOG_2D || type == GEOG_3D || type == GEOCENTRIC) {
return createGeodeticCRS(code);
}
- if (type == "vertical") {
+ if (type == VERTICAL) {
return createVerticalCRS(code);
}
- if (type == "projected") {
+ if (type == PROJECTED) {
return createProjectedCRS(code);
}
- if (allowCompound && type == "compound") {
+ if (allowCompound && type == COMPOUND) {
return createCompoundCRS(code);
}
throw FactoryException("unhandled CRS type: " + type);
@@ -3782,17 +3789,22 @@ AuthorityFactory::getAuthorityCodes(const ObjectType &type,
sql = "SELECT code FROM geodetic_crs WHERE ";
break;
case ObjectType::GEOCENTRIC_CRS:
- sql = "SELECT code FROM geodetic_crs WHERE type = " GEOCENTRIC " AND ";
+ sql = "SELECT code FROM geodetic_crs WHERE type "
+ "= " GEOCENTRIC_SINGLE_QUOTED " AND ";
break;
case ObjectType::GEOGRAPHIC_CRS:
- sql = "SELECT code FROM geodetic_crs WHERE type IN (" GEOG_2D
- "," GEOG_3D ") AND ";
+ sql = "SELECT code FROM geodetic_crs WHERE type IN "
+ "(" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED ") AND ";
break;
case ObjectType::GEOGRAPHIC_2D_CRS:
- sql = "SELECT code FROM geodetic_crs WHERE type = " GEOG_2D " AND ";
+ sql =
+ "SELECT code FROM geodetic_crs WHERE type = " GEOG_2D_SINGLE_QUOTED
+ " AND ";
break;
case ObjectType::GEOGRAPHIC_3D_CRS:
- sql = "SELECT code FROM geodetic_crs WHERE type = " GEOG_3D " AND ";
+ sql =
+ "SELECT code FROM geodetic_crs WHERE type = " GEOG_3D_SINGLE_QUOTED
+ " AND ";
break;
case ObjectType::VERTICAL_CRS:
sql = "SELECT code FROM vertical_crs WHERE ";
@@ -3858,6 +3870,107 @@ AuthorityFactory::getDescriptionText(const std::string &code) const {
// ---------------------------------------------------------------------------
+/** \brief Return a list of information on CRS objects
+ *
+ * This is functionnaly equivalent of listing the codes from an authority,
+ * instanciating
+ * a CRS object for each of them and getting the information from this CRS
+ * object, but this implementation has much less overhead.
+ *
+ * @throw FactoryException
+ */
+std::list<AuthorityFactory::CRSInfo> 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 "
+ "JOIN area a ON "
+ "c.area_of_use_auth_name = a.auth_name AND "
+ "c.area_of_use_code = a.code";
+ ListOfParams params;
+ if (d->hasAuthorityRestriction()) {
+ sql += " WHERE c.auth_name = ?";
+ params.emplace_back(d->authority());
+ }
+ sql += " UNION ALL ";
+ 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, conv.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 "
+ "LEFT JOIN conversion conv ON "
+ "c.conversion_auth_name = conv.auth_name AND "
+ "c.conversion_code = conv.code";
+ if (d->hasAuthorityRestriction()) {
+ sql += " WHERE c.auth_name = ?";
+ params.emplace_back(d->authority());
+ }
+ sql += " UNION ALL ";
+ 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";
+ if (d->hasAuthorityRestriction()) {
+ sql += " WHERE c.auth_name = ?";
+ params.emplace_back(d->authority());
+ }
+ sql += " UNION ALL ";
+ 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";
+ if (d->hasAuthorityRestriction()) {
+ sql += " WHERE c.auth_name = ?";
+ params.emplace_back(d->authority());
+ }
+ auto sqlRes = d->run(sql, params);
+ std::list<AuthorityFactory::CRSInfo> res;
+ for (const auto &row : sqlRes) {
+ AuthorityFactory::CRSInfo info;
+ info.authName = row[0];
+ info.code = row[1];
+ info.name = row[2];
+ const auto &type = row[3];
+ if (type == GEOG_2D) {
+ info.type = AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS;
+ } else if (type == GEOG_3D) {
+ info.type = AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS;
+ } else if (type == GEOCENTRIC) {
+ info.type = AuthorityFactory::ObjectType::GEOCENTRIC_CRS;
+ } else if (type == PROJECTED) {
+ info.type = AuthorityFactory::ObjectType::PROJECTED_CRS;
+ } else if (type == VERTICAL) {
+ info.type = AuthorityFactory::ObjectType::VERTICAL_CRS;
+ } else if (type == COMPOUND) {
+ info.type = AuthorityFactory::ObjectType::COMPOUND_CRS;
+ }
+ info.deprecated = row[4] == "1";
+ if (row[5].empty()) {
+ info.bbox_valid = false;
+ } else {
+ info.bbox_valid = true;
+ info.west_lon_degree = c_locale_stod(row[5]);
+ info.south_lat_degree = c_locale_stod(row[6]);
+ info.east_lon_degree = c_locale_stod(row[7]);
+ info.north_lat_degree = c_locale_stod(row[8]);
+ }
+ info.areaName = row[9];
+ info.projectionMethodName = row[10];
+ res.emplace_back(info);
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Gets the official name from a possibly alias name.
*
* @param aliasedName Alias name.
@@ -4056,23 +4169,25 @@ AuthorityFactory::createObjectsFromName(
break;
case ObjectType::GEOCENTRIC_CRS:
addToListStringWithOR(otherConditions,
- "(table_name = " GEOCENTRIC " AND "
- "type = " GEOCENTRIC ")");
+ "(table_name = " GEOCENTRIC_SINGLE_QUOTED
+ " AND "
+ "type = " GEOCENTRIC_SINGLE_QUOTED ")");
break;
case ObjectType::GEOGRAPHIC_CRS:
addToListStringWithOR(otherConditions,
"(table_name = 'geodetic_crs' AND "
- "type IN (" GEOG_2D "," GEOG_3D "))");
+ "type IN (" GEOG_2D_SINGLE_QUOTED
+ "," GEOG_3D_SINGLE_QUOTED "))");
break;
case ObjectType::GEOGRAPHIC_2D_CRS:
addToListStringWithOR(otherConditions,
"(table_name = 'geodetic_crs' AND "
- "type = " GEOG_2D ")");
+ "type = " GEOG_2D_SINGLE_QUOTED ")");
break;
case ObjectType::GEOGRAPHIC_3D_CRS:
addToListStringWithOR(otherConditions,
"(table_name = 'geodetic_crs' AND "
- "type = " GEOG_3D ")");
+ "type = " GEOG_3D_SINGLE_QUOTED ")");
break;
case ObjectType::PROJECTED_CRS:
addToListString(tableNameList, "'projected_crs'");
diff --git a/src/proj.h b/src/proj.h
index 552f4b26..4f5fb1d1 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -645,6 +645,74 @@ typedef enum
PJ_CS_TYPE_TEMPORALMEASURE
} PJ_COORDINATE_SYSTEM_TYPE;
+/** \brief Structure given overall description of a CRS.
+ *
+ * This structure may grow over time, and should not be directly allocated by
+ * client code.
+ */
+typedef struct
+{
+ /** Authority name. */
+ char* auth_name;
+ /** Object code. */
+ char* code;
+ /** Object name. */
+ char* name;
+ /** Object type. */
+ PJ_TYPE type;
+ /** Whether the object is deprecated */
+ int deprecated;
+ /** Whereas the west_lon_degree, south_lat_degree, east_lon_degree and
+ * north_lat_degree fields are valid. */
+ int bbox_valid;
+ /** Western-most longitude of the area of use, in degrees. */
+ double west_lon_degree;
+ /** Southern-most latitude of the area of use, in degrees. */
+ double south_lat_degree;
+ /** Eastern-most longitude of the area of use, in degrees. */
+ double east_lon_degree;
+ /** Northern-most latitude of the area of use, in degrees. */
+ double north_lat_degree;
+ /** Name of the area of use. */
+ char* area_name;
+ /** Name of the projection method for a projected CRS. Might be NULL even
+ *for projected CRS in some cases. */
+ char* projection_method_name;
+} PROJ_CRS_INFO;
+
+/** \brief Structure describing optional parameters for proj_get_crs_list();
+ *
+ * This structure may grow over time, and should not be directly allocated by
+ * client code.
+ */
+typedef struct
+{
+ /** Array of allowed object types. Should be NULL if all types are allowed*/
+ const PJ_TYPE* types;
+ /** Size of types. Should be 0 if all types are allowed*/
+ size_t typesCount;
+
+ /** If TRUE and bbox_valid == TRUE, then only CRS whose area of use
+ * entirely contains the specified bounding box will be returned.
+ * If FALSE and bbox_valid == TRUE, then only CRS whose area of use
+ * intersects the specified bounding box will be returned.
+ */
+ int crs_area_of_use_contains_bbox;
+ /** To set to TRUE so that west_lon_degree, south_lat_degree,
+ * east_lon_degree and north_lat_degree fields are taken into account. */
+ int bbox_valid;
+ /** Western-most longitude of the area of use, in degrees. */
+ double west_lon_degree;
+ /** Southern-most latitude of the area of use, in degrees. */
+ double south_lat_degree;
+ /** Eastern-most longitude of the area of use, in degrees. */
+ double east_lon_degree;
+ /** Northern-most latitude of the area of use, in degrees. */
+ double north_lat_degree;
+
+ /** Whether deprecated objects are allowed. Default to FALSE. */
+ int allow_deprecated;
+} PROJ_CRS_LIST_PARAMETERS;
/**@}*/
@@ -774,6 +842,19 @@ PROJ_STRING_LIST PROJ_DLL proj_get_codes_from_database(PJ_CONTEXT *ctx,
PJ_TYPE type,
int allow_deprecated);
+PROJ_CRS_LIST_PARAMETERS PROJ_DLL *proj_get_crs_list_parameters_create(void);
+
+void PROJ_DLL proj_get_crs_list_parameters_destroy(
+ PROJ_CRS_LIST_PARAMETERS* params);
+
+PROJ_CRS_INFO PROJ_DLL **proj_get_crs_info_list_from_database(
+ PJ_CONTEXT *ctx,
+ const char *auth_name,
+ const PROJ_CRS_LIST_PARAMETERS* params,
+ int *out_result_count);
+
+void PROJ_DLL proj_crs_list_destroy(PROJ_CRS_INFO** list);
+
/* ------------------------------------------------------------------------- */
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index ff29185f..be43f715 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -3022,4 +3022,210 @@ TEST_F(CApi, proj_create_cartesian_2D_cs) {
}
}
+// ---------------------------------------------------------------------------
+
+TEST_F(CApi, proj_get_crs_info_list_from_database) {
+ { proj_crs_list_destroy(nullptr); }
+
+ { proj_get_crs_list_parameters_destroy(nullptr); }
+
+ // All null parameters
+ {
+ auto list = proj_get_crs_info_list_from_database(nullptr, nullptr,
+ nullptr, nullptr);
+ ASSERT_NE(list, nullptr);
+ ASSERT_NE(list[0], nullptr);
+ EXPECT_NE(list[0]->auth_name, nullptr);
+ EXPECT_NE(list[0]->code, nullptr);
+ EXPECT_NE(list[0]->name, nullptr);
+ proj_crs_list_destroy(list);
+ }
+
+ // Default parameters
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ proj_get_crs_list_parameters_destroy(params);
+ ASSERT_NE(list, nullptr);
+ EXPECT_GT(result_count, 1);
+ EXPECT_EQ(list[result_count], nullptr);
+ bool found4326 = false;
+ bool found4978 = false;
+ bool found4979 = false;
+ bool found32631 = false;
+ bool found3855 = false;
+ bool found3901 = false;
+ for (int i = 0; i < result_count; i++) {
+ auto code = std::string(list[i]->code);
+ if (code == "4326") {
+ found4326 = true;
+ EXPECT_EQ(std::string(list[i]->auth_name), "EPSG");
+ EXPECT_EQ(std::string(list[i]->name), "WGS 84");
+ EXPECT_EQ(list[i]->type, PJ_TYPE_GEOGRAPHIC_2D_CRS);
+ EXPECT_EQ(list[i]->deprecated, 0);
+ EXPECT_EQ(list[i]->bbox_valid, 1);
+ EXPECT_EQ(list[i]->west_lon_degree, -180.0);
+ EXPECT_EQ(list[i]->south_lat_degree, -90.0);
+ EXPECT_EQ(list[i]->east_lon_degree, 180.0);
+ EXPECT_EQ(list[i]->north_lat_degree, 90.0);
+ EXPECT_EQ(std::string(list[i]->area_name), "World");
+ EXPECT_EQ(list[i]->projection_method_name, nullptr);
+ } else if (code == "4978") {
+ found4978 = true;
+ EXPECT_EQ(list[i]->type, PJ_TYPE_GEOCENTRIC_CRS);
+ } else if (code == "4979") {
+ found4979 = true;
+ EXPECT_EQ(list[i]->type, PJ_TYPE_GEOGRAPHIC_3D_CRS);
+ } else if (code == "32631") {
+ found32631 = true;
+ EXPECT_EQ(list[i]->type, PJ_TYPE_PROJECTED_CRS);
+ EXPECT_EQ(std::string(list[i]->projection_method_name),
+ "Transverse Mercator");
+ } else if (code == "3855") {
+ found3855 = true;
+ EXPECT_EQ(list[i]->type, PJ_TYPE_VERTICAL_CRS);
+ } else if (code == "3901") {
+ found3901 = true;
+ EXPECT_EQ(list[i]->type, PJ_TYPE_COMPOUND_CRS);
+ }
+ EXPECT_EQ(list[i]->deprecated, 0);
+ }
+ EXPECT_TRUE(found4326);
+ EXPECT_TRUE(found4978);
+ EXPECT_TRUE(found4979);
+ EXPECT_TRUE(found32631);
+ EXPECT_TRUE(found3855);
+ EXPECT_TRUE(found3901);
+ proj_crs_list_destroy(list);
+ }
+
+ // Filter on only geodetic crs
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ params->typesCount = 1;
+ auto type = PJ_TYPE_GEODETIC_CRS;
+ params->types = &type;
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ bool foundGeog2D = false;
+ bool foundGeog3D = false;
+ bool foundGeocentric = false;
+ for (int i = 0; i < result_count; i++) {
+ foundGeog2D |= list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS;
+ foundGeog3D |= list[i]->type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
+ foundGeocentric |= list[i]->type == PJ_TYPE_GEOCENTRIC_CRS;
+ EXPECT_TRUE(list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
+ list[i]->type == PJ_TYPE_GEOGRAPHIC_3D_CRS ||
+ list[i]->type == PJ_TYPE_GEOCENTRIC_CRS);
+ }
+ EXPECT_TRUE(foundGeog2D);
+ EXPECT_TRUE(foundGeog3D);
+ EXPECT_TRUE(foundGeocentric);
+ proj_get_crs_list_parameters_destroy(params);
+ proj_crs_list_destroy(list);
+ }
+
+ // Filter on only geographic crs
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ params->typesCount = 1;
+ auto type = PJ_TYPE_GEOGRAPHIC_CRS;
+ params->types = &type;
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ bool foundGeog2D = false;
+ bool foundGeog3D = false;
+ for (int i = 0; i < result_count; i++) {
+ foundGeog2D |= list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS;
+ foundGeog3D |= list[i]->type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
+ EXPECT_TRUE(list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
+ list[i]->type == PJ_TYPE_GEOGRAPHIC_3D_CRS);
+ }
+ EXPECT_TRUE(foundGeog2D);
+ EXPECT_TRUE(foundGeog3D);
+ proj_get_crs_list_parameters_destroy(params);
+ proj_crs_list_destroy(list);
+ }
+
+ // Filter on only geographic 2D crs and projected CRS
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ params->typesCount = 2;
+ const PJ_TYPE types[] = {PJ_TYPE_GEOGRAPHIC_2D_CRS,
+ PJ_TYPE_PROJECTED_CRS};
+ params->types = types;
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ bool foundGeog2D = false;
+ bool foundProjected = false;
+ for (int i = 0; i < result_count; i++) {
+ foundGeog2D |= list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS;
+ foundProjected |= list[i]->type == PJ_TYPE_PROJECTED_CRS;
+ EXPECT_TRUE(list[i]->type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
+ list[i]->type == PJ_TYPE_PROJECTED_CRS);
+ }
+ EXPECT_TRUE(foundGeog2D);
+ EXPECT_TRUE(foundProjected);
+ proj_get_crs_list_parameters_destroy(params);
+ proj_crs_list_destroy(list);
+ }
+
+ // Filter on bbox (inclusion)
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ params->bbox_valid = 1;
+ params->west_lon_degree = 2;
+ params->south_lat_degree = 49;
+ params->east_lon_degree = 2.1;
+ params->north_lat_degree = 49.1;
+ params->typesCount = 1;
+ auto type = PJ_TYPE_PROJECTED_CRS;
+ params->types = &type;
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ ASSERT_NE(list, nullptr);
+ EXPECT_GT(result_count, 1);
+ for (int i = 0; i < result_count; i++) {
+ EXPECT_LE(list[i]->west_lon_degree, params->west_lon_degree);
+ EXPECT_LE(list[i]->south_lat_degree, params->south_lat_degree);
+ EXPECT_GE(list[i]->east_lon_degree, params->east_lon_degree);
+ EXPECT_GE(list[i]->north_lat_degree, params->north_lat_degree);
+ }
+ proj_get_crs_list_parameters_destroy(params);
+ proj_crs_list_destroy(list);
+ }
+
+ // Filter on bbox (intersection)
+ {
+ int result_count = 0;
+ auto params = proj_get_crs_list_parameters_create();
+ params->bbox_valid = 1;
+ params->west_lon_degree = 2;
+ params->south_lat_degree = 49;
+ params->east_lon_degree = 2.1;
+ params->north_lat_degree = 49.1;
+ params->crs_area_of_use_contains_bbox = 0;
+ params->typesCount = 1;
+ auto type = PJ_TYPE_PROJECTED_CRS;
+ params->types = &type;
+ auto list = proj_get_crs_info_list_from_database(m_ctxt, "EPSG", params,
+ &result_count);
+ ASSERT_NE(list, nullptr);
+ EXPECT_GT(result_count, 1);
+ for (int i = 0; i < result_count; i++) {
+ EXPECT_LE(list[i]->west_lon_degree, params->east_lon_degree);
+ EXPECT_LE(list[i]->south_lat_degree, params->north_lat_degree);
+ EXPECT_GE(list[i]->east_lon_degree, params->west_lon_degree);
+ EXPECT_GE(list[i]->north_lat_degree, params->south_lat_degree);
+ }
+ proj_get_crs_list_parameters_destroy(params);
+ proj_crs_list_destroy(list);
+ }
+}
} // namespace
diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp
index 95436c75..ce019079 100644
--- a/test/unit/test_factory.cpp
+++ b/test/unit/test_factory.cpp
@@ -2810,4 +2810,103 @@ TEST(factory, listAreaOfUseFromName) {
}
}
+// ---------------------------------------------------------------------------
+
+TEST(factory, getCRSInfoList) {
+ auto ctxt = DatabaseContext::create();
+ {
+ auto factory = AuthorityFactory::create(ctxt, std::string());
+ auto list = factory->getCRSInfoList();
+ EXPECT_GT(list.size(), 1U);
+ bool foundEPSG = false;
+ bool foundIGNF = true;
+ bool found4326 = false;
+ for (const auto &info : list) {
+ foundEPSG |= info.authName == "EPSG";
+ foundIGNF |= info.authName == "IGNF";
+ if (info.authName == "EPSG" && info.code == "4326") {
+ found4326 = true;
+ }
+ }
+ EXPECT_TRUE(foundEPSG);
+ EXPECT_TRUE(foundIGNF);
+ EXPECT_TRUE(found4326);
+ }
+ {
+ auto factory = AuthorityFactory::create(ctxt, "EPSG");
+ auto list = factory->getCRSInfoList();
+ EXPECT_GT(list.size(), 1U);
+ bool found4326 = false;
+ bool found4978 = false;
+ bool found4979 = false;
+ bool found32631 = false;
+ bool found3855 = false;
+ bool found6871 = false;
+ for (const auto &info : list) {
+ EXPECT_EQ(info.authName, "EPSG");
+ if (info.code == "4326") {
+ EXPECT_EQ(info.name, "WGS 84");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS);
+ EXPECT_EQ(info.deprecated, false);
+ EXPECT_EQ(info.bbox_valid, true);
+ EXPECT_EQ(info.west_lon_degree, -180.0);
+ EXPECT_EQ(info.south_lat_degree, -90.0);
+ EXPECT_EQ(info.east_lon_degree, 180.0);
+ EXPECT_EQ(info.north_lat_degree, 90.0);
+ EXPECT_EQ(info.areaName, "World");
+ EXPECT_TRUE(info.projectionMethodName.empty());
+ found4326 = true;
+ } else if (info.code == "4296") { // Soudan - deprecated
+ EXPECT_EQ(info.bbox_valid, false);
+ EXPECT_EQ(info.west_lon_degree, 0.0);
+ EXPECT_EQ(info.south_lat_degree, 0.0);
+ EXPECT_EQ(info.east_lon_degree, 0.0);
+ EXPECT_EQ(info.north_lat_degree, 0.0);
+ } else if (info.code == "4978") {
+ EXPECT_EQ(info.name, "WGS 84");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::GEOCENTRIC_CRS);
+ found4978 = true;
+ } else if (info.code == "4979") {
+ EXPECT_EQ(info.name, "WGS 84");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS);
+ found4979 = true;
+ } else if (info.code == "32631") {
+ EXPECT_EQ(info.name, "WGS 84 / UTM zone 31N");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::PROJECTED_CRS);
+ EXPECT_EQ(info.deprecated, false);
+ EXPECT_EQ(info.bbox_valid, true);
+ EXPECT_EQ(info.west_lon_degree, 0.0);
+ EXPECT_EQ(info.south_lat_degree, 0.0);
+ EXPECT_EQ(info.east_lon_degree, 6.0);
+ EXPECT_EQ(info.north_lat_degree, 84.0);
+ EXPECT_EQ(info.areaName, "World - N hemisphere - 0\xC2\xB0"
+ "E to 6\xC2\xB0"
+ "E - by country");
+ EXPECT_EQ(info.projectionMethodName, "Transverse Mercator");
+ found32631 = true;
+ } else if (info.code == "3855") {
+ EXPECT_EQ(info.name, "EGM2008 height");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::VERTICAL_CRS);
+ found3855 = true;
+ } else if (info.code == "6871") {
+ EXPECT_EQ(info.name,
+ "WGS 84 / Pseudo-Mercator + EGM2008 geoid height");
+ EXPECT_EQ(info.type,
+ AuthorityFactory::ObjectType::COMPOUND_CRS);
+ found6871 = true;
+ }
+ }
+ EXPECT_TRUE(found4326);
+ EXPECT_TRUE(found4978);
+ EXPECT_TRUE(found4979);
+ EXPECT_TRUE(found32631);
+ EXPECT_TRUE(found3855);
+ EXPECT_TRUE(found6871);
+ }
+}
} // namespace