Skip to content

Commit

Permalink
Add proj_get_units_from_database() (fixes #2004)
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault committed Mar 12, 2020
1 parent ca3caf0 commit 3542576
Show file tree
Hide file tree
Showing 11 changed files with 497 additions and 114 deletions.
32 changes: 32 additions & 0 deletions data/sql/customizations.sql
Expand Up @@ -135,3 +135,35 @@ INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6312','hermannskogel','
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6299','ire65','PROJ');
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6272','nzgd49','PROJ');
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6277','OSGB36','PROJ');

---- PROJ unit short names -----

-- Linear units
UPDATE unit_of_measure SET proj_short_name = 'mm' WHERE auth_name = 'EPSG' AND code = '1025';
UPDATE unit_of_measure SET proj_short_name = 'cm' WHERE auth_name = 'EPSG' AND code = '1033';
UPDATE unit_of_measure SET proj_short_name = 'm' WHERE auth_name = 'EPSG' AND code = '9001';
UPDATE unit_of_measure SET proj_short_name = 'ft' WHERE auth_name = 'EPSG' AND code = '9002';
UPDATE unit_of_measure SET proj_short_name = 'us-ft' WHERE auth_name = 'EPSG' AND code = '9003';
UPDATE unit_of_measure SET proj_short_name = 'fath' WHERE auth_name = 'EPSG' AND code = '9014';
UPDATE unit_of_measure SET proj_short_name = 'kmi' WHERE auth_name = 'EPSG' AND code = '9030';
UPDATE unit_of_measure SET proj_short_name = 'us-ch' WHERE auth_name = 'EPSG' AND code = '9033';
UPDATE unit_of_measure SET proj_short_name = 'us-mi' WHERE auth_name = 'EPSG' AND code = '9035';
UPDATE unit_of_measure SET proj_short_name = 'km' WHERE auth_name = 'EPSG' AND code = '9036';
UPDATE unit_of_measure SET proj_short_name = 'ind-ft' WHERE auth_name = 'EPSG' AND code = '9081';
UPDATE unit_of_measure SET proj_short_name = 'ind-yd' WHERE auth_name = 'EPSG' AND code = '9085';
UPDATE unit_of_measure SET proj_short_name = 'mi' WHERE auth_name = 'EPSG' AND code = '9093';
UPDATE unit_of_measure SET proj_short_name = 'yd' WHERE auth_name = 'EPSG' AND code = '9096';
UPDATE unit_of_measure SET proj_short_name = 'ch' WHERE auth_name = 'EPSG' AND code = '9097';
UPDATE unit_of_measure SET proj_short_name = 'link' WHERE auth_name = 'EPSG' AND code = '9098';

-- Angular units
UPDATE unit_of_measure SET proj_short_name = 'rad' WHERE auth_name = 'EPSG' AND code = '9101';
UPDATE unit_of_measure SET proj_short_name = 'deg' WHERE auth_name = 'EPSG' AND code = '9102';
UPDATE unit_of_measure SET proj_short_name = 'grad' WHERE auth_name = 'EPSG' AND code = '9105';

-- PROJ specific units
INSERT INTO "unit_of_measure" VALUES('PROJ','DM','decimeter','length',0.01,'dm',0);
INSERT INTO "unit_of_measure" VALUES('PROJ','IN','inch','length',0.0254,'in',0);
INSERT INTO "unit_of_measure" VALUES('PROJ','US_IN','US survey inch','length',0.025400050800101,'us-in',0);
INSERT INTO "unit_of_measure" VALUES('PROJ','US_YD','US survey yard','length',0.914401828803658,'us-yd',0);
INSERT INTO "unit_of_measure" VALUES('PROJ','IND_CH','Indian chain','length',20.11669506,'ind-ch',0);
1 change: 1 addition & 0 deletions data/sql/proj_db_table_defs.sql
Expand Up @@ -14,6 +14,7 @@ CREATE TABLE unit_of_measure(
name TEXT NOT NULL CHECK (length(name) >= 2),
type TEXT NOT NULL CHECK (type IN ('length', 'angle', 'scale', 'time')),
conv_factor FLOAT,
proj_short_name TEXT, -- PROJ string name, like 'm', 'ft'. Might be NULL
deprecated BOOLEAN NOT NULL CHECK (deprecated IN (0, 1)),
CONSTRAINT pk_unit_of_measure PRIMARY KEY (auth_name, code)
);
Expand Down
190 changes: 95 additions & 95 deletions data/sql/unit_of_measure.sql

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions docs/source/development/reference/functions.rst
Expand Up @@ -521,6 +521,9 @@ Lists
entry of the returned array is a NULL-entry. The array is statically
allocated and does not need to be freed after use.
Note: starting with PROJ 7.1, this function is deprecated by
:cpp:func:`proj_get_units_from_database`
:returns: :c:type:`PJ_UNITS*`
.. c:function:: const PJ_PRIME_MERIDIANS* proj_list_prime_meridians(void)
Expand Down
30 changes: 29 additions & 1 deletion include/proj/io.hpp
Expand Up @@ -1013,6 +1013,8 @@ class PROJ_GCC_DLL AuthorityFactory {

PROJ_DLL std::string getDescriptionText(const std::string &code) const;

// non-standard

/** CRS information */
struct CRSInfo {
/** Authority name */
Expand Down Expand Up @@ -1049,7 +1051,33 @@ class PROJ_GCC_DLL AuthorityFactory {

PROJ_DLL std::list<CRSInfo> getCRSInfoList() const;

// non-standard
/** Unit information */
struct UnitInfo {
/** Authority name */
std::string authName;
/** Code */
std::string code;
/** Name */
std::string name;
/** Category: one of "linear", "linear_per_time", "angular",
* "angular_per_time", "scale", "scale_per_time" or "time" */
std::string category;
/** Conversion factor to the SI unit.
* It might be 0 in some cases to indicate no known conversion factor.
*/
double convFactor;
/** PROJ short name (may be empty) */
std::string projShortName;
/** Whether the object is deprecated */
bool deprecated;

//! @cond Doxygen_Suppress
UnitInfo();
//! @endcond
};

PROJ_DLL std::list<UnitInfo> getUnitList() const;

PROJ_DLL static AuthorityFactoryNNPtr
create(const DatabaseContextNNPtr &context,
const std::string &authorityName);
Expand Down
2 changes: 1 addition & 1 deletion scripts/build_db.py
Expand Up @@ -68,7 +68,7 @@ def ingest_epsg():

def fill_unit_of_measure(proj_db_cursor):
proj_db_cursor.execute(
"INSERT INTO unit_of_measure SELECT ?, uom_code, unit_of_meas_name, unit_of_meas_type, factor_b / factor_c, deprecated FROM epsg.epsg_unitofmeasure", (EPSG_AUTHORITY,))
"INSERT INTO unit_of_measure SELECT ?, uom_code, unit_of_meas_name, unit_of_meas_type, factor_b / factor_c, NULL, deprecated FROM epsg.epsg_unitofmeasure", (EPSG_AUTHORITY,))


def fill_ellipsoid(proj_db_cursor):
Expand Down
127 changes: 116 additions & 11 deletions src/iso19111/c_api.cpp
Expand Up @@ -662,7 +662,8 @@ PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name,
// ---------------------------------------------------------------------------

//! @cond Doxygen_Suppress
static const char *get_unit_category(UnitOfMeasure::Type type) {
static const char *get_unit_category(const std::string &unit_name,
UnitOfMeasure::Type type) {
const char *ret = nullptr;
switch (type) {
case UnitOfMeasure::Type::UNKNOWN:
Expand All @@ -672,19 +673,26 @@ static const char *get_unit_category(UnitOfMeasure::Type type) {
ret = "none";
break;
case UnitOfMeasure::Type::ANGULAR:
ret = "angular";
ret = unit_name.find(" per ") != std::string::npos ? "angular_per_time"
: "angular";
break;
case UnitOfMeasure::Type::LINEAR:
ret = "linear";
ret = unit_name.find(" per ") != std::string::npos ? "linear_per_time"
: "linear";
break;
case UnitOfMeasure::Type::SCALE:
ret = "scale";
ret = unit_name.find(" per year") != std::string::npos ||
unit_name.find(" per second") != std::string::npos
? "scale_per_time"
: "scale";
break;
case UnitOfMeasure::Type::TIME:
ret = "time";
break;
case UnitOfMeasure::Type::PARAMETRIC:
ret = "parametric";
ret = unit_name.find(" per ") != std::string::npos
? "parametric_per_time"
: "parametric";
break;
}
return ret;
Expand All @@ -704,8 +712,9 @@ static const char *get_unit_category(UnitOfMeasure::Type type) {
* @param out_conv_factor Pointer to a value to store the conversion
* factor of the prime meridian longitude unit to radian. or NULL
* @param out_category Pointer to a string value to store the parameter name. or
* NULL. This value might be "unknown", "none", "linear", "angular", "scale",
* "time" or "parametric";
* NULL. This value might be "unknown", "none", "linear", "linear_per_time",
* "angular", "angular_per_time", "scale", "scale_per_time", "time",
* "parametric" or "parametric_per_time"
* @return TRUE in case of success
*/
int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
Expand All @@ -726,7 +735,7 @@ int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
*out_conv_factor = obj->conversionToSI();
}
if (out_category) {
*out_category = get_unit_category(obj->type());
*out_category = get_unit_category(obj->name(), obj->type());
}
ctx->cpp_context->autoCloseDbIfNeeded();
return true;
Expand Down Expand Up @@ -2585,6 +2594,100 @@ void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) {

// ---------------------------------------------------------------------------

/** \brief Enumerate units from the database, taking into account various
* criteria.
*
* The returned object is an array of PROJ_UNIT_INFO* pointers, whose last
* entry is NULL. This array should be freed with proj_unit_list_destroy()
*
* @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 category Filter by category, if this parameter is not NULL. Category
* is one of "linear", "linear_per_time", "angular", "angular_per_time",
* "scale", "scale_per_time" or "time"
* @param allow_deprecated whether we should return deprecated objects as well.
* @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_UNIT_INFO* pointers to be freed with
* proj_unit_list_destroy(), or NULL in case of error.
*
* @since 7.1
*/
PROJ_UNIT_INFO **proj_get_units_from_database(PJ_CONTEXT *ctx,
const char *auth_name,
const char *category,
int allow_deprecated,
int *out_result_count) {
SANITIZE_CTX(ctx);
PROJ_UNIT_INFO **ret = nullptr;
int i = 0;
try {
auto factory = AuthorityFactory::create(getDBcontext(ctx),
auth_name ? auth_name : "");
auto list = factory->getUnitList();
ret = new PROJ_UNIT_INFO *[list.size() + 1];
for (const auto &info : list) {
if (category && info.category != category) {
continue;
}
if (!allow_deprecated && info.deprecated) {
continue;
}
ret[i] = new PROJ_UNIT_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]->category = pj_strdup(info.category.c_str());
ret[i]->conv_factor = info.convFactor;
ret[i]->proj_short_name =
info.projShortName.empty()
? nullptr
: pj_strdup(info.projShortName.c_str());
ret[i]->deprecated = info.deprecated;
i++;
}
ret[i] = nullptr;
if (out_result_count)
*out_result_count = i;
ctx->cpp_context->autoCloseDbIfNeeded();
return ret;
} catch (const std::exception &e) {
proj_log_error(ctx, __FUNCTION__, e.what());
if (ret) {
ret[i + 1] = nullptr;
proj_unit_list_destroy(ret);
}
if (out_result_count)
*out_result_count = 0;
}
ctx->cpp_context->autoCloseDbIfNeeded();
return nullptr;
}

// ---------------------------------------------------------------------------

/** \brief Destroy the result returned by
* proj_get_units_from_database().
*
* @since 7.1
*/
void proj_unit_list_destroy(PROJ_UNIT_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]->category);
pj_dalloc(list[i]->proj_short_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
*
Expand Down Expand Up @@ -6749,8 +6852,9 @@ int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx,
* unit code. or NULL
* @param out_unit_category Pointer to a string value to store the parameter
* name. or
* NULL. This value might be "unknown", "none", "linear", "angular", "scale",
* "time" or "parametric";
* NULL. This value might be "unknown", "none", "linear", "linear_per_time",
* "angular", "angular_per_time", "scale", "scale_per_time", "time",
* "parametric" or "parametric_per_time"
* @return TRUE in case of success.
*/

Expand Down Expand Up @@ -6852,7 +6956,8 @@ int proj_coordoperation_get_param(
*out_unit_code = unit.code().c_str();
}
if (out_unit_category) {
*out_unit_category = get_unit_category(unit.type());
*out_unit_category =
get_unit_category(unit.name(), unit.type());
}
}
}
Expand Down
58 changes: 58 additions & 0 deletions src/iso19111/factory.cpp
Expand Up @@ -5214,6 +5214,64 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {

// ---------------------------------------------------------------------------

//! @cond Doxygen_Suppress
AuthorityFactory::UnitInfo::UnitInfo()
: authName{}, code{}, name{}, category{}, convFactor{}, projShortName{},
deprecated{} {}
//! @endcond

// ---------------------------------------------------------------------------

/** \brief Return the list of units.
* @throw FactoryException
*
* @since 7.1
*/
std::list<AuthorityFactory::UnitInfo> AuthorityFactory::getUnitList() const {
std::string sql = "SELECT auth_name, code, name, type, conv_factor, "
"proj_short_name, deprecated FROM unit_of_measure";
ListOfParams params;
if (d->hasAuthorityRestriction()) {
sql += " WHERE auth_name = ?";
params.emplace_back(d->authority());
}
sql += " ORDER BY auth_name, code";

auto sqlRes = d->run(sql, params);
std::list<AuthorityFactory::UnitInfo> res;
for (const auto &row : sqlRes) {
AuthorityFactory::UnitInfo info;
info.authName = row[0];
info.code = row[1];
info.name = row[2];
const std::string &raw_category(row[3]);
if (raw_category == "length") {
info.category = info.name.find(" per ") != std::string::npos
? "linear_per_time"
: "linear";
} else if (raw_category == "angle") {
info.category = info.name.find(" per ") != std::string::npos
? "angular_per_time"
: "angular";
} else if (raw_category == "scale") {
info.category =
info.name.find(" per year") != std::string::npos ||
info.name.find(" per second") != std::string::npos
? "scale_per_time"
: "scale";
} else {
info.category = raw_category;
}
info.convFactor = row[4].empty() ? 0 : c_locale_stod(row[4]);
info.projShortName = row[5];
info.deprecated = row[6] == "1";
res.emplace_back(info);
}
return res;
}

// ---------------------------------------------------------------------------

/** \brief Gets the official name from a possibly alias name.
*
* @param aliasedName Alias name.
Expand Down

0 comments on commit 3542576

Please sign in to comment.