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