aboutsummaryrefslogtreecommitdiff
path: root/src/internal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal.cpp')
-rw-r--r--src/internal.cpp639
1 files changed, 355 insertions, 284 deletions
diff --git a/src/internal.cpp b/src/internal.cpp
index c43605d1..3f3d191e 100644
--- a/src/internal.cpp
+++ b/src/internal.cpp
@@ -1,11 +1,14 @@
/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: This is primarily material originating from pj_obs_api.c
+ * (now proj_4D_api.c), that does not fit into the API
+ * category. Hence this pile of tubings and fittings for
+ * PROJ.4 internal plumbing.
*
- * Project: PROJ
- * Purpose: ISO19111:2018 implementation
- * Author: Even Rouault <even dot rouault at spatialys dot com>
+ * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05
*
******************************************************************************
- * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
+ * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -24,351 +27,419 @@
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
- ****************************************************************************/
+ *****************************************************************************/
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
-#ifndef FROM_PROJ_CPP
-#define FROM_PROJ_CPP
-#endif
+#include "geodesic.h"
+#include "proj_internal.h"
+#include "projects.h"
-#include "proj/internal/internal.hpp"
-#include <cstdint>
-#include <cstring>
-#ifdef _MSC_VER
-#include <string.h>
-#else
-#include <strings.h>
-#endif
-#include <exception>
-#include <iomanip> // std::setprecision
-#include <locale>
-#include <sstream> // std::istringstream and std::ostringstream
-#include <string>
-
-#include "sqlite3.h"
-
-NS_PROJ_START
-
-namespace internal {
-
-// ---------------------------------------------------------------------------
-
-/**
- * Replace all occurrences of before with after.
- */
-std::string replaceAll(const std::string &str, const std::string &before,
- const std::string &after) {
- std::string ret(str);
- const size_t nBeforeSize = before.size();
- const size_t nAfterSize = after.size();
- if (nBeforeSize) {
- size_t nStartPos = 0;
- while ((nStartPos = ret.find(before, nStartPos)) != std::string::npos) {
- ret.replace(nStartPos, nBeforeSize, after);
- nStartPos += nAfterSize;
- }
- }
- return ret;
+enum pj_io_units pj_left (PJ *P) {
+ enum pj_io_units u = P->inverted? P->right: P->left;
+ if (u==PJ_IO_UNITS_CLASSIC)
+ return PJ_IO_UNITS_PROJECTED;
+ return u;
}
-// ---------------------------------------------------------------------------
-
-inline static bool EQUALN(const char *a, const char *b, size_t size) {
-#ifdef _MSC_VER
- return _strnicmp(a, b, size) == 0;
-#else
- return strncasecmp(a, b, size) == 0;
-#endif
+enum pj_io_units pj_right (PJ *P) {
+ enum pj_io_units u = P->inverted? P->left: P->right;
+ if (u==PJ_IO_UNITS_CLASSIC)
+ return PJ_IO_UNITS_PROJECTED;
+ return u;
}
-/**
- * Case-insensitive equality test
- */
-bool ci_equal(const std::string &a, const std::string &b) noexcept {
- const auto size = a.size();
- if (size != b.size()) {
- return false;
- }
- return EQUALN(a.c_str(), b.c_str(), size);
+
+/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */
+PJ_COORD proj_coord_error (void) {
+ PJ_COORD c;
+ c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
+ return c;
}
-bool ci_equal(const std::string &a, const char *b) noexcept {
- const auto size = a.size();
- if (size != strlen(b)) {
- return false;
+
+
+/**************************************************************************************/
+PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
+/***************************************************************************************
+Behave mostly as proj_trans, but attempt to use 2D interfaces only.
+Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement
+chained calls starting out with a call to its 2D interface.
+***************************************************************************************/
+ if (nullptr==P)
+ return coo;
+ if (P->inverted)
+ direction = static_cast<PJ_DIRECTION>(-direction);
+ switch (direction) {
+ case PJ_FWD:
+ coo.xy = pj_fwd (coo.lp, P);
+ return coo;
+ case PJ_INV:
+ coo.lp = pj_inv (coo.xy, P);
+ return coo;
+ case PJ_IDENT:
+ return coo;
+ default:
+ break;
}
- return EQUALN(a.c_str(), b, size);
+ proj_errno_set (P, EINVAL);
+ return proj_coord_error ();
}
-bool ci_equal(const char *a, const char *b) noexcept {
- const auto size = strlen(a);
- if (size != strlen(b)) {
- return false;
+
+/**************************************************************************************/
+PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
+/***************************************************************************************
+Companion to pj_approx_2D_trans.
+
+Behave mostly as proj_trans, but attempt to use 3D interfaces only.
+Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement
+chained calls starting out with a call to its 3D interface.
+***************************************************************************************/
+ if (nullptr==P)
+ return coo;
+ if (P->inverted)
+ direction = static_cast<PJ_DIRECTION>(-direction);
+ switch (direction) {
+ case PJ_FWD:
+ coo.xyz = pj_fwd3d (coo.lpz, P);
+ return coo;
+ case PJ_INV:
+ coo.lpz = pj_inv3d (coo.xyz, P);
+ return coo;
+ case PJ_IDENT:
+ return coo;
+ default:
+ break;
}
- return EQUALN(a, b, size);
+ proj_errno_set (P, EINVAL);
+ return proj_coord_error ();
}
-// ---------------------------------------------------------------------------
-
-bool ci_less(const std::string &a, const std::string &b) noexcept {
-#ifdef _MSC_VER
- return _stricmp(a.c_str(), b.c_str()) < 0;
-#else
- return strcasecmp(a.c_str(), b.c_str()) < 0;
-#endif
+/**************************************************************************************/
+int pj_has_inverse(PJ *P) {
+/***************************************************************************************
+Check if a a PJ has an inverse.
+***************************************************************************************/
+ return ( (P->inverted && (P->fwd || P->fwd3d || P->fwd4d) ) ||
+ ( P->inv || P->inv3d || P->inv4d) );
}
-// ---------------------------------------------------------------------------
-/**
- * Convert to lower case.
- */
+/* Move P to a new context - or to the default context if 0 is specified */
+void proj_context_set (PJ *P, PJ_CONTEXT *ctx) {
+ if (nullptr==ctx)
+ ctx = pj_get_default_ctx ();
+ pj_set_ctx (P, ctx);
+}
-std::string tolower(const std::string &str)
-{
- std::string ret(str);
- for (size_t i = 0; i < ret.size(); i++)
- ret[i] = static_cast<char>(::tolower(ret[i]));
- return ret;
+void proj_context_inherit (PJ *parent, PJ *child) {
+ if (nullptr==parent)
+ pj_set_ctx (child, pj_get_default_ctx());
+ else
+ pj_set_ctx (child, pj_get_ctx(parent));
}
-// ---------------------------------------------------------------------------
-/**
- * Convert to upper case.
- */
-std::string toupper(const std::string &str)
+/*****************************************************************************/
+char *pj_chomp (char *c) {
+/******************************************************************************
+Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
+considered whitespace.
+******************************************************************************/
+ size_t i, n;
+ char *comment;
+ char *start = c;
+
+ if (nullptr==c)
+ return nullptr;
+
+ comment = strchr (c, '#');
+ if (comment)
+ *comment = 0;
+
+ n = strlen (c);
+ if (0==n)
+ return c;
+
+ /* Eliminate postfix whitespace */
+ for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--)
+ c[i] = 0;
+
+ /* Find start of non-whitespace */
+ while (0 != *start && (';'==*start || isspace (*start)))
+ start++;
+
+ n = strlen (start);
+ if (0==n) {
+ c[0] = 0;
+ return c;
+ }
-{
- std::string ret(str);
- for (size_t i = 0; i < ret.size(); i++)
- ret[i] = static_cast<char>(::toupper(ret[i]));
- return ret;
+ memmove (c, start, n + 1);
+ return c;
}
-// ---------------------------------------------------------------------------
-/** Strip leading and trailing double quote characters */
-std::string stripQuotes(const std::string &str) {
- if (str.size() >= 2 && str[0] == '"' && str.back() == '"') {
- return str.substr(1, str.size() - 2);
- }
- return str;
-}
-// ---------------------------------------------------------------------------
-
-size_t ci_find(const std::string &str, const char *needle) noexcept {
- const size_t needleSize = strlen(needle);
- for (size_t i = 0; i + needleSize <= str.size(); i++) {
- if (EQUALN(str.c_str() + i, needle, needleSize)) {
- return i;
+/*****************************************************************************/
+char *pj_shrink (char *c) {
+/******************************************************************************
+Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
+consuming their surrounding whitespace.
+******************************************************************************/
+ size_t i, j, n;
+
+ /* Flag showing that a whitespace (ws) has been written after last non-ws */
+ size_t ws;
+
+ if (nullptr==c)
+ return nullptr;
+
+ pj_chomp (c);
+ n = strlen (c);
+
+ /* First collapse repeated whitespace (including +/;) */
+ i = 0;
+ ws = 0;
+ for (j = 0; j < n; j++) {
+
+ /* Eliminate prefix '+', only if preceded by whitespace */
+ /* (i.e. keep it in 1.23e+08) */
+ if ((i > 0) && ('+'==c[j]) && ws)
+ c[j] = ' ';
+ if ((i==0) && ('+'==c[j]))
+ c[j] = ' ';
+
+ if (isspace (c[j]) || ';'==c[j]) {
+ if (0==ws && (i > 0))
+ c[i++] = ' ';
+ ws = 1;
+ continue;
}
- }
- return std::string::npos;
-}
-
-// ---------------------------------------------------------------------------
-
-size_t ci_find(const std::string &str, const std::string &needle,
- size_t startPos) noexcept {
- const size_t needleSize = needle.size();
- for (size_t i = startPos; i + needleSize <= str.size(); i++) {
- if (EQUALN(str.c_str() + i, needle.c_str(), needleSize)) {
- return i;
+ else {
+ ws = 0;
+ c[i++] = c[j];
}
}
- return std::string::npos;
-}
-
-// ---------------------------------------------------------------------------
+ c[i] = 0;
+ n = strlen(c);
+
+ /* Then make ',' and '=' greedy */
+ i = 0;
+ for (j = 0; j < n; j++) {
+ if (i==0) {
+ c[i++] = c[j];
+ continue;
+ }
-/*
-bool starts_with(const std::string &str, const std::string &prefix) noexcept {
- if (str.size() < prefix.size()) {
- return false;
- }
- return std::memcmp(str.c_str(), prefix.c_str(), prefix.size()) == 0;
-}
-*/
+ /* Skip space before '='/',' */
+ if ('='==c[j] || ','==c[j]) {
+ if (c[i - 1]==' ')
+ c[i - 1] = c[j];
+ else
+ c[i++] = c[j];
+ continue;
+ }
-// ---------------------------------------------------------------------------
+ if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) )
+ continue;
-/*
-bool starts_with(const std::string &str, const char *prefix) noexcept {
- const size_t prefixSize = std::strlen(prefix);
- if (str.size() < prefixSize) {
- return false;
+ c[i++] = c[j];
}
- return std::memcmp(str.c_str(), prefix, prefixSize) == 0;
+ c[i] = 0;
+ return c;
}
-*/
-// ---------------------------------------------------------------------------
-bool ci_starts_with(const char *str, const char *prefix) noexcept {
- const auto str_size = strlen(str);
- const auto prefix_size = strlen(prefix);
- if (str_size < prefix_size) {
- return false;
- }
- return EQUALN(str, prefix, prefix_size);
-}
-// ---------------------------------------------------------------------------
-
-bool ci_starts_with(const std::string &str,
- const std::string &prefix) noexcept {
- if (str.size() < prefix.size()) {
- return false;
+/*****************************************************************************/
+size_t pj_trim_argc (char *args) {
+/******************************************************************************
+Trim all unnecessary whitespace (and non-essential syntactic tokens) from the
+argument string, args, and count its number of elements.
+******************************************************************************/
+ size_t i, m, n;
+ pj_shrink (args);
+ n = strlen (args);
+ if (n==0)
+ return 0;
+ for (i = m = 0; i < n; i++) {
+ if (' '==args[i]) {
+ args[i] = 0;
+ m++;
+ }
}
- return EQUALN(str.c_str(), prefix.c_str(), prefix.size());
+ return m + 1;
}
-// ---------------------------------------------------------------------------
-bool ends_with(const std::string &str, const std::string &suffix) noexcept {
- if (str.size() < suffix.size()) {
- return false;
- }
- return std::memcmp(str.c_str() + str.size() - suffix.size(), suffix.c_str(),
- suffix.size()) == 0;
-}
-// ---------------------------------------------------------------------------
-
-double c_locale_stod(const std::string &s) {
-
- const auto s_size = s.size();
- // Fast path
- if (s_size > 0 && s_size < 15) {
- std::int64_t acc = 0;
- std::int64_t div = 1;
- bool afterDot = false;
- size_t i = 0;
- if (s[0] == '-') {
- ++i;
- div = -1;
- } else if (s[0] == '+') {
- ++i;
- }
- for (; i < s_size; ++i) {
- const auto ch = s[i];
- if (ch >= '0' && ch <= '9') {
- acc = acc * 10 + ch - '0';
- if (afterDot) {
- div *= 10;
- }
- } else if (ch == '.') {
- afterDot = true;
- } else {
- div = 0;
- }
- }
- if (div) {
- return static_cast<double>(acc) / div;
+/*****************************************************************************/
+char **pj_trim_argv (size_t argc, char *args) {
+/******************************************************************************
+Create an argv-style array from elements placed in the argument string, args.
+
+args is a trimmed string as returned by pj_trim_argc(), and argc is the number
+of trimmed strings found (i.e. the return value of pj_trim_args()). Hence,
+ int argc = pj_trim_argc (args);
+ char **argv = pj_trim_argv (argc, args);
+will produce a classic style (argc, argv) pair from a string of whitespace
+separated args. No new memory is allocated for storing the individual args
+(they stay in the args string), but for the pointers to the args a new array
+is allocated and returned.
+
+It is the duty of the caller to free this array.
+******************************************************************************/
+ size_t i, j;
+ char **argv;
+
+ if (nullptr==args)
+ return nullptr;
+ if (0==argc)
+ return nullptr;
+
+
+ /* turn the input string into an array of strings */
+ argv = (char **) calloc (argc, sizeof (char *));
+ if (nullptr==argv)
+ return nullptr;
+ argv[0] = args;
+ j = 1;
+ for (i = 0; ; i++) {
+ if (0==args[i]) {
+ argv[j++] = args + (i + 1);
}
+ if (j==argc)
+ break;
}
-
- std::istringstream iss(s);
- iss.imbue(std::locale::classic());
- double d;
- iss >> d;
- if (!iss.eof() || iss.fail()) {
- throw std::invalid_argument("non double value");
- }
- return d;
+ return argv;
}
-// ---------------------------------------------------------------------------
-std::vector<std::string> split(const std::string &str, char separator) {
- std::vector<std::string> res;
- size_t lastPos = 0;
- size_t newPos = 0;
- while ((newPos = str.find(separator, lastPos)) != std::string::npos) {
- res.push_back(str.substr(lastPos, newPos - lastPos));
- lastPos = newPos + 1;
+
+/*****************************************************************************/
+char *pj_make_args (size_t argc, char **argv) {
+/******************************************************************************
+pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It
+converts free format command line input to something proj_create can consume.
+
+Allocates, and returns, an array of char, large enough to hold a whitespace
+separated copy of the args in argv. It is the duty of the caller to free this
+array.
+******************************************************************************/
+ size_t i, n;
+ char *p;
+
+ for (i = n = 0; i < argc; i++)
+ n += strlen (argv[i]);
+
+ p = static_cast<char*>(pj_calloc (n + argc + 1, sizeof (char)));
+ if (nullptr==p)
+ return nullptr;
+ if (0==argc)
+ return p;
+
+ for (i = 0; i < argc; i++) {
+ strcat (p, argv[i]);
+ strcat (p, " ");
}
- res.push_back(str.substr(lastPos));
- return res;
+ return pj_shrink (p);
}
-// ---------------------------------------------------------------------------
-#ifdef _WIN32
-// For some reason, sqlite3_snprintf() in the sqlite3 builds used on AppVeyor
-// doesn't round identically to the Unix builds, and thus breaks a number of
-// unit test. So to avoid this, use the stdlib formatting
-
-std::string toString(int val) {
- std::ostringstream buffer;
- buffer.imbue(std::locale::classic());
- buffer << val;
- return buffer.str();
+/*****************************************************************************/
+void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
+/******************************************************************************
+Raise an error directly on a context, without going through a PJ belonging
+to that context.
+******************************************************************************/
+ if (nullptr==ctx)
+ ctx = pj_get_default_ctx();
+ pj_ctx_set_errno (ctx, err);
}
-std::string toString(double val, int precision) {
- std::ostringstream buffer;
- buffer.imbue(std::locale::classic());
- buffer << std::setprecision(precision);
- buffer << val;
- auto str = buffer.str();
- if (precision == 15 && str.find("9999999999") != std::string::npos) {
- buffer.str("");
- buffer.clear();
- buffer << std::setprecision(14);
- buffer << val;
- return buffer.str();
- }
- return str;
+/* logging */
+
+/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
+void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
+
+
+/***************************************************************************************/
+PJ_LOG_LEVEL proj_log_level (PJ_CONTEXT *ctx, PJ_LOG_LEVEL log_level) {
+/****************************************************************************************
+ Set logging level 0-3. Higher number means more debug info. 0 turns it off
+****************************************************************************************/
+ PJ_LOG_LEVEL previous;
+ if (nullptr==ctx)
+ ctx = pj_get_default_ctx();
+ if (nullptr==ctx)
+ return PJ_LOG_TELL;
+ previous = static_cast<PJ_LOG_LEVEL>(abs (ctx->debug_level));
+ if (PJ_LOG_TELL==log_level)
+ return previous;
+ ctx->debug_level = log_level;
+ return previous;
}
-#else
-std::string toString(int val) {
- // use sqlite3 API that is slighly faster than std::ostringstream
- // with forcing the C locale. sqlite3_snprintf() emulates a C locale.
- constexpr int BUF_SIZE = 16;
- char szBuffer[BUF_SIZE];
- sqlite3_snprintf(BUF_SIZE, szBuffer, "%d", val);
- return szBuffer;
-}
-
-std::string toString(double val, int precision) {
- // use sqlite3 API that is slighly faster than std::ostringstream
- // with forcing the C locale. sqlite3_snprintf() emulates a C locale.
- constexpr int BUF_SIZE = 32;
- char szBuffer[BUF_SIZE];
- sqlite3_snprintf(BUF_SIZE, szBuffer, "%.*g", precision, val);
- if (precision == 15 && strstr(szBuffer, "9999999999")) {
- sqlite3_snprintf(BUF_SIZE, szBuffer, "%.14g", val);
- }
- return szBuffer;
+/*****************************************************************************/
+void proj_log_error (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting the most severe events.
+******************************************************************************/
+ va_list args;
+ va_start( args, fmt );
+ pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args);
+ va_end( args );
}
-#endif
-// ---------------------------------------------------------------------------
-
-std::string concat(const char *a, const std::string &b) {
- std::string res(a);
- res += b;
- return res;
+/*****************************************************************************/
+void proj_log_debug (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting debugging information.
+******************************************************************************/
+ va_list args;
+ va_start( args, fmt );
+ pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args);
+ va_end( args );
}
-std::string concat(const char *a, const std::string &b, const char *c) {
- std::string res(a);
- res += b;
- res += c;
- return res;
-}
-// ---------------------------------------------------------------------------
+/*****************************************************************************/
+void proj_log_trace (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting embarrasingly detailed debugging information.
+******************************************************************************/
+ va_list args;
+ va_start( args, fmt );
+ pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args);
+ va_end( args );
+}
-} // namespace internal
-NS_PROJ_END
+/*****************************************************************************/
+void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION logf) {
+/******************************************************************************
+ Put a new logging function into P's context. The opaque object app_data is
+ passed as first arg at each call to the logger
+******************************************************************************/
+ if (nullptr==ctx)
+ pj_get_default_ctx ();
+ if (nullptr==ctx)
+ return;
+ ctx->app_data = app_data;
+ if (nullptr!=logf)
+ ctx->logger = logf;
+}