Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * Project: PROJ.4 |
3 | | * Purpose: Inverse operation invocation |
4 | | * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 |
5 | | * Based on material from Gerald Evenden (original pj_inv) |
6 | | * and Piyush Agram (original pj_inv3d) |
7 | | * |
8 | | ****************************************************************************** |
9 | | * Copyright (c) 2000, Frank Warmerdam |
10 | | * Copyright (c) 2018, Thomas Knudsen / SDFE |
11 | | * |
12 | | * Permission is hereby granted, free of charge, to any person obtaining a |
13 | | * copy of this software and associated documentation files (the "Software"), |
14 | | * to deal in the Software without restriction, including without limitation |
15 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
16 | | * and/or sell copies of the Software, and to permit persons to whom the |
17 | | * Software is furnished to do so, subject to the following conditions: |
18 | | * |
19 | | * The above copyright notice and this permission notice shall be included |
20 | | * in all copies or substantial portions of the Software. |
21 | | * |
22 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
23 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
24 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
25 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
26 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
27 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
28 | | * DEALINGS IN THE SOFTWARE. |
29 | | *****************************************************************************/ |
30 | | #include <errno.h> |
31 | | #include <math.h> |
32 | | |
33 | | #include "proj_internal.h" |
34 | | #include <math.h> |
35 | | |
36 | 4 | #define INPUT_UNITS P->right |
37 | 4 | #define OUTPUT_UNITS P->left |
38 | | |
39 | 4 | static void inv_prepare(PJ *P, PJ_COORD &coo) { |
40 | 4 | if (coo.v[0] == HUGE_VAL || coo.v[1] == HUGE_VAL || coo.v[2] == HUGE_VAL) { |
41 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
42 | 0 | coo = proj_coord_error(); |
43 | 0 | return; |
44 | 0 | } |
45 | | |
46 | | /* The helmert datum shift will choke unless it gets a sensible 4D |
47 | | * coordinate |
48 | | */ |
49 | 4 | if (HUGE_VAL == coo.v[2] && P->helmert) |
50 | 0 | coo.v[2] = 0.0; |
51 | 4 | if (HUGE_VAL == coo.v[3] && P->helmert) |
52 | 0 | coo.v[3] = 0.0; |
53 | | |
54 | 4 | if (P->axisswap) |
55 | 0 | coo = proj_trans(P->axisswap, PJ_INV, coo); |
56 | | |
57 | | /* Handle remaining possible input types */ |
58 | 4 | switch (INPUT_UNITS) { |
59 | 0 | case PJ_IO_UNITS_WHATEVER: |
60 | 0 | break; |
61 | | |
62 | 0 | case PJ_IO_UNITS_DEGREES: |
63 | 0 | break; |
64 | | |
65 | | /* de-scale and de-offset */ |
66 | 4 | case PJ_IO_UNITS_CARTESIAN: |
67 | 4 | coo.xyz.x *= P->to_meter; |
68 | 4 | coo.xyz.y *= P->to_meter; |
69 | 4 | coo.xyz.z *= P->to_meter; |
70 | 4 | if (P->is_geocent) { |
71 | 0 | coo = proj_trans(P->cart, PJ_INV, coo); |
72 | 0 | } |
73 | 4 | break; |
74 | | |
75 | 0 | case PJ_IO_UNITS_PROJECTED: |
76 | 0 | case PJ_IO_UNITS_CLASSIC: |
77 | 0 | coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; |
78 | 0 | coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; |
79 | 0 | coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0; |
80 | 0 | if (INPUT_UNITS == PJ_IO_UNITS_PROJECTED) |
81 | 0 | return; |
82 | | |
83 | | /* Classic proj.4 functions expect plane coordinates in units of the |
84 | | * semimajor axis */ |
85 | | /* Multiplying by ra, rather than dividing by a because the CalCOFI |
86 | | * projection */ |
87 | | /* stomps on a and hence (apparently) depends on this to roundtrip |
88 | | * correctly |
89 | | */ |
90 | | /* (CalCOFI avoids further scaling by stomping - but a better solution |
91 | | * is possible) */ |
92 | 0 | coo.xyz.x *= P->ra; |
93 | 0 | coo.xyz.y *= P->ra; |
94 | 0 | break; |
95 | | |
96 | 0 | case PJ_IO_UNITS_RADIANS: |
97 | 0 | coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0; |
98 | 0 | break; |
99 | 4 | } |
100 | 4 | } |
101 | | |
102 | 4 | static void inv_finalize(PJ *P, PJ_COORD &coo) { |
103 | 4 | if (coo.xyz.x == HUGE_VAL) { |
104 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
105 | 0 | coo = proj_coord_error(); |
106 | 0 | } |
107 | | |
108 | 4 | if (OUTPUT_UNITS == PJ_IO_UNITS_RADIANS) { |
109 | | |
110 | | /* Distance from central meridian, taking system zero meridian into |
111 | | * account |
112 | | */ |
113 | 4 | coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; |
114 | | |
115 | | /* adjust longitude to central meridian */ |
116 | 4 | if (0 == P->over) |
117 | 4 | coo.lpz.lam = adjlon(coo.lpz.lam); |
118 | | |
119 | 4 | if (P->vgridshift) |
120 | 0 | coo = proj_trans(P->vgridshift, PJ_INV, |
121 | 0 | coo); /* Go geometric from orthometric */ |
122 | 4 | if (coo.lp.lam == HUGE_VAL) |
123 | 0 | return; |
124 | 4 | if (P->hgridshift) |
125 | 0 | coo = proj_trans(P->hgridshift, PJ_FWD, coo); |
126 | 4 | else if (P->helmert || |
127 | 4 | (P->cart_wgs84 != nullptr && P->cart != nullptr)) { |
128 | 0 | coo = proj_trans(P->cart, PJ_FWD, |
129 | 0 | coo); /* Go cartesian in local frame */ |
130 | 0 | if (P->helmert) |
131 | 0 | coo = proj_trans(P->helmert, PJ_FWD, coo); /* Step into WGS84 */ |
132 | 0 | coo = proj_trans(P->cart_wgs84, PJ_INV, |
133 | 0 | coo); /* Go back to angular using WGS84 ellps */ |
134 | 0 | } |
135 | 4 | if (coo.lp.lam == HUGE_VAL) |
136 | 0 | return; |
137 | | |
138 | | /* If input latitude was geocentrical, convert back to geocentrical */ |
139 | 4 | if (P->geoc) |
140 | 0 | coo = pj_geocentric_latitude(P, PJ_FWD, coo); |
141 | 4 | } |
142 | 4 | } |
143 | | |
144 | 4 | static inline PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { |
145 | 4 | if (P->ctx->last_errno) |
146 | 0 | return proj_coord_error(); |
147 | | |
148 | 4 | P->ctx->last_errno = last_errno; |
149 | | |
150 | 4 | return coord; |
151 | 4 | } |
152 | | |
153 | 0 | PJ_LP pj_inv(PJ_XY xy, PJ *P) { |
154 | 0 | PJ_COORD coo = {{0, 0, 0, 0}}; |
155 | 0 | coo.xy = xy; |
156 | |
|
157 | 0 | const int last_errno = P->ctx->last_errno; |
158 | 0 | P->ctx->last_errno = 0; |
159 | |
|
160 | 0 | if (!P->skip_inv_prepare) |
161 | 0 | inv_prepare(P, coo); |
162 | 0 | if (HUGE_VAL == coo.v[0]) |
163 | 0 | return proj_coord_error().lp; |
164 | | |
165 | | /* Do the transformation, using the lowest dimensional transformer available |
166 | | */ |
167 | 0 | if (P->inv) { |
168 | 0 | const auto lp = P->inv(coo.xy, P); |
169 | 0 | coo.lp = lp; |
170 | 0 | } else if (P->inv3d) { |
171 | 0 | const auto lpz = P->inv3d(coo.xyz, P); |
172 | 0 | coo.lpz = lpz; |
173 | 0 | } else if (P->inv4d) |
174 | 0 | P->inv4d(coo, P); |
175 | 0 | else { |
176 | 0 | proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP); |
177 | 0 | return proj_coord_error().lp; |
178 | 0 | } |
179 | 0 | if (HUGE_VAL == coo.v[0]) |
180 | 0 | return proj_coord_error().lp; |
181 | | |
182 | 0 | if (!P->skip_inv_finalize) |
183 | 0 | inv_finalize(P, coo); |
184 | |
|
185 | 0 | return error_or_coord(P, coo, last_errno).lp; |
186 | 0 | } |
187 | | |
188 | 4 | PJ_LPZ pj_inv3d(PJ_XYZ xyz, PJ *P) { |
189 | 4 | PJ_COORD coo = {{0, 0, 0, 0}}; |
190 | 4 | coo.xyz = xyz; |
191 | | |
192 | 4 | const int last_errno = P->ctx->last_errno; |
193 | 4 | P->ctx->last_errno = 0; |
194 | | |
195 | 4 | if (!P->skip_inv_prepare) |
196 | 4 | inv_prepare(P, coo); |
197 | 4 | if (HUGE_VAL == coo.v[0]) |
198 | 0 | return proj_coord_error().lpz; |
199 | | |
200 | | /* Do the transformation, using the lowest dimensional transformer feasible |
201 | | */ |
202 | 4 | if (P->inv3d) { |
203 | 4 | const auto lpz = P->inv3d(coo.xyz, P); |
204 | 4 | coo.lpz = lpz; |
205 | 4 | } else if (P->inv4d) |
206 | 0 | P->inv4d(coo, P); |
207 | 0 | else if (P->inv) { |
208 | 0 | const auto lp = P->inv(coo.xy, P); |
209 | 0 | coo.lp = lp; |
210 | 0 | } else { |
211 | 0 | proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP); |
212 | 0 | return proj_coord_error().lpz; |
213 | 0 | } |
214 | 4 | if (HUGE_VAL == coo.v[0]) |
215 | 0 | return proj_coord_error().lpz; |
216 | | |
217 | 4 | if (!P->skip_inv_finalize) |
218 | 4 | inv_finalize(P, coo); |
219 | | |
220 | 4 | return error_or_coord(P, coo, last_errno).lpz; |
221 | 4 | } |
222 | | |
223 | 0 | bool pj_inv4d(PJ_COORD &coo, PJ *P) { |
224 | |
|
225 | 0 | const int last_errno = P->ctx->last_errno; |
226 | 0 | P->ctx->last_errno = 0; |
227 | |
|
228 | 0 | if (!P->skip_inv_prepare) |
229 | 0 | inv_prepare(P, coo); |
230 | 0 | if (HUGE_VAL == coo.v[0]) { |
231 | 0 | coo = proj_coord_error(); |
232 | 0 | return false; |
233 | 0 | } |
234 | | |
235 | | /* Call the highest dimensional converter available */ |
236 | 0 | if (P->inv4d) |
237 | 0 | P->inv4d(coo, P); |
238 | 0 | else if (P->inv3d) { |
239 | 0 | const auto lpz = P->inv3d(coo.xyz, P); |
240 | 0 | coo.lpz = lpz; |
241 | 0 | } else if (P->inv) { |
242 | 0 | const auto lp = P->inv(coo.xy, P); |
243 | 0 | coo.lp = lp; |
244 | 0 | } else { |
245 | 0 | proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP); |
246 | 0 | coo = proj_coord_error(); |
247 | 0 | return false; |
248 | 0 | } |
249 | 0 | if (HUGE_VAL == coo.v[0]) { |
250 | 0 | coo = proj_coord_error(); |
251 | 0 | return false; |
252 | 0 | } |
253 | | |
254 | 0 | if (!P->skip_inv_finalize) |
255 | 0 | inv_finalize(P, coo); |
256 | |
|
257 | 0 | if (P->ctx->last_errno) { |
258 | 0 | coo = proj_coord_error(); |
259 | 0 | return false; |
260 | 0 | } |
261 | | |
262 | 0 | P->ctx->last_errno = last_errno; |
263 | 0 | return true; |
264 | 0 | } |