From a3fa749bc4f378d005c9e3fd809c0be25de5ffb2 Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Wed, 25 Oct 2017 10:39:56 +0200 Subject: Remove PJ_OBS from the API surface, rename pj_obs_api.c to pj_4D_api.c (#625) * Remove PJ_OBS from the API surface, rename pj_obs_api.c to pj_4D_api.c * Repair proj.def --- src/Makefile.am | 2 +- src/PJ_cart.c | 10 +- src/PJ_helmert.c | 4 +- src/PJ_latlong.c | 3 +- src/lib_proj.cmake | 2 +- src/makefile.vc | 2 +- src/pj_internal.c | 55 +++- src/pj_obs_api.c | 818 ---------------------------------------------------- src/proj.def | 75 +++-- src/proj.h | 19 +- src/proj_4D_api.c | 752 +++++++++++++++++++++++++++++++++++++++++++++++ src/proj_internal.h | 9 + src/projects.h | 6 +- 13 files changed, 863 insertions(+), 894 deletions(-) delete mode 100644 src/pj_obs_api.c create mode 100644 src/proj_4D_api.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index a039151d..363f2cce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -85,7 +85,7 @@ libproj_la_SOURCES = \ jniproj.c pj_mutex.c pj_initcache.c pj_apply_vgridshift.c geodesic.c \ pj_strtod.c \ \ - pj_obs_api.c PJ_cart.c PJ_pipeline.c PJ_horner.c PJ_helmert.c \ + proj_4D_api.c PJ_cart.c PJ_pipeline.c PJ_horner.c PJ_helmert.c \ PJ_vgridshift.c PJ_hgridshift.c PJ_unitconvert.c PJ_molodensky.c \ pj_internal.c diff --git a/src/PJ_cart.c b/src/PJ_cart.c index 37aa3b97..99e1af7d 100644 --- a/src/PJ_cart.c +++ b/src/PJ_cart.c @@ -387,10 +387,10 @@ int pj_cart_selftest (void) { b = proj_trans_obs (P, PJ_FWD, obs[1]); n = proj_transform ( - P, PJ_FWD, + P, PJ_FWD, &(obs[0].coo.lpz.lam), sz, 2, &(obs[0].coo.lpz.phi), sz, 2, - &(obs[0].coo.lpz.z), sz, 2, + &(obs[0].coo.lpz.z), sz, 2, 0, sz, 0 ); if (2!=n) @@ -408,10 +408,10 @@ int pj_cart_selftest (void) { h = 27; t = 33; n = proj_transform ( - P, PJ_FWD, + P, PJ_FWD, &(obs[0].coo.lpz.lam), sz, 2, &(obs[0].coo.lpz.phi), sz, 2, - &h, 0, 1, + &h, 0, 1, &t, 0, 1 ); if (2!=n) @@ -429,7 +429,7 @@ int pj_cart_selftest (void) { obs[0].coo = proj_coord (PJ_TORAD(12), PJ_TORAD(55), 45, 0); obs[1].coo = proj_coord (PJ_TORAD(12), PJ_TORAD(56), 50, 0); - if (proj_transform_obs(P, PJ_FWD, 2, obs)) + if (proj_transform_coord(P, PJ_FWD, 2, (PJ_COORD *) obs)) return 30; if (a.coo.lpz.lam != obs[0].coo.lpz.lam) return 31; diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c index ffbdd01a..503dc392 100644 --- a/src/PJ_helmert.c +++ b/src/PJ_helmert.c @@ -668,11 +668,11 @@ int pj_helmert_selftest (void) { matrix is updated when necessary. Test coordinates from GNSStrans. */ XYZ expect4a = {3370658.18890, 711877.42370, 5349787.12430}; XYZ expect4b = {3370658.18087, 711877.42750, 5349787.12648}; - PJ_OBS in4 = {{{3370658.378, 711877.314, 5349787.086, 2017.0}}, {{ 0, 0, 0}}, 0, 0}; + PJ_OBS in4 = {{{3370658.378, 711877.314, 5349787.086, 2017.0}}}; PJ_OBS out; PJ *helmert = proj_create( - 0, + 0, " +proj=helmert +ellps=GRS80" " +x=0.0127 +y=0.0065 +z=-0.0209 +s=0.00195" " +rx=-0.00039 +ry=0.00080 +rz=-0.00114" diff --git a/src/PJ_latlong.c b/src/PJ_latlong.c index 1677142a..7ee41e2a 100644 --- a/src/PJ_latlong.c +++ b/src/PJ_latlong.c @@ -1,6 +1,6 @@ /****************************************************************************** * Project: PROJ.4 - * Purpose: Stub projection implementation for lat/long coordinates. We + * Purpose: Stub projection implementation for lat/long coordinates. We * don't actually change the coordinates, but we want proj=latlong * to act sort of like a projection. * Author: Frank Warmerdam, warmerdam@pobox.com @@ -29,6 +29,7 @@ /* very loosely based upon DMA code by Bradford W. Drew */ #define PJ_LIB__ +#include "proj_internal.h" #include #include "projects.h" diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 1be10362..053e9ef6 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -201,7 +201,7 @@ SET(SRC_LIBPROJ_CORE pj_mlfn.c pj_msfn.c pj_mutex.c - pj_obs_api.c + proj_4D_api.c pj_internal.c proj_internal.h pj_open_lib.c diff --git a/src/makefile.vc b/src/makefile.vc index fdf03bd3..1330e9bb 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -60,7 +60,7 @@ support = \ pj_internal.obj pipeline = \ - pj_obs_api.obj PJ_cart.obj PJ_pipeline.obj PJ_horner.obj PJ_helmert.obj \ + proj_4D_api.obj PJ_cart.obj PJ_pipeline.obj PJ_horner.obj PJ_helmert.obj \ PJ_vgridshift.obj PJ_hgridshift.obj PJ_unitconvert.obj PJ_molodensky.obj geodesic = geodesic.obj diff --git a/src/pj_internal.c b/src/pj_internal.c index 31c299ac..5eb98afb 100644 --- a/src/pj_internal.c +++ b/src/pj_internal.c @@ -1,8 +1,9 @@ /****************************************************************************** * Project: PROJ.4 - * Purpose: This is primarily material originating from pj_obs_api.c, - * that does not fit into the API category. Hence this pile of - * tubings and fittings for PROJ.4 internal plumbing. + * 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. * * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05 * @@ -42,12 +43,52 @@ /* Used for zero-initializing new objects */ const PJ_COORD proj_coord_null = {{0, 0, 0, 0}}; const PJ_OBS proj_obs_null = { - {{0, 0, 0, 0}}, - {{0, 0, 0}}, - 0, 0 + {{0, 0, 0, 0}} }; + +/* Initialize PJ_OBS struct */ +PJ_OBS proj_obs (double x, double y, double z, double t) { + PJ_OBS res; + res.coo = proj_coord (x, y, z, t); + return res; +} + + + + + + + + + + + + + + +/* Apply the transformation P to the coordinate coo */ +PJ_OBS proj_trans_obs (PJ *P, 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; + } + + proj_errno_set (P, EINVAL); + return proj_obs_error (); +} + + /* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */ PJ_COORD proj_coord_error (void) { PJ_COORD c; @@ -58,8 +99,6 @@ PJ_COORD proj_coord_error (void) { PJ_OBS proj_obs_error (void) { PJ_OBS obs; obs.coo = proj_coord_error (); - obs.anc.v[0] = obs.anc.v[1] = obs.anc.v[2] = HUGE_VAL; - obs.id = obs.flags = 0; return obs; } diff --git a/src/pj_obs_api.c b/src/pj_obs_api.c deleted file mode 100644 index 55da9fa2..00000000 --- a/src/pj_obs_api.c +++ /dev/null @@ -1,818 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implement a (currently minimalistic) proj API based primarily - * on the PJ_OBS generic geodetic data type. - * - * proj thread contexts have not seen widespread use, so one of the - * intentions with this new API is to make them less visible on the - * API surface: Contexts do not have a life by themselves, they are - * visible only through their associated PJs, and the number of - * functions supporting them is limited. - * - * Author: Thomas Knudsen, thokn@sdfe.dk, 2016-06-09/2016-11-06 - * - ****************************************************************************** - * Copyright (c) 2016, 2017 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"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * 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. - *****************************************************************************/ -#define PJ_OBS_API_C -#include -#include "proj_internal.h" -#include "projects.h" -#include "geodesic.h" -#include -#include - - -/* Initialize PJ_COORD struct */ -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; -} - -/* Initialize PJ_OBS struct */ -PJ_OBS proj_obs (double x, double y, double z, double t, double o, double p, double k, int id, unsigned int flags) { - PJ_OBS res; - res.coo.v[0] = x; - res.coo.v[1] = y; - res.coo.v[2] = z; - res.coo.v[3] = t; - res.anc.v[0] = o; - res.anc.v[1] = p; - res.anc.v[2] = k; - res.id = id; - res.flags = flags; - - return res; -} - - - -/* Geodesic distance (in meter) between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, LP a, LP b) { - double s12, azi1, azi2; - /* Note: the geodesic code takes arguments in degrees */ - 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 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 proj_xyz_dist (XYZ a, XYZ b) { - return hypot (hypot (a.x - b.x, a.y - b.y), a.z - b.z); -} - - - -/* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ -double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD coo) { - int i; - PJ_COORD o, u; - enum pj_io_units unit; - - if (0==P) - return HUGE_VAL; - - if (n < 1) { - proj_errno_set (P, EINVAL); - return HUGE_VAL; - } - - o = coo; - - switch (direction) { - case PJ_FWD: - for (i = 0; i < n; i++) { - u = pj_fwdcoord (o, P); - o = pj_invcoord (u, P); - } - break; - case PJ_INV: - for (i = 0; i < n; i++) { - u = pj_invcoord (o, P); - o = pj_fwdcoord (u, P); - } - break; - default: - proj_errno_set (P, EINVAL); - return HUGE_VAL; - } - - /* left when forward, because we do a roundtrip, and end where we begin */ - unit = direction==PJ_FWD? P->left: P->right; - if (unit==PJ_IO_UNITS_RADIANS) - return hypot (proj_lp_dist (P, coo.lp, o.lp), coo.lpz.z - o.lpz.z); - - return proj_xyz_dist (coo.xyz, coo.xyz); -} - - - - - - - - - - - - - -/* Apply the transformation P to the coordinate coo */ -PJ_OBS proj_trans_obs (PJ *P, 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; - } - - proj_errno_set (P, EINVAL); - return proj_obs_error (); -} - - - -/* Apply the transformation P to the coordinate coo */ -PJ_COORD proj_trans_coord (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { - if (0==P) - 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; - } - - proj_errno_set (P, EINVAL); - return proj_coord_error (); -} - - - -/*************************************************************************************/ -size_t proj_transform ( - PJ *P, - PJ_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; - } - - /* 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; -} - -/*****************************************************************************/ -int proj_transform_obs (PJ *P, PJ_DIRECTION direction, size_t n, PJ_OBS *obs) { -/****************************************************************************** - Batch transform an array of PJ_OBS. - - Returns 0 if all observations are transformed without error, otherwise - returns error number. -******************************************************************************/ - size_t i; - for (i=0; i= 511) - continue; - - if (strlen(info.searchpath) != 0) { - strcat(info.searchpath, ";"); - len = strlen(paths[i]); - strncat(info.searchpath, paths[i], sizeof(info.searchpath)-len-1); - } else { - pj_strlcpy(info.searchpath, paths[i], sizeof(info.searchpath)); - } - } - - return info; -} - - -/*****************************************************************************/ -PJ_PROJ_INFO proj_pj_info(PJ *P) { -/****************************************************************************** - Basic info about a particular instance of a projection object. - - Returns PJ_PROJ_INFO struct. - -******************************************************************************/ - PJ_PROJ_INFO info; - char *def; - - memset(&info, 0, sizeof(PJ_PROJ_INFO)); - - /* Expected accuracy of the transformation. Hardcoded for now, will be improved */ - /* later. Most likely to be used when a transformation is set up with */ - /* proj_create_crs_to_crs in a future version that leverages the EPSG database. */ - info.accuracy = -1.0; - - if (!P) { - return info; - } - - /* projection id */ - if (pj_param(P->ctx, P->params, "tproj").i) - pj_strlcpy(info.id, pj_param(P->ctx, P->params, "sproj").s, sizeof(info.id)); - - /* projection description */ - pj_strlcpy(info.description, P->descr, sizeof(info.description)); - - /* projection definition */ - def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ - pj_strlcpy(info.definition, &def[1], sizeof(info.definition)); /* def includes a leading space */ - pj_dealloc(def); - - /* this does not take into account that a pipeline potentially does not */ - /* have an inverse. */ - info.has_inverse = (P->inv != 0 || P->inv3d != 0 || P->invobs != 0); - - return info; -} - - -/*****************************************************************************/ -PJ_GRID_INFO proj_grid_info(const char *gridname) { -/****************************************************************************** - Information about a named datum grid. - - Returns PJ_GRID_INFO struct. - -******************************************************************************/ - PJ_GRID_INFO info; - - /*PJ_CONTEXT *ctx = proj_context_create(); */ - PJ_CONTEXT *ctx = pj_get_default_ctx(); - PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); - memset(&info, 0, sizeof(PJ_GRID_INFO)); - - /* in case the grid wasn't found */ - if (gridinfo->filename == NULL) { - pj_gridinfo_free(ctx, gridinfo); - strcpy(info.format, "missing"); - return info; - } - - /* name of grid */ - pj_strlcpy(info.gridname, gridname, sizeof(info.gridname)); - - /* full path of grid */ - pj_find_file(ctx, gridname, info.filename, sizeof(info.filename)); - - /* grid format */ - pj_strlcpy(info.format, gridinfo->format, sizeof(info.format)); - - /* grid size */ - info.n_lon = gridinfo->ct->lim.lam; - info.n_lat = gridinfo->ct->lim.phi; - - /* cell size */ - info.cs_lon = gridinfo->ct->del.lam; - info.cs_lat = gridinfo->ct->del.phi; - - /* bounds of grid */ - info.lowerleft = gridinfo->ct->ll; - info.upperright.lam = info.lowerleft.lam + info.n_lon*info.cs_lon; - info.upperright.phi = info.lowerleft.phi + info.n_lat*info.cs_lat; - - pj_gridinfo_free(ctx, gridinfo); - - return info; -} - -/*****************************************************************************/ -PJ_INIT_INFO proj_init_info(const char *initname){ -/****************************************************************************** - Information about a named init file. - - Maximum length of initname is 64. - - Returns PJ_INIT_INFO struct. - - If the init file is not found all members of - the return struct are set to 0. If the init file is found, but it the - metadata is missing, the value is set to "Unknown". - -******************************************************************************/ - int file_found, def_found=0; - char param[80], key[74]; - paralist *start, *next; - PJ_INIT_INFO info; - PJ_CONTEXT *ctx = pj_get_default_ctx(); - - memset(&info, 0, sizeof(PJ_INIT_INFO)); - - file_found = pj_find_file(ctx, initname, info.filename, sizeof(info.filename)); - if (!file_found || strlen(initname) > 64) { - return info; - } - - pj_strlcpy(info.name, initname, sizeof(info.name)); - strcpy(info.origin, "Unknown"); - strcpy(info.version, "Unknown"); - strcpy(info.lastupdate, "Unknown"); - - pj_strlcpy(key, initname, 64); /* make room for ":metadata\0" at the end */ - strncat(key, ":metadata", 9); - strcpy(param, "+init="); - strncat(param, key, 73); - - start = pj_mkparam(param); - next = pj_get_init(ctx, &start, start, key, &def_found); - - if (pj_param(ctx, start, "tversion").i) { - pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version)); - } - - if (pj_param(ctx, start, "torigin").i) { - pj_strlcpy(info.origin, pj_param(ctx, start, "sorigin").s, sizeof(info.origin)); - } - - if (pj_param(ctx, start, "tlastupdate").i) { - pj_strlcpy(info.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(info.lastupdate)); - } - - for ( ; start; start = next) { - next = start->next; - pj_dalloc(start); - } - - return info; -} - - -/*****************************************************************************/ -PJ_DERIVS proj_derivatives(PJ *P, const LP lp) { -/****************************************************************************** - Derivatives of coordinates. - - returns PJ_DERIVS. If unsuccessfull error number is set and the returned - struct contains NULL data. - -******************************************************************************/ - PJ_DERIVS derivs; - - if (pj_deriv(lp, 1e-5, P, &derivs)) { - /* errno set in pj_derivs */ - memset(&derivs, 0, sizeof(PJ_DERIVS)); - } - - return derivs; -} - - -/*****************************************************************************/ -PJ_FACTORS proj_factors(PJ *P, const LP lp) { -/****************************************************************************** - Cartographic characteristics at point lp. - - Characteristics include meridian, parallel and areal scales, angular - distortion, meridian/parallel, meridian convergence and scale error. - - returns PJ_FACTORS. If unsuccessfull error number is set and the returned - struct contains NULL data. - -******************************************************************************/ - PJ_FACTORS factors; - - /* pj_factors rely code being zero */ - factors.code = 0; - - if (pj_factors(lp, P, 0.0, &factors)) { - /* errno set in pj_factors */ - memset(&factors, 0, sizeof(PJ_FACTORS)); - } - - return factors; -} - - -const PJ_ELLPS *proj_list_ellps(void) { - return pj_get_ellps_ref(); -} - -const PJ_UNITS *proj_list_units(void) { - return pj_get_units_ref(); -} - -const PJ_OPERATIONS *proj_list_operations(void) { - return pj_get_list_ref(); -} - -const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) { - return pj_get_prime_meridians_ref(); -} - -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);} - - -double proj_dmstor(const char *is, char **rs) { - return dmstor(is, rs); -} - -char* proj_rtodms(char *s, double r, int pos, int neg) { - return rtodms(s, r, pos, neg); -} diff --git a/src/proj.def b/src/proj.def index 598f2824..86deb24d 100644 --- a/src/proj.def +++ b/src/proj.def @@ -100,50 +100,49 @@ EXPORTS proj_trans_obs @95 proj_trans_coord @96 proj_transform @97 - proj_transform_obs @98 - proj_transform_coord @99 - proj_roundtrip @100 + proj_transform_coord @98 + proj_roundtrip @99 - proj_coord @101 - proj_obs @102 - proj_coord_error @103 - proj_obs_error @104 + proj_coord @100 + proj_obs @101 + proj_coord_error @102 + proj_obs_error @103 - proj_errno @105 - proj_errno_set @106 - proj_errno_reset @107 - proj_errno_restore @108 - proj_context_errno_set @109 + proj_errno @104 + proj_errno_set @105 + proj_errno_reset @106 + proj_errno_restore @107 + proj_context_errno_set @108 - proj_context_create @110 - proj_context_set @111 - proj_context_inherit @112 - proj_context_destroy @113 + proj_context_create @109 + proj_context_set @110 + proj_context_inherit @111 + proj_context_destroy @112 - proj_lp_dist @114 - proj_xy_dist @115 - proj_xyz_dist @116 + proj_lp_dist @113 + proj_xy_dist @114 + proj_xyz_dist @115 - proj_log_level @117 - proj_log_func @118 - proj_log_error @119 - proj_log_debug @120 - proj_log_trace @121 + proj_log_level @116 + proj_log_func @117 + proj_log_error @118 + proj_log_debug @119 + proj_log_trace @120 - proj_info @122 - proj_pj_info @123 - proj_grid_info @124 - proj_init_info @125 + proj_info @121 + proj_pj_info @122 + proj_grid_info @123 + proj_init_info @124 - proj_torad @126 - proj_todeg @127 - proj_rtodms @128 - proj_dmstor @129 + proj_torad @125 + proj_todeg @126 + proj_rtodms @127 + proj_dmstor @128 - proj_derivatives @130 - proj_factors @131 + proj_derivatives @129 + proj_factors @130 - proj_list_operations @132 - proj_list_ellps @133 - proj_list_units @134 - proj_list_prime_meridians @135 + proj_list_operations @131 + proj_list_ellps @132 + proj_list_units @133 + proj_list_prime_meridians @134 diff --git a/src/proj.h b/src/proj.h index aa9f3b17..ed885091 100644 --- a/src/proj.h +++ b/src/proj.h @@ -90,7 +90,7 @@ * compatible call proj_context_create(0), which will not create * a new context, but simply provide a pointer to the default one. * - * See pj_obs_api_test.c for an example of how to use the API. + * See proj_4D_api_test.c for examples of how to use the API. * * Author: Thomas Knudsen, * Benefitting from a large number of comments and suggestions @@ -170,10 +170,6 @@ extern char const pj_release[]; /* global release id string */ /* first forward declare everything needed */ -/* Data type for generic geodetic observations */ -struct PJ_OBS; -typedef struct PJ_OBS PJ_OBS; - /* Data type for generic geodetic 3D data */ union PJ_TRIPLET; typedef union PJ_TRIPLET PJ_TRIPLET; @@ -299,14 +295,6 @@ union PJ_PAIR { }; -struct PJ_OBS { - PJ_COORD coo; /* coordinate data */ - PJ_TRIPLET anc; /* ancillary data */ - int id; /* integer ancillary data - e.g. observation number, EPSG code... */ - unsigned int flags; /* additional data, intended for flags */ -}; - - #define PJ_IS_ANAL_XL_YL 01 /* derivatives of lon analytic */ #define PJ_IS_ANAL_XP_YP 02 /* derivatives of lat analytic */ #define PJ_IS_ANAL_HK 04 /* h and k analytic */ @@ -385,7 +373,6 @@ enum PJ_DIRECTION { }; typedef enum PJ_DIRECTION PJ_DIRECTION; -PJ_OBS proj_trans_obs (PJ *P, PJ_DIRECTION direction, PJ_OBS obs); PJ_COORD proj_trans_coord (PJ *P, PJ_DIRECTION direction, PJ_COORD coord); @@ -398,16 +385,14 @@ size_t proj_transform ( double *t, size_t st, size_t nt ); -int proj_transform_obs (PJ *P, PJ_DIRECTION direction, size_t n, PJ_OBS *obs); int proj_transform_coord (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord); /* Initializers */ PJ_COORD proj_coord (double x, double y, double z, double t); -PJ_OBS proj_obs (double x, double y, double z, double t, double o, double p, double k, int id, unsigned int flags); /* Measure internal consistency - in forward or inverse direction */ double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD coo); - + /* Geodesic distance between two points with angular 2D coordinates */ double proj_lp_dist (const PJ *P, LP a, LP b); diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c new file mode 100644 index 00000000..b1aa3883 --- /dev/null +++ b/src/proj_4D_api.c @@ -0,0 +1,752 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implement a (currently minimalistic) proj API based primarily + * on the PJ_COORD 4D geodetic spatiotemporal data type. + * + * proj thread contexts have not seen widespread use, so one of the + * intentions with this new API is to make them less visible on the + * API surface: Contexts do not have a life by themselves, they are + * visible only through their associated PJs, and the number of + * functions supporting them is limited. + * + * Author: Thomas Knudsen, thokn@sdfe.dk, 2016-06-09/2016-11-06 + * + ****************************************************************************** + * Copyright (c) 2016, 2017 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"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * 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 +#include "proj_internal.h" +#include "projects.h" +#include "geodesic.h" +#include +#include + + +/* Initialize PJ_COORD struct */ +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 (in meter) between two points with angular 2D coordinates */ +double proj_lp_dist (const PJ *P, LP a, LP b) { + double s12, azi1, azi2; + /* Note: the geodesic code takes arguments in degrees */ + 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 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 proj_xyz_dist (XYZ a, XYZ b) { + return hypot (hypot (a.x - b.x, a.y - b.y), a.z - b.z); +} + + + +/* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ +double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD coo) { + int i; + PJ_COORD o, u; + enum pj_io_units unit; + + if (0==P) + return HUGE_VAL; + + if (n < 1) { + proj_errno_set (P, EINVAL); + return HUGE_VAL; + } + + o = coo; + + switch (direction) { + case PJ_FWD: + for (i = 0; i < n; i++) { + u = pj_fwdcoord (o, P); + o = pj_invcoord (u, P); + } + break; + case PJ_INV: + for (i = 0; i < n; i++) { + u = pj_invcoord (o, P); + o = pj_fwdcoord (u, P); + } + break; + default: + proj_errno_set (P, EINVAL); + return HUGE_VAL; + } + + /* left when forward, because we do a roundtrip, and end where we begin */ + unit = direction==PJ_FWD? P->left: P->right; + if (unit==PJ_IO_UNITS_RADIANS) + return hypot (proj_lp_dist (P, coo.lp, o.lp), coo.lpz.z - o.lpz.z); + + return proj_xyz_dist (coo.xyz, coo.xyz); +} + + + +/* Apply the transformation P to the coordinate coo */ +PJ_COORD proj_trans_coord (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { + if (0==P) + 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; + } + + proj_errno_set (P, EINVAL); + return proj_coord_error (); +} + + + +/*************************************************************************************/ +size_t proj_transform ( + PJ *P, + PJ_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; + } + + /* 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; +} + +/*****************************************************************************/ +int proj_transform_coord (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) { +/****************************************************************************** + Batch transform an array of PJ_COORD. + + Returns 0 if all coordinates are transformed without error, otherwise + returns error number. +******************************************************************************/ + size_t i; + for (i=0; i= 511) + continue; + + if (strlen(info.searchpath) != 0) { + strcat(info.searchpath, ";"); + len = strlen(paths[i]); + strncat(info.searchpath, paths[i], sizeof(info.searchpath)-len-1); + } else { + pj_strlcpy(info.searchpath, paths[i], sizeof(info.searchpath)); + } + } + + return info; +} + + +/*****************************************************************************/ +PJ_PROJ_INFO proj_pj_info(PJ *P) { +/****************************************************************************** + Basic info about a particular instance of a projection object. + + Returns PJ_PROJ_INFO struct. + +******************************************************************************/ + PJ_PROJ_INFO info; + char *def; + + memset(&info, 0, sizeof(PJ_PROJ_INFO)); + + /* Expected accuracy of the transformation. Hardcoded for now, will be improved */ + /* later. Most likely to be used when a transformation is set up with */ + /* proj_create_crs_to_crs in a future version that leverages the EPSG database. */ + info.accuracy = -1.0; + + if (!P) { + return info; + } + + /* projection id */ + if (pj_param(P->ctx, P->params, "tproj").i) + pj_strlcpy(info.id, pj_param(P->ctx, P->params, "sproj").s, sizeof(info.id)); + + /* projection description */ + pj_strlcpy(info.description, P->descr, sizeof(info.description)); + + /* projection definition */ + def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ + pj_strlcpy(info.definition, &def[1], sizeof(info.definition)); /* def includes a leading space */ + pj_dealloc(def); + + /* this does not take into account that a pipeline potentially does not */ + /* have an inverse. */ + info.has_inverse = (P->inv != 0 || P->inv3d != 0 || P->invobs != 0); + + return info; +} + + +/*****************************************************************************/ +PJ_GRID_INFO proj_grid_info(const char *gridname) { +/****************************************************************************** + Information about a named datum grid. + + Returns PJ_GRID_INFO struct. + +******************************************************************************/ + PJ_GRID_INFO info; + + /*PJ_CONTEXT *ctx = proj_context_create(); */ + PJ_CONTEXT *ctx = pj_get_default_ctx(); + PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); + memset(&info, 0, sizeof(PJ_GRID_INFO)); + + /* in case the grid wasn't found */ + if (gridinfo->filename == NULL) { + pj_gridinfo_free(ctx, gridinfo); + strcpy(info.format, "missing"); + return info; + } + + /* name of grid */ + pj_strlcpy(info.gridname, gridname, sizeof(info.gridname)); + + /* full path of grid */ + pj_find_file(ctx, gridname, info.filename, sizeof(info.filename)); + + /* grid format */ + pj_strlcpy(info.format, gridinfo->format, sizeof(info.format)); + + /* grid size */ + info.n_lon = gridinfo->ct->lim.lam; + info.n_lat = gridinfo->ct->lim.phi; + + /* cell size */ + info.cs_lon = gridinfo->ct->del.lam; + info.cs_lat = gridinfo->ct->del.phi; + + /* bounds of grid */ + info.lowerleft = gridinfo->ct->ll; + info.upperright.lam = info.lowerleft.lam + info.n_lon*info.cs_lon; + info.upperright.phi = info.lowerleft.phi + info.n_lat*info.cs_lat; + + pj_gridinfo_free(ctx, gridinfo); + + return info; +} + +/*****************************************************************************/ +PJ_INIT_INFO proj_init_info(const char *initname){ +/****************************************************************************** + Information about a named init file. + + Maximum length of initname is 64. + + Returns PJ_INIT_INFO struct. + + If the init file is not found all members of + the return struct are set to 0. If the init file is found, but it the + metadata is missing, the value is set to "Unknown". + +******************************************************************************/ + int file_found, def_found=0; + char param[80], key[74]; + paralist *start, *next; + PJ_INIT_INFO info; + PJ_CONTEXT *ctx = pj_get_default_ctx(); + + memset(&info, 0, sizeof(PJ_INIT_INFO)); + + file_found = pj_find_file(ctx, initname, info.filename, sizeof(info.filename)); + if (!file_found || strlen(initname) > 64) { + return info; + } + + pj_strlcpy(info.name, initname, sizeof(info.name)); + strcpy(info.origin, "Unknown"); + strcpy(info.version, "Unknown"); + strcpy(info.lastupdate, "Unknown"); + + pj_strlcpy(key, initname, 64); /* make room for ":metadata\0" at the end */ + strncat(key, ":metadata", 9); + strcpy(param, "+init="); + strncat(param, key, 73); + + start = pj_mkparam(param); + next = pj_get_init(ctx, &start, start, key, &def_found); + + if (pj_param(ctx, start, "tversion").i) { + pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version)); + } + + if (pj_param(ctx, start, "torigin").i) { + pj_strlcpy(info.origin, pj_param(ctx, start, "sorigin").s, sizeof(info.origin)); + } + + if (pj_param(ctx, start, "tlastupdate").i) { + pj_strlcpy(info.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(info.lastupdate)); + } + + for ( ; start; start = next) { + next = start->next; + pj_dalloc(start); + } + + return info; +} + + +/*****************************************************************************/ +PJ_DERIVS proj_derivatives(PJ *P, const LP lp) { +/****************************************************************************** + Derivatives of coordinates. + + returns PJ_DERIVS. If unsuccessfull error number is set and the returned + struct contains NULL data. + +******************************************************************************/ + PJ_DERIVS derivs; + + if (pj_deriv(lp, 1e-5, P, &derivs)) { + /* errno set in pj_derivs */ + memset(&derivs, 0, sizeof(PJ_DERIVS)); + } + + return derivs; +} + + +/*****************************************************************************/ +PJ_FACTORS proj_factors(PJ *P, const LP lp) { +/****************************************************************************** + Cartographic characteristics at point lp. + + Characteristics include meridian, parallel and areal scales, angular + distortion, meridian/parallel, meridian convergence and scale error. + + returns PJ_FACTORS. If unsuccessfull error number is set and the returned + struct contains NULL data. + +******************************************************************************/ + PJ_FACTORS factors; + + /* pj_factors rely code being zero */ + factors.code = 0; + + if (pj_factors(lp, P, 0.0, &factors)) { + /* errno set in pj_factors */ + memset(&factors, 0, sizeof(PJ_FACTORS)); + } + + return factors; +} + + +const PJ_ELLPS *proj_list_ellps(void) { + return pj_get_ellps_ref(); +} + +const PJ_UNITS *proj_list_units(void) { + return pj_get_units_ref(); +} + +const PJ_OPERATIONS *proj_list_operations(void) { + return pj_get_list_ref(); +} + +const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) { + return pj_get_prime_meridians_ref(); +} + +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);} + + +double proj_dmstor(const char *is, char **rs) { + return dmstor(is, rs); +} + +char* proj_rtodms(char *s, double r, int pos, int neg) { + return rtodms(s, r, pos, neg); +} diff --git a/src/proj_internal.h b/src/proj_internal.h index 0b74b563..5773637c 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -50,6 +50,12 @@ extern "C" { +struct PJ_OBS { + PJ_COORD coo; /* coordinate data */ +}; +typedef struct PJ_OBS PJ_OBS; + + #ifndef PJ_TODEG #define PJ_TODEG(rad) ((rad)*180.0/M_PI) #endif @@ -59,6 +65,9 @@ extern "C" { +PJ_OBS proj_obs (double x, double y, double z, double t); +PJ_OBS proj_trans_obs (PJ *P, PJ_DIRECTION direction, PJ_OBS obs); + PJ_COORD proj_coord_error (void); PJ_OBS proj_obs_error (void); #ifndef PJ_INTERNAL_C diff --git a/src/projects.h b/src/projects.h index cbb980ca..b9d88cf3 100644 --- a/src/projects.h +++ b/src/projects.h @@ -174,6 +174,9 @@ typedef struct { double u, v, w; } UVW; /* Forward declarations and typedefs for stuff needed inside the PJ object */ struct PJconsts; struct PJ_OBS; +#ifndef PROJ_INTERNAL_H +typedef struct PJ_OBS PJ_OBS; +#endif union PJ_COORD; struct geod_geodesic; struct pj_opaque; @@ -189,7 +192,6 @@ enum pj_io_units { }; #ifndef PROJ_H typedef struct PJconsts PJ; /* the PJ object herself */ -typedef struct PJ_OBS PJ_OBS; typedef union PJ_COORD PJ_COORD; #endif @@ -259,7 +261,7 @@ struct PJconsts { void (*spc)(LP, PJ *, struct FACTORS *); void *(*destructor)(PJ *, int); - + /************************************************************************************* -- cgit v1.2.3