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