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