LCOV - code coverage report
Current view: top level - ogr - ogrutils.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 968 1150 84.2 %
Date: 2025-01-18 12:42:00 Functions: 35 44 79.5 %

          Line data    Source code
       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             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_port.h"
      16             : #include "ogr_geometry.h"
      17             : #include "ogr_p.h"
      18             : 
      19             : #include <cassert>
      20             : #include <cmath>
      21             : #include <cstddef>
      22             : #include <cstdio>
      23             : #include <cstdlib>
      24             : #include <cstring>
      25             : #include <cctype>
      26             : #include <limits>
      27             : #include <sstream>
      28             : #include <iomanip>
      29             : 
      30             : #include "cpl_conv.h"
      31             : #include "cpl_error.h"
      32             : #include "cpl_string.h"
      33             : #include "cpl_time.h"
      34             : #include "cpl_vsi.h"
      35             : #include "gdal.h"
      36             : #include "ogr_core.h"
      37             : #include "ogr_geometry.h"
      38             : #include "ogrsf_frmts.h"
      39             : 
      40             : // Returns whether a double fits within an int.
      41             : // Unable to put this in cpl_port.h as include limit breaks grib.
      42       66749 : inline bool CPLIsDoubleAnInt(double d)
      43             : {
      44             :     // Write it this way to detect NaN
      45      133477 :     if (!(d >= std::numeric_limits<int>::min() &&
      46       66728 :           d <= std::numeric_limits<int>::max()))
      47             :     {
      48          22 :         return false;
      49             :     }
      50       66727 :     return d == static_cast<double>(static_cast<int>(d));
      51             : }
      52             : 
      53             : namespace
      54             : {
      55             : 
      56             : // Remove trailing zeros except the last one.
      57       71855 : void removeTrailingZeros(std::string &s)
      58             : {
      59       71855 :     auto pos = s.find('.');
      60       71855 :     if (pos == std::string::npos)
      61        7309 :         return;
      62             : 
      63             :     // Remove zeros at the end.  We know this won't be npos because we
      64             :     // have a decimal point.
      65       64546 :     auto nzpos = s.find_last_not_of('0');
      66       64546 :     s = s.substr(0, nzpos + 1);
      67             : 
      68             :     // Make sure there is one 0 after the decimal point.
      69       64546 :     if (s.back() == '.')
      70       17337 :         s += '0';
      71             : }
      72             : 
      73             : // Round a string representing a number by 1 in the least significant digit.
      74          33 : void roundup(std::string &s)
      75             : {
      76             :     // Remove a negative sign if it exists to make processing
      77             :     // more straigtforward.
      78          33 :     bool negative(false);
      79          33 :     if (s[0] == '-')
      80             :     {
      81           9 :         negative = true;
      82           9 :         s = s.substr(1);
      83             :     }
      84             : 
      85             :     // Go from the back to the front.  If we increment a digit other than
      86             :     // a '9', we're done.  If we increment a '9', set it to a '0' and move
      87             :     // to the next (more significant) digit.  If we get to the front of the
      88             :     // string, add a '1' to the front of the string.
      89         210 :     for (int pos = static_cast<int>(s.size() - 1); pos >= 0; pos--)
      90             :     {
      91         210 :         if (s[pos] == '.')
      92           3 :             continue;
      93         207 :         s[pos]++;
      94             : 
      95             :         // Incrementing past 9 gets you a colon in ASCII.
      96         207 :         if (s[pos] != ':')
      97          33 :             break;
      98             :         else
      99         174 :             s[pos] = '0';
     100         174 :         if (pos == 0)
     101           0 :             s = '1' + s;
     102             :     }
     103          33 :     if (negative)
     104           9 :         s = '-' + s;
     105          33 : }
     106             : 
     107             : // This attempts to eliminate what is likely binary -> decimal representation
     108             : // error or the result of low-order rounding with calculations.  The result
     109             : // may be more visually pleasing and takes up fewer places.
     110       26491 : void intelliround(std::string &s)
     111             : {
     112       26491 :     const size_t len = s.size();
     113             : 
     114             :     // If we don't have ten characters (a bit arbitrary), don't do anything.
     115       26491 :     constexpr size_t MIN_THRESHOLD_FOR_INELLIROUND = 10;
     116       26491 :     if (len <= MIN_THRESHOLD_FOR_INELLIROUND)
     117        2644 :         return;
     118             : 
     119             :     // If there is no decimal point, just return.
     120       23847 :     size_t iDotPos = std::string::npos;
     121       23847 :     size_t i = 0;
     122      148660 :     for (; i < len; ++i)
     123             :     {
     124      148660 :         if (s[i] == '.')
     125             :         {
     126       23847 :             iDotPos = i;
     127       23847 :             break;
     128             :         }
     129             :     }
     130       23847 :     if (iDotPos == std::string::npos)
     131           0 :         return;
     132      396068 :     for (; i < len; ++i)
     133             :     {
     134      372221 :         if (s[i] == 'e' || s[i] == 'E')
     135             :         {
     136             :             // Don't mess with exponential formatting.
     137           0 :             return;
     138             :         }
     139             :     }
     140             : 
     141       23847 :     size_t nCountBeforeDot = iDotPos - 1;
     142       23847 :     if (s[0] == '-')
     143        1577 :         nCountBeforeDot--;
     144             : 
     145             :     /* -------------------------------------------------------------------- */
     146             :     /*      Trim trailing 00000x's as they are likely roundoff error.       */
     147             :     /* -------------------------------------------------------------------- */
     148       44744 :     if (s[len - 2] == '0' && s[len - 3] == '0' && s[len - 4] == '0' &&
     149       44744 :         s[len - 5] == '0' && s[len - 6] == '0')
     150             :     {
     151       20301 :         s.pop_back();
     152             :     }
     153             :     // I don't understand this case exactly.  It's like saying if the
     154             :     // value is large enough and there are sufficient sig digits before
     155             :     // a bunch of zeros, remove the zeros and any digits at the end that
     156             :     // may be nonzero.  Perhaps if we can't exactly explain in words what
     157             :     // we're doing here, we shouldn't do it?  Perhaps it should
     158             :     // be generalized?
     159             :     // The value "12345.000000011" invokes this case, if anyone
     160             :     // is interested.
     161        2755 :     else if (iDotPos < len - 8 && (nCountBeforeDot >= 4 || s[len - 3] == '0') &&
     162         429 :              (nCountBeforeDot >= 5 || s[len - 4] == '0') &&
     163         247 :              (nCountBeforeDot >= 6 || s[len - 5] == '0') &&
     164         208 :              (nCountBeforeDot >= 7 || s[len - 6] == '0') &&
     165        6495 :              (nCountBeforeDot >= 8 || s[len - 7] == '0') && s[len - 8] == '0' &&
     166         194 :              s[len - 9] == '0')
     167             :     {
     168         194 :         s.resize(s.size() - 8);
     169             :     }
     170             :     /* -------------------------------------------------------------------- */
     171             :     /*      Trim trailing 99999x's as they are likely roundoff error.       */
     172             :     /* -------------------------------------------------------------------- */
     173        3704 :     else if (s[len - 2] == '9' && s[len - 3] == '9' && s[len - 4] == '9' &&
     174        3704 :              s[len - 5] == '9' && s[len - 6] == '9')
     175             :     {
     176          23 :         s.resize(len - 6);
     177          23 :         roundup(s);
     178             :     }
     179        2534 :     else if (iDotPos < len - 9 && (nCountBeforeDot >= 4 || s[len - 3] == '9') &&
     180         284 :              (nCountBeforeDot >= 5 || s[len - 4] == '9') &&
     181          64 :              (nCountBeforeDot >= 6 || s[len - 5] == '9') &&
     182          17 :              (nCountBeforeDot >= 7 || s[len - 6] == '9') &&
     183        5873 :              (nCountBeforeDot >= 8 || s[len - 7] == '9') && s[len - 8] == '9' &&
     184          10 :              s[len - 9] == '9')
     185             :     {
     186          10 :         s.resize(len - 9);
     187          10 :         roundup(s);
     188             :     }
     189             : }
     190             : 
     191             : }  // unnamed namespace
     192             : 
     193             : /************************************************************************/
     194             : /*                        OGRFormatDouble()                             */
     195             : /************************************************************************/
     196             : 
     197          96 : void OGRFormatDouble(char *pszBuffer, int nBufferLen, double dfVal,
     198             :                      char chDecimalSep, int nPrecision,
     199             :                      char chConversionSpecifier)
     200             : {
     201          96 :     OGRWktOptions opts(nPrecision, OGRWktOptions::getDefaultRound());
     202          96 :     opts.format = (chConversionSpecifier == 'g' || chConversionSpecifier == 'G')
     203         192 :                       ? OGRWktFormat::G
     204             :                       : OGRWktFormat::F;
     205             : 
     206          96 :     std::string s = OGRFormatDouble(dfVal, opts, 1);
     207          96 :     if (chDecimalSep != '\0' && chDecimalSep != '.')
     208             :     {
     209           0 :         auto pos = s.find('.');
     210           0 :         if (pos != std::string::npos)
     211           0 :             s.replace(pos, 1, std::string(1, chDecimalSep));
     212             :     }
     213          96 :     if (s.size() + 1 > static_cast<size_t>(nBufferLen))
     214             :     {
     215           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     216             :                  "Truncated double value %s to "
     217             :                  "%s.",
     218           0 :                  s.data(), s.substr(0, nBufferLen - 1).data());
     219           0 :         s.resize(nBufferLen - 1);
     220             :     }
     221          96 :     strcpy(pszBuffer, s.data());
     222          96 : }
     223             : 
     224             : /// Simplified OGRFormatDouble that can be made to adhere to provided
     225             : /// options.
     226       71886 : std::string OGRFormatDouble(double val, const OGRWktOptions &opts, int nDimIdx)
     227             : {
     228             :     // So to have identical cross platform representation.
     229       71886 :     if (std::isinf(val))
     230           2 :         return (val > 0) ? "inf" : "-inf";
     231       71884 :     if (std::isnan(val))
     232          29 :         return "nan";
     233             : 
     234          86 :     static thread_local std::locale classic_locale = []()
     235       71941 :     { return std::locale::classic(); }();
     236      143710 :     std::ostringstream oss;
     237       71855 :     oss.imbue(classic_locale);  // Make sure we output decimal points.
     238       71855 :     bool l_round(opts.round);
     239       71855 :     if (opts.format == OGRWktFormat::F ||
     240       48206 :         (opts.format == OGRWktFormat::Default && fabs(val) < 1))
     241       26491 :         oss << std::fixed;
     242             :     else
     243             :     {
     244             :         // Uppercase because OGC spec says capital 'E'.
     245       45364 :         oss << std::uppercase;
     246       45364 :         l_round = false;
     247             :     }
     248         257 :     oss << std::setprecision(nDimIdx < 3    ? opts.xyPrecision
     249             :                              : nDimIdx == 3 ? opts.zPrecision
     250       72112 :                                             : opts.mPrecision);
     251       71855 :     oss << val;
     252             : 
     253      143710 :     std::string sval = oss.str();
     254             : 
     255       71855 :     if (l_round)
     256       26491 :         intelliround(sval);
     257       71855 :     removeTrailingZeros(sval);
     258       71855 :     return sval;
     259             : }
     260             : 
     261             : /************************************************************************/
     262             : /*                        OGRMakeWktCoordinate()                        */
     263             : /*                                                                      */
     264             : /*      Format a well known text coordinate, trying to keep the         */
     265             : /*      ASCII representation compact, but accurate.  These rules        */
     266             : /*      will have to tighten up in the future.                          */
     267             : /*                                                                      */
     268             : /*      Currently a new point should require no more than 64            */
     269             : /*      characters barring the X or Y value being extremely large.      */
     270             : /************************************************************************/
     271             : 
     272         961 : void OGRMakeWktCoordinate(char *pszTarget, double x, double y, double z,
     273             :                           int nDimension)
     274             : 
     275             : {
     276             :     std::string wkt =
     277         961 :         OGRMakeWktCoordinate(x, y, z, nDimension, OGRWktOptions());
     278         961 :     memcpy(pszTarget, wkt.data(), wkt.size() + 1);
     279         961 : }
     280             : 
     281       48028 : static bool isInteger(const std::string &s)
     282             : {
     283       48028 :     return s.find_first_not_of("0123456789") == std::string::npos;
     284             : }
     285             : 
     286        3948 : std::string OGRMakeWktCoordinate(double x, double y, double z, int nDimension,
     287             :                                  const OGRWktOptions &opts)
     288             : {
     289        3948 :     std::string wkt;
     290             : 
     291             :     // Why do we do this?  Seems especially strange since we're ADDING
     292             :     // ".0" onto values in the case below.  The "&&" here also seems strange.
     293        5702 :     if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
     294        1754 :         CPLIsDoubleAnInt(y))
     295             :     {
     296        1648 :         wkt = std::to_string(static_cast<int>(x));
     297        1648 :         wkt += ' ';
     298        1648 :         wkt += std::to_string(static_cast<int>(y));
     299             :     }
     300             :     else
     301             :     {
     302        2300 :         wkt = OGRFormatDouble(x, opts, 1);
     303             :         // ABELL - Why do we do special formatting?
     304        2300 :         if (isInteger(wkt))
     305         101 :             wkt += ".0";
     306        2300 :         wkt += ' ';
     307             : 
     308        4600 :         std::string yval = OGRFormatDouble(y, opts, 2);
     309        2300 :         if (isInteger(yval))
     310         963 :             yval += ".0";
     311        2300 :         wkt += yval;
     312             :     }
     313             : 
     314        3948 :     if (nDimension == 3)
     315             :     {
     316         916 :         wkt += ' ';
     317         916 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
     318         905 :             wkt += std::to_string(static_cast<int>(z));
     319             :         else
     320             :         {
     321          11 :             wkt += OGRFormatDouble(z, opts, 3);
     322             :         }
     323             :     }
     324        3948 :     return wkt;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                        OGRMakeWktCoordinateM()                       */
     329             : /*                                                                      */
     330             : /*      Format a well known text coordinate, trying to keep the         */
     331             : /*      ASCII representation compact, but accurate.  These rules        */
     332             : /*      will have to tighten up in the future.                          */
     333             : /*                                                                      */
     334             : /*      Currently a new point should require no more than 64            */
     335             : /*      characters barring the X or Y value being extremely large.      */
     336             : /************************************************************************/
     337             : 
     338           0 : void OGRMakeWktCoordinateM(char *pszTarget, double x, double y, double z,
     339             :                            double m, OGRBoolean hasZ, OGRBoolean hasM)
     340             : 
     341             : {
     342             :     std::string wkt =
     343           0 :         OGRMakeWktCoordinateM(x, y, z, m, hasZ, hasM, OGRWktOptions());
     344           0 :     memcpy(pszTarget, wkt.data(), wkt.size() + 1);
     345           0 : }
     346             : 
     347       37804 : std::string OGRMakeWktCoordinateM(double x, double y, double z, double m,
     348             :                                   OGRBoolean hasZ, OGRBoolean hasM,
     349             :                                   const OGRWktOptions &opts)
     350             : {
     351       37804 :     std::string wkt;
     352       54533 :     if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
     353       16729 :         CPLIsDoubleAnInt(y))
     354             :     {
     355       16090 :         wkt = std::to_string(static_cast<int>(x));
     356       16090 :         wkt += ' ';
     357       16090 :         wkt += std::to_string(static_cast<int>(y));
     358             :     }
     359             :     else
     360             :     {
     361       21714 :         wkt = OGRFormatDouble(x, opts, 1);
     362       21714 :         if (isInteger(wkt))
     363         612 :             wkt += ".0";
     364       21714 :         wkt += ' ';
     365             : 
     366       43428 :         std::string yval = OGRFormatDouble(y, opts, 2);
     367       21714 :         if (isInteger(yval))
     368        5604 :             yval += ".0";
     369       21714 :         wkt += yval;
     370             :     }
     371             : 
     372       37804 :     if (hasZ)
     373             :     {
     374        3851 :         wkt += ' ';
     375        3851 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
     376        3600 :             wkt += std::to_string(static_cast<int>(z));
     377             :         else
     378         251 :             wkt += OGRFormatDouble(z, opts, 3);
     379             :     }
     380             : 
     381       37804 :     if (hasM)
     382             :     {
     383        1783 :         wkt += ' ';
     384        1783 :         if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(m))
     385        1777 :             wkt += std::to_string(static_cast<int>(m));
     386             :         else
     387           6 :             wkt += OGRFormatDouble(m, opts, 4);
     388             :     }
     389       37804 :     return wkt;
     390             : }
     391             : 
     392             : /************************************************************************/
     393             : /*                          OGRWktReadToken()                           */
     394             : /*                                                                      */
     395             : /*      Read one token or delimiter and put into token buffer.  Pre     */
     396             : /*      and post white space is swallowed.                              */
     397             : /************************************************************************/
     398             : 
     399     1431350 : const char *OGRWktReadToken(const char *pszInput, char *pszToken)
     400             : 
     401             : {
     402     1431350 :     if (pszInput == nullptr)
     403           0 :         return nullptr;
     404             : 
     405             :     /* -------------------------------------------------------------------- */
     406             :     /*      Swallow pre-white space.                                        */
     407             :     /* -------------------------------------------------------------------- */
     408     1431350 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
     409     1431350 :            *pszInput == '\r')
     410           3 :         ++pszInput;
     411             : 
     412             :     /* -------------------------------------------------------------------- */
     413             :     /*      If this is a delimiter, read just one character.                */
     414             :     /* -------------------------------------------------------------------- */
     415     1431350 :     if (*pszInput == '(' || *pszInput == ')' || *pszInput == ',')
     416             :     {
     417      555779 :         pszToken[0] = *pszInput;
     418      555779 :         pszToken[1] = '\0';
     419             : 
     420      555779 :         ++pszInput;
     421             :     }
     422             : 
     423             :     /* -------------------------------------------------------------------- */
     424             :     /*      Or if it alpha numeric read till we reach non-alpha numeric     */
     425             :     /*      text.                                                           */
     426             :     /* -------------------------------------------------------------------- */
     427             :     else
     428             :     {
     429      875570 :         int iChar = 0;
     430             : 
     431     6426730 :         while (iChar < OGR_WKT_TOKEN_MAX - 1 &&
     432     6426730 :                ((*pszInput >= 'a' && *pszInput <= 'z') ||
     433     6422320 :                 (*pszInput >= 'A' && *pszInput <= 'Z') ||
     434     4940410 :                 (*pszInput >= '0' && *pszInput <= '9') || *pszInput == '.' ||
     435     1025240 :                 *pszInput == '+' || *pszInput == '-'))
     436             :         {
     437     5551160 :             pszToken[iChar++] = *(pszInput++);
     438             :         }
     439             : 
     440      875570 :         pszToken[iChar++] = '\0';
     441             :     }
     442             : 
     443             :     /* -------------------------------------------------------------------- */
     444             :     /*      Eat any trailing white space.                                   */
     445             :     /* -------------------------------------------------------------------- */
     446     1868360 :     while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
     447     1431350 :            *pszInput == '\r')
     448      437009 :         ++pszInput;
     449             : 
     450     1431350 :     return pszInput;
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*                          OGRWktReadPoints()                          */
     455             : /*                                                                      */
     456             : /*      Read a point string.  The point list must be contained in       */
     457             : /*      brackets and each point pair separated by a comma.              */
     458             : /************************************************************************/
     459             : 
     460           0 : const char *OGRWktReadPoints(const char *pszInput, OGRRawPoint **ppaoPoints,
     461             :                              double **ppadfZ, int *pnMaxPoints,
     462             :                              int *pnPointsRead)
     463             : 
     464             : {
     465           0 :     const char *pszOrigInput = pszInput;
     466           0 :     *pnPointsRead = 0;
     467             : 
     468           0 :     if (pszInput == nullptr)
     469           0 :         return nullptr;
     470             : 
     471             :     /* -------------------------------------------------------------------- */
     472             :     /*      Eat any leading white space.                                    */
     473             :     /* -------------------------------------------------------------------- */
     474           0 :     while (*pszInput == ' ' || *pszInput == '\t')
     475           0 :         ++pszInput;
     476             : 
     477             :     /* -------------------------------------------------------------------- */
     478             :     /*      If this isn't an opening bracket then we have a problem.        */
     479             :     /* -------------------------------------------------------------------- */
     480           0 :     if (*pszInput != '(')
     481             :     {
     482           0 :         CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPoints().",
     483             :                  pszInput);
     484             : 
     485           0 :         return pszInput;
     486             :     }
     487             : 
     488           0 :     ++pszInput;
     489             : 
     490             :     /* ==================================================================== */
     491             :     /*      This loop reads a single point.  It will continue till we       */
     492             :     /*      run out of well formed points, or a closing bracket is          */
     493             :     /*      encountered.                                                    */
     494             :     /* ==================================================================== */
     495           0 :     char szDelim[OGR_WKT_TOKEN_MAX] = {};
     496             : 
     497           0 :     do
     498             :     {
     499             :         /* --------------------------------------------------------------------
     500             :          */
     501             :         /*      Read the X and Y values, verify they are numeric. */
     502             :         /* --------------------------------------------------------------------
     503             :          */
     504           0 :         char szTokenX[OGR_WKT_TOKEN_MAX] = {};
     505           0 :         char szTokenY[OGR_WKT_TOKEN_MAX] = {};
     506             : 
     507           0 :         pszInput = OGRWktReadToken(pszInput, szTokenX);
     508           0 :         pszInput = OGRWktReadToken(pszInput, szTokenY);
     509             : 
     510           0 :         if ((!isdigit(static_cast<unsigned char>(szTokenX[0])) &&
     511           0 :              szTokenX[0] != '-' && szTokenX[0] != '.') ||
     512           0 :             (!isdigit(static_cast<unsigned char>(szTokenY[0])) &&
     513           0 :              szTokenY[0] != '-' && szTokenY[0] != '.'))
     514           0 :             return nullptr;
     515             : 
     516             :         /* --------------------------------------------------------------------
     517             :          */
     518             :         /*      Do we need to grow the point list to hold this point? */
     519             :         /* --------------------------------------------------------------------
     520             :          */
     521           0 :         if (*pnPointsRead == *pnMaxPoints)
     522             :         {
     523           0 :             *pnMaxPoints = *pnMaxPoints * 2 + 10;
     524           0 :             *ppaoPoints = static_cast<OGRRawPoint *>(
     525           0 :                 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints));
     526             : 
     527           0 :             if (*ppadfZ != nullptr)
     528             :             {
     529           0 :                 *ppadfZ = static_cast<double *>(
     530           0 :                     CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
     531             :             }
     532             :         }
     533             : 
     534             :         /* --------------------------------------------------------------------
     535             :          */
     536             :         /*      Add point to list. */
     537             :         /* --------------------------------------------------------------------
     538             :          */
     539           0 :         (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
     540           0 :         (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
     541             : 
     542             :         /* --------------------------------------------------------------------
     543             :          */
     544             :         /*      Do we have a Z coordinate? */
     545             :         /* --------------------------------------------------------------------
     546             :          */
     547           0 :         pszInput = OGRWktReadToken(pszInput, szDelim);
     548             : 
     549           0 :         if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     550           0 :             szDelim[0] == '-' || szDelim[0] == '.')
     551             :         {
     552           0 :             if (*ppadfZ == nullptr)
     553             :             {
     554           0 :                 *ppadfZ = static_cast<double *>(
     555           0 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     556             :             }
     557             : 
     558           0 :             (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
     559             : 
     560           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     561             :         }
     562           0 :         else if (*ppadfZ != nullptr)
     563             :         {
     564           0 :             (*ppadfZ)[*pnPointsRead] = 0.0;
     565             :         }
     566             : 
     567           0 :         ++(*pnPointsRead);
     568             : 
     569             :         /* --------------------------------------------------------------------
     570             :          */
     571             :         /*      Do we have a M coordinate? */
     572             :         /*      If we do, just skip it. */
     573             :         /* --------------------------------------------------------------------
     574             :          */
     575           0 :         if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     576           0 :             szDelim[0] == '-' || szDelim[0] == '.')
     577             :         {
     578           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     579             :         }
     580             : 
     581             :         /* --------------------------------------------------------------------
     582             :          */
     583             :         /*      Read next delimiter ... it should be a comma if there are */
     584             :         /*      more points. */
     585             :         /* --------------------------------------------------------------------
     586             :          */
     587           0 :         if (szDelim[0] != ')' && szDelim[0] != ',')
     588             :         {
     589           0 :             CPLDebug("OGR",
     590             :                      "Corrupt input in OGRWktReadPoints().  "
     591             :                      "Got `%s' when expecting `,' or `)', near `%s' in %s.",
     592             :                      szDelim, pszInput, pszOrigInput);
     593           0 :             return nullptr;
     594             :         }
     595           0 :     } while (szDelim[0] == ',');
     596             : 
     597           0 :     return pszInput;
     598             : }
     599             : 
     600             : /************************************************************************/
     601             : /*                          OGRWktReadPointsM()                         */
     602             : /*                                                                      */
     603             : /*      Read a point string.  The point list must be contained in       */
     604             : /*      brackets and each point pair separated by a comma.              */
     605             : /************************************************************************/
     606             : 
     607      122040 : const char *OGRWktReadPointsM(const char *pszInput, OGRRawPoint **ppaoPoints,
     608             :                               double **ppadfZ, double **ppadfM, int *flags,
     609             :                               int *pnMaxPoints, int *pnPointsRead)
     610             : 
     611             : {
     612      122040 :     const char *pszOrigInput = pszInput;
     613      242477 :     const bool bNoFlags = !(*flags & OGRGeometry::OGR_G_3D) &&
     614      120437 :                           !(*flags & OGRGeometry::OGR_G_MEASURED);
     615      122040 :     *pnPointsRead = 0;
     616             : 
     617      122040 :     if (pszInput == nullptr)
     618           0 :         return nullptr;
     619             : 
     620             :     /* -------------------------------------------------------------------- */
     621             :     /*      Eat any leading white space.                                    */
     622             :     /* -------------------------------------------------------------------- */
     623      122040 :     while (*pszInput == ' ' || *pszInput == '\t')
     624           0 :         ++pszInput;
     625             : 
     626             :     /* -------------------------------------------------------------------- */
     627             :     /*      If this isn't an opening bracket then we have a problem.        */
     628             :     /* -------------------------------------------------------------------- */
     629      122040 :     if (*pszInput != '(')
     630             :     {
     631          26 :         CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPointsM().",
     632             :                  pszInput);
     633             : 
     634          26 :         return pszInput;
     635             :     }
     636             : 
     637      122014 :     ++pszInput;
     638             : 
     639             :     /* ==================================================================== */
     640             :     /*      This loop reads a single point.  It will continue till we       */
     641             :     /*      run out of well formed points, or a closing bracket is          */
     642             :     /*      encountered.                                                    */
     643             :     /* ==================================================================== */
     644      122014 :     char szDelim[OGR_WKT_TOKEN_MAX] = {};
     645             : 
     646      107613 :     do
     647             :     {
     648             :         /* --------------------------------------------------------------------
     649             :          */
     650             :         /*      Read the X and Y values, verify they are numeric. */
     651             :         /* --------------------------------------------------------------------
     652             :          */
     653      229627 :         char szTokenX[OGR_WKT_TOKEN_MAX] = {};
     654      229627 :         char szTokenY[OGR_WKT_TOKEN_MAX] = {};
     655             : 
     656      229627 :         pszInput = OGRWktReadToken(pszInput, szTokenX);
     657      229627 :         pszInput = OGRWktReadToken(pszInput, szTokenY);
     658             : 
     659      229627 :         if ((!isdigit(static_cast<unsigned char>(szTokenX[0])) &&
     660       67176 :              szTokenX[0] != '-' && szTokenX[0] != '.' &&
     661          48 :              !EQUAL(szTokenX, "nan")) ||
     662      229585 :             (!isdigit(static_cast<unsigned char>(szTokenY[0])) &&
     663       22351 :              szTokenY[0] != '-' && szTokenY[0] != '.' &&
     664           9 :              !EQUAL(szTokenY, "nan")))
     665          58 :             return nullptr;
     666             : 
     667             :         /* --------------------------------------------------------------------
     668             :          */
     669             :         /*      Do we need to grow the point list to hold this point? */
     670             :         /* --------------------------------------------------------------------
     671             :          */
     672      229582 :         if (*pnPointsRead == *pnMaxPoints)
     673             :         {
     674      121553 :             *pnMaxPoints = *pnMaxPoints * 2 + 10;
     675      121553 :             *ppaoPoints = static_cast<OGRRawPoint *>(
     676      121553 :                 CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints));
     677             : 
     678      121553 :             if (*ppadfZ != nullptr)
     679             :             {
     680         219 :                 *ppadfZ = static_cast<double *>(
     681         219 :                     CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
     682             :             }
     683             : 
     684      121553 :             if (*ppadfM != nullptr)
     685             :             {
     686          56 :                 *ppadfM = static_cast<double *>(
     687          56 :                     CPLRealloc(*ppadfM, sizeof(double) * *pnMaxPoints));
     688             :             }
     689             :         }
     690             : 
     691             :         /* --------------------------------------------------------------------
     692             :          */
     693             :         /*      Add point to list. */
     694             :         /* --------------------------------------------------------------------
     695             :          */
     696      229582 :         (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
     697      229582 :         (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
     698             : 
     699             :         /* --------------------------------------------------------------------
     700             :          */
     701             :         /*      Read the next token. */
     702             :         /* --------------------------------------------------------------------
     703             :          */
     704      229582 :         pszInput = OGRWktReadToken(pszInput, szDelim);
     705             : 
     706             :         /* --------------------------------------------------------------------
     707             :          */
     708             :         /*      If there are unexpectedly more coordinates, they are Z. */
     709             :         /* --------------------------------------------------------------------
     710             :          */
     711             : 
     712      229582 :         if (!(*flags & OGRGeometry::OGR_G_3D) &&
     713      217934 :             !(*flags & OGRGeometry::OGR_G_MEASURED) &&
     714      217141 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     715      171967 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     716             :         {
     717       45355 :             *flags |= OGRGeometry::OGR_G_3D;
     718             :         }
     719             : 
     720             :         /* --------------------------------------------------------------------
     721             :          */
     722             :         /*      Get Z if flag says so. */
     723             :         /*      Zero out possible remains from earlier strings. */
     724             :         /* --------------------------------------------------------------------
     725             :          */
     726             : 
     727      229582 :         if (*flags & OGRGeometry::OGR_G_3D)
     728             :         {
     729       57003 :             if (*ppadfZ == nullptr)
     730             :             {
     731       46492 :                 *ppadfZ = static_cast<double *>(
     732       46492 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     733             :             }
     734       57003 :             if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     735        3073 :                 szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
     736             :             {
     737       56924 :                 (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
     738       56924 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     739             :             }
     740             :             else
     741             :             {
     742          79 :                 (*ppadfZ)[*pnPointsRead] = 0.0;
     743             :             }
     744             :         }
     745      172579 :         else if (*ppadfZ != nullptr)
     746             :         {
     747          43 :             (*ppadfZ)[*pnPointsRead] = 0.0;
     748             :         }
     749             : 
     750             :         /* --------------------------------------------------------------------
     751             :          */
     752             :         /*      If there are unexpectedly even more coordinates, */
     753             :         /*      they are discarded unless there were no flags originally. */
     754             :         /*      This is for backwards compatibility. Should this be an error? */
     755             :         /* --------------------------------------------------------------------
     756             :          */
     757             : 
     758      229582 :         if (!(*flags & OGRGeometry::OGR_G_MEASURED) &&
     759      227423 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     760      227422 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     761             :         {
     762           5 :             if (bNoFlags)
     763             :             {
     764           5 :                 *flags |= OGRGeometry::OGR_G_MEASURED;
     765             :             }
     766             :             else
     767             :             {
     768           0 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     769             :             }
     770             :         }
     771             : 
     772             :         /* --------------------------------------------------------------------
     773             :          */
     774             :         /*      Get M if flag says so. */
     775             :         /*      Zero out possible remains from earlier strings. */
     776             :         /* --------------------------------------------------------------------
     777             :          */
     778             : 
     779      229582 :         if (*flags & OGRGeometry::OGR_G_MEASURED)
     780             :         {
     781        2164 :             if (*ppadfM == nullptr)
     782             :             {
     783         701 :                 *ppadfM = static_cast<double *>(
     784         701 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     785             :             }
     786        2164 :             if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     787         163 :                 szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
     788             :             {
     789        2158 :                 (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
     790        2158 :                 pszInput = OGRWktReadToken(pszInput, szDelim);
     791             :             }
     792             :             else
     793             :             {
     794           6 :                 (*ppadfM)[*pnPointsRead] = 0.0;
     795             :             }
     796             :         }
     797      227418 :         else if (*ppadfM != nullptr)
     798             :         {
     799           0 :             (*ppadfM)[*pnPointsRead] = 0.0;
     800             :         }
     801             : 
     802             :         /* --------------------------------------------------------------------
     803             :          */
     804             :         /*      If there are still more coordinates and we do not have Z */
     805             :         /*      then we have a case of flags == M and four coordinates. */
     806             :         /*      This is allowed in BNF. */
     807             :         /* --------------------------------------------------------------------
     808             :          */
     809             : 
     810      229582 :         if (!(*flags & OGRGeometry::OGR_G_3D) &&
     811      172579 :             (isdigit(static_cast<unsigned char>(szDelim[0])) ||
     812      172579 :              szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
     813             :         {
     814           0 :             *flags |= OGRGeometry::OGR_G_3D;
     815           0 :             if (*ppadfZ == nullptr)
     816             :             {
     817           0 :                 *ppadfZ = static_cast<double *>(
     818           0 :                     CPLCalloc(sizeof(double), *pnMaxPoints));
     819             :             }
     820           0 :             (*ppadfZ)[*pnPointsRead] = (*ppadfM)[*pnPointsRead];
     821           0 :             (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
     822           0 :             pszInput = OGRWktReadToken(pszInput, szDelim);
     823             :         }
     824             : 
     825             :         /* --------------------------------------------------------------------
     826             :          */
     827             :         /*      Increase points index. */
     828             :         /* --------------------------------------------------------------------
     829             :          */
     830      229582 :         ++(*pnPointsRead);
     831             : 
     832             :         /* --------------------------------------------------------------------
     833             :          */
     834             :         /*      The next delimiter should be a comma or an ending bracket. */
     835             :         /* --------------------------------------------------------------------
     836             :          */
     837      229582 :         if (szDelim[0] != ')' && szDelim[0] != ',')
     838             :         {
     839          13 :             CPLDebug("OGR",
     840             :                      "Corrupt input in OGRWktReadPointsM()  "
     841             :                      "Got `%s' when expecting `,' or `)', near `%s' in %s.",
     842             :                      szDelim, pszInput, pszOrigInput);
     843          13 :             return nullptr;
     844             :         }
     845      229569 :     } while (szDelim[0] == ',');
     846             : 
     847      121956 :     return pszInput;
     848             : }
     849             : 
     850             : /************************************************************************/
     851             : /*                             OGRMalloc()                              */
     852             : /*                                                                      */
     853             : /*      Cover for CPLMalloc()                                           */
     854             : /************************************************************************/
     855             : 
     856           0 : void *OGRMalloc(size_t size)
     857             : 
     858             : {
     859           0 :     return CPLMalloc(size);
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                             OGRCalloc()                              */
     864             : /*                                                                      */
     865             : /*      Cover for CPLCalloc()                                           */
     866             : /************************************************************************/
     867             : 
     868           0 : void *OGRCalloc(size_t count, size_t size)
     869             : 
     870             : {
     871           0 :     return CPLCalloc(count, size);
     872             : }
     873             : 
     874             : /************************************************************************/
     875             : /*                             OGRRealloc()                             */
     876             : /*                                                                      */
     877             : /*      Cover for CPLRealloc()                                          */
     878             : /************************************************************************/
     879             : 
     880           0 : void *OGRRealloc(void *pOld, size_t size)
     881             : 
     882             : {
     883           0 :     return CPLRealloc(pOld, size);
     884             : }
     885             : 
     886             : /************************************************************************/
     887             : /*                              OGRFree()                               */
     888             : /*                                                                      */
     889             : /*      Cover for CPLFree().                                            */
     890             : /************************************************************************/
     891             : 
     892           0 : void OGRFree(void *pMemory)
     893             : 
     894             : {
     895           0 :     CPLFree(pMemory);
     896           0 : }
     897             : 
     898             : /**
     899             :  * \fn OGRGeneralCmdLineProcessor(int, char***, int)
     900             :  * General utility option processing.
     901             :  *
     902             :  * This function is intended to provide a variety of generic commandline
     903             :  * options for all OGR commandline utilities.  It takes care of the following
     904             :  * commandline options:
     905             :  *
     906             :  *  --version: report version of GDAL in use.
     907             :  *  --license: report GDAL license info.
     908             :  *  --format [format]: report details of one format driver.
     909             :  *  --formats: report all format drivers configured.
     910             :  *  --optfile filename: expand an option file into the argument list.
     911             :  *  --config key value: set system configuration option.
     912             :  *  --debug [on/off/value]: set debug level.
     913             :  *  --pause: Pause for user input (allows time to attach debugger)
     914             :  *  --locale [locale]: Install a locale using setlocale() (debugging)
     915             :  *  --help-general: report detailed help on general options.
     916             :  *
     917             :  * The argument array is replaced "in place" and should be freed with
     918             :  * CSLDestroy() when no longer needed.  The typical usage looks something
     919             :  * like the following.  Note that the formats should be registered so that
     920             :  * the --formats option will work properly.
     921             :  *
     922             :  *  int main( int argc, char ** argv )
     923             :  *  {
     924             :  *    OGRRegisterAll();
     925             :  *
     926             :  *    argc = OGRGeneralCmdLineProcessor( argc, &argv, 0 );
     927             :  *    if( argc < 1 )
     928             :  *        exit( -argc );
     929             :  *
     930             :  * @param nArgc number of values in the argument list.
     931             :  * @param ppapszArgv pointer to the argument list array (will be updated in
     932             :  * place).
     933             :  * @param nOptions unused.
     934             :  *
     935             :  * @return updated nArgc argument count.  Return of 0 requests terminate
     936             :  * without error, return of -1 requests exit with error code.
     937             :  */
     938             : 
     939         314 : int OGRGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
     940             :                                CPL_UNUSED int nOptions)
     941             : 
     942             : {
     943         314 :     return GDALGeneralCmdLineProcessor(nArgc, ppapszArgv, GDAL_OF_VECTOR);
     944             : }
     945             : 
     946             : /************************************************************************/
     947             : /*                       OGRTimezoneToTZFlag()                          */
     948             : /************************************************************************/
     949             : 
     950             : /** \brief Converts a text timezone into OGR TZFlag integer representation.
     951             :  *
     952             :  * @param pszTZ "UTC", "Etc/UTC", or "(+/-)[0-9][0-9](:?)[0-9][0-9]"
     953             :  * @param bEmitErrorIfUnhandledFormat Whether to emit an error if pszTZ is
     954             :  *                                    a non-empty string with unrecognized
     955             :  *                                    format.
     956             :  */
     957        2482 : int OGRTimezoneToTZFlag(const char *pszTZ, bool bEmitErrorIfUnhandledFormat)
     958             : {
     959        2482 :     int nTZFlag = OGR_TZFLAG_UNKNOWN;
     960        2482 :     const size_t nTZLen = strlen(pszTZ);
     961        2482 :     if (strcmp(pszTZ, "UTC") == 0 || strcmp(pszTZ, "Etc/UTC") == 0)
     962             :     {
     963         399 :         nTZFlag = OGR_TZFLAG_UTC;
     964             :     }
     965        2083 :     else if ((pszTZ[0] == '+' || pszTZ[0] == '-') &&
     966         820 :              ((nTZLen == 6 && pszTZ[3] == ':') ||
     967           0 :               (nTZLen == 5 && pszTZ[3] >= '0' && pszTZ[3] <= '9')))
     968             :     {
     969         820 :         int nTZHour = atoi(pszTZ + 1);
     970         820 :         int nTZMin = atoi(pszTZ + (nTZLen == 6 ? 4 : 3));
     971         820 :         if (nTZHour >= 0 && nTZHour <= 14 && nTZMin >= 0 && nTZMin < 60 &&
     972         820 :             (nTZMin % 15) == 0)
     973             :         {
     974         820 :             nTZFlag = (nTZHour * 4) + (nTZMin / 15);
     975         820 :             if (pszTZ[0] == '+')
     976             :             {
     977         414 :                 nTZFlag = OGR_TZFLAG_UTC + nTZFlag;
     978             :             }
     979             :             else
     980             :             {
     981         406 :                 nTZFlag = OGR_TZFLAG_UTC - nTZFlag;
     982             :             }
     983         820 :         }
     984             :     }
     985        1263 :     else if (pszTZ[0] != 0 && bEmitErrorIfUnhandledFormat)
     986             :     {
     987           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized timezone: '%s'",
     988             :                  pszTZ);
     989             :     }
     990        2482 :     return nTZFlag;
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                       OGRTZFlagToTimezone()                          */
     995             : /************************************************************************/
     996             : 
     997             : /** \brief Converts a OGR TZFlag integer representation into a string
     998             :  *
     999             :  * @param nTZFlag OGR TZFlag integer (only ones with
    1000             :  *        value > OGR_TZFLAG_MIXED_TZ will yield a non-empty output)
    1001             :  * @param pszUTCRepresentation String to return if nTZFlag == OGR_TZFLAG_UTC.
    1002             :  *                             Typically "UTC", "Z", or "+00:00"
    1003             :  */
    1004          16 : std::string OGRTZFlagToTimezone(int nTZFlag, const char *pszUTCRepresentation)
    1005             : {
    1006          16 :     if (nTZFlag == OGR_TZFLAG_UTC)
    1007             :     {
    1008           0 :         return pszUTCRepresentation;
    1009             :     }
    1010          16 :     else if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
    1011             :     {
    1012             :         char chSign;
    1013          16 :         const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
    1014          16 :         int nHours = static_cast<int>(nOffset / 60);  // Round towards zero.
    1015          16 :         const int nMinutes = std::abs(nOffset - nHours * 60);
    1016             : 
    1017          16 :         if (nOffset < 0)
    1018             :         {
    1019           8 :             chSign = '-';
    1020           8 :             nHours = std::abs(nHours);
    1021             :         }
    1022             :         else
    1023             :         {
    1024           8 :             chSign = '+';
    1025             :         }
    1026          16 :         return CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
    1027             :     }
    1028             :     else
    1029             :     {
    1030           0 :         return std::string();
    1031             :     }
    1032             : }
    1033             : 
    1034             : /************************************************************************/
    1035             : /*                            OGRParseDate()                            */
    1036             : /*                                                                      */
    1037             : /*      Parse a variety of text date formats into an OGRField.          */
    1038             : /************************************************************************/
    1039             : 
    1040             : /**
    1041             :  * Parse date string.
    1042             :  *
    1043             :  * This function attempts to parse a date string in a variety of formats
    1044             :  * into the OGRField.Date format suitable for use with OGR.  Generally
    1045             :  * speaking this function is expecting values like:
    1046             :  *
    1047             :  *   YYYY-MM-DD HH:MM:SS(.sss)?+nn
    1048             :  *   or YYYY-MM-DDTHH:MM:SS(.sss)?Z (ISO 8601 format)
    1049             :  *   or YYYY-MM-DDZ
    1050             :  *   or THH:MM(:SS(.sss)?)?Z? (ISO 8601 extended format)
    1051             :  *   or THHMM(SS(.sss)?)?Z? (ISO 8601 basic format)
    1052             :  *
    1053             :  * The seconds may also have a decimal portion (parsed as milliseconds).  And
    1054             :  * just dates (YYYY-MM-DD) or just times (HH:MM:SS[.sss]) are also supported.
    1055             :  * The date may also be in YYYY/MM/DD format.  If the year is less than 100
    1056             :  * and greater than 30 a "1900" century value will be set.  If it is less than
    1057             :  * 30 and greater than -1 then a "2000" century value will be set.  In
    1058             :  * the future this function may be generalized, and additional control
    1059             :  * provided through nOptions, but an nOptions value of "0" should always do
    1060             :  * a reasonable default form of processing.
    1061             :  *
    1062             :  * The value of psField will be indeterminate if the function fails (returns
    1063             :  * FALSE).
    1064             :  *
    1065             :  * @param pszInput the input date string.
    1066             :  * @param psField the OGRField that will be updated with the parsed result.
    1067             :  * @param nOptions parsing options. 0 or OGRPARSEDATE_OPTION_LAX
    1068             :  *
    1069             :  * @return TRUE if apparently successful or FALSE on failure.
    1070             :  */
    1071             : 
    1072       13944 : int OGRParseDate(const char *pszInput, OGRField *psField, int nOptions)
    1073             : {
    1074       13944 :     psField->Date.Year = 0;
    1075       13944 :     psField->Date.Month = 0;
    1076       13944 :     psField->Date.Day = 0;
    1077       13944 :     psField->Date.Hour = 0;
    1078       13944 :     psField->Date.Minute = 0;
    1079       13944 :     psField->Date.Second = 0;
    1080       13944 :     psField->Date.TZFlag = 0;
    1081       13944 :     psField->Date.Reserved = 0;
    1082             : 
    1083             :     /* -------------------------------------------------------------------- */
    1084             :     /*      Do we have a date?                                              */
    1085             :     /* -------------------------------------------------------------------- */
    1086       13945 :     while (*pszInput == ' ')
    1087           1 :         ++pszInput;
    1088             : 
    1089       13944 :     bool bGotSomething = false;
    1090       13944 :     bool bTFound = false;
    1091       13944 :     if (strchr(pszInput, '-') || strchr(pszInput, '/'))
    1092             :     {
    1093        8251 :         if (!(*pszInput == '-' || *pszInput == '+' ||
    1094        8250 :               (*pszInput >= '0' && *pszInput <= '9')))
    1095          60 :             return FALSE;
    1096        8191 :         int nYear = atoi(pszInput);
    1097       16381 :         if (nYear > std::numeric_limits<GInt16>::max() ||
    1098        8190 :             nYear < std::numeric_limits<GInt16>::min())
    1099             :         {
    1100           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1101             :                      "Years < %d or > %d are not supported",
    1102           1 :                      std::numeric_limits<GInt16>::min(),
    1103           1 :                      std::numeric_limits<GInt16>::max());
    1104           1 :             return FALSE;
    1105             :         }
    1106        8190 :         psField->Date.Year = static_cast<GInt16>(nYear);
    1107        8190 :         if ((pszInput[1] == '-' || pszInput[1] == '/') ||
    1108        8188 :             (pszInput[1] != '\0' && (pszInput[2] == '-' || pszInput[2] == '/')))
    1109             :         {
    1110           6 :             if (psField->Date.Year < 100 && psField->Date.Year >= 30)
    1111           1 :                 psField->Date.Year += 1900;
    1112           5 :             else if (psField->Date.Year < 30 && psField->Date.Year >= 0)
    1113           5 :                 psField->Date.Year += 2000;
    1114             :         }
    1115             : 
    1116        8190 :         if (*pszInput == '-')
    1117           1 :             ++pszInput;
    1118       40923 :         while (*pszInput >= '0' && *pszInput <= '9')
    1119       32733 :             ++pszInput;
    1120        8190 :         if (*pszInput != '-' && *pszInput != '/')
    1121          11 :             return FALSE;
    1122             :         else
    1123        8179 :             ++pszInput;
    1124             : 
    1125        8179 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1126           2 :             return FALSE;
    1127        8177 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1128             :         {
    1129          16 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1130           1 :                 return FALSE;
    1131          15 :             const int nMonth = (pszInput[0] - '0');
    1132          15 :             if (nMonth == 0)
    1133           1 :                 return FALSE;
    1134          14 :             psField->Date.Month = static_cast<GByte>(nMonth);
    1135          14 :             ++pszInput;
    1136             :         }
    1137             :         else
    1138             :         {
    1139        8161 :             const int nMonth = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1140        8161 :             if (nMonth == 0 || nMonth > 12)
    1141           8 :                 return FALSE;
    1142        8153 :             psField->Date.Month = static_cast<GByte>(nMonth);
    1143             : 
    1144        8153 :             pszInput += 2;
    1145             :         }
    1146        8167 :         if (*pszInput != '-' && *pszInput != '/')
    1147           1 :             return FALSE;
    1148             :         else
    1149        8166 :             ++pszInput;
    1150             : 
    1151        8166 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1152           3 :             return FALSE;
    1153        8163 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1154             :         {
    1155           8 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1156           1 :                 return FALSE;
    1157           7 :             const int nDay = (pszInput[0] - '0');
    1158           7 :             if (nDay == 0)
    1159           1 :                 return FALSE;
    1160           6 :             psField->Date.Day = static_cast<GByte>(nDay);
    1161           6 :             ++pszInput;
    1162             :         }
    1163             :         else
    1164             :         {
    1165        8155 :             const int nDay = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1166        8155 :             if (nDay == 0 || nDay > 31)
    1167           5 :                 return FALSE;
    1168        8150 :             psField->Date.Day = static_cast<GByte>(nDay);
    1169             : 
    1170        8150 :             pszInput += 2;
    1171             :         }
    1172        8156 :         if (*pszInput == '\0')
    1173        3367 :             return TRUE;
    1174             : 
    1175        4789 :         bGotSomething = true;
    1176             : 
    1177             :         // If ISO 8601 format.
    1178        4789 :         if (*pszInput == 'T')
    1179             :         {
    1180        2182 :             bTFound = true;
    1181        2182 :             ++pszInput;
    1182             :         }
    1183        2607 :         else if (*pszInput == 'Z')
    1184           1 :             return TRUE;
    1185        2606 :         else if (*pszInput != ' ')
    1186           5 :             return FALSE;
    1187             :     }
    1188             : 
    1189             :     /* -------------------------------------------------------------------- */
    1190             :     /*      Do we have a time?                                              */
    1191             :     /* -------------------------------------------------------------------- */
    1192       13141 :     while (*pszInput == ' ')
    1193        2665 :         ++pszInput;
    1194       10476 :     if (*pszInput == 'T')
    1195             :     {
    1196          19 :         bTFound = true;
    1197          19 :         ++pszInput;
    1198             :     }
    1199             : 
    1200       10476 :     if (bTFound || strchr(pszInput, ':'))
    1201             :     {
    1202        9673 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1203          45 :             return FALSE;
    1204        9628 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1205             :         {
    1206          10 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1207           4 :                 return FALSE;
    1208             : 
    1209           6 :             if (!((bTFound || pszInput[1] == ':')))
    1210           1 :                 return FALSE;
    1211           5 :             const int nHour = (pszInput[0] - '0');
    1212           5 :             psField->Date.Hour = static_cast<GByte>(nHour);
    1213             : 
    1214           5 :             pszInput++;
    1215             :         }
    1216             :         else
    1217             :         {
    1218        9618 :             if (!((bTFound || pszInput[2] == ':')))
    1219           0 :                 return FALSE;
    1220        9618 :             const int nHour = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1221        9618 :             if (nHour > 23)
    1222           1 :                 return FALSE;
    1223        9617 :             psField->Date.Hour = static_cast<GByte>(nHour);
    1224             : 
    1225        9617 :             pszInput += 2;
    1226             :         }
    1227        9622 :         if (*pszInput == ':')
    1228        9619 :             ++pszInput;
    1229             : 
    1230        9622 :         if (!(*pszInput >= '0' && *pszInput <= '9'))
    1231           6 :             return FALSE;
    1232        9616 :         if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
    1233             :         {
    1234           4 :             if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
    1235           1 :                 return FALSE;
    1236             : 
    1237           3 :             const int nMinute = (pszInput[0] - '0');
    1238           3 :             psField->Date.Minute = static_cast<GByte>(nMinute);
    1239             : 
    1240           3 :             pszInput++;
    1241             :         }
    1242             :         else
    1243             :         {
    1244        9612 :             const int nMinute = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
    1245        9612 :             if (nMinute > 59)
    1246           1 :                 return FALSE;
    1247        9611 :             psField->Date.Minute = static_cast<GByte>(nMinute);
    1248             : 
    1249        9611 :             pszInput += 2;
    1250             :         }
    1251             : 
    1252        9614 :         if ((bTFound && *pszInput >= '0' && *pszInput <= '9') ||
    1253        9612 :             *pszInput == ':')
    1254             :         {
    1255        9598 :             if (*pszInput == ':')
    1256        9596 :                 ++pszInput;
    1257             : 
    1258        9598 :             if (!(*pszInput >= '0' && *pszInput <= '9' &&
    1259        9596 :                   (((nOptions & OGRPARSEDATE_OPTION_LAX) != 0) ||
    1260        9591 :                    (pszInput[1] >= '0' && pszInput[1] <= '9'))))
    1261           3 :                 return FALSE;
    1262        9595 :             const double dfSeconds = CPLAtof(pszInput);
    1263             :             // We accept second=60 for leap seconds
    1264        9595 :             if (dfSeconds > 60.0)
    1265           1 :                 return FALSE;
    1266        9594 :             psField->Date.Second = static_cast<float>(dfSeconds);
    1267             : 
    1268        9594 :             pszInput += 2;
    1269        9594 :             if (*pszInput == '.')
    1270             :             {
    1271        2019 :                 ++pszInput;
    1272        8118 :                 while (*pszInput >= '0' && *pszInput <= '9')
    1273             :                 {
    1274        6099 :                     ++pszInput;
    1275             :                 }
    1276             :             }
    1277             : 
    1278             :             // If ISO 8601 format.
    1279        9594 :             if (*pszInput == 'Z')
    1280             :             {
    1281         453 :                 psField->Date.TZFlag = 100;
    1282             :             }
    1283             :         }
    1284             : 
    1285        9610 :         bGotSomething = true;
    1286             :     }
    1287         803 :     else if (bGotSomething && *pszInput != '\0')
    1288           1 :         return FALSE;
    1289             : 
    1290             :     // No date or time!
    1291       10412 :     if (!bGotSomething)
    1292         741 :         return FALSE;
    1293             : 
    1294             :     /* -------------------------------------------------------------------- */
    1295             :     /*      Do we have a timezone?                                          */
    1296             :     /* -------------------------------------------------------------------- */
    1297        9675 :     while (*pszInput == ' ')
    1298           4 :         ++pszInput;
    1299             : 
    1300        9671 :     if (*pszInput == '-' || *pszInput == '+')
    1301             :     {
    1302             :         // +HH integral offset
    1303         310 :         if (strlen(pszInput) <= 3)
    1304             :         {
    1305         150 :             psField->Date.TZFlag = static_cast<GByte>(100 + atoi(pszInput) * 4);
    1306             :         }
    1307         160 :         else if (pszInput[3] == ':'  // +HH:MM offset
    1308          90 :                  && atoi(pszInput + 4) % 15 == 0)
    1309             :         {
    1310          90 :             psField->Date.TZFlag = static_cast<GByte>(
    1311          90 :                 100 + atoi(pszInput + 1) * 4 + (atoi(pszInput + 4) / 15));
    1312             : 
    1313          90 :             if (pszInput[0] == '-')
    1314          23 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1315             :         }
    1316          70 :         else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
    1317          70 :                  isdigit(
    1318          70 :                      static_cast<unsigned char>(pszInput[4]))  // +HHMM offset
    1319          69 :                  && atoi(pszInput + 3) % 15 == 0)
    1320             :         {
    1321          69 :             psField->Date.TZFlag = static_cast<GByte>(
    1322          69 :                 100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 2)) * 4 +
    1323          69 :                 (atoi(pszInput + 3) / 15));
    1324             : 
    1325          69 :             if (pszInput[0] == '-')
    1326           0 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1327             :         }
    1328           1 :         else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
    1329           1 :                  pszInput[4] == '\0'  // +HMM offset
    1330           1 :                  && atoi(pszInput + 2) % 15 == 0)
    1331             :         {
    1332           1 :             psField->Date.TZFlag = static_cast<GByte>(
    1333           1 :                 100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 1)) * 4 +
    1334           1 :                 (atoi(pszInput + 2) / 15));
    1335             : 
    1336           1 :             if (pszInput[0] == '-')
    1337           0 :                 psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
    1338             :         }
    1339             :         // otherwise ignore any timezone info.
    1340             :     }
    1341             : 
    1342        9671 :     return TRUE;
    1343             : }
    1344             : 
    1345             : /************************************************************************/
    1346             : /*               OGRParseDateTimeYYYYMMDDTHHMMZ()                       */
    1347             : /************************************************************************/
    1348             : 
    1349          10 : bool OGRParseDateTimeYYYYMMDDTHHMMZ(const char *pszInput, size_t nLen,
    1350             :                                     OGRField *psField)
    1351             : {
    1352             :     // Detect "YYYY-MM-DDTHH:MM[Z]" (16 or 17 characters)
    1353          10 :     if ((nLen == 16 || (nLen == 17 && pszInput[16] == 'Z')) &&
    1354           4 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1355           4 :         pszInput[13] == ':' && static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1356           4 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1357           4 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1358           4 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1359           4 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1360           4 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1361           4 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1362           4 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1363           4 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1364           4 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1365           4 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1366           4 :         static_cast<unsigned>(pszInput[15] - '0') <= 9)
    1367             :     {
    1368           4 :         psField->Date.Year = static_cast<GInt16>(
    1369           4 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1370           4 :              (pszInput[2] - '0')) *
    1371           4 :                 10 +
    1372           4 :             (pszInput[3] - '0'));
    1373           4 :         psField->Date.Month =
    1374           4 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1375           4 :         psField->Date.Day =
    1376           4 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1377           4 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1378           4 :                                                 (pszInput[12] - '0'));
    1379           4 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1380           4 :                                                   (pszInput[15] - '0'));
    1381           4 :         psField->Date.Second = 0.0f;
    1382           4 :         psField->Date.TZFlag = nLen == 16 ? 0 : 100;
    1383           4 :         psField->Date.Reserved = 0;
    1384           4 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1385           4 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1386           4 :             psField->Date.Hour > 23 || psField->Date.Minute > 59)
    1387             :         {
    1388           1 :             return false;
    1389             :         }
    1390           3 :         return true;
    1391             :     }
    1392             : 
    1393           6 :     return false;
    1394             : }
    1395             : 
    1396             : /************************************************************************/
    1397             : /*               OGRParseDateTimeYYYYMMDDTHHMMSSZ()                     */
    1398             : /************************************************************************/
    1399             : 
    1400          37 : bool OGRParseDateTimeYYYYMMDDTHHMMSSZ(const char *pszInput, size_t nLen,
    1401             :                                       OGRField *psField)
    1402             : {
    1403             :     // Detect "YYYY-MM-DDTHH:MM:SS[Z]" (19 or 20 characters)
    1404          37 :     if ((nLen == 19 || (nLen == 20 && pszInput[19] == 'Z')) &&
    1405           7 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1406           7 :         pszInput[13] == ':' && pszInput[16] == ':' &&
    1407           7 :         static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1408           7 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1409           7 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1410           7 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1411           7 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1412           7 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1413           7 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1414           7 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1415           7 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1416           7 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1417           7 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1418           7 :         static_cast<unsigned>(pszInput[15] - '0') <= 9 &&
    1419           7 :         static_cast<unsigned>(pszInput[17] - '0') <= 9 &&
    1420           7 :         static_cast<unsigned>(pszInput[18] - '0') <= 9)
    1421             :     {
    1422           7 :         psField->Date.Year = static_cast<GInt16>(
    1423           7 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1424           7 :              (pszInput[2] - '0')) *
    1425           7 :                 10 +
    1426           7 :             (pszInput[3] - '0'));
    1427           7 :         psField->Date.Month =
    1428           7 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1429           7 :         psField->Date.Day =
    1430           7 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1431           7 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1432           7 :                                                 (pszInput[12] - '0'));
    1433           7 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1434           7 :                                                   (pszInput[15] - '0'));
    1435           7 :         psField->Date.Second = static_cast<float>(
    1436           7 :             ((pszInput[17] - '0') * 10 + (pszInput[18] - '0')));
    1437           7 :         psField->Date.TZFlag = nLen == 19 ? 0 : 100;
    1438           7 :         psField->Date.Reserved = 0;
    1439           7 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1440           7 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1441           7 :             psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
    1442           7 :             psField->Date.Second >= 61.0f)
    1443             :         {
    1444           1 :             return false;
    1445             :         }
    1446           6 :         return true;
    1447             :     }
    1448             : 
    1449          30 :     return false;
    1450             : }
    1451             : 
    1452             : /************************************************************************/
    1453             : /*              OGRParseDateTimeYYYYMMDDTHHMMSSsssZ()                   */
    1454             : /************************************************************************/
    1455             : 
    1456         521 : bool OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(const char *pszInput, size_t nLen,
    1457             :                                          OGRField *psField)
    1458             : {
    1459             :     // Detect "YYYY-MM-DDTHH:MM:SS.SSS[Z]" (23 or 24 characters)
    1460         521 :     if ((nLen == 23 || (nLen == 24 && pszInput[23] == 'Z')) &&
    1461         512 :         pszInput[4] == '-' && pszInput[7] == '-' && pszInput[10] == 'T' &&
    1462         512 :         pszInput[13] == ':' && pszInput[16] == ':' && pszInput[19] == '.' &&
    1463         512 :         static_cast<unsigned>(pszInput[0] - '0') <= 9 &&
    1464         512 :         static_cast<unsigned>(pszInput[1] - '0') <= 9 &&
    1465         512 :         static_cast<unsigned>(pszInput[2] - '0') <= 9 &&
    1466         512 :         static_cast<unsigned>(pszInput[3] - '0') <= 9 &&
    1467         512 :         static_cast<unsigned>(pszInput[5] - '0') <= 9 &&
    1468         512 :         static_cast<unsigned>(pszInput[6] - '0') <= 9 &&
    1469         512 :         static_cast<unsigned>(pszInput[8] - '0') <= 9 &&
    1470         512 :         static_cast<unsigned>(pszInput[9] - '0') <= 9 &&
    1471         512 :         static_cast<unsigned>(pszInput[11] - '0') <= 9 &&
    1472         512 :         static_cast<unsigned>(pszInput[12] - '0') <= 9 &&
    1473         512 :         static_cast<unsigned>(pszInput[14] - '0') <= 9 &&
    1474         512 :         static_cast<unsigned>(pszInput[15] - '0') <= 9 &&
    1475         512 :         static_cast<unsigned>(pszInput[17] - '0') <= 9 &&
    1476         512 :         static_cast<unsigned>(pszInput[18] - '0') <= 9 &&
    1477         512 :         static_cast<unsigned>(pszInput[20] - '0') <= 9 &&
    1478         512 :         static_cast<unsigned>(pszInput[21] - '0') <= 9 &&
    1479         512 :         static_cast<unsigned>(pszInput[22] - '0') <= 9)
    1480             :     {
    1481         512 :         psField->Date.Year = static_cast<GInt16>(
    1482         512 :             ((((pszInput[0] - '0') * 10 + (pszInput[1] - '0')) * 10) +
    1483         512 :              (pszInput[2] - '0')) *
    1484         512 :                 10 +
    1485         512 :             (pszInput[3] - '0'));
    1486         512 :         psField->Date.Month =
    1487         512 :             static_cast<GByte>((pszInput[5] - '0') * 10 + (pszInput[6] - '0'));
    1488         512 :         psField->Date.Day =
    1489         512 :             static_cast<GByte>((pszInput[8] - '0') * 10 + (pszInput[9] - '0'));
    1490         512 :         psField->Date.Hour = static_cast<GByte>((pszInput[11] - '0') * 10 +
    1491         512 :                                                 (pszInput[12] - '0'));
    1492         512 :         psField->Date.Minute = static_cast<GByte>((pszInput[14] - '0') * 10 +
    1493         512 :                                                   (pszInput[15] - '0'));
    1494         512 :         psField->Date.Second = static_cast<float>(
    1495         512 :             ((pszInput[17] - '0') * 10 + (pszInput[18] - '0')) +
    1496         512 :             ((pszInput[20] - '0') * 100 + (pszInput[21] - '0') * 10 +
    1497         512 :              (pszInput[22] - '0')) /
    1498             :                 1000.0);
    1499         512 :         psField->Date.TZFlag = nLen == 23 ? 0 : 100;
    1500         512 :         psField->Date.Reserved = 0;
    1501         512 :         if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
    1502         512 :             psField->Date.Day == 0 || psField->Date.Day > 31 ||
    1503         512 :             psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
    1504         512 :             psField->Date.Second >= 61.0f)
    1505             :         {
    1506           1 :             return false;
    1507             :         }
    1508         511 :         return true;
    1509             :     }
    1510             : 
    1511           9 :     return false;
    1512             : }
    1513             : 
    1514             : /************************************************************************/
    1515             : /*                           OGRParseXMLDateTime()                      */
    1516             : /************************************************************************/
    1517             : 
    1518       18277 : int OGRParseXMLDateTime(const char *pszXMLDateTime, OGRField *psField)
    1519             : {
    1520       18277 :     int year = 0;
    1521       18277 :     int month = 0;
    1522       18277 :     int day = 0;
    1523       18277 :     int hour = 0;
    1524       18277 :     int minute = 0;
    1525       18277 :     int TZHour = 0;
    1526       18277 :     int TZMinute = 0;
    1527       18277 :     float second = 0;
    1528       18277 :     char c = '\0';
    1529       18277 :     int TZ = 0;
    1530       18277 :     bool bRet = false;
    1531             : 
    1532             :     // Date is expressed as a UTC date.
    1533       36554 :     if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c", &year, &month,
    1534       35083 :                &day, &hour, &minute, &second, &c) == 7 &&
    1535       16806 :         c == 'Z')
    1536             :     {
    1537       16803 :         TZ = 100;
    1538       16803 :         bRet = true;
    1539             :     }
    1540             :     // Date is expressed as a UTC date, with a timezone.
    1541        2948 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d",
    1542             :                     &year, &month, &day, &hour, &minute, &second, &c, &TZHour,
    1543        1477 :                     &TZMinute) == 9 &&
    1544           3 :              (c == '+' || c == '-'))
    1545             :     {
    1546           3 :         TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
    1547           3 :         bRet = true;
    1548             :     }
    1549             :     // Date is expressed into an unknown timezone.
    1550        1471 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f", &year,
    1551        1471 :                     &month, &day, &hour, &minute, &second) == 6)
    1552             :     {
    1553         387 :         TZ = 0;
    1554         387 :         bRet = true;
    1555             :     }
    1556             :     // Date is expressed as a UTC date with only year:month:day.
    1557        1084 :     else if (sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) == 3)
    1558             :     {
    1559        1084 :         TZ = 0;
    1560        1084 :         bRet = true;
    1561             :     }
    1562             :     // Date is expressed as a UTC date with only year:month.
    1563           0 :     else if (sscanf(pszXMLDateTime, "%04d-%02d", &year, &month) == 2)
    1564             :     {
    1565           0 :         TZ = 0;
    1566           0 :         bRet = true;
    1567           0 :         day = 1;
    1568             :     }
    1569             : 
    1570       18277 :     if (!bRet)
    1571           0 :         return FALSE;
    1572             : 
    1573       18277 :     psField->Date.Year = static_cast<GInt16>(year);
    1574       18277 :     psField->Date.Month = static_cast<GByte>(month);
    1575       18277 :     psField->Date.Day = static_cast<GByte>(day);
    1576       18277 :     psField->Date.Hour = static_cast<GByte>(hour);
    1577       18277 :     psField->Date.Minute = static_cast<GByte>(minute);
    1578       18277 :     psField->Date.Second = second;
    1579       18277 :     psField->Date.TZFlag = static_cast<GByte>(TZ);
    1580       18277 :     psField->Date.Reserved = 0;
    1581             : 
    1582       18277 :     return TRUE;
    1583             : }
    1584             : 
    1585             : /************************************************************************/
    1586             : /*                      OGRParseRFC822DateTime()                        */
    1587             : /************************************************************************/
    1588             : 
    1589             : static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
    1590             :                                           "May", "Jun", "Jul", "Aug",
    1591             :                                           "Sep", "Oct", "Nov", "Dec"};
    1592             : 
    1593          27 : int OGRParseRFC822DateTime(const char *pszRFC822DateTime, OGRField *psField)
    1594             : {
    1595             :     int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
    1596          27 :     if (!CPLParseRFC822DateTime(pszRFC822DateTime, &nYear, &nMonth, &nDay,
    1597             :                                 &nHour, &nMinute, &nSecond, &nTZFlag, nullptr))
    1598             :     {
    1599           6 :         return false;
    1600             :     }
    1601             : 
    1602          21 :     psField->Date.Year = static_cast<GInt16>(nYear);
    1603          21 :     psField->Date.Month = static_cast<GByte>(nMonth);
    1604          21 :     psField->Date.Day = static_cast<GByte>(nDay);
    1605          21 :     psField->Date.Hour = static_cast<GByte>(nHour);
    1606          21 :     psField->Date.Minute = static_cast<GByte>(nMinute);
    1607          21 :     psField->Date.Second = (nSecond < 0) ? 0.0f : static_cast<float>(nSecond);
    1608          21 :     psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
    1609          21 :     psField->Date.Reserved = 0;
    1610             : 
    1611          21 :     return true;
    1612             : }
    1613             : 
    1614             : /**
    1615             :   * Returns the day of the week in Gregorian calendar
    1616             :   *
    1617             :   * @param day : day of the month, between 1 and 31
    1618             :   * @param month : month of the year, between 1 (Jan) and 12 (Dec)
    1619             :   * @param year : year
    1620             : 
    1621             :   * @return day of the week : 0 for Monday, ... 6 for Sunday
    1622             :   */
    1623             : 
    1624          12 : int OGRGetDayOfWeek(int day, int month, int year)
    1625             : {
    1626             :     // Reference: Zeller's congruence.
    1627          12 :     const int q = day;
    1628          12 :     int m = month;
    1629          12 :     if (month >= 3)
    1630             :     {
    1631             :         // m = month;
    1632             :     }
    1633             :     else
    1634             :     {
    1635           0 :         m = month + 12;
    1636           0 :         year--;
    1637             :     }
    1638          12 :     const int K = year % 100;
    1639          12 :     const int J = year / 100;
    1640          12 :     const int h = (q + (((m + 1) * 26) / 10) + K + K / 4 + J / 4 + 5 * J) % 7;
    1641          12 :     return (h + 5) % 7;
    1642             : }
    1643             : 
    1644             : /************************************************************************/
    1645             : /*                         OGRGetRFC822DateTime()                       */
    1646             : /************************************************************************/
    1647             : 
    1648          12 : char *OGRGetRFC822DateTime(const OGRField *psField)
    1649             : {
    1650          12 :     char *pszTZ = nullptr;
    1651          12 :     const char *const aszDayOfWeek[] = {"Mon", "Tue", "Wed", "Thu",
    1652             :                                         "Fri", "Sat", "Sun"};
    1653             : 
    1654          24 :     int dayofweek = OGRGetDayOfWeek(psField->Date.Day, psField->Date.Month,
    1655          12 :                                     psField->Date.Year);
    1656             : 
    1657          12 :     int month = psField->Date.Month;
    1658          12 :     if (month < 1 || month > 12)
    1659           0 :         month = 1;
    1660             : 
    1661          12 :     int TZFlag = psField->Date.TZFlag;
    1662          12 :     if (TZFlag == 0 || TZFlag == 100)
    1663             :     {
    1664           0 :         pszTZ = CPLStrdup("GMT");
    1665             :     }
    1666             :     else
    1667             :     {
    1668          12 :         int TZOffset = std::abs(TZFlag - 100) * 15;
    1669          12 :         int TZHour = TZOffset / 60;
    1670          12 :         int TZMinute = TZOffset - TZHour * 60;
    1671          12 :         pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-',
    1672             :                                      TZHour, TZMinute));
    1673             :     }
    1674          12 :     char *pszRet = CPLStrdup(CPLSPrintf(
    1675          12 :         "%s, %02d %s %04d %02d:%02d:%02d %s", aszDayOfWeek[dayofweek],
    1676          12 :         psField->Date.Day, aszMonthStr[month - 1], psField->Date.Year,
    1677          12 :         psField->Date.Hour, psField->Date.Minute,
    1678          12 :         static_cast<int>(psField->Date.Second), pszTZ));
    1679          12 :     CPLFree(pszTZ);
    1680          12 :     return pszRet;
    1681             : }
    1682             : 
    1683             : /************************************************************************/
    1684             : /*                            OGRGetXMLDateTime()                       */
    1685             : /************************************************************************/
    1686             : 
    1687         879 : char *OGRGetXMLDateTime(const OGRField *psField)
    1688             : {
    1689             :     char *pszRet =
    1690         879 :         static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
    1691         879 :     OGRGetISO8601DateTime(psField, false, pszRet);
    1692         879 :     return pszRet;
    1693             : }
    1694             : 
    1695           0 : char *OGRGetXMLDateTime(const OGRField *psField, bool bAlwaysMillisecond)
    1696             : {
    1697             :     char *pszRet =
    1698           0 :         static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
    1699           0 :     OGRGetISO8601DateTime(psField, bAlwaysMillisecond, pszRet);
    1700           0 :     return pszRet;
    1701             : }
    1702             : 
    1703        1043 : int OGRGetISO8601DateTime(const OGRField *psField, bool bAlwaysMillisecond,
    1704             :                           char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
    1705             : {
    1706             :     OGRISO8601Format sFormat;
    1707        1043 :     sFormat.ePrecision = bAlwaysMillisecond ? OGRISO8601Precision::MILLISECOND
    1708             :                                             : OGRISO8601Precision::AUTO;
    1709        2086 :     return OGRGetISO8601DateTime(psField, sFormat, szBuffer);
    1710             : }
    1711             : 
    1712        1250 : int OGRGetISO8601DateTime(const OGRField *psField,
    1713             :                           const OGRISO8601Format &sFormat,
    1714             :                           char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
    1715             : {
    1716        1250 :     const GInt16 year = psField->Date.Year;
    1717        1250 :     const GByte month = psField->Date.Month;
    1718        1250 :     const GByte day = psField->Date.Day;
    1719        1250 :     const GByte hour = psField->Date.Hour;
    1720        1250 :     const GByte minute = psField->Date.Minute;
    1721        1250 :     const float second = psField->Date.Second;
    1722        1250 :     const GByte TZFlag = psField->Date.TZFlag;
    1723             : 
    1724        1250 :     if (year < 0 || year >= 10000)
    1725             :     {
    1726           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1727             :                  "OGRGetISO8601DateTime(): year %d unsupported ", year);
    1728           0 :         szBuffer[0] = 0;
    1729           0 :         return 0;
    1730             :     }
    1731             : 
    1732        1250 :     int nYear = year;
    1733        1250 :     szBuffer[3] = (nYear % 10) + '0';
    1734        1250 :     nYear /= 10;
    1735        1250 :     szBuffer[2] = (nYear % 10) + '0';
    1736        1250 :     nYear /= 10;
    1737        1250 :     szBuffer[1] = (nYear % 10) + '0';
    1738        1250 :     nYear /= 10;
    1739        1250 :     szBuffer[0] = static_cast<char>(nYear /*% 10*/ + '0');
    1740        1250 :     szBuffer[4] = '-';
    1741        1250 :     szBuffer[5] = ((month / 10) % 10) + '0';
    1742        1250 :     szBuffer[6] = (month % 10) + '0';
    1743        1250 :     szBuffer[7] = '-';
    1744        1250 :     szBuffer[8] = ((day / 10) % 10) + '0';
    1745        1250 :     szBuffer[9] = (day % 10) + '0';
    1746        1250 :     szBuffer[10] = 'T';
    1747        1250 :     szBuffer[11] = ((hour / 10) % 10) + '0';
    1748        1250 :     szBuffer[12] = (hour % 10) + '0';
    1749        1250 :     szBuffer[13] = ':';
    1750        1250 :     szBuffer[14] = ((minute / 10) % 10) + '0';
    1751        1250 :     szBuffer[15] = (minute % 10) + '0';
    1752             :     int nPos;
    1753        1250 :     if (sFormat.ePrecision == OGRISO8601Precision::MINUTE)
    1754             :     {
    1755           2 :         nPos = 16;
    1756             :     }
    1757             :     else
    1758             :     {
    1759        1248 :         szBuffer[16] = ':';
    1760             : 
    1761        2340 :         if (sFormat.ePrecision == OGRISO8601Precision::MILLISECOND ||
    1762        1092 :             (sFormat.ePrecision == OGRISO8601Precision::AUTO &&
    1763        1090 :              OGR_GET_MS(second)))
    1764             :         {
    1765             :             /* Below is equivalent of the below snprintf(), but hand-made for
    1766             :              * faster execution. */
    1767             :             /* snprintf(szBuffer, nMaxSize,
    1768             :                                    "%04d-%02u-%02uT%02u:%02u:%06.3f%s",
    1769             :                                    year, month, day, hour, minute, second,
    1770             :                                    szTimeZone);
    1771             :             */
    1772         218 :             int nMilliSecond = static_cast<int>(second * 1000.0f + 0.5f);
    1773         218 :             szBuffer[22] = (nMilliSecond % 10) + '0';
    1774         218 :             nMilliSecond /= 10;
    1775         218 :             szBuffer[21] = (nMilliSecond % 10) + '0';
    1776         218 :             nMilliSecond /= 10;
    1777         218 :             szBuffer[20] = (nMilliSecond % 10) + '0';
    1778         218 :             nMilliSecond /= 10;
    1779         218 :             szBuffer[19] = '.';
    1780         218 :             szBuffer[18] = (nMilliSecond % 10) + '0';
    1781         218 :             nMilliSecond /= 10;
    1782         218 :             szBuffer[17] = (nMilliSecond % 10) + '0';
    1783         218 :             nPos = 23;
    1784             :         }
    1785             :         else
    1786             :         {
    1787             :             /* Below is equivalent of the below snprintf(), but hand-made for
    1788             :              * faster execution. */
    1789             :             /* snprintf(szBuffer, nMaxSize,
    1790             :                                    "%04d-%02u-%02uT%02u:%02u:%02u%s",
    1791             :                                    year, month, day, hour, minute,
    1792             :                                    static_cast<GByte>(second), szTimeZone);
    1793             :             */
    1794        1030 :             int nSecond = static_cast<int>(second + 0.5f);
    1795        1030 :             szBuffer[17] = ((nSecond / 10) % 10) + '0';
    1796        1030 :             szBuffer[18] = (nSecond % 10) + '0';
    1797        1030 :             nPos = 19;
    1798             :         }
    1799             :     }
    1800             : 
    1801        1250 :     switch (TZFlag)
    1802             :     {
    1803         965 :         case 0:  // Unknown time zone
    1804             :         case 1:  // Local time zone (not specified)
    1805         965 :             break;
    1806             : 
    1807         250 :         case 100:  // GMT
    1808         250 :             szBuffer[nPos++] = 'Z';
    1809         250 :             break;
    1810             : 
    1811          35 :         default:  // Offset (in quarter-hour units) from GMT
    1812          35 :             const int TZOffset = std::abs(TZFlag - 100) * 15;
    1813          35 :             const int TZHour = TZOffset / 60;
    1814          35 :             const int TZMinute = TZOffset % 60;
    1815             : 
    1816          35 :             szBuffer[nPos++] = (TZFlag > 100) ? '+' : '-';
    1817          35 :             szBuffer[nPos++] = ((TZHour / 10) % 10) + '0';
    1818          35 :             szBuffer[nPos++] = (TZHour % 10) + '0';
    1819          35 :             szBuffer[nPos++] = ':';
    1820          35 :             szBuffer[nPos++] = ((TZMinute / 10) % 10) + '0';
    1821          35 :             szBuffer[nPos++] = (TZMinute % 10) + '0';
    1822             :     }
    1823             : 
    1824        1250 :     szBuffer[nPos] = 0;
    1825             : 
    1826        1250 :     return nPos;
    1827             : }
    1828             : 
    1829             : /************************************************************************/
    1830             : /*                 OGRGetXML_UTF8_EscapedString()                       */
    1831             : /************************************************************************/
    1832             : 
    1833        3036 : char *OGRGetXML_UTF8_EscapedString(const char *pszString)
    1834             : {
    1835        3036 :     char *pszEscaped = nullptr;
    1836        3037 :     if (!CPLIsUTF8(pszString, -1) &&
    1837           1 :         CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
    1838             :     {
    1839             :         static bool bFirstTime = true;
    1840           1 :         if (bFirstTime)
    1841             :         {
    1842           1 :             bFirstTime = false;
    1843           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    1844             :                      "%s is not a valid UTF-8 string. Forcing it to ASCII.  "
    1845             :                      "If you still want the original string and change the XML "
    1846             :                      "file encoding afterwards, you can define "
    1847             :                      "OGR_FORCE_ASCII=NO as configuration option.  "
    1848             :                      "This warning won't be issued anymore",
    1849             :                      pszString);
    1850             :         }
    1851             :         else
    1852             :         {
    1853           0 :             CPLDebug("OGR",
    1854             :                      "%s is not a valid UTF-8 string. Forcing it to ASCII",
    1855             :                      pszString);
    1856             :         }
    1857           1 :         char *pszTemp = CPLForceToASCII(pszString, -1, '?');
    1858           1 :         pszEscaped = CPLEscapeString(pszTemp, -1, CPLES_XML);
    1859           1 :         CPLFree(pszTemp);
    1860             :     }
    1861             :     else
    1862        3035 :         pszEscaped = CPLEscapeString(pszString, -1, CPLES_XML);
    1863        3036 :     return pszEscaped;
    1864             : }
    1865             : 
    1866             : /************************************************************************/
    1867             : /*                        OGRCompareDate()                              */
    1868             : /************************************************************************/
    1869             : 
    1870         225 : int OGRCompareDate(const OGRField *psFirstTuple, const OGRField *psSecondTuple)
    1871             : {
    1872             :     // TODO: We ignore TZFlag.
    1873             : 
    1874         225 :     if (psFirstTuple->Date.Year < psSecondTuple->Date.Year)
    1875          12 :         return -1;
    1876         213 :     else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year)
    1877           9 :         return 1;
    1878             : 
    1879         204 :     if (psFirstTuple->Date.Month < psSecondTuple->Date.Month)
    1880           1 :         return -1;
    1881         203 :     else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month)
    1882           6 :         return 1;
    1883             : 
    1884         197 :     if (psFirstTuple->Date.Day < psSecondTuple->Date.Day)
    1885           8 :         return -1;
    1886         189 :     else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day)
    1887           8 :         return 1;
    1888             : 
    1889         181 :     if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour)
    1890           1 :         return -1;
    1891         180 :     else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour)
    1892           8 :         return 1;
    1893             : 
    1894         172 :     if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute)
    1895           0 :         return -1;
    1896         172 :     else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute)
    1897           0 :         return 1;
    1898             : 
    1899         172 :     if (psFirstTuple->Date.Second < psSecondTuple->Date.Second)
    1900           9 :         return -1;
    1901         163 :     else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second)
    1902          11 :         return 1;
    1903             : 
    1904         152 :     return 0;
    1905             : }
    1906             : 
    1907             : /************************************************************************/
    1908             : /*                        OGRFastAtof()                                 */
    1909             : /************************************************************************/
    1910             : 
    1911             : // On Windows, CPLAtof() is very slow if the number is followed by other long
    1912             : // content.  Just extract the number into a short string before calling
    1913             : // CPLAtof() on it.
    1914           0 : static double OGRCallAtofOnShortString(const char *pszStr)
    1915             : {
    1916           0 :     const char *p = pszStr;
    1917           0 :     while (*p == ' ' || *p == '\t')
    1918           0 :         ++p;
    1919             : 
    1920           0 :     char szTemp[128] = {};
    1921           0 :     int nCounter = 0;
    1922           0 :     while (*p == '+' || *p == '-' || (*p >= '0' && *p <= '9') || *p == '.' ||
    1923           0 :            (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D'))
    1924             :     {
    1925           0 :         szTemp[nCounter++] = *(p++);
    1926           0 :         if (nCounter == 127)
    1927           0 :             return CPLAtof(pszStr);
    1928             :     }
    1929           0 :     szTemp[nCounter] = '\0';
    1930           0 :     return CPLAtof(szTemp);
    1931             : }
    1932             : 
    1933             : /** Same contract as CPLAtof, except than it doesn't always call the
    1934             :  *  system CPLAtof() that may be slow on some platforms. For simple but
    1935             :  *  common strings, it'll use a faster implementation (up to 20x faster
    1936             :  *  than CPLAtof() on MS runtime libraries) that has no guaranty to return
    1937             :  *  exactly the same floating point number.
    1938             :  */
    1939             : 
    1940      394317 : double OGRFastAtof(const char *pszStr)
    1941             : {
    1942      394317 :     double dfVal = 0;
    1943      394317 :     double dfSign = 1.0;
    1944      394317 :     const char *p = pszStr;
    1945             : 
    1946      394317 :     constexpr double adfTenPower[] = {
    1947             :         1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,  1e10,
    1948             :         1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,
    1949             :         1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31};
    1950             : 
    1951      394319 :     while (*p == ' ' || *p == '\t')
    1952           2 :         ++p;
    1953             : 
    1954      394317 :     if (*p == '+')
    1955           9 :         ++p;
    1956      394308 :     else if (*p == '-')
    1957             :     {
    1958        1550 :         dfSign = -1.0;
    1959        1550 :         ++p;
    1960             :     }
    1961             : 
    1962             :     while (true)
    1963             :     {
    1964     2602140 :         if (*p >= '0' && *p <= '9')
    1965             :         {
    1966     2207820 :             dfVal = dfVal * 10.0 + (*p - '0');
    1967     2207820 :             ++p;
    1968             :         }
    1969      394317 :         else if (*p == '.')
    1970             :         {
    1971      386926 :             ++p;
    1972      386926 :             break;
    1973             :         }
    1974        7391 :         else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
    1975           0 :             return OGRCallAtofOnShortString(pszStr);
    1976             :         else
    1977        7391 :             return dfSign * dfVal;
    1978             :     }
    1979             : 
    1980      386926 :     unsigned int countFractionnal = 0;
    1981             :     while (true)
    1982             :     {
    1983     1463980 :         if (*p >= '0' && *p <= '9')
    1984             :         {
    1985     1077060 :             dfVal = dfVal * 10.0 + (*p - '0');
    1986     1077060 :             ++countFractionnal;
    1987     1077060 :             ++p;
    1988             :         }
    1989      386926 :         else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
    1990           0 :             return OGRCallAtofOnShortString(pszStr);
    1991             :         else
    1992             :         {
    1993      386926 :             if (countFractionnal < CPL_ARRAYSIZE(adfTenPower))
    1994      386926 :                 return dfSign * (dfVal / adfTenPower[countFractionnal]);
    1995             :             else
    1996           0 :                 return OGRCallAtofOnShortString(pszStr);
    1997             :         }
    1998             :     }
    1999             : }
    2000             : 
    2001             : /**
    2002             :  * Check that panPermutation is a permutation of [0, nSize-1].
    2003             :  * @param panPermutation an array of nSize elements.
    2004             :  * @param nSize size of the array.
    2005             :  * @return OGRERR_NONE if panPermutation is a permutation of [0, nSize - 1].
    2006             :  * @since OGR 1.9.0
    2007             :  */
    2008         145 : OGRErr OGRCheckPermutation(const int *panPermutation, int nSize)
    2009             : {
    2010         145 :     OGRErr eErr = OGRERR_NONE;
    2011         145 :     int *panCheck = static_cast<int *>(CPLCalloc(nSize, sizeof(int)));
    2012         829 :     for (int i = 0; i < nSize; ++i)
    2013             :     {
    2014         691 :         if (panPermutation[i] < 0 || panPermutation[i] >= nSize)
    2015             :         {
    2016           3 :             CPLError(CE_Failure, CPLE_IllegalArg, "Bad value for element %d",
    2017             :                      i);
    2018           3 :             eErr = OGRERR_FAILURE;
    2019           3 :             break;
    2020             :         }
    2021         688 :         if (panCheck[panPermutation[i]] != 0)
    2022             :         {
    2023           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
    2024             :                      "Array is not a permutation of [0,%d]", nSize - 1);
    2025           4 :             eErr = OGRERR_FAILURE;
    2026           4 :             break;
    2027             :         }
    2028         684 :         panCheck[panPermutation[i]] = 1;
    2029             :     }
    2030         145 :     CPLFree(panCheck);
    2031         145 :     return eErr;
    2032             : }
    2033             : 
    2034     1285970 : OGRErr OGRReadWKBGeometryType(const unsigned char *pabyData,
    2035             :                               OGRwkbVariant eWkbVariant,
    2036             :                               OGRwkbGeometryType *peGeometryType)
    2037             : {
    2038     1285970 :     if (!peGeometryType)
    2039           0 :         return OGRERR_FAILURE;
    2040             : 
    2041             :     /* -------------------------------------------------------------------- */
    2042             :     /*      Get the byte order byte.                                        */
    2043             :     /* -------------------------------------------------------------------- */
    2044     1285970 :     int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
    2045     1285970 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    2046         123 :         return OGRERR_CORRUPT_DATA;
    2047     1285850 :     OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    2048             : 
    2049             :     /* -------------------------------------------------------------------- */
    2050             :     /*      Get the geometry type.                                          */
    2051             :     /* -------------------------------------------------------------------- */
    2052     1285850 :     bool bIs3D = false;
    2053     1285850 :     bool bIsMeasured = false;
    2054     1285850 :     int iRawType = 0;
    2055             : 
    2056     1285850 :     memcpy(&iRawType, pabyData + 1, 4);
    2057     1285850 :     if (OGR_SWAP(eByteOrder))
    2058             :     {
    2059        3092 :         CPL_SWAP32PTR(&iRawType);
    2060             :     }
    2061             : 
    2062             :     // Test for M bit in PostGIS WKB, see ogrgeometry.cpp:4956.
    2063     1285850 :     if (0x40000000 & iRawType)
    2064             :     {
    2065        2136 :         iRawType &= ~0x40000000;
    2066        2136 :         bIsMeasured = true;
    2067             :     }
    2068             :     // Old-style OGC z-bit is flipped? Tests also Z bit in PostGIS WKB.
    2069     1285850 :     if (wkb25DBitInternalUse & iRawType)
    2070             :     {
    2071             :         // Clean off top 3 bytes.
    2072        7371 :         iRawType &= 0x000000FF;
    2073        7371 :         bIs3D = true;
    2074             :     }
    2075             : 
    2076             :     // ISO SQL/MM Part3 draft -> Deprecated.
    2077             :     // See http://jtc1sc32.org/doc/N1101-1150/32N1107-WD13249-3--spatial.pdf
    2078     1285850 :     if (iRawType == 1000001)
    2079           0 :         iRawType = wkbCircularString;
    2080     1285850 :     else if (iRawType == 1000002)
    2081           0 :         iRawType = wkbCompoundCurve;
    2082     1285850 :     else if (iRawType == 1000003)
    2083           0 :         iRawType = wkbCurvePolygon;
    2084     1285850 :     else if (iRawType == 1000004)
    2085           0 :         iRawType = wkbMultiCurve;
    2086     1285850 :     else if (iRawType == 1000005)
    2087           0 :         iRawType = wkbMultiSurface;
    2088     1285850 :     else if (iRawType == 2000001)
    2089           0 :         iRawType = wkbPointZM;
    2090     1285850 :     else if (iRawType == 2000002)
    2091           0 :         iRawType = wkbLineStringZM;
    2092     1285850 :     else if (iRawType == 2000003)
    2093           0 :         iRawType = wkbCircularStringZM;
    2094     1285850 :     else if (iRawType == 2000004)
    2095           0 :         iRawType = wkbCompoundCurveZM;
    2096     1285850 :     else if (iRawType == 2000005)
    2097           0 :         iRawType = wkbPolygonZM;
    2098     1285850 :     else if (iRawType == 2000006)
    2099           0 :         iRawType = wkbCurvePolygonZM;
    2100     1285850 :     else if (iRawType == 2000007)
    2101           0 :         iRawType = wkbMultiPointZM;
    2102     1285850 :     else if (iRawType == 2000008)
    2103           0 :         iRawType = wkbMultiCurveZM;
    2104     1285850 :     else if (iRawType == 2000009)
    2105           0 :         iRawType = wkbMultiLineStringZM;
    2106     1285850 :     else if (iRawType == 2000010)
    2107           0 :         iRawType = wkbMultiSurfaceZM;
    2108     1285850 :     else if (iRawType == 2000011)
    2109           0 :         iRawType = wkbMultiPolygonZM;
    2110     1285850 :     else if (iRawType == 2000012)
    2111           0 :         iRawType = wkbGeometryCollectionZM;
    2112     1285850 :     else if (iRawType == 3000001)
    2113           0 :         iRawType = wkbPoint25D;
    2114     1285850 :     else if (iRawType == 3000002)
    2115           0 :         iRawType = wkbLineString25D;
    2116     1285850 :     else if (iRawType == 3000003)
    2117           0 :         iRawType = wkbCircularStringZ;
    2118     1285850 :     else if (iRawType == 3000004)
    2119           0 :         iRawType = wkbCompoundCurveZ;
    2120     1285850 :     else if (iRawType == 3000005)
    2121           0 :         iRawType = wkbPolygon25D;
    2122     1285850 :     else if (iRawType == 3000006)
    2123           0 :         iRawType = wkbCurvePolygonZ;
    2124     1285850 :     else if (iRawType == 3000007)
    2125           0 :         iRawType = wkbMultiPoint25D;
    2126     1285850 :     else if (iRawType == 3000008)
    2127           0 :         iRawType = wkbMultiCurveZ;
    2128     1285850 :     else if (iRawType == 3000009)
    2129           0 :         iRawType = wkbMultiLineString25D;
    2130     1285850 :     else if (iRawType == 3000010)
    2131           0 :         iRawType = wkbMultiSurfaceZ;
    2132     1285850 :     else if (iRawType == 3000011)
    2133           0 :         iRawType = wkbMultiPolygon25D;
    2134     1285850 :     else if (iRawType == 3000012)
    2135           0 :         iRawType = wkbGeometryCollection25D;
    2136     1285850 :     else if (iRawType == 4000001)
    2137           0 :         iRawType = wkbPointM;
    2138     1285850 :     else if (iRawType == 4000002)
    2139           0 :         iRawType = wkbLineStringM;
    2140     1285850 :     else if (iRawType == 4000003)
    2141           0 :         iRawType = wkbCircularStringM;
    2142     1285850 :     else if (iRawType == 4000004)
    2143           0 :         iRawType = wkbCompoundCurveM;
    2144     1285850 :     else if (iRawType == 4000005)
    2145           0 :         iRawType = wkbPolygonM;
    2146     1285850 :     else if (iRawType == 4000006)
    2147           0 :         iRawType = wkbCurvePolygonM;
    2148     1285850 :     else if (iRawType == 4000007)
    2149           0 :         iRawType = wkbMultiPointM;
    2150     1285850 :     else if (iRawType == 4000008)
    2151           0 :         iRawType = wkbMultiCurveM;
    2152     1285850 :     else if (iRawType == 4000009)
    2153           0 :         iRawType = wkbMultiLineStringM;
    2154     1285850 :     else if (iRawType == 4000010)
    2155           0 :         iRawType = wkbMultiSurfaceM;
    2156     1285850 :     else if (iRawType == 4000011)
    2157           0 :         iRawType = wkbMultiPolygonM;
    2158     1285850 :     else if (iRawType == 4000012)
    2159           0 :         iRawType = wkbGeometryCollectionM;
    2160             : 
    2161             :     // Sometimes the Z flag is in the 2nd byte?
    2162     1285850 :     if (iRawType & (wkb25DBitInternalUse >> 16))
    2163             :     {
    2164             :         // Clean off top 3 bytes.
    2165         231 :         iRawType &= 0x000000FF;
    2166         231 :         bIs3D = true;
    2167             :     }
    2168             : 
    2169     1285850 :     if (eWkbVariant == wkbVariantPostGIS1)
    2170             :     {
    2171           0 :         if (iRawType == POSTGIS15_CURVEPOLYGON)
    2172           0 :             iRawType = wkbCurvePolygon;
    2173           0 :         else if (iRawType == POSTGIS15_MULTICURVE)
    2174           0 :             iRawType = wkbMultiCurve;
    2175           0 :         else if (iRawType == POSTGIS15_MULTISURFACE)
    2176           0 :             iRawType = wkbMultiSurface;
    2177             :     }
    2178             : 
    2179             :     // Below additions cannot occur due to clearing higher bits previously
    2180     1285850 :     if (bIs3D)
    2181             :     {
    2182             :         // coverity[overflow_const]
    2183        7602 :         iRawType += 1000;
    2184             :     }
    2185     1285850 :     if (bIsMeasured)
    2186             :     {
    2187             :         // coverity[overflow_const]
    2188        2136 :         iRawType += 2000;
    2189             :     }
    2190             : 
    2191             :     // ISO SQL/MM style types are between 1-17, 1001-1017, 2001-2017, and
    2192             :     // 3001-3017.
    2193     1285850 :     if (!((iRawType > 0 && iRawType <= 17) ||
    2194      163755 :           (iRawType > 1000 && iRawType <= 1017) ||
    2195      156030 :           (iRawType > 2000 && iRawType <= 2017) ||
    2196      154081 :           (iRawType > 3000 && iRawType <= 3017)))
    2197             :     {
    2198        1328 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WKB type %d",
    2199             :                  iRawType);
    2200        1333 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2201             :     }
    2202             : 
    2203             :     // Convert to OGRwkbGeometryType value.
    2204     1284520 :     if (iRawType >= 1001 && iRawType <= 1007)
    2205             :     {
    2206        7553 :         iRawType -= 1000;
    2207        7553 :         iRawType |= wkb25DBitInternalUse;
    2208             :     }
    2209             : 
    2210     1284520 :     *peGeometryType = static_cast<OGRwkbGeometryType>(iRawType);
    2211             : 
    2212     1284520 :     return OGRERR_NONE;
    2213             : }
    2214             : 
    2215             : /************************************************************************/
    2216             : /*                      OGRReadWKTGeometryType()                        */
    2217             : /************************************************************************/
    2218             : 
    2219         415 : OGRErr OGRReadWKTGeometryType(const char *pszWKT,
    2220             :                               OGRwkbGeometryType *peGeometryType)
    2221             : {
    2222         415 :     if (!peGeometryType)
    2223           0 :         return OGRERR_FAILURE;
    2224             : 
    2225         415 :     OGRwkbGeometryType eGeomType = wkbUnknown;
    2226         415 :     if (STARTS_WITH_CI(pszWKT, "POINT"))
    2227          99 :         eGeomType = wkbPoint;
    2228         316 :     else if (STARTS_WITH_CI(pszWKT, "LINESTRING"))
    2229          43 :         eGeomType = wkbLineString;
    2230         273 :     else if (STARTS_WITH_CI(pszWKT, "POLYGON"))
    2231          72 :         eGeomType = wkbPolygon;
    2232         201 :     else if (STARTS_WITH_CI(pszWKT, "MULTIPOINT"))
    2233          31 :         eGeomType = wkbMultiPoint;
    2234         170 :     else if (STARTS_WITH_CI(pszWKT, "MULTILINESTRING"))
    2235          35 :         eGeomType = wkbMultiLineString;
    2236         135 :     else if (STARTS_WITH_CI(pszWKT, "MULTIPOLYGON"))
    2237          67 :         eGeomType = wkbMultiPolygon;
    2238          68 :     else if (STARTS_WITH_CI(pszWKT, "GEOMETRYCOLLECTION"))
    2239          43 :         eGeomType = wkbGeometryCollection;
    2240          25 :     else if (STARTS_WITH_CI(pszWKT, "CIRCULARSTRING"))
    2241           1 :         eGeomType = wkbCircularString;
    2242          24 :     else if (STARTS_WITH_CI(pszWKT, "COMPOUNDCURVE"))
    2243           1 :         eGeomType = wkbCompoundCurve;
    2244          23 :     else if (STARTS_WITH_CI(pszWKT, "CURVEPOLYGON"))
    2245           1 :         eGeomType = wkbCurvePolygon;
    2246          22 :     else if (STARTS_WITH_CI(pszWKT, "MULTICURVE"))
    2247           1 :         eGeomType = wkbMultiCurve;
    2248          21 :     else if (STARTS_WITH_CI(pszWKT, "MULTISURFACE"))
    2249           1 :         eGeomType = wkbMultiSurface;
    2250          20 :     else if (STARTS_WITH_CI(pszWKT, "POLYHEDRALSURFACE"))
    2251           1 :         eGeomType = wkbPolyhedralSurface;
    2252          19 :     else if (STARTS_WITH_CI(pszWKT, "TIN"))
    2253           1 :         eGeomType = wkbTIN;
    2254             :     else
    2255          18 :         return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
    2256             : 
    2257         397 :     if (strstr(pszWKT, " ZM"))
    2258          34 :         eGeomType = OGR_GT_SetModifier(eGeomType, true, true);
    2259         363 :     else if (strstr(pszWKT, " Z"))
    2260          94 :         eGeomType = OGR_GT_SetModifier(eGeomType, true, false);
    2261         269 :     else if (strstr(pszWKT, " M"))
    2262          34 :         eGeomType = OGR_GT_SetModifier(eGeomType, false, true);
    2263             : 
    2264         397 :     *peGeometryType = eGeomType;
    2265             : 
    2266         397 :     return OGRERR_NONE;
    2267             : }
    2268             : 
    2269             : /************************************************************************/
    2270             : /*                        OGRFormatFloat()                              */
    2271             : /************************************************************************/
    2272             : 
    2273          66 : int OGRFormatFloat(char *pszBuffer, int nBufferLen, float fVal, int nPrecision,
    2274             :                    char chConversionSpecifier)
    2275             : {
    2276             :     // So to have identical cross platform representation.
    2277          66 :     if (std::isinf(fVal))
    2278           2 :         return CPLsnprintf(pszBuffer, nBufferLen, (fVal > 0) ? "inf" : "-inf");
    2279          64 :     if (std::isnan(fVal))
    2280           1 :         return CPLsnprintf(pszBuffer, nBufferLen, "nan");
    2281             : 
    2282          63 :     int nSize = 0;
    2283          63 :     char szFormatting[32] = {};
    2284          63 :     constexpr int MAX_SIGNIFICANT_DIGITS_FLOAT32 = 8;
    2285          63 :     const int nInitialSignificantFigures =
    2286          63 :         nPrecision >= 0 ? nPrecision : MAX_SIGNIFICANT_DIGITS_FLOAT32;
    2287             : 
    2288          63 :     CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
    2289             :                 nInitialSignificantFigures, chConversionSpecifier);
    2290          63 :     nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting, fVal);
    2291          63 :     const char *pszDot = strchr(pszBuffer, '.');
    2292             : 
    2293             :     // Try to avoid 0.34999999 or 0.15000001 rounding issues by
    2294             :     // decreasing a bit precision.
    2295          63 :     if (nInitialSignificantFigures >= 8 && pszDot != nullptr &&
    2296          52 :         (strstr(pszDot, "99999") != nullptr ||
    2297          42 :          strstr(pszDot, "00000") != nullptr))
    2298             :     {
    2299          24 :         const CPLString osOriBuffer(pszBuffer, nSize);
    2300             : 
    2301          12 :         bool bOK = false;
    2302          12 :         for (int i = 1; i <= 3; i++)
    2303             :         {
    2304          12 :             CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
    2305             :                         nInitialSignificantFigures - i, chConversionSpecifier);
    2306          12 :             nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting, fVal);
    2307          12 :             pszDot = strchr(pszBuffer, '.');
    2308          12 :             if (pszDot != nullptr && strstr(pszDot, "99999") == nullptr &&
    2309          36 :                 strstr(pszDot, "00000") == nullptr &&
    2310          12 :                 static_cast<float>(CPLAtof(pszBuffer)) == fVal)
    2311             :             {
    2312          12 :                 bOK = true;
    2313          12 :                 break;
    2314             :             }
    2315             :         }
    2316          12 :         if (!bOK)
    2317             :         {
    2318           0 :             memcpy(pszBuffer, osOriBuffer.c_str(), osOriBuffer.size() + 1);
    2319           0 :             nSize = static_cast<int>(osOriBuffer.size());
    2320             :         }
    2321             :     }
    2322             : 
    2323          63 :     if (nSize + 2 < static_cast<int>(nBufferLen) &&
    2324          63 :         strchr(pszBuffer, '.') == nullptr && strchr(pszBuffer, 'e') == nullptr)
    2325             :     {
    2326          10 :         nSize += CPLsnprintf(pszBuffer + nSize, nBufferLen - nSize, ".0");
    2327             :     }
    2328             : 
    2329          63 :     return nSize;
    2330             : }
    2331             : 
    2332        3021 : int OGR_GET_MS(float fSec)
    2333             : {
    2334        3021 :     if (std::isnan(fSec))
    2335           0 :         return 0;
    2336        3021 :     if (fSec >= 999)
    2337           0 :         return 999;
    2338        3021 :     if (fSec <= 0)
    2339         825 :         return 0;
    2340        2196 :     const float fValue = (fSec - static_cast<int>(fSec)) * 1000 + 0.5f;
    2341        2196 :     return static_cast<int>(fValue);
    2342             : }
    2343             : 
    2344             : /************************************************************************/
    2345             : /*                    OGRDuplicateCharacter()                           */
    2346             : /************************************************************************/
    2347             : 
    2348           0 : std::string OGRDuplicateCharacter(const std::string &osStr, char ch)
    2349             : {
    2350           0 :     char aszReplacement[] = {ch, ch, 0};
    2351           0 :     return CPLString(osStr).replaceAll(ch, aszReplacement);
    2352             : }

Generated by: LCOV version 1.14