/src/proj/src/conversions/axisswap.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /*********************************************************************** |
2 | | |
3 | | Axis order operation for use with transformation pipelines. |
4 | | |
5 | | Kristian Evers, kreve@sdfe.dk, 2017-10-31 |
6 | | |
7 | | ************************************************************************ |
8 | | |
9 | | Change the order and sign of 2,3 or 4 axes. Each of the possible four |
10 | | axes are numbered with 1-4, such that the first input axis is 1, the |
11 | | second is 2 and so on. The output ordering is controlled by a list of the |
12 | | input axes re-ordered to the new mapping. Examples: |
13 | | |
14 | | Reversing the order of the axes: |
15 | | |
16 | | +proj=axisswap +order=4,3,2,1 |
17 | | |
18 | | Swapping the first two axes (x and y): |
19 | | |
20 | | +proj=axisswap +order=2,1,3,4 |
21 | | |
22 | | The direction, or sign, of an axis can be changed by adding a minus in |
23 | | front of the axis-number: |
24 | | |
25 | | +proj=axisswap +order=1,-2,3,4 |
26 | | |
27 | | It is only necessary to specify the axes that are affected by the swap |
28 | | operation: |
29 | | |
30 | | +proj=axisswap +order=2,1 |
31 | | |
32 | | ************************************************************************ |
33 | | * Copyright (c) 2017, Kristian Evers / SDFE |
34 | | * |
35 | | * Permission is hereby granted, free of charge, to any person obtaining a |
36 | | * copy of this software and associated documentation files (the "Software"), |
37 | | * to deal in the Software without restriction, including without limitation |
38 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
39 | | * and/or sell copies of the Software, and to permit persons to whom the |
40 | | * Software is furnished to do so, subject to the following conditions: |
41 | | * |
42 | | * The above copyright notice and this permission notice shall be included |
43 | | * in all copies or substantial portions of the Software. |
44 | | * |
45 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
46 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
47 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
48 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
49 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
50 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
51 | | * DEALINGS IN THE SOFTWARE. |
52 | | * |
53 | | ***********************************************************************/ |
54 | | |
55 | | #include <errno.h> |
56 | | #include <stdlib.h> |
57 | | #include <string.h> |
58 | | |
59 | | #include <algorithm> |
60 | | |
61 | | #include "proj.h" |
62 | | #include "proj_internal.h" |
63 | | |
64 | | PROJ_HEAD(axisswap, "Axis ordering"); |
65 | | |
66 | | namespace { // anonymous namespace |
67 | | struct pj_axisswap_data { |
68 | | unsigned int axis[4]; |
69 | | int sign[4]; |
70 | | }; |
71 | | } // anonymous namespace |
72 | | |
73 | 2.57k | static int sign(int x) { return (x > 0) - (x < 0); } |
74 | | |
75 | 0 | static PJ_XY pj_axisswap_forward_2d(PJ_LP lp, PJ *P) { |
76 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
77 | 0 | PJ_XY xy; |
78 | |
|
79 | 0 | double in[2] = {lp.lam, lp.phi}; |
80 | 0 | xy.x = in[Q->axis[0]] * Q->sign[0]; |
81 | 0 | xy.y = in[Q->axis[1]] * Q->sign[1]; |
82 | 0 | return xy; |
83 | 0 | } |
84 | | |
85 | 0 | static PJ_LP pj_axisswap_reverse_2d(PJ_XY xy, PJ *P) { |
86 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
87 | 0 | unsigned int i; |
88 | 0 | PJ_COORD out, in; |
89 | |
|
90 | 0 | in.v[0] = xy.x; |
91 | 0 | in.v[1] = xy.y; |
92 | 0 | out = proj_coord_error(); |
93 | |
|
94 | 0 | for (i = 0; i < 2; i++) |
95 | 0 | out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; |
96 | |
|
97 | 0 | return out.lp; |
98 | 0 | } |
99 | | |
100 | 0 | static PJ_XYZ pj_axisswap_forward_3d(PJ_LPZ lpz, PJ *P) { |
101 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
102 | 0 | unsigned int i; |
103 | 0 | PJ_COORD out, in; |
104 | |
|
105 | 0 | in.v[0] = lpz.lam; |
106 | 0 | in.v[1] = lpz.phi; |
107 | 0 | in.v[2] = lpz.z; |
108 | 0 | out = proj_coord_error(); |
109 | |
|
110 | 0 | for (i = 0; i < 3; i++) |
111 | 0 | out.v[i] = in.v[Q->axis[i]] * Q->sign[i]; |
112 | |
|
113 | 0 | return out.xyz; |
114 | 0 | } |
115 | | |
116 | 0 | static PJ_LPZ pj_axisswap_reverse_3d(PJ_XYZ xyz, PJ *P) { |
117 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
118 | 0 | unsigned int i; |
119 | 0 | PJ_COORD in, out; |
120 | |
|
121 | 0 | out = proj_coord_error(); |
122 | 0 | in.v[0] = xyz.x; |
123 | 0 | in.v[1] = xyz.y; |
124 | 0 | in.v[2] = xyz.z; |
125 | |
|
126 | 0 | for (i = 0; i < 3; i++) |
127 | 0 | out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; |
128 | |
|
129 | 0 | return out.lpz; |
130 | 0 | } |
131 | | |
132 | 0 | static void swap_xy_4d(PJ_COORD &coo, PJ *) { |
133 | 0 | std::swap(coo.xyzt.x, coo.xyzt.y); |
134 | 0 | } |
135 | | |
136 | 0 | static void pj_axisswap_forward_4d(PJ_COORD &coo, PJ *P) { |
137 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
138 | 0 | unsigned int i; |
139 | 0 | PJ_COORD out; |
140 | |
|
141 | 0 | for (i = 0; i < 4; i++) |
142 | 0 | out.v[i] = coo.v[Q->axis[i]] * Q->sign[i]; |
143 | 0 | coo = out; |
144 | 0 | } |
145 | | |
146 | 0 | static void pj_axisswap_reverse_4d(PJ_COORD &coo, PJ *P) { |
147 | 0 | struct pj_axisswap_data *Q = (struct pj_axisswap_data *)P->opaque; |
148 | 0 | unsigned int i; |
149 | 0 | PJ_COORD out; |
150 | |
|
151 | 0 | for (i = 0; i < 4; i++) |
152 | 0 | out.v[Q->axis[i]] = coo.v[i] * Q->sign[i]; |
153 | |
|
154 | 0 | coo = out; |
155 | 0 | } |
156 | | |
157 | | /***********************************************************************/ |
158 | 2.48k | PJ *PJ_CONVERSION(axisswap, 0) { |
159 | | /***********************************************************************/ |
160 | 2.48k | struct pj_axisswap_data *Q = static_cast<struct pj_axisswap_data *>( |
161 | 2.48k | calloc(1, sizeof(struct pj_axisswap_data))); |
162 | 2.48k | char *s; |
163 | 2.48k | unsigned int i, j, n = 0; |
164 | | |
165 | 2.48k | if (nullptr == Q) |
166 | 0 | return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/); |
167 | 2.48k | P->opaque = (void *)Q; |
168 | | |
169 | | /* +order and +axis are mutually exclusive */ |
170 | 2.48k | if (!pj_param_exists(P->params, "order") == |
171 | 2.48k | !pj_param_exists(P->params, "axis")) { |
172 | 21 | proj_log_error(P, |
173 | 21 | _("must provide EITHER 'order' OR 'axis' parameter.")); |
174 | 21 | return pj_default_destructor( |
175 | 21 | P, PROJ_ERR_INVALID_OP_MUTUALLY_EXCLUSIVE_ARGS); |
176 | 21 | } |
177 | | |
178 | | /* fill axis list with indices from 4-7 to simplify duplicate search further |
179 | | * down */ |
180 | 12.3k | for (i = 0; i < 4; i++) { |
181 | 9.84k | Q->axis[i] = i + 4; |
182 | 9.84k | Q->sign[i] = 1; |
183 | 9.84k | } |
184 | | |
185 | | /* if the "order" parameter is used */ |
186 | 2.46k | if (pj_param_exists(P->params, "order")) { |
187 | | /* read axis order */ |
188 | 1.19k | char *order = pj_param(P->ctx, P->params, "sorder").s; |
189 | | |
190 | | /* check that all characters are valid */ |
191 | 17.0k | for (i = 0; i < strlen(order); i++) |
192 | 15.9k | if (strchr("1234-,", order[i]) == nullptr) { |
193 | 86 | proj_log_error(P, _("unknown axis '%c'"), order[i]); |
194 | 86 | return pj_default_destructor( |
195 | 86 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
196 | 86 | } |
197 | | |
198 | | /* read axes numbers and signs */ |
199 | 1.11k | s = order; |
200 | 1.11k | n = 0; |
201 | 3.68k | while (*s != '\0' && n < 4) { |
202 | 2.59k | Q->axis[n] = abs(atoi(s)) - 1; |
203 | 2.59k | if (Q->axis[n] > 3) { |
204 | 18 | proj_log_error(P, _("invalid axis '%d'"), Q->axis[n]); |
205 | 18 | return pj_default_destructor( |
206 | 18 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
207 | 18 | } |
208 | 2.57k | Q->sign[n++] = sign(atoi(s)); |
209 | 14.8k | while (*s != '\0' && *s != ',') |
210 | 12.2k | s++; |
211 | 2.57k | if (*s == ',') |
212 | 1.68k | s++; |
213 | 2.57k | } |
214 | 1.11k | } |
215 | | |
216 | | /* if the "axis" parameter is used */ |
217 | 2.35k | if (pj_param_exists(P->params, "axis")) { |
218 | | /* parse the classic PROJ.4 enu axis specification */ |
219 | 5.06k | for (i = 0; i < 3; i++) { |
220 | 3.79k | switch (P->axis[i]) { |
221 | 1.14k | case 'w': |
222 | 1.14k | Q->sign[i] = -1; |
223 | 1.14k | Q->axis[i] = 0; |
224 | 1.14k | break; |
225 | 125 | case 'e': |
226 | 125 | Q->sign[i] = 1; |
227 | 125 | Q->axis[i] = 0; |
228 | 125 | break; |
229 | 1.21k | case 's': |
230 | 1.21k | Q->sign[i] = -1; |
231 | 1.21k | Q->axis[i] = 1; |
232 | 1.21k | break; |
233 | 51 | case 'n': |
234 | 51 | Q->sign[i] = 1; |
235 | 51 | Q->axis[i] = 1; |
236 | 51 | break; |
237 | 19 | case 'd': |
238 | 19 | Q->sign[i] = -1; |
239 | 19 | Q->axis[i] = 2; |
240 | 19 | break; |
241 | 1.24k | case 'u': |
242 | 1.24k | Q->sign[i] = 1; |
243 | 1.24k | Q->axis[i] = 2; |
244 | 1.24k | break; |
245 | 0 | default: |
246 | 0 | proj_log_error(P, _("unknown axis '%c'"), P->axis[i]); |
247 | 0 | return pj_default_destructor( |
248 | 0 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
249 | 3.79k | } |
250 | 3.79k | } |
251 | 1.26k | n = 3; |
252 | 1.26k | } |
253 | | |
254 | | /* check for duplicate axes */ |
255 | 11.4k | for (i = 0; i < 4; i++) |
256 | 45.7k | for (j = 0; j < 4; j++) { |
257 | 36.6k | if (i == j) |
258 | 9.17k | continue; |
259 | 27.4k | if (Q->axis[i] == Q->axis[j]) { |
260 | 106 | proj_log_error(P, _("axisswap: duplicate axes specified")); |
261 | 106 | return pj_default_destructor( |
262 | 106 | P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
263 | 106 | } |
264 | 27.4k | } |
265 | | |
266 | | /* only map fwd/inv functions that are possible with the given axis setup */ |
267 | 2.25k | if (n == 4) { |
268 | 5 | P->fwd4d = pj_axisswap_forward_4d; |
269 | 5 | P->inv4d = pj_axisswap_reverse_4d; |
270 | 5 | } |
271 | 2.25k | if (n == 3 && Q->axis[0] < 3 && Q->axis[1] < 3 && Q->axis[2] < 3) { |
272 | 1.41k | P->fwd3d = pj_axisswap_forward_3d; |
273 | 1.41k | P->inv3d = pj_axisswap_reverse_3d; |
274 | 1.41k | } |
275 | 2.25k | if (n == 2) { |
276 | 829 | if (Q->axis[0] == 1 && Q->sign[0] == 1 && Q->axis[1] == 0 && |
277 | 829 | Q->sign[1] == 1) { |
278 | 389 | P->fwd4d = swap_xy_4d; |
279 | 389 | P->inv4d = swap_xy_4d; |
280 | 440 | } else if (Q->axis[0] < 2 && Q->axis[1] < 2) { |
281 | 426 | P->fwd = pj_axisswap_forward_2d; |
282 | 426 | P->inv = pj_axisswap_reverse_2d; |
283 | 426 | } |
284 | 829 | } |
285 | | |
286 | 2.25k | if (P->fwd4d == nullptr && P->fwd3d == nullptr && P->fwd == nullptr) { |
287 | 20 | proj_log_error(P, _("axisswap: bad axis order")); |
288 | 20 | return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE); |
289 | 20 | } |
290 | | |
291 | 2.23k | if (pj_param(P->ctx, P->params, "tangularunits").i) { |
292 | 0 | P->left = PJ_IO_UNITS_RADIANS; |
293 | 0 | P->right = PJ_IO_UNITS_RADIANS; |
294 | 2.23k | } else { |
295 | 2.23k | P->left = PJ_IO_UNITS_WHATEVER; |
296 | 2.23k | P->right = PJ_IO_UNITS_WHATEVER; |
297 | 2.23k | } |
298 | | |
299 | | /* Preparation and finalization steps are skipped, since the reason */ |
300 | | /* d'etre of axisswap is to bring input coordinates in line with the */ |
301 | | /* the internally expected order (ENU), such that handling of offsets */ |
302 | | /* etc. can be done correctly in a later step of a pipeline */ |
303 | 2.23k | P->skip_fwd_prepare = 1; |
304 | 2.23k | P->skip_fwd_finalize = 1; |
305 | 2.23k | P->skip_inv_prepare = 1; |
306 | 2.23k | P->skip_inv_finalize = 1; |
307 | | |
308 | 2.23k | return P; |
309 | 2.25k | } |