aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-02-06 18:32:58 +0100
committerEven Rouault <even.rouault@spatialys.com>2019-02-07 10:37:51 +0100
commit02efe72181814097284196de9b9b984db0fb3d26 (patch)
tree3aa91671fad83ae5225489f80731803ffd682d71
parent457b173cbe8fdb790b011d1828a0fd9f8f6221a4 (diff)
downloadPROJ-02efe72181814097284196de9b9b984db0fb3d26.tar.gz
PROJ-02efe72181814097284196de9b9b984db0fb3d26.zip
Add proj_get_crs_info_list_from_database()
This method is intended to be used typically by GUI that lists all possible CRS. What is does could be done by previously existing functions, but it is much faster. It typically runs in less than 0.1s (hot run) versus ~0.5s with the method that consists in enumerating all codes and instanciating a PJ object for each of them.
-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