diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2020-01-13 00:22:56 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-01-13 00:22:56 +0100 |
| commit | 9d8647371d27bdbd717644f7df5514a6f2b07a00 (patch) | |
| tree | 1ff8fa7fd812fea2ae53792b5488a90f6418095c /src | |
| parent | b6f0153e5aa27dc11d2c879dc4a62a0f35a122cb (diff) | |
| parent | 22792cd55ba41ffadb248c246cc871612a5139c1 (diff) | |
| download | PROJ-9d8647371d27bdbd717644f7df5514a6f2b07a00.tar.gz PROJ-9d8647371d27bdbd717644f7df5514a6f2b07a00.zip | |
Merge pull request #1777 from rouault/rfc4_impl
[RFC4_dev] Grid loading refactoring
Diffstat (limited to 'src')
| -rw-r--r-- | src/4D_api.cpp | 77 | ||||
| -rw-r--r-- | src/Makefile.am | 9 | ||||
| -rw-r--r-- | src/apply_gridshift.cpp | 508 | ||||
| -rw-r--r-- | src/apply_vgridshift.cpp | 392 | ||||
| -rw-r--r-- | src/datum_set.cpp | 21 | ||||
| -rw-r--r-- | src/gc_reader.cpp | 248 | ||||
| -rw-r--r-- | src/gridcatalog.cpp | 306 | ||||
| -rw-r--r-- | src/gridinfo.cpp | 988 | ||||
| -rw-r--r-- | src/gridlist.cpp | 220 | ||||
| -rw-r--r-- | src/grids.cpp | 895 | ||||
| -rw-r--r-- | src/grids.hpp | 167 | ||||
| -rw-r--r-- | src/init.cpp | 6 | ||||
| -rw-r--r-- | src/lib_proj.cmake | 9 | ||||
| -rw-r--r-- | src/malloc.cpp | 9 | ||||
| -rw-r--r-- | src/nad_cvt.cpp | 77 | ||||
| -rw-r--r-- | src/nad_init.cpp | 308 | ||||
| -rw-r--r-- | src/nad_intr.cpp | 67 | ||||
| -rw-r--r-- | src/proj_internal.h | 124 | ||||
| -rw-r--r-- | src/transform.cpp | 145 | ||||
| -rw-r--r-- | src/transformations/deformation.cpp | 51 | ||||
| -rw-r--r-- | src/transformations/hgridshift.cpp | 44 | ||||
| -rw-r--r-- | src/transformations/vgridshift.cpp | 48 |
22 files changed, 1742 insertions, 2977 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp index f37594b5..efb4a86a 100644 --- a/src/4D_api.cpp +++ b/src/4D_api.cpp @@ -47,6 +47,7 @@ #include "proj_internal.h" #include <math.h> #include "geodesic.h" +#include "grids.hpp" #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" @@ -1553,43 +1554,65 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) { /*PJ_CONTEXT *ctx = proj_context_create(); */ PJ_CONTEXT *ctx = pj_get_default_ctx(); - PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); memset(&grinfo, 0, sizeof(PJ_GRID_INFO)); - /* in case the grid wasn't found */ - if (gridinfo->filename == nullptr || gridinfo->ct == nullptr) { - pj_gridinfo_free(ctx, gridinfo); - strcpy(grinfo.format, "missing"); - return grinfo; - } - - /* The string copies below are automatically null-terminated due to */ - /* the memset above, so strncpy is safe */ + const auto fillGridInfo = [&grinfo, ctx, gridname] + (const NS_PROJ::Grid& grid, const std::string& format) + { + const auto& extent = grid.extentAndRes(); - /* name of grid */ - strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); + /* name of grid */ + strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); - /* full path of grid */ - pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1); + /* full path of grid */ + pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1); - /* grid format */ - strncpy (grinfo.format, gridinfo->format, sizeof(grinfo.format) - 1); + /* grid format */ + strncpy (grinfo.format, format.c_str(), sizeof(grinfo.format) - 1); - /* grid size */ - grinfo.n_lon = gridinfo->ct->lim.lam; - grinfo.n_lat = gridinfo->ct->lim.phi; + /* grid size */ + grinfo.n_lon = grid.width(); + grinfo.n_lat = grid.height(); - /* cell size */ - grinfo.cs_lon = gridinfo->ct->del.lam; - grinfo.cs_lat = gridinfo->ct->del.phi; + /* cell size */ + grinfo.cs_lon = extent.resLon; + grinfo.cs_lat = extent.resLat; - /* bounds of grid */ - grinfo.lowerleft = gridinfo->ct->ll; - grinfo.upperright.lam = grinfo.lowerleft.lam + grinfo.n_lon*grinfo.cs_lon; - grinfo.upperright.phi = grinfo.lowerleft.phi + grinfo.n_lat*grinfo.cs_lat; + /* bounds of grid */ + grinfo.lowerleft.lam = extent.westLon; + grinfo.lowerleft.phi = extent.southLat; + grinfo.upperright.lam = extent.eastLon; + grinfo.upperright.phi = extent.northLat; + }; - pj_gridinfo_free(ctx, gridinfo); + { + const auto gridSet = NS_PROJ::VerticalShiftGridSet::open(ctx, gridname); + if( gridSet ) + { + const auto& grids = gridSet->grids(); + if( !grids.empty() ) + { + const auto& grid = grids.front(); + fillGridInfo(*grid, gridSet->format()); + return grinfo; + } + } + } + { + const auto gridSet = NS_PROJ::HorizontalShiftGridSet::open(ctx, gridname); + if( gridSet ) + { + const auto& grids = gridSet->grids(); + if( !grids.empty() ) + { + const auto& grid = grids.front(); + fillGridInfo(*grid, gridSet->format()); + return grinfo; + } + } + } + strcpy(grinfo.format, "missing"); return grinfo; } diff --git a/src/Makefile.am b/src/Makefile.am index 2e14d1dd..a12de4e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -195,10 +195,8 @@ libproj_la_SOURCES = \ release.cpp gauss.cpp \ fileapi.cpp \ \ - gc_reader.cpp gridcatalog.cpp \ - nad_cvt.cpp nad_init.cpp nad_intr.cpp \ apply_gridshift.cpp datums.cpp datum_set.cpp transform.cpp \ - geocent.cpp geocent.h utils.cpp gridinfo.cpp gridlist.cpp \ + geocent.cpp geocent.h utils.cpp \ jniproj.cpp mutex.cpp initcache.cpp apply_vgridshift.cpp geodesic.c \ strtod.cpp \ \ @@ -213,7 +211,10 @@ libproj_la_SOURCES = \ proj_json_streaming_writer.hpp \ proj_json_streaming_writer.cpp \ \ - tracing.cpp + tracing.cpp \ + \ + grids.hpp \ + grids.cpp # The sed hack is to please MSVC diff --git a/src/apply_gridshift.cpp b/src/apply_gridshift.cpp index e6c7b2b1..a0ffa394 100644 --- a/src/apply_gridshift.cpp +++ b/src/apply_gridshift.cpp @@ -28,7 +28,9 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PJ_LIB__ +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif #include <math.h> #include <stdio.h> @@ -36,235 +38,60 @@ #include "proj.h" #include "proj_internal.h" +#include "proj/internal/internal.hpp" +#include "grids.hpp" -/************************************************************************/ -/* pj_apply_gridshift() */ -/* */ -/* This is the externally callable interface - part of the */ -/* public API - though it is not used internally any more and I */ -/* doubt it is used by any other applications. But we preserve */ -/* it to honour our public api. */ -/************************************************************************/ +NS_PROJ_START -int pj_apply_gridshift( projCtx ctx, const char *nadgrids, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) +// --------------------------------------------------------------------------- +static const HorizontalShiftGrid* findGrid(const ListOfHGrids& grids, + PJ_LP input) { - PJ_GRIDINFO **gridlist; - int grid_count; - int ret; - - gridlist = pj_gridlist_from_nadgrids( ctx, nadgrids, &grid_count ); - - if( gridlist == nullptr || grid_count == 0 ) - { - pj_dalloc( gridlist ); - return ctx->last_errno; - } - - ret = pj_apply_gridshift_3( ctx, gridlist, grid_count, inverse, - point_count, point_offset, x, y, z ); - - /* - ** Note this frees the array of grid list pointers, but not the grids - ** which is as intended. The grids themselves live on. - */ - pj_dalloc( gridlist ); - - return ret; -} - -/************************************************************************/ -/* pj_apply_gridshift_2() */ -/* */ -/* This implementation uses the gridlist from a coordinate */ -/* system definition. If the gridlist has not yet been */ -/* populated in the coordinate system definition we set it up */ -/* now. */ -/************************************************************************/ - -int pj_apply_gridshift_2( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - if( defn->catalog_name != nullptr ) - return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset, - x, y, z ); - - if( defn->gridlist == nullptr ) - { - defn->gridlist = - pj_gridlist_from_nadgrids( pj_get_ctx( defn ), - pj_param(defn->ctx, defn->params,"snadgrids").s, - &(defn->gridlist_count) ); - - if( defn->gridlist == nullptr || defn->gridlist_count == 0 ) - return defn->ctx->last_errno; - } - - return pj_apply_gridshift_3( pj_get_ctx( defn ), - defn->gridlist, defn->gridlist_count, inverse, - point_count, point_offset, x, y, z ); -} - -/************************************************************************/ -/* find_ctable() */ -/* */ -/* Determine which grid is the correct given an input coordinate. */ -/************************************************************************/ - -static struct CTABLE* find_ctable(projCtx ctx, PJ_LP input, int grid_count, PJ_GRIDINFO **tables) { - int itable; - - /* keep trying till we find a table that works */ - for( itable = 0; itable < grid_count; itable++ ) + for( const auto& gridset: grids ) { - - PJ_GRIDINFO *gi = tables[itable]; - struct CTABLE *ct = gi->ct; - double epsilon = (fabs(ct->del.phi)+fabs(ct->del.lam))/10000.0; - /* skip tables that don't match our point at all. */ - if ( ct->ll.phi - epsilon > input.phi - || ct->ll.lam - epsilon > input.lam - || (ct->ll.phi + (ct->lim.phi-1) * ct->del.phi + epsilon < input.phi) - || (ct->ll.lam + (ct->lim.lam-1) * ct->del.lam + epsilon < input.lam) ) { - continue; - } - - /* If we have child nodes, check to see if any of them apply. */ - while( gi->child ) - { - PJ_GRIDINFO *child; - - for( child = gi->child; child != nullptr; child = child->next ) - { - struct CTABLE *ct1 = child->ct; - epsilon = (fabs(ct1->del.phi)+fabs(ct1->del.lam))/10000.0; - - if( ct1->ll.phi - epsilon > input.phi - || ct1->ll.lam - epsilon > input.lam - || (ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi + epsilon < input.phi) - || (ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam + epsilon < input.lam) ) { - continue; - } - break; - } - - /* If we didn't find a child then nothing more to do */ - if( child == nullptr ) break; - - /* Otherwise use the child, first checking it's children */ - gi = child; - ct = child->ct; - } - /* load the grid shift info if we don't have it. */ - if( ct->cvs == nullptr) { - if (!pj_gridinfo_load( ctx, gi ) ) { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return nullptr; - } - } - /* if we get this far we have found a suitable grid */ - return ct; + auto grid = gridset->gridAt(input.lam, input.phi); + if( grid ) + return grid; } - return nullptr; } -/************************************************************************/ -/* pj_apply_gridshift_3() */ -/* */ -/* This is the real workhorse, given a gridlist. */ -/************************************************************************/ +// --------------------------------------------------------------------------- -int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_count, - int inverse, long point_count, int point_offset, - double *x, double *y, double *z ) +static ListOfHGrids getListOfGridSets(PJ_CONTEXT* ctx, const char* grids) { - int i; - struct CTABLE *ct; - static int debug_count = 0; - (void) z; - - if( gridlist== nullptr || gridlist_count == 0 ) + ListOfHGrids list; + auto listOfGrids = internal::split(std::string(grids), ','); + for( const auto& grid: listOfGrids ) { - pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - PJ_LP input, output; - int itable; - - input.phi = y[io]; - input.lam = x[io]; - output.phi = HUGE_VAL; - output.lam = HUGE_VAL; - - ct = find_ctable(ctx, input, gridlist_count, gridlist); - if( ct != nullptr ) + const char* gridname = grid.c_str(); + bool canFail = false; + if( gridname[0] == '@' ) { - output = nad_cvt( input, inverse, ct ); - - if ( output.lam != HUGE_VAL && debug_count++ < 20 ) - pj_log( ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id ); + canFail = true; + gridname ++; } - - if ( output.lam == HUGE_VAL ) + auto gridSet = HorizontalShiftGridSet::open(ctx, gridname); + if( !gridSet ) { - if( ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + if( !canFail ) { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - for( itable = 0; itable < gridlist_count; itable++ ) - { - PJ_GRIDINFO *gi = gridlist[itable]; - if( itable == 0 ) - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, " tried: %s", gi->gridname ); - else - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, ",%s", gi->gridname ); - } + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return {}; } - - /* - * We don't actually have any machinery currently to set the - * following macro, so this is mostly kept here to make it clear - * how we ought to operate if we wanted to make it super clear - * that an error has occurred when points are outside our available - * datum shift areas. But if this is on, we will find that "low - * value" points on the fringes of some datasets will completely - * fail causing lots of problems when it is more or less ok to - * just not apply a datum shift. So rather than deal with - * that we just fallback to no shift. (see also bug #45). - */ -#ifdef ERR_GRID_AREA_TRANSIENT_SEVERE - y[io] = HUGE_VAL; - x[io] = HUGE_VAL; -#else - /* leave x/y unshifted. */ -#endif } else { - y[io] = output.phi; - x[io] = output.lam; + list.emplace_back(std::move(gridSet)); } } - - return 0; + return list; } + /**********************************************/ -int proj_hgrid_init(PJ* P, const char *grids) { +ListOfHGrids proj_hgrid_init(PJ* P, const char *gridkey) { /********************************************** Initizalize and populate list of horizontal @@ -279,29 +106,157 @@ int proj_hgrid_init(PJ* P, const char *grids) { ***********************************************/ - /* prepend "s" to the "grids" string to allow usage with pj_param */ - char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); - sprintf(sgrids, "%s%s", "s", grids); + std::string key("s"); + key += gridkey; + const char* grids = pj_param(P->ctx, P->params, key.c_str()).s; + if( grids == nullptr ) + return {}; - if (P->gridlist == nullptr) { - P->gridlist = pj_gridlist_from_nadgrids( - P->ctx, - pj_param(P->ctx, P->params, sgrids).s, - &(P->gridlist_count) - ); + return getListOfGridSets(P->ctx, grids); +} - if( P->gridlist == nullptr || P->gridlist_count == 0 ) { - pj_dealloc(sgrids); - return 0; +typedef struct { pj_int32 lam, phi; } ILP; + +static PJ_LP nad_intr(PJ_LP t, const HorizontalShiftGrid* grid) { + PJ_LP val, frct; + ILP indx; + int in; + + const auto& extent = grid->extentAndRes(); + t.lam /= extent.resLon; + indx.lam = isnan(t.lam) ? 0 : (pj_int32)lround(floor(t.lam)); + t.phi /= extent.resLat; + indx.phi = isnan(t.phi) ? 0 : (pj_int32)lround(floor(t.phi)); + + frct.lam = t.lam - indx.lam; + frct.phi = t.phi - indx.phi; + val.lam = val.phi = HUGE_VAL; + if (indx.lam < 0) { + if (indx.lam == -1 && frct.lam > 0.99999999999) { + ++indx.lam; + frct.lam = 0.; + } else + return val; + } else if ((in = indx.lam + 1) >= grid->width()) { + if (in == grid->width() && frct.lam < 1e-11) { + --indx.lam; + frct.lam = 1.; + } else + return val; + } + if (indx.phi < 0) { + if (indx.phi == -1 && frct.phi > 0.99999999999) { + ++indx.phi; + frct.phi = 0.; + } else + return val; + } else if ((in = indx.phi + 1) >= grid->height()) { + if (in == grid->height() && frct.phi < 1e-11) { + --indx.phi; + frct.phi = 1.; + } else + return val; + } + + float f00Lon = 0, f00Lat = 0; + float f10Lon = 0, f10Lat = 0; + float f01Lon = 0, f01Lat = 0; + float f11Lon = 0, f11Lat = 0; + if( !grid->valueAt(indx.lam, indx.phi, f00Lon, f00Lat) || + !grid->valueAt(indx.lam + 1, indx.phi, f10Lon, f10Lat) || + !grid->valueAt(indx.lam, indx.phi + 1, f01Lon, f01Lat) || + !grid->valueAt(indx.lam + 1, indx.phi + 1, f11Lon, f11Lat) ) + { + return val; } + + double m10 = frct.lam; + double m11 = m10; + double m01 = 1. - frct.lam; + double m00 = m01; + m11 *= frct.phi; + m01 *= frct.phi; + frct.phi = 1. - frct.phi; + m00 *= frct.phi; + m10 *= frct.phi; + val.lam = m00 * f00Lon + m10 * f10Lon + + m01 * f01Lon + m11 * f11Lon; + val.phi = m00 * f00Lat + m10 * f10Lat + + m01 * f01Lat + m11 * f11Lat; + return val; +} + + +#define MAX_ITERATIONS 10 +#define TOL 1e-12 + +static +PJ_LP nad_cvt(PJ_LP in, int inverse, const HorizontalShiftGrid* grid) { + PJ_LP t, tb,del, dif; + int i = MAX_ITERATIONS; + const double toltol = TOL*TOL; + + if (in.lam == HUGE_VAL) + return in; + + /* normalize input to ll origin */ + tb = in; + const auto& extent = grid->extentAndRes(); + tb.lam -= extent.westLon; + tb.phi -= extent.southLat; + + tb.lam = adjlon (tb.lam - M_PI) + M_PI; + + t = nad_intr (tb, grid); + if (t.lam == HUGE_VAL) + return t; + + if (!inverse) { + in.lam -= t.lam; + in.phi += t.phi; + return in; } - if (P->gridlist_count == 0) { - proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); + t.lam = tb.lam + t.lam; + t.phi = tb.phi - t.phi; + + do { + del = nad_intr(t, grid); + + /* This case used to return failure, but I have + changed it to return the first order approximation + of the inverse shift. This avoids cases where the + grid shift *into* this grid came from another grid. + While we aren't returning optimally correct results + I feel a close result in this case is better than + no result. NFW + To demonstrate use -112.5839956 49.4914451 against + the NTv2 grid shift file from Canada. */ + if (del.lam == HUGE_VAL) + break; + + dif.lam = t.lam - del.lam - tb.lam; + dif.phi = t.phi + del.phi - tb.phi; + t.lam -= dif.lam; + t.phi -= dif.phi; + + } while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); /* prob. slightly faster than hypot() */ + + if (i==0) { + /* If we had access to a context, this should go through pj_log, and we should set ctx->errno */ + if (getenv ("PROJ_DEBUG")) + fprintf( stderr, "Inverse grid shift iterator failed to converge.\n" ); + t.lam = t.phi = HUGE_VAL; + return t; } - pj_dealloc(sgrids); - return P->gridlist_count; + /* and again: pj_log and ctx->errno */ + if (del.lam==HUGE_VAL && getenv ("PROJ_DEBUG")) + fprintf (stderr, "Inverse grid shift iteration failed, presumably at grid edge.\nUsing first approximation.\n"); + + in.lam = adjlon (t.lam + extent.westLon); + in.phi = t.phi + extent.southLat; + return in; } /********************************************/ @@ -309,23 +264,23 @@ int proj_hgrid_init(PJ* P, const char *grids) { /* */ /* Return coordinate offset in grid */ /********************************************/ -PJ_LP proj_hgrid_value(PJ *P, PJ_LP lp) { - struct CTABLE *ct; +PJ_LP proj_hgrid_value(PJ *P, const ListOfHGrids& grids, PJ_LP lp) { PJ_LP out = proj_coord_error().lp; - ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); - if (ct == nullptr) { - pj_ctx_set_errno( P->ctx, PJD_ERR_GRID_AREA); + const auto grid = findGrid(grids, lp); + if( !grid ) { + pj_ctx_set_errno( P->ctx, PJD_ERR_GRID_AREA ); return out; } /* normalize input to ll origin */ - lp.lam -= ct->ll.lam; - lp.phi -= ct->ll.phi; + const auto& extent = grid->extentAndRes(); + lp.lam -= extent.westLon; + lp.phi -= extent.southLat; lp.lam = adjlon(lp.lam - M_PI) + M_PI; - out = nad_intr(lp, ct); + out = nad_intr(lp, grid); if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) { pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); @@ -334,33 +289,92 @@ PJ_LP proj_hgrid_value(PJ *P, PJ_LP lp) { return out; } -PJ_LP proj_hgrid_apply(PJ *P, PJ_LP lp, PJ_DIRECTION direction) { - struct CTABLE *ct; - int inverse; +static +PJ_LP proj_hgrid_apply_internal(PJ_CONTEXT* ctx, + PJ_LP lp, + PJ_DIRECTION direction, + const ListOfHGrids& grids) +{ PJ_LP out; - out.lam = HUGE_VAL; out.phi = HUGE_VAL; + out.lam = HUGE_VAL; + out.phi = HUGE_VAL; - ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); - - if (ct == nullptr || ct->cvs == nullptr) { - if( P->gridlist_count == 1 && - strcmp(P->gridlist[0]->gridname, "null") == 0) { - // TODO: remove this particular case that is put there just to be - // able to handle longitudes outside of -180,180 - out = lp; - } else { - pj_ctx_set_errno( P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - } + const auto grid = findGrid(grids, lp); + if( !grid ) { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); return out; } + if( grid->isNullGrid() ) + { + return lp; + } - inverse = direction == PJ_FWD ? 0 : 1; - out = nad_cvt(lp, inverse, ct); + int inverse = direction == PJ_FWD ? 0 : 1; + out = nad_cvt(lp, inverse, grid); if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) - pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); + pj_ctx_set_errno(ctx, PJD_ERR_GRID_AREA); return out; +} + +PJ_LP proj_hgrid_apply(PJ *P, const ListOfHGrids& grids, PJ_LP lp, PJ_DIRECTION direction) { + return proj_hgrid_apply_internal(P->ctx, lp, direction, grids); +} + + +NS_PROJ_END + +/************************************************************************/ +/* pj_apply_gridshift() */ +/* */ +/* This is the externally callable interface - part of the */ +/* public API - though it is not used internally any more and I */ +/* doubt it is used by any other applications. But we preserve */ +/* it to honour our public api. */ +/************************************************************************/ + +int pj_apply_gridshift( projCtx ctx, const char *nadgrids, int inverse, + long point_count, int point_offset, + double *x, double *y, double * /*z */ ) + +{ + auto hgrids = NS_PROJ::getListOfGridSets(ctx, nadgrids); + if( hgrids.empty() ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 1; + } + + + for( long i = 0; i < point_count; i++ ) + { + PJ_LP input; + + long io = i * point_offset; + input.phi = y[io]; + input.lam = x[io]; + + auto output = proj_hgrid_apply_internal(ctx, input, inverse ? PJ_INV : PJ_FWD, hgrids); + if ( output.lam != HUGE_VAL ) + { + y[io] = output.phi; + x[io] = output.lam; + } + else + { + if( ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "pj_apply_gridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + } + } + } + + return 0; } diff --git a/src/apply_vgridshift.cpp b/src/apply_vgridshift.cpp index daa44858..b0136e5c 100644 --- a/src/apply_vgridshift.cpp +++ b/src/apply_vgridshift.cpp @@ -26,7 +26,9 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PJ_LIB__ +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif #include <assert.h> #include <stdio.h> @@ -34,273 +36,117 @@ #include <math.h> #include "proj_internal.h" +#include "proj/internal/internal.hpp" +#include "grids.hpp" -static int is_nodata(float value, double vmultiplier) -{ - /* nodata? */ - /* GTX official nodata value if -88.88880f, but some grids also */ - /* use other big values for nodata (e.g naptrans2008.gtx has */ - /* nodata values like -2147479936), so test them too */ - return value * vmultiplier > 1000 || value * vmultiplier < -1000 || value == -88.88880f; -} +NS_PROJ_START + +static double read_vgrid_value(const ListOfVGrids& grids, PJ_LP input, double vmultiplier) { -static double read_vgrid_value( PJ *defn, PJ_LP input, double vmultiplier, int *gridlist_count_p, PJ_GRIDINFO **tables, struct CTABLE *ct) { - int itable = 0; - double value = HUGE_VAL; - double grid_x, grid_y; - long grid_ix, grid_iy; - long grid_ix2, grid_iy2; - float *cvs; /* do not deal with NaN coordinates */ /* cppcheck-suppress duplicateExpression */ if( isnan(input.phi) || isnan(input.lam) ) - itable = *gridlist_count_p; - - /* keep trying till we find a table that works */ - for ( ; itable < *gridlist_count_p; itable++ ) { - PJ_GRIDINFO *gi = tables[itable]; - - ct = gi->ct; - - /* skip tables that don't match our point at all (latitude check). */ - if( ct->ll.phi > input.phi - || ct->ll.phi + (ct->lim.phi-1) * ct->del.phi < input.phi ) - continue; - - bool fullWorldLongExtent = false; - if( fabs(ct->lim.lam * ct->del.lam - 2 * M_PI) < 1e-10 ) - { - fullWorldLongExtent = true; - } - - /* skip tables that don't match our point at all (longitude check). */ - else if( ct->ll.lam > input.lam - || ct->ll.lam + (ct->lim.lam-1) * ct->del.lam < input.lam ) - continue; - - /* If we have child nodes, check to see if any of them apply. */ - while( gi->child != nullptr ) - { - PJ_GRIDINFO *child; - - for( child = gi->child; child != nullptr; child = child->next ) - { - struct CTABLE *ct1 = child->ct; - - fullWorldLongExtent = false; - - if( ct1->ll.phi > input.phi - || ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi < input.phi) - continue; - - if( fabs(ct1->lim.lam * ct1->del.lam - 2 * M_PI) < 1e-10 ) - { - fullWorldLongExtent = true; - } - else if( ct1->ll.lam > input.lam - || ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam < input.lam) - continue; - - break; - } - - /* we didn't find a more refined child node to use, so go with current grid */ - if( child == nullptr ) - { - break; - } - - /* Otherwise let's try for childrens children .. */ - gi = child; - ct = child->ct; - } - - /* load the grid shift info if we don't have it. */ - if( ct->cvs == nullptr ) - { - pj_gridinfo_load( pj_get_ctx(defn), gi ); - } - if( ct->cvs == nullptr ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } + return HUGE_VAL; + } - /* Interpolation a location within the grid */ - grid_x = (input.lam - ct->ll.lam) / ct->del.lam; - if( fullWorldLongExtent ) { - // The first fmod goes to ]-lim, lim[ range - // So we add lim again to be in ]0, 2*lim[ and fmod again - grid_x = fmod( - fmod(grid_x + ct->lim.lam, ct->lim.lam) + ct->lim.lam, - ct->lim.lam); - } - grid_y = (input.phi - ct->ll.phi) / ct->del.phi; - grid_ix = lround(floor(grid_x)); - assert(grid_ix >= 0 && grid_ix < ct->lim.lam); - grid_iy = lround(floor(grid_y)); - assert(grid_iy >= 0 && grid_iy < ct->lim.phi); - grid_x -= grid_ix; - grid_y -= grid_iy; + const VerticalShiftGrid* grid = nullptr; + for( const auto& gridset: grids ) + { + grid = gridset->gridAt(input.lam, input.phi); + if( grid ) + break; + } + if( !grid ) + { + return HUGE_VAL; + } - grid_ix2 = grid_ix + 1; - if( grid_ix2 >= ct->lim.lam ) { - if( fullWorldLongExtent ) { - grid_ix2 = 0; - } else { - grid_ix2 = ct->lim.lam - 1; - } - } - grid_iy2 = grid_iy + 1; - if( grid_iy2 >= ct->lim.phi ) - grid_iy2 = ct->lim.phi - 1; + const auto& extent = grid->extentAndRes(); - cvs = (float *) ct->cvs; - { - float value_a = cvs[grid_ix + grid_iy * ct->lim.lam]; - float value_b = cvs[grid_ix2 + grid_iy * ct->lim.lam]; - float value_c = cvs[grid_ix + grid_iy2 * ct->lim.lam]; - float value_d = cvs[grid_ix2 + grid_iy2 * ct->lim.lam]; - double total_weight = 0.0; - int n_weights = 0; - value = 0.0f; - if( !is_nodata(value_a, vmultiplier) ) - { - double weight = (1.0-grid_x) * (1.0-grid_y); - value += value_a * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_b, vmultiplier) ) - { - double weight = (grid_x) * (1.0-grid_y); - value += value_b * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_c, vmultiplier) ) - { - double weight = (1.0-grid_x) * (grid_y); - value += value_c * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_d, vmultiplier) ) - { - double weight = (grid_x) * (grid_y); - value += value_d * weight; - total_weight += weight; - n_weights ++; - } - if( n_weights == 0 ) - value = HUGE_VAL; - else if( n_weights != 4 ) - value /= total_weight; + /* Interpolation a location within the grid */ + double grid_x = (input.lam - extent.westLon) / extent.resLon; + if( extent.fullWorldLongitude() ) { + // The first fmod goes to ]-lim, lim[ range + // So we add lim again to be in ]0, 2*lim[ and fmod again + grid_x = fmod( + fmod(grid_x + grid->width(), grid->width()) + grid->width(), + grid->width()); + } + double grid_y = (input.phi - extent.southLat) / extent.resLat; + int grid_ix = static_cast<int>(lround(floor(grid_x))); + assert(grid_ix >= 0 && grid_ix < grid->width()); + int grid_iy = static_cast<int>(lround(floor(grid_y))); + assert(grid_iy >= 0 && grid_iy < grid->height()); + grid_x -= grid_ix; + grid_y -= grid_iy; + + int grid_ix2 = grid_ix + 1; + if( grid_ix2 >= grid->width() ) { + if( extent.fullWorldLongitude() ) { + grid_ix2 = 0; + } else { + grid_ix2 = grid->width() - 1; } - + } + int grid_iy2 = grid_iy + 1; + if( grid_iy2 >= grid->height() ) + grid_iy2 = grid->height() - 1; + + float value_a = 0; + float value_b = 0; + float value_c = 0; + float value_d = 0; + if( !grid->valueAt(grid_ix, grid_iy, value_a) || + !grid->valueAt(grid_ix2, grid_iy, value_b) || + !grid->valueAt(grid_ix, grid_iy2, value_c) || + !grid->valueAt(grid_ix2, grid_iy2, value_d) ) + { + return HUGE_VAL; } - return value * vmultiplier; -} - -/************************************************************************/ -/* pj_apply_vgridshift() */ -/* */ -/* This implementation takes uses the gridlist from a coordinate */ -/* system definition. If the gridlist has not yet been */ -/* populated in the coordinate system definition we set it up */ -/* now. */ -/************************************************************************/ -int pj_apply_vgridshift( PJ *defn, const char *listname, - PJ_GRIDINFO ***gridlist_p, - int *gridlist_count_p, - int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - static int debug_count = 0; - PJ_GRIDINFO **tables; - struct CTABLE ct; + double total_weight = 0.0; + int n_weights = 0; + double value = 0.0f; - if( *gridlist_p == nullptr ) + if( !grid->isNodata(value_a, vmultiplier) ) { - *gridlist_p = - pj_gridlist_from_nadgrids( pj_get_ctx(defn), - pj_param(defn->ctx,defn->params,listname).s, - gridlist_count_p ); - - if( *gridlist_p == nullptr || *gridlist_count_p == 0 ) - return defn->ctx->last_errno; + double weight = (1.0-grid_x) * (1.0-grid_y); + value += value_a * weight; + total_weight += weight; + n_weights ++; } - - if( *gridlist_count_p == 0 ) + if( !grid->isNodata(value_b, vmultiplier) ) { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID); - return PJD_ERR_FAILED_TO_LOAD_GRID; + double weight = (grid_x) * (1.0-grid_y); + value += value_b * weight; + total_weight += weight; + n_weights ++; } - - tables = *gridlist_p; - defn->ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) + if( !grid->isNodata(value_c, vmultiplier) ) { - double value; - long io = i * point_offset; - PJ_LP input; - - input.phi = y[io]; - input.lam = x[io]; - - value = read_vgrid_value(defn, input, 1.0, gridlist_count_p, tables, &ct); - - if( inverse ) - z[io] -= value; - else - z[io] += value; - if( value != HUGE_VAL ) - { - if( debug_count++ < 20 ) { - proj_log_trace(defn, "pj_apply_gridshift(): used %s", ct.id); - break; - } - } - - if( value == HUGE_VAL ) - { - int itable; - std::string gridlist; - - proj_log_debug(defn, - "pj_apply_vgridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - - for( itable = 0; itable < *gridlist_count_p; itable++ ) - { - PJ_GRIDINFO *gi = tables[itable]; - if( itable == 0 ) - gridlist += " tried: "; - else - gridlist += ','; - gridlist += gi->gridname; - } - - proj_log_debug(defn, "%s", gridlist.c_str()); - pj_ctx_set_errno( defn->ctx, PJD_ERR_GRID_AREA ); - - return PJD_ERR_GRID_AREA; - } + double weight = (1.0-grid_x) * (grid_y); + value += value_c * weight; + total_weight += weight; + n_weights ++; } + if( !grid->isNodata(value_d, vmultiplier) ) + { + double weight = (grid_x) * (grid_y); + value += value_d * weight; + total_weight += weight; + n_weights ++; + } + if( n_weights == 0 ) + value = HUGE_VAL; + else if( n_weights != 4 ) + value /= total_weight; - return 0; + return value * vmultiplier; } /**********************************************/ -int proj_vgrid_init(PJ* P, const char *grids) { +ListOfVGrids proj_vgrid_init(PJ* P, const char *gridkey) { /********************************************** Initizalize and populate gridlist. @@ -314,33 +160,43 @@ int proj_vgrid_init(PJ* P, const char *grids) { ***********************************************/ - /* prepend "s" to the "grids" string to allow usage with pj_param */ - char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); - sprintf(sgrids, "%s%s", "s", grids); + std::string key("s"); + key += gridkey; + const char* gridnames = pj_param(P->ctx, P->params, key.c_str()).s; + if( gridnames == nullptr ) + return {}; - if (P->vgridlist_geoid == nullptr) { - P->vgridlist_geoid = pj_gridlist_from_nadgrids( - P->ctx, - pj_param(P->ctx, P->params, sgrids).s, - &(P->vgridlist_geoid_count) - ); - - if( P->vgridlist_geoid == nullptr || P->vgridlist_geoid_count == 0 ) { - pj_dealloc(sgrids); - return 0; + auto listOfGridNames = internal::split(std::string(gridnames), ','); + ListOfVGrids grids; + for( const auto& gridnameStr: listOfGridNames ) + { + const char* gridname = gridnameStr.c_str(); + bool canFail = false; + if( gridname[0] == '@' ) + { + canFail = true; + gridname ++; + } + auto gridSet = VerticalShiftGridSet::open(P->ctx, gridname); + if( !gridSet ) + { + if( !canFail ) + { + pj_ctx_set_errno( P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return {}; + } + } + else + { + grids.emplace_back(std::move(gridSet)); } } - if (P->vgridlist_geoid_count == 0) { - proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - pj_dealloc(sgrids); - return P->vgridlist_geoid_count; + return grids; } /***********************************************/ -double proj_vgrid_value(PJ *P, PJ_LP lp, double vmultiplier){ +double proj_vgrid_value(PJ *P, const ListOfVGrids& grids, PJ_LP lp, double vmultiplier){ /*********************************************** Read grid value at position lp in grids loaded @@ -350,12 +206,12 @@ double proj_vgrid_value(PJ *P, PJ_LP lp, double vmultiplier){ ************************************************/ - struct CTABLE used_grid; double value; - memset(&used_grid, 0, sizeof(struct CTABLE)); - value = read_vgrid_value(P, lp, vmultiplier, &(P->vgridlist_geoid_count), P->vgridlist_geoid, &used_grid); + value = read_vgrid_value(grids, lp, vmultiplier); proj_log_trace(P, "proj_vgrid_value: (%f, %f) = %f", lp.lam*RAD_TO_DEG, lp.phi*RAD_TO_DEG, value); return value; } + +NS_PROJ_END diff --git a/src/datum_set.cpp b/src/datum_set.cpp index 873d7be5..15d51613 100644 --- a/src/datum_set.cpp +++ b/src/datum_set.cpp @@ -41,7 +41,7 @@ int pj_datum_set(projCtx ctx, paralist *pl, PJ *projdef) { - const char *name, *towgs84, *nadgrids, *catalog; + const char *name, *towgs84, *nadgrids; projdef->datum_type = PJD_UNKNOWN; @@ -118,25 +118,6 @@ int pj_datum_set(projCtx ctx, paralist *pl, PJ *projdef) } /* -------------------------------------------------------------------- */ -/* Check for grid catalog parameter, and optional date. */ -/* -------------------------------------------------------------------- */ - else if( (catalog = pj_param(ctx, pl,"scatalog").s) != nullptr ) - { - const char *date; - - projdef->datum_type = PJD_GRIDSHIFT; - projdef->catalog_name = pj_strdup(catalog); - if (!projdef->catalog_name) { - pj_ctx_set_errno(ctx, ENOMEM); - return 1; - } - - date = pj_param(ctx, pl, "sdate").s; - if( date != nullptr) - projdef->datum_date = pj_gc_parsedate( ctx, date); - } - -/* -------------------------------------------------------------------- */ /* Check for towgs84 parameter. */ /* -------------------------------------------------------------------- */ else if( (towgs84 = pj_param(ctx, pl,"stowgs84").s) != nullptr ) diff --git a/src/gc_reader.cpp b/src/gc_reader.cpp deleted file mode 100644 index def52a11..00000000 --- a/src/gc_reader.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code to read a grid catalog from a .cvs file. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2012, Frank Warmerdam <warmerdam@pobox.com> - * - * 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_LIB__ - -#include <ctype.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "proj.h" -#include "proj_internal.h" - -static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry); - -/************************************************************************/ -/* pj_gc_readcatalog() */ -/* */ -/* Read a grid catalog from a .csv file. */ -/************************************************************************/ - -PJ_GridCatalog *pj_gc_readcatalog( projCtx ctx, const char *catalog_name ) -{ - PAFile fid; - PJ_GridCatalog *catalog; - int entry_max; - char line[302]; - - fid = pj_open_lib( ctx, catalog_name, "r" ); - if (fid == nullptr) - return nullptr; - - /* discard title line */ - pj_ctx_fgets(ctx, line, sizeof(line)-1, fid); - - catalog = (PJ_GridCatalog *) calloc(1,sizeof(PJ_GridCatalog)); - if( !catalog ) - { - pj_ctx_set_errno(ctx, ENOMEM); - pj_ctx_fclose(ctx, fid); - return nullptr; - } - - catalog->catalog_name = pj_strdup(catalog_name); - if (!catalog->catalog_name) { - pj_ctx_set_errno(ctx, ENOMEM); - free(catalog); - pj_ctx_fclose(ctx, fid); - return nullptr; - } - - entry_max = 10; - catalog->entries = (PJ_GridCatalogEntry *) - malloc(entry_max * sizeof(PJ_GridCatalogEntry)); - if (!catalog->entries) { - pj_ctx_set_errno(ctx, ENOMEM); - free(catalog->catalog_name); - free(catalog); - pj_ctx_fclose(ctx, fid); - return nullptr; - } - - while( gc_readentry( ctx, fid, - catalog->entries+catalog->entry_count) == 0) - { - catalog->entry_count++; - - if( catalog->entry_count == entry_max ) - { - PJ_GridCatalogEntry* new_entries; - entry_max = entry_max * 2; - new_entries = (PJ_GridCatalogEntry *) - realloc(catalog->entries, - entry_max * sizeof(PJ_GridCatalogEntry)); - if (new_entries == nullptr ) - { - int i; - for( i = 0; i < catalog->entry_count; i++ ) - free( catalog->entries[i].definition ); - free( catalog->entries ); - free( catalog->catalog_name ); - free( catalog ); - pj_ctx_fclose(ctx, fid); - return nullptr; - } - catalog->entries = new_entries; - } - } - - pj_ctx_fclose(ctx, fid); - - return catalog; -} - -/************************************************************************/ -/* gc_read_csv_line() */ -/* */ -/* Simple csv line splitter with fixed maximum line size and */ -/* token count. */ -/************************************************************************/ - -static int gc_read_csv_line( projCtx ctx, PAFile fid, - char **tokens, int max_tokens ) -{ - char line[302]; - - while( pj_ctx_fgets(ctx, line, sizeof(line)-1, fid) != nullptr ) - { - char *next = line; - int token_count = 0; - - while( isspace(*next) ) - next++; - - /* skip blank and comment lines */ - if( next[0] == '#' || next[0] == '\0' ) - continue; - - while( token_count < max_tokens && *next != '\0' ) - { - const char *start = next; - char* token; - - while( *next != '\0' && *next != ',' ) - next++; - - if( *next == ',' ) - { - *next = '\0'; - next++; - } - - token = pj_strdup(start); - if (!token) { - while (token_count > 0) - free(tokens[--token_count]); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - tokens[token_count++] = token; - } - - return token_count; - } - - return 0; -} - -/************************************************************************/ -/* pj_gc_parsedate() */ -/* */ -/* Parse a date into a floating point year value. Acceptable */ -/* values are "yyyy.fraction" and "yyyy-mm-dd". Anything else */ -/* returns 0.0. */ -/************************************************************************/ - -double pj_gc_parsedate( projCtx ctx, const char *date_string ) -{ - (void) ctx; - - if( strlen(date_string) == 10 - && date_string[4] == '-' && date_string[7] == '-' ) - { - int year = atoi(date_string); - int month = atoi(date_string+5); - int day = atoi(date_string+8); - - /* simplified calculation so we don't need to know all about months */ - return year + ((month-1) * 31 + (day-1)) / 372.0; - } - else - { - return pj_atof(date_string); - } -} - - -/************************************************************************/ -/* gc_readentry() */ -/* */ -/* Read one catalog entry from the file */ -/* */ -/* Format: */ -/* gridname,ll_long,ll_lat,ur_long,ur_lat,priority,date */ -/************************************************************************/ - -static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry) -{ -#define MAX_TOKENS 30 - char *tokens[MAX_TOKENS]; - int token_count, i; - int error = 0; - - memset( entry, 0, sizeof(PJ_GridCatalogEntry) ); - - token_count = gc_read_csv_line( ctx, fid, tokens, MAX_TOKENS ); - if( token_count < 5 ) - { - error = 1; /* TODO: need real error codes */ - if( token_count != 0 ) - pj_log( ctx, PJ_LOG_ERROR, "Short line in grid catalog." ); - } - else - { - entry->definition = tokens[0]; - tokens[0] = nullptr; /* We take ownership of tokens[0] */ - entry->region.ll_long = dmstor_ctx( ctx, tokens[1], nullptr ); - entry->region.ll_lat = dmstor_ctx( ctx, tokens[2], nullptr ); - entry->region.ur_long = dmstor_ctx( ctx, tokens[3], nullptr ); - entry->region.ur_lat = dmstor_ctx( ctx, tokens[4], nullptr ); - if( token_count > 5 ) - entry->priority = atoi( tokens[5] ); /* defaults to zero */ - if( token_count > 6 ) - entry->date = pj_gc_parsedate( ctx, tokens[6] ); - } - - for( i = 0; i < token_count; i++ ) - free( tokens[i] ); - - return error; -} - - - diff --git a/src/gridcatalog.cpp b/src/gridcatalog.cpp deleted file mode 100644 index 15d81dd7..00000000 --- a/src/gridcatalog.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code in support of grid catalogs - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2012, Frank Warmerdam <warmerdam@pobox.com> - * - * 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_LIB__ - -#include <assert.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> - -#include "proj.h" -#include "proj_internal.h" - -static PJ_GridCatalog *grid_catalog_list = nullptr; - -static -PJ_GRIDINFO *pj_gc_findgrid( projCtx_t *ctx, - PJ_GridCatalog *catalog, int after, - PJ_LP location, double date, - PJ_Region *optional_region, - double *grid_date ); - -/************************************************************************/ -/* pj_gc_unloadall() */ -/* */ -/* Deallocate all the grid catalogs (but not the referenced */ -/* grids). */ -/************************************************************************/ - -void pj_gc_unloadall( projCtx ctx ) -{ - (void) ctx; - - while( grid_catalog_list != nullptr ) - { - int i; - PJ_GridCatalog *catalog = grid_catalog_list; - grid_catalog_list = grid_catalog_list->next; - - for( i = 0; i < catalog->entry_count; i++ ) - { - /* we don't own gridinfo - do not free here */ - free( catalog->entries[i].definition ); - } - free( catalog->entries ); - free( catalog->catalog_name ); - free( catalog ); - } -} - -/************************************************************************/ -/* pj_gc_findcatalog() */ -/************************************************************************/ - -PJ_GridCatalog *pj_gc_findcatalog( projCtx ctx, const char *name ) - -{ - PJ_GridCatalog *catalog; - - pj_acquire_lock(); - - for( catalog=grid_catalog_list; catalog != nullptr; catalog = catalog->next ) - { - if( strcmp(catalog->catalog_name, name) == 0 ) - { - pj_release_lock(); - return catalog; - } - } - - pj_release_lock(); - - catalog = pj_gc_readcatalog( ctx, name ); - if( catalog == nullptr ) - return nullptr; - - pj_acquire_lock(); - catalog->next = grid_catalog_list; - grid_catalog_list = catalog; - pj_release_lock(); - - return catalog; -} - -/************************************************************************/ -/* pj_gc_apply_gridshift() */ -/************************************************************************/ - -int pj_gc_apply_gridshift( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - (void) z; - - if( defn->catalog == nullptr ) - { - defn->catalog = pj_gc_findcatalog( defn->ctx, defn->catalog_name ); - if( defn->catalog == nullptr ) - return defn->ctx->last_errno; - } - - defn->ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - PJ_LP input, output_after, output_before; - double mix_ratio; - PJ_GRIDINFO *gi; - - input.phi = y[io]; - input.lam = x[io]; - - /* make sure we have appropriate "after" shift file available */ - if( defn->last_after_grid == nullptr - || input.lam < defn->last_after_region.ll_long - || input.lam > defn->last_after_region.ur_long - || input.phi < defn->last_after_region.ll_lat - || input.phi > defn->last_after_region.ll_lat ) { - defn->last_after_grid = - pj_gc_findgrid( defn->ctx, defn->catalog, - 1, input, defn->datum_date, - &(defn->last_after_region), - &(defn->last_after_date)); - if( defn->last_after_grid == nullptr ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - } - gi = defn->last_after_grid; - assert( gi->child == nullptr ); - - /* load the grid shift info if we don't have it. */ - if( gi->ct->cvs == nullptr && !pj_gridinfo_load( defn->ctx, gi ) ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - output_after = nad_cvt( input, inverse, gi->ct ); - if( output_after.lam == HUGE_VAL ) - { - if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) - { - pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - } - continue; - } - - if( defn->datum_date == 0.0 ) - { - y[io] = output_after.phi; - x[io] = output_after.lam; - continue; - } - - /* make sure we have appropriate "before" shift file available */ - if( defn->last_before_grid == nullptr - || input.lam < defn->last_before_region.ll_long - || input.lam > defn->last_before_region.ur_long - || input.phi < defn->last_before_region.ll_lat - || input.phi > defn->last_before_region.ll_lat ) { - defn->last_before_grid = - pj_gc_findgrid( defn->ctx, defn->catalog, - 0, input, defn->datum_date, - &(defn->last_before_region), - &(defn->last_before_date)); - if( defn->last_before_grid == nullptr ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - } - - gi = defn->last_before_grid; - assert( gi->child == nullptr ); - - /* load the grid shift info if we don't have it. */ - if( gi->ct->cvs == nullptr && !pj_gridinfo_load( defn->ctx, gi ) ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - output_before = nad_cvt( input, inverse, gi->ct ); - if( output_before.lam == HUGE_VAL ) - { - if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) - { - pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - } - continue; - } - - mix_ratio = (defn->datum_date - defn->last_before_date) - / (defn->last_after_date - defn->last_before_date); - - y[io] = mix_ratio * output_after.phi - + (1.0-mix_ratio) * output_before.phi; - x[io] = mix_ratio * output_after.lam - + (1.0-mix_ratio) * output_before.lam; - } - - return 0; -} - -/************************************************************************/ -/* pj_c_findgrid() */ -/************************************************************************/ - -static -PJ_GRIDINFO *pj_gc_findgrid( projCtx ctx, PJ_GridCatalog *catalog, int after, - PJ_LP location, double date, - PJ_Region *optional_region, - double *grid_date ) -{ - int iEntry; - PJ_GridCatalogEntry *entry = nullptr; - - for( iEntry = 0; iEntry < catalog->entry_count; iEntry++ ) - { - entry = catalog->entries + iEntry; - - if( (after && entry->date < date) - || (!after && entry->date > date) ) - continue; - - if( location.lam < entry->region.ll_long - || location.lam > entry->region.ur_long - || location.phi < entry->region.ll_lat - || location.phi > entry->region.ur_lat ) - continue; - - if( entry->available == -1 ) - continue; - - break; - } - - if( entry == nullptr ) - { - if( grid_date ) - *grid_date = 0.0; - if( optional_region != nullptr ) - memset( optional_region, 0, sizeof(PJ_Region)); - return nullptr; - } - - if( grid_date ) - *grid_date = entry->date; - - if( optional_region ) - { - - } - - if( entry->gridinfo == nullptr ) - { - PJ_GRIDINFO **gridlist = nullptr; - int grid_count = 0; - gridlist = pj_gridlist_from_nadgrids( ctx, entry->definition, - &grid_count); - // FIXME: this leaks gridlist itself, and memory ownership of - // entry->gridinfo is also confusing. Coverity CID 193539 - if( grid_count == 1 ) - entry->gridinfo = gridlist[0]; - } - - return entry->gridinfo; -} - diff --git a/src/gridinfo.cpp b/src/gridinfo.cpp deleted file mode 100644 index 7f7d930f..00000000 --- a/src/gridinfo.cpp +++ /dev/null @@ -1,988 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Functions for handling individual PJ_GRIDINFO's. Includes - * loaders for all formats but CTABLE (in nad_init.c). - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com> - * - * 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_LIB__ - -#include <errno.h> -#include <math.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "proj_internal.h" - -/************************************************************************/ -/* swap_words() */ -/* */ -/* Convert the byte order of the given word(s) in place. */ -/************************************************************************/ - -static const int byte_order_test = 1; -#define IS_LSB (1 == ((const unsigned char *) (&byte_order_test))[0]) - -static void swap_words( unsigned char *data, int word_size, int word_count ) - -{ - int word; - - for( word = 0; word < word_count; word++ ) - { - int i; - - for( i = 0; i < word_size/2; i++ ) - { - unsigned char t; - - t = data[i]; - data[i] = data[word_size-i-1]; - data[word_size-i-1] = t; - } - - data += word_size; - } -} - -/************************************************************************/ -/* to_double() */ -/* */ -/* Returns a double from the pointed data. */ -/************************************************************************/ - -static double to_double( unsigned char* data ) -{ - double d; - memcpy(&d, data, sizeof(d)); - return d; -} - -/************************************************************************/ -/* pj_gridinfo_free() */ -/************************************************************************/ - -void pj_gridinfo_free( projCtx ctx, PJ_GRIDINFO *gi ) - -{ - if( gi == nullptr ) - return; - - if( gi->child != nullptr ) - { - PJ_GRIDINFO *child, *next; - - for( child = gi->child; child != nullptr; child=next) - { - next=child->next; - pj_gridinfo_free( ctx, child ); - } - } - - if( gi->ct != nullptr ) - nad_free( gi->ct ); - - free( gi->gridname ); - if( gi->filename != nullptr ) - free( gi->filename ); - - pj_dalloc( gi ); -} - -/************************************************************************/ -/* pj_gridinfo_load() */ -/* */ -/* This function is intended to implement delayed loading of */ -/* the data contents of a grid file. The header and related */ -/* stuff are loaded by pj_gridinfo_init(). */ -/************************************************************************/ - -int pj_gridinfo_load( projCtx_t* ctx, PJ_GRIDINFO *gi ) - -{ - struct CTABLE ct_tmp; - - if( gi == nullptr || gi->ct == nullptr ) - return 0; - - pj_acquire_lock(); - if( gi->ct->cvs != nullptr ) - { - pj_release_lock(); - return 1; - } - - memcpy(&ct_tmp, gi->ct, sizeof(struct CTABLE)); - -/* -------------------------------------------------------------------- */ -/* Original platform specific CTable format. */ -/* -------------------------------------------------------------------- */ - if( strcmp(gi->format,"ctable") == 0 ) - { - PAFile fid; - int result; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == nullptr ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - result = nad_ctable_load( ctx, &ct_tmp, (struct projFileAPI_t*)fid ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - - return result; - } - -/* -------------------------------------------------------------------- */ -/* CTable2 format. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ctable2") == 0 ) - { - PAFile fid; - int result; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == nullptr ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - result = nad_ctable2_load( ctx, &ct_tmp, (struct projFileAPI_t*)fid ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - - pj_release_lock(); - return result; - } - -/* -------------------------------------------------------------------- */ -/* NTv1 format. */ -/* We process one line at a time. Note that the array storage */ -/* direction (e-w) is different in the NTv1 file and what */ -/* the CTABLE is supposed to have. The phi/lam are also */ -/* reversed, and we have to be aware of byte swapping. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ntv1") == 0 ) - { - double *row_buf; - int row; - PAFile fid; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == nullptr ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - row_buf = (double *) pj_malloc(gi->ct->lim.lam * sizeof(double) * 2); - ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); - if( row_buf == nullptr || ct_tmp.cvs == nullptr ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - for( row = 0; row < gi->ct->lim.phi; row++ ) - { - int i; - FLP *cvs; - double *diff_seconds; - - if( pj_ctx_fread( ctx, row_buf, - sizeof(double), gi->ct->lim.lam * 2, fid ) - != (size_t)( 2 * gi->ct->lim.lam ) ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( IS_LSB ) - swap_words( (unsigned char *) row_buf, 8, gi->ct->lim.lam*2 ); - - /* convert seconds to radians */ - diff_seconds = row_buf; - - for( i = 0; i < gi->ct->lim.lam; i++ ) - { - cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam - + (gi->ct->lim.lam - i - 1); - - cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - } - } - - pj_dalloc( row_buf ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - - return 1; - } - -/* -------------------------------------------------------------------- */ -/* NTv2 format. */ -/* We process one line at a time. Note that the array storage */ -/* direction (e-w) is different in the NTv2 file and what */ -/* the CTABLE is supposed to have. The phi/lam are also */ -/* reversed, and we have to be aware of byte swapping. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ntv2") == 0 ) - { - float *row_buf; - int row; - PAFile fid; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv2 - loading grid %s", gi->ct->id ); - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == nullptr ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - row_buf = (float *) pj_malloc(gi->ct->lim.lam * sizeof(float) * 4); - ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); - if( row_buf == nullptr || ct_tmp.cvs == nullptr ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - for( row = 0; row < gi->ct->lim.phi; row++ ) - { - int i; - FLP *cvs; - float *diff_seconds; - - if( pj_ctx_fread( ctx, row_buf, sizeof(float), - gi->ct->lim.lam*4, fid ) - != (size_t)( 4 * gi->ct->lim.lam ) ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( gi->must_swap ) - swap_words( (unsigned char *) row_buf, 4, - gi->ct->lim.lam*4 ); - - /* convert seconds to radians */ - diff_seconds = row_buf; - - for( i = 0; i < gi->ct->lim.lam; i++ ) - { - cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam - + (gi->ct->lim.lam - i - 1); - - cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - diff_seconds += 2; /* skip accuracy values */ - } - } - - pj_dalloc( row_buf ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - - pj_release_lock(); - return 1; - } - -/* -------------------------------------------------------------------- */ -/* GTX format. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"gtx") == 0 ) - { - int words = gi->ct->lim.lam * gi->ct->lim.phi; - PAFile fid; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == nullptr ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - ct_tmp.cvs = (FLP *) pj_malloc(words*sizeof(float)); - if( ct_tmp.cvs == nullptr ) - { - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - if( pj_ctx_fread( ctx, ct_tmp.cvs, sizeof(float), words, fid ) - != (size_t)words ) - { - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( IS_LSB ) - swap_words( (unsigned char *) ct_tmp.cvs, 4, words ); - - pj_ctx_fclose( ctx, fid ); - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - return 1; - } - - else - { - pj_release_lock(); - return 0; - } -} - -/************************************************************************/ -/* gridinfo_parent() */ -/* */ -/* Seek a parent grid file by name from a grid list */ -/************************************************************************/ - -static PJ_GRIDINFO* gridinfo_parent( PJ_GRIDINFO *gilist, - const char *name, int length ) -{ - while( gilist ) - { - if( strncmp(gilist->ct->id,name,length) == 0 ) return gilist; - if( gilist->child ) - { - PJ_GRIDINFO *parent=gridinfo_parent( gilist->child, name, length ); - if( parent ) return parent; - } - gilist=gilist->next; - } - return gilist; -} - -/************************************************************************/ -/* pj_gridinfo_init_ntv2() */ -/* */ -/* Load a ntv2 (.gsb) file. */ -/************************************************************************/ - -static int pj_gridinfo_init_ntv2( projCtx ctx, PAFile fid, PJ_GRIDINFO *gilist ) -{ - unsigned char header[11*16]; - int num_subfiles, subfile; - int must_swap; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the overview header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( header[8] == 11 ) - must_swap = !IS_LSB; - else - must_swap = IS_LSB; - -/* -------------------------------------------------------------------- */ -/* Byte swap interesting fields if needed. */ -/* -------------------------------------------------------------------- */ - if( must_swap ) - { - swap_words( header+8, 4, 1 ); - swap_words( header+8+16, 4, 1 ); - swap_words( header+8+32, 4, 1 ); - swap_words( header+8+7*16, 8, 1 ); - swap_words( header+8+8*16, 8, 1 ); - swap_words( header+8+9*16, 8, 1 ); - swap_words( header+8+10*16, 8, 1 ); - } - -/* -------------------------------------------------------------------- */ -/* Get the subfile count out ... all we really use for now. */ -/* -------------------------------------------------------------------- */ - memcpy( &num_subfiles, header+8+32, 4 ); - -/* ==================================================================== */ -/* Step through the subfiles, creating a PJ_GRIDINFO for each. */ -/* ==================================================================== */ - for( subfile = 0; subfile < num_subfiles; subfile++ ) - { - struct CTABLE *ct; - PJ_LP ur; - int gs_count; - PJ_GRIDINFO *gi; - -/* -------------------------------------------------------------------- */ -/* Read header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( strncmp((const char *) header,"SUB_NAME",8) != 0 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Byte swap interesting fields if needed. */ -/* -------------------------------------------------------------------- */ - if( must_swap ) - { - swap_words( header+8+16*4, 8, 1 ); - swap_words( header+8+16*5, 8, 1 ); - swap_words( header+8+16*6, 8, 1 ); - swap_words( header+8+16*7, 8, 1 ); - swap_words( header+8+16*8, 8, 1 ); - swap_words( header+8+16*9, 8, 1 ); - swap_words( header+8+16*10, 4, 1 ); - } - -/* -------------------------------------------------------------------- */ -/* Initialize a corresponding "ct" structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strncpy( ct->id, (const char *) header + 8, 8 ); - ct->id[8] = '\0'; - - ct->ll.lam = - to_double(header+7*16+8); /* W_LONG */ - ct->ll.phi = to_double(header+4*16+8); /* S_LAT */ - - ur.lam = - to_double(header+6*16+8); /* E_LONG */ - ur.phi = to_double(header+5*16+8); /* N_LAT */ - - ct->del.lam = to_double(header+9*16+8); - ct->del.phi = to_double(header+8*16+8); - - ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; - ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam/3600.0, ct->ll.phi/3600.0, - ur.lam/3600.0, ur.phi/3600.0 ); - - ct->ll.lam *= DEG_TO_RAD/3600.0; - ct->ll.phi *= DEG_TO_RAD/3600.0; - ct->del.lam *= DEG_TO_RAD/3600.0; - ct->del.phi *= DEG_TO_RAD/3600.0; - - memcpy( &gs_count, header + 8 + 16*10, 4 ); - if( gs_count != ct->lim.lam * ct->lim.phi ) - { - pj_log( ctx, PJ_LOG_ERROR, - "GS_COUNT(%d) does not match expected cells (%dx%d=%d)", - gs_count, ct->lim.lam, ct->lim.phi, - ct->lim.lam * ct->lim.phi ); - pj_dalloc(ct); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - ct->cvs = nullptr; - -/* -------------------------------------------------------------------- */ -/* Create a new gridinfo for this if we aren't processing the */ -/* 1st subfile, and initialize our grid info. */ -/* -------------------------------------------------------------------- */ - if( subfile == 0 ) - gi = gilist; - else - { - gi = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); - if (!gi) { - pj_dalloc(ct); - pj_gridinfo_free(ctx, gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - - gi->gridname = pj_strdup( gilist->gridname ); - gi->filename = pj_strdup( gilist->filename ); - if (!gi->gridname || !gi->filename) { - pj_gridinfo_free(ctx, gi); - pj_dalloc(ct); - pj_gridinfo_free(ctx, gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - gi->next = nullptr; - } - - gi->must_swap = must_swap; - gi->ct = ct; - gi->format = "ntv2"; - gi->grid_offset = pj_ctx_ftell( ctx, fid ); - -/* -------------------------------------------------------------------- */ -/* Attach to the correct list or sublist. */ -/* -------------------------------------------------------------------- */ - if( strncmp((const char *)header+24,"NONE",4) == 0 ) - { - if( gi != gilist ) - { - PJ_GRIDINFO *lnk; - - for( lnk = gilist; lnk->next != nullptr; lnk = lnk->next ) {} - lnk->next = gi; - } - } - - else - { - PJ_GRIDINFO *lnk; - PJ_GRIDINFO *gp = gridinfo_parent(gilist, - (const char*)header+24,8); - - if( gp == nullptr ) - { - pj_log( ctx, PJ_LOG_ERROR, - "pj_gridinfo_init_ntv2(): " - "failed to find parent %8.8s for %s.", - (const char *) header+24, gi->ct->id ); - - for( lnk = gilist; lnk->next != nullptr; lnk = lnk->next ) {} - lnk->next = gi; - } - else - { - if( gp->child == nullptr ) - { - gp->child = gi; - } - else - { - for( lnk = gp->child; lnk->next != nullptr; lnk = lnk->next ) {} - lnk->next = gi; - } - } - } - -/* -------------------------------------------------------------------- */ -/* Seek past the data. */ -/* -------------------------------------------------------------------- */ - pj_ctx_fseek( ctx, fid, gs_count * 16, SEEK_CUR ); - } - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init_ntv1() */ -/* */ -/* Load an NTv1 style Canadian grid shift file. */ -/************************************************************************/ - -static int pj_gridinfo_init_ntv1( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) - -{ - unsigned char header[192]; /* 12 records of 16 bytes */ - struct CTABLE *ct; - PJ_LP ur; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Regularize fields of interest. */ -/* -------------------------------------------------------------------- */ - if( IS_LSB ) - { - swap_words( header+8, 4, 1 ); - swap_words( header+24, 8, 1 ); - swap_words( header+40, 8, 1 ); - swap_words( header+56, 8, 1 ); - swap_words( header+72, 8, 1 ); - swap_words( header+88, 8, 1 ); - swap_words( header+104, 8, 1 ); - } - - if( *((int *) (header+8)) != 12 ) - { - pj_log( ctx, PJ_LOG_ERROR, - "NTv1 grid shift file has wrong record count, corrupt?" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Fill in CTABLE structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strcpy( ct->id, "NTv1 Grid Shift File" ); - - ct->ll.lam = - to_double(header+72); - ct->ll.phi = to_double(header+24); - ur.lam = - to_double(header+56); - ur.phi = to_double(header+40); - ct->del.lam = to_double(header+104); - ct->del.phi = to_double(header+88); - ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; - ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv1 %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->lim.lam, ct->lim.phi, - ct->ll.lam, ct->ll.phi, ur.lam, ur.phi ); - - ct->ll.lam *= DEG_TO_RAD; - ct->ll.phi *= DEG_TO_RAD; - ct->del.lam *= DEG_TO_RAD; - ct->del.phi *= DEG_TO_RAD; - ct->cvs = nullptr; - - gi->ct = ct; - gi->grid_offset = (long) sizeof(header); - gi->format = "ntv1"; - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init_gtx() */ -/* */ -/* Load a NOAA .gtx vertical datum shift file. */ -/************************************************************************/ - -static int pj_gridinfo_init_gtx( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) - -{ - unsigned char header[40]; - struct CTABLE *ct; - double xorigin,yorigin,xstep,ystep; - int rows, columns; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Regularize fields of interest and extract. */ -/* -------------------------------------------------------------------- */ - if( IS_LSB ) - { - swap_words( header+0, 8, 4 ); - swap_words( header+32, 4, 2 ); - } - - memcpy( &yorigin, header+0, 8 ); - memcpy( &xorigin, header+8, 8 ); - memcpy( &ystep, header+16, 8 ); - memcpy( &xstep, header+24, 8 ); - - memcpy( &rows, header+32, 4 ); - memcpy( &columns, header+36, 4 ); - - if( xorigin < -360 || xorigin > 360 - || yorigin < -90 || yorigin > 90 ) - { - pj_log( ctx, PJ_LOG_ERROR, - "gtx file header has invalid extents, corrupt?"); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Fill in CTABLE structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strcpy( ct->id, "GTX Vertical Grid Shift File" ); - - ct->ll.lam = xorigin; - ct->ll.phi = yorigin; - ct->del.lam = xstep; - ct->del.phi = ystep; - ct->lim.lam = columns; - ct->lim.phi = rows; - - /* some GTX files come in 0-360 and we shift them back into the - expected -180 to 180 range if possible. This does not solve - problems with grids spanning the dateline. */ - if( ct->ll.lam >= 180.0 ) - ct->ll.lam -= 360.0; - - if( ct->ll.lam >= 0.0 && ct->ll.lam + ct->del.lam * ct->lim.lam > 180.0 ) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "This GTX spans the dateline! This will cause problems." ); - } - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "GTX %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->lim.lam, ct->lim.phi, - ct->ll.lam, ct->ll.phi, - ct->ll.lam + (columns-1)*xstep, ct->ll.phi + (rows-1)*ystep); - - ct->ll.lam *= DEG_TO_RAD; - ct->ll.phi *= DEG_TO_RAD; - ct->del.lam *= DEG_TO_RAD; - ct->del.phi *= DEG_TO_RAD; - ct->cvs = nullptr; - - gi->ct = ct; - gi->grid_offset = 40; - gi->format = "gtx"; - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init() */ -/* */ -/* Open and parse header details from a datum gridshift file */ -/* returning a list of PJ_GRIDINFOs for the grids in that */ -/* file. This superceeds use of nad_init() for modern */ -/* applications. */ -/************************************************************************/ - -PJ_GRIDINFO *pj_gridinfo_init( projCtx ctx, const char *gridname ) - -{ - PJ_GRIDINFO *gilist; - PAFile fp; - char header[160]; - size_t header_size = 0; - - errno = pj_errno = 0; - ctx->last_errno = 0; - -/* -------------------------------------------------------------------- */ -/* Initialize a GRIDINFO with stub info we would use if it */ -/* cannot be loaded. */ -/* -------------------------------------------------------------------- */ - gilist = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); - if (!gilist) { - pj_ctx_set_errno(ctx, ENOMEM); - return nullptr; - } - - gilist->gridname = pj_strdup( gridname ); - if (!gilist->gridname) { - pj_dalloc(gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return nullptr; - } - gilist->filename = nullptr; - gilist->format = "missing"; - gilist->grid_offset = 0; - gilist->ct = nullptr; - gilist->next = nullptr; - -/* -------------------------------------------------------------------- */ -/* Open the file using the usual search rules. */ -/* -------------------------------------------------------------------- */ - if (!(fp = pj_open_lib(ctx, gridname, "rb"))) { - ctx->last_errno = 0; /* don't treat as a persistent error */ - return gilist; - } - - gilist->filename = pj_strdup(gridname); - if (!gilist->filename) { - pj_dalloc(gilist->gridname); - pj_dalloc(gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return nullptr; - } - -/* -------------------------------------------------------------------- */ -/* Load a header, to determine the file type. */ -/* -------------------------------------------------------------------- */ - if( (header_size = pj_ctx_fread( ctx, header, 1, - sizeof(header), fp ) ) != sizeof(header) ) - { - /* some files may be smaller that sizeof(header), eg 160, so */ - ctx->last_errno = 0; /* don't treat as a persistent error */ - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "pj_gridinfo_init: short header read of %d bytes", - (int)header_size ); - } - - pj_ctx_fseek( ctx, fp, SEEK_SET, 0 ); - -/* -------------------------------------------------------------------- */ -/* Determine file type. */ -/* -------------------------------------------------------------------- */ - if( header_size >= 144 + 16 - && strncmp(header + 0, "HEADER", 6) == 0 - && strncmp(header + 96, "W GRID", 6) == 0 - && strncmp(header + 144, "TO NAD83 ", 16) == 0 ) - { - pj_gridinfo_init_ntv1( ctx, fp, gilist ); - } - - else if( header_size >= 48 + 7 - && strncmp(header + 0, "NUM_OREC", 8) == 0 - && strncmp(header + 48, "GS_TYPE", 7) == 0 ) - { - pj_gridinfo_init_ntv2( ctx, fp, gilist ); - } - - else if( strlen(gridname) > 4 - && (strcmp(gridname+strlen(gridname)-3,"gtx") == 0 - || strcmp(gridname+strlen(gridname)-3,"GTX") == 0) ) - { - pj_gridinfo_init_gtx( ctx, fp, gilist ); - } - - else if( header_size >= 9 && strncmp(header + 0,"CTABLE V2",9) == 0 ) - { - struct CTABLE *ct = nad_ctable2_init( ctx, (struct projFileAPI_t*)fp ); - - gilist->format = "ctable2"; - gilist->ct = ct; - - if (ct == nullptr) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "CTABLE V2 ct is NULL."); - } - else - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "Ctable2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, - (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, - (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); - } - } - - else - { - struct CTABLE *ct = nad_ctable_init( ctx, (struct projFileAPI_t*)fp ); - if (ct == nullptr) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "CTABLE ct is NULL."); - } else - { - gilist->format = "ctable"; - gilist->ct = ct; - - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "Ctable %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, - (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, - (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); - } - } - - pj_ctx_fclose(ctx, fp); - - return gilist; -} diff --git a/src/gridlist.cpp b/src/gridlist.cpp deleted file mode 100644 index c540b134..00000000 --- a/src/gridlist.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code to manage the list of currently loaded (cached) PJ_GRIDINFOs - * See pj_gridinfo.c for details of loading individual grids. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com> - * - * 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_LIB__ - -#include <errno.h> -#include <stddef.h> -#include <string.h> - -#include "proj.h" -#include "proj_internal.h" - -static PJ_GRIDINFO *grid_list = nullptr; -#define PJ_MAX_PATH_LENGTH 1024 - -/************************************************************************/ -/* pj_deallocate_grids() */ -/* */ -/* Deallocate all loaded grids. */ -/************************************************************************/ - -void pj_deallocate_grids() - -{ - while( grid_list != nullptr ) - { - PJ_GRIDINFO *item = grid_list; - grid_list = grid_list->next; - item->next = nullptr; - - pj_gridinfo_free( pj_get_default_ctx(), item ); - } -} - -/************************************************************************/ -/* pj_gridlist_merge_grid() */ -/* */ -/* Find/load the named gridfile and merge it into the */ -/* last_nadgrids_list. */ -/************************************************************************/ - -static int pj_gridlist_merge_gridfile( projCtx ctx, - const char *gridname, - PJ_GRIDINFO ***p_gridlist, - int *p_gridcount, - int *p_gridmax ) - -{ - int got_match=0; - PJ_GRIDINFO *this_grid, *tail = nullptr; - -/* -------------------------------------------------------------------- */ -/* Try to find in the existing list of loaded grids. Add all */ -/* matching grids as with NTv2 we can get many grids from one */ -/* file (one shared gridname). */ -/* -------------------------------------------------------------------- */ - for( this_grid = grid_list; this_grid != nullptr; this_grid = this_grid->next) - { - if( strcmp(this_grid->gridname,gridname) == 0 ) - { - got_match = 1; - - /* don't add to the list if it is invalid. */ - if( this_grid->ct == nullptr ) - return 0; - - /* do we need to grow the list? */ - if( *p_gridcount >= *p_gridmax - 2 ) - { - PJ_GRIDINFO **new_list; - int new_max = *p_gridmax + 20; - - new_list = (PJ_GRIDINFO **) pj_calloc(new_max, sizeof(void *)); - if (!new_list) { - pj_ctx_set_errno( ctx, ENOMEM ); - return 0; - } - if( *p_gridlist != nullptr ) - { - memcpy( new_list, *p_gridlist, - sizeof(void *) * (*p_gridmax) ); - pj_dalloc( *p_gridlist ); - } - - *p_gridlist = new_list; - *p_gridmax = new_max; - } - - /* add to the list */ - (*p_gridlist)[(*p_gridcount)++] = this_grid; - (*p_gridlist)[*p_gridcount] = nullptr; - } - - tail = this_grid; - } - - if( got_match ) - return 1; - -/* -------------------------------------------------------------------- */ -/* Try to load the named grid. */ -/* -------------------------------------------------------------------- */ - this_grid = pj_gridinfo_init( ctx, gridname ); - - if( this_grid == nullptr ) - { - return 0; - } - - if( tail != nullptr ) - tail->next = this_grid; - else - grid_list = this_grid; - -/* -------------------------------------------------------------------- */ -/* Recurse to add the grid now that it is loaded. */ -/* -------------------------------------------------------------------- */ - return pj_gridlist_merge_gridfile( ctx, gridname, p_gridlist, - p_gridcount, p_gridmax ); -} - -/************************************************************************/ -/* pj_gridlist_from_nadgrids() */ -/* */ -/* This functions loads the list of grids corresponding to a */ -/* particular nadgrids string into a list, and returns it. The */ -/* list is kept around till a request is made with a different */ -/* string in order to cut down on the string parsing cost, and */ -/* the cost of building the list of tables each time. */ -/************************************************************************/ - -PJ_GRIDINFO **pj_gridlist_from_nadgrids( projCtx ctx, const char *nadgrids, - int *grid_count) - -{ - const char *s; - PJ_GRIDINFO **gridlist = nullptr; - int grid_max = 0; - - pj_errno = 0; - *grid_count = 0; - - pj_acquire_lock(); - -/* -------------------------------------------------------------------- */ -/* Loop processing names out of nadgrids one at a time. */ -/* -------------------------------------------------------------------- */ - for( s = nadgrids; *s != '\0'; ) - { - size_t end_char; - int required = 1; - char name[PJ_MAX_PATH_LENGTH]; - - if( *s == '@' ) - { - required = 0; - s++; - } - - for( end_char = 0; - s[end_char] != '\0' && s[end_char] != ','; - end_char++ ) {} - - if( end_char >= sizeof(name) ) - { - pj_dalloc( gridlist ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return nullptr; - } - - strncpy( name, s, end_char ); - name[end_char] = '\0'; - - s += end_char; - if( *s == ',' ) - s++; - - if( !pj_gridlist_merge_gridfile( ctx, name, &gridlist, grid_count, - &grid_max) - && required ) - { - pj_dalloc( gridlist ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return nullptr; - } - else - pj_errno = 0; - } - - pj_release_lock(); - - return gridlist; -} diff --git a/src/grids.cpp b/src/grids.cpp new file mode 100644 index 00000000..7d19b1f7 --- /dev/null +++ b/src/grids.cpp @@ -0,0 +1,895 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: Grid management + * Author: Even Rouault, <even.rouault at spatialys.com> + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam <warmerdam@pobox.com> + * Copyright (c) 2019, Even Rouault, <even.rouault at spatialys.com> + * + * 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. + *****************************************************************************/ + +#ifndef FROM_PROJ_CPP +#define FROM_PROJ_CPP +#endif + +#include "grids.hpp" +#include "proj/internal/internal.hpp" +#include "proj_internal.h" + +NS_PROJ_START + +using namespace internal; + +/************************************************************************/ +/* swap_words() */ +/* */ +/* Convert the byte order of the given word(s) in place. */ +/************************************************************************/ + +static const int byte_order_test = 1; +#define IS_LSB (1 == ((const unsigned char *)(&byte_order_test))[0]) + +static void swap_words(void *dataIn, size_t word_size, size_t word_count) + +{ + unsigned char *data = static_cast<unsigned char *>(dataIn); + for (size_t word = 0; word < word_count; word++) { + for (size_t i = 0; i < word_size / 2; i++) { + unsigned char t; + + t = data[i]; + data[i] = data[word_size - i - 1]; + data[word_size - i - 1] = t; + } + + data += word_size; + } +} + +bool ExtentAndRes::fullWorldLongitude() const { + return eastLon - westLon + resLon >= 2 * M_PI - 1e-10; +} + +// --------------------------------------------------------------------------- + +Grid::Grid(int widthIn, int heightIn, const ExtentAndRes &extentIn) + : m_width(widthIn), m_height(heightIn), m_extent(extentIn) {} + +// --------------------------------------------------------------------------- + +Grid::~Grid() = default; + +// --------------------------------------------------------------------------- + +VerticalShiftGrid::VerticalShiftGrid(int widthIn, int heightIn, + const ExtentAndRes &extentIn) + : Grid(widthIn, heightIn, extentIn) {} + +// --------------------------------------------------------------------------- + +bool VerticalShiftGrid::isNodata(float /*val*/, double /* multiplier */) const { + return false; +} + +// --------------------------------------------------------------------------- + +static ExtentAndRes globalExtent() { + ExtentAndRes extent; + extent.westLon = -M_PI; + extent.southLat = -M_PI / 2; + extent.eastLon = M_PI; + extent.northLat = M_PI / 2; + extent.resLon = M_PI; + extent.resLat = M_PI / 2; + return extent; +} + +// --------------------------------------------------------------------------- + +class NullVerticalShiftGrid : public VerticalShiftGrid { + + public: + NullVerticalShiftGrid() : VerticalShiftGrid(3, 3, globalExtent()) {} + + bool isNullGrid() const override { return true; } + bool valueAt(int, int, float &out) const override; + bool isNodata(float, double) const override { return false; } +}; + +// --------------------------------------------------------------------------- + +bool NullVerticalShiftGrid::valueAt(int, int, float &out) const { + out = 0.0f; + return true; +} + +// --------------------------------------------------------------------------- + +class GTXVerticalShiftGrid : public VerticalShiftGrid { + PJ_CONTEXT *m_ctx; + PAFile m_fp; + + GTXVerticalShiftGrid(const GTXVerticalShiftGrid &) = delete; + GTXVerticalShiftGrid &operator=(const GTXVerticalShiftGrid &) = delete; + + public: + GTXVerticalShiftGrid(PJ_CONTEXT *ctx, PAFile fp, int widthIn, int heightIn, + const ExtentAndRes &extentIn) + : VerticalShiftGrid(widthIn, heightIn, extentIn), m_ctx(ctx), m_fp(fp) { + } + + ~GTXVerticalShiftGrid() override; + + bool valueAt(int x, int y, float &out) const override; + bool isNodata(float val, double multiplier) const override; + + static GTXVerticalShiftGrid *open(PJ_CONTEXT *ctx, PAFile fp); +}; + +// --------------------------------------------------------------------------- + +GTXVerticalShiftGrid::~GTXVerticalShiftGrid() { pj_ctx_fclose(m_ctx, m_fp); } + +// --------------------------------------------------------------------------- + +GTXVerticalShiftGrid *GTXVerticalShiftGrid::open(PJ_CONTEXT *ctx, PAFile fp) { + unsigned char header[40]; + + /* -------------------------------------------------------------------- */ + /* Read the header. */ + /* -------------------------------------------------------------------- */ + if (pj_ctx_fread(ctx, header, sizeof(header), 1, fp) != 1) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + /* -------------------------------------------------------------------- */ + /* Regularize fields of interest and extract. */ + /* -------------------------------------------------------------------- */ + if (IS_LSB) { + swap_words(header + 0, 8, 4); + swap_words(header + 32, 4, 2); + } + + double xorigin, yorigin, xstep, ystep; + int rows, columns; + + memcpy(&yorigin, header + 0, 8); + memcpy(&xorigin, header + 8, 8); + memcpy(&ystep, header + 16, 8); + memcpy(&xstep, header + 24, 8); + + memcpy(&rows, header + 32, 4); + memcpy(&columns, header + 36, 4); + + if (xorigin < -360 || xorigin > 360 || yorigin < -90 || yorigin > 90) { + pj_log(ctx, PJ_LOG_ERROR, + "gtx file header has invalid extents, corrupt?"); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + /* some GTX files come in 0-360 and we shift them back into the + expected -180 to 180 range if possible. This does not solve + problems with grids spanning the dateline. */ + if (xorigin >= 180.0) + xorigin -= 360.0; + + if (xorigin >= 0.0 && xorigin + xstep * columns > 180.0) { + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, + "This GTX spans the dateline! This will cause problems."); + } + + ExtentAndRes extent; + extent.westLon = xorigin * DEG_TO_RAD; + extent.southLat = yorigin * DEG_TO_RAD; + extent.resLon = xstep * DEG_TO_RAD; + extent.resLat = ystep * DEG_TO_RAD; + extent.eastLon = (xorigin + xstep * (columns - 1)) * DEG_TO_RAD; + extent.northLat = (yorigin + ystep * (rows - 1)) * DEG_TO_RAD; + + return new GTXVerticalShiftGrid(ctx, fp, columns, rows, extent); +} + +// --------------------------------------------------------------------------- + +bool GTXVerticalShiftGrid::valueAt(int x, int y, float &out) const { + assert(x >= 0 && y >= 0 && x < m_width && y < m_height); + + pj_ctx_fseek(m_ctx, m_fp, 40 + sizeof(float) * (y * m_width + x), SEEK_SET); + if (pj_ctx_fread(m_ctx, &out, sizeof(out), 1, m_fp) != 1) { + pj_ctx_set_errno(m_ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return false; + } + if (IS_LSB) { + swap_words(&out, sizeof(float), 1); + } + return true; +} + +// --------------------------------------------------------------------------- + +bool GTXVerticalShiftGrid::isNodata(float val, double multiplier) const { + /* nodata? */ + /* GTX official nodata value if -88.88880f, but some grids also */ + /* use other big values for nodata (e.g naptrans2008.gtx has */ + /* nodata values like -2147479936), so test them too */ + return val * multiplier > 1000 || val * multiplier < -1000 || + val == -88.88880f; +} + +// --------------------------------------------------------------------------- + +VerticalShiftGridSet::VerticalShiftGridSet() = default; + +// --------------------------------------------------------------------------- + +VerticalShiftGridSet::~VerticalShiftGridSet() = default; + +// --------------------------------------------------------------------------- + +std::unique_ptr<VerticalShiftGridSet> +VerticalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) { + if (filename == "null") { + auto set = + std::unique_ptr<VerticalShiftGridSet>(new VerticalShiftGridSet()); + set->m_name = filename; + set->m_format = "null"; + set->m_grids.push_back(std::unique_ptr<NullVerticalShiftGrid>( + new NullVerticalShiftGrid())); + return set; + } + + PAFile fp; + if (!(fp = pj_open_lib(ctx, filename.c_str(), "rb"))) { + ctx->last_errno = 0; /* don't treat as a persistent error */ + return nullptr; + } + if (ends_with(filename, "gtx") || ends_with(filename, "GTX")) { + auto grid = GTXVerticalShiftGrid::open(ctx, fp); + if (!grid) { + pj_ctx_fclose(ctx, fp); + return nullptr; + } + auto set = + std::unique_ptr<VerticalShiftGridSet>(new VerticalShiftGridSet()); + set->m_name = filename; + set->m_format = "gtx"; + set->m_grids.push_back(std::unique_ptr<VerticalShiftGrid>(grid)); + return set; + } + + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Unrecognized vertical grid format"); + pj_ctx_fclose(ctx, fp); + return nullptr; +} + +// --------------------------------------------------------------------------- + +const VerticalShiftGrid *VerticalShiftGrid::gridAt(double lon, + double lat) const { + for (const auto &child : m_children) { + const auto &extentChild = child->extentAndRes(); + if ((extentChild.fullWorldLongitude() || + (lon >= extentChild.westLon && lon <= extentChild.eastLon)) && + lat >= extentChild.southLat && lat <= extentChild.northLat) { + return child->gridAt(lon, lat); + } + } + return this; +} +// --------------------------------------------------------------------------- + +const VerticalShiftGrid *VerticalShiftGridSet::gridAt(double lon, + double lat) const { + for (const auto &grid : m_grids) { + if (dynamic_cast<NullVerticalShiftGrid *>(grid.get())) { + return grid.get(); + } + const auto &extent = grid->extentAndRes(); + if ((extent.fullWorldLongitude() || + (lon >= extent.westLon && lon <= extent.eastLon)) && + lat >= extent.southLat && lat <= extent.northLat) { + return grid->gridAt(lon, lat); + } + } + return nullptr; +} + +// --------------------------------------------------------------------------- + +HorizontalShiftGrid::HorizontalShiftGrid(int widthIn, int heightIn, + const ExtentAndRes &extentIn) + : Grid(widthIn, heightIn, extentIn) {} + +// --------------------------------------------------------------------------- + +HorizontalShiftGrid::~HorizontalShiftGrid() = default; + +// --------------------------------------------------------------------------- + +HorizontalShiftGridSet::HorizontalShiftGridSet() = default; + +// --------------------------------------------------------------------------- + +HorizontalShiftGridSet::~HorizontalShiftGridSet() = default; + +// --------------------------------------------------------------------------- + +class NullHorizontalShiftGrid : public HorizontalShiftGrid { + + public: + NullHorizontalShiftGrid() : HorizontalShiftGrid(3, 3, globalExtent()) {} + + bool isNullGrid() const override { return true; } + + bool valueAt(int, int, float &lonShift, float &latShift) const override; +}; + +// --------------------------------------------------------------------------- + +bool NullHorizontalShiftGrid::valueAt(int, int, float &lonShift, + float &latShift) const { + lonShift = 0.0f; + latShift = 0.0f; + return true; +} + +// --------------------------------------------------------------------------- + +static double to_double(const void *data) { + double d; + memcpy(&d, data, sizeof(d)); + return d; +} + +// --------------------------------------------------------------------------- + +class NTv1Grid : public HorizontalShiftGrid { + PJ_CONTEXT *m_ctx; + PAFile m_fp; + + NTv1Grid(const NTv1Grid &) = delete; + NTv1Grid &operator=(const NTv1Grid &) = delete; + + public: + NTv1Grid(PJ_CONTEXT *ctx, PAFile fp, int widthIn, int heightIn, + const ExtentAndRes &extentIn) + : HorizontalShiftGrid(widthIn, heightIn, extentIn), m_ctx(ctx), + m_fp(fp) {} + + ~NTv1Grid() override; + + bool valueAt(int, int, float &lonShift, float &latShift) const override; + + static NTv1Grid *open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename); +}; + +// --------------------------------------------------------------------------- + +NTv1Grid::~NTv1Grid() { pj_ctx_fclose(m_ctx, m_fp); } + +// --------------------------------------------------------------------------- + +NTv1Grid *NTv1Grid::open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename) { + unsigned char header[192]; + + /* -------------------------------------------------------------------- */ + /* Read the header. */ + /* -------------------------------------------------------------------- */ + if (pj_ctx_fread(ctx, header, sizeof(header), 1, fp) != 1) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + /* -------------------------------------------------------------------- */ + /* Regularize fields of interest. */ + /* -------------------------------------------------------------------- */ + if (IS_LSB) { + swap_words(header + 8, sizeof(int), 1); + swap_words(header + 24, sizeof(double), 1); + swap_words(header + 40, sizeof(double), 1); + swap_words(header + 56, sizeof(double), 1); + swap_words(header + 72, sizeof(double), 1); + swap_words(header + 88, sizeof(double), 1); + swap_words(header + 104, sizeof(double), 1); + } + + if (*((int *)(header + 8)) != 12) { + pj_log(ctx, PJ_LOG_ERROR, + "NTv1 grid shift file has wrong record count, corrupt?"); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + ExtentAndRes extent; + extent.westLon = -to_double(header + 72) * DEG_TO_RAD; + extent.southLat = to_double(header + 24) * DEG_TO_RAD; + extent.eastLon = -to_double(header + 56) * DEG_TO_RAD; + extent.northLat = to_double(header + 40) * DEG_TO_RAD; + extent.resLon = to_double(header + 104) * DEG_TO_RAD; + extent.resLat = to_double(header + 88) * DEG_TO_RAD; + if (!(fabs(extent.westLon) <= 4 * M_PI && + fabs(extent.eastLon) <= 4 * M_PI && + fabs(extent.northLat) <= M_PI + 1e-5 && + fabs(extent.southLat) <= M_PI + 1e-5 && + extent.westLon < extent.eastLon && + extent.southLat < extent.northLat && extent.resLon > 1e-10 && + extent.resLat > 1e-10)) { + pj_log(ctx, PJ_LOG_ERROR, "Inconsistent georeferencing for %s", + filename.c_str()); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + const int columns = static_cast<int>( + fabs((extent.eastLon - extent.westLon) / extent.resLon + 0.5) + 1); + const int rows = static_cast<int>( + fabs((extent.northLat - extent.southLat) / extent.resLat + 0.5) + 1); + + return new NTv1Grid(ctx, fp, columns, rows, extent); +} + +// --------------------------------------------------------------------------- + +bool NTv1Grid::valueAt(int x, int y, float &lonShift, float &latShift) const { + assert(x >= 0 && y >= 0 && x < m_width && y < m_height); + + double two_doubles[2]; + // NTv1 is organized from east to west ! + pj_ctx_fseek(m_ctx, m_fp, + 192 + 2 * sizeof(double) * (y * m_width + m_width - 1 - x), + SEEK_SET); + if (pj_ctx_fread(m_ctx, &two_doubles[0], sizeof(two_doubles), 1, m_fp) != + 1) { + pj_ctx_set_errno(m_ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return false; + } + if (IS_LSB) { + swap_words(&two_doubles[0], sizeof(double), 2); + } + /* convert seconds to radians */ + latShift = static_cast<float>(two_doubles[0] * ((M_PI / 180.0) / 3600.0)); + lonShift = static_cast<float>(two_doubles[1] * ((M_PI / 180.0) / 3600.0)); + + return true; +} + +// --------------------------------------------------------------------------- + +class CTable2Grid : public HorizontalShiftGrid { + PJ_CONTEXT *m_ctx; + PAFile m_fp; + + CTable2Grid(const CTable2Grid &) = delete; + CTable2Grid &operator=(const CTable2Grid &) = delete; + + public: + CTable2Grid(PJ_CONTEXT *ctx, PAFile fp, int widthIn, int heightIn, + const ExtentAndRes &extentIn) + : HorizontalShiftGrid(widthIn, heightIn, extentIn), m_ctx(ctx), + m_fp(fp) {} + + ~CTable2Grid() override; + + bool valueAt(int, int, float &lonShift, float &latShift) const override; + + static CTable2Grid *open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename); +}; + +// --------------------------------------------------------------------------- + +CTable2Grid::~CTable2Grid() { pj_ctx_fclose(m_ctx, m_fp); } + +// --------------------------------------------------------------------------- + +CTable2Grid *CTable2Grid::open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename) { + unsigned char header[160]; + + /* -------------------------------------------------------------------- */ + /* Read the header. */ + /* -------------------------------------------------------------------- */ + if (pj_ctx_fread(ctx, header, sizeof(header), 1, fp) != 1) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + /* -------------------------------------------------------------------- */ + /* Regularize fields of interest. */ + /* -------------------------------------------------------------------- */ + if (!IS_LSB) { + swap_words(header + 96, sizeof(double), 4); + swap_words(header + 128, sizeof(int), 2); + } + + ExtentAndRes extent; + static_assert(sizeof(extent.westLon) == 8, "wrong sizeof"); + static_assert(sizeof(extent.southLat) == 8, "wrong sizeof"); + static_assert(sizeof(extent.resLon) == 8, "wrong sizeof"); + static_assert(sizeof(extent.resLat) == 8, "wrong sizeof"); + memcpy(&extent.westLon, header + 96, 8); + memcpy(&extent.southLat, header + 104, 8); + memcpy(&extent.resLon, header + 112, 8); + memcpy(&extent.resLat, header + 120, 8); + if (!(fabs(extent.westLon) <= 4 * M_PI && + fabs(extent.southLat) <= M_PI + 1e-5 && extent.resLon > 1e-10 && + extent.resLat > 1e-10)) { + pj_log(ctx, PJ_LOG_ERROR, "Inconsistent georeferencing for %s", + filename.c_str()); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + int width; + int height; + memcpy(&width, header + 128, 4); + memcpy(&height, header + 132, 4); + if (width <= 0 || height <= 0) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + extent.eastLon = extent.westLon + (width - 1) * extent.resLon; + extent.northLat = extent.southLat + (height - 1) * extent.resLon; + + return new CTable2Grid(ctx, fp, width, height, extent); +} + +// --------------------------------------------------------------------------- + +bool CTable2Grid::valueAt(int x, int y, float &lonShift, + float &latShift) const { + assert(x >= 0 && y >= 0 && x < m_width && y < m_height); + + float two_floats[2]; + pj_ctx_fseek(m_ctx, m_fp, 160 + 2 * sizeof(float) * (y * m_width + x), + SEEK_SET); + if (pj_ctx_fread(m_ctx, &two_floats[0], sizeof(two_floats), 1, m_fp) != 1) { + pj_ctx_set_errno(m_ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return false; + } + if (!IS_LSB) { + swap_words(&two_floats[0], sizeof(float), 2); + } + + latShift = two_floats[1]; + lonShift = two_floats[0]; + + return true; +} + +// --------------------------------------------------------------------------- + +class NTv2GridSet : public HorizontalShiftGridSet { + PJ_CONTEXT *m_ctx; + PAFile m_fp; + + NTv2GridSet(const NTv2GridSet &) = delete; + NTv2GridSet &operator=(const NTv2GridSet &) = delete; + + NTv2GridSet(PJ_CONTEXT *ctx, PAFile fp) : m_ctx(ctx), m_fp(fp) {} + + public: + ~NTv2GridSet() override; + + static std::unique_ptr<NTv2GridSet> open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename); +}; + +// --------------------------------------------------------------------------- + +class NTv2Grid : public HorizontalShiftGrid { + friend class NTv2GridSet; + + std::string m_name; + PJ_CONTEXT *m_ctx; // owned by the parent NTv2GridSet + PAFile m_fp; // owned by the parent NTv2GridSet + unsigned long long m_offset; + bool m_mustSwap; + + NTv2Grid(const NTv2Grid &) = delete; + NTv2Grid &operator=(const NTv2Grid &) = delete; + + public: + NTv2Grid(const std::string &nameIn, PJ_CONTEXT *ctx, PAFile fp, + unsigned long long offsetIn, bool mustSwapIn, int widthIn, + int heightIn, const ExtentAndRes &extentIn) + : HorizontalShiftGrid(widthIn, heightIn, extentIn), m_name(nameIn), + m_ctx(ctx), m_fp(fp), m_offset(offsetIn), m_mustSwap(mustSwapIn) {} + + bool valueAt(int, int, float &lonShift, float &latShift) const override; +}; + +// --------------------------------------------------------------------------- + +bool NTv2Grid::valueAt(int x, int y, float &lonShift, float &latShift) const { + assert(x >= 0 && y >= 0 && x < m_width && y < m_height); + + float two_float[2]; + // NTv2 is organized from east to west ! + // there are 4 components: lat shift, lon shift, lat error, lon error + pj_ctx_fseek( + m_ctx, m_fp, + // FIXME when fseek support unsigned long long + static_cast<long>(m_offset + + 4 * sizeof(float) * (y * m_width + m_width - 1 - x)), + SEEK_SET); + if (pj_ctx_fread(m_ctx, &two_float[0], sizeof(two_float), 1, m_fp) != 1) { + pj_ctx_set_errno(m_ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return false; + } + if (m_mustSwap) { + swap_words(&two_float[0], sizeof(float), 2); + } + /* convert seconds to radians */ + latShift = static_cast<float>(two_float[0] * ((M_PI / 180.0) / 3600.0)); + lonShift = static_cast<float>(two_float[1] * ((M_PI / 180.0) / 3600.0)); + return true; +} + +// --------------------------------------------------------------------------- + +NTv2GridSet::~NTv2GridSet() { pj_ctx_fclose(m_ctx, m_fp); } + +// --------------------------------------------------------------------------- + +std::unique_ptr<NTv2GridSet> NTv2GridSet::open(PJ_CONTEXT *ctx, PAFile fp, + const std::string &filename) { + auto set = std::unique_ptr<NTv2GridSet>(new NTv2GridSet(ctx, fp)); + set->m_name = filename; + set->m_format = "ntv2"; + + char header[11 * 16]; + + /* -------------------------------------------------------------------- */ + /* Read the header. */ + /* -------------------------------------------------------------------- */ + if (pj_ctx_fread(ctx, header, sizeof(header), 1, fp) != 1) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + const bool must_swap = (header[8] == 11) ? !IS_LSB : IS_LSB; + if (must_swap) { + // swap_words( header+8, 4, 1 ); + // swap_words( header+8+16, 4, 1 ); + swap_words(header + 8 + 32, 4, 1); + // swap_words( header+8+7*16, 8, 1 ); + // swap_words( header+8+8*16, 8, 1 ); + // swap_words( header+8+9*16, 8, 1 ); + // swap_words( header+8+10*16, 8, 1 ); + } + + /* -------------------------------------------------------------------- */ + /* Get the subfile count out ... all we really use for now. */ + /* -------------------------------------------------------------------- */ + unsigned int num_subfiles; + memcpy(&num_subfiles, header + 8 + 32, 4); + + std::map<std::string, NTv2Grid *> mapGrids; + + /* ==================================================================== */ + /* Step through the subfiles, creating a grid for each. */ + /* ==================================================================== */ + for (unsigned subfile = 0; subfile < num_subfiles; subfile++) { + // Read header + if (pj_ctx_fread(ctx, header, sizeof(header), 1, fp) != 1) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + if (strncmp(header, "SUB_NAME", 8) != 0) { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + // Byte swap interesting fields if needed. + if (must_swap) { + swap_words(header + 8 + 16 * 4, sizeof(double), 6); + swap_words(header + 8 + 16 * 10, sizeof(int), 1); + } + + std::string gridName; + gridName.append(header + 8, 8); + + ExtentAndRes extent; + extent.westLon = + -to_double(header + 7 * 16 + 8) * DEG_TO_RAD / 3600.0; /* W_LONG */ + extent.southLat = + to_double(header + 4 * 16 + 8) * DEG_TO_RAD / 3600.0; /* S_LAT */ + extent.eastLon = + -to_double(header + 6 * 16 + 8) * DEG_TO_RAD / 3600.0; /* E_LONG */ + extent.northLat = + to_double(header + 5 * 16 + 8) * DEG_TO_RAD / 3600.0; /* N_LAT */ + extent.resLon = to_double(header + 9 * 16 + 8) * DEG_TO_RAD / 3600.0; + extent.resLat = to_double(header + 8 * 16 + 8) * DEG_TO_RAD / 3600.0; + + if (!(fabs(extent.westLon) <= 4 * M_PI && + fabs(extent.eastLon) <= 4 * M_PI && + fabs(extent.northLat) <= M_PI + 1e-5 && + fabs(extent.southLat) <= M_PI + 1e-5 && + extent.westLon < extent.eastLon && + extent.southLat < extent.northLat && extent.resLon > 1e-10 && + extent.resLat > 1e-10)) { + pj_log(ctx, PJ_LOG_ERROR, "Inconsistent georeferencing for %s", + filename.c_str()); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + const int columns = static_cast<int>( + fabs((extent.eastLon - extent.westLon) / extent.resLon + 0.5) + 1); + const int rows = static_cast<int>( + fabs((extent.northLat - extent.southLat) / extent.resLat + 0.5) + + 1); + + pj_log(ctx, PJ_LOG_DEBUG_MINOR, + "NTv2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", gridName.c_str(), + columns, rows, extent.westLon * RAD_TO_DEG, + extent.southLat * RAD_TO_DEG, extent.eastLon * RAD_TO_DEG, + extent.northLat * RAD_TO_DEG); + + unsigned int gs_count; + memcpy(&gs_count, header + 8 + 16 * 10, 4); + if (gs_count / columns != static_cast<unsigned>(rows)) { + pj_log(ctx, PJ_LOG_ERROR, + "GS_COUNT(%u) does not match expected cells (%dx%d)", + gs_count, columns, rows); + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return nullptr; + } + + auto offset = pj_ctx_ftell(ctx, fp); + auto grid = std::unique_ptr<NTv2Grid>(new NTv2Grid( + gridName, ctx, fp, offset, must_swap, columns, rows, extent)); + std::string parentName; + parentName.assign(header + 24, 8); + auto iter = mapGrids.find(parentName); + auto gridPtr = grid.get(); + if (iter == mapGrids.end()) { + set->m_grids.emplace_back(std::move(grid)); + } else { + iter->second->m_children.emplace_back(std::move(grid)); + } + mapGrids[gridName] = gridPtr; + + // Skip grid data. 4 components of size float + pj_ctx_fseek(ctx, fp, gs_count * 4 * 4, SEEK_CUR); + } + return set; +} + +// --------------------------------------------------------------------------- + +std::unique_ptr<HorizontalShiftGridSet> +HorizontalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) { + if (filename == "null") { + auto set = std::unique_ptr<HorizontalShiftGridSet>( + new HorizontalShiftGridSet()); + set->m_name = filename; + set->m_format = "null"; + set->m_grids.push_back(std::unique_ptr<NullHorizontalShiftGrid>( + new NullHorizontalShiftGrid())); + return set; + } + + PAFile fp; + if (!(fp = pj_open_lib(ctx, filename.c_str(), "rb"))) { + ctx->last_errno = 0; /* don't treat as a persistent error */ + return nullptr; + } + + char header[160]; + /* -------------------------------------------------------------------- */ + /* Load a header, to determine the file type. */ + /* -------------------------------------------------------------------- */ + size_t header_size; + if ((header_size = pj_ctx_fread(ctx, header, 1, sizeof(header), fp)) != + sizeof(header)) { + /* some files may be smaller that sizeof(header), eg 160, so */ + ctx->last_errno = 0; /* don't treat as a persistent error */ + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, + "pj_gridinfo_init: short header read of %d bytes", + (int)header_size); + } + + pj_ctx_fseek(ctx, fp, SEEK_SET, 0); + + /* -------------------------------------------------------------------- */ + /* Determine file type. */ + /* -------------------------------------------------------------------- */ + if (header_size >= 144 + 16 && strncmp(header + 0, "HEADER", 6) == 0 && + strncmp(header + 96, "W GRID", 6) == 0 && + strncmp(header + 144, "TO NAD83 ", 16) == 0) { + auto grid = NTv1Grid::open(ctx, fp, filename); + if (!grid) { + pj_ctx_fclose(ctx, fp); + return nullptr; + } + auto set = std::unique_ptr<HorizontalShiftGridSet>( + new HorizontalShiftGridSet()); + set->m_name = filename; + set->m_format = "ntv1"; + set->m_grids.push_back(std::unique_ptr<HorizontalShiftGrid>(grid)); + return set; + } else if (header_size >= 9 && strncmp(header + 0, "CTABLE V2", 9) == 0) { + auto grid = CTable2Grid::open(ctx, fp, filename); + if (!grid) { + pj_ctx_fclose(ctx, fp); + return nullptr; + } + auto set = std::unique_ptr<HorizontalShiftGridSet>( + new HorizontalShiftGridSet()); + set->m_name = filename; + set->m_format = "ctable2"; + set->m_grids.push_back(std::unique_ptr<HorizontalShiftGrid>(grid)); + return set; + } else if (header_size >= 48 + 7 && + strncmp(header + 0, "NUM_OREC", 8) == 0 && + strncmp(header + 48, "GS_TYPE", 7) == 0) { + return NTv2GridSet::open(ctx, fp, filename); + } + + pj_log(ctx, PJ_LOG_DEBUG_MAJOR, "Unrecognized horizontal grid format"); + pj_ctx_fclose(ctx, fp); + return nullptr; +} + +// --------------------------------------------------------------------------- + +const HorizontalShiftGrid *HorizontalShiftGrid::gridAt(double lon, + double lat) const { + for (const auto &child : m_children) { + const auto &extentChild = child->extentAndRes(); + const double epsilon = + (extentChild.resLon + extentChild.resLat) / 10000.0; + if ((extentChild.fullWorldLongitude() || + (lon + epsilon >= extentChild.westLon && + lon - epsilon <= extentChild.eastLon)) && + lat + epsilon >= extentChild.southLat && + lat - epsilon <= extentChild.northLat) { + return child->gridAt(lon, lat); + } + } + return this; +} +// --------------------------------------------------------------------------- + +const HorizontalShiftGrid *HorizontalShiftGridSet::gridAt(double lon, + double lat) const { + for (const auto &grid : m_grids) { + if (dynamic_cast<NullHorizontalShiftGrid *>(grid.get())) { + return grid.get(); + } + const auto &extent = grid->extentAndRes(); + const double epsilon = (extent.resLon + extent.resLat) / 10000.0; + if ((extent.fullWorldLongitude() || + (lon + epsilon >= extent.westLon && + lon - epsilon <= extent.eastLon)) && + lat + epsilon >= extent.southLat && + lat - epsilon <= extent.northLat) { + return grid->gridAt(lon, lat); + } + } + return nullptr; +} + +NS_PROJ_END diff --git a/src/grids.hpp b/src/grids.hpp new file mode 100644 index 00000000..65bf502b --- /dev/null +++ b/src/grids.hpp @@ -0,0 +1,167 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: Grid management + * Author: Even Rouault, <even.rouault at spatialys.com> + * + ****************************************************************************** + * Copyright (c) 2019, Even Rouault, <even.rouault at spatialys.com> + * + * 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. + *****************************************************************************/ + +#ifndef GRIDS_HPP_INCLUDED +#define GRIDS_HPP_INCLUDED + +#include <memory> +#include <vector> + +#include "proj.h" +#include "proj/util.hpp" + +NS_PROJ_START + +struct ExtentAndRes { + double westLon; // in radian + double southLat; // in radian + double eastLon; // in radian + double northLat; // in radian + double resLon; // in radian + double resLat; // in radian + + bool fullWorldLongitude() const; +}; + +// --------------------------------------------------------------------------- + +class Grid { + protected: + int m_width; + int m_height; + ExtentAndRes m_extent; + + Grid(int widthIn, int heightIn, const ExtentAndRes &extentIn); + + public: + virtual ~Grid(); + + int width() const { return m_width; } + int height() const { return m_height; } + const ExtentAndRes &extentAndRes() const { return m_extent; } + + virtual bool isNullGrid() const { return false; } +}; + +// --------------------------------------------------------------------------- + +class VerticalShiftGrid : public Grid { + protected: + std::vector<std::unique_ptr<VerticalShiftGrid>> m_children{}; + + public: + VerticalShiftGrid(int widthIn, int heightIn, const ExtentAndRes &extentIn); + + const VerticalShiftGrid *gridAt(double lon, double lat) const; + + virtual bool isNodata(float /*val*/, double /* multiplier */) const; + + // x = 0 is western-most column, y = 0 is southern-most line + virtual bool valueAt(int x, int y, float &out) const = 0; +}; + +// --------------------------------------------------------------------------- + +class VerticalShiftGridSet { + std::string m_name{}; + std::string m_format{}; + std::vector<std::unique_ptr<VerticalShiftGrid>> m_grids{}; + + VerticalShiftGridSet(); + + public: + virtual ~VerticalShiftGridSet(); + + static std::unique_ptr<VerticalShiftGridSet> + open(PJ_CONTEXT *ctx, const std::string &filename); + + const std::string &name() const { return m_name; } + const std::string &format() const { return m_format; } + const std::vector<std::unique_ptr<VerticalShiftGrid>> &grids() const { + return m_grids; + } + const VerticalShiftGrid *gridAt(double lon, double lat) const; +}; + +// --------------------------------------------------------------------------- + +class HorizontalShiftGrid : public Grid { + protected: + std::vector<std::unique_ptr<HorizontalShiftGrid>> m_children{}; + + public: + HorizontalShiftGrid(int widthIn, int heightIn, + const ExtentAndRes &extentIn); + ~HorizontalShiftGrid() override; + + const HorizontalShiftGrid *gridAt(double lon, double lat) const; + + // x = 0 is western-most column, y = 0 is southern-most line + virtual bool valueAt(int x, int y, float &lonShift, + float &latShift) const = 0; +}; + +// --------------------------------------------------------------------------- + +class HorizontalShiftGridSet { + protected: + std::string m_name{}; + std::string m_format{}; + std::vector<std::unique_ptr<HorizontalShiftGrid>> m_grids{}; + + HorizontalShiftGridSet(); + + public: + virtual ~HorizontalShiftGridSet(); + + static std::unique_ptr<HorizontalShiftGridSet> + open(PJ_CONTEXT *ctx, const std::string &filename); + + const std::string &name() const { return m_name; } + const std::string &format() const { return m_format; } + const std::vector<std::unique_ptr<HorizontalShiftGrid>> &grids() const { + return m_grids; + } + const HorizontalShiftGrid *gridAt(double lon, double lat) const; +}; + +// --------------------------------------------------------------------------- + +typedef std::vector<std::unique_ptr<HorizontalShiftGridSet>> ListOfHGrids; +typedef std::vector<std::unique_ptr<VerticalShiftGridSet>> ListOfVGrids; + +ListOfVGrids proj_vgrid_init(PJ *P, const char *grids); +ListOfHGrids proj_hgrid_init(PJ *P, const char *grids); +double proj_vgrid_value(PJ *P, const ListOfVGrids &, PJ_LP lp, + double vmultiplier); +PJ_LP proj_hgrid_value(PJ *P, const ListOfHGrids &, PJ_LP lp); +PJ_LP proj_hgrid_apply(PJ *P, const ListOfHGrids &, PJ_LP lp, + PJ_DIRECTION direction); + +NS_PROJ_END + +#endif // GRIDS_HPP_INCLUDED diff --git a/src/init.cpp b/src/init.cpp index 1ed46e5a..19fcf47b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -646,12 +646,6 @@ pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_i PIN->long_wrap_center = 0.0; strcpy( PIN->axis, "enu" ); - PIN->gridlist = nullptr; - PIN->gridlist_count = 0; - - PIN->vgridlist_geoid = nullptr; - PIN->vgridlist_geoid_count = 0; - /* Set datum parameters. Similarly to +init parameters we want to expand */ /* +datum parameters as late as possible when dealing with pipelines. */ /* otherwise only the first occurrence of +datum will be expanded and that */ diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index d1bc8836..f6112aef 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -234,13 +234,9 @@ set(SRC_LIBPROJ_CORE fileapi.cpp fwd.cpp gauss.cpp - gc_reader.cpp geocent.cpp geocent.h geodesic.c - gridcatalog.cpp - gridinfo.cpp - gridlist.cpp init.cpp initcache.cpp internal.cpp @@ -252,9 +248,6 @@ set(SRC_LIBPROJ_CORE mlfn.cpp msfn.cpp mutex.cpp - nad_cvt.cpp - nad_init.cpp - nad_intr.cpp open_lib.cpp param.cpp phi2.cpp @@ -286,6 +279,8 @@ set(SRC_LIBPROJ_CORE proj_json_streaming_writer.hpp proj_json_streaming_writer.cpp tracing.cpp + grids.hpp + grids.cpp ${CMAKE_CURRENT_BINARY_DIR}/proj_config.h ) diff --git a/src/malloc.cpp b/src/malloc.cpp index 393437e3..3fd3699f 100644 --- a/src/malloc.cpp +++ b/src/malloc.cpp @@ -48,6 +48,9 @@ #include "proj.h" #include "proj_internal.h" +#include "grids.hpp" + +using namespace NS_PROJ; /**********************************************************************/ void *pj_malloc(size_t size) { @@ -225,10 +228,8 @@ PJ *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ pj_dealloc(P->def_spherification); pj_dealloc(P->def_ellps); - /* free grid lists */ - pj_dealloc( P->gridlist ); - pj_dealloc( P->vgridlist_geoid ); - pj_dealloc( P->catalog_name ); + delete static_cast<ListOfHGrids*>(P->hgrids_legacy); + delete static_cast<ListOfVGrids*>(P->vgrids_legacy); /* We used to call pj_dalloc( P->catalog ), but this will leak */ /* memory. The safe way to clear catalog and grid is to call */ diff --git a/src/nad_cvt.cpp b/src/nad_cvt.cpp deleted file mode 100644 index 79441d0a..00000000 --- a/src/nad_cvt.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#define PJ_LIB__ - -#include <stdio.h> -#include <stdlib.h> - -#include "proj.h" -#include "proj_internal.h" -#include <math.h> - -#define MAX_ITERATIONS 10 -#define TOL 1e-12 - -PJ_LP nad_cvt(PJ_LP in, int inverse, struct CTABLE *ct) { - PJ_LP t, tb,del, dif; - int i = MAX_ITERATIONS; - const double toltol = TOL*TOL; - - if (in.lam == HUGE_VAL) - return in; - - /* normalize input to ll origin */ - tb = in; - tb.lam -= ct->ll.lam; - tb.phi -= ct->ll.phi; - tb.lam = adjlon (tb.lam - M_PI) + M_PI; - - t = nad_intr (tb, ct); - if (t.lam == HUGE_VAL) - return t; - - if (!inverse) { - in.lam -= t.lam; - in.phi += t.phi; - return in; - } - - t.lam = tb.lam + t.lam; - t.phi = tb.phi - t.phi; - - do { - del = nad_intr(t, ct); - - /* This case used to return failure, but I have - changed it to return the first order approximation - of the inverse shift. This avoids cases where the - grid shift *into* this grid came from another grid. - While we aren't returning optimally correct results - I feel a close result in this case is better than - no result. NFW - To demonstrate use -112.5839956 49.4914451 against - the NTv2 grid shift file from Canada. */ - if (del.lam == HUGE_VAL) - break; - - dif.lam = t.lam - del.lam - tb.lam; - dif.phi = t.phi + del.phi - tb.phi; - t.lam -= dif.lam; - t.phi -= dif.phi; - - } while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); /* prob. slightly faster than hypot() */ - - if (i==0) { - /* If we had access to a context, this should go through pj_log, and we should set ctx->errno */ - if (getenv ("PROJ_DEBUG")) - fprintf( stderr, "Inverse grid shift iterator failed to converge.\n" ); - t.lam = t.phi = HUGE_VAL; - return t; - } - - /* and again: pj_log and ctx->errno */ - if (del.lam==HUGE_VAL && getenv ("PROJ_DEBUG")) - fprintf (stderr, "Inverse grid shift iteration failed, presumably at grid edge.\nUsing first approximation.\n"); - - in.lam = adjlon (t.lam + ct->ll.lam); - in.phi = t.phi + ct->ll.phi; - return in; -} diff --git a/src/nad_init.cpp b/src/nad_init.cpp deleted file mode 100644 index 315318be..00000000 --- a/src/nad_init.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Load datum shift files into memory. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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_LIB__ - -#include <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "proj.h" -#include "proj_internal.h" - -/************************************************************************/ -/* swap_words() */ -/* */ -/* Convert the byte order of the given word(s) in place. */ -/************************************************************************/ - -static const int byte_order_test = 1; -#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) - -static void swap_words( void *data_in, int word_size, int word_count ) - -{ - int word; - unsigned char *data = (unsigned char *) data_in; - - for( word = 0; word < word_count; word++ ) - { - int i; - - for( i = 0; i < word_size/2; i++ ) - { - unsigned char t; - - t = data[i]; - data[i] = data[word_size-i-1]; - data[word_size-i-1] = t; - } - - data += word_size; - } -} - -/************************************************************************/ -/* nad_ctable_load() */ -/* */ -/* Load the data portion of a ctable formatted grid. */ -/************************************************************************/ - -int nad_ctable_load( projCtx ctx, struct CTABLE *ct, struct projFileAPI_t* fileapi ) - -{ - PAFile fid = (PAFile)fileapi; - size_t a_size; - - pj_ctx_fseek( ctx, fid, sizeof(struct CTABLE), SEEK_SET ); - - /* read all the actual shift values */ - a_size = ct->lim.lam * ct->lim.phi; - ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); - if( ct->cvs == nullptr - || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) - { - pj_dalloc( ct->cvs ); - ct->cvs = nullptr; - - pj_log( ctx, PJ_LOG_ERROR, - "ctable loading failed on fread() - binary incompatible?" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - return 1; -} - -/************************************************************************/ -/* nad_ctable_init() */ -/* */ -/* Read the header portion of a "ctable" format grid. */ -/************************************************************************/ - -struct CTABLE *nad_ctable_init( projCtx ctx, struct projFileAPI_t* fileapi ) -{ - PAFile fid = (PAFile)fileapi; - struct CTABLE *ct; - int id_end; - - /* read the table header */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if( ct == nullptr - || pj_ctx_fread( ctx, ct, sizeof(struct CTABLE), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return nullptr; - } - - /* do some minimal validation to ensure the structure isn't corrupt */ - if( ct->lim.lam < 1 || ct->lim.lam > 100000 - || ct->lim.phi < 1 || ct->lim.phi > 100000 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return nullptr; - } - - /* trim white space and newlines off id */ - for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) - { - if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) - ct->id[id_end] = '\0'; - else - break; - } - - ct->cvs = nullptr; - - return ct; -} - -/************************************************************************/ -/* nad_ctable2_load() */ -/* */ -/* Load the data portion of a ctable2 formatted grid. */ -/************************************************************************/ - -int nad_ctable2_load( projCtx ctx, struct CTABLE *ct, struct projFileAPI_t* fileapi ) - -{ - PAFile fid = (PAFile)fileapi; - size_t a_size; - - pj_ctx_fseek( ctx, fid, 160, SEEK_SET ); - - /* read all the actual shift values */ - a_size = ct->lim.lam * ct->lim.phi; - ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); - if( ct->cvs == nullptr - || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) - { - pj_dalloc( ct->cvs ); - ct->cvs = nullptr; - - if( getenv("PROJ_DEBUG") != nullptr ) - { - fprintf( stderr, - "ctable2 loading failed on fread() - binary incompatible?\n" ); - } - - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( !IS_LSB ) - { - swap_words( ct->cvs, 4, (int)a_size * 2 ); - } - - return 1; -} - -/************************************************************************/ -/* nad_ctable2_init() */ -/* */ -/* Read the header portion of a "ctable2" format grid. */ -/************************************************************************/ - -struct CTABLE *nad_ctable2_init( projCtx ctx, struct projFileAPI_t* fileapi ) -{ - PAFile fid = (PAFile)fileapi; - struct CTABLE *ct; - int id_end; - char header[160]; - - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return nullptr; - } - - if( !IS_LSB ) - { - swap_words( header + 96, 8, 4 ); - swap_words( header + 128, 4, 2 ); - } - - if( strncmp(header,"CTABLE V2",9) != 0 ) - { - pj_log( ctx, PJ_LOG_ERROR, "ctable2 - wrong header!" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return nullptr; - } - - /* read the table header */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if( ct == nullptr ) - { - pj_ctx_set_errno( ctx, ENOMEM ); - return nullptr; - } - - memcpy( ct->id, header + 16, 80 ); - memcpy( &ct->ll.lam, header + 96, 8 ); - memcpy( &ct->ll.phi, header + 104, 8 ); - memcpy( &ct->del.lam, header + 112, 8 ); - memcpy( &ct->del.phi, header + 120, 8 ); - memcpy( &ct->lim.lam, header + 128, 4 ); - memcpy( &ct->lim.phi, header + 132, 4 ); - - /* do some minimal validation to ensure the structure isn't corrupt */ - if( ct->lim.lam < 1 || ct->lim.lam > 100000 - || ct->lim.phi < 1 || ct->lim.phi > 100000 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return nullptr; - } - - /* trim white space and newlines off id */ - for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) - { - if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) - ct->id[id_end] = '\0'; - else - break; - } - - ct->cvs = nullptr; - - return ct; -} - -/************************************************************************/ -/* nad_init() */ -/* */ -/* Read a datum shift file in any of the supported binary formats. */ -/************************************************************************/ - -struct CTABLE *nad_init(projCtx ctx, char *name) -{ - struct CTABLE *ct; - PAFile fid; - - ctx->last_errno = 0; - -/* -------------------------------------------------------------------- */ -/* Open the file using the usual search rules. */ -/* -------------------------------------------------------------------- */ - if (!(fid = pj_open_lib(ctx, name, "rb"))) { - return nullptr; - } - - ct = nad_ctable_init( ctx, (struct projFileAPI_t*)fid ); - if( ct != nullptr ) - { - if( !nad_ctable_load( ctx, ct, (struct projFileAPI_t*)fid ) ) - { - nad_free( ct ); - ct = nullptr; - } - } - - pj_ctx_fclose(ctx, fid); - return ct; -} - -/************************************************************************/ -/* nad_free() */ -/* */ -/* Free a CTABLE grid shift structure produced by nad_init(). */ -/************************************************************************/ - -void nad_free(struct CTABLE *ct) -{ - if (ct) { - if( ct->cvs != nullptr ) - pj_dalloc(ct->cvs); - - pj_dalloc(ct); - } -} diff --git a/src/nad_intr.cpp b/src/nad_intr.cpp deleted file mode 100644 index 36ab0a99..00000000 --- a/src/nad_intr.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* Determine nad table correction value */ -#define PJ_LIB__ -#include "proj.h" -#include "proj_internal.h" -#include <math.h> - -PJ_LP nad_intr(PJ_LP t, struct CTABLE *ct) { - PJ_LP val, frct; - ILP indx; - double m00, m10, m01, m11; - FLP *f00, *f10, *f01, *f11; - long index; - int in; - - t.lam /= ct->del.lam; - indx.lam = isnan(t.lam) ? 0 : (pj_int32)lround(floor(t.lam)); - t.phi /= ct->del.phi; - indx.phi = isnan(t.phi) ? 0 : (pj_int32)lround(floor(t.phi)); - - frct.lam = t.lam - indx.lam; - frct.phi = t.phi - indx.phi; - val.lam = val.phi = HUGE_VAL; - if (indx.lam < 0) { - if (indx.lam == -1 && frct.lam > 0.99999999999) { - ++indx.lam; - frct.lam = 0.; - } else - return val; - } else if ((in = indx.lam + 1) >= ct->lim.lam) { - if (in == ct->lim.lam && frct.lam < 1e-11) { - --indx.lam; - frct.lam = 1.; - } else - return val; - } - if (indx.phi < 0) { - if (indx.phi == -1 && frct.phi > 0.99999999999) { - ++indx.phi; - frct.phi = 0.; - } else - return val; - } else if ((in = indx.phi + 1) >= ct->lim.phi) { - if (in == ct->lim.phi && frct.phi < 1e-11) { - --indx.phi; - frct.phi = 1.; - } else - return val; - } - index = indx.phi * ct->lim.lam + indx.lam; - f00 = ct->cvs + index++; - f10 = ct->cvs + index; - index += ct->lim.lam; - f11 = ct->cvs + index--; - f01 = ct->cvs + index; - m11 = m10 = frct.lam; - m00 = m01 = 1. - frct.lam; - m11 *= frct.phi; - m01 *= frct.phi; - frct.phi = 1. - frct.phi; - m00 *= frct.phi; - m10 *= frct.phi; - val.lam = m00 * f00->lam + m10 * f10->lam + - m01 * f01->lam + m11 * f11->lam; - val.phi = m00 * f00->phi + m10 * f10->phi + - m01 * f01->phi + m11 * f11->phi; - return val; -} diff --git a/src/proj_internal.h b/src/proj_internal.h index 3ca927a3..7d826414 100644 --- a/src/proj_internal.h +++ b/src/proj_internal.h @@ -195,14 +195,6 @@ PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P); PJ_COORD PROJ_DLL pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo); PJ_COORD PROJ_DLL pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo); - -/* Grid functionality */ -int proj_vgrid_init(PJ *P, const char *grids); -int proj_hgrid_init(PJ *P, const char *grids); -double proj_vgrid_value(PJ *P, PJ_LP lp, double vmultiplier); -PJ_LP proj_hgrid_value(PJ *P, PJ_LP lp); -PJ_LP proj_hgrid_apply(PJ *P, PJ_LP lp, PJ_DIRECTION direction); - void PROJ_DLL proj_log_error (PJ *P, const char *fmt, ...); void proj_log_debug (PJ *P, const char *fmt, ...); void proj_log_trace (PJ *P, const char *fmt, ...); @@ -478,32 +470,16 @@ struct PJconsts { int datum_type = PJD_UNKNOWN; /* PJD_UNKNOWN/3PARAM/7PARAM/GRIDSHIFT/WGS84 */ double datum_params[7] = {0,0,0,0,0,0,0}; /* Parameters for 3PARAM and 7PARAM */ - struct _pj_gi **gridlist = nullptr; /* TODO: Description needed */ - int gridlist_count = 0; - int has_geoid_vgrids = 0; /* TODO: Description needed */ - struct _pj_gi **vgridlist_geoid = nullptr; /* TODO: Description needed */ - int vgridlist_geoid_count = 0; + int has_geoid_vgrids = 0; /* used by legacy transform.cpp */ + void* hgrids_legacy = nullptr; /* used by legacy transform.cpp. Is a pointer to a ListOfHGrids* */ + void* vgrids_legacy = nullptr; /* used by legacy transform.cpp. Is a pointer to a ListOfVGrids* */ double from_greenwich = 0.0; /* prime meridian offset (in radians) */ double long_wrap_center = 0.0; /* 0.0 for -180 to 180, actually in radians*/ int is_long_wrap_set = 0; char axis[4] = {0,0,0,0}; /* Axis order, pj_transform/pj_adjust_axis */ - /* New Datum Shift Grid Catalogs */ - char *catalog_name = nullptr; - struct _PJ_GridCatalog *catalog = nullptr; - - double datum_date = 0.0; /* TODO: Description needed */ - - struct _pj_gi *last_before_grid = nullptr; /* TODO: Description needed */ - PJ_Region last_before_region = {0,0,0,0}; /* TODO: Description needed */ - double last_before_date = 0.0; /* TODO: Description needed */ - - struct _pj_gi *last_after_grid = nullptr; /* TODO: Description needed */ - PJ_Region last_after_region = {0,0,0,0}; /* TODO: Description needed */ - double last_after_date = 0.0; /* TODO: Description needed */ - /************************************************************************************* ISO-19111 interface **************************************************************************************/ @@ -766,56 +742,6 @@ PJ *pj_projection_specific_setup_##name (PJ *P) #endif /* def PJ_LIB__ */ - -#define MAX_TAB_ID 80 -typedef struct { float lam, phi; } FLP; -typedef struct { pj_int32 lam, phi; } ILP; - -struct CTABLE { - char id[MAX_TAB_ID]; /* ascii info */ - PJ_LP ll; /* lower left corner coordinates */ - PJ_LP del; /* size of cells */ - ILP lim; /* limits of conversion matrix */ - FLP *cvs; /* conversion matrix */ -}; - -typedef struct _pj_gi { - char *gridname; /* identifying name of grid, eg "conus" or ntv2_0.gsb */ - char *filename; /* full path to filename */ - - const char *format; /* format of this grid, ie "ctable", "ntv1", - "ntv2" or "missing". */ - - long grid_offset; /* offset in file, for delayed loading */ - int must_swap; /* only for NTv2 */ - - struct CTABLE *ct; - - struct _pj_gi *next; - struct _pj_gi *child; -} PJ_GRIDINFO; - -typedef struct { - PJ_Region region; - int priority; /* higher used before lower */ - double date; /* year.fraction */ - char *definition; /* usually the gridname */ - - PJ_GRIDINFO *gridinfo; - int available; /* 0=unknown, 1=true, -1=false */ -} PJ_GridCatalogEntry; - -typedef struct _PJ_GridCatalog { - char *catalog_name; - - PJ_Region region; /* maximum extent of catalog data */ - - int entry_count; - PJ_GridCatalogEntry *entries; - - struct _PJ_GridCatalog *next; -} PJ_GridCatalog; - /* procedure prototypes */ double PROJ_DLL dmstor(const char *, char **); double dmstor_ctx(projCtx_t *ctx, const char *, char **); @@ -862,50 +788,6 @@ COMPLEX pj_zpolyd1(COMPLEX, const COMPLEX *, int, COMPLEX *); int pj_deriv(PJ_LP, double, const PJ *, struct DERIVS *); int pj_factors(PJ_LP, const PJ *, double, struct FACTORS *); -/* nadcon related protos */ -PJ_LP nad_intr(PJ_LP, struct CTABLE *); -PJ_LP nad_cvt(PJ_LP, int, struct CTABLE *); -struct CTABLE *nad_init(projCtx_t *ctx, char *); -struct CTABLE *nad_ctable_init( projCtx_t *ctx, struct projFileAPI_t* fid ); -int nad_ctable_load( projCtx_t *ctx, struct CTABLE *, struct projFileAPI_t* fid ); -struct CTABLE *nad_ctable2_init( projCtx_t *ctx, struct projFileAPI_t* fid ); -int nad_ctable2_load( projCtx_t *ctx, struct CTABLE *, struct projFileAPI_t* fid ); -void nad_free(struct CTABLE *); - -/* higher level handling of datum grid shift files */ - -int pj_apply_vgridshift( PJ *defn, const char *listname, - PJ_GRIDINFO ***gridlist_p, - int *gridlist_count_p, - int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ); -int pj_apply_gridshift_2( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ); -int pj_apply_gridshift_3( projCtx_t *ctx, - PJ_GRIDINFO **gridlist, int gridlist_count, - int inverse, long point_count, int point_offset, - double *x, double *y, double *z ); - -PJ_GRIDINFO **pj_gridlist_from_nadgrids( projCtx_t *, const char *, int * ); - -PJ_GRIDINFO *pj_gridinfo_init( projCtx_t *, const char * ); -int pj_gridinfo_load( projCtx_t *, PJ_GRIDINFO * ); -void pj_gridinfo_free( projCtx_t *, PJ_GRIDINFO * ); - -PJ_GridCatalog *pj_gc_findcatalog( projCtx_t *, const char * ); -PJ_GridCatalog *pj_gc_readcatalog( projCtx_t *, const char * ); -void pj_gc_unloadall( projCtx_t *); -int pj_gc_apply_gridshift( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ); -int pj_gc_apply_gridshift( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ); - -double pj_gc_parsedate( projCtx_t *, const char * ); - void *proj_mdist_ini(double); double proj_mdist(double, double, double, const void *); double proj_inv_mdist(projCtx_t *ctx, double, const void *); diff --git a/src/transform.cpp b/src/transform.cpp index d111d835..020d62ea 100644 --- a/src/transform.cpp +++ b/src/transform.cpp @@ -34,6 +34,9 @@ #include "proj.h" #include "proj_internal.h" #include "geocent.h" +#include "grids.hpp" + +using namespace NS_PROJ; static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, long point_count, int point_offset, @@ -431,6 +434,78 @@ static int height_unit (PJ *P, PJ_DIRECTION dir, long n, int dist, double *z) { } +/************************************************************************/ +/* pj_apply_vgridshift() */ +/* */ +/* This implementation takes uses the gridlist from a coordinate */ +/* system definition. If the gridlist has not yet been */ +/* populated in the coordinate system definition we set it up */ +/* now. */ +/************************************************************************/ +static int pj_apply_vgridshift( PJ *defn, + int inverse, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + if( defn->vgrids_legacy == nullptr ) + { + defn->vgrids_legacy = new ListOfVGrids; + auto vgrids = proj_vgrid_init(defn, "geoidgrids"); + if( vgrids.empty() ) + return 0; + *static_cast<ListOfVGrids*>(defn->vgrids_legacy) = std::move(vgrids); + } + if( static_cast<ListOfVGrids*>(defn->vgrids_legacy)->empty() ) + { + return 0; + } + + for( int i = 0; i < point_count; i++ ) + { + double value; + long io = i * point_offset; + PJ_LP input; + + input.phi = y[io]; + input.lam = x[io]; + + value = proj_vgrid_value(defn, *static_cast<ListOfVGrids*>(defn->vgrids_legacy), input, 1.0); + + if( inverse ) + z[io] -= value; + else + z[io] += value; + + if( value == HUGE_VAL ) + { + std::string gridlist; + + proj_log_debug(defn, + "pj_apply_vgridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + + for( const auto& gridset: *static_cast<ListOfVGrids*>(defn->vgrids_legacy) ) + { + if( gridlist.empty() ) + gridlist += " tried: "; + else + gridlist += ','; + gridlist += gridset->name(); + } + + proj_log_debug(defn, "%s", gridlist.c_str()); + pj_ctx_set_errno( defn->ctx, PJD_ERR_GRID_AREA ); + + return PJD_ERR_GRID_AREA; + } + } + + return 0; +} + /* -------------------------------------------------------------------- */ /* Transform to ellipsoidal heights if needed */ @@ -441,10 +516,7 @@ static int geometric_to_orthometric (PJ *P, PJ_DIRECTION dir, long n, int dist, return 0; if (z==nullptr) return PJD_ERR_GEOCENTRIC; - err = pj_apply_vgridshift (P, "sgeoidgrids", - &(P->vgridlist_geoid), - &(P->vgridlist_geoid_count), - dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); + err = pj_apply_vgridshift (P, dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); if (err) return pj_ctx_get_errno(P->ctx); return 0; @@ -806,6 +878,66 @@ int pj_geocentric_from_wgs84( PJ *defn, return 0; } + +/************************************************************************/ +/* pj_apply_gridshift_2() */ +/* */ +/* This implementation uses the gridlist from a coordinate */ +/* system definition. If the gridlist has not yet been */ +/* populated in the coordinate system definition we set it up */ +/* now. */ +/************************************************************************/ +static +int pj_apply_gridshift_2( PJ *defn, int inverse, + long point_count, int point_offset, + double *x, double *y, double * /*z*/ ) + +{ + if( defn->hgrids_legacy == nullptr ) + { + defn->hgrids_legacy = new ListOfHGrids; + auto hgrids = proj_hgrid_init(defn, "nadgrids"); + if( hgrids.empty() ) + return 0; + *static_cast<ListOfHGrids*>(defn->hgrids_legacy) = std::move(hgrids); + } + if( static_cast<ListOfHGrids*>(defn->hgrids_legacy)->empty() ) + { + return 0; + } + + for( long i = 0; i < point_count; i++ ) + { + PJ_LP input; + + long io = i * point_offset; + input.phi = y[io]; + input.lam = x[io]; + + auto output = proj_hgrid_apply(defn, *static_cast<ListOfHGrids*>(defn->hgrids_legacy), input, inverse ? PJ_INV : PJ_FWD); + + if ( output.lam != HUGE_VAL ) + { + y[io] = output.phi; + x[io] = output.lam; + } + else + { + if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + { + pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, + "pj_apply_gridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + } + } + } + + return 0; +} + + /************************************************************************/ /* pj_datum_transform() */ /* */ @@ -1046,3 +1178,8 @@ static int adjust_axis( projCtx ctx, return 0; } +// --------------------------------------------------------------------------- + +void pj_deallocate_grids() +{ +} diff --git a/src/transformations/deformation.cpp b/src/transformations/deformation.cpp index f1311a54..f5468775 100644 --- a/src/transformations/deformation.cpp +++ b/src/transformations/deformation.cpp @@ -56,17 +56,22 @@ grid-values in units of mm/year in ENU-space. #include "proj.h" #include "proj_internal.h" #include <math.h> +#include "grids.hpp" PROJ_HEAD(deformation, "Kinematic grid shift"); #define TOL 1e-8 #define MAX_ITERATIONS 10 +using namespace NS_PROJ; + namespace { // anonymous namespace -struct pj_opaque { - double dt; - double t_epoch; - PJ *cart; +struct deformationData { + double dt = 0; + double t_epoch = 0; + PJ *cart = nullptr; + ListOfHGrids hgrids{}; + ListOfVGrids vgrids{}; }; } // anonymous namespace @@ -85,13 +90,14 @@ static PJ_XYZ get_grid_shift(PJ* P, const PJ_XYZ& cartesian) { PJ_COORD geodetic, shift, temp; double sp, cp, sl, cl; int previous_errno = proj_errno_reset(P); + auto Q = static_cast<deformationData*>(P->opaque); /* cartesian to geodetic */ - geodetic.lpz = pj_inv3d(cartesian, static_cast<struct pj_opaque*>(P->opaque)->cart); + geodetic.lpz = pj_inv3d(cartesian, Q->cart); /* look up correction values in grids */ - shift.lp = proj_hgrid_value(P, geodetic.lp); - shift.enu.u = proj_vgrid_value(P, geodetic.lp, 1.0); + shift.lp = proj_hgrid_value(P, Q->hgrids, geodetic.lp); + shift.enu.u = proj_vgrid_value(P, Q->vgrids, geodetic.lp, 1.0); if (proj_errno(P) == PJD_ERR_GRID_AREA) proj_log_debug(P, "deformation: coordinate (%.3f, %.3f) outside deformation model", @@ -163,7 +169,7 @@ static PJ_XYZ reverse_shift(PJ *P, PJ_XYZ input, double dt) { } static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + struct deformationData *Q = (struct deformationData *) P->opaque; PJ_COORD out, in; PJ_XYZ shift; in.lpz = lpz; @@ -186,7 +192,7 @@ static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { static PJ_COORD forward_4d(PJ_COORD in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + struct deformationData *Q = (struct deformationData *) P->opaque; double dt; PJ_XYZ shift; PJ_COORD out = in; @@ -209,7 +215,7 @@ static PJ_COORD forward_4d(PJ_COORD in, PJ *P) { static PJ_LPZ reverse_3d(PJ_XYZ in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + struct deformationData *Q = (struct deformationData *) P->opaque; PJ_COORD out; out.xyz = in; @@ -225,7 +231,7 @@ static PJ_LPZ reverse_3d(PJ_XYZ in, PJ *P) { } static PJ_COORD reverse_4d(PJ_COORD in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + struct deformationData *Q = (struct deformationData *) P->opaque; PJ_COORD out = in; double dt; @@ -244,11 +250,14 @@ static PJ *destructor(PJ *P, int errlev) { if (nullptr==P) return nullptr; - if (nullptr==P->opaque) - return pj_default_destructor (P, errlev); - - if (static_cast<struct pj_opaque*>(P->opaque)->cart) - static_cast<struct pj_opaque*>(P->opaque)->cart->destructor (static_cast<struct pj_opaque*>(P->opaque)->cart, errlev); + auto Q = static_cast<struct deformationData*>(P->opaque); + if( Q ) + { + if (Q->cart) + Q->cart->destructor (Q->cart, errlev); + delete Q; + } + P->opaque = nullptr; return pj_default_destructor(P, errlev); } @@ -257,10 +266,9 @@ static PJ *destructor(PJ *P, int errlev) { PJ *TRANSFORMATION(deformation,1) { int has_xy_grids = 0; int has_z_grids = 0; - struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque))); - if (nullptr==Q) - return destructor(P, ENOMEM); + auto Q = new deformationData; P->opaque = (void *) Q; + P->destructor = destructor; // Pass a dummy ellipsoid definition that will be overridden just afterwards Q->cart = proj_create(P->ctx, "+proj=cart +a=1"); @@ -279,13 +287,13 @@ PJ *TRANSFORMATION(deformation,1) { return destructor(P, PJD_ERR_NO_ARGS ); } - proj_hgrid_init(P, "xy_grids"); + Q->hgrids = proj_hgrid_init(P, "xy_grids"); if (proj_errno(P)) { proj_log_error(P, "deformation: could not find requested xy_grid(s)."); return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); } - proj_vgrid_init(P, "z_grids"); + Q->vgrids = proj_vgrid_init(P, "z_grids"); if (proj_errno(P)) { proj_log_error(P, "deformation: could not find requested z_grid(s)."); return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); @@ -325,7 +333,6 @@ PJ *TRANSFORMATION(deformation,1) { P->left = PJ_IO_UNITS_CARTESIAN; P->right = PJ_IO_UNITS_CARTESIAN; - P->destructor = destructor; return P; } diff --git a/src/transformations/hgridshift.cpp b/src/transformations/hgridshift.cpp index 90633939..e9983df6 100644 --- a/src/transformations/hgridshift.cpp +++ b/src/transformations/hgridshift.cpp @@ -6,24 +6,29 @@ #include <time.h> #include "proj_internal.h" +#include "grids.hpp" PROJ_HEAD(hgridshift, "Horizontal grid shift"); +using namespace NS_PROJ; + namespace { // anonymous namespace -struct pj_opaque_hgridshift { - double t_final; - double t_epoch; +struct hgridshiftData { + double t_final = 0; + double t_epoch = 0; + ListOfHGrids grids{}; }; } // anonymous namespace static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { + auto Q = static_cast<hgridshiftData*>(P->opaque); PJ_COORD point = {{0,0,0,0}}; point.lpz = lpz; - if (P->gridlist != nullptr) { + if (!Q->grids.empty()) { /* Only try the gridshift if at least one grid is loaded, * otherwise just pass the coordinate through unchanged. */ - point.lp = proj_hgrid_apply(P, point.lp, PJ_FWD); + point.lp = proj_hgrid_apply(P, Q->grids, point.lp, PJ_FWD); } return point.xyz; @@ -31,20 +36,21 @@ static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) { + auto Q = static_cast<hgridshiftData*>(P->opaque); PJ_COORD point = {{0,0,0,0}}; point.xyz = xyz; - if (P->gridlist != nullptr) { + if (!Q->grids.empty()) { /* Only try the gridshift if at least one grid is loaded, * otherwise just pass the coordinate through unchanged. */ - point.lp = proj_hgrid_apply(P, point.lp, PJ_INV); + point.lp = proj_hgrid_apply(P, Q->grids, point.lp, PJ_INV); } return point.lpz; } static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; + struct hgridshiftData *Q = (struct hgridshiftData *) P->opaque; PJ_COORD point = obs; /* If transformation is not time restricted, we always call it */ @@ -62,7 +68,7 @@ static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { } static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; + struct hgridshiftData *Q = (struct hgridshiftData *) P->opaque; PJ_COORD point = obs; /* If transformation is not time restricted, we always call it */ @@ -78,12 +84,20 @@ static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { return point; } +static PJ *destructor (PJ *P, int errlev) { + if (nullptr==P) + return nullptr; + + delete static_cast<struct hgridshiftData*>(P->opaque); + P->opaque = nullptr; + + return pj_default_destructor(P, errlev); +} PJ *TRANSFORMATION(hgridshift,0) { - struct pj_opaque_hgridshift *Q = static_cast<struct pj_opaque_hgridshift*>(pj_calloc (1, sizeof (struct pj_opaque_hgridshift))); - if (nullptr==Q) - return pj_default_destructor (P, ENOMEM); + auto Q = new hgridshiftData; P->opaque = (void *) Q; + P->destructor = destructor; P->fwd4d = forward_4d; P->inv4d = reverse_4d; @@ -97,7 +111,7 @@ PJ *TRANSFORMATION(hgridshift,0) { if (0==pj_param(P->ctx, P->params, "tgrids").i) { proj_log_error(P, "hgridshift: +grids parameter missing."); - return pj_default_destructor (P, PJD_ERR_NO_ARGS); + return destructor (P, PJD_ERR_NO_ARGS); } /* TODO: Refactor into shared function that can be used */ @@ -121,11 +135,11 @@ PJ *TRANSFORMATION(hgridshift,0) { Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; - proj_hgrid_init(P, "grids"); + Q->grids = proj_hgrid_init(P, "grids"); /* Was gridlist compiled properly? */ if ( proj_errno(P) ) { proj_log_error(P, "hgridshift: could not find required grid(s)."); - return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); } return P; diff --git a/src/transformations/vgridshift.cpp b/src/transformations/vgridshift.cpp index de0cdd8c..b964f45b 100644 --- a/src/transformations/vgridshift.cpp +++ b/src/transformations/vgridshift.cpp @@ -6,26 +6,30 @@ #include <time.h> #include "proj_internal.h" +#include "grids.hpp" PROJ_HEAD(vgridshift, "Vertical grid shift"); +using namespace NS_PROJ; + namespace { // anonymous namespace -struct pj_opaque_vgridshift { - double t_final; - double t_epoch; - double forward_multiplier; +struct vgridshiftData { + double t_final = 0; + double t_epoch = 0; + double forward_multiplier = 0; + ListOfVGrids grids{}; }; } // anonymous namespace static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque; PJ_COORD point = {{0,0,0,0}}; point.lpz = lpz; - if (P->vgridlist_geoid != nullptr) { + if (!Q->grids.empty()) { /* Only try the gridshift if at least one grid is loaded, * otherwise just pass the coordinate through unchanged. */ - point.xyz.z += proj_vgrid_value(P, point.lp, Q->forward_multiplier); + point.xyz.z += proj_vgrid_value(P, Q->grids, point.lp, Q->forward_multiplier); } return point.xyz; @@ -33,14 +37,14 @@ static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) { static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque; PJ_COORD point = {{0,0,0,0}}; point.xyz = xyz; - if (P->vgridlist_geoid != nullptr) { + if (!Q->grids.empty()) { /* Only try the gridshift if at least one grid is loaded, * otherwise just pass the coordinate through unchanged. */ - point.xyz.z -= proj_vgrid_value(P, point.lp, Q->forward_multiplier); + point.xyz.z -= proj_vgrid_value(P, Q->grids, point.lp, Q->forward_multiplier); } return point.lpz; @@ -48,7 +52,7 @@ static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) { static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque; PJ_COORD point = obs; /* If transformation is not time restricted, we always call it */ @@ -66,7 +70,7 @@ static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { } static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + struct vgridshiftData *Q = (struct vgridshiftData *) P->opaque; PJ_COORD point = obs; /* If transformation is not time restricted, we always call it */ @@ -82,16 +86,24 @@ static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { return point; } +static PJ *destructor (PJ *P, int errlev) { + if (nullptr==P) + return nullptr; + + delete static_cast<struct vgridshiftData*>(P->opaque); + P->opaque = nullptr; + + return pj_default_destructor(P, errlev); +} PJ *TRANSFORMATION(vgridshift,0) { - struct pj_opaque_vgridshift *Q = static_cast<struct pj_opaque_vgridshift*>(pj_calloc (1, sizeof (struct pj_opaque_vgridshift))); - if (nullptr==Q) - return pj_default_destructor (P, ENOMEM); + auto Q = new vgridshiftData; P->opaque = (void *) Q; + P->destructor = destructor; if (!pj_param(P->ctx, P->params, "tgrids").i) { proj_log_error(P, "vgridshift: +grids parameter missing."); - return pj_default_destructor(P, PJD_ERR_NO_ARGS); + return destructor(P, PJD_ERR_NO_ARGS); } /* TODO: Refactor into shared function that can be used */ @@ -121,12 +133,12 @@ PJ *TRANSFORMATION(vgridshift,0) { } /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */ - proj_vgrid_init(P, "grids"); + Q->grids = proj_vgrid_init(P, "grids"); /* Was gridlist compiled properly? */ if ( proj_errno(P) ) { proj_log_error(P, "vgridshift: could not find required grid(s)."); - return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); } P->fwd4d = forward_4d; |
