/src/PROJ/src/internal.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * Project: PROJ.4 |
3 | | * Purpose: This is primarily material originating from pj_obs_api.c |
4 | | * (now proj_4D_api.c), that does not fit into the API |
5 | | * category. Hence this pile of tubings and fittings for |
6 | | * PROJ.4 internal plumbing. |
7 | | * |
8 | | * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05 |
9 | | * |
10 | | ****************************************************************************** |
11 | | * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE |
12 | | * |
13 | | * Permission is hereby granted, free of charge, to any person obtaining a |
14 | | * copy of this software and associated documentation files (the "Software"), |
15 | | * to deal in the Software without restriction, including without limitation |
16 | | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
17 | | * and/or sell copies of the Software, and to permit persons to whom the |
18 | | * Software is furnished to do so, subject to the following conditions: |
19 | | * |
20 | | * The above copyright notice and this permission notice shall be included |
21 | | * in all copies or substantial portions of the Software. |
22 | | * |
23 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
24 | | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
25 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
26 | | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
27 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
28 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
29 | | * DEALINGS IN THE SOFTWARE. |
30 | | *****************************************************************************/ |
31 | | |
32 | | #define FROM_PROJ_CPP |
33 | | |
34 | | #include <ctype.h> |
35 | | #include <errno.h> |
36 | | #include <math.h> |
37 | | #include <stdarg.h> |
38 | | #include <stddef.h> |
39 | | #include <stdlib.h> |
40 | | #include <string.h> |
41 | | |
42 | | #include "geodesic.h" |
43 | | #include "proj_internal.h" |
44 | | |
45 | | #include "proj/internal/internal.hpp" |
46 | | |
47 | | using namespace NS_PROJ::internal; |
48 | | |
49 | 158k | enum pj_io_units pj_left(PJ *P) { |
50 | 158k | enum pj_io_units u = P->inverted ? P->right : P->left; |
51 | 158k | if (u == PJ_IO_UNITS_CLASSIC) |
52 | 5.60k | return PJ_IO_UNITS_PROJECTED; |
53 | 153k | return u; |
54 | 158k | } |
55 | | |
56 | 85.2k | enum pj_io_units pj_right(PJ *P) { |
57 | 85.2k | enum pj_io_units u = P->inverted ? P->left : P->right; |
58 | 85.2k | if (u == PJ_IO_UNITS_CLASSIC) |
59 | 5.27k | return PJ_IO_UNITS_PROJECTED; |
60 | 79.9k | return u; |
61 | 85.2k | } |
62 | | |
63 | | /* Work around non-constness of MSVC HUGE_VAL by providing functions rather than |
64 | | * constants */ |
65 | 627k | PJ_COORD proj_coord_error(void) { |
66 | 627k | PJ_COORD c; |
67 | 627k | c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL; |
68 | 627k | return c; |
69 | 627k | } |
70 | | |
71 | | /**************************************************************************************/ |
72 | 0 | PJ_COORD pj_approx_2D_trans(PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { |
73 | | /*************************************************************************************** |
74 | | Behave mostly as proj_trans, but attempt to use 2D interfaces only. |
75 | | Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement |
76 | | chained calls starting out with a call to its 2D interface. |
77 | | ***************************************************************************************/ |
78 | 0 | if (nullptr == P) |
79 | 0 | return coo; |
80 | 0 | if (P->inverted) |
81 | 0 | direction = static_cast<PJ_DIRECTION>(-direction); |
82 | 0 | switch (direction) { |
83 | 0 | case PJ_FWD: { |
84 | 0 | const auto xy = pj_fwd(coo.lp, P); |
85 | 0 | coo.xy = xy; |
86 | 0 | return coo; |
87 | 0 | } |
88 | 0 | case PJ_INV: { |
89 | 0 | const auto lp = pj_inv(coo.xy, P); |
90 | 0 | coo.lp = lp; |
91 | 0 | return coo; |
92 | 0 | } |
93 | 0 | case PJ_IDENT: |
94 | 0 | break; |
95 | 0 | } |
96 | 0 | return coo; |
97 | 0 | } |
98 | | |
99 | | /**************************************************************************************/ |
100 | 0 | PJ_COORD pj_approx_3D_trans(PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { |
101 | | /*************************************************************************************** |
102 | | Companion to pj_approx_2D_trans. |
103 | | |
104 | | Behave mostly as proj_trans, but attempt to use 3D interfaces only. |
105 | | Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement |
106 | | chained calls starting out with a call to its 3D interface. |
107 | | ***************************************************************************************/ |
108 | 0 | if (nullptr == P) |
109 | 0 | return coo; |
110 | 0 | if (P->inverted) |
111 | 0 | direction = static_cast<PJ_DIRECTION>(-direction); |
112 | 0 | switch (direction) { |
113 | 0 | case PJ_FWD: { |
114 | 0 | const auto xyz = pj_fwd3d(coo.lpz, P); |
115 | 0 | coo.xyz = xyz; |
116 | 0 | return coo; |
117 | 0 | } |
118 | 0 | case PJ_INV: { |
119 | 0 | const auto lpz = pj_inv3d(coo.xyz, P); |
120 | 0 | coo.lpz = lpz; |
121 | 0 | return coo; |
122 | 0 | } |
123 | 0 | case PJ_IDENT: |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | return coo; |
127 | 0 | } |
128 | | |
129 | | /**************************************************************************************/ |
130 | 56.8k | int pj_has_inverse(PJ *P) { |
131 | | /*************************************************************************************** |
132 | | Check if a a PJ has an inverse. |
133 | | ***************************************************************************************/ |
134 | 56.8k | return ((P->inverted && (P->fwd || P->fwd3d || P->fwd4d)) || |
135 | 39.6k | (P->inv || P->inv3d || P->inv4d)); |
136 | 56.8k | } |
137 | | |
138 | | /* Move P to a new context - or to the default context if 0 is specified */ |
139 | 0 | void proj_context_set(PJ *P, PJ_CONTEXT *ctx) { |
140 | 0 | if (nullptr == ctx) |
141 | 0 | ctx = pj_get_default_ctx(); |
142 | 0 | proj_assign_context(P, ctx); |
143 | 0 | } |
144 | | |
145 | 0 | void proj_context_inherit(PJ *parent, PJ *child) { |
146 | 0 | if (nullptr == parent) |
147 | 0 | proj_assign_context(child, pj_get_default_ctx()); |
148 | 0 | else |
149 | 0 | proj_assign_context(child, pj_get_ctx(parent)); |
150 | 0 | } |
151 | | |
152 | | /*****************************************************************************/ |
153 | 433k | char *pj_chomp(char *c) { |
154 | | /****************************************************************************** |
155 | | Strip pre- and postfix whitespace. Inline comments (indicated by '#') are |
156 | | considered whitespace. |
157 | | ******************************************************************************/ |
158 | 433k | size_t i, n; |
159 | 433k | char *comment; |
160 | 433k | char *start = c; |
161 | | |
162 | 433k | if (nullptr == c) |
163 | 0 | return nullptr; |
164 | | |
165 | 433k | comment = strchr(c, '#'); |
166 | 433k | if (comment) |
167 | 8.34k | *comment = 0; |
168 | | |
169 | 433k | n = strlen(c); |
170 | 433k | if (0 == n) |
171 | 17.7k | return c; |
172 | | |
173 | | /* Eliminate postfix whitespace */ |
174 | 419k | for (i = n - 1; (i > 0) && (isspace(c[i]) || ';' == c[i]); i--) |
175 | 3.19k | c[i] = 0; |
176 | | |
177 | | /* Find start of non-whitespace */ |
178 | 417k | while (0 != *start && (';' == *start || isspace(*start))) |
179 | 1.66k | start++; |
180 | | |
181 | 415k | n = strlen(start); |
182 | 415k | if (0 == n) { |
183 | 2 | c[0] = 0; |
184 | 2 | return c; |
185 | 2 | } |
186 | | |
187 | 415k | memmove(c, start, n + 1); |
188 | 415k | return c; |
189 | 415k | } |
190 | | |
191 | | /*****************************************************************************/ |
192 | 425k | char *pj_shrink(char *c) { |
193 | | /****************************************************************************** |
194 | | Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy, |
195 | | consuming their surrounding whitespace. |
196 | | ******************************************************************************/ |
197 | 425k | size_t i, j, n; |
198 | | |
199 | | /* Flag showing that a whitespace (ws) has been written after last non-ws */ |
200 | 425k | bool ws = false; |
201 | | |
202 | 425k | if (nullptr == c) |
203 | 0 | return nullptr; |
204 | | |
205 | 425k | pj_chomp(c); |
206 | 425k | n = strlen(c); |
207 | 425k | if (n == 0) |
208 | 10.3k | return c; |
209 | | |
210 | | /* First collapse repeated whitespace (including +/;) */ |
211 | 414k | i = 0; |
212 | 414k | bool in_string = false; |
213 | 130M | for (j = 0; j < n; j++) { |
214 | | |
215 | 129M | if (in_string) { |
216 | 14.0M | if (c[j] == '"' && c[j + 1] == '"') { |
217 | 192k | c[i++] = c[j]; |
218 | 192k | j++; |
219 | 13.8M | } else if (c[j] == '"') { |
220 | 197k | in_string = false; |
221 | 197k | } |
222 | 14.0M | c[i++] = c[j]; |
223 | 14.0M | continue; |
224 | 14.0M | } |
225 | | |
226 | | /* Eliminate prefix '+', only if preceded by whitespace */ |
227 | | /* (i.e. keep it in 1.23e+08) */ |
228 | 115M | if ((i > 0) && ('+' == c[j]) && ws) |
229 | 8.26M | c[j] = ' '; |
230 | 115M | if ((i == 0) && ('+' == c[j])) |
231 | 287k | c[j] = ' '; |
232 | | |
233 | | // Detect a string beginning after '=' |
234 | 115M | if (c[j] == '"' && i > 0 && c[i - 1] == '=') { |
235 | 221k | in_string = true; |
236 | 221k | ws = false; |
237 | 221k | c[i++] = c[j]; |
238 | 221k | continue; |
239 | 221k | } |
240 | | |
241 | 115M | if (isspace(c[j]) || ';' == c[j]) { |
242 | 20.7M | if (false == ws && (i > 0)) |
243 | 9.26M | c[i++] = ' '; |
244 | 20.7M | ws = true; |
245 | 20.7M | continue; |
246 | 94.9M | } else { |
247 | 94.9M | ws = false; |
248 | 94.9M | c[i++] = c[j]; |
249 | 94.9M | } |
250 | 115M | } |
251 | 414k | c[i] = 0; |
252 | 414k | n = strlen(c); |
253 | | |
254 | | /* Then make ',' and '=' greedy */ |
255 | 414k | i = 0; |
256 | 119M | for (j = 0; j < n; j++) { |
257 | 118M | if (i == 0) { |
258 | 414k | c[i++] = c[j]; |
259 | 414k | continue; |
260 | 414k | } |
261 | | |
262 | | /* Skip space before '='/',' */ |
263 | 118M | if ('=' == c[j] || ',' == c[j]) { |
264 | 7.45M | if (c[i - 1] == ' ') |
265 | 40.5k | c[i - 1] = c[j]; |
266 | 7.41M | else |
267 | 7.41M | c[i++] = c[j]; |
268 | 7.45M | continue; |
269 | 7.45M | } |
270 | | |
271 | 110M | if (' ' == c[j] && ('=' == c[i - 1] || ',' == c[i - 1])) |
272 | 127k | continue; |
273 | | |
274 | 110M | c[i++] = c[j]; |
275 | 110M | } |
276 | 414k | c[i] = 0; |
277 | 414k | return c; |
278 | 425k | } |
279 | | |
280 | | /*****************************************************************************/ |
281 | 424k | size_t pj_trim_argc(char *args) { |
282 | | /****************************************************************************** |
283 | | Trim all unnecessary whitespace (and non-essential syntactic tokens) from |
284 | | the argument string, args, and count its number of elements. |
285 | | ******************************************************************************/ |
286 | 424k | size_t i, m, n; |
287 | 424k | pj_shrink(args); |
288 | 424k | n = strlen(args); |
289 | 424k | if (n == 0) |
290 | 10.3k | return 0; |
291 | 414k | bool in_string = false; |
292 | 118M | for (i = m = 0; i < n; i++) { |
293 | 118M | if (in_string) { |
294 | 8.73M | if (args[i] == '"' && args[i + 1] == '"') { |
295 | 187k | i++; |
296 | 8.55M | } else if (args[i] == '"') { |
297 | 215k | in_string = false; |
298 | 215k | } |
299 | 109M | } else if (args[i] == '=' && args[i + 1] == '"') { |
300 | 223k | i++; |
301 | 223k | in_string = true; |
302 | 109M | } else if (' ' == args[i]) { |
303 | 9.86M | args[i] = 0; |
304 | 9.86M | m++; |
305 | 9.86M | } |
306 | 118M | } |
307 | 414k | return m + 1; |
308 | 424k | } |
309 | | |
310 | 10.2M | static void unquote_string(char *param_str) { |
311 | | |
312 | 10.2M | size_t len = strlen(param_str); |
313 | | // Remove leading and terminating spaces after equal sign |
314 | 10.2M | const char *equal = strstr(param_str, "=\""); |
315 | 10.2M | if (equal && equal - param_str + 1 >= 2 && param_str[len - 1] == '"') { |
316 | 103k | size_t dst = equal + 1 - param_str; |
317 | 103k | size_t src = dst + 1; |
318 | 5.38M | for (; param_str[src]; dst++, src++) { |
319 | 5.38M | if (param_str[src] == '"') { |
320 | 272k | if (param_str[src + 1] == '"') { |
321 | 169k | src++; |
322 | 169k | } else { |
323 | 102k | break; |
324 | 102k | } |
325 | 272k | } |
326 | 5.28M | param_str[dst] = param_str[src]; |
327 | 5.28M | } |
328 | 103k | param_str[dst] = '\0'; |
329 | 103k | } |
330 | 10.2M | } |
331 | | |
332 | | /*****************************************************************************/ |
333 | 424k | char **pj_trim_argv(size_t argc, char *args) { |
334 | | /****************************************************************************** |
335 | | Create an argv-style array from elements placed in the argument string, |
336 | | args. |
337 | | |
338 | | args is a trimmed string as returned by pj_trim_argc(), and argc is the |
339 | | number of trimmed strings found (i.e. the return value of pj_trim_args()). |
340 | | Hence, int argc = pj_trim_argc (args); char **argv = pj_trim_argv (argc, |
341 | | args); will produce a classic style (argc, argv) pair from a string of |
342 | | whitespace separated args. No new memory is allocated for storing the |
343 | | individual args (they stay in the args string), but for the pointers to the |
344 | | args a new array is allocated and returned. |
345 | | |
346 | | It is the duty of the caller to free this array. |
347 | | ******************************************************************************/ |
348 | | |
349 | 424k | if (nullptr == args) |
350 | 0 | return nullptr; |
351 | 424k | if (0 == argc) |
352 | 10.3k | return nullptr; |
353 | | |
354 | | /* turn the input string into an array of strings */ |
355 | 414k | char **argv = (char **)calloc(argc, sizeof(char *)); |
356 | 414k | if (nullptr == argv) |
357 | 0 | return nullptr; |
358 | 10.6M | for (size_t i = 0, j = 0; j < argc; j++) { |
359 | 10.2M | argv[j] = args + i; |
360 | 10.2M | char *str = argv[j]; |
361 | 10.2M | size_t nLen = strlen(str); |
362 | 10.2M | i += nLen + 1; |
363 | 10.2M | unquote_string(str); |
364 | 10.2M | } |
365 | 414k | return argv; |
366 | 414k | } |
367 | | |
368 | | /*****************************************************************************/ |
369 | 3.12M | std::string pj_double_quote_string_param_if_needed(const std::string &str) { |
370 | | /*****************************************************************************/ |
371 | 3.12M | if (str.find(' ') == std::string::npos) { |
372 | 3.06M | return str; |
373 | 3.06M | } |
374 | 61.2k | std::string ret; |
375 | 61.2k | ret += '"'; |
376 | 61.2k | ret += replaceAll(str, "\"", "\"\""); |
377 | 61.2k | ret += '"'; |
378 | 61.2k | return ret; |
379 | 3.12M | } |
380 | | |
381 | | /*****************************************************************************/ |
382 | 0 | char *pj_make_args(size_t argc, char **argv) { |
383 | | /****************************************************************************** |
384 | | pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It |
385 | | converts free format command line input to something proj_create can |
386 | | consume. |
387 | | |
388 | | Allocates, and returns, an array of char, large enough to hold a whitespace |
389 | | separated copy of the args in argv. It is the duty of the caller to free |
390 | | this array. |
391 | | ******************************************************************************/ |
392 | 0 | try { |
393 | 0 | std::string s; |
394 | 0 | for (size_t i = 0; i < argc; i++) { |
395 | 0 | const char *equal = strchr(argv[i], '='); |
396 | 0 | if (equal) { |
397 | 0 | s += std::string(argv[i], equal - argv[i] + 1); |
398 | 0 | s += pj_double_quote_string_param_if_needed(equal + 1); |
399 | 0 | } else { |
400 | 0 | s += argv[i]; |
401 | 0 | } |
402 | 0 | s += ' '; |
403 | 0 | } |
404 | |
|
405 | 0 | char *p = pj_strdup(s.c_str()); |
406 | 0 | return pj_shrink(p); |
407 | 0 | } catch (const std::exception &) { |
408 | 0 | return nullptr; |
409 | 0 | } |
410 | 0 | } |
411 | | |
412 | | /*****************************************************************************/ |
413 | 2.02M | void proj_context_errno_set(PJ_CONTEXT *ctx, int err) { |
414 | | /****************************************************************************** |
415 | | Raise an error directly on a context, without going through a PJ belonging |
416 | | to that context. |
417 | | ******************************************************************************/ |
418 | 2.02M | if (nullptr == ctx) |
419 | 0 | ctx = pj_get_default_ctx(); |
420 | 2.02M | ctx->last_errno = err; |
421 | 2.02M | if (err == 0) |
422 | 1.08M | return; |
423 | 2.02M | errno = err; |
424 | 946k | } |