diff options
Diffstat (limited to 'src/pj_obs_api.c')
| -rw-r--r-- | src/pj_obs_api.c | 485 |
1 files changed, 325 insertions, 160 deletions
diff --git a/src/pj_obs_api.c b/src/pj_obs_api.c index 37e55bc3..d8395b0f 100644 --- a/src/pj_obs_api.c +++ b/src/pj_obs_api.c @@ -34,118 +34,45 @@ *****************************************************************************/ #define PJ_OBS_API_C #include <proj.h> -#include <projects.h> +#include "proj_internal.h" +#include "projects.h" #include <geodesic.h> -#include <float.h> -#include <math.h> +#include <stddef.h> +#include <errno.h> -/* Used for zero-initializing new objects */ -const PJ_COORD pj_coo_null = {{0, 0, 0, 0}}; -const PJ_OBS pj_obs_null = { - {{0, 0, 0, 0}}, - {{0, 0, 0}}, - 0, 0 -}; -/* Magic object signaling proj system shutdown mode to routines taking a PJ * arg */ -const PJ *pj_shutdown = (PJ *) &pj_shutdown; +PJ_COORD proj_coord (double x, double y, double z, double t) { + PJ_COORD res; + res.v[0] = x; + res.v[1] = y; + res.v[2] = z; + res.v[3] = t; + return res; +} /* Geodesic distance between two points with angular 2D coordinates */ -double pj_lp_dist (PJ *P, LP a, LP b) { +double proj_lp_dist (PJ *P, LP a, LP b) { double s12, azi1, azi2; /* Note: the geodesic code takes arguments in degrees */ - geod_inverse (P->geod, TODEG(a.phi), TODEG(a.lam), TODEG(b.phi), TODEG(b.lam), &s12, &azi1, &azi2); + geod_inverse (P->geod, PJ_TODEG(a.phi), PJ_TODEG(a.lam), PJ_TODEG(b.phi), PJ_TODEG(b.lam), &s12, &azi1, &azi2); return s12; } /* Euclidean distance between two points with linear 2D coordinates */ -double pj_xy_dist (XY a, XY b) { +double proj_xy_dist (XY a, XY b) { return hypot (a.x - b.x, a.y - b.y); } /* Euclidean distance between two points with linear 3D coordinates */ -double pj_xyz_dist (XYZ a, XYZ b) { +double proj_xyz_dist (XYZ a, XYZ b) { return hypot (hypot (a.x - b.x, a.y - b.y), a.z - b.z); } -/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */ -static PJ_COORD pj_coo_error (void) { - PJ_COORD c; - c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL; - return c; -} - -static PJ_OBS pj_obs_error (void) { - PJ_OBS obs; - obs.coo = pj_coo_error (); - obs.anc.v[0] = obs.anc.v[1] = obs.anc.v[2] = HUGE_VAL; - obs.id = obs.flags = 0; - return obs; -} - - - -static PJ_OBS pj_fwdobs (PJ_OBS obs, PJ *P) { - if (0!=P->fwdobs) { - obs = P->fwdobs (obs, P); - return obs; - } - if (0!=P->fwd3d) { - obs.coo.xyz = pj_fwd3d (obs.coo.lpz, P); - return obs; - } - if (0!=P->fwd) { - obs.coo.xy = pj_fwd (obs.coo.lp, P); - return obs; - } - pj_err_level (P, EINVAL); - return pj_obs_error (); -} - - -static PJ_OBS pj_invobs (PJ_OBS obs, PJ *P) { - if (0!=P->invobs) { - obs = P->invobs (obs, P); - return obs; - } - if (0!=P->inv3d) { - obs.coo.lpz = pj_inv3d (obs.coo.xyz, P); - return obs; - } - if (0!=P->inv) { - obs.coo.lp = pj_inv (obs.coo.xy, P); - return obs; - } - pj_err_level (P, EINVAL); - return pj_obs_error (); -} - - -/* Apply the transformation P to the observation obs */ -PJ_OBS pj_trans (PJ *P, enum pj_direction direction, PJ_OBS obs) { - if (0==P) - return obs; - - switch (direction) { - case PJ_FWD: - return pj_fwdobs (obs, P); - case PJ_INV: - return pj_invobs (obs, P); - case PJ_IDENT: - return obs; - default: - break; - } - - pj_err_level (P, EINVAL); - return pj_obs_error (); -} - /* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ -double pj_roundtrip (PJ *P, enum pj_direction direction, int n, PJ_OBS obs) { +double proj_roundtrip (PJ *P, enum proj_direction direction, int n, PJ_OBS obs) { int i; PJ_OBS o, u; @@ -153,7 +80,7 @@ double pj_roundtrip (PJ *P, enum pj_direction direction, int n, PJ_OBS obs) { return HUGE_VAL; if (n < 1) { - pj_err_level (P, EINVAL); + proj_errno_set (P, EINVAL); return HUGE_VAL; } @@ -170,116 +97,354 @@ double pj_roundtrip (PJ *P, enum pj_direction direction, int n, PJ_OBS obs) { o = pj_fwdobs (u, P); break; default: - pj_err_level (P, EINVAL); + proj_errno_set (P, EINVAL); return HUGE_VAL; } } - return pj_xyz_dist (o.coo.xyz, obs.coo.xyz); + return proj_xyz_dist (o.coo.xyz, obs.coo.xyz); } -PJ *pj_create (const char *definition) { - return pj_init_plus (definition); -} +/* Apply the transformation P to the coordinate coo */ +PJ_OBS proj_trans_obs (PJ *P, enum proj_direction direction, PJ_OBS obs) { + if (0==P) + return obs; + switch (direction) { + case PJ_FWD: + return pj_fwdobs (obs, P); + case PJ_INV: + return pj_invobs (obs, P); + case PJ_IDENT: + return obs; + default: + break; + } -PJ *pj_create_argv (int argc, char **argv) { - return pj_init (argc, argv); + proj_errno_set (P, EINVAL); + return proj_obs_error (); } -/* Below: Minimum viable support for contexts. The first four functions */ -/* relate to error reporting, debugging, and logging, hence being generically */ -/* useful. The remaining is a compact implementation of the more low level */ -/* proj_api.h thread contexts, which may or may not be useful */ -/* Set error level 0-3, or query current level */ -int pj_err_level (PJ *P, int err_level) { - int previous; - PJ_CONTEXT *ctx; +/* Apply the transformation P to the coordinate coo */ +PJ_COORD proj_trans_coord (PJ *P, enum proj_direction direction, PJ_COORD coo) { if (0==P) - ctx = pj_get_default_ctx(); - else - ctx = pj_get_ctx (P); - previous = pj_ctx_get_errno (ctx); - if (PJ_ERR_TELL==err_level) - return previous; - pj_ctx_set_errno (ctx, err_level); - return previous; -} + return coo; + switch (direction) { + case PJ_FWD: + return pj_fwdcoord (coo, P); + case PJ_INV: + return pj_invcoord (coo, P); + case PJ_IDENT: + return coo; + default: + break; + } -/* Set logging level 0-3. Higher number means more debug info. 0 turns it off */ -enum pj_log_level pj_log_level (PJ *P, enum pj_log_level log_level) { - enum pj_log_level previous; - PJ_CONTEXT *ctx; - if (0==P) - ctx = pj_get_default_ctx(); - else - ctx = pj_get_ctx (P); - previous = ctx->debug_level; - if (PJ_LOG_TELL==log_level) - return previous; - ctx->debug_level = log_level; - return previous; + proj_errno_set (P, EINVAL); + return proj_coord_error (); } -/* 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 */ -void pj_log_func (PJ *P, void *app_data, void (*log)(void *, int, const char *)) { - PJ_CONTEXT *ctx = pj_get_ctx (P); - ctx->app_data = app_data; - if (0!=log) - ctx->logger = log; -} - -/* Move P to a new context - initially a copy of the default context */ -int pj_context_renew (PJ *P) { - PJ_CONTEXT *ctx = pj_ctx_alloc (); - if (0==ctx) { - pj_err_level (P, ENOMEM); - return 1; +/*************************************************************************************/ +size_t proj_transform ( + PJ *P, + enum proj_direction direction, + double *x, size_t sx, size_t nx, + double *y, size_t sy, size_t ny, + double *z, size_t sz, size_t nz, + double *t, size_t st, size_t nt +) { +/************************************************************************************** + + Transform a series of coordinates, where the individual coordinate dimension + may be represented by an array that is either + + 1. fully populated + 2. a null pointer and/or a length of zero, which will be treated as a + fully populated array of zeroes + 3. of length one, i.e. a constant, which will be treated as a fully + populated array of that constant value + + The strides, sx, sy, sz, st, represent the step length, in bytes, between + consecutive elements of the corresponding array. This makes it possible for + proj_transform to handle transformation of a large class of application + specific data structures, without necessarily understanding the data structure + format, as in: + + typedef struct {double x, y; int quality_level; char surveyor_name[134];} XYQS; + XYQS survey[345]; + double height = 23.45; + PJ *P = {...}; + size_t stride = sizeof (XYQS); + ... + proj_transform ( + P, PJ_INV, sizeof(XYQS), + &(survey[0].x), stride, 345, (* We have 345 eastings *) + &(survey[0].y), stride, 345, (* ...and 345 northings. *) + &height, 1, (* The height is the constant 23.45 m *) + 0, 0 (* and the time is the constant 0.00 s *) + ); + + This is similar to the inner workings of the pj_transform function, but the + stride functionality has been generalized to work for any size of basic unit, + not just a fixed number of doubles. + + In most cases, the stride will be identical for x, y,z, and t, since they will + typically be either individual arrays (stride = sizeof(double)), or strided + views into an array of application specific data structures (stride = sizeof (...)). + + But in order to support cases where x, y, z, and t come from heterogeneous + sources, individual strides, sx, sy, sz, st, are used. + + Caveat: Since proj_transform does its work *in place*, this means that even the + supposedly constants (i.e. length 1 arrays) will return from the call in altered + state. Hence, remember to reinitialize between repeated calls. + + Return value: Number of transformations completed. + +**************************************************************************************/ + PJ_COORD coord = proj_coord_null; + size_t i, nmin; + double null_broadcast = 0; + if (0==P) + return 0; + + /* ignore lengths of null arrays */ + if (0==x) nx = 0; + if (0==y) ny = 0; + if (0==z) nz = 0; + if (0==t) nt = 0; + + /* and make the nullities point to some real world memory for broadcasting nulls */ + if (0==nx) x = &null_broadcast; + if (0==ny) y = &null_broadcast; + if (0==nz) z = &null_broadcast; + if (0==nt) t = &null_broadcast; + + /* nothing to do? */ + if (0==nx+ny+nz+nt) + return 0; + + /* arrays of length 1 are constants, which we broadcast along the longer arrays */ + /* so we need to find the length of the shortest non-unity array to figure out */ + /* how many coordinate pairs we must transform */ + nmin = (nx > 1)? nx: (ny > 1)? ny: (nz > 1)? nz: (nt > 1)? nt: 1; + if ((nx > 1) && (nx < nmin)) nmin = nx; + if ((ny > 1) && (ny < nmin)) nmin = ny; + if ((nz > 1) && (nz < nmin)) nmin = nz; + if ((nt > 1) && (nt < nmin)) nmin = nt; + + /* Check validity of direction flag */ + switch (direction) { + case PJ_FWD: + case PJ_INV: + break; + case PJ_IDENT: + return nmin; + default: + proj_errno_set (P, EINVAL); + return 0; } - pj_set_ctx (P, ctx); - return 0; + /* Arrays of length==0 are broadcast as the constant 0 */ + /* Arrays of length==1 are broadcast as their single value */ + /* Arrays of length >1 are iterated over (for the first nmin values) */ + /* The slightly convolved incremental indexing is used due */ + /* to the stride, which may be any size supported by the platform */ + for (i = 0; i < nmin; i++) { + coord.xyzt.x = *x; + coord.xyzt.y = *y; + coord.xyzt.z = *z; + coord.xyzt.t = *t; + + if (PJ_FWD==direction) + coord = pj_fwdcoord (coord, P); + else + coord = pj_invcoord (coord, P); + + /* in all full length cases, we overwrite the input with the output */ + if (nx > 1) { + *x = coord.xyzt.x; + x = (double *) ( ((char *) x) + sx); + } + if (ny > 1) { + *y = coord.xyzt.y; + y = (double *) ( ((char *) y) + sy); + } + if (nz > 1) { + *z = coord.xyzt.z; + z = (double *) ( ((char *) z) + sz); + } + if (nt > 1) { + *t = coord.xyzt.t; + t = (double *) ( ((char *) t) + st); + } + } + /* Last time around, we update the length 1 cases with their transformed alter egos */ + /* ... or would we rather not? Then what about the nmin==1 case? */ + /* perhaps signalling the non-array case by setting all strides to 0? */ + if (nx==1) + *x = coord.xyzt.x; + if (ny==1) + *y = coord.xyzt.y; + if (nz==1) + *z = coord.xyzt.z; + if (nt==1) + *t = coord.xyzt.t; + + return i; } -/* Move daughter to mother's context */ -void pj_context_inherit (PJ *mother, PJ *daughter) { - pj_set_ctx (daughter, pj_get_ctx (mother)); + +PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { + if (0==ctx) + ctx = pj_get_default_ctx (); + return pj_init_plus_ctx (ctx, definition); } +PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv) { + if (0==ctx) + ctx = pj_get_default_ctx (); + return pj_init_ctx (ctx, argc, argv); +} -void pj_context_free (const PJ *P) { - PJ_CONTEXT *ctx; +PJ *proj_destroy (PJ *P) { + pj_free (P); + return 0; +} +/* For now, if PJ itself is clean, we return the thread local error level. */ +/* This may change as OBS_API error reporting matures */ +int proj_errno (PJ *P) { if (0==P) + return pj_ctx_get_errno (pj_get_default_ctx ()); + if (0 != P->last_errno) + return P->last_errno; + return pj_ctx_get_errno (pj_get_ctx (P)); +} + +/*****************************************************************************/ +void proj_errno_set (PJ *P, int err) { +/****************************************************************************** + Sets errno in the PJ, and bubbles it up to the context and pj_errno levels + through the low level pj_ctx interface. +******************************************************************************/ + if (0==P) { + errno = EINVAL; return; + } + + /* Use proj_errno_reset to explicitly clear the error status */ + if (0==err) + return; + + /* set local error level */ + P->last_errno = err; + /* and let it bubble up */ + proj_context_errno_set (pj_get_ctx (P), err); + errno = err; + return; +} + +/*****************************************************************************/ +void proj_errno_restore (PJ *P, int err) { +/****************************************************************************** + Reduce some mental impedance in the canonical reset/restore use case: + Basically, proj_errno_restore() is a synonym for proj_errno_set(), + but the use cases are very different (_set: indicate an error to higher + level user code, _restore: pass previously set error indicators in case + of no errors at this level). + + Hence, although the inner working is identical, we provide both options, + to avoid some rather confusing real world code. + + See usage example under proj_errno_reset () +******************************************************************************/ + proj_errno_set (P, err); +} - /* During shutdown it should be OK to free the default context - but this needs more work */ - if (pj_shutdown==P) { - /* The obvious solution "pj_ctx_free (pj_get_default_ctx ())" fails */ +/*****************************************************************************/ +int proj_errno_reset (PJ *P) { +/****************************************************************************** + Clears errno in the PJ, and bubbles it up to the context and + pj_errno levels through the low level pj_ctx interface. + + Returns the previous value of the errno, for convenient reset/restore + operations: + + void foo (PJ *P) { + int last_errno = proj_errno_reset (P); + + do_something_with_P (P); + + (* failure - keep latest error status *) + if (proj_errno(P)) + return; + (* success - restore previous error status *) + proj_errno_restore (P, last_errno); return; } +******************************************************************************/ + int last_errno; + if (0==P) { + errno = EINVAL; + return EINVAL; + } + last_errno = proj_errno (P); + + /* set local error level */ + P->last_errno = 0; + /* and let it bubble up */ + pj_ctx_set_errno (pj_get_ctx (P), 0); + errno = 0; + return last_errno; +} - ctx = pj_get_ctx ((PJ *) P); - /* Otherwise, trying to free the default context is a no-op */ +/* Create a new context - or provide a pointer to the default context */ +PJ_CONTEXT *proj_context_create (void) { + return pj_ctx_alloc (); +} + + +void proj_context_destroy (PJ_CONTEXT *ctx) { + if (0==ctx) + return; + + /* Trying to free the default context is a no-op (since it is statically allocated) */ if (pj_get_default_ctx ()==ctx) return; - /* The common (?) case: free the context and move the PJ to the default context */ pj_ctx_free (ctx); - pj_set_ctx ((PJ *) P, pj_get_default_ctx ()); } -/* Minimum support for fileapi - which may never have been used... */ -void pj_fileapi_set (PJ *P, void *fileapi) { - PJ_CONTEXT *ctx = pj_get_ctx (P); - ctx->fileapi = (projFileAPI *) fileapi; +/* Build a fully expanded proj_create() compatible representation of P */ +char *proj_definition_retrieve (PJ *P) { + if (0==P) + return 0; + return pj_get_def(P, 0); } + +/* ...and get rid of it safely */ +void *proj_release (void *buffer) { + return pj_dealloc (buffer); +} + + +double proj_torad (double angle_in_degrees) { return PJ_TORAD (angle_in_degrees);} +double proj_todeg (double angle_in_radians) { return PJ_TODEG (angle_in_radians);} + + +/* The shape of jazz to come! */ + +int proj_transform_obs (PJ *P, enum proj_direction direction, size_t n, PJ_OBS *obs); +int proj_transform_coord (PJ *P, enum proj_direction direction, size_t n, PJ_COORD *coord); +PJ_OBS proj_obs (double x, double y, double z, double t, double o, double p, double k, int id, unsigned int flags); +PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *def_from, const char *def_to); |
