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