1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: Utility functions for OGR classes, including some related to
5 * parsing well known text format vectors.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 1999, Frank Warmerdam
10 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
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 "cpl_port.h"
32 #include "ogr_p.h"
33
34 #include <cassert>
35 #include <cmath>
36 #include <cstddef>
37 #include <cstdio>
38 #include <cstdlib>
39 #include <cstring>
40 #include <cctype>
41 #include <limits>
42 #include <sstream>
43 #include <iomanip>
44
45 #include "cpl_conv.h"
46 #include "cpl_error.h"
47 #include "cpl_string.h"
48 #include "cpl_time.h"
49 #include "cpl_vsi.h"
50 #include "gdal.h"
51 #include "ogr_core.h"
52 #include "ogr_geometry.h"
53 #include "ogrsf_frmts.h"
54
55 CPL_CVSID("$Id: ogrutils.cpp ec2f3126303c95ed1190097992f84739f33c8603 2021-05-06 12:51:16 +0200 Even Rouault $")
56
57 // Returns whether a double fits within an int.
58 // Unable to put this in cpl_port.h as include limit breaks grib.
CPLIsDoubleAnInt(double d)59 inline bool CPLIsDoubleAnInt(double d)
60 {
61 // Write it this way to detect NaN
62 if ( !(d >= std::numeric_limits<int>::min() &&
63 d <= std::numeric_limits<int>::max()) )
64 {
65 return false;
66 }
67 return d == static_cast<double>(static_cast<int>(d));
68 }
69
70 namespace
71 {
72
73 // Remove trailing zeros except the last one.
removeTrailingZeros(std::string s)74 std::string removeTrailingZeros(std::string s)
75 {
76 auto pos = s.find('.');
77 if (pos == std::string::npos)
78 return s;
79
80 // Remove zeros at the end. We know this won't be npos because we
81 // have a decimal point.
82 auto nzpos = s.find_last_not_of('0');
83 s = s.substr(0, nzpos + 1);
84
85 // Make sure there is one 0 after the decimal point.
86 if (s.back() == '.')
87 s += '0';
88 return s;
89 }
90
91 // Round a string representing a number by 1 in the least significant digit.
roundup(std::string s)92 std::string roundup(std::string s)
93 {
94 // Remove a negative sign if it exists to make processing
95 // more straigtforward.
96 bool negative(false);
97 if (s[0] == '-')
98 {
99 negative = true;
100 s = s.substr(1);
101 }
102
103 // Go from the back to the front. If we increment a digit other than
104 // a '9', we're done. If we increment a '9', set it to a '0' and move
105 // to the next (more significant) digit. If we get to the front of the
106 // string, add a '1' to the front of the string.
107 for (int pos = static_cast<int>(s.size() - 1); pos >= 0; pos--)
108 {
109 if (s[pos] == '.')
110 continue;
111 s[pos]++;
112
113 // Incrementing past 9 gets you a colon in ASCII.
114 if (s[pos] != ':')
115 break;
116 else
117 s[pos] = '0';
118 if (pos == 0)
119 s = '1' + s;
120 }
121 if (negative)
122 s = '-' + s;
123 return s;
124 }
125
126
127 // This attempts to eliminate what is likely binary -> decimal representation
128 // error or the result of low-order rounding with calculations. The result
129 // may be more visually pleasing and takes up fewer places.
intelliround(std::string & s)130 std::string intelliround(std::string& s)
131 {
132 // If there is no decimal point, just return.
133 auto dotPos = s.find(".");
134 if (dotPos == std::string::npos)
135 return s;
136
137 // Don't mess with exponential formatting.
138 if (s.find_first_of("eE") != std::string::npos)
139 return s;
140 size_t iDotPos = static_cast<size_t>(dotPos);
141 size_t nCountBeforeDot = iDotPos - 1;
142 if (s[0] == '-')
143 nCountBeforeDot--;
144 size_t i = s.size();
145
146 // If we don't have ten characters, don't do anything.
147 if (i <= 10)
148 return s;
149
150 /* -------------------------------------------------------------------- */
151 /* Trim trailing 00000x's as they are likely roundoff error. */
152 /* -------------------------------------------------------------------- */
153 if (s[i-2] == '0' && s[i-3] == '0' && s[i-4] == '0' &&
154 s[i-5] == '0' && s[i-6] == '0')
155 {
156 s.resize(s.size() - 1);
157 }
158 // I don't understand this case exactly. It's like saying if the
159 // value is large enough and there are sufficient sig digits before
160 // a bunch of zeros, remove the zeros and any digits at the end that
161 // may be nonzero. Perhaps if we can't exactly explain in words what
162 // we're doing here, we shouldn't do it? Perhaps it should
163 // be generalized?
164 // The value "12345.000000011" invokes this case, if anyone
165 // is interested.
166 else if (iDotPos < i - 8 &&
167 (nCountBeforeDot >= 4 || s[i-3] == '0') &&
168 (nCountBeforeDot >= 5 || s[i-4] == '0') &&
169 (nCountBeforeDot >= 6 || s[i-5] == '0') &&
170 (nCountBeforeDot >= 7 || s[i-6] == '0') &&
171 (nCountBeforeDot >= 8 || s[i-7] == '0') &&
172 s[i-8] == '0' && s[i-9] == '0')
173 {
174 s.resize(s.size() - 8);
175 }
176 /* -------------------------------------------------------------------- */
177 /* Trim trailing 99999x's as they are likely roundoff error. */
178 /* -------------------------------------------------------------------- */
179 else if (s[i-2] == '9' && s[i-3] == '9' && s[i-4] == '9' &&
180 s[i-5] == '9' && s[i-6] == '9' )
181 {
182 s.resize(i - 6);
183 s = roundup(s);
184 }
185 else if (iDotPos < i - 9 &&
186 (nCountBeforeDot >= 4 || s[i-3] == '9') &&
187 (nCountBeforeDot >= 5 || s[i-4] == '9') &&
188 (nCountBeforeDot >= 6 || s[i-5] == '9') &&
189 (nCountBeforeDot >= 7 || s[i-6] == '9') &&
190 (nCountBeforeDot >= 8 || s[i-7] == '9') &&
191 s[i-8] == '9' && s[i-9] == '9')
192 {
193 s.resize(i - 9);
194 s = roundup(s);
195 }
196 return s;
197 }
198
199 } // unnamed namespace
200
201 /************************************************************************/
202 /* OGRFormatDouble() */
203 /************************************************************************/
204
OGRFormatDouble(char * pszBuffer,int nBufferLen,double dfVal,char chDecimalSep,int nPrecision,char chConversionSpecifier)205 void OGRFormatDouble( char *pszBuffer, int nBufferLen, double dfVal,
206 char chDecimalSep, int nPrecision,
207 char chConversionSpecifier )
208 {
209 OGRWktOptions opts;
210
211 opts.precision = nPrecision;
212 opts.format =
213 (chConversionSpecifier == 'g' || chConversionSpecifier == 'G') ?
214 OGRWktFormat::G :
215 OGRWktFormat::F;
216
217 std::string s = OGRFormatDouble(dfVal, opts);
218 if (chDecimalSep != '\0' && chDecimalSep != '.')
219 {
220 auto pos = s.find('.');
221 if (pos != std::string::npos)
222 s.replace(pos, 1, std::string(1, chDecimalSep));
223 }
224 if (s.size() + 1 > static_cast<size_t>(nBufferLen))
225 {
226 CPLError(CE_Warning, CPLE_AppDefined, "Truncated double value %s to "
227 "%s.", s.data(), s.substr(0, nBufferLen - 1).data());
228 s.resize(nBufferLen - 1);
229 }
230 strcpy(pszBuffer, s.data());
231 }
232
233
234 /// Simplified OGRFormatDouble that can be made to adhere to provided
235 /// options.
OGRFormatDouble(double val,const OGRWktOptions & opts)236 std::string OGRFormatDouble(double val, const OGRWktOptions& opts)
237 {
238 // So to have identical cross platform representation.
239 if( CPLIsInf(val) )
240 return (val > 0) ? "inf" : "-inf";
241 if( CPLIsNan(val) )
242 return "nan";
243
244 std::ostringstream oss;
245 oss.imbue(std::locale::classic()); // Make sure we output decimal points.
246 bool l_round(opts.round);
247 if (opts.format == OGRWktFormat::F ||
248 (opts.format == OGRWktFormat::Default && fabs(val) < 1))
249 oss << std::fixed;
250 else
251 {
252 // Uppercase because OGC spec says capital 'E'.
253 oss << std::uppercase;
254 l_round = false;
255 }
256 oss << std::setprecision(opts.precision);
257 oss << val;
258
259 std::string sval = oss.str();
260
261 if (l_round)
262 sval = intelliround(sval);
263 return removeTrailingZeros(sval);
264 }
265
266 /************************************************************************/
267 /* OGRMakeWktCoordinate() */
268 /* */
269 /* Format a well known text coordinate, trying to keep the */
270 /* ASCII representation compact, but accurate. These rules */
271 /* will have to tighten up in the future. */
272 /* */
273 /* Currently a new point should require no more than 64 */
274 /* characters barring the X or Y value being extremely large. */
275 /************************************************************************/
276
OGRMakeWktCoordinate(char * pszTarget,double x,double y,double z,int nDimension)277 void OGRMakeWktCoordinate( char *pszTarget, double x, double y, double z,
278 int nDimension )
279
280 {
281 std::string wkt = OGRMakeWktCoordinate(x, y, z, nDimension,
282 OGRWktOptions());
283 memcpy(pszTarget, wkt.data(), wkt.size() + 1);
284 }
285
isInteger(const std::string & s)286 static bool isInteger(const std::string& s)
287 {
288 return s.find_first_not_of("0123456789") == std::string::npos;
289 }
290
OGRMakeWktCoordinate(double x,double y,double z,int nDimension,OGRWktOptions opts)291 std::string OGRMakeWktCoordinate(double x, double y, double z, int nDimension,
292 OGRWktOptions opts)
293 {
294 std::string xval;
295 std::string yval;
296
297 // Why do we do this? Seems especially strange since we're ADDING
298 // ".0" onto values in the case below. The "&&" here also seems strange.
299 if( opts.format == OGRWktFormat::Default &&
300 CPLIsDoubleAnInt(x) && CPLIsDoubleAnInt(y) )
301 {
302 xval = std::to_string(static_cast<int>(x));
303 yval = std::to_string(static_cast<int>(y));
304 }
305 else
306 {
307 xval = OGRFormatDouble(x, opts);
308 //ABELL - Why do we do special formatting?
309 if (isInteger(xval))
310 xval += ".0";
311
312 yval = OGRFormatDouble(y, opts);
313 if (isInteger(yval))
314 yval += ".0";
315 }
316 std::string wkt = xval + " " + yval;
317
318 // Why do we always format Z with type G.
319 if( nDimension == 3 )
320 {
321 if(opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z) )
322 wkt += " " + std::to_string(static_cast<int>(z));
323 else
324 {
325 opts.format = OGRWktFormat::G;
326 wkt += " " + OGRFormatDouble(z, opts);
327 }
328 }
329 return wkt;
330 }
331
332 /************************************************************************/
333 /* OGRMakeWktCoordinateM() */
334 /* */
335 /* Format a well known text coordinate, trying to keep the */
336 /* ASCII representation compact, but accurate. These rules */
337 /* will have to tighten up in the future. */
338 /* */
339 /* Currently a new point should require no more than 64 */
340 /* characters barring the X or Y value being extremely large. */
341 /************************************************************************/
342
OGRMakeWktCoordinateM(char * pszTarget,double x,double y,double z,double m,OGRBoolean hasZ,OGRBoolean hasM)343 void OGRMakeWktCoordinateM( char *pszTarget,
344 double x, double y, double z, double m,
345 OGRBoolean hasZ, OGRBoolean hasM )
346
347 {
348 std::string wkt = OGRMakeWktCoordinateM(x, y, z, m, hasZ, hasM,
349 OGRWktOptions());
350 memcpy(pszTarget, wkt.data(), wkt.size() + 1);
351 }
352
OGRMakeWktCoordinateM(double x,double y,double z,double m,OGRBoolean hasZ,OGRBoolean hasM,OGRWktOptions opts)353 std::string OGRMakeWktCoordinateM(double x, double y, double z, double m,
354 OGRBoolean hasZ, OGRBoolean hasM,
355 OGRWktOptions opts)
356 {
357 std::string xval, yval;
358 if( opts.format == OGRWktFormat::Default &&
359 CPLIsDoubleAnInt(x) && CPLIsDoubleAnInt(y) )
360 {
361 xval = std::to_string(static_cast<int>(x));
362 yval = std::to_string(static_cast<int>(y));
363 }
364 else
365 {
366 xval = OGRFormatDouble(x, opts);
367 if (isInteger(xval))
368 xval += ".0";
369
370 yval = OGRFormatDouble(y, opts);
371 if (isInteger(yval))
372 yval += ".0";
373 }
374 std::string wkt = xval + " " + yval;
375
376 // For some reason we always format Z and M as G-type
377 opts.format = OGRWktFormat::G;
378 if( hasZ )
379 {
380 /*if( opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z) )
381 wkt += " " + std::to_string(static_cast<int>(z));
382 else*/
383 wkt += " " + OGRFormatDouble(z, opts);
384 }
385
386 if( hasM )
387 {
388 /*if( opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(m) )
389 wkt += " " + std::to_string(static_cast<int>(m));
390 else*/
391 wkt += " " + OGRFormatDouble(m, opts);
392 }
393 return wkt;
394 }
395
396 /************************************************************************/
397 /* OGRWktReadToken() */
398 /* */
399 /* Read one token or delimiter and put into token buffer. Pre */
400 /* and post white space is swallowed. */
401 /************************************************************************/
402
OGRWktReadToken(const char * pszInput,char * pszToken)403 const char *OGRWktReadToken( const char * pszInput, char * pszToken )
404
405 {
406 if( pszInput == nullptr )
407 return nullptr;
408
409 /* -------------------------------------------------------------------- */
410 /* Swallow pre-white space. */
411 /* -------------------------------------------------------------------- */
412 while( *pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' || *pszInput == '\r' )
413 ++pszInput;
414
415 /* -------------------------------------------------------------------- */
416 /* If this is a delimiter, read just one character. */
417 /* -------------------------------------------------------------------- */
418 if( *pszInput == '(' || *pszInput == ')' || *pszInput == ',' )
419 {
420 pszToken[0] = *pszInput;
421 pszToken[1] = '\0';
422
423 ++pszInput;
424 }
425
426 /* -------------------------------------------------------------------- */
427 /* Or if it alpha numeric read till we reach non-alpha numeric */
428 /* text. */
429 /* -------------------------------------------------------------------- */
430 else
431 {
432 int iChar = 0;
433
434 while( iChar < OGR_WKT_TOKEN_MAX-1
435 && ((*pszInput >= 'a' && *pszInput <= 'z')
436 || (*pszInput >= 'A' && *pszInput <= 'Z')
437 || (*pszInput >= '0' && *pszInput <= '9')
438 || *pszInput == '.'
439 || *pszInput == '+'
440 || *pszInput == '-') )
441 {
442 pszToken[iChar++] = *(pszInput++);
443 }
444
445 pszToken[iChar++] = '\0';
446 }
447
448 /* -------------------------------------------------------------------- */
449 /* Eat any trailing white space. */
450 /* -------------------------------------------------------------------- */
451 while( *pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' || *pszInput == '\r' )
452 ++pszInput;
453
454 return pszInput;
455 }
456
457 /************************************************************************/
458 /* OGRWktReadPoints() */
459 /* */
460 /* Read a point string. The point list must be contained in */
461 /* brackets and each point pair separated by a comma. */
462 /************************************************************************/
463
OGRWktReadPoints(const char * pszInput,OGRRawPoint ** ppaoPoints,double ** ppadfZ,int * pnMaxPoints,int * pnPointsRead)464 const char * OGRWktReadPoints( const char * pszInput,
465 OGRRawPoint ** ppaoPoints, double **ppadfZ,
466 int * pnMaxPoints,
467 int * pnPointsRead )
468
469 {
470 const char *pszOrigInput = pszInput;
471 *pnPointsRead = 0;
472
473 if( pszInput == nullptr )
474 return nullptr;
475
476 /* -------------------------------------------------------------------- */
477 /* Eat any leading white space. */
478 /* -------------------------------------------------------------------- */
479 while( *pszInput == ' ' || *pszInput == '\t' )
480 ++pszInput;
481
482 /* -------------------------------------------------------------------- */
483 /* If this isn't an opening bracket then we have a problem. */
484 /* -------------------------------------------------------------------- */
485 if( *pszInput != '(' )
486 {
487 CPLDebug( "OGR",
488 "Expected '(', but got %s in OGRWktReadPoints().",
489 pszInput );
490
491 return pszInput;
492 }
493
494 ++pszInput;
495
496 /* ==================================================================== */
497 /* This loop reads a single point. It will continue till we */
498 /* run out of well formed points, or a closing bracket is */
499 /* encountered. */
500 /* ==================================================================== */
501 char szDelim[OGR_WKT_TOKEN_MAX] = {};
502
503 do {
504 /* -------------------------------------------------------------------- */
505 /* Read the X and Y values, verify they are numeric. */
506 /* -------------------------------------------------------------------- */
507 char szTokenX[OGR_WKT_TOKEN_MAX] = {};
508 char szTokenY[OGR_WKT_TOKEN_MAX] = {};
509
510 pszInput = OGRWktReadToken( pszInput, szTokenX );
511 pszInput = OGRWktReadToken( pszInput, szTokenY );
512
513 if( (!isdigit(szTokenX[0]) && szTokenX[0] != '-' && szTokenX[0] != '.' )
514 || (!isdigit(szTokenY[0]) && szTokenY[0] != '-' &&
515 szTokenY[0] != '.') )
516 return nullptr;
517
518 /* -------------------------------------------------------------------- */
519 /* Do we need to grow the point list to hold this point? */
520 /* -------------------------------------------------------------------- */
521 if( *pnPointsRead == *pnMaxPoints )
522 {
523 *pnMaxPoints = *pnMaxPoints * 2 + 10;
524 *ppaoPoints = static_cast<OGRRawPoint *>(
525 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints) );
526
527 if( *ppadfZ != nullptr )
528 {
529 *ppadfZ = static_cast<double *>(
530 CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints) );
531 }
532 }
533
534 /* -------------------------------------------------------------------- */
535 /* Add point to list. */
536 /* -------------------------------------------------------------------- */
537 (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
538 (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
539
540 /* -------------------------------------------------------------------- */
541 /* Do we have a Z coordinate? */
542 /* -------------------------------------------------------------------- */
543 pszInput = OGRWktReadToken( pszInput, szDelim );
544
545 if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
546 {
547 if( *ppadfZ == nullptr )
548 {
549 *ppadfZ = static_cast<double *>(
550 CPLCalloc(sizeof(double), *pnMaxPoints) );
551 }
552
553 (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
554
555 pszInput = OGRWktReadToken( pszInput, szDelim );
556 }
557 else if( *ppadfZ != nullptr )
558 {
559 (*ppadfZ)[*pnPointsRead] = 0.0;
560 }
561
562 ++(*pnPointsRead);
563
564 /* -------------------------------------------------------------------- */
565 /* Do we have a M coordinate? */
566 /* If we do, just skip it. */
567 /* -------------------------------------------------------------------- */
568 if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
569 {
570 pszInput = OGRWktReadToken( pszInput, szDelim );
571 }
572
573 /* -------------------------------------------------------------------- */
574 /* Read next delimiter ... it should be a comma if there are */
575 /* more points. */
576 /* -------------------------------------------------------------------- */
577 if( szDelim[0] != ')' && szDelim[0] != ',' )
578 {
579 CPLDebug( "OGR",
580 "Corrupt input in OGRWktReadPoints(). "
581 "Got `%s' when expecting `,' or `)', near `%s' in %s.",
582 szDelim, pszInput, pszOrigInput );
583 return nullptr;
584 }
585 } while( szDelim[0] == ',' );
586
587 return pszInput;
588 }
589
590 /************************************************************************/
591 /* OGRWktReadPointsM() */
592 /* */
593 /* Read a point string. The point list must be contained in */
594 /* brackets and each point pair separated by a comma. */
595 /************************************************************************/
596
OGRWktReadPointsM(const char * pszInput,OGRRawPoint ** ppaoPoints,double ** ppadfZ,double ** ppadfM,int * flags,int * pnMaxPoints,int * pnPointsRead)597 const char * OGRWktReadPointsM( const char * pszInput,
598 OGRRawPoint ** ppaoPoints,
599 double **ppadfZ, double **ppadfM,
600 int * flags,
601 int * pnMaxPoints,
602 int * pnPointsRead )
603
604 {
605 const char *pszOrigInput = pszInput;
606 const bool bNoFlags =
607 !(*flags & OGRGeometry::OGR_G_3D) &&
608 !(*flags & OGRGeometry::OGR_G_MEASURED);
609 *pnPointsRead = 0;
610
611 if( pszInput == nullptr )
612 return nullptr;
613
614 /* -------------------------------------------------------------------- */
615 /* Eat any leading white space. */
616 /* -------------------------------------------------------------------- */
617 while( *pszInput == ' ' || *pszInput == '\t' )
618 ++pszInput;
619
620 /* -------------------------------------------------------------------- */
621 /* If this isn't an opening bracket then we have a problem. */
622 /* -------------------------------------------------------------------- */
623 if( *pszInput != '(' )
624 {
625 CPLDebug( "OGR",
626 "Expected '(', but got %s in OGRWktReadPointsM().",
627 pszInput );
628
629 return pszInput;
630 }
631
632 ++pszInput;
633
634 /* ==================================================================== */
635 /* This loop reads a single point. It will continue till we */
636 /* run out of well formed points, or a closing bracket is */
637 /* encountered. */
638 /* ==================================================================== */
639 char szDelim[OGR_WKT_TOKEN_MAX] = {};
640
641 do {
642 /* -------------------------------------------------------------------- */
643 /* Read the X and Y values, verify they are numeric. */
644 /* -------------------------------------------------------------------- */
645 char szTokenX[OGR_WKT_TOKEN_MAX] = {};
646 char szTokenY[OGR_WKT_TOKEN_MAX] = {};
647
648 pszInput = OGRWktReadToken( pszInput, szTokenX );
649 pszInput = OGRWktReadToken( pszInput, szTokenY );
650
651 if( (!isdigit(szTokenX[0]) && szTokenX[0] != '-' && szTokenX[0] != '.' )
652 || (!isdigit(szTokenY[0]) && szTokenY[0] != '-' &&
653 szTokenY[0] != '.') )
654 return nullptr;
655
656 /* -------------------------------------------------------------------- */
657 /* Do we need to grow the point list to hold this point? */
658 /* -------------------------------------------------------------------- */
659 if( *pnPointsRead == *pnMaxPoints )
660 {
661 *pnMaxPoints = *pnMaxPoints * 2 + 10;
662 *ppaoPoints = static_cast<OGRRawPoint *>(
663 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints) );
664
665 if( *ppadfZ != nullptr )
666 {
667 *ppadfZ = static_cast<double *>(
668 CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints) );
669 }
670
671 if( *ppadfM != nullptr )
672 {
673 *ppadfM = static_cast<double *>(
674 CPLRealloc(*ppadfM, sizeof(double) * *pnMaxPoints) );
675 }
676 }
677
678 /* -------------------------------------------------------------------- */
679 /* Add point to list. */
680 /* -------------------------------------------------------------------- */
681 (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
682 (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
683
684 /* -------------------------------------------------------------------- */
685 /* Read the next token. */
686 /* -------------------------------------------------------------------- */
687 pszInput = OGRWktReadToken( pszInput, szDelim );
688
689 /* -------------------------------------------------------------------- */
690 /* If there are unexpectedly more coordinates, they are Z. */
691 /* -------------------------------------------------------------------- */
692
693 if( !(*flags & OGRGeometry::OGR_G_3D) &&
694 !(*flags & OGRGeometry::OGR_G_MEASURED) &&
695 (isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' ))
696 {
697 *flags |= OGRGeometry::OGR_G_3D;
698 }
699
700 /* -------------------------------------------------------------------- */
701 /* Get Z if flag says so. */
702 /* Zero out possible remains from earlier strings. */
703 /* -------------------------------------------------------------------- */
704
705 if( *flags & OGRGeometry::OGR_G_3D )
706 {
707 if( *ppadfZ == nullptr )
708 {
709 *ppadfZ = static_cast<double *>(
710 CPLCalloc(sizeof(double), *pnMaxPoints) );
711 }
712 if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
713 {
714 (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
715 pszInput = OGRWktReadToken( pszInput, szDelim );
716 }
717 else
718 {
719 (*ppadfZ)[*pnPointsRead] = 0.0;
720 }
721 }
722 else if( *ppadfZ != nullptr )
723 {
724 (*ppadfZ)[*pnPointsRead] = 0.0;
725 }
726
727 /* -------------------------------------------------------------------- */
728 /* If there are unexpectedly even more coordinates, */
729 /* they are discarded unless there were no flags originally. */
730 /* This is for backwards compatibility. Should this be an error? */
731 /* -------------------------------------------------------------------- */
732
733 if( !(*flags & OGRGeometry::OGR_G_MEASURED) &&
734 (isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' ) )
735 {
736 if( bNoFlags )
737 {
738 *flags |= OGRGeometry::OGR_G_MEASURED;
739 }
740 else
741 {
742 pszInput = OGRWktReadToken( pszInput, szDelim );
743 }
744 }
745
746 /* -------------------------------------------------------------------- */
747 /* Get M if flag says so. */
748 /* Zero out possible remains from earlier strings. */
749 /* -------------------------------------------------------------------- */
750
751 if( *flags & OGRGeometry::OGR_G_MEASURED )
752 {
753 if( *ppadfM == nullptr )
754 {
755 *ppadfM = static_cast<double *>(
756 CPLCalloc(sizeof(double), *pnMaxPoints) );
757 }
758 if( isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.' )
759 {
760 (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
761 pszInput = OGRWktReadToken( pszInput, szDelim );
762 }
763 else
764 {
765 (*ppadfM)[*pnPointsRead] = 0.0;
766 }
767 }
768 else if( *ppadfM != nullptr )
769 {
770 (*ppadfM)[*pnPointsRead] = 0.0;
771 }
772
773 /* -------------------------------------------------------------------- */
774 /* If there are still more coordinates and we do not have Z */
775 /* then we have a case of flags == M and four coordinates. */
776 /* This is allowed in BNF. */
777 /* -------------------------------------------------------------------- */
778
779 if( !(*flags & OGRGeometry::OGR_G_3D) &&
780 (isdigit(szDelim[0]) || szDelim[0] == '-' || szDelim[0] == '.') )
781 {
782 *flags |= OGRGeometry::OGR_G_3D;
783 if( *ppadfZ == nullptr )
784 {
785 *ppadfZ = static_cast<double *>(
786 CPLCalloc(sizeof(double), *pnMaxPoints) );
787 }
788 (*ppadfZ)[*pnPointsRead] = (*ppadfM)[*pnPointsRead];
789 (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
790 pszInput = OGRWktReadToken( pszInput, szDelim );
791 }
792
793 /* -------------------------------------------------------------------- */
794 /* Increase points index. */
795 /* -------------------------------------------------------------------- */
796 ++(*pnPointsRead);
797
798 /* -------------------------------------------------------------------- */
799 /* The next delimiter should be a comma or an ending bracket. */
800 /* -------------------------------------------------------------------- */
801 if( szDelim[0] != ')' && szDelim[0] != ',' )
802 {
803 CPLDebug( "OGR",
804 "Corrupt input in OGRWktReadPointsM() "
805 "Got `%s' when expecting `,' or `)', near `%s' in %s.",
806 szDelim, pszInput, pszOrigInput );
807 return nullptr;
808 }
809 } while( szDelim[0] == ',' );
810
811 return pszInput;
812 }
813
814 /************************************************************************/
815 /* OGRMalloc() */
816 /* */
817 /* Cover for CPLMalloc() */
818 /************************************************************************/
819
OGRMalloc(size_t size)820 void *OGRMalloc( size_t size )
821
822 {
823 return CPLMalloc( size );
824 }
825
826 /************************************************************************/
827 /* OGRCalloc() */
828 /* */
829 /* Cover for CPLCalloc() */
830 /************************************************************************/
831
OGRCalloc(size_t count,size_t size)832 void *OGRCalloc( size_t count, size_t size )
833
834 {
835 return CPLCalloc( count, size );
836 }
837
838 /************************************************************************/
839 /* OGRRealloc() */
840 /* */
841 /* Cover for CPLRealloc() */
842 /************************************************************************/
843
OGRRealloc(void * pOld,size_t size)844 void *OGRRealloc( void * pOld, size_t size )
845
846 {
847 return CPLRealloc( pOld, size );
848 }
849
850 /************************************************************************/
851 /* OGRFree() */
852 /* */
853 /* Cover for CPLFree(). */
854 /************************************************************************/
855
OGRFree(void * pMemory)856 void OGRFree( void * pMemory )
857
858 {
859 CPLFree( pMemory );
860 }
861
862 /**
863 * \fn OGRGeneralCmdLineProcessor(int, char***, int)
864 * General utility option processing.
865 *
866 * This function is intended to provide a variety of generic commandline
867 * options for all OGR commandline utilities. It takes care of the following
868 * commandline options:
869 *
870 * --version: report version of GDAL in use.
871 * --license: report GDAL license info.
872 * --format [format]: report details of one format driver.
873 * --formats: report all format drivers configured.
874 * --optfile filename: expand an option file into the argument list.
875 * --config key value: set system configuration option.
876 * --debug [on/off/value]: set debug level.
877 * --pause: Pause for user input (allows time to attach debugger)
878 * --locale [locale]: Install a locale using setlocale() (debugging)
879 * --help-general: report detailed help on general options.
880 *
881 * The argument array is replaced "in place" and should be freed with
882 * CSLDestroy() when no longer needed. The typical usage looks something
883 * like the following. Note that the formats should be registered so that
884 * the --formats option will work properly.
885 *
886 * int main( int argc, char ** argv )
887 * {
888 * OGRRegisterAll();
889 *
890 * argc = OGRGeneralCmdLineProcessor( argc, &argv, 0 );
891 * if( argc < 1 )
892 * exit( -argc );
893 *
894 * @param nArgc number of values in the argument list.
895 * @param ppapszArgv pointer to the argument list array (will be updated in
896 * place).
897 * @param nOptions unused.
898 *
899 * @return updated nArgc argument count. Return of 0 requests terminate
900 * without error, return of -1 requests exit with error code.
901 */
902
OGRGeneralCmdLineProcessor(int nArgc,char *** ppapszArgv,CPL_UNUSED int nOptions)903 int OGRGeneralCmdLineProcessor( int nArgc, char ***ppapszArgv,
904 CPL_UNUSED int nOptions )
905
906 {
907 return GDALGeneralCmdLineProcessor( nArgc, ppapszArgv, GDAL_OF_VECTOR );
908 }
909
910 /************************************************************************/
911 /* OGRParseDate() */
912 /* */
913 /* Parse a variety of text date formats into an OGRField. */
914 /************************************************************************/
915
916 /**
917 * Parse date string.
918 *
919 * This function attempts to parse a date string in a variety of formats
920 * into the OGRField.Date format suitable for use with OGR. Generally
921 * speaking this function is expecting values like:
922 *
923 * YYYY-MM-DD HH:MM:SS[.sss]+nn
924 * or YYYY-MM-DDTHH:MM:SS[.sss]Z (ISO 8601 format)
925 * or YYYY-MM-DDZ
926 *
927 * The seconds may also have a decimal portion (which is ignored). And
928 * just dates (YYYY-MM-DD) or just times (HH:MM:SS[.sss]) are also supported.
929 * The date may also be in YYYY/MM/DD format. If the year is less than 100
930 * and greater than 30 a "1900" century value will be set. If it is less than
931 * 30 and greater than -1 then a "2000" century value will be set. In
932 * the future this function may be generalized, and additional control
933 * provided through nOptions, but an nOptions value of "0" should always do
934 * a reasonable default form of processing.
935 *
936 * The value of psField will be indeterminate if the function fails (returns
937 * FALSE).
938 *
939 * @param pszInput the input date string.
940 * @param psField the OGRField that will be updated with the parsed result.
941 * @param nOptions parsing options, for now always 0.
942 *
943 * @return TRUE if apparently successful or FALSE on failure.
944 */
945
OGRParseDate(const char * pszInput,OGRField * psField,CPL_UNUSED int nOptions)946 int OGRParseDate( const char *pszInput,
947 OGRField *psField,
948 CPL_UNUSED int nOptions )
949 {
950 psField->Date.Year = 0;
951 psField->Date.Month = 0;
952 psField->Date.Day = 0;
953 psField->Date.Hour = 0;
954 psField->Date.Minute = 0;
955 psField->Date.Second = 0;
956 psField->Date.TZFlag = 0;
957 psField->Date.Reserved = 0;
958
959 /* -------------------------------------------------------------------- */
960 /* Do we have a date? */
961 /* -------------------------------------------------------------------- */
962 while( *pszInput == ' ' )
963 ++pszInput;
964
965 bool bGotSomething = false;
966 if( strstr(pszInput,"-") != nullptr || strstr(pszInput,"/") != nullptr )
967 {
968 if( !(*pszInput == '-' || *pszInput == '+' ||
969 (*pszInput >= '0' && *pszInput <= '9')) )
970 return FALSE;
971 int nYear = atoi(pszInput);
972 if (nYear > std::numeric_limits<GInt16>::max() ||
973 nYear < std::numeric_limits<GInt16>::min() )
974 {
975 CPLError(CE_Failure, CPLE_NotSupported,
976 "Years < %d or > %d are not supported",
977 std::numeric_limits<GInt16>::min(),
978 std::numeric_limits<GInt16>::max());
979 return FALSE;
980 }
981 psField->Date.Year = static_cast<GInt16>(nYear);
982 if( (pszInput[1] == '-' || pszInput[1] == '/' ) ||
983 (pszInput[1] != '\0' &&
984 (pszInput[2] == '-' || pszInput[2] == '/' )) )
985 {
986 if( psField->Date.Year < 100 && psField->Date.Year >= 30 )
987 psField->Date.Year += 1900;
988 else if( psField->Date.Year < 30 && psField->Date.Year >= 0 )
989 psField->Date.Year += 2000;
990 }
991
992 if( *pszInput == '-' )
993 ++pszInput;
994 while( *pszInput >= '0' && *pszInput <= '9' )
995 ++pszInput;
996 if( *pszInput != '-' && *pszInput != '/' )
997 return FALSE;
998 else
999 ++pszInput;
1000
1001 const int nMonth = atoi(pszInput);
1002 if( nMonth <= 0 || nMonth > 12 )
1003 return FALSE;
1004 psField->Date.Month = static_cast<GByte>(nMonth);
1005
1006 while( *pszInput >= '0' && *pszInput <= '9' )
1007 ++pszInput;
1008 if( *pszInput != '-' && *pszInput != '/' )
1009 return FALSE;
1010 else
1011 ++pszInput;
1012
1013 const int nDay = atoi(pszInput);
1014 if( nDay <= 0 || nDay > 31 )
1015 return FALSE;
1016 psField->Date.Day = static_cast<GByte>(nDay);
1017
1018 while( *pszInput >= '0' && *pszInput <= '9' )
1019 ++pszInput;
1020 if( *pszInput == '\0' )
1021 return TRUE;
1022
1023 bGotSomething = true;
1024
1025 // If ISO 8601 format.
1026 if( *pszInput == 'T' )
1027 ++pszInput;
1028 else if( *pszInput == 'Z' )
1029 return TRUE;
1030 else if( *pszInput != ' ' )
1031 return FALSE;
1032 }
1033
1034 /* -------------------------------------------------------------------- */
1035 /* Do we have a time? */
1036 /* -------------------------------------------------------------------- */
1037 while( *pszInput == ' ' )
1038 ++pszInput;
1039
1040 if( strstr(pszInput, ":") != nullptr )
1041 {
1042 if( !(*pszInput >= '0' && *pszInput <= '9') )
1043 return FALSE;
1044 const int nHour = atoi(pszInput);
1045 if( nHour < 0 || nHour > 23 )
1046 return FALSE;
1047 psField->Date.Hour = static_cast<GByte>(nHour);
1048
1049 while( *pszInput >= '0' && *pszInput <= '9' )
1050 ++pszInput;
1051 if( *pszInput != ':' )
1052 return FALSE;
1053 else
1054 ++pszInput;
1055
1056 if( !(*pszInput >= '0' && *pszInput <= '9') )
1057 return FALSE;
1058 const int nMinute = atoi(pszInput);
1059 if( nMinute < 0 || nMinute > 59 )
1060 return FALSE;
1061 psField->Date.Minute = static_cast<GByte>(nMinute);
1062
1063 while( *pszInput >= '0' && *pszInput <= '9' )
1064 ++pszInput;
1065 if( *pszInput == ':' )
1066 {
1067 ++pszInput;
1068
1069 if( !(*pszInput >= '0' && *pszInput <= '9') )
1070 return FALSE;
1071 const double dfSeconds = CPLAtof(pszInput);
1072 // We accept second=60 for leap seconds
1073 if (dfSeconds > 60.0 || dfSeconds < 0.0) return FALSE;
1074 psField->Date.Second = static_cast<float>(dfSeconds);
1075
1076 while( (*pszInput >= '0' && *pszInput <= '9')
1077 || *pszInput == '.' )
1078 {
1079 ++pszInput;
1080 }
1081
1082 // If ISO 8601 format.
1083 if( *pszInput == 'Z' )
1084 {
1085 psField->Date.TZFlag = 100;
1086 }
1087 }
1088
1089 bGotSomething = true;
1090 }
1091
1092 // No date or time!
1093 if( !bGotSomething )
1094 return FALSE;
1095
1096 /* -------------------------------------------------------------------- */
1097 /* Do we have a timezone? */
1098 /* -------------------------------------------------------------------- */
1099 while( *pszInput == ' ' )
1100 ++pszInput;
1101
1102 if( *pszInput == '-' || *pszInput == '+' )
1103 {
1104 // +HH integral offset
1105 if( strlen(pszInput) <= 3 )
1106 {
1107 psField->Date.TZFlag = static_cast<GByte>(100 + atoi(pszInput) * 4);
1108 }
1109 else if( pszInput[3] == ':' // +HH:MM offset
1110 && atoi(pszInput + 4) % 15 == 0 )
1111 {
1112 psField->Date.TZFlag = static_cast<GByte>(100
1113 + atoi(pszInput + 1) * 4
1114 + (atoi(pszInput + 4) / 15));
1115
1116 if( pszInput[0] == '-' )
1117 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1118 }
1119 else if( isdigit(pszInput[3]) && isdigit(pszInput[4]) // +HHMM offset
1120 && atoi(pszInput + 3) % 15 == 0 )
1121 {
1122 psField->Date.TZFlag = static_cast<GByte>(100
1123 + static_cast<GByte>(CPLScanLong(pszInput + 1, 2)) * 4
1124 + (atoi(pszInput + 3) / 15));
1125
1126 if( pszInput[0] == '-' )
1127 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1128 }
1129 else if( isdigit(pszInput[3]) && pszInput[4] == '\0' // +HMM offset
1130 && atoi(pszInput + 2) % 15 == 0 )
1131 {
1132 psField->Date.TZFlag = static_cast<GByte>(100
1133 + static_cast<GByte>(CPLScanLong(pszInput + 1, 1)) * 4
1134 + (atoi(pszInput + 2) / 15));
1135
1136 if( pszInput[0] == '-' )
1137 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1138 }
1139 // otherwise ignore any timezone info.
1140 }
1141
1142 return TRUE;
1143 }
1144
1145 /************************************************************************/
1146 /* OGRParseXMLDateTime() */
1147 /************************************************************************/
1148
OGRParseXMLDateTime(const char * pszXMLDateTime,OGRField * psField)1149 int OGRParseXMLDateTime( const char* pszXMLDateTime,
1150 OGRField* psField)
1151 {
1152 int year = 0;
1153 int month = 0;
1154 int day = 0;
1155 int hour = 0;
1156 int minute = 0;
1157 int TZHour = 0;
1158 int TZMinute = 0;
1159 float second = 0;
1160 char c = '\0';
1161 int TZ = 0;
1162 bool bRet = false;
1163
1164 // Date is expressed as a UTC date.
1165 if( sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c",
1166 &year, &month, &day, &hour, &minute, &second, &c) == 7 &&
1167 c == 'Z' )
1168 {
1169 TZ = 100;
1170 bRet = true;
1171 }
1172 // Date is expressed as a UTC date, with a timezone.
1173 else if( sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d",
1174 &year, &month, &day, &hour, &minute, &second, &c,
1175 &TZHour, &TZMinute) == 9 &&
1176 (c == '+' || c == '-') )
1177 {
1178 TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
1179 bRet = true;
1180 }
1181 // Date is expressed into an unknown timezone.
1182 else if( sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f",
1183 &year, &month, &day, &hour, &minute, &second) == 6 )
1184 {
1185 TZ = 0;
1186 bRet = true;
1187 }
1188 // Date is expressed as a UTC date with only year:month:day.
1189 else if( sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) ==
1190 3 )
1191 {
1192 TZ = 0;
1193 bRet = true;
1194 }
1195 // Date is expressed as a UTC date with only year:month.
1196 else if( sscanf(pszXMLDateTime, "%04d-%02d", &year, &month) ==
1197 2 )
1198 {
1199 TZ = 0;
1200 bRet = true;
1201 day = 1;
1202 }
1203
1204 if( !bRet )
1205 return FALSE;
1206
1207 psField->Date.Year = static_cast<GInt16>(year);
1208 psField->Date.Month = static_cast<GByte>(month);
1209 psField->Date.Day = static_cast<GByte>(day);
1210 psField->Date.Hour = static_cast<GByte>(hour);
1211 psField->Date.Minute = static_cast<GByte>(minute);
1212 psField->Date.Second = second;
1213 psField->Date.TZFlag = static_cast<GByte>(TZ);
1214 psField->Date.Reserved = 0;
1215
1216 return TRUE;
1217 }
1218
1219 /************************************************************************/
1220 /* OGRParseRFC822DateTime() */
1221 /************************************************************************/
1222
1223 static const char* const aszMonthStr[] = {
1224 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1225 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
1226
OGRParseRFC822DateTime(const char * pszRFC822DateTime,OGRField * psField)1227 int OGRParseRFC822DateTime( const char* pszRFC822DateTime, OGRField* psField )
1228 {
1229 int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
1230 if( !CPLParseRFC822DateTime( pszRFC822DateTime,
1231 &nYear,
1232 &nMonth,
1233 &nDay,
1234 &nHour,
1235 &nMinute,
1236 &nSecond,
1237 &nTZFlag,
1238 nullptr ) )
1239 {
1240 return false;
1241 }
1242
1243 psField->Date.Year = static_cast<GInt16>(nYear);
1244 psField->Date.Month = static_cast<GByte>(nMonth);
1245 psField->Date.Day = static_cast<GByte>(nDay);
1246 psField->Date.Hour = static_cast<GByte>(nHour);
1247 psField->Date.Minute = static_cast<GByte>(nMinute);
1248 psField->Date.Second = (nSecond < 0) ? 0.0f : static_cast<float>(nSecond);
1249 psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
1250 psField->Date.Reserved = 0;
1251
1252 return true;
1253 }
1254
1255 /**
1256 * Returns the day of the week in Gregorian calendar
1257 *
1258 * @param day : day of the month, between 1 and 31
1259 * @param month : month of the year, between 1 (Jan) and 12 (Dec)
1260 * @param year : year
1261
1262 * @return day of the week : 0 for Monday, ... 6 for Sunday
1263 */
1264
OGRGetDayOfWeek(int day,int month,int year)1265 int OGRGetDayOfWeek( int day, int month, int year )
1266 {
1267 // Reference: Zeller's congruence.
1268 const int q = day;
1269 int m = month;
1270 if( month >=3 )
1271 {
1272 // m = month;
1273 }
1274 else
1275 {
1276 m = month + 12;
1277 year--;
1278 }
1279 const int K = year % 100;
1280 const int J = year / 100;
1281 const int h = ( q + (((m+1)*26)/10) + K + K/4 + J/4 + 5 * J) % 7;
1282 return ( h + 5 ) % 7;
1283 }
1284
1285 /************************************************************************/
1286 /* OGRGetRFC822DateTime() */
1287 /************************************************************************/
1288
OGRGetRFC822DateTime(const OGRField * psField)1289 char* OGRGetRFC822DateTime( const OGRField* psField )
1290 {
1291 char* pszTZ = nullptr;
1292 const char* const aszDayOfWeek[] =
1293 { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
1294
1295 int dayofweek = OGRGetDayOfWeek(psField->Date.Day, psField->Date.Month,
1296 psField->Date.Year);
1297
1298 int month = psField->Date.Month;
1299 if( month < 1 || month > 12 )
1300 month = 1;
1301
1302 int TZFlag = psField->Date.TZFlag;
1303 if( TZFlag == 0 || TZFlag == 100 )
1304 {
1305 pszTZ = CPLStrdup("GMT");
1306 }
1307 else
1308 {
1309 int TZOffset = std::abs(TZFlag - 100) * 15;
1310 int TZHour = TZOffset / 60;
1311 int TZMinute = TZOffset - TZHour * 60;
1312 pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-',
1313 TZHour, TZMinute));
1314 }
1315 char* pszRet = CPLStrdup(CPLSPrintf(
1316 "%s, %02d %s %04d %02d:%02d:%02d %s",
1317 aszDayOfWeek[dayofweek], psField->Date.Day, aszMonthStr[month - 1],
1318 psField->Date.Year, psField->Date.Hour,
1319 psField->Date.Minute, static_cast<int>(psField->Date.Second), pszTZ));
1320 CPLFree(pszTZ);
1321 return pszRet;
1322 }
1323
1324 /************************************************************************/
1325 /* OGRGetXMLDateTime() */
1326 /************************************************************************/
1327
OGRGetXMLDateTime(const OGRField * psField)1328 char* OGRGetXMLDateTime(const OGRField* psField)
1329 {
1330 return OGRGetXMLDateTime(psField, false);
1331 }
1332
OGRGetXMLDateTime(const OGRField * psField,bool bAlwaysMillisecond)1333 char* OGRGetXMLDateTime(const OGRField* psField, bool bAlwaysMillisecond)
1334 {
1335 const GInt16 year = psField->Date.Year;
1336 const GByte month = psField->Date.Month;
1337 const GByte day = psField->Date.Day;
1338 const GByte hour = psField->Date.Hour;
1339 const GByte minute = psField->Date.Minute;
1340 const float second = psField->Date.Second;
1341 const GByte TZFlag = psField->Date.TZFlag;
1342
1343 char szTimeZone[7];
1344 char* pszRet = nullptr;
1345
1346 switch( TZFlag )
1347 {
1348 case 0: // Unknown time zone
1349 case 1: // Local time zone (not specified)
1350 szTimeZone[0] = 0;
1351 break;
1352
1353 case 100: // GMT
1354 szTimeZone[0] = 'Z';
1355 szTimeZone[1] = 0;
1356 break;
1357
1358 default: // Offset (in quarter-hour units) from GMT
1359 const int TZOffset = std::abs(TZFlag - 100) * 15;
1360 const int TZHour = TZOffset / 60;
1361 const int TZMinute = TZOffset % 60;
1362
1363 snprintf(szTimeZone, 7, "%c%02d:%02d",
1364 (TZFlag > 100) ? '+' : '-', TZHour, TZMinute);
1365 }
1366
1367 if( OGR_GET_MS(second) || bAlwaysMillisecond )
1368 pszRet = CPLStrdup(CPLSPrintf(
1369 "%04d-%02u-%02uT%02u:%02u:%06.3f%s",
1370 year, month, day, hour, minute, second,
1371 szTimeZone));
1372 else
1373 pszRet = CPLStrdup(CPLSPrintf(
1374 "%04d-%02u-%02uT%02u:%02u:%02u%s",
1375 year, month, day, hour, minute,
1376 static_cast<GByte>(second), szTimeZone));
1377
1378 return pszRet;
1379 }
1380
1381 /************************************************************************/
1382 /* OGRGetXML_UTF8_EscapedString() */
1383 /************************************************************************/
1384
OGRGetXML_UTF8_EscapedString(const char * pszString)1385 char* OGRGetXML_UTF8_EscapedString(const char* pszString)
1386 {
1387 char *pszEscaped = nullptr;
1388 if( !CPLIsUTF8(pszString, -1) &&
1389 CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")) )
1390 {
1391 static bool bFirstTime = true;
1392 if( bFirstTime )
1393 {
1394 bFirstTime = false;
1395 CPLError(CE_Warning, CPLE_AppDefined,
1396 "%s is not a valid UTF-8 string. Forcing it to ASCII. "
1397 "If you still want the original string and change the XML "
1398 "file encoding afterwards, you can define "
1399 "OGR_FORCE_ASCII=NO as configuration option. "
1400 "This warning won't be issued anymore", pszString);
1401 }
1402 else
1403 {
1404 CPLDebug("OGR",
1405 "%s is not a valid UTF-8 string. Forcing it to ASCII",
1406 pszString);
1407 }
1408 char* pszTemp = CPLForceToASCII(pszString, -1, '?');
1409 pszEscaped = CPLEscapeString( pszTemp, -1, CPLES_XML );
1410 CPLFree(pszTemp);
1411 }
1412 else
1413 pszEscaped = CPLEscapeString( pszString, -1, CPLES_XML );
1414 return pszEscaped;
1415 }
1416
1417 /************************************************************************/
1418 /* OGRCompareDate() */
1419 /************************************************************************/
1420
OGRCompareDate(const OGRField * psFirstTuple,const OGRField * psSecondTuple)1421 int OGRCompareDate( const OGRField *psFirstTuple,
1422 const OGRField *psSecondTuple )
1423 {
1424 // TODO: We ignore TZFlag.
1425
1426 if( psFirstTuple->Date.Year < psSecondTuple->Date.Year )
1427 return -1;
1428 else if( psFirstTuple->Date.Year > psSecondTuple->Date.Year )
1429 return 1;
1430
1431 if( psFirstTuple->Date.Month < psSecondTuple->Date.Month )
1432 return -1;
1433 else if( psFirstTuple->Date.Month > psSecondTuple->Date.Month )
1434 return 1;
1435
1436 if( psFirstTuple->Date.Day < psSecondTuple->Date.Day )
1437 return -1;
1438 else if( psFirstTuple->Date.Day > psSecondTuple->Date.Day )
1439 return 1;
1440
1441 if( psFirstTuple->Date.Hour < psSecondTuple->Date.Hour )
1442 return -1;
1443 else if( psFirstTuple->Date.Hour > psSecondTuple->Date.Hour )
1444 return 1;
1445
1446 if( psFirstTuple->Date.Minute < psSecondTuple->Date.Minute )
1447 return -1;
1448 else if( psFirstTuple->Date.Minute > psSecondTuple->Date.Minute )
1449 return 1;
1450
1451 if( psFirstTuple->Date.Second < psSecondTuple->Date.Second )
1452 return -1;
1453 else if( psFirstTuple->Date.Second > psSecondTuple->Date.Second )
1454 return 1;
1455
1456 return 0;
1457 }
1458
1459 /************************************************************************/
1460 /* OGRFastAtof() */
1461 /************************************************************************/
1462
1463 // On Windows, CPLAtof() is very slow if the number is followed by other long
1464 // content. Just extract the number into a short string before calling
1465 // CPLAtof() on it.
1466 static
OGRCallAtofOnShortString(const char * pszStr)1467 double OGRCallAtofOnShortString(const char* pszStr)
1468 {
1469 const char* p = pszStr;
1470 while( *p == ' ' || *p == '\t' )
1471 ++p;
1472
1473 char szTemp[128] = {};
1474 int nCounter = 0;
1475 while( *p == '+' ||
1476 *p == '-' ||
1477 (*p >= '0' && *p <= '9') ||
1478 *p == '.' ||
1479 (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D') )
1480 {
1481 szTemp[nCounter++] = *(p++);
1482 if( nCounter == 127 )
1483 return CPLAtof(pszStr);
1484 }
1485 szTemp[nCounter] = '\0';
1486 return CPLAtof(szTemp);
1487 }
1488
1489 /** Same contract as CPLAtof, except than it doesn't always call the
1490 * system CPLAtof() that may be slow on some platforms. For simple but
1491 * common strings, it'll use a faster implementation (up to 20x faster
1492 * than CPLAtof() on MS runtime libraries) that has no guaranty to return
1493 * exactly the same floating point number.
1494 */
1495
OGRFastAtof(const char * pszStr)1496 double OGRFastAtof(const char* pszStr)
1497 {
1498 double dfVal = 0;
1499 double dfSign = 1.0;
1500 const char* p = pszStr;
1501
1502 constexpr double adfTenPower[] =
1503 {
1504 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10,
1505 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20,
1506 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31
1507 };
1508
1509 while( *p == ' ' || *p == '\t' )
1510 ++p;
1511
1512 if( *p == '+' )
1513 ++p;
1514 else if( *p == '-' )
1515 {
1516 dfSign = -1.0;
1517 ++p;
1518 }
1519
1520 while( true )
1521 {
1522 if( *p >= '0' && *p <= '9' )
1523 {
1524 dfVal = dfVal * 10.0 + (*p - '0');
1525 ++p;
1526 }
1527 else if( *p == '.' )
1528 {
1529 ++p;
1530 break;
1531 }
1532 else if( *p == 'e' || *p == 'E' || *p == 'd' || *p == 'D' )
1533 return OGRCallAtofOnShortString(pszStr);
1534 else
1535 return dfSign * dfVal;
1536 }
1537
1538 unsigned int countFractionnal = 0;
1539 while( true )
1540 {
1541 if( *p >= '0' && *p <= '9' )
1542 {
1543 dfVal = dfVal * 10.0 + (*p - '0');
1544 ++countFractionnal;
1545 ++p;
1546 }
1547 else if( *p == 'e' || *p == 'E' || *p == 'd' || *p == 'D' )
1548 return OGRCallAtofOnShortString(pszStr);
1549 else
1550 {
1551 if( countFractionnal < CPL_ARRAYSIZE(adfTenPower) )
1552 return dfSign * (dfVal / adfTenPower[countFractionnal]);
1553 else
1554 return OGRCallAtofOnShortString(pszStr);
1555 }
1556 }
1557 }
1558
1559 /**
1560 * Check that panPermutation is a permutation of [0, nSize-1].
1561 * @param panPermutation an array of nSize elements.
1562 * @param nSize size of the array.
1563 * @return OGRERR_NONE if panPermutation is a permutation of [0, nSize - 1].
1564 * @since OGR 1.9.0
1565 */
OGRCheckPermutation(int * panPermutation,int nSize)1566 OGRErr OGRCheckPermutation( int* panPermutation, int nSize )
1567 {
1568 OGRErr eErr = OGRERR_NONE;
1569 int* panCheck = static_cast<int *>(CPLCalloc(nSize, sizeof(int)));
1570 for( int i = 0; i < nSize; ++i )
1571 {
1572 if( panPermutation[i] < 0 || panPermutation[i] >= nSize )
1573 {
1574 CPLError(CE_Failure, CPLE_IllegalArg,
1575 "Bad value for element %d", i);
1576 eErr = OGRERR_FAILURE;
1577 break;
1578 }
1579 if( panCheck[panPermutation[i]] != 0 )
1580 {
1581 CPLError(CE_Failure, CPLE_IllegalArg,
1582 "Array is not a permutation of [0,%d]",
1583 nSize - 1);
1584 eErr = OGRERR_FAILURE;
1585 break;
1586 }
1587 panCheck[panPermutation[i]] = 1;
1588 }
1589 CPLFree(panCheck);
1590 return eErr;
1591 }
1592
OGRReadWKBGeometryType(const unsigned char * pabyData,OGRwkbVariant eWkbVariant,OGRwkbGeometryType * peGeometryType)1593 OGRErr OGRReadWKBGeometryType( const unsigned char * pabyData,
1594 OGRwkbVariant eWkbVariant,
1595 OGRwkbGeometryType *peGeometryType )
1596 {
1597 if( !peGeometryType )
1598 return OGRERR_FAILURE;
1599
1600 /* -------------------------------------------------------------------- */
1601 /* Get the byte order byte. */
1602 /* -------------------------------------------------------------------- */
1603 int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
1604 if( !( nByteOrder == wkbXDR || nByteOrder == wkbNDR ) )
1605 return OGRERR_CORRUPT_DATA;
1606 OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
1607
1608 /* -------------------------------------------------------------------- */
1609 /* Get the geometry type. */
1610 /* -------------------------------------------------------------------- */
1611 bool bIs3D = false;
1612 bool bIsMeasured = false;
1613 int iRawType = 0;
1614
1615 memcpy(&iRawType, pabyData + 1, 4);
1616 if( OGR_SWAP(eByteOrder))
1617 {
1618 CPL_SWAP32PTR(&iRawType);
1619 }
1620
1621 // Test for M bit in PostGIS WKB, see ogrgeometry.cpp:4956.
1622 if( 0x40000000 & iRawType )
1623 {
1624 iRawType &= ~0x40000000;
1625 bIsMeasured = true;
1626 }
1627 // Old-style OGC z-bit is flipped? Tests also Z bit in PostGIS WKB.
1628 if( wkb25DBitInternalUse & iRawType )
1629 {
1630 // Clean off top 3 bytes.
1631 iRawType &= 0x000000FF;
1632 bIs3D = true;
1633 }
1634
1635 // ISO SQL/MM Part3 draft -> Deprecated.
1636 // See http://jtc1sc32.org/doc/N1101-1150/32N1107-WD13249-3--spatial.pdf
1637 if( iRawType == 1000001 )
1638 iRawType = wkbCircularString;
1639 else if( iRawType == 1000002 )
1640 iRawType = wkbCompoundCurve;
1641 else if( iRawType == 1000003 )
1642 iRawType = wkbCurvePolygon;
1643 else if( iRawType == 1000004 )
1644 iRawType = wkbMultiCurve;
1645 else if( iRawType == 1000005 )
1646 iRawType = wkbMultiSurface;
1647 else if( iRawType == 2000001 )
1648 iRawType = wkbPointZM;
1649 else if( iRawType == 2000002 )
1650 iRawType = wkbLineStringZM;
1651 else if( iRawType == 2000003 )
1652 iRawType = wkbCircularStringZM;
1653 else if( iRawType == 2000004 )
1654 iRawType = wkbCompoundCurveZM;
1655 else if( iRawType == 2000005 )
1656 iRawType = wkbPolygonZM;
1657 else if( iRawType == 2000006 )
1658 iRawType = wkbCurvePolygonZM;
1659 else if( iRawType == 2000007 )
1660 iRawType = wkbMultiPointZM;
1661 else if( iRawType == 2000008 )
1662 iRawType = wkbMultiCurveZM;
1663 else if( iRawType == 2000009 )
1664 iRawType = wkbMultiLineStringZM;
1665 else if( iRawType == 2000010 )
1666 iRawType = wkbMultiSurfaceZM;
1667 else if( iRawType == 2000011 )
1668 iRawType = wkbMultiPolygonZM;
1669 else if( iRawType == 2000012 )
1670 iRawType = wkbGeometryCollectionZM;
1671 else if( iRawType == 3000001 )
1672 iRawType = wkbPoint25D;
1673 else if( iRawType == 3000002 )
1674 iRawType = wkbLineString25D;
1675 else if( iRawType == 3000003 )
1676 iRawType = wkbCircularStringZ;
1677 else if( iRawType == 3000004 )
1678 iRawType = wkbCompoundCurveZ;
1679 else if( iRawType == 3000005 )
1680 iRawType = wkbPolygon25D;
1681 else if( iRawType == 3000006 )
1682 iRawType = wkbCurvePolygonZ;
1683 else if( iRawType == 3000007 )
1684 iRawType = wkbMultiPoint25D;
1685 else if( iRawType == 3000008 )
1686 iRawType = wkbMultiCurveZ;
1687 else if( iRawType == 3000009 )
1688 iRawType = wkbMultiLineString25D;
1689 else if( iRawType == 3000010 )
1690 iRawType = wkbMultiSurfaceZ;
1691 else if( iRawType == 3000011 )
1692 iRawType = wkbMultiPolygon25D;
1693 else if( iRawType == 3000012 )
1694 iRawType = wkbGeometryCollection25D;
1695 else if( iRawType == 4000001 )
1696 iRawType = wkbPointM;
1697 else if( iRawType == 4000002 )
1698 iRawType = wkbLineStringM;
1699 else if( iRawType == 4000003 )
1700 iRawType = wkbCircularStringM;
1701 else if( iRawType == 4000004 )
1702 iRawType = wkbCompoundCurveM;
1703 else if( iRawType == 4000005 )
1704 iRawType = wkbPolygonM;
1705 else if( iRawType == 4000006 )
1706 iRawType = wkbCurvePolygonM;
1707 else if( iRawType == 4000007 )
1708 iRawType = wkbMultiPointM;
1709 else if( iRawType == 4000008 )
1710 iRawType = wkbMultiCurveM;
1711 else if( iRawType == 4000009 )
1712 iRawType = wkbMultiLineStringM;
1713 else if( iRawType == 4000010 )
1714 iRawType = wkbMultiSurfaceM;
1715 else if( iRawType == 4000011 )
1716 iRawType = wkbMultiPolygonM;
1717 else if( iRawType == 4000012 )
1718 iRawType = wkbGeometryCollectionM;
1719
1720 // Sometimes the Z flag is in the 2nd byte?
1721 if( iRawType & (wkb25DBitInternalUse >> 16) )
1722 {
1723 // Clean off top 3 bytes.
1724 iRawType &= 0x000000FF;
1725 bIs3D = true;
1726 }
1727
1728 if( eWkbVariant == wkbVariantPostGIS1 )
1729 {
1730 if( iRawType == POSTGIS15_CURVEPOLYGON )
1731 iRawType = wkbCurvePolygon;
1732 else if( iRawType == POSTGIS15_MULTICURVE )
1733 iRawType = wkbMultiCurve;
1734 else if( iRawType == POSTGIS15_MULTISURFACE )
1735 iRawType = wkbMultiSurface;
1736 }
1737
1738 if( bIs3D )
1739 {
1740 iRawType += 1000;
1741 }
1742 if( bIsMeasured )
1743 {
1744 iRawType += 2000;
1745 }
1746
1747 // ISO SQL/MM style types are between 1-17, 1001-1017, 2001-2017, and
1748 // 3001-3017.
1749 if( !((iRawType > 0 && iRawType <= 17) ||
1750 (iRawType > 1000 && iRawType <= 1017) ||
1751 (iRawType > 2000 && iRawType <= 2017) ||
1752 (iRawType > 3000 && iRawType <= 3017)) )
1753 {
1754 CPLError(CE_Failure, CPLE_NotSupported,
1755 "Unsupported WKB type %d", iRawType);
1756 return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
1757 }
1758
1759 // Convert to OGRwkbGeometryType value.
1760 if( iRawType >= 1001 && iRawType <= 1007 )
1761 {
1762 iRawType -= 1000;
1763 iRawType |= wkb25DBitInternalUse;
1764 }
1765
1766 *peGeometryType = static_cast<OGRwkbGeometryType>(iRawType);
1767
1768 return OGRERR_NONE;
1769 }
1770
1771 /************************************************************************/
1772 /* OGRFormatFloat() */
1773 /************************************************************************/
1774
OGRFormatFloat(char * pszBuffer,int nBufferLen,float fVal,int nPrecision,char chConversionSpecifier)1775 int OGRFormatFloat(char *pszBuffer, int nBufferLen,
1776 float fVal, int nPrecision, char chConversionSpecifier)
1777 {
1778 int nSize = 0;
1779 char szFormatting[32] = {};
1780 constexpr int MAX_SIGNIFICANT_DIGITS_FLOAT32 = 8;
1781 const int nInitialSignificantFigures =
1782 nPrecision >= 0 ? nPrecision : MAX_SIGNIFICANT_DIGITS_FLOAT32;
1783
1784 CPLsnprintf(szFormatting, sizeof(szFormatting),
1785 "%%.%d%c",
1786 nInitialSignificantFigures,
1787 chConversionSpecifier);
1788 nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting, fVal);
1789 const char* pszDot = strchr(pszBuffer, '.');
1790
1791 // Try to avoid 0.34999999 or 0.15000001 rounding issues by
1792 // decreasing a bit precision.
1793 if( nInitialSignificantFigures >= 8 &&
1794 pszDot != nullptr &&
1795 (strstr(pszDot, "99999") != nullptr ||
1796 strstr(pszDot, "00000") != nullptr) )
1797 {
1798 const CPLString osOriBuffer(pszBuffer, nSize);
1799
1800 bool bOK = false;
1801 for( int i = 1; i <= 3; i++ )
1802 {
1803 CPLsnprintf(szFormatting, sizeof(szFormatting),
1804 "%%.%d%c",
1805 nInitialSignificantFigures - i,
1806 chConversionSpecifier);
1807 nSize = CPLsnprintf(pszBuffer, nBufferLen,
1808 szFormatting, fVal);
1809 pszDot = strchr(pszBuffer, '.');
1810 if( pszDot != nullptr &&
1811 strstr(pszDot, "99999") == nullptr &&
1812 strstr(pszDot, "00000") == nullptr &&
1813 static_cast<float>(CPLAtof(pszBuffer)) == fVal )
1814 {
1815 bOK = true;
1816 break;
1817 }
1818 }
1819 if( !bOK )
1820 {
1821 memcpy(pszBuffer, osOriBuffer.c_str(), osOriBuffer.size()+1);
1822 nSize = static_cast<int>(osOriBuffer.size());
1823 }
1824 }
1825
1826 if( nSize+2 < static_cast<int>(nBufferLen) &&
1827 strchr(pszBuffer, '.') == nullptr &&
1828 strchr(pszBuffer, 'e') == nullptr )
1829 {
1830 nSize += CPLsnprintf(pszBuffer + nSize, nBufferLen - nSize, ".0");
1831 }
1832
1833 return nSize;
1834 }
1835