Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add proj_get_units_from_database() (fixes #2004) #2065

Merged
merged 2 commits into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions data/sql/customizations.sql
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
rouault marked this conversation as resolved.
Show resolved Hide resolved
: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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading