diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-02-06 18:32:58 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2019-02-07 10:37:51 +0100 |
| commit | 02efe72181814097284196de9b9b984db0fb3d26 (patch) | |
| tree | 3aa91671fad83ae5225489f80731803ffd682d71 /src | |
| parent | 457b173cbe8fdb790b011d1828a0fd9f8f6221a4 (diff) | |
| download | PROJ-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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/iso19111/c_api.cpp | 212 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 161 | ||||
| -rw-r--r-- | src/proj.h | 81 |
3 files changed, 431 insertions, 23 deletions
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'"); @@ -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); + /* ------------------------------------------------------------------------- */ |
