aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-03-16 17:55:36 +0100
committerGitHub <noreply@github.com>2020-03-16 17:55:36 +0100
commit175cbad0a7ca202cefff33f240100b01752f8f73 (patch)
tree3969a91e8499312c698d928f262d219d91aaea3b /src
parent78eaa34a4816fd9a36e902adae4663f13bc39fab (diff)
parent2247841879faebe007ccade45d04027361d5d26c (diff)
downloadPROJ-175cbad0a7ca202cefff33f240100b01752f8f73.tar.gz
PROJ-175cbad0a7ca202cefff33f240100b01752f8f73.zip
Merge pull request #2065 from rouault/add_proj_get_units_from_database
Add proj_get_units_from_database() (fixes #2004)
Diffstat (limited to 'src')
-rw-r--r--src/apps/cs2cs.cpp17
-rw-r--r--src/apps/geod.cpp17
-rw-r--r--src/apps/geod_set.cpp24
-rw-r--r--src/apps/proj.cpp17
-rw-r--r--src/conversions/unitconvert.cpp4
-rw-r--r--src/init.cpp2
-rw-r--r--src/iso19111/c_api.cpp127
-rw-r--r--src/iso19111/common.cpp3
-rw-r--r--src/iso19111/factory.cpp58
-rw-r--r--src/proj.h62
-rw-r--r--src/proj_internal.h2
-rw-r--r--src/units.cpp6
12 files changed, 298 insertions, 41 deletions
diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp
index 6c85d4aa..affd2bec 100644
--- a/src/apps/cs2cs.cpp
+++ b/src/apps/cs2cs.cpp
@@ -425,11 +425,18 @@ int main(int argc, char **argv) {
(void)printf("%9s %-16s %-16s %s\n", le->id,
le->major, le->ell, le->name);
} else if (arg[1] == 'u') { /* list units */
- const struct PJ_UNITS *lu;
-
- for (lu = proj_list_units(); lu->id; ++lu)
- (void)printf("%12s %-20s %s\n", lu->id,
- lu->to_meter, lu->name);
+ auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr);
+ for( int i = 0; units && units[i]; i++ )
+ {
+ if( units[i]->proj_short_name )
+ {
+ (void)printf("%12s %-20.15g %s\n",
+ units[i]->proj_short_name,
+ units[i]->conv_factor,
+ units[i]->name);
+ }
+ }
+ proj_unit_list_destroy(units);
} else if (arg[1] == 'm') { /* list prime meridians */
const struct PJ_PRIME_MERIDIANS *lpm;
diff --git a/src/apps/geod.cpp b/src/apps/geod.cpp
index b46188d3..919430ca 100644
--- a/src/apps/geod.cpp
+++ b/src/apps/geod.cpp
@@ -185,11 +185,18 @@ noargument: emess(1,"missing argument for -%c",*arg);
(void)printf("%9s %-16s %-16s %s\n",
le->id, le->major, le->ell, le->name);
} else if (arg[1] == 'u') { /* list of units */
- const struct PJ_UNITS *lu;
-
- for (lu = proj_list_units();lu->id ; ++lu)
- (void)printf("%12s %-20s %s\n",
- lu->id, lu->to_meter, lu->name);
+ auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr);
+ for( int i = 0; units && units[i]; i++ )
+ {
+ if( units[i]->proj_short_name )
+ {
+ (void)printf("%12s %-20.15g %s\n",
+ units[i]->proj_short_name,
+ units[i]->conv_factor,
+ units[i]->name);
+ }
+ }
+ proj_unit_list_destroy(units);
} else
emess(1,"invalid list option: l%c",arg[1]);
exit( 0 );
diff --git a/src/apps/geod_set.cpp b/src/apps/geod_set.cpp
index ed7edeb9..603f0d95 100644
--- a/src/apps/geod_set.cpp
+++ b/src/apps/geod_set.cpp
@@ -14,7 +14,6 @@ geod_set(int argc, char **argv) {
paralist *start = nullptr, *curr;
double es;
char *name;
- int i;
/* put arguments into internal linked list */
if (argc <= 0)
@@ -22,7 +21,7 @@ geod_set(int argc, char **argv) {
start = curr = pj_mkparam(argv[0]);
if (!curr)
emess(1, "memory allocation failed");
- for (i = 1; curr != nullptr && i < argc; ++i) {
+ for (int i = 1; curr != nullptr && i < argc; ++i) {
curr->next = pj_mkparam(argv[i]);
if (!curr->next)
emess(1, "memory allocation failed");
@@ -32,13 +31,20 @@ geod_set(int argc, char **argv) {
if (pj_ell_set(pj_get_default_ctx(),start, &geod_a, &es)) emess(1,"ellipse setup failure");
/* set units */
if ((name = pj_param(nullptr,start, "sunits").s) != nullptr) {
- const char *s;
- const struct PJ_UNITS *unit_list = proj_list_units();
- for (i = 0; (s = unit_list[i].id) && strcmp(name, s) ; ++i) ;
- if (!s)
- emess(1,"%s unknown unit conversion id", name);
- to_meter = unit_list[i].factor;
- fr_meter = 1 / to_meter;
+ bool unit_found = false;
+ auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr);
+ for( int i = 0; units && units[i]; i++ )
+ {
+ if( units[i]->proj_short_name &&
+ strcmp(units[i]->proj_short_name, name) == 0 ) {
+ unit_found = true;
+ to_meter = units[i]->conv_factor;
+ fr_meter = 1 / to_meter;
+ }
+ }
+ proj_unit_list_destroy(units);
+ if( !unit_found )
+ emess(1,"%s unknown unit conversion id", name);
} else
to_meter = fr_meter = 1;
geod_f = es/(1 + sqrt(1 - es));
diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp
index 852cea04..0bf98b3a 100644
--- a/src/apps/proj.cpp
+++ b/src/apps/proj.cpp
@@ -380,11 +380,18 @@ int main(int argc, char **argv) {
(void)printf("%9s %-16s %-16s %s\n",
le->id, le->major, le->ell, le->name);
} else if (arg[1] == 'u') { /* list units */
- const struct PJ_UNITS *lu;
-
- for (lu = proj_list_units(); lu->id ; ++lu)
- (void)printf("%12s %-20s %s\n",
- lu->id, lu->to_meter, lu->name);
+ auto units = proj_get_units_from_database(nullptr, nullptr, "linear", false, nullptr);
+ for( int i = 0; units && units[i]; i++ )
+ {
+ if( units[i]->proj_short_name )
+ {
+ (void)printf("%12s %-20.15g %s\n",
+ units[i]->proj_short_name,
+ units[i]->conv_factor,
+ units[i]->name);
+ }
+ }
+ proj_unit_list_destroy(units);
} else
emess(1,"invalid list option: l%c",arg[1]);
exit(0);
diff --git a/src/conversions/unitconvert.cpp b/src/conversions/unitconvert.cpp
index f8439aee..6ce55b02 100644
--- a/src/conversions/unitconvert.cpp
+++ b/src/conversions/unitconvert.cpp
@@ -393,9 +393,7 @@ static double get_unit_conversion_factor(const char* name,
/***********************************************************************/
int i;
const char* s;
- const PJ_UNITS *units;
-
- units = proj_list_units();
+ const PJ_UNITS *units = pj_list_linear_units();
/* Try first with linear units */
for (i = 0; (s = units[i].id) ; ++i) {
diff --git a/src/init.cpp b/src/init.cpp
index a25d1ccd..101fc8ad 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -738,7 +738,7 @@ pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_i
return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO);
/* Set units */
- units = proj_list_units();
+ units = pj_list_linear_units();
s = nullptr;
if ((name = pj_param(ctx, start, "sunits").s) != nullptr) {
for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ;
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index d5f299c2..13152662 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -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:
@@ -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;
@@ -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,
@@ -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;
@@ -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
*
@@ -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.
*/
@@ -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());
}
}
}
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
index f2e4de4c..4606905b 100644
--- a/src/iso19111/common.cpp
+++ b/src/iso19111/common.cpp
@@ -39,6 +39,7 @@
#include "proj/internal/io_internal.hpp"
#include "proj.h"
+#include "proj_internal.h"
#include <cmath> // M_PI
#include <cstdlib>
@@ -312,7 +313,7 @@ bool UnitOfMeasure::operator!=(const UnitOfMeasure &other) PROJ_PURE_DEFN {
//! @cond Doxygen_Suppress
std::string UnitOfMeasure::exportToPROJString() const {
if (type() == Type::LINEAR) {
- auto proj_units = proj_list_units();
+ auto proj_units = pj_list_linear_units();
for (int i = 0; proj_units[i].id != nullptr; i++) {
if (::fabs(proj_units[i].factor - conversionToSI()) <
1e-10 * conversionToSI()) {
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 1c836367..6a707e0d 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -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.
diff --git a/src/proj.h b/src/proj.h
index 90a11739..69aae6d8 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -151,6 +151,24 @@ extern "C" {
#endif
#endif
+#ifdef PROJ_SUPPRESS_DEPRECATION_MESSAGE
+ #define PROJ_DEPRECATED(decl, msg) decl
+#elif defined(__has_extension)
+ #if __has_extension(attribute_deprecated_with_message)
+ #define PROJ_DEPRECATED(decl, msg) decl __attribute__ ((deprecated(msg)))
+ #elif defined(__GNUC__)
+ #define PROJ_DEPRECATED(decl, msg) decl __attribute__ ((deprecated))
+ #else
+ #define PROJ_DEPRECATED(decl, msg) decl
+ #endif
+#elif defined(__GNUC__)
+ #define PROJ_DEPRECATED(decl, msg) decl __attribute__ ((deprecated))
+#elif defined(_MSVC_VER)
+ #define PROJ_DEPRECATED(decl, msg) __declspec(deprecated(msg)) decl
+#else
+ #define PROJ_DEPRECATED(decl, msg) decl
+#endif
+
/* The version numbers should be updated with every release! **/
#define PROJ_VERSION_MAJOR 7
#define PROJ_VERSION_MINOR 1
@@ -608,7 +626,7 @@ PJ_INIT_INFO PROJ_DLL proj_init_info(const char *initname);
/* Get lists of operations, ellipsoids, units and prime meridians. */
const PJ_OPERATIONS PROJ_DLL *proj_list_operations(void);
const PJ_ELLPS PROJ_DLL *proj_list_ellps(void);
-const PJ_UNITS PROJ_DLL *proj_list_units(void);
+PROJ_DEPRECATED(const PJ_UNITS PROJ_DLL *proj_list_units(void), "Deprecated by proj_get_units_from_database");
const PJ_UNITS PROJ_DLL *proj_list_angular_units(void);
const PJ_PRIME_MERIDIANS PROJ_DLL *proj_list_prime_meridians(void);
@@ -912,6 +930,39 @@ typedef struct
int allow_deprecated;
} PROJ_CRS_LIST_PARAMETERS;
+/** \brief Structure given description of a unit.
+ *
+ * This structure may grow over time, and should not be directly allocated by
+ * client code.
+ * @since 7.1
+ */
+typedef struct
+{
+ /** Authority name. */
+ char* auth_name;
+
+ /** Object code. */
+ char* code;
+
+ /** Object name. For example "metre", "US survey foot", etc. */
+ char* name;
+
+ /** Category of the unit: one of "linear", "linear_per_time", "angular",
+ * "angular_per_time", "scale", "scale_per_time" or "time" */
+ char* category;
+
+ /** Conversion factor to apply to transform from that unit to the
+ * corresponding SI unit (metre for "linear", radian for "angular", etc.).
+ * It might be 0 in some cases to indicate no known conversion factor. */
+ double conv_factor;
+
+ /** PROJ short name, like "m", "ft", "us-ft", etc... Might be NULL */
+ char* proj_short_name;
+
+ /** Whether the object is deprecated */
+ int deprecated;
+} PROJ_UNIT_INFO;
+
/**@}*/
@@ -1077,6 +1128,15 @@ PROJ_CRS_INFO PROJ_DLL **proj_get_crs_info_list_from_database(
void PROJ_DLL proj_crs_info_list_destroy(PROJ_CRS_INFO** list);
+PROJ_UNIT_INFO PROJ_DLL **proj_get_units_from_database(
+ PJ_CONTEXT *ctx,
+ const char *auth_name,
+ const char *category,
+ int allow_deprecated,
+ int *out_result_count);
+
+void PROJ_DLL proj_unit_list_destroy(PROJ_UNIT_INFO** list);
+
/* ------------------------------------------------------------------------- */
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 8f73200d..78aff49f 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -873,6 +873,8 @@ std::string PROJ_DLL pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, boo
void PROJ_DLL pj_context_set_user_writable_directory(PJ_CONTEXT* ctx, const std::string& path);
std::string PROJ_DLL pj_get_relative_share_proj(PJ_CONTEXT *ctx);
+const PJ_UNITS *pj_list_linear_units();
+
/* classic public API */
#include "proj_api.h"
diff --git a/src/units.cpp b/src/units.cpp
index 34a71db1..36f2d4c7 100644
--- a/src/units.cpp
+++ b/src/units.cpp
@@ -36,6 +36,12 @@ pj_units[] = {
{nullptr, nullptr, nullptr, 0.0}
};
+// For internal use
+const PJ_UNITS *pj_list_linear_units()
+{
+ return pj_units;
+}
+
const PJ_UNITS *proj_list_units()
{
return pj_units;