1 #define PJ_LIB__
2 
3 #include <errno.h>
4 #include <string.h>
5 #include <stddef.h>
6 #include <time.h>
7 
8 #include "proj_internal.h"
9 #include "grids.hpp"
10 
11 PROJ_HEAD(hgridshift, "Horizontal grid shift");
12 
13 using namespace NS_PROJ;
14 
15 #ifdef __MINGW32__
16 // mingw32-win32 doesn't implement std::mutex
17 namespace {
18 class MyMutex {
19   public:
20     // cppcheck-suppress functionStatic
lock()21     void lock() { pj_acquire_lock(); }
22     // cppcheck-suppress functionStatic
unlock()23     void unlock() { pj_release_lock(); }
24 };
25 }
26 #else
27 #include <mutex>
28 #define MyMutex std::mutex
29 #endif
30 
31 static MyMutex gMutex{};
32 static std::set<std::string> gKnownGrids{};
33 
34 namespace { // anonymous namespace
35 struct hgridshiftData {
36     double t_final = 0;
37     double t_epoch = 0;
38     ListOfHGrids grids{};
39     bool defer_grid_opening = false;
40 };
41 } // anonymous namespace
42 
forward_3d(PJ_LPZ lpz,PJ * P)43 static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) {
44     auto Q = static_cast<hgridshiftData*>(P->opaque);
45     PJ_COORD point = {{0,0,0,0}};
46     point.lpz = lpz;
47 
48     if ( Q->defer_grid_opening ) {
49         Q->defer_grid_opening = false;
50         Q->grids = pj_hgrid_init(P, "grids");
51         if ( proj_errno(P) ) {
52             return proj_coord_error().xyz;
53         }
54     }
55 
56     if (!Q->grids.empty()) {
57         /* Only try the gridshift if at least one grid is loaded,
58          * otherwise just pass the coordinate through unchanged. */
59         point.lp = pj_hgrid_apply(P->ctx, Q->grids, point.lp, PJ_FWD);
60     }
61 
62     return point.xyz;
63 }
64 
65 
reverse_3d(PJ_XYZ xyz,PJ * P)66 static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) {
67     auto Q = static_cast<hgridshiftData*>(P->opaque);
68     PJ_COORD point = {{0,0,0,0}};
69     point.xyz = xyz;
70 
71     if ( Q->defer_grid_opening ) {
72         Q->defer_grid_opening = false;
73         Q->grids = pj_hgrid_init(P, "grids");
74         if ( proj_errno(P) ) {
75             return proj_coord_error().lpz;
76         }
77     }
78 
79     if (!Q->grids.empty()) {
80         /* Only try the gridshift if at least one grid is loaded,
81          * otherwise just pass the coordinate through unchanged. */
82         point.lp = pj_hgrid_apply(P->ctx, Q->grids, point.lp, PJ_INV);
83     }
84 
85     return point.lpz;
86 }
87 
forward_4d(PJ_COORD obs,PJ * P)88 static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) {
89     struct hgridshiftData *Q = (struct hgridshiftData *) P->opaque;
90     PJ_COORD point = obs;
91 
92     /* If transformation is not time restricted, we always call it */
93     if (Q->t_final==0 || Q->t_epoch==0) {
94         point.xyz = forward_3d (obs.lpz, P);
95         return point;
96     }
97 
98     /* Time restricted - only apply transform if within time bracket */
99     if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch)
100         point.xyz = forward_3d (obs.lpz, P);
101 
102 
103     return point;
104 }
105 
reverse_4d(PJ_COORD obs,PJ * P)106 static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) {
107     struct hgridshiftData *Q = (struct hgridshiftData *) P->opaque;
108     PJ_COORD point = obs;
109 
110     /* If transformation is not time restricted, we always call it */
111     if (Q->t_final==0 || Q->t_epoch==0) {
112         point.lpz = reverse_3d (obs.xyz, P);
113         return point;
114     }
115 
116     /* Time restricted - only apply transform if within time bracket */
117     if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch)
118         point.lpz = reverse_3d (obs.xyz, P);
119 
120     return point;
121 }
122 
destructor(PJ * P,int errlev)123 static PJ *destructor (PJ *P, int errlev) {
124     if (nullptr==P)
125         return nullptr;
126 
127     delete static_cast<struct hgridshiftData*>(P->opaque);
128     P->opaque = nullptr;
129 
130     return pj_default_destructor(P, errlev);
131 }
132 
reassign_context(PJ * P,PJ_CONTEXT * ctx)133 static void reassign_context( PJ* P, PJ_CONTEXT* ctx )
134 {
135     auto Q = (struct hgridshiftData *) P->opaque;
136     for( auto& grid: Q->grids ) {
137         grid->reassign_context(ctx);
138     }
139 }
140 
141 PJ *TRANSFORMATION(hgridshift,0) {
142     auto Q = new hgridshiftData;
143     P->opaque = (void *) Q;
144     P->destructor = destructor;
145     P->reassign_context = reassign_context;
146 
147     P->fwd4d  = forward_4d;
148     P->inv4d  = reverse_4d;
149     P->fwd3d  = forward_3d;
150     P->inv3d  = reverse_3d;
151     P->fwd    = nullptr;
152     P->inv    = nullptr;
153 
154     P->left  = PJ_IO_UNITS_RADIANS;
155     P->right = PJ_IO_UNITS_RADIANS;
156 
157     if (0==pj_param(P->ctx, P->params, "tgrids").i) {
158         proj_log_error(P, "hgridshift: +grids parameter missing.");
159         return destructor (P, PJD_ERR_NO_ARGS);
160     }
161 
162     /* TODO: Refactor into shared function that can be used  */
163     /*       by both vgridshift and hgridshift               */
164     if (pj_param(P->ctx, P->params, "tt_final").i) {
165         Q->t_final = pj_param (P->ctx, P->params, "dt_final").f;
166         if (Q->t_final == 0) {
167             /* a number wasn't passed to +t_final, let's see if it was "now" */
168             /* and set the time accordingly.                                 */
169             if (!strcmp("now", pj_param(P->ctx, P->params, "st_final").s)) {
170                     time_t now;
171                     struct tm *date;
172                     time(&now);
173                     date = localtime(&now);
174                     Q->t_final = 1900.0 + date->tm_year + date->tm_yday/365.0;
175             }
176         }
177     }
178 
179     if (pj_param(P->ctx, P->params, "tt_epoch").i)
180         Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f;
181 
182     if( P->ctx->defer_grid_opening ) {
183         Q->defer_grid_opening = true;
184     }
185     else {
186         const char *gridnames = pj_param(P->ctx, P->params, "sgrids").s;
187         gMutex.lock();
188         const bool isKnownGrid = gKnownGrids.find(gridnames) != gKnownGrids.end();
189         gMutex.unlock();
190         if( isKnownGrid ) {
191             Q->defer_grid_opening = true;
192         }
193         else {
194             Q->grids = pj_hgrid_init(P, "grids");
195             /* Was gridlist compiled properly? */
196             if ( proj_errno(P) ) {
197                 proj_log_error(P, "hgridshift: could not find required grid(s).");
198                 return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
199             }
200 
201             gMutex.lock();
202             gKnownGrids.insert(gridnames);
203             gMutex.unlock();
204         }
205     }
206 
207     return P;
208 }
209 
pj_clear_hgridshift_knowngrids_cache()210 void pj_clear_hgridshift_knowngrids_cache() {
211     gMutex.lock();
212     gKnownGrids.clear();
213     gMutex.unlock();
214 }
215