Skip to content

Commit 1e16989

Browse files
committed
Add proj_get_units_from_database() (fixes #2004)
1 parent ca3caf0 commit 1e16989

File tree

11 files changed

+496
-114
lines changed

11 files changed

+496
-114
lines changed

data/sql/customizations.sql

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,35 @@ INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6312','hermannskogel','
135135
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6299','ire65','PROJ');
136136
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6272','nzgd49','PROJ');
137137
INSERT INTO "alias_name" VALUES('geodetic_datum','EPSG','6277','OSGB36','PROJ');
138+
139+
---- PROJ unit short names -----
140+
141+
-- Linear units
142+
UPDATE unit_of_measure SET proj_short_name = 'mm' WHERE auth_name = 'EPSG' AND code = '1025';
143+
UPDATE unit_of_measure SET proj_short_name = 'cm' WHERE auth_name = 'EPSG' AND code = '1033';
144+
UPDATE unit_of_measure SET proj_short_name = 'm' WHERE auth_name = 'EPSG' AND code = '9001';
145+
UPDATE unit_of_measure SET proj_short_name = 'ft' WHERE auth_name = 'EPSG' AND code = '9002';
146+
UPDATE unit_of_measure SET proj_short_name = 'us-ft' WHERE auth_name = 'EPSG' AND code = '9003';
147+
UPDATE unit_of_measure SET proj_short_name = 'fath' WHERE auth_name = 'EPSG' AND code = '9014';
148+
UPDATE unit_of_measure SET proj_short_name = 'kmi' WHERE auth_name = 'EPSG' AND code = '9030';
149+
UPDATE unit_of_measure SET proj_short_name = 'us-ch' WHERE auth_name = 'EPSG' AND code = '9033';
150+
UPDATE unit_of_measure SET proj_short_name = 'us-mi' WHERE auth_name = 'EPSG' AND code = '9035';
151+
UPDATE unit_of_measure SET proj_short_name = 'km' WHERE auth_name = 'EPSG' AND code = '9036';
152+
UPDATE unit_of_measure SET proj_short_name = 'ind-ft' WHERE auth_name = 'EPSG' AND code = '9081';
153+
UPDATE unit_of_measure SET proj_short_name = 'ind-yd' WHERE auth_name = 'EPSG' AND code = '9085';
154+
UPDATE unit_of_measure SET proj_short_name = 'mi' WHERE auth_name = 'EPSG' AND code = '9093';
155+
UPDATE unit_of_measure SET proj_short_name = 'yd' WHERE auth_name = 'EPSG' AND code = '9096';
156+
UPDATE unit_of_measure SET proj_short_name = 'ch' WHERE auth_name = 'EPSG' AND code = '9097';
157+
UPDATE unit_of_measure SET proj_short_name = 'link' WHERE auth_name = 'EPSG' AND code = '9098';
158+
159+
-- Angular units
160+
UPDATE unit_of_measure SET proj_short_name = 'rad' WHERE auth_name = 'EPSG' AND code = '9101';
161+
UPDATE unit_of_measure SET proj_short_name = 'deg' WHERE auth_name = 'EPSG' AND code = '9102';
162+
UPDATE unit_of_measure SET proj_short_name = 'grad' WHERE auth_name = 'EPSG' AND code = '9105';
163+
164+
-- PROJ specific units
165+
INSERT INTO "unit_of_measure" VALUES('PROJ','DM','decimeter','length',0.01,'dm',0);
166+
INSERT INTO "unit_of_measure" VALUES('PROJ','IN','inch','length',0.0254,'in',0);
167+
INSERT INTO "unit_of_measure" VALUES('PROJ','US_IN','US survey inch','length',0.025400050800101,'us-in',0);
168+
INSERT INTO "unit_of_measure" VALUES('PROJ','US_YD','US survey yard','length',0.914401828803658,'us-yd',0);
169+
INSERT INTO "unit_of_measure" VALUES('PROJ','IND_CH','Indian chain','length',20.11669506,'ind-ch',0);

data/sql/proj_db_table_defs.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ CREATE TABLE unit_of_measure(
1414
name TEXT NOT NULL CHECK (length(name) >= 2),
1515
type TEXT NOT NULL CHECK (type IN ('length', 'angle', 'scale', 'time')),
1616
conv_factor FLOAT,
17+
proj_short_name TEXT, -- PROJ string name, like 'm', 'ft'. Might be NULL
1718
deprecated BOOLEAN NOT NULL CHECK (deprecated IN (0, 1)),
1819
CONSTRAINT pk_unit_of_measure PRIMARY KEY (auth_name, code)
1920
);

data/sql/unit_of_measure.sql

Lines changed: 95 additions & 95 deletions
Large diffs are not rendered by default.

docs/source/development/reference/functions.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,9 @@ Lists
521521
entry of the returned array is a NULL-entry. The array is statically
522522
allocated and does not need to be freed after use.
523523
524+
Note: starting with PROJ 7.1, this function is deprecated by
525+
:cpp:func:`proj_get_units_from_database`
526+
524527
:returns: :c:type:`PJ_UNITS*`
525528
526529
.. c:function:: const PJ_PRIME_MERIDIANS* proj_list_prime_meridians(void)

include/proj/io.hpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,8 @@ class PROJ_GCC_DLL AuthorityFactory {
10131013

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

1016+
// non-standard
1017+
10161018
/** CRS information */
10171019
struct CRSInfo {
10181020
/** Authority name */
@@ -1049,7 +1051,33 @@ class PROJ_GCC_DLL AuthorityFactory {
10491051

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

1052-
// non-standard
1054+
/** Unit information */
1055+
struct UnitInfo {
1056+
/** Authority name */
1057+
std::string authName;
1058+
/** Code */
1059+
std::string code;
1060+
/** Name */
1061+
std::string name;
1062+
/** Category: one of "linear", "linear_per_time", "angular",
1063+
* "angular_per_time", "scale", "scale_per_time" or "time" */
1064+
std::string category;
1065+
/** Conversion factor to the SI unit.
1066+
* It might be 0 in some cases to indicate no known conversion factor.
1067+
*/
1068+
double convFactor;
1069+
/** PROJ short name (may be empty) */
1070+
std::string projShortName;
1071+
/** Whether the object is deprecated */
1072+
bool deprecated;
1073+
1074+
//! @cond Doxygen_Suppress
1075+
UnitInfo();
1076+
//! @endcond
1077+
};
1078+
1079+
PROJ_DLL std::list<UnitInfo> getUnitList() const;
1080+
10531081
PROJ_DLL static AuthorityFactoryNNPtr
10541082
create(const DatabaseContextNNPtr &context,
10551083
const std::string &authorityName);

scripts/build_db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def ingest_epsg():
6868

6969
def fill_unit_of_measure(proj_db_cursor):
7070
proj_db_cursor.execute(
71-
"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,))
71+
"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,))
7272

7373

7474
def fill_ellipsoid(proj_db_cursor):

src/iso19111/c_api.cpp

Lines changed: 116 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,8 @@ PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name,
662662
// ---------------------------------------------------------------------------
663663

664664
//! @cond Doxygen_Suppress
665-
static const char *get_unit_category(UnitOfMeasure::Type type) {
665+
static const char *get_unit_category(const std::string &unit_name,
666+
UnitOfMeasure::Type type) {
666667
const char *ret = nullptr;
667668
switch (type) {
668669
case UnitOfMeasure::Type::UNKNOWN:
@@ -672,19 +673,26 @@ static const char *get_unit_category(UnitOfMeasure::Type type) {
672673
ret = "none";
673674
break;
674675
case UnitOfMeasure::Type::ANGULAR:
675-
ret = "angular";
676+
ret = unit_name.find(" per ") != std::string::npos ? "angular_per_time"
677+
: "angular";
676678
break;
677679
case UnitOfMeasure::Type::LINEAR:
678-
ret = "linear";
680+
ret = unit_name.find(" per ") != std::string::npos ? "linear_per_time"
681+
: "linear";
679682
break;
680683
case UnitOfMeasure::Type::SCALE:
681-
ret = "scale";
684+
ret = unit_name.find(" per year") != std::string::npos ||
685+
unit_name.find(" per second") != std::string::npos
686+
? "scale_per_time"
687+
: "scale";
682688
break;
683689
case UnitOfMeasure::Type::TIME:
684690
ret = "time";
685691
break;
686692
case UnitOfMeasure::Type::PARAMETRIC:
687-
ret = "parametric";
693+
ret = unit_name.find(" per ") != std::string::npos
694+
? "parametric_per_time"
695+
: "parametric";
688696
break;
689697
}
690698
return ret;
@@ -704,8 +712,9 @@ static const char *get_unit_category(UnitOfMeasure::Type type) {
704712
* @param out_conv_factor Pointer to a value to store the conversion
705713
* factor of the prime meridian longitude unit to radian. or NULL
706714
* @param out_category Pointer to a string value to store the parameter name. or
707-
* NULL. This value might be "unknown", "none", "linear", "angular", "scale",
708-
* "time" or "parametric";
715+
* NULL. This value might be "unknown", "none", "linear", "linear_per_time",
716+
* "angular", "angular_per_time", "scale", "scale_per_time", "time",
717+
* "parametric" or "parametric_per_time"
709718
* @return TRUE in case of success
710719
*/
711720
int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
@@ -726,7 +735,7 @@ int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
726735
*out_conv_factor = obj->conversionToSI();
727736
}
728737
if (out_category) {
729-
*out_category = get_unit_category(obj->type());
738+
*out_category = get_unit_category(obj->name(), obj->type());
730739
}
731740
ctx->cpp_context->autoCloseDbIfNeeded();
732741
return true;
@@ -2585,6 +2594,100 @@ void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) {
25852594

25862595
// ---------------------------------------------------------------------------
25872596

2597+
/** \brief Enumerate units from the database, taking into account various
2598+
* criteria.
2599+
*
2600+
* The returned object is an array of PROJ_UNIT_INFO* pointers, whose last
2601+
* entry is NULL. This array should be freed with proj_unit_list_destroy()
2602+
*
2603+
* @param ctx PROJ context, or NULL for default context
2604+
* @param auth_name Authority name, used to restrict the search.
2605+
* Or NULL for all authorities.
2606+
* @param category Filter by category, if this parameter is not NULL. Category
2607+
* is one of "linear", "linear_per_time", "angular", "angular_per_time",
2608+
* "scale", "scale_per_time" or "time"
2609+
* @param allow_deprecated whether we should return deprecated objects as well.
2610+
* @param out_result_count Output parameter pointing to an integer to receive
2611+
* the size of the result list. Might be NULL
2612+
* @return an array of PROJ_UNIT_INFO* pointers to be freed with
2613+
* proj_unit_list_destroy(), or NULL in case of error.
2614+
*
2615+
* @since 7.1
2616+
*/
2617+
PROJ_UNIT_INFO **proj_get_units_from_database(PJ_CONTEXT *ctx,
2618+
const char *auth_name,
2619+
const char *category,
2620+
int allow_deprecated,
2621+
int *out_result_count) {
2622+
SANITIZE_CTX(ctx);
2623+
PROJ_UNIT_INFO **ret = nullptr;
2624+
int i = 0;
2625+
try {
2626+
auto factory = AuthorityFactory::create(getDBcontext(ctx),
2627+
auth_name ? auth_name : "");
2628+
auto list = factory->getUnitList();
2629+
ret = new PROJ_UNIT_INFO *[list.size() + 1];
2630+
for (const auto &info : list) {
2631+
if (category && info.category != category) {
2632+
continue;
2633+
}
2634+
if (!allow_deprecated && info.deprecated) {
2635+
continue;
2636+
}
2637+
ret[i] = new PROJ_UNIT_INFO;
2638+
ret[i]->auth_name = pj_strdup(info.authName.c_str());
2639+
ret[i]->code = pj_strdup(info.code.c_str());
2640+
ret[i]->name = pj_strdup(info.name.c_str());
2641+
ret[i]->category = pj_strdup(info.category.c_str());
2642+
ret[i]->conv_factor = info.convFactor;
2643+
ret[i]->proj_short_name =
2644+
info.projShortName.empty()
2645+
? nullptr
2646+
: pj_strdup(info.projShortName.c_str());
2647+
ret[i]->deprecated = info.deprecated;
2648+
i++;
2649+
}
2650+
ret[i] = nullptr;
2651+
if (out_result_count)
2652+
*out_result_count = i;
2653+
ctx->cpp_context->autoCloseDbIfNeeded();
2654+
return ret;
2655+
} catch (const std::exception &e) {
2656+
proj_log_error(ctx, __FUNCTION__, e.what());
2657+
if (ret) {
2658+
ret[i + 1] = nullptr;
2659+
proj_unit_list_destroy(ret);
2660+
}
2661+
if (out_result_count)
2662+
*out_result_count = 0;
2663+
}
2664+
ctx->cpp_context->autoCloseDbIfNeeded();
2665+
return nullptr;
2666+
}
2667+
2668+
// ---------------------------------------------------------------------------
2669+
2670+
/** \brief Destroy the result returned by
2671+
* proj_get_units_from_database().
2672+
*
2673+
* @since 7.1
2674+
*/
2675+
void proj_unit_list_destroy(PROJ_UNIT_INFO **list) {
2676+
if (list) {
2677+
for (int i = 0; list[i] != nullptr; i++) {
2678+
pj_dalloc(list[i]->auth_name);
2679+
pj_dalloc(list[i]->code);
2680+
pj_dalloc(list[i]->name);
2681+
pj_dalloc(list[i]->category);
2682+
pj_dalloc(list[i]->proj_short_name);
2683+
delete list[i];
2684+
}
2685+
delete[] list;
2686+
}
2687+
}
2688+
2689+
// ---------------------------------------------------------------------------
2690+
25882691
/** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS),
25892692
* or the Transformation from the baseCRS to the hubCRS of a BoundCRS
25902693
*
@@ -6749,8 +6852,9 @@ int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx,
67496852
* unit code. or NULL
67506853
* @param out_unit_category Pointer to a string value to store the parameter
67516854
* name. or
6752-
* NULL. This value might be "unknown", "none", "linear", "angular", "scale",
6753-
* "time" or "parametric";
6855+
* NULL. This value might be "unknown", "none", "linear", "linear_per_time",
6856+
* "angular", "angular_per_time", "scale", "scale_per_time", "time",
6857+
* "parametric" or "parametric_per_time"
67546858
* @return TRUE in case of success.
67556859
*/
67566860

@@ -6852,7 +6956,8 @@ int proj_coordoperation_get_param(
68526956
*out_unit_code = unit.code().c_str();
68536957
}
68546958
if (out_unit_category) {
6855-
*out_unit_category = get_unit_category(unit.type());
6959+
*out_unit_category =
6960+
get_unit_category(unit.name(), unit.type());
68566961
}
68576962
}
68586963
}

src/iso19111/factory.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5214,6 +5214,64 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const {
52145214

52155215
// ---------------------------------------------------------------------------
52165216

5217+
//! @cond Doxygen_Suppress
5218+
AuthorityFactory::UnitInfo::UnitInfo()
5219+
: authName{}, code{}, name{}, category{}, convFactor{}, projShortName{},
5220+
deprecated{} {}
5221+
//! @endcond
5222+
5223+
// ---------------------------------------------------------------------------
5224+
5225+
/** \brief Return the list of units.
5226+
* @throw FactoryException
5227+
*
5228+
* @since 7.1
5229+
*/
5230+
std::list<AuthorityFactory::UnitInfo> AuthorityFactory::getUnitList() const {
5231+
std::string sql = "SELECT auth_name, code, name, type, conv_factor, "
5232+
"proj_short_name, deprecated FROM unit_of_measure";
5233+
ListOfParams params;
5234+
if (d->hasAuthorityRestriction()) {
5235+
sql += " WHERE auth_name = ?";
5236+
params.emplace_back(d->authority());
5237+
}
5238+
sql += " ORDER BY auth_name, code";
5239+
5240+
auto sqlRes = d->run(sql, params);
5241+
std::list<AuthorityFactory::UnitInfo> res;
5242+
for (const auto &row : sqlRes) {
5243+
AuthorityFactory::UnitInfo info;
5244+
info.authName = row[0];
5245+
info.code = row[1];
5246+
info.name = row[2];
5247+
const std::string &raw_category(row[3]);
5248+
if (raw_category == "length") {
5249+
info.category = info.name.find(" per ") != std::string::npos
5250+
? "linear_per_time"
5251+
: "linear";
5252+
} else if (raw_category == "angle") {
5253+
info.category = info.name.find(" per ") != std::string::npos
5254+
? "angular_per_time"
5255+
: "angular";
5256+
} else if (raw_category == "scale") {
5257+
info.category =
5258+
info.name.find(" per year") != std::string::npos ||
5259+
info.name.find(" per second") != std::string::npos
5260+
? "scale_per_time"
5261+
: "scale";
5262+
} else {
5263+
info.category = raw_category;
5264+
}
5265+
info.convFactor = row[4].empty() ? 0 : c_locale_stod(row[4]);
5266+
info.projShortName = row[5];
5267+
info.deprecated = row[6] == "1";
5268+
res.emplace_back(info);
5269+
}
5270+
return res;
5271+
}
5272+
5273+
// ---------------------------------------------------------------------------
5274+
52175275
/** \brief Gets the official name from a possibly alias name.
52185276
*
52195277
* @param aliasedName Alias name.

0 commit comments

Comments
 (0)