1 : /******************************************************************************
2 : * $Id: ogrutils.cpp 17696 2009-09-26 15:56:39Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Utility functions for OGR classes, including some related to
6 : * parsing well known text format vectors.
7 : * Author: Frank Warmerdam, warmerdam@pobox.com
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 1999, Frank Warmerdam
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 :
31 : #include <ctype.h>
32 :
33 : #include "ogr_geometry.h"
34 : #include "ogr_p.h"
35 :
36 : #ifdef OGR_ENABLED
37 : # include "ogrsf_frmts.h"
38 : #endif /* OGR_ENABLED */
39 :
40 : CPL_CVSID("$Id: ogrutils.cpp 17696 2009-09-26 15:56:39Z rouault $");
41 :
42 : /************************************************************************/
43 : /* OGRTrimExtraZeros() */
44 : /************************************************************************/
45 :
46 6505 : static void OGRTrimExtraZeros( char *pszTarget )
47 :
48 : {
49 6505 : int i = 0;
50 :
51 156280 : while( pszTarget[i] != '\0' )
52 143270 : i++;
53 :
54 : /* -------------------------------------------------------------------- */
55 : /* Trim trailing 000001's as they are likely roundoff error. */
56 : /* -------------------------------------------------------------------- */
57 13610 : if( i > 10
58 6505 : && pszTarget[i-1] == '1'
59 208 : && pszTarget[i-2] == '0'
60 98 : && pszTarget[i-3] == '0'
61 98 : && pszTarget[i-4] == '0'
62 98 : && pszTarget[i-5] == '0'
63 98 : && pszTarget[i-6] == '0' )
64 : {
65 54 : pszTarget[--i] = '\0';
66 : }
67 :
68 : /* -------------------------------------------------------------------- */
69 : /* Trim trailing zeros. */
70 : /* -------------------------------------------------------------------- */
71 81584 : while( i > 2 && pszTarget[i-1] == '0' && pszTarget[i-2] != '.' )
72 : {
73 68574 : pszTarget[--i] = '\0';
74 : }
75 6505 : }
76 :
77 : /************************************************************************/
78 : /* OGRMakeWktCoordinate() */
79 : /* */
80 : /* Format a well known text coordinate, trying to keep the */
81 : /* ASCII representation compact, but accurate. These rules */
82 : /* will have to tighten up in the future. */
83 : /* */
84 : /* Currently a new point should require no more than 64 */
85 : /* characters barring the X or Y value being extremely large. */
86 : /************************************************************************/
87 :
88 4089 : void OGRMakeWktCoordinate( char *pszTarget, double x, double y, double z,
89 : int nDimension )
90 :
91 : {
92 4089 : const size_t bufSize = 400;
93 4089 : const size_t maxTargetSize= 75; /* Assumed max length of the target buffer. */
94 :
95 : char szX[bufSize];
96 : char szY[bufSize];
97 : char szZ[bufSize];
98 :
99 4089 : memset( szX, '\0', bufSize );
100 4089 : memset( szY, '\0', bufSize );
101 4089 : memset( szZ, '\0', bufSize );
102 :
103 4929 : if( x == (int) x && y == (int) y && z == (int) z )
104 : {
105 840 : snprintf( szX, bufSize, "%d", (int) x );
106 840 : snprintf( szY, bufSize, " %d", (int) y );
107 : }
108 : else
109 : {
110 3249 : snprintf( szX, bufSize, "%.15f", x );
111 3249 : OGRTrimExtraZeros( szX );
112 :
113 3249 : snprintf( szY, bufSize, " %.15f", y );
114 3249 : OGRTrimExtraZeros( szY );
115 : }
116 :
117 4089 : if( nDimension == 3 )
118 : {
119 558 : if( z == (int) z )
120 : {
121 551 : snprintf( szZ, bufSize, " %d", (int) z );
122 : }
123 : else
124 : {
125 7 : snprintf( szZ, bufSize, " %.15f", z );
126 7 : OGRTrimExtraZeros( szZ );
127 : }
128 : }
129 :
130 4089 : if( strlen(szX) + strlen(szY) + strlen(szZ) > maxTargetSize )
131 : {
132 1 : strcpy( szX, "0" );
133 1 : strcpy( szY, " 0" );
134 1 : if( nDimension == 3 )
135 1 : strcpy( szZ, " 0" );
136 :
137 : #ifdef DEBUG
138 : CPLDebug( "OGR",
139 : "Yow! Got this big result in OGRMakeWktCoordinate()\n"
140 : "%s %s %s",
141 : szX, szY, szZ );
142 : #endif
143 : }
144 :
145 4089 : strcpy( pszTarget, szX );
146 4089 : strcat( pszTarget, szY );
147 4089 : strcat( pszTarget, szZ );
148 4089 : }
149 :
150 : /************************************************************************/
151 : /* OGRWktReadToken() */
152 : /* */
153 : /* Read one token or delimeter and put into token buffer. Pre */
154 : /* and post white space is swallowed. */
155 : /************************************************************************/
156 :
157 133729 : const char *OGRWktReadToken( const char * pszInput, char * pszToken )
158 :
159 : {
160 133729 : if( pszInput == NULL )
161 0 : return NULL;
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* Swallow pre-white space. */
165 : /* -------------------------------------------------------------------- */
166 267459 : while( *pszInput == ' ' || *pszInput == '\t' )
167 1 : pszInput++;
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* If this is a delimeter, read just one character. */
171 : /* -------------------------------------------------------------------- */
172 168141 : if( *pszInput == '(' || *pszInput == ')' || *pszInput == ',' )
173 : {
174 34412 : pszToken[0] = *pszInput;
175 34412 : pszToken[1] = '\0';
176 :
177 34412 : pszInput++;
178 : }
179 :
180 : /* -------------------------------------------------------------------- */
181 : /* Or if it alpha numeric read till we reach non-alpha numeric */
182 : /* text. */
183 : /* -------------------------------------------------------------------- */
184 : else
185 : {
186 99317 : int iChar = 0;
187 :
188 878186 : while( iChar < OGR_WKT_TOKEN_MAX-1
189 : && ((*pszInput >= 'a' && *pszInput <= 'z')
190 : || (*pszInput >= 'A' && *pszInput <= 'Z')
191 : || (*pszInput >= '0' && *pszInput <= '9')
192 : || *pszInput == '.'
193 : || *pszInput == '+'
194 : || *pszInput == '-') )
195 : {
196 679552 : pszToken[iChar++] = *(pszInput++);
197 : }
198 :
199 99317 : pszToken[iChar++] = '\0';
200 : }
201 :
202 : /* -------------------------------------------------------------------- */
203 : /* Eat any trailing white space. */
204 : /* -------------------------------------------------------------------- */
205 317726 : while( *pszInput == ' ' || *pszInput == '\t' )
206 50268 : pszInput++;
207 :
208 133729 : return( pszInput );
209 : }
210 :
211 : /************************************************************************/
212 : /* OGRWktReadPoints() */
213 : /* */
214 : /* Read a point string. The point list must be contained in */
215 : /* brackets and each point pair separated by a comma. */
216 : /************************************************************************/
217 :
218 15453 : const char * OGRWktReadPoints( const char * pszInput,
219 : OGRRawPoint ** ppaoPoints, double **ppadfZ,
220 : int * pnMaxPoints,
221 : int * pnPointsRead )
222 :
223 : {
224 15453 : const char *pszOrigInput = pszInput;
225 15453 : *pnPointsRead = 0;
226 :
227 15453 : if( pszInput == NULL )
228 0 : return NULL;
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Eat any leading white space. */
232 : /* -------------------------------------------------------------------- */
233 30906 : while( *pszInput == ' ' || *pszInput == '\t' )
234 0 : pszInput++;
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* If this isn't an opening bracket then we have a problem! */
238 : /* -------------------------------------------------------------------- */
239 15453 : if( *pszInput != '(' )
240 : {
241 : CPLDebug( "OGR",
242 : "Expected '(', but got %s in OGRWktReadPoints().\n",
243 0 : pszInput );
244 :
245 0 : return pszInput;
246 : }
247 :
248 15453 : pszInput++;
249 :
250 : /* ==================================================================== */
251 : /* This loop reads a single point. It will continue till we */
252 : /* run out of well formed points, or a closing bracket is */
253 : /* encountered. */
254 : /* ==================================================================== */
255 : char szDelim[OGR_WKT_TOKEN_MAX];
256 :
257 18191 : do {
258 : /* -------------------------------------------------------------------- */
259 : /* Read the X and Y values, verify they are numeric. */
260 : /* -------------------------------------------------------------------- */
261 : char szTokenX[OGR_WKT_TOKEN_MAX];
262 : char szTokenY[OGR_WKT_TOKEN_MAX];
263 :
264 18191 : pszInput = OGRWktReadToken( pszInput, szTokenX );
265 18191 : pszInput = OGRWktReadToken( pszInput, szTokenY );
266 :
267 36572 : if( (!isdigit(szTokenX[0]) && szTokenX[0] != '-' && szTokenX[0] != '.' )
268 18381 : || (!isdigit(szTokenY[0]) && szTokenY[0] != '-' && szTokenY[0] != '.') )
269 0 : return NULL;
270 :
271 : /* -------------------------------------------------------------------- */
272 : /* Do we need to grow the point list to hold this point? */
273 : /* -------------------------------------------------------------------- */
274 18191 : if( *pnPointsRead == *pnMaxPoints )
275 : {
276 15343 : *pnMaxPoints = *pnMaxPoints * 2 + 10;
277 : *ppaoPoints = (OGRRawPoint *)
278 15343 : CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints);
279 :
280 15343 : if( *ppadfZ != NULL )
281 : {
282 : *ppadfZ = (double *)
283 48 : CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints);
284 : }
285 : }
286 :
287 : /* -------------------------------------------------------------------- */
288 : /* Add point to list. */
289 : /* -------------------------------------------------------------------- */
290 18191 : (*ppaoPoints)[*pnPointsRead].x = atof(szTokenX);
291 18191 : (*ppaoPoints)[*pnPointsRead].y = atof(szTokenY);
292 :
293 : /* -------------------------------------------------------------------- */
294 : /* Do we have a Z coordinate? */
295 : /* -------------------------------------------------------------------- */
296 18191 : pszInput = OGRWktReadToken( pszInput, szDelim );
297 :
298 18191 : if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
299 : {
300 16122 : if( *ppadfZ == NULL )
301 : {
302 14814 : *ppadfZ = (double *) CPLCalloc(sizeof(double),*pnMaxPoints);
303 : }
304 :
305 16122 : (*ppadfZ)[*pnPointsRead] = atof(szDelim);
306 :
307 16122 : pszInput = OGRWktReadToken( pszInput, szDelim );
308 : }
309 :
310 18191 : (*pnPointsRead)++;
311 :
312 : /* -------------------------------------------------------------------- */
313 : /* Do we have a M coordinate? */
314 : /* If we do, just skip it. */
315 : /* -------------------------------------------------------------------- */
316 18191 : if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
317 : {
318 34 : pszInput = OGRWktReadToken( pszInput, szDelim );
319 : }
320 :
321 : /* -------------------------------------------------------------------- */
322 : /* Read next delimeter ... it should be a comma if there are */
323 : /* more points. */
324 : /* -------------------------------------------------------------------- */
325 18191 : if( szDelim[0] != ')' && szDelim[0] != ',' )
326 : {
327 : CPLDebug( "OGR",
328 : "Corrupt input in OGRWktReadPoints()\n"
329 : "Got `%s' when expecting `,' or `)', near `%s' in %s.\n",
330 0 : szDelim, pszInput, pszOrigInput );
331 0 : return NULL;
332 : }
333 :
334 18191 : } while( szDelim[0] == ',' );
335 :
336 15453 : return pszInput;
337 : }
338 :
339 : /************************************************************************/
340 : /* OGRMalloc() */
341 : /* */
342 : /* Cover for CPLMalloc() */
343 : /************************************************************************/
344 :
345 0 : void *OGRMalloc( size_t size )
346 :
347 : {
348 0 : return CPLMalloc( size );
349 : }
350 :
351 : /************************************************************************/
352 : /* OGRCalloc() */
353 : /* */
354 : /* Cover for CPLCalloc() */
355 : /************************************************************************/
356 :
357 1543 : void * OGRCalloc( size_t count, size_t size )
358 :
359 : {
360 1543 : return CPLCalloc( count, size );
361 : }
362 :
363 : /************************************************************************/
364 : /* OGRRealloc() */
365 : /* */
366 : /* Cover for CPLRealloc() */
367 : /************************************************************************/
368 :
369 33810 : void *OGRRealloc( void * pOld, size_t size )
370 :
371 : {
372 33810 : return CPLRealloc( pOld, size );
373 : }
374 :
375 : /************************************************************************/
376 : /* OGRFree() */
377 : /* */
378 : /* Cover for CPLFree(). */
379 : /************************************************************************/
380 :
381 20414 : void OGRFree( void * pMemory )
382 :
383 : {
384 20414 : CPLFree( pMemory );
385 20414 : }
386 :
387 : /**
388 : * General utility option processing.
389 : *
390 : * This function is intended to provide a variety of generic commandline
391 : * options for all OGR commandline utilities. It takes care of the following
392 : * commandline options:
393 : *
394 : * --formats: report all format drivers configured.
395 : * --optfile filename: expand an option file into the argument list.
396 : * --config key value: set system configuration option.
397 : * --debug [on/off/value]: set debug level.
398 : * --help-general: report detailed help on general options.
399 : *
400 : * The argument array is replaced "in place" and should be freed with
401 : * CSLDestroy() when no longer needed. The typical usage looks something
402 : * like the following. Note that the formats should be registered so that
403 : * the --formats option will work properly.
404 : *
405 : * int main( int argc, char ** argv )
406 : * {
407 : * OGRAllRegister();
408 : *
409 : * argc = OGRGeneralCmdLineProcessor( argc, &argv, 0 );
410 : * if( argc < 1 )
411 : * exit( -argc );
412 : *
413 : * @param nArgc number of values in the argument list.
414 : * @param Pointer to the argument list array (will be updated in place).
415 : *
416 : * @return updated nArgc argument count. Return of 0 requests terminate
417 : * without error, return of -1 requests exit with error code.
418 : */
419 :
420 40 : int OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv, int nOptions )
421 :
422 : {
423 40 : char **papszReturn = NULL;
424 : int iArg;
425 40 : char **papszArgv = *ppapszArgv;
426 :
427 : (void) nOptions;
428 :
429 : /* -------------------------------------------------------------------- */
430 : /* Preserve the program name. */
431 : /* -------------------------------------------------------------------- */
432 40 : papszReturn = CSLAddString( papszReturn, papszArgv[0] );
433 :
434 : /* ==================================================================== */
435 : /* Loop over all arguments. */
436 : /* ==================================================================== */
437 194 : for( iArg = 1; iArg < nArgc; iArg++ )
438 : {
439 : /* -------------------------------------------------------------------- */
440 : /* --version */
441 : /* -------------------------------------------------------------------- */
442 154 : if( EQUAL(papszArgv[iArg],"--version") )
443 : {
444 0 : printf( "%s\n", GDALVersionInfo( "--version" ) );
445 0 : CSLDestroy( papszReturn );
446 0 : return 0;
447 : }
448 :
449 : /* -------------------------------------------------------------------- */
450 : /* --license */
451 : /* -------------------------------------------------------------------- */
452 154 : else if( EQUAL(papszArgv[iArg],"--license") )
453 : {
454 0 : printf( "%s\n", GDALVersionInfo( "LICENSE" ) );
455 0 : CSLDestroy( papszReturn );
456 0 : return 0;
457 : }
458 :
459 : /* -------------------------------------------------------------------- */
460 : /* --config */
461 : /* -------------------------------------------------------------------- */
462 154 : else if( EQUAL(papszArgv[iArg],"--config") )
463 : {
464 0 : if( iArg + 2 >= nArgc )
465 : {
466 : CPLError( CE_Failure, CPLE_AppDefined,
467 0 : "--config option given without a key and value argument." );
468 0 : CSLDestroy( papszReturn );
469 0 : return -1;
470 : }
471 :
472 0 : CPLSetConfigOption( papszArgv[iArg+1], papszArgv[iArg+2] );
473 :
474 0 : iArg += 2;
475 : }
476 :
477 : /* -------------------------------------------------------------------- */
478 : /* --mempreload */
479 : /* -------------------------------------------------------------------- */
480 154 : else if( EQUAL(papszArgv[iArg],"--mempreload") )
481 : {
482 : int i;
483 :
484 0 : if( iArg + 1 >= nArgc )
485 : {
486 : CPLError( CE_Failure, CPLE_AppDefined,
487 0 : "--mempreload option given without directory path.");
488 0 : CSLDestroy( papszReturn );
489 0 : return -1;
490 : }
491 :
492 0 : char **papszFiles = CPLReadDir( papszArgv[iArg+1] );
493 0 : if( CSLCount(papszFiles) == 0 )
494 : {
495 : CPLError( CE_Failure, CPLE_AppDefined,
496 0 : "--mempreload given invalid or empty directory.");
497 0 : CSLDestroy( papszReturn );
498 0 : return -1;
499 : }
500 :
501 0 : for( i = 0; papszFiles[i] != NULL; i++ )
502 : {
503 0 : CPLString osOldPath, osNewPath;
504 :
505 0 : if( EQUAL(papszFiles[i],".") || EQUAL(papszFiles[i],"..") )
506 0 : continue;
507 :
508 0 : osOldPath = CPLFormFilename( papszArgv[iArg+1],
509 0 : papszFiles[i], NULL );
510 0 : osNewPath.Printf( "/vsimem/%s", papszFiles[i] );
511 :
512 : CPLDebug( "VSI", "Preloading %s to %s.",
513 0 : osOldPath.c_str(), osNewPath.c_str() );
514 :
515 0 : if( CPLCopyFile( osNewPath, osOldPath ) != 0 )
516 0 : return -1;
517 : }
518 :
519 0 : CSLDestroy( papszFiles );
520 0 : iArg += 1;
521 : }
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* --debug */
525 : /* -------------------------------------------------------------------- */
526 154 : else if( EQUAL(papszArgv[iArg],"--debug") )
527 : {
528 0 : if( iArg + 1 >= nArgc )
529 : {
530 : CPLError( CE_Failure, CPLE_AppDefined,
531 0 : "--debug option given without debug level." );
532 0 : CSLDestroy( papszReturn );
533 0 : return -1;
534 : }
535 :
536 0 : CPLSetConfigOption( "CPL_DEBUG", papszArgv[iArg+1] );
537 0 : iArg += 1;
538 : }
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* --optfile */
542 : /* */
543 : /* Annoyingly the options inserted by --optfile will *not* be */
544 : /* processed properly if they are general options. */
545 : /* -------------------------------------------------------------------- */
546 154 : else if( EQUAL(papszArgv[iArg],"--optfile") )
547 : {
548 : const char *pszLine;
549 : FILE *fpOptFile;
550 :
551 0 : if( iArg + 1 >= nArgc )
552 : {
553 : CPLError( CE_Failure, CPLE_AppDefined,
554 0 : "--optfile option given without filename." );
555 0 : CSLDestroy( papszReturn );
556 0 : return -1;
557 : }
558 :
559 0 : fpOptFile = VSIFOpen( papszArgv[iArg+1], "rb" );
560 :
561 0 : if( fpOptFile == NULL )
562 : {
563 : CPLError( CE_Failure, CPLE_AppDefined,
564 : "Unable to open optfile '%s'.\n%s",
565 0 : papszArgv[iArg+1], VSIStrerror( errno ) );
566 0 : CSLDestroy( papszReturn );
567 0 : return -1;
568 : }
569 :
570 0 : while( (pszLine = CPLReadLine( fpOptFile )) != NULL )
571 : {
572 : char **papszTokens;
573 : int i;
574 :
575 0 : if( pszLine[0] == '#' || strlen(pszLine) == 0 )
576 0 : continue;
577 :
578 0 : papszTokens = CSLTokenizeString( pszLine );
579 0 : for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; i++)
580 0 : papszReturn = CSLAddString( papszReturn, papszTokens[i] );
581 0 : CSLDestroy( papszTokens );
582 : }
583 :
584 0 : VSIFClose( fpOptFile );
585 :
586 0 : iArg += 1;
587 : }
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* --formats */
591 : /* -------------------------------------------------------------------- */
592 : #ifdef OGR_ENABLED
593 154 : else if( EQUAL(papszArgv[iArg], "--formats") )
594 : {
595 : int iDr;
596 :
597 0 : printf( "Supported Formats:\n" );
598 :
599 0 : OGRSFDriverRegistrar *poR = OGRSFDriverRegistrar::GetRegistrar();
600 :
601 0 : for( iDr = 0; iDr < poR->GetDriverCount(); iDr++ )
602 : {
603 0 : OGRSFDriver *poDriver = poR->GetDriver(iDr);
604 :
605 0 : if( poDriver->TestCapability( ODrCCreateDataSource ) )
606 : printf( " -> \"%s\" (read/write)\n",
607 0 : poDriver->GetName() );
608 : else
609 : printf( " -> \"%s\" (readonly)\n",
610 0 : poDriver->GetName() );
611 : }
612 :
613 0 : CSLDestroy( papszReturn );
614 0 : return 0;
615 : }
616 : #endif /* OGR_ENABLED */
617 :
618 : /* -------------------------------------------------------------------- */
619 : /* --locale */
620 : /* -------------------------------------------------------------------- */
621 154 : else if( EQUAL(papszArgv[iArg],"--locale") && iArg < nArgc-1 )
622 : {
623 0 : setlocale( LC_ALL, papszArgv[++iArg] );
624 : }
625 :
626 : /* -------------------------------------------------------------------- */
627 : /* --help-general */
628 : /* -------------------------------------------------------------------- */
629 154 : else if( EQUAL(papszArgv[iArg],"--help-general") )
630 : {
631 0 : printf( "Generic GDAL/OGR utility command options:\n" );
632 0 : printf( " --version: report version of GDAL/OGR in use.\n" );
633 0 : printf( " --license: report GDAL/OGR license info.\n" );
634 : #ifdef OGR_ENABLED
635 0 : printf( " --formats: report all configured format drivers.\n" );
636 : #endif /* OGR_ENABLED */
637 0 : printf( " --optfile filename: expand an option file into the argument list.\n" );
638 0 : printf( " --config key value: set system configuration option.\n" );
639 0 : printf( " --debug [on/off/value]: set debug level.\n" );
640 0 : printf( " --help-general: report detailed help on general options.\n" );
641 0 : CSLDestroy( papszReturn );
642 0 : return 0;
643 : }
644 :
645 : /* -------------------------------------------------------------------- */
646 : /* carry through unrecognised options. */
647 : /* -------------------------------------------------------------------- */
648 : else
649 : {
650 154 : papszReturn = CSLAddString( papszReturn, papszArgv[iArg] );
651 : }
652 : }
653 :
654 40 : *ppapszArgv = papszReturn;
655 :
656 40 : return CSLCount( *ppapszArgv );
657 : }
658 :
659 : /************************************************************************/
660 : /* OGRParseDate() */
661 : /* */
662 : /* Parse a variety of text date formats into an OGRField. */
663 : /************************************************************************/
664 :
665 : /**
666 : * Parse date string.
667 : *
668 : * This function attempts to parse a date string in a variety of formats
669 : * into the OGRField.Date format suitable for use with OGR. Generally
670 : * speaking this function is expecting values like:
671 : *
672 : * YYYY-MM-DD HH:MM:SS+nn
673 : *
674 : * The seconds may also have a decimal portion (which is ignored). And
675 : * just dates (YYYY-MM-DD) or just times (HH:MM:SS) are also supported.
676 : * The date may also be in YYYY/MM/DD format. If the year is less than 100
677 : * and greater than 30 a "1900" century value will be set. If it is less than
678 : * 30 and greater than -1 then a "2000" century value will be set. In
679 : * the future this function may be generalized, and additional control
680 : * provided through nOptions, but an nOptions value of "0" should always do
681 : * a reasonable default form of processing.
682 : *
683 : * The value of psField will be indeterminate if the function fails (returns
684 : * FALSE).
685 : *
686 : * @param pszInput the input date string.
687 : * @param psField the OGRField that will be updated with the parsed result.
688 : * @param nOptions parsing options, for now always 0.
689 : *
690 : * @return TRUE if apparently successful or FALSE on failure.
691 : */
692 :
693 161 : int OGRParseDate( const char *pszInput, OGRField *psField, int nOptions )
694 :
695 : {
696 161 : int bGotSomething = FALSE;
697 :
698 161 : psField->Date.Year = 0;
699 161 : psField->Date.Month = 0;
700 161 : psField->Date.Day = 0;
701 161 : psField->Date.Hour = 0;
702 161 : psField->Date.Minute = 0;
703 161 : psField->Date.Second = 0;
704 161 : psField->Date.TZFlag = 0;
705 :
706 : /* -------------------------------------------------------------------- */
707 : /* Do we have a date? */
708 : /* -------------------------------------------------------------------- */
709 322 : while( *pszInput == ' ' )
710 0 : pszInput++;
711 :
712 161 : if( strstr(pszInput,"-") != NULL || strstr(pszInput,"/") != NULL )
713 : {
714 119 : psField->Date.Year = (GInt16)atoi(pszInput);
715 119 : if( psField->Date.Year < 100 && psField->Date.Year >= 30 )
716 0 : psField->Date.Year += 1900;
717 119 : else if( psField->Date.Year < 30 && psField->Date.Year >= 0 )
718 0 : psField->Date.Year += 2000;
719 :
720 714 : while( *pszInput >= '0' && *pszInput <= '9' )
721 476 : pszInput++;
722 119 : if( *pszInput != '-' && *pszInput != '/' )
723 0 : return FALSE;
724 : else
725 119 : pszInput++;
726 :
727 119 : psField->Date.Month = (GByte)atoi(pszInput);
728 119 : if( psField->Date.Month > 12 )
729 0 : return FALSE;
730 :
731 476 : while( *pszInput >= '0' && *pszInput <= '9' )
732 238 : pszInput++;
733 119 : if( *pszInput != '-' && *pszInput != '/' )
734 0 : return FALSE;
735 : else
736 119 : pszInput++;
737 :
738 119 : psField->Date.Day = (GByte)atoi(pszInput);
739 119 : if( psField->Date.Day > 31 )
740 0 : return FALSE;
741 :
742 476 : while( *pszInput >= '0' && *pszInput <= '9' )
743 238 : pszInput++;
744 :
745 119 : bGotSomething = TRUE;
746 : }
747 :
748 : /* -------------------------------------------------------------------- */
749 : /* Do we have a time? */
750 : /* -------------------------------------------------------------------- */
751 401 : while( *pszInput == ' ' )
752 79 : pszInput++;
753 :
754 161 : if( strstr(pszInput,":") != NULL )
755 : {
756 120 : psField->Date.Hour = (GByte)atoi(pszInput);
757 120 : if( psField->Date.Hour > 23 )
758 0 : return FALSE;
759 :
760 479 : while( *pszInput >= '0' && *pszInput <= '9' )
761 239 : pszInput++;
762 120 : if( *pszInput != ':' )
763 1 : return FALSE;
764 : else
765 119 : pszInput++;
766 :
767 119 : psField->Date.Minute = (GByte)atoi(pszInput);
768 119 : if( psField->Date.Minute > 59 )
769 0 : return FALSE;
770 :
771 476 : while( *pszInput >= '0' && *pszInput <= '9' )
772 238 : pszInput++;
773 119 : if( *pszInput != ':' )
774 0 : return FALSE;
775 : else
776 119 : pszInput++;
777 :
778 119 : psField->Date.Second = (GByte)atoi(pszInput);
779 119 : if( psField->Date.Second > 59 )
780 0 : return FALSE;
781 :
782 476 : while( (*pszInput >= '0' && *pszInput <= '9')
783 : || *pszInput == '.' )
784 238 : pszInput++;
785 :
786 119 : bGotSomething = TRUE;
787 : }
788 :
789 : // No date or time!
790 160 : if( !bGotSomething )
791 0 : return FALSE;
792 :
793 : /* -------------------------------------------------------------------- */
794 : /* Do we have a timezone? */
795 : /* -------------------------------------------------------------------- */
796 320 : while( *pszInput == ' ' )
797 0 : pszInput++;
798 :
799 160 : if( *pszInput == '-' || *pszInput == '+' )
800 : {
801 : // +HH integral offset
802 46 : if( strlen(pszInput) <= 3 )
803 42 : psField->Date.TZFlag = (GByte)(100 + atoi(pszInput) * 4);
804 :
805 8 : else if( pszInput[3] == ':' // +HH:MM offset
806 : && atoi(pszInput+4) % 15 == 0 )
807 : {
808 : psField->Date.TZFlag = (GByte)(100
809 : + atoi(pszInput+1) * 4
810 4 : + (atoi(pszInput+4) / 15));
811 :
812 4 : if( pszInput[0] == '-' )
813 4 : psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
814 : }
815 0 : else if( isdigit(pszInput[3]) && isdigit(pszInput[4]) // +HHMM offset
816 : && atoi(pszInput+3) % 15 == 0 )
817 : {
818 : psField->Date.TZFlag = (GByte)(100
819 : + static_cast<GByte>(CPLScanLong(pszInput+1,2)) * 4
820 0 : + (atoi(pszInput+3) / 15));
821 :
822 0 : if( pszInput[0] == '-' )
823 0 : psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
824 : }
825 0 : else if( isdigit(pszInput[3]) && pszInput[4] == '\0' // +HMM offset
826 : && atoi(pszInput+2) % 15 == 0 )
827 : {
828 : psField->Date.TZFlag = (GByte)(100
829 : + static_cast<GByte>(CPLScanLong(pszInput+1,1)) * 4
830 0 : + (atoi(pszInput+2) / 15));
831 :
832 0 : if( pszInput[0] == '-' )
833 0 : psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
834 : }
835 : // otherwise ignore any timezone info.
836 : }
837 :
838 160 : return TRUE;
839 : }
840 :
841 :
842 : /************************************************************************/
843 : /* OGRParseXMLDateTime() */
844 : /************************************************************************/
845 :
846 27 : int OGRParseXMLDateTime( const char* pszXMLDateTime,
847 : int *pnYear, int *pnMonth, int *pnDay,
848 : int *pnHour, int *pnMinute, float* pfSecond, int *pnTZ)
849 : {
850 27 : int year = 0, month = 0, day = 0, hour = 0, minute = 0, TZHour, TZMinute;
851 27 : float second = 0;
852 : char c;
853 27 : int TZ = 0;
854 27 : int bRet = FALSE;
855 :
856 : /* Date is expressed as a UTC date */
857 27 : if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c",
858 : &year, &month, &day, &hour, &minute, &second, &c) == 7 && c == 'Z')
859 : {
860 4 : TZ = 100;
861 4 : bRet = TRUE;
862 : }
863 : /* Date is expressed as a UTC date, with a timezone */
864 23 : else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d",
865 : &year, &month, &day, &hour, &minute, &second, &c, &TZHour, &TZMinute) == 9 &&
866 : (c == '+' || c == '-'))
867 : {
868 23 : TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
869 23 : bRet = TRUE;
870 : }
871 : /* Date is expressed into an unknown timezone */
872 0 : else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f",
873 : &year, &month, &day, &hour, &minute, &second) == 6)
874 : {
875 0 : TZ = 0;
876 0 : bRet = TRUE;
877 : }
878 27 : if (bRet)
879 : {
880 27 : if (pnYear) *pnYear = year;
881 27 : if (pnMonth) *pnMonth = month;
882 27 : if (pnDay) *pnDay = day;
883 27 : if (pnHour) *pnHour = hour;
884 27 : if (pnMinute) *pnMinute = minute;
885 27 : if (pfSecond) *pfSecond = second;
886 27 : if (pnTZ) *pnTZ = TZ;
887 : }
888 :
889 27 : return bRet;
890 : }
891 :
892 : /************************************************************************/
893 : /* OGRParseRFC822DateTime() */
894 : /************************************************************************/
895 :
896 : static const char* aszMonthStr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
897 : "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
898 :
899 25 : int OGRParseRFC822DateTime( const char* pszRFC822DateTime,
900 : int *pnYear, int *pnMonth, int *pnDay,
901 : int *pnHour, int *pnMinute, int *pnSecond, int *pnTZ)
902 : {
903 : /* Following http://asg.web.cmu.edu/rfc/rfc822.html#sec-5 : [Fri,] 28 Dec 2007 05:24[:17] GMT */
904 25 : char** papszTokens = CSLTokenizeStringComplex( pszRFC822DateTime, " ,:", TRUE, FALSE );
905 25 : char** papszVal = papszTokens;
906 25 : int bRet = FALSE;
907 25 : int nTokens = CSLCount(papszTokens);
908 25 : if (nTokens >= 6)
909 : {
910 21 : if ( ! ((*papszVal)[0] >= '0' && (*papszVal)[0] <= '9') )
911 : {
912 : /* Ignore day of week */
913 21 : papszVal ++;
914 : }
915 :
916 21 : int day = atoi(*papszVal);
917 21 : papszVal ++;
918 :
919 21 : int month = 0;
920 :
921 273 : for(int i = 0; i < 12; i++)
922 : {
923 252 : if (EQUAL(*papszVal, aszMonthStr[i]))
924 21 : month = i + 1;
925 : }
926 21 : papszVal ++;
927 :
928 21 : int year = atoi(*papszVal);
929 21 : papszVal ++;
930 21 : if( year < 100 && year >= 30 )
931 0 : year += 1900;
932 21 : else if( year < 30 && year >= 0 )
933 0 : year += 2000;
934 :
935 21 : int hour = atoi(*papszVal);
936 21 : papszVal ++;
937 :
938 21 : int minute = atoi(*papszVal);
939 21 : papszVal ++;
940 :
941 21 : int second = 0;
942 21 : if (*papszVal != NULL && (*papszVal)[0] >= '0' && (*papszVal)[0] <= '9')
943 : {
944 21 : second = atoi(*papszVal);
945 21 : papszVal ++;
946 : }
947 :
948 21 : if (month != 0)
949 : {
950 21 : bRet = TRUE;
951 21 : int TZ = 0;
952 :
953 21 : if (*papszVal == NULL)
954 : {
955 : }
956 63 : else if (strlen(*papszVal) == 5 &&
957 21 : ((*papszVal)[0] == '+' || (*papszVal)[0] == '-'))
958 : {
959 : char szBuf[3];
960 21 : szBuf[0] = (*papszVal)[1];
961 21 : szBuf[1] = (*papszVal)[2];
962 21 : szBuf[2] = 0;
963 21 : int TZHour = atoi(szBuf);
964 21 : szBuf[0] = (*papszVal)[3];
965 21 : szBuf[1] = (*papszVal)[4];
966 21 : szBuf[2] = 0;
967 21 : int TZMinute = atoi(szBuf);
968 21 : TZ = 100 + (((*papszVal)[0] == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
969 : }
970 : else
971 : {
972 0 : const char* aszTZStr[] = { "GMT", "UT", "Z", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT" };
973 0 : int anTZVal[] = { 0, 0, 0, -5, -4, -6, -5, -7, -6, -8, -7 };
974 0 : for(int i = 0; i < 11; i++)
975 : {
976 0 : if (EQUAL(*papszVal, aszTZStr[i]))
977 : {
978 0 : TZ = 100 + anTZVal[i] * 4;
979 0 : break;
980 : }
981 : }
982 : }
983 :
984 21 : if (pnYear) *pnYear = year;
985 21 : if (pnMonth) *pnMonth = month;
986 21 : if (pnDay) *pnDay = day;
987 21 : if (pnHour) *pnHour = hour;
988 21 : if (pnMinute) *pnMinute = minute;
989 21 : if (pnSecond) *pnSecond = second;
990 21 : if (pnTZ) *pnTZ = TZ;
991 : }
992 : }
993 25 : CSLDestroy(papszTokens);
994 25 : return bRet;
995 : }
996 :
997 :
998 : /**
999 : * Returns the day of the week in Gregorian calendar
1000 : *
1001 : * @param day : day of the month, between 1 and 31
1002 : * @param month : month of the year, between 1 (Jan) and 12 (Dec)
1003 : * @param year : year
1004 :
1005 : * @return day of the week : 0 for Monday, ... 6 for Sunday
1006 : */
1007 :
1008 12 : int OGRGetDayOfWeek(int day, int month, int year)
1009 : {
1010 : /* Reference: Zeller's congruence */
1011 12 : int q = day;
1012 : int m;
1013 12 : if (month >=3)
1014 12 : m = month;
1015 : else
1016 : {
1017 0 : m = month + 12;
1018 0 : year --;
1019 : }
1020 12 : int K = year % 100;
1021 12 : int J = year / 100;
1022 12 : int h = ( q + (((m+1)*26)/10) + K + K/4 + J/4 + 5 * J) % 7;
1023 12 : return ( h + 5 ) % 7;
1024 : }
1025 :
1026 :
1027 : /************************************************************************/
1028 : /* OGRGetRFC822DateTime() */
1029 : /************************************************************************/
1030 :
1031 12 : char* OGRGetRFC822DateTime(int year, int month, int day, int hour, int minute, int second, int TZFlag)
1032 : {
1033 12 : char* pszTZ = NULL;
1034 12 : const char* aszDayOfWeek[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
1035 :
1036 12 : int dayofweek = OGRGetDayOfWeek(day, month, year);
1037 :
1038 12 : if (month < 1 || month > 12)
1039 0 : month = 1;
1040 :
1041 12 : if (TZFlag == 0 || TZFlag == 100)
1042 : {
1043 0 : pszTZ = CPLStrdup("GMT");
1044 : }
1045 : else
1046 : {
1047 12 : int TZOffset = ABS(TZFlag - 100) * 15;
1048 12 : int TZHour = TZOffset / 60;
1049 12 : int TZMinute = TZOffset - TZHour * 60;
1050 : pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-',
1051 12 : TZHour, TZMinute));
1052 : }
1053 : char* pszRet = CPLStrdup(CPLSPrintf("%s, %02d %s %04d %02d:%02d:%02d %s",
1054 12 : aszDayOfWeek[dayofweek], day, aszMonthStr[month - 1], year, hour, minute, second, pszTZ));
1055 12 : CPLFree(pszTZ);
1056 12 : return pszRet;
1057 : }
1058 :
1059 : /************************************************************************/
1060 : /* OGRGetXMLDateTime() */
1061 : /************************************************************************/
1062 :
1063 3 : char* OGRGetXMLDateTime(int year, int month, int day, int hour, int minute, int second, int TZFlag)
1064 : {
1065 : char* pszRet;
1066 4 : if (TZFlag == 0 || TZFlag == 100)
1067 : {
1068 : pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
1069 1 : year, month, day, hour, minute, second));
1070 : }
1071 : else
1072 : {
1073 2 : int TZOffset = ABS(TZFlag - 100) * 15;
1074 2 : int TZHour = TZOffset / 60;
1075 2 : int TZMinute = TZOffset - TZHour * 60;
1076 : pszRet = CPLStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
1077 : year, month, day, hour, minute, second,
1078 2 : (TZFlag > 100) ? '+' : '-', TZHour, TZMinute));
1079 : }
1080 3 : return pszRet;
1081 : }
1082 :
1083 : /************************************************************************/
1084 : /* OGRGetXML_UTF8_EscapedString() */
1085 : /************************************************************************/
1086 :
1087 94 : char* OGRGetXML_UTF8_EscapedString(const char* pszString)
1088 : {
1089 : char *pszEscaped;
1090 94 : if (!CPLIsUTF8(pszString, -1) &&
1091 : CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
1092 : {
1093 : static int bFirstTime = TRUE;
1094 1 : if (bFirstTime)
1095 : {
1096 1 : bFirstTime = FALSE;
1097 : CPLError(CE_Warning, CPLE_AppDefined,
1098 : "%s is not a valid UTF-8 string. Forcing it to ASCII.\n"
1099 : "If you still want the original string and change the XML file encoding\n"
1100 : "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n"
1101 1 : "This warning won't be issued anymore", pszString);
1102 : }
1103 : else
1104 : {
1105 : CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII",
1106 0 : pszString);
1107 : }
1108 1 : char* pszTemp = CPLForceToASCII(pszString, -1, '?');
1109 1 : pszEscaped = CPLEscapeString( pszTemp, -1, CPLES_XML );
1110 1 : CPLFree(pszTemp);
1111 : }
1112 : else
1113 93 : pszEscaped = CPLEscapeString( pszString, -1, CPLES_XML );
1114 94 : return pszEscaped;
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* OGRFastAtof() */
1119 : /************************************************************************/
1120 :
1121 : /* On Windows, atof() is very slow if the number */
1122 : /* is followed by other long content. */
1123 : /* So we just extract the number into a short string */
1124 : /* before calling atof() on it */
1125 : static
1126 0 : double OGRCallAtofOnShortString(const char* pszStr)
1127 : {
1128 : char szTemp[128];
1129 0 : int nCounter = 0;
1130 0 : const char* p = pszStr;
1131 0 : while(*p == ' ' || *p == '\t')
1132 0 : p++;
1133 0 : while(*p == '+' ||
1134 : *p == '-' ||
1135 : (*p >= '0' && *p <= '9') ||
1136 : (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D'))
1137 : {
1138 0 : szTemp[nCounter++] = *(p++);
1139 0 : if (nCounter == 127)
1140 0 : return atof(pszStr);
1141 : }
1142 0 : szTemp[nCounter] = '\0';
1143 0 : return atof(szTemp);
1144 : }
1145 :
1146 : /** Same contract as CPLAtof, except than it doesn't always call the
1147 : * system atof() that may be slow on some platforms. For simple but
1148 : * common strings, it'll use a faster implementation (up to 20x faster
1149 : * than atof() on MS runtime libraries) that has no garanty to return
1150 : * exactly the same floating point number.
1151 : */
1152 :
1153 744 : double OGRFastAtof(const char* pszStr)
1154 : {
1155 744 : double dfVal = 0;
1156 744 : double dfSign = 1.0;
1157 744 : const char* p = pszStr;
1158 :
1159 : static const double adfTenPower[] =
1160 : {
1161 : 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
1162 : 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20,
1163 : 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31
1164 : };
1165 :
1166 1490 : while(*p == ' ' || *p == '\t')
1167 2 : p++;
1168 :
1169 744 : if (*p == '+')
1170 9 : p++;
1171 735 : else if (*p == '-')
1172 : {
1173 62 : dfSign = -1.0;
1174 62 : p++;
1175 : }
1176 :
1177 2446 : while(TRUE)
1178 : {
1179 3190 : if (*p >= '0' && *p <= '9')
1180 : {
1181 2446 : dfVal = dfVal * 10.0 + (*p - '0');
1182 2446 : p++;
1183 : }
1184 744 : else if (*p == '.')
1185 : {
1186 292 : p++;
1187 : break;
1188 : }
1189 452 : else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
1190 0 : return OGRCallAtofOnShortString(pszStr);
1191 : else
1192 452 : return dfSign * dfVal;
1193 : }
1194 :
1195 292 : unsigned int countFractionnal = 0;
1196 3697 : while(TRUE)
1197 : {
1198 3989 : if (*p >= '0' && *p <= '9')
1199 : {
1200 3697 : dfVal = dfVal * 10.0 + (*p - '0');
1201 3697 : countFractionnal ++;
1202 3697 : p++;
1203 : }
1204 292 : else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
1205 0 : return OGRCallAtofOnShortString(pszStr);
1206 : else
1207 : {
1208 292 : if (countFractionnal < sizeof(adfTenPower) / sizeof(adfTenPower[0]))
1209 292 : return dfSign * (dfVal / adfTenPower[countFractionnal]);
1210 : else
1211 0 : return OGRCallAtofOnShortString(pszStr);
1212 : }
1213 : }
1214 : }
|