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
pj_left(PJ * P)49 enum pj_io_units pj_left (PJ *P) {
50 enum pj_io_units u = P->inverted? P->right: P->left;
51 if (u==PJ_IO_UNITS_CLASSIC)
52 return PJ_IO_UNITS_PROJECTED;
53 return u;
54 }
55
pj_right(PJ * P)56 enum pj_io_units pj_right (PJ *P) {
57 enum pj_io_units u = P->inverted? P->left: P->right;
58 if (u==PJ_IO_UNITS_CLASSIC)
59 return PJ_IO_UNITS_PROJECTED;
60 return u;
61 }
62
63
64 /* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */
proj_coord_error(void)65 PJ_COORD proj_coord_error (void) {
66 PJ_COORD c;
67 c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL;
68 return c;
69 }
70
71
72
73 /**************************************************************************************/
pj_approx_2D_trans(PJ * P,PJ_DIRECTION direction,PJ_COORD coo)74 PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
75 /***************************************************************************************
76 Behave mostly as proj_trans, but attempt to use 2D interfaces only.
77 Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement
78 chained calls starting out with a call to its 2D interface.
79 ***************************************************************************************/
80 if (nullptr==P)
81 return coo;
82 if (P->inverted)
83 direction = static_cast<PJ_DIRECTION>(-direction);
84 switch (direction) {
85 case PJ_FWD:
86 coo.xy = pj_fwd (coo.lp, P);
87 return coo;
88 case PJ_INV:
89 coo.lp = pj_inv (coo.xy, P);
90 return coo;
91 case PJ_IDENT:
92 return coo;
93 default:
94 break;
95 }
96 proj_errno_set (P, EINVAL);
97 return proj_coord_error ();
98 }
99
100
101 /**************************************************************************************/
pj_approx_3D_trans(PJ * P,PJ_DIRECTION direction,PJ_COORD coo)102 PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
103 /***************************************************************************************
104 Companion to pj_approx_2D_trans.
105
106 Behave mostly as proj_trans, but attempt to use 3D interfaces only.
107 Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement
108 chained calls starting out with a call to its 3D interface.
109 ***************************************************************************************/
110 if (nullptr==P)
111 return coo;
112 if (P->inverted)
113 direction = static_cast<PJ_DIRECTION>(-direction);
114 switch (direction) {
115 case PJ_FWD:
116 coo.xyz = pj_fwd3d (coo.lpz, P);
117 return coo;
118 case PJ_INV:
119 coo.lpz = pj_inv3d (coo.xyz, P);
120 return coo;
121 case PJ_IDENT:
122 return coo;
123 default:
124 break;
125 }
126 proj_errno_set (P, EINVAL);
127 return proj_coord_error ();
128 }
129
130 /**************************************************************************************/
pj_has_inverse(PJ * P)131 int pj_has_inverse(PJ *P) {
132 /***************************************************************************************
133 Check if a a PJ has an inverse.
134 ***************************************************************************************/
135 return ( (P->inverted && (P->fwd || P->fwd3d || P->fwd4d) ) ||
136 ( P->inv || P->inv3d || P->inv4d) );
137 }
138
139
140 /* Move P to a new context - or to the default context if 0 is specified */
proj_context_set(PJ * P,PJ_CONTEXT * ctx)141 void proj_context_set (PJ *P, PJ_CONTEXT *ctx) {
142 if (nullptr==ctx)
143 ctx = pj_get_default_ctx ();
144 pj_set_ctx (P, ctx);
145 }
146
147
proj_context_inherit(PJ * parent,PJ * child)148 void proj_context_inherit (PJ *parent, PJ *child) {
149 if (nullptr==parent)
150 pj_set_ctx (child, pj_get_default_ctx());
151 else
152 pj_set_ctx (child, pj_get_ctx(parent));
153 }
154
155
156
157 /*****************************************************************************/
pj_chomp(char * c)158 char *pj_chomp (char *c) {
159 /******************************************************************************
160 Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
161 considered whitespace.
162 ******************************************************************************/
163 size_t i, n;
164 char *comment;
165 char *start = c;
166
167 if (nullptr==c)
168 return nullptr;
169
170 comment = strchr (c, '#');
171 if (comment)
172 *comment = 0;
173
174 n = strlen (c);
175 if (0==n)
176 return c;
177
178 /* Eliminate postfix whitespace */
179 for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--)
180 c[i] = 0;
181
182 /* Find start of non-whitespace */
183 while (0 != *start && (';'==*start || isspace (*start)))
184 start++;
185
186 n = strlen (start);
187 if (0==n) {
188 c[0] = 0;
189 return c;
190 }
191
192 memmove (c, start, n + 1);
193 return c;
194 }
195
196
197
198 /*****************************************************************************/
pj_shrink(char * c)199 char *pj_shrink (char *c) {
200 /******************************************************************************
201 Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
202 consuming their surrounding whitespace.
203 ******************************************************************************/
204 size_t i, j, n;
205
206 /* Flag showing that a whitespace (ws) has been written after last non-ws */
207 bool ws = false;
208
209 if (nullptr==c)
210 return nullptr;
211
212 pj_chomp (c);
213 n = strlen (c);
214 if (n==0)
215 return c;
216
217 /* First collapse repeated whitespace (including +/;) */
218 i = 0;
219 bool in_string = false;
220 for (j = 0; j < n; j++) {
221
222 if( in_string ) {
223 if( c[j] == '"' && c[j+1] == '"' ) {
224 c[i++] = c[j];
225 j++;
226 } else if( c[j] == '"' ) {
227 in_string = false;
228 }
229 c[i++] = c[j];
230 continue;
231 }
232
233 /* Eliminate prefix '+', only if preceded by whitespace */
234 /* (i.e. keep it in 1.23e+08) */
235 if ((i > 0) && ('+'==c[j]) && ws)
236 c[j] = ' ';
237 if ((i==0) && ('+'==c[j]))
238 c[j] = ' ';
239
240 // Detect a string beginning after '='
241 if( c[j] == '"' && i > 0 && c[i-1] == '=' ) {
242 in_string = true;
243 ws = false;
244 c[i++] = c[j];
245 continue;
246 }
247
248 if (isspace (c[j]) || ';'==c[j]) {
249 if (false==ws && (i > 0))
250 c[i++] = ' ';
251 ws = true;
252 continue;
253 }
254 else {
255 ws = false;
256 c[i++] = c[j];
257 }
258 }
259 c[i] = 0;
260 n = strlen(c);
261
262 /* Then make ',' and '=' greedy */
263 i = 0;
264 for (j = 0; j < n; j++) {
265 if (i==0) {
266 c[i++] = c[j];
267 continue;
268 }
269
270 /* Skip space before '='/',' */
271 if ('='==c[j] || ','==c[j]) {
272 if (c[i - 1]==' ')
273 c[i - 1] = c[j];
274 else
275 c[i++] = c[j];
276 continue;
277 }
278
279 if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) )
280 continue;
281
282 c[i++] = c[j];
283 }
284 c[i] = 0;
285 return c;
286 }
287
288
289
290 /*****************************************************************************/
pj_trim_argc(char * args)291 size_t pj_trim_argc (char *args) {
292 /******************************************************************************
293 Trim all unnecessary whitespace (and non-essential syntactic tokens) from the
294 argument string, args, and count its number of elements.
295 ******************************************************************************/
296 size_t i, m, n;
297 pj_shrink (args);
298 n = strlen (args);
299 if (n==0)
300 return 0;
301 bool in_string = false;
302 for (i = m = 0; i < n; i++) {
303 if (in_string ) {
304 if( args[i] == '"' && args[i+1] == '"' ) {
305 i++;
306 } else if( args[i] == '"' ) {
307 in_string = false;
308 }
309 }
310 else if (args[i] == '=' && args[i+1] == '"' ) {
311 i++;
312 in_string = true;
313 }
314 else if (' '==args[i]) {
315 args[i] = 0;
316 m++;
317 }
318 }
319 return m + 1;
320 }
321
322
323
324 /*****************************************************************************/
pj_trim_argv(size_t argc,char * args)325 char **pj_trim_argv (size_t argc, char *args) {
326 /******************************************************************************
327 Create an argv-style array from elements placed in the argument string, args.
328
329 args is a trimmed string as returned by pj_trim_argc(), and argc is the number
330 of trimmed strings found (i.e. the return value of pj_trim_args()). Hence,
331 int argc = pj_trim_argc (args);
332 char **argv = pj_trim_argv (argc, args);
333 will produce a classic style (argc, argv) pair from a string of whitespace
334 separated args. No new memory is allocated for storing the individual args
335 (they stay in the args string), but for the pointers to the args a new array
336 is allocated and returned.
337
338 It is the duty of the caller to free this array.
339 ******************************************************************************/
340
341 if (nullptr==args)
342 return nullptr;
343 if (0==argc)
344 return nullptr;
345
346
347 /* turn the input string into an array of strings */
348 char** argv = (char **) calloc (argc, sizeof (char *));
349 if (nullptr==argv)
350 return nullptr;
351 for(size_t i = 0, j = 0; j < argc; j++) {
352 argv[j] = args + i;
353 char* str = argv[j];
354 size_t nLen = strlen(str);
355 i += nLen + 1;
356 }
357 return argv;
358 }
359
360
361 /*****************************************************************************/
pj_double_quote_string_param_if_needed(const std::string & str)362 std::string pj_double_quote_string_param_if_needed(const std::string& str) {
363 /*****************************************************************************/
364 if( str.find(' ') == std::string::npos ) {
365 return str;
366 }
367 return '"' + replaceAll(str, "\"", "\"\"") + '"';
368 }
369
370 /*****************************************************************************/
pj_make_args(size_t argc,char ** argv)371 char *pj_make_args (size_t argc, char **argv) {
372 /******************************************************************************
373 pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It
374 converts free format command line input to something proj_create can consume.
375
376 Allocates, and returns, an array of char, large enough to hold a whitespace
377 separated copy of the args in argv. It is the duty of the caller to free this
378 array.
379 ******************************************************************************/
380
381 try
382 {
383 std::string s;
384 for( size_t i = 0; i < argc; i++ )
385 {
386 const char* equal = strchr(argv[i], '=');
387 if( equal ) {
388 s += std::string(argv[i], equal - argv[i] + 1);
389 s += pj_double_quote_string_param_if_needed(equal + 1);
390 } else {
391 s += argv[i];
392 }
393 s += ' ';
394 }
395
396 char* p = pj_strdup(s.c_str());
397 return pj_shrink (p);
398 }
399 catch( const std::exception& ) {
400 return nullptr;
401 }
402 }
403
404
405
406 /*****************************************************************************/
proj_context_errno_set(PJ_CONTEXT * ctx,int err)407 void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
408 /******************************************************************************
409 Raise an error directly on a context, without going through a PJ belonging
410 to that context.
411 ******************************************************************************/
412 if (nullptr==ctx)
413 ctx = pj_get_default_ctx();
414 pj_ctx_set_errno (ctx, err);
415 }
416
417 /* logging */
418
419 /* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
420 void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
421
422
423 /***************************************************************************************/
proj_log_level(PJ_CONTEXT * ctx,PJ_LOG_LEVEL log_level)424 PJ_LOG_LEVEL proj_log_level (PJ_CONTEXT *ctx, PJ_LOG_LEVEL log_level) {
425 /****************************************************************************************
426 Set logging level 0-3. Higher number means more debug info. 0 turns it off
427 ****************************************************************************************/
428 PJ_LOG_LEVEL previous;
429 if (nullptr==ctx)
430 ctx = pj_get_default_ctx();
431 if (nullptr==ctx)
432 return PJ_LOG_TELL;
433 previous = static_cast<PJ_LOG_LEVEL>(abs (ctx->debug_level));
434 if (PJ_LOG_TELL==log_level)
435 return previous;
436 ctx->debug_level = log_level;
437 return previous;
438 }
439
440
441 /*****************************************************************************/
proj_log_error(PJ * P,const char * fmt,...)442 void proj_log_error (PJ *P, const char *fmt, ...) {
443 /******************************************************************************
444 For reporting the most severe events.
445 ******************************************************************************/
446 va_list args;
447 va_start( args, fmt );
448 pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args);
449 va_end( args );
450 }
451
452
453 /*****************************************************************************/
proj_log_debug(PJ * P,const char * fmt,...)454 void proj_log_debug (PJ *P, const char *fmt, ...) {
455 /******************************************************************************
456 For reporting debugging information.
457 ******************************************************************************/
458 va_list args;
459 va_start( args, fmt );
460 pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args);
461 va_end( args );
462 }
463
464 /*****************************************************************************/
proj_context_log_debug(PJ_CONTEXT * ctx,const char * fmt,...)465 void proj_context_log_debug (PJ_CONTEXT *ctx, const char *fmt, ...) {
466 /******************************************************************************
467 For reporting debugging information.
468 ******************************************************************************/
469 va_list args;
470 va_start( args, fmt );
471 pj_vlog (ctx, PJ_LOG_DEBUG_MAJOR , fmt, args);
472 va_end( args );
473 }
474
475 /*****************************************************************************/
proj_log_trace(PJ * P,const char * fmt,...)476 void proj_log_trace (PJ *P, const char *fmt, ...) {
477 /******************************************************************************
478 For reporting embarrassingly detailed debugging information.
479 ******************************************************************************/
480 va_list args;
481 va_start( args, fmt );
482 pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args);
483 va_end( args );
484 }
485
486
487 /*****************************************************************************/
proj_log_func(PJ_CONTEXT * ctx,void * app_data,PJ_LOG_FUNCTION logf)488 void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION logf) {
489 /******************************************************************************
490 Put a new logging function into P's context. The opaque object app_data is
491 passed as first arg at each call to the logger
492 ******************************************************************************/
493 if (nullptr==ctx)
494 ctx = pj_get_default_ctx ();
495 ctx->logger_app_data = app_data;
496 if (nullptr!=logf)
497 ctx->logger = logf;
498 }
499