Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrutils.cpp
Line
Count
Source
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
inline bool CPLIsDoubleAnInt(double d)
43
4.40M
{
44
    // Write it this way to detect NaN
45
4.40M
    if (!(d >= std::numeric_limits<int>::min() &&
46
3.24M
          d <= std::numeric_limits<int>::max()))
47
1.30M
    {
48
1.30M
        return false;
49
1.30M
    }
50
3.09M
    return d == static_cast<double>(static_cast<int>(d));
51
4.40M
}
52
53
namespace
54
{
55
56
// Remove trailing zeros except the last one.
57
void removeTrailingZeros(std::string &s)
58
24.1M
{
59
24.1M
    auto pos = s.find('.');
60
24.1M
    if (pos == std::string::npos)
61
43.5k
        return;
62
63
    // Remove zeros at the end.  We know this won't be npos because we
64
    // have a decimal point.
65
24.0M
    auto nzpos = s.find_last_not_of('0');
66
24.0M
    s = s.substr(0, nzpos + 1);
67
68
    // Make sure there is one 0 after the decimal point.
69
24.0M
    if (s.back() == '.')
70
2.77M
        s += '0';
71
24.0M
}
72
73
// Round a string representing a number by 1 in the least significant digit.
74
void roundup(std::string &s)
75
66.7k
{
76
    // Remove a negative sign if it exists to make processing
77
    // more straightforward.
78
66.7k
    bool negative(false);
79
66.7k
    if (s[0] == '-')
80
19.5k
    {
81
19.5k
        negative = true;
82
19.5k
        s = s.substr(1);
83
19.5k
    }
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
271k
    for (int pos = static_cast<int>(s.size() - 1); pos >= 0; pos--)
90
270k
    {
91
270k
        if (s[pos] == '.')
92
1.63k
            continue;
93
269k
        s[pos]++;
94
95
        // Incrementing past 9 gets you a colon in ASCII.
96
269k
        if (s[pos] != ':')
97
66.0k
            break;
98
203k
        else
99
203k
            s[pos] = '0';
100
203k
        if (pos == 0)
101
725
            s = '1' + s;
102
203k
    }
103
66.7k
    if (negative)
104
19.5k
        s = '-' + s;
105
66.7k
}
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
void intelliround(std::string &s)
111
21.8M
{
112
21.8M
    const size_t len = s.size();
113
114
    // If we don't have ten characters (a bit arbitrary), don't do anything.
115
21.8M
    constexpr size_t MIN_THRESHOLD_FOR_INELLIROUND = 10;
116
21.8M
    if (len <= MIN_THRESHOLD_FOR_INELLIROUND)
117
0
        return;
118
119
    // If there is no decimal point, just return.
120
21.8M
    size_t iDotPos = std::string::npos;
121
21.8M
    size_t i = 0;
122
95.0M
    for (; i < len; ++i)
123
95.0M
    {
124
95.0M
        if (s[i] == '.')
125
21.8M
        {
126
21.8M
            iDotPos = i;
127
21.8M
            break;
128
21.8M
        }
129
95.0M
    }
130
21.8M
    if (iDotPos == std::string::npos)
131
0
        return;
132
372M
    for (; i < len; ++i)
133
350M
    {
134
350M
        if (s[i] == 'e' || s[i] == 'E')
135
0
        {
136
            // Don't mess with exponential formatting.
137
0
            return;
138
0
        }
139
350M
    }
140
141
21.8M
    size_t nCountBeforeDot = iDotPos - 1;
142
21.8M
    if (s[0] == '-')
143
7.94M
        nCountBeforeDot--;
144
145
    /* -------------------------------------------------------------------- */
146
    /*      Trim trailing 00000x's as they are likely roundoff error.       */
147
    /* -------------------------------------------------------------------- */
148
21.8M
    if (s[len - 2] == '0' && s[len - 3] == '0' && s[len - 4] == '0' &&
149
2.83M
        s[len - 5] == '0' && s[len - 6] == '0')
150
2.77M
    {
151
2.77M
        s.pop_back();
152
2.77M
    }
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
19.1M
    else if (iDotPos < len - 8 && (nCountBeforeDot >= 4 || s[len - 3] == '0') &&
162
6.58M
             (nCountBeforeDot >= 5 || s[len - 4] == '0') &&
163
4.40M
             (nCountBeforeDot >= 6 || s[len - 5] == '0') &&
164
2.92M
             (nCountBeforeDot >= 7 || s[len - 6] == '0') &&
165
1.94M
             (nCountBeforeDot >= 8 || s[len - 7] == '0') && s[len - 8] == '0' &&
166
144k
             s[len - 9] == '0')
167
74.0k
    {
168
74.0k
        s.resize(s.size() - 8);
169
74.0k
    }
170
    /* -------------------------------------------------------------------- */
171
    /*      Trim trailing 99999x's as they are likely roundoff error.       */
172
    /* -------------------------------------------------------------------- */
173
19.0M
    else if (s[len - 2] == '9' && s[len - 3] == '9' && s[len - 4] == '9' &&
174
47.3k
             s[len - 5] == '9' && s[len - 6] == '9')
175
19.3k
    {
176
19.3k
        s.resize(len - 6);
177
19.3k
        roundup(s);
178
19.3k
    }
179
19.0M
    else if (iDotPos < len - 9 && (nCountBeforeDot >= 4 || s[len - 3] == '9') &&
180
6.53M
             (nCountBeforeDot >= 5 || s[len - 4] == '9') &&
181
4.34M
             (nCountBeforeDot >= 6 || s[len - 5] == '9') &&
182
2.87M
             (nCountBeforeDot >= 7 || s[len - 6] == '9') &&
183
1.88M
             (nCountBeforeDot >= 8 || s[len - 7] == '9') && s[len - 8] == '9' &&
184
135k
             s[len - 9] == '9')
185
47.3k
    {
186
47.3k
        s.resize(len - 9);
187
47.3k
        roundup(s);
188
47.3k
    }
189
21.8M
}
190
191
}  // unnamed namespace
192
193
/************************************************************************/
194
/*                        OGRFormatDouble()                             */
195
/************************************************************************/
196
197
void OGRFormatDouble(char *pszBuffer, int nBufferLen, double dfVal,
198
                     char chDecimalSep, int nPrecision,
199
                     char chConversionSpecifier)
200
80
{
201
80
    OGRWktOptions opts(nPrecision, OGRWktOptions::getDefaultRound());
202
80
    opts.format = (chConversionSpecifier == 'g' || chConversionSpecifier == 'G')
203
80
                      ? OGRWktFormat::G
204
80
                      : OGRWktFormat::F;
205
206
80
    std::string s = OGRFormatDouble(dfVal, opts, 1);
207
80
    if (chDecimalSep != '\0' && chDecimalSep != '.')
208
0
    {
209
0
        auto pos = s.find('.');
210
0
        if (pos != std::string::npos)
211
0
            s.replace(pos, 1, std::string(1, chDecimalSep));
212
0
    }
213
80
    if (s.size() + 1 > static_cast<size_t>(nBufferLen))
214
27
    {
215
27
        CPLError(CE_Warning, CPLE_AppDefined,
216
27
                 "Truncated double value %s to "
217
27
                 "%s.",
218
27
                 s.data(), s.substr(0, nBufferLen - 1).data());
219
27
        s.resize(nBufferLen - 1);
220
27
    }
221
80
    strcpy(pszBuffer, s.data());
222
80
}
223
224
/// Simplified OGRFormatDouble that can be made to adhere to provided
225
/// options.
226
std::string OGRFormatDouble(double val, const OGRWktOptions &opts, int nDimIdx)
227
24.3M
{
228
    // So to have identical cross platform representation.
229
24.3M
    if (std::isinf(val))
230
2.78k
        return (val > 0) ? "inf" : "-inf";
231
24.3M
    if (std::isnan(val))
232
222k
        return "nan";
233
234
24.1M
    static thread_local std::locale classic_locale = []()
235
24.1M
    { return std::locale::classic(); }();
236
24.1M
    std::ostringstream oss;
237
24.1M
    oss.imbue(classic_locale);  // Make sure we output decimal points.
238
24.1M
    bool l_round(opts.round);
239
24.1M
    if (opts.format == OGRWktFormat::F ||
240
3.85M
        (opts.format == OGRWktFormat::Default && fabs(val) < 1))
241
21.8M
        oss << std::fixed;
242
2.21M
    else
243
2.21M
    {
244
        // Uppercase because OGC spec says capital 'E'.
245
2.21M
        oss << std::uppercase;
246
2.21M
        l_round = false;
247
2.21M
    }
248
24.1M
    oss << std::setprecision(nDimIdx < 3    ? opts.xyPrecision
249
24.1M
                             : nDimIdx == 3 ? opts.zPrecision
250
533k
                                            : opts.mPrecision);
251
24.1M
    oss << val;
252
253
24.1M
    std::string sval = oss.str();
254
255
24.1M
    if (l_round)
256
21.8M
        intelliround(sval);
257
24.1M
    removeTrailingZeros(sval);
258
24.1M
    return sval;
259
24.3M
}
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
void OGRMakeWktCoordinate(char *pszTarget, double x, double y, double z,
273
                          int nDimension)
274
275
151k
{
276
151k
    std::string wkt =
277
151k
        OGRMakeWktCoordinate(x, y, z, nDimension, OGRWktOptions());
278
151k
    memcpy(pszTarget, wkt.data(), wkt.size() + 1);
279
151k
}
280
281
static bool isInteger(const std::string &s)
282
3.53M
{
283
3.53M
    return s.find_first_not_of("0123456789") == std::string::npos;
284
3.53M
}
285
286
std::string OGRMakeWktCoordinate(double x, double y, double z, int nDimension,
287
                                 const OGRWktOptions &opts)
288
930k
{
289
930k
    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
930k
    if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
294
184k
        CPLIsDoubleAnInt(y))
295
101k
    {
296
101k
        wkt = std::to_string(static_cast<int>(x));
297
101k
        wkt += ' ';
298
101k
        wkt += std::to_string(static_cast<int>(y));
299
101k
    }
300
829k
    else
301
829k
    {
302
829k
        wkt = OGRFormatDouble(x, opts, 1);
303
        // ABELL - Why do we do special formatting?
304
829k
        if (isInteger(wkt))
305
2.78k
            wkt += ".0";
306
829k
        wkt += ' ';
307
308
829k
        std::string yval = OGRFormatDouble(y, opts, 2);
309
829k
        if (isInteger(yval))
310
2.93k
            yval += ".0";
311
829k
        wkt += yval;
312
829k
    }
313
314
930k
    if (nDimension == 3)
315
616k
    {
316
616k
        wkt += ' ';
317
616k
        if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
318
443k
            wkt += std::to_string(static_cast<int>(z));
319
172k
        else
320
172k
        {
321
172k
            wkt += OGRFormatDouble(z, opts, 3);
322
172k
        }
323
616k
    }
324
930k
    return wkt;
325
930k
}
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
void OGRMakeWktCoordinateM(char *pszTarget, double x, double y, double z,
339
                           double m, OGRBoolean hasZ, OGRBoolean hasM)
340
341
0
{
342
0
    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
std::string OGRMakeWktCoordinateM(double x, double y, double z, double m,
348
                                  OGRBoolean hasZ, OGRBoolean hasM,
349
                                  const OGRWktOptions &opts)
350
1.13M
{
351
1.13M
    std::string wkt;
352
1.13M
    if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(x) &&
353
257k
        CPLIsDoubleAnInt(y))
354
199k
    {
355
199k
        wkt = std::to_string(static_cast<int>(x));
356
199k
        wkt += ' ';
357
199k
        wkt += std::to_string(static_cast<int>(y));
358
199k
    }
359
939k
    else
360
939k
    {
361
939k
        wkt = OGRFormatDouble(x, opts, 1);
362
939k
        if (isInteger(wkt))
363
28.6k
            wkt += ".0";
364
939k
        wkt += ' ';
365
366
939k
        std::string yval = OGRFormatDouble(y, opts, 2);
367
939k
        if (isInteger(yval))
368
3.45k
            yval += ".0";
369
939k
        wkt += yval;
370
939k
    }
371
372
1.13M
    if (hasZ)
373
731k
    {
374
731k
        wkt += ' ';
375
731k
        if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(z))
376
503k
            wkt += std::to_string(static_cast<int>(z));
377
227k
        else
378
227k
            wkt += OGRFormatDouble(z, opts, 3);
379
731k
    }
380
381
1.13M
    if (hasM)
382
544k
    {
383
544k
        wkt += ' ';
384
544k
        if (opts.format == OGRWktFormat::Default && CPLIsDoubleAnInt(m))
385
403k
            wkt += std::to_string(static_cast<int>(m));
386
140k
        else
387
140k
            wkt += OGRFormatDouble(m, opts, 4);
388
544k
    }
389
1.13M
    return wkt;
390
1.13M
}
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
const char *OGRWktReadToken(const char *pszInput, char *pszToken)
400
401
12.7M
{
402
12.7M
    if (pszInput == nullptr)
403
0
        return nullptr;
404
405
    /* -------------------------------------------------------------------- */
406
    /*      Swallow pre-white space.                                        */
407
    /* -------------------------------------------------------------------- */
408
13.0M
    while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
409
12.7M
           *pszInput == '\r')
410
278k
        ++pszInput;
411
412
    /* -------------------------------------------------------------------- */
413
    /*      If this is a delimiter, read just one character.                */
414
    /* -------------------------------------------------------------------- */
415
12.7M
    if (*pszInput == '(' || *pszInput == ')' || *pszInput == ',')
416
3.07M
    {
417
3.07M
        pszToken[0] = *pszInput;
418
3.07M
        pszToken[1] = '\0';
419
420
3.07M
        ++pszInput;
421
3.07M
    }
422
423
    /* -------------------------------------------------------------------- */
424
    /*      Or if it alpha numeric read till we reach non-alpha numeric     */
425
    /*      text.                                                           */
426
    /* -------------------------------------------------------------------- */
427
9.72M
    else
428
9.72M
    {
429
9.72M
        int iChar = 0;
430
431
40.0M
        while (iChar < OGR_WKT_TOKEN_MAX - 1 &&
432
40.0M
               ((*pszInput >= 'a' && *pszInput <= 'z') ||
433
36.1M
                (*pszInput >= 'A' && *pszInput <= 'Z') ||
434
18.6M
                (*pszInput >= '0' && *pszInput <= '9') || *pszInput == '.' ||
435
10.2M
                *pszInput == '+' || *pszInput == '-'))
436
30.3M
        {
437
30.3M
            pszToken[iChar++] = *(pszInput++);
438
30.3M
        }
439
440
9.72M
        pszToken[iChar++] = '\0';
441
9.72M
    }
442
443
    /* -------------------------------------------------------------------- */
444
    /*      Eat any trailing white space.                                   */
445
    /* -------------------------------------------------------------------- */
446
16.4M
    while (*pszInput == ' ' || *pszInput == '\t' || *pszInput == '\n' ||
447
12.7M
           *pszInput == '\r')
448
3.60M
        ++pszInput;
449
450
12.7M
    return pszInput;
451
12.7M
}
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
const char *OGRWktReadPoints(const char *pszInput, OGRRawPoint **ppaoPoints,
461
                             double **ppadfZ, int *pnMaxPoints,
462
                             int *pnPointsRead)
463
464
0
{
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
0
    {
482
0
        CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPoints().",
483
0
                 pszInput);
484
485
0
        return pszInput;
486
0
    }
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
0
    {
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
0
        {
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
0
            {
529
0
                *ppadfZ = static_cast<double *>(
530
0
                    CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
531
0
            }
532
0
        }
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
0
        {
552
0
            if (*ppadfZ == nullptr)
553
0
            {
554
0
                *ppadfZ = static_cast<double *>(
555
0
                    CPLCalloc(sizeof(double), *pnMaxPoints));
556
0
            }
557
558
0
            (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
559
560
0
            pszInput = OGRWktReadToken(pszInput, szDelim);
561
0
        }
562
0
        else if (*ppadfZ != nullptr)
563
0
        {
564
0
            (*ppadfZ)[*pnPointsRead] = 0.0;
565
0
        }
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
0
        {
578
0
            pszInput = OGRWktReadToken(pszInput, szDelim);
579
0
        }
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
0
        {
589
0
            CPLDebug("OGR",
590
0
                     "Corrupt input in OGRWktReadPoints().  "
591
0
                     "Got `%s' when expecting `,' or `)', near `%s' in %s.",
592
0
                     szDelim, pszInput, pszOrigInput);
593
0
            return nullptr;
594
0
        }
595
0
    } while (szDelim[0] == ',');
596
597
0
    return pszInput;
598
0
}
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
const char *OGRWktReadPointsM(const char *pszInput, OGRRawPoint **ppaoPoints,
608
                              double **ppadfZ, double **ppadfM, int *flags,
609
                              int *pnMaxPoints, int *pnPointsRead)
610
611
485k
{
612
485k
    const char *pszOrigInput = pszInput;
613
485k
    const bool bNoFlags = !(*flags & OGRGeometry::OGR_G_3D) &&
614
330k
                          !(*flags & OGRGeometry::OGR_G_MEASURED);
615
485k
    *pnPointsRead = 0;
616
617
485k
    if (pszInput == nullptr)
618
0
        return nullptr;
619
620
    /* -------------------------------------------------------------------- */
621
    /*      Eat any leading white space.                                    */
622
    /* -------------------------------------------------------------------- */
623
485k
    while (*pszInput == ' ' || *pszInput == '\t')
624
0
        ++pszInput;
625
626
    /* -------------------------------------------------------------------- */
627
    /*      If this isn't an opening bracket then we have a problem.        */
628
    /* -------------------------------------------------------------------- */
629
485k
    if (*pszInput != '(')
630
30.5k
    {
631
30.5k
        CPLDebug("OGR", "Expected '(', but got %s in OGRWktReadPointsM().",
632
30.5k
                 pszInput);
633
634
30.5k
        return pszInput;
635
30.5k
    }
636
637
454k
    ++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
454k
    char szDelim[OGR_WKT_TOKEN_MAX] = {};
645
646
454k
    do
647
1.22M
    {
648
        /* --------------------------------------------------------------------
649
         */
650
        /*      Read the X and Y values, verify they are numeric. */
651
        /* --------------------------------------------------------------------
652
         */
653
1.22M
        char szTokenX[OGR_WKT_TOKEN_MAX] = {};
654
1.22M
        char szTokenY[OGR_WKT_TOKEN_MAX] = {};
655
656
1.22M
        pszInput = OGRWktReadToken(pszInput, szTokenX);
657
1.22M
        pszInput = OGRWktReadToken(pszInput, szTokenY);
658
659
1.22M
        if ((!isdigit(static_cast<unsigned char>(szTokenX[0])) &&
660
140k
             szTokenX[0] != '-' && szTokenX[0] != '.' &&
661
39.5k
             !EQUAL(szTokenX, "nan")) ||
662
1.18M
            (!isdigit(static_cast<unsigned char>(szTokenY[0])) &&
663
117k
             szTokenY[0] != '-' && szTokenY[0] != '.' &&
664
51.6k
             !EQUAL(szTokenY, "nan")))
665
81.1k
            return nullptr;
666
667
        /* --------------------------------------------------------------------
668
         */
669
        /*      Do we need to grow the point list to hold this point? */
670
        /* --------------------------------------------------------------------
671
         */
672
1.13M
        if (*pnPointsRead == *pnMaxPoints)
673
227k
        {
674
227k
            *pnMaxPoints = *pnMaxPoints * 2 + 10;
675
227k
            *ppaoPoints = static_cast<OGRRawPoint *>(
676
227k
                CPLRealloc(*ppaoPoints, sizeof(OGRRawPoint) * *pnMaxPoints));
677
678
227k
            if (*ppadfZ != nullptr)
679
10.6k
            {
680
10.6k
                *ppadfZ = static_cast<double *>(
681
10.6k
                    CPLRealloc(*ppadfZ, sizeof(double) * *pnMaxPoints));
682
10.6k
            }
683
684
227k
            if (*ppadfM != nullptr)
685
4.17k
            {
686
4.17k
                *ppadfM = static_cast<double *>(
687
4.17k
                    CPLRealloc(*ppadfM, sizeof(double) * *pnMaxPoints));
688
4.17k
            }
689
227k
        }
690
691
        /* --------------------------------------------------------------------
692
         */
693
        /*      Add point to list. */
694
        /* --------------------------------------------------------------------
695
         */
696
1.13M
        (*ppaoPoints)[*pnPointsRead].x = CPLAtof(szTokenX);
697
1.13M
        (*ppaoPoints)[*pnPointsRead].y = CPLAtof(szTokenY);
698
699
        /* --------------------------------------------------------------------
700
         */
701
        /*      Read the next token. */
702
        /* --------------------------------------------------------------------
703
         */
704
1.13M
        pszInput = OGRWktReadToken(pszInput, szDelim);
705
706
        /* --------------------------------------------------------------------
707
         */
708
        /*      If there are unexpectedly more coordinates, they are Z. */
709
        /* --------------------------------------------------------------------
710
         */
711
712
1.13M
        if (!(*flags & OGRGeometry::OGR_G_3D) &&
713
618k
            !(*flags & OGRGeometry::OGR_G_MEASURED) &&
714
402k
            (isdigit(static_cast<unsigned char>(szDelim[0])) ||
715
342k
             szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
716
81.8k
        {
717
81.8k
            *flags |= OGRGeometry::OGR_G_3D;
718
81.8k
        }
719
720
        /* --------------------------------------------------------------------
721
         */
722
        /*      Get Z if flag says so. */
723
        /*      Zero out possible remains from earlier strings. */
724
        /* --------------------------------------------------------------------
725
         */
726
727
1.13M
        if (*flags & OGRGeometry::OGR_G_3D)
728
601k
        {
729
601k
            if (*ppadfZ == nullptr)
730
86.3k
            {
731
86.3k
                *ppadfZ = static_cast<double *>(
732
86.3k
                    CPLCalloc(sizeof(double), *pnMaxPoints));
733
86.3k
            }
734
601k
            if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
735
502k
                szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
736
129k
            {
737
129k
                (*ppadfZ)[*pnPointsRead] = CPLAtof(szDelim);
738
129k
                pszInput = OGRWktReadToken(pszInput, szDelim);
739
129k
            }
740
472k
            else
741
472k
            {
742
472k
                (*ppadfZ)[*pnPointsRead] = 0.0;
743
472k
            }
744
601k
        }
745
537k
        else if (*ppadfZ != nullptr)
746
55.2k
        {
747
55.2k
            (*ppadfZ)[*pnPointsRead] = 0.0;
748
55.2k
        }
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
1.13M
        if (!(*flags & OGRGeometry::OGR_G_MEASURED) &&
759
706k
            (isdigit(static_cast<unsigned char>(szDelim[0])) ||
760
676k
             szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
761
43.4k
        {
762
43.4k
            if (bNoFlags)
763
38.7k
            {
764
38.7k
                *flags |= OGRGeometry::OGR_G_MEASURED;
765
38.7k
            }
766
4.74k
            else
767
4.74k
            {
768
4.74k
                pszInput = OGRWktReadToken(pszInput, szDelim);
769
4.74k
            }
770
43.4k
        }
771
772
        /* --------------------------------------------------------------------
773
         */
774
        /*      Get M if flag says so. */
775
        /*      Zero out possible remains from earlier strings. */
776
        /* --------------------------------------------------------------------
777
         */
778
779
1.13M
        if (*flags & OGRGeometry::OGR_G_MEASURED)
780
471k
        {
781
471k
            if (*ppadfM == nullptr)
782
133k
            {
783
133k
                *ppadfM = static_cast<double *>(
784
133k
                    CPLCalloc(sizeof(double), *pnMaxPoints));
785
133k
            }
786
471k
            if (isdigit(static_cast<unsigned char>(szDelim[0])) ||
787
368k
                szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan"))
788
123k
            {
789
123k
                (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
790
123k
                pszInput = OGRWktReadToken(pszInput, szDelim);
791
123k
            }
792
348k
            else
793
348k
            {
794
348k
                (*ppadfM)[*pnPointsRead] = 0.0;
795
348k
            }
796
471k
        }
797
667k
        else if (*ppadfM != nullptr)
798
0
        {
799
0
            (*ppadfM)[*pnPointsRead] = 0.0;
800
0
        }
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
1.13M
        if (!(*flags & OGRGeometry::OGR_G_3D) &&
811
537k
            (isdigit(static_cast<unsigned char>(szDelim[0])) ||
812
519k
             szDelim[0] == '-' || szDelim[0] == '.' || EQUAL(szDelim, "nan")))
813
27.1k
        {
814
27.1k
            *flags |= OGRGeometry::OGR_G_3D;
815
27.1k
            if (*ppadfZ == nullptr)
816
25.3k
            {
817
25.3k
                *ppadfZ = static_cast<double *>(
818
25.3k
                    CPLCalloc(sizeof(double), *pnMaxPoints));
819
25.3k
            }
820
27.1k
            (*ppadfZ)[*pnPointsRead] = (*ppadfM)[*pnPointsRead];
821
27.1k
            (*ppadfM)[*pnPointsRead] = CPLAtof(szDelim);
822
27.1k
            pszInput = OGRWktReadToken(pszInput, szDelim);
823
27.1k
        }
824
825
        /* --------------------------------------------------------------------
826
         */
827
        /*      Increase points index. */
828
        /* --------------------------------------------------------------------
829
         */
830
1.13M
        ++(*pnPointsRead);
831
832
        /* --------------------------------------------------------------------
833
         */
834
        /*      The next delimiter should be a comma or an ending bracket. */
835
        /* --------------------------------------------------------------------
836
         */
837
1.13M
        if (szDelim[0] != ')' && szDelim[0] != ',')
838
69.7k
        {
839
69.7k
            CPLDebug("OGR",
840
69.7k
                     "Corrupt input in OGRWktReadPointsM()  "
841
69.7k
                     "Got `%s' when expecting `,' or `)', near `%s' in %s.",
842
69.7k
                     szDelim, pszInput, pszOrigInput);
843
69.7k
            return nullptr;
844
69.7k
        }
845
1.13M
    } while (szDelim[0] == ',');
846
847
303k
    return pszInput;
848
454k
}
849
850
/************************************************************************/
851
/*                             OGRMalloc()                              */
852
/*                                                                      */
853
/*      Cover for CPLMalloc()                                           */
854
/************************************************************************/
855
856
void *OGRMalloc(size_t size)
857
858
0
{
859
0
    return CPLMalloc(size);
860
0
}
861
862
/************************************************************************/
863
/*                             OGRCalloc()                              */
864
/*                                                                      */
865
/*      Cover for CPLCalloc()                                           */
866
/************************************************************************/
867
868
void *OGRCalloc(size_t count, size_t size)
869
870
0
{
871
0
    return CPLCalloc(count, size);
872
0
}
873
874
/************************************************************************/
875
/*                             OGRRealloc()                             */
876
/*                                                                      */
877
/*      Cover for CPLRealloc()                                          */
878
/************************************************************************/
879
880
void *OGRRealloc(void *pOld, size_t size)
881
882
0
{
883
0
    return CPLRealloc(pOld, size);
884
0
}
885
886
/************************************************************************/
887
/*                              OGRFree()                               */
888
/*                                                                      */
889
/*      Cover for CPLFree().                                            */
890
/************************************************************************/
891
892
void OGRFree(void *pMemory)
893
894
0
{
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
int OGRGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
940
                               CPL_UNUSED int nOptions)
941
942
0
{
943
0
    return GDALGeneralCmdLineProcessor(nArgc, ppapszArgv, GDAL_OF_VECTOR);
944
0
}
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
int OGRTimezoneToTZFlag(const char *pszTZ, bool bEmitErrorIfUnhandledFormat)
958
0
{
959
0
    int nTZFlag = OGR_TZFLAG_UNKNOWN;
960
0
    const size_t nTZLen = strlen(pszTZ);
961
0
    if (strcmp(pszTZ, "UTC") == 0 || strcmp(pszTZ, "Etc/UTC") == 0)
962
0
    {
963
0
        nTZFlag = OGR_TZFLAG_UTC;
964
0
    }
965
0
    else if ((pszTZ[0] == '+' || pszTZ[0] == '-') &&
966
0
             ((nTZLen == 6 && pszTZ[3] == ':') ||
967
0
              (nTZLen == 5 && pszTZ[3] >= '0' && pszTZ[3] <= '9')))
968
0
    {
969
0
        int nTZHour = atoi(pszTZ + 1);
970
0
        int nTZMin = atoi(pszTZ + (nTZLen == 6 ? 4 : 3));
971
0
        if (nTZHour >= 0 && nTZHour <= 14 && nTZMin >= 0 && nTZMin < 60 &&
972
0
            (nTZMin % 15) == 0)
973
0
        {
974
0
            nTZFlag = (nTZHour * 4) + (nTZMin / 15);
975
0
            if (pszTZ[0] == '+')
976
0
            {
977
0
                nTZFlag = OGR_TZFLAG_UTC + nTZFlag;
978
0
            }
979
0
            else
980
0
            {
981
0
                nTZFlag = OGR_TZFLAG_UTC - nTZFlag;
982
0
            }
983
0
        }
984
0
    }
985
0
    else if (pszTZ[0] != 0 && bEmitErrorIfUnhandledFormat)
986
0
    {
987
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized timezone: '%s'",
988
0
                 pszTZ);
989
0
    }
990
0
    return nTZFlag;
991
0
}
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
std::string OGRTZFlagToTimezone(int nTZFlag, const char *pszUTCRepresentation)
1005
0
{
1006
0
    if (nTZFlag == OGR_TZFLAG_UTC)
1007
0
    {
1008
0
        return pszUTCRepresentation;
1009
0
    }
1010
0
    else if (nTZFlag > OGR_TZFLAG_MIXED_TZ)
1011
0
    {
1012
0
        char chSign;
1013
0
        const int nOffset = (nTZFlag - OGR_TZFLAG_UTC) * 15;
1014
0
        int nHours = static_cast<int>(nOffset / 60);  // Round towards zero.
1015
0
        const int nMinutes = std::abs(nOffset - nHours * 60);
1016
1017
0
        if (nOffset < 0)
1018
0
        {
1019
0
            chSign = '-';
1020
0
            nHours = std::abs(nHours);
1021
0
        }
1022
0
        else
1023
0
        {
1024
0
            chSign = '+';
1025
0
        }
1026
0
        return CPLSPrintf("%c%02d:%02d", chSign, nHours, nMinutes);
1027
0
    }
1028
0
    else
1029
0
    {
1030
0
        return std::string();
1031
0
    }
1032
0
}
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
int OGRParseDate(const char *pszInput, OGRField *psField, int nOptions)
1073
1.64M
{
1074
1.64M
    psField->Date.Year = 0;
1075
1.64M
    psField->Date.Month = 0;
1076
1.64M
    psField->Date.Day = 0;
1077
1.64M
    psField->Date.Hour = 0;
1078
1.64M
    psField->Date.Minute = 0;
1079
1.64M
    psField->Date.Second = 0;
1080
1.64M
    psField->Date.TZFlag = 0;
1081
1.64M
    psField->Date.Reserved = 0;
1082
1083
    /* -------------------------------------------------------------------- */
1084
    /*      Do we have a date?                                              */
1085
    /* -------------------------------------------------------------------- */
1086
1.69M
    for (int i = 0; i < 256 && *pszInput == ' '; ++i)
1087
45.4k
        ++pszInput;
1088
1089
1.64M
    bool bGotSomething = false;
1090
1.64M
    bool bTFound = false;
1091
1.64M
    if (strchr(pszInput, '-') || strchr(pszInput, '/'))
1092
764k
    {
1093
764k
        if (!(*pszInput == '-' || *pszInput == '+' ||
1094
728k
              (*pszInput >= '0' && *pszInput <= '9')))
1095
322k
            return FALSE;
1096
441k
        int nYear = atoi(pszInput);
1097
441k
        if (nYear > std::numeric_limits<GInt16>::max() ||
1098
436k
            nYear < std::numeric_limits<GInt16>::min())
1099
8.74k
        {
1100
8.74k
            CPLError(CE_Failure, CPLE_NotSupported,
1101
8.74k
                     "Years < %d or > %d are not supported",
1102
8.74k
                     std::numeric_limits<GInt16>::min(),
1103
8.74k
                     std::numeric_limits<GInt16>::max());
1104
8.74k
            return FALSE;
1105
8.74k
        }
1106
432k
        psField->Date.Year = static_cast<GInt16>(nYear);
1107
432k
        if ((pszInput[1] == '-' || pszInput[1] == '/') ||
1108
358k
            (pszInput[1] != '\0' && (pszInput[2] == '-' || pszInput[2] == '/')))
1109
104k
        {
1110
104k
            if (psField->Date.Year < 100 && psField->Date.Year >= 30)
1111
3.19k
                psField->Date.Year += 1900;
1112
101k
            else if (psField->Date.Year < 30 && psField->Date.Year >= 0)
1113
93.0k
                psField->Date.Year += 2000;
1114
104k
        }
1115
1116
432k
        if (*pszInput == '-')
1117
29.2k
            ++pszInput;
1118
1.70M
        for (int i = 0; i < 5 && *pszInput >= '0' && *pszInput <= '9'; ++i)
1119
1.27M
            ++pszInput;
1120
432k
        if (*pszInput != '-' && *pszInput != '/')
1121
33.2k
            return FALSE;
1122
399k
        else
1123
399k
            ++pszInput;
1124
1125
399k
        if (!(*pszInput >= '0' && *pszInput <= '9'))
1126
68.4k
            return FALSE;
1127
331k
        if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
1128
63.1k
        {
1129
63.1k
            if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
1130
63.1k
                return FALSE;
1131
0
            const int nMonth = (pszInput[0] - '0');
1132
0
            if (nMonth == 0)
1133
0
                return FALSE;
1134
0
            psField->Date.Month = static_cast<GByte>(nMonth);
1135
0
            ++pszInput;
1136
0
        }
1137
268k
        else
1138
268k
        {
1139
268k
            const int nMonth = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
1140
268k
            if (nMonth == 0 || nMonth > 12)
1141
10.6k
                return FALSE;
1142
257k
            psField->Date.Month = static_cast<GByte>(nMonth);
1143
1144
257k
            pszInput += 2;
1145
257k
        }
1146
257k
        if (*pszInput != '-' && *pszInput != '/')
1147
22.2k
            return FALSE;
1148
235k
        else
1149
235k
            ++pszInput;
1150
1151
235k
        if (!(*pszInput >= '0' && *pszInput <= '9'))
1152
10.4k
            return FALSE;
1153
224k
        if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
1154
13.0k
        {
1155
13.0k
            if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
1156
13.0k
                return FALSE;
1157
0
            const int nDay = (pszInput[0] - '0');
1158
0
            if (nDay == 0)
1159
0
                return FALSE;
1160
0
            psField->Date.Day = static_cast<GByte>(nDay);
1161
0
            ++pszInput;
1162
0
        }
1163
211k
        else
1164
211k
        {
1165
211k
            const int nDay = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
1166
211k
            if (nDay == 0 || nDay > 31)
1167
11.1k
                return FALSE;
1168
200k
            psField->Date.Day = static_cast<GByte>(nDay);
1169
1170
200k
            pszInput += 2;
1171
200k
        }
1172
200k
        if (*pszInput == '\0')
1173
6.26k
            return TRUE;
1174
1175
194k
        bGotSomething = true;
1176
1177
        // If ISO 8601 format.
1178
194k
        if (*pszInput == 'T')
1179
170k
        {
1180
170k
            bTFound = true;
1181
170k
            ++pszInput;
1182
170k
        }
1183
23.9k
        else if (*pszInput == 'Z')
1184
846
            return TRUE;
1185
23.0k
        else if (*pszInput != ' ')
1186
8.99k
            return FALSE;
1187
194k
    }
1188
1189
    /* -------------------------------------------------------------------- */
1190
    /*      Do we have a time?                                              */
1191
    /* -------------------------------------------------------------------- */
1192
1.31M
    for (int i = 0; i < 256 && *pszInput == ' '; ++i)
1193
245k
        ++pszInput;
1194
1.06M
    if (*pszInput == 'T')
1195
5.20k
    {
1196
5.20k
        bTFound = true;
1197
5.20k
        ++pszInput;
1198
5.20k
    }
1199
1200
1.06M
    if (bTFound || strchr(pszInput, ':'))
1201
354k
    {
1202
354k
        if (!(*pszInput >= '0' && *pszInput <= '9'))
1203
107k
            return FALSE;
1204
247k
        if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
1205
59.6k
        {
1206
59.6k
            if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
1207
59.6k
                return FALSE;
1208
1209
0
            if (!((bTFound || pszInput[1] == ':')))
1210
0
                return FALSE;
1211
0
            const int nHour = (pszInput[0] - '0');
1212
0
            psField->Date.Hour = static_cast<GByte>(nHour);
1213
1214
0
            pszInput++;
1215
0
        }
1216
187k
        else
1217
187k
        {
1218
187k
            if (!((bTFound || pszInput[2] == ':')))
1219
5.13k
                return FALSE;
1220
182k
            const int nHour = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
1221
182k
            if (nHour > 23)
1222
6.95k
                return FALSE;
1223
175k
            psField->Date.Hour = static_cast<GByte>(nHour);
1224
1225
175k
            pszInput += 2;
1226
175k
        }
1227
175k
        if (*pszInput == ':')
1228
154k
            ++pszInput;
1229
1230
175k
        if (!(*pszInput >= '0' && *pszInput <= '9'))
1231
34.4k
            return FALSE;
1232
140k
        if (!(pszInput[1] >= '0' && pszInput[1] <= '9'))
1233
19.7k
        {
1234
19.7k
            if ((nOptions & OGRPARSEDATE_OPTION_LAX) == 0)
1235
19.7k
                return FALSE;
1236
1237
0
            const int nMinute = (pszInput[0] - '0');
1238
0
            psField->Date.Minute = static_cast<GByte>(nMinute);
1239
1240
0
            pszInput++;
1241
0
        }
1242
121k
        else
1243
121k
        {
1244
121k
            const int nMinute = (pszInput[0] - '0') * 10 + (pszInput[1] - '0');
1245
121k
            if (nMinute > 59)
1246
2.67k
                return FALSE;
1247
118k
            psField->Date.Minute = static_cast<GByte>(nMinute);
1248
1249
118k
            pszInput += 2;
1250
118k
        }
1251
1252
118k
        if ((bTFound && *pszInput >= '0' && *pszInput <= '9') ||
1253
101k
            *pszInput == ':')
1254
104k
        {
1255
104k
            if (*pszInput == ':')
1256
87.4k
                ++pszInput;
1257
1258
104k
            if (!(*pszInput >= '0' && *pszInput <= '9' &&
1259
71.2k
                  (((nOptions & OGRPARSEDATE_OPTION_LAX) != 0) ||
1260
71.2k
                   (pszInput[1] >= '0' && pszInput[1] <= '9'))))
1261
36.4k
                return FALSE;
1262
68.1k
            const double dfSeconds = CPLAtof(pszInput);
1263
            // We accept second=60 for leap seconds
1264
68.1k
            if (dfSeconds >= 61.0)
1265
7.96k
                return FALSE;
1266
60.2k
            psField->Date.Second = static_cast<float>(dfSeconds);
1267
1268
            // Avoid rounding 59.999xxx to 60.0f (or set second to zero and
1269
            // increment the minute value) where x is 9, but round to 59.999 as
1270
            // the maximum value representable on a float.
1271
60.2k
            if (pszInput[0] == '5' && pszInput[1] == '9' &&
1272
338
                pszInput[2] == '.' && pszInput[3] == '9' &&
1273
3
                psField->Date.Second == 60.000f)
1274
0
            {
1275
0
                psField->Date.Second = 59.999f;
1276
0
            }
1277
1278
60.2k
            if ((nOptions & OGRPARSEDATE_OPTION_LAX) != 0 &&
1279
0
                !(pszInput[1] >= '0' && pszInput[1] <= '9'))
1280
0
            {
1281
0
                ++pszInput;
1282
0
            }
1283
60.2k
            else
1284
60.2k
            {
1285
60.2k
                pszInput += 2;
1286
60.2k
            }
1287
60.2k
            if (*pszInput == '.')
1288
4.03k
            {
1289
4.03k
                ++pszInput;
1290
123k
                while (*pszInput >= '0' && *pszInput <= '9')
1291
119k
                {
1292
119k
                    ++pszInput;
1293
119k
                }
1294
4.03k
            }
1295
1296
            // If ISO 8601 format.
1297
60.2k
            if (*pszInput == 'Z')
1298
2.56k
            {
1299
2.56k
                psField->Date.TZFlag = 100;
1300
2.56k
            }
1301
60.2k
        }
1302
1303
74.0k
        bGotSomething = true;
1304
74.0k
    }
1305
710k
    else if (bGotSomething && *pszInput != '\0')
1306
4.68k
        return FALSE;
1307
1308
    // No date or time!
1309
780k
    if (!bGotSomething)
1310
703k
        return FALSE;
1311
1312
    /* -------------------------------------------------------------------- */
1313
    /*      Do we have a timezone?                                          */
1314
    /* -------------------------------------------------------------------- */
1315
80.9k
    while (*pszInput == ' ')
1316
4.37k
        ++pszInput;
1317
1318
76.5k
    if (*pszInput == '-' || *pszInput == '+')
1319
53.0k
    {
1320
        // +HH integral offset
1321
53.0k
        if (strlen(pszInput) <= 3)
1322
2.85k
        {
1323
2.85k
            psField->Date.TZFlag = static_cast<GByte>(100 + atoi(pszInput) * 4);
1324
2.85k
        }
1325
50.2k
        else if (pszInput[3] == ':'  // +HH:MM offset
1326
34.6k
                 && atoi(pszInput + 4) % 15 == 0)
1327
28.9k
        {
1328
28.9k
            psField->Date.TZFlag = static_cast<GByte>(
1329
28.9k
                100 + atoi(pszInput + 1) * 4 + (atoi(pszInput + 4) / 15));
1330
1331
28.9k
            if (pszInput[0] == '-')
1332
3.72k
                psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1333
28.9k
        }
1334
21.2k
        else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
1335
13.9k
                 isdigit(
1336
13.9k
                     static_cast<unsigned char>(pszInput[4]))  // +HHMM offset
1337
7.32k
                 && atoi(pszInput + 3) % 15 == 0)
1338
5.34k
        {
1339
5.34k
            psField->Date.TZFlag = static_cast<GByte>(
1340
5.34k
                100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 2)) * 4 +
1341
5.34k
                (atoi(pszInput + 3) / 15));
1342
1343
5.34k
            if (pszInput[0] == '-')
1344
2.10k
                psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1345
5.34k
        }
1346
15.9k
        else if (isdigit(static_cast<unsigned char>(pszInput[3])) &&
1347
8.64k
                 pszInput[4] == '\0'  // +HMM offset
1348
5.87k
                 && atoi(pszInput + 2) % 15 == 0)
1349
2.30k
        {
1350
2.30k
            psField->Date.TZFlag = static_cast<GByte>(
1351
2.30k
                100 + static_cast<GByte>(CPLScanLong(pszInput + 1, 1)) * 4 +
1352
2.30k
                (atoi(pszInput + 2) / 15));
1353
1354
2.30k
            if (pszInput[0] == '-')
1355
585
                psField->Date.TZFlag = -1 * (psField->Date.TZFlag - 100) + 100;
1356
2.30k
        }
1357
        // otherwise ignore any timezone info.
1358
53.0k
    }
1359
1360
76.5k
    return TRUE;
1361
780k
}
1362
1363
/************************************************************************/
1364
/*               OGRParseDateTimeYYYYMMDDTHHMMZ()                       */
1365
/************************************************************************/
1366
1367
bool OGRParseDateTimeYYYYMMDDTHHMMZ(std::string_view sInput, OGRField *psField)
1368
20
{
1369
    // Detect "YYYY-MM-DDTHH:MM[Z]" (16 or 17 characters)
1370
20
    if ((sInput.size() == 16 || (sInput.size() == 17 && sInput[16] == 'Z')) &&
1371
0
        sInput[4] == '-' && sInput[7] == '-' && sInput[10] == 'T' &&
1372
0
        sInput[13] == ':' && static_cast<unsigned>(sInput[0] - '0') <= 9 &&
1373
0
        static_cast<unsigned>(sInput[1] - '0') <= 9 &&
1374
0
        static_cast<unsigned>(sInput[2] - '0') <= 9 &&
1375
0
        static_cast<unsigned>(sInput[3] - '0') <= 9 &&
1376
0
        static_cast<unsigned>(sInput[5] - '0') <= 9 &&
1377
0
        static_cast<unsigned>(sInput[6] - '0') <= 9 &&
1378
0
        static_cast<unsigned>(sInput[8] - '0') <= 9 &&
1379
0
        static_cast<unsigned>(sInput[9] - '0') <= 9 &&
1380
0
        static_cast<unsigned>(sInput[11] - '0') <= 9 &&
1381
0
        static_cast<unsigned>(sInput[12] - '0') <= 9 &&
1382
0
        static_cast<unsigned>(sInput[14] - '0') <= 9 &&
1383
0
        static_cast<unsigned>(sInput[15] - '0') <= 9)
1384
0
    {
1385
0
        psField->Date.Year = static_cast<GInt16>(
1386
0
            ((((sInput[0] - '0') * 10 + (sInput[1] - '0')) * 10) +
1387
0
             (sInput[2] - '0')) *
1388
0
                10 +
1389
0
            (sInput[3] - '0'));
1390
0
        psField->Date.Month =
1391
0
            static_cast<GByte>((sInput[5] - '0') * 10 + (sInput[6] - '0'));
1392
0
        psField->Date.Day =
1393
0
            static_cast<GByte>((sInput[8] - '0') * 10 + (sInput[9] - '0'));
1394
0
        psField->Date.Hour =
1395
0
            static_cast<GByte>((sInput[11] - '0') * 10 + (sInput[12] - '0'));
1396
0
        psField->Date.Minute =
1397
0
            static_cast<GByte>((sInput[14] - '0') * 10 + (sInput[15] - '0'));
1398
0
        psField->Date.Second = 0.0f;
1399
0
        psField->Date.TZFlag = sInput.size() == 16 ? 0 : 100;
1400
0
        psField->Date.Reserved = 0;
1401
0
        if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
1402
0
            psField->Date.Day == 0 || psField->Date.Day > 31 ||
1403
0
            psField->Date.Hour > 23 || psField->Date.Minute > 59)
1404
0
        {
1405
0
            return false;
1406
0
        }
1407
0
        return true;
1408
0
    }
1409
1410
20
    return false;
1411
20
}
1412
1413
/************************************************************************/
1414
/*               OGRParseDateTimeYYYYMMDDTHHMMSSZ()                     */
1415
/************************************************************************/
1416
1417
bool OGRParseDateTimeYYYYMMDDTHHMMSSZ(std::string_view sInput,
1418
                                      OGRField *psField)
1419
187
{
1420
    // Detect "YYYY-MM-DDTHH:MM:SS[Z]" (19 or 20 characters)
1421
187
    if ((sInput.size() == 19 || (sInput.size() == 20 && sInput[19] == 'Z')) &&
1422
17
        sInput[4] == '-' && sInput[7] == '-' && sInput[10] == 'T' &&
1423
14
        sInput[13] == ':' && sInput[16] == ':' &&
1424
13
        static_cast<unsigned>(sInput[0] - '0') <= 9 &&
1425
12
        static_cast<unsigned>(sInput[1] - '0') <= 9 &&
1426
11
        static_cast<unsigned>(sInput[2] - '0') <= 9 &&
1427
11
        static_cast<unsigned>(sInput[3] - '0') <= 9 &&
1428
10
        static_cast<unsigned>(sInput[5] - '0') <= 9 &&
1429
9
        static_cast<unsigned>(sInput[6] - '0') <= 9 &&
1430
8
        static_cast<unsigned>(sInput[8] - '0') <= 9 &&
1431
7
        static_cast<unsigned>(sInput[9] - '0') <= 9 &&
1432
6
        static_cast<unsigned>(sInput[11] - '0') <= 9 &&
1433
6
        static_cast<unsigned>(sInput[12] - '0') <= 9 &&
1434
5
        static_cast<unsigned>(sInput[14] - '0') <= 9 &&
1435
5
        static_cast<unsigned>(sInput[15] - '0') <= 9 &&
1436
5
        static_cast<unsigned>(sInput[17] - '0') <= 9 &&
1437
4
        static_cast<unsigned>(sInput[18] - '0') <= 9)
1438
3
    {
1439
3
        psField->Date.Year = static_cast<GInt16>(
1440
3
            ((((sInput[0] - '0') * 10 + (sInput[1] - '0')) * 10) +
1441
3
             (sInput[2] - '0')) *
1442
3
                10 +
1443
3
            (sInput[3] - '0'));
1444
3
        psField->Date.Month =
1445
3
            static_cast<GByte>((sInput[5] - '0') * 10 + (sInput[6] - '0'));
1446
3
        psField->Date.Day =
1447
3
            static_cast<GByte>((sInput[8] - '0') * 10 + (sInput[9] - '0'));
1448
3
        psField->Date.Hour =
1449
3
            static_cast<GByte>((sInput[11] - '0') * 10 + (sInput[12] - '0'));
1450
3
        psField->Date.Minute =
1451
3
            static_cast<GByte>((sInput[14] - '0') * 10 + (sInput[15] - '0'));
1452
3
        psField->Date.Second =
1453
3
            static_cast<float>(((sInput[17] - '0') * 10 + (sInput[18] - '0')));
1454
3
        psField->Date.TZFlag = sInput.size() == 19 ? 0 : 100;
1455
3
        psField->Date.Reserved = 0;
1456
3
        if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
1457
2
            psField->Date.Day == 0 || psField->Date.Day > 31 ||
1458
1
            psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
1459
1
            psField->Date.Second >= 61.0f)
1460
2
        {
1461
2
            return false;
1462
2
        }
1463
1
        return true;
1464
3
    }
1465
1466
184
    return false;
1467
187
}
1468
1469
/************************************************************************/
1470
/*              OGRParseDateTimeYYYYMMDDTHHMMSSsssZ()                   */
1471
/************************************************************************/
1472
1473
bool OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(std::string_view sInput,
1474
                                         OGRField *psField)
1475
186
{
1476
    // Detect "YYYY-MM-DDTHH:MM:SS.SSS[Z]" (23 or 24 characters)
1477
186
    if ((sInput.size() == 23 || (sInput.size() == 24 && sInput[23] == 'Z')) &&
1478
23
        sInput[4] == '-' && sInput[7] == '-' && sInput[10] == 'T' &&
1479
16
        sInput[13] == ':' && sInput[16] == ':' && sInput[19] == '.' &&
1480
13
        static_cast<unsigned>(sInput[0] - '0') <= 9 &&
1481
12
        static_cast<unsigned>(sInput[1] - '0') <= 9 &&
1482
11
        static_cast<unsigned>(sInput[2] - '0') <= 9 &&
1483
10
        static_cast<unsigned>(sInput[3] - '0') <= 9 &&
1484
10
        static_cast<unsigned>(sInput[5] - '0') <= 9 &&
1485
10
        static_cast<unsigned>(sInput[6] - '0') <= 9 &&
1486
10
        static_cast<unsigned>(sInput[8] - '0') <= 9 &&
1487
9
        static_cast<unsigned>(sInput[9] - '0') <= 9 &&
1488
9
        static_cast<unsigned>(sInput[11] - '0') <= 9 &&
1489
8
        static_cast<unsigned>(sInput[12] - '0') <= 9 &&
1490
7
        static_cast<unsigned>(sInput[14] - '0') <= 9 &&
1491
6
        static_cast<unsigned>(sInput[15] - '0') <= 9 &&
1492
6
        static_cast<unsigned>(sInput[17] - '0') <= 9 &&
1493
4
        static_cast<unsigned>(sInput[18] - '0') <= 9 &&
1494
3
        static_cast<unsigned>(sInput[20] - '0') <= 9 &&
1495
2
        static_cast<unsigned>(sInput[21] - '0') <= 9 &&
1496
2
        static_cast<unsigned>(sInput[22] - '0') <= 9)
1497
2
    {
1498
2
        psField->Date.Year = static_cast<GInt16>(
1499
2
            ((((sInput[0] - '0') * 10 + (sInput[1] - '0')) * 10) +
1500
2
             (sInput[2] - '0')) *
1501
2
                10 +
1502
2
            (sInput[3] - '0'));
1503
2
        psField->Date.Month =
1504
2
            static_cast<GByte>((sInput[5] - '0') * 10 + (sInput[6] - '0'));
1505
2
        psField->Date.Day =
1506
2
            static_cast<GByte>((sInput[8] - '0') * 10 + (sInput[9] - '0'));
1507
2
        psField->Date.Hour =
1508
2
            static_cast<GByte>((sInput[11] - '0') * 10 + (sInput[12] - '0'));
1509
2
        psField->Date.Minute =
1510
2
            static_cast<GByte>((sInput[14] - '0') * 10 + (sInput[15] - '0'));
1511
2
        psField->Date.Second =
1512
2
            static_cast<float>(((sInput[17] - '0') * 10 + (sInput[18] - '0')) +
1513
2
                               ((sInput[20] - '0') * 100 +
1514
2
                                (sInput[21] - '0') * 10 + (sInput[22] - '0')) /
1515
2
                                   1000.0);
1516
2
        psField->Date.TZFlag = sInput.size() == 23 ? 0 : 100;
1517
2
        psField->Date.Reserved = 0;
1518
2
        if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
1519
2
            psField->Date.Day == 0 || psField->Date.Day > 31 ||
1520
2
            psField->Date.Hour > 23 || psField->Date.Minute > 59 ||
1521
1
            psField->Date.Second >= 61.0f)
1522
1
        {
1523
1
            return false;
1524
1
        }
1525
1
        return true;
1526
2
    }
1527
1528
184
    return false;
1529
186
}
1530
1531
/************************************************************************/
1532
/*                           OGRParseXMLDateTime()                      */
1533
/************************************************************************/
1534
1535
int OGRParseXMLDateTime(const char *pszXMLDateTime, OGRField *psField)
1536
149k
{
1537
149k
    int year = 0;
1538
149k
    int month = 0;
1539
149k
    int day = 0;
1540
149k
    int hour = 0;
1541
149k
    int minute = 0;
1542
149k
    int TZHour = 0;
1543
149k
    int TZMinute = 0;
1544
149k
    float second = 0;
1545
149k
    char c = '\0';
1546
149k
    int TZ = 0;
1547
149k
    bool bRet = false;
1548
1549
    // Date is expressed as a UTC date.
1550
149k
    if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c", &year, &month,
1551
149k
               &day, &hour, &minute, &second, &c) == 7 &&
1552
28.8k
        c == 'Z')
1553
2.64k
    {
1554
2.64k
        TZ = 100;
1555
2.64k
        bRet = true;
1556
2.64k
    }
1557
    // Date is expressed as a UTC date, with a timezone.
1558
147k
    else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f%c%02d:%02d",
1559
147k
                    &year, &month, &day, &hour, &minute, &second, &c, &TZHour,
1560
147k
                    &TZMinute) == 9 &&
1561
19.1k
             (c == '+' || c == '-'))
1562
2.56k
    {
1563
2.56k
        TZ = 100 + ((c == '+') ? 1 : -1) * ((TZHour * 60 + TZMinute) / 15);
1564
2.56k
        bRet = true;
1565
2.56k
    }
1566
    // Date is expressed into an unknown timezone.
1567
144k
    else if (sscanf(pszXMLDateTime, "%04d-%02d-%02dT%02d:%02d:%f", &year,
1568
144k
                    &month, &day, &hour, &minute, &second) == 6)
1569
23.8k
    {
1570
23.8k
        TZ = 0;
1571
23.8k
        bRet = true;
1572
23.8k
    }
1573
    // Date is expressed as a UTC date with only year:month:day.
1574
120k
    else if (sscanf(pszXMLDateTime, "%04d-%02d-%02d", &year, &month, &day) == 3)
1575
66.5k
    {
1576
66.5k
        TZ = 0;
1577
66.5k
        bRet = true;
1578
66.5k
    }
1579
    // Date is expressed as a UTC date with only year:month.
1580
54.1k
    else if (sscanf(pszXMLDateTime, "%04d-%02d", &year, &month) == 2)
1581
2.01k
    {
1582
2.01k
        TZ = 0;
1583
2.01k
        bRet = true;
1584
2.01k
        day = 1;
1585
2.01k
    }
1586
1587
149k
    if (!bRet)
1588
52.1k
        return FALSE;
1589
1590
97.6k
    psField->Date.Year = static_cast<GInt16>(year);
1591
97.6k
    psField->Date.Month = static_cast<GByte>(month);
1592
97.6k
    psField->Date.Day = static_cast<GByte>(day);
1593
97.6k
    psField->Date.Hour = static_cast<GByte>(hour);
1594
97.6k
    psField->Date.Minute = static_cast<GByte>(minute);
1595
97.6k
    psField->Date.Second = second;
1596
97.6k
    psField->Date.TZFlag = static_cast<GByte>(TZ);
1597
97.6k
    psField->Date.Reserved = 0;
1598
1599
97.6k
    return TRUE;
1600
149k
}
1601
1602
/************************************************************************/
1603
/*                      OGRParseRFC822DateTime()                        */
1604
/************************************************************************/
1605
1606
static const char *const aszMonthStr[] = {"Jan", "Feb", "Mar", "Apr",
1607
                                          "May", "Jun", "Jul", "Aug",
1608
                                          "Sep", "Oct", "Nov", "Dec"};
1609
1610
int OGRParseRFC822DateTime(const char *pszRFC822DateTime, OGRField *psField)
1611
42
{
1612
42
    int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
1613
42
    if (!CPLParseRFC822DateTime(pszRFC822DateTime, &nYear, &nMonth, &nDay,
1614
42
                                &nHour, &nMinute, &nSecond, &nTZFlag, nullptr))
1615
5
    {
1616
5
        return false;
1617
5
    }
1618
1619
37
    psField->Date.Year = static_cast<GInt16>(nYear);
1620
37
    psField->Date.Month = static_cast<GByte>(nMonth);
1621
37
    psField->Date.Day = static_cast<GByte>(nDay);
1622
37
    psField->Date.Hour = static_cast<GByte>(nHour);
1623
37
    psField->Date.Minute = static_cast<GByte>(nMinute);
1624
37
    psField->Date.Second = (nSecond < 0) ? 0.0f : static_cast<float>(nSecond);
1625
37
    psField->Date.TZFlag = static_cast<GByte>(nTZFlag);
1626
37
    psField->Date.Reserved = 0;
1627
1628
37
    return true;
1629
42
}
1630
1631
/**
1632
  * Returns the day of the week in Gregorian calendar
1633
  *
1634
  * @param day : day of the month, between 1 and 31
1635
  * @param month : month of the year, between 1 (Jan) and 12 (Dec)
1636
  * @param year : year
1637
1638
  * @return day of the week : 0 for Monday, ... 6 for Sunday
1639
  */
1640
1641
int OGRGetDayOfWeek(int day, int month, int year)
1642
0
{
1643
    // Reference: Zeller's congruence.
1644
0
    const int q = day;
1645
0
    int m = month;
1646
0
    if (month >= 3)
1647
0
    {
1648
        // m = month;
1649
0
    }
1650
0
    else
1651
0
    {
1652
0
        m = month + 12;
1653
0
        year--;
1654
0
    }
1655
0
    const int K = year % 100;
1656
0
    const int J = year / 100;
1657
0
    const int h = (q + (((m + 1) * 26) / 10) + K + K / 4 + J / 4 + 5 * J) % 7;
1658
0
    return (h + 5) % 7;
1659
0
}
1660
1661
/************************************************************************/
1662
/*                         OGRGetRFC822DateTime()                       */
1663
/************************************************************************/
1664
1665
char *OGRGetRFC822DateTime(const OGRField *psField)
1666
0
{
1667
0
    char *pszTZ = nullptr;
1668
0
    const char *const aszDayOfWeek[] = {"Mon", "Tue", "Wed", "Thu",
1669
0
                                        "Fri", "Sat", "Sun"};
1670
1671
0
    int dayofweek = OGRGetDayOfWeek(psField->Date.Day, psField->Date.Month,
1672
0
                                    psField->Date.Year);
1673
1674
0
    int month = psField->Date.Month;
1675
0
    if (month < 1 || month > 12)
1676
0
        month = 1;
1677
1678
0
    int TZFlag = psField->Date.TZFlag;
1679
0
    if (TZFlag == 0 || TZFlag == 100)
1680
0
    {
1681
0
        pszTZ = CPLStrdup("GMT");
1682
0
    }
1683
0
    else
1684
0
    {
1685
0
        int TZOffset = std::abs(TZFlag - 100) * 15;
1686
0
        int TZHour = TZOffset / 60;
1687
0
        int TZMinute = TZOffset - TZHour * 60;
1688
0
        pszTZ = CPLStrdup(CPLSPrintf("%c%02d%02d", TZFlag > 100 ? '+' : '-',
1689
0
                                     TZHour, TZMinute));
1690
0
    }
1691
0
    char *pszRet = CPLStrdup(CPLSPrintf(
1692
0
        "%s, %02d %s %04d %02d:%02d:%02d %s", aszDayOfWeek[dayofweek],
1693
0
        psField->Date.Day, aszMonthStr[month - 1], psField->Date.Year,
1694
0
        psField->Date.Hour, psField->Date.Minute,
1695
0
        static_cast<int>(psField->Date.Second), pszTZ));
1696
0
    CPLFree(pszTZ);
1697
0
    return pszRet;
1698
0
}
1699
1700
/************************************************************************/
1701
/*                            OGRGetXMLDateTime()                       */
1702
/************************************************************************/
1703
1704
char *OGRGetXMLDateTime(const OGRField *psField)
1705
0
{
1706
0
    char *pszRet =
1707
0
        static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
1708
0
    OGRGetISO8601DateTime(psField, false, pszRet);
1709
0
    return pszRet;
1710
0
}
1711
1712
char *OGRGetXMLDateTime(const OGRField *psField, bool bAlwaysMillisecond)
1713
0
{
1714
0
    char *pszRet =
1715
0
        static_cast<char *>(CPLMalloc(OGR_SIZEOF_ISO8601_DATETIME_BUFFER));
1716
0
    OGRGetISO8601DateTime(psField, bAlwaysMillisecond, pszRet);
1717
0
    return pszRet;
1718
0
}
1719
1720
int OGRGetISO8601DateTime(const OGRField *psField, bool bAlwaysMillisecond,
1721
                          char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
1722
0
{
1723
0
    OGRISO8601Format sFormat;
1724
0
    sFormat.ePrecision = bAlwaysMillisecond ? OGRISO8601Precision::MILLISECOND
1725
0
                                            : OGRISO8601Precision::AUTO;
1726
0
    return OGRGetISO8601DateTime(psField, sFormat, szBuffer);
1727
0
}
1728
1729
int OGRGetISO8601DateTime(const OGRField *psField,
1730
                          const OGRISO8601Format &sFormat,
1731
                          char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER])
1732
0
{
1733
0
    const GInt16 year = psField->Date.Year;
1734
0
    const GByte month = psField->Date.Month;
1735
0
    const GByte day = psField->Date.Day;
1736
0
    const GByte hour = psField->Date.Hour;
1737
0
    const GByte minute = psField->Date.Minute;
1738
0
    const float second = psField->Date.Second;
1739
0
    const GByte TZFlag = psField->Date.TZFlag;
1740
1741
0
    if (year < 0 || year >= 10000)
1742
0
    {
1743
0
        CPLError(CE_Failure, CPLE_AppDefined,
1744
0
                 "OGRGetISO8601DateTime(): year %d unsupported ", year);
1745
0
        szBuffer[0] = 0;
1746
0
        return 0;
1747
0
    }
1748
1749
0
    int nYear = year;
1750
0
    szBuffer[3] = (nYear % 10) + '0';
1751
0
    nYear /= 10;
1752
0
    szBuffer[2] = (nYear % 10) + '0';
1753
0
    nYear /= 10;
1754
0
    szBuffer[1] = (nYear % 10) + '0';
1755
0
    nYear /= 10;
1756
0
    szBuffer[0] = static_cast<char>(nYear /*% 10*/ + '0');
1757
0
    szBuffer[4] = '-';
1758
0
    szBuffer[5] = ((month / 10) % 10) + '0';
1759
0
    szBuffer[6] = (month % 10) + '0';
1760
0
    szBuffer[7] = '-';
1761
0
    szBuffer[8] = ((day / 10) % 10) + '0';
1762
0
    szBuffer[9] = (day % 10) + '0';
1763
0
    szBuffer[10] = 'T';
1764
0
    szBuffer[11] = ((hour / 10) % 10) + '0';
1765
0
    szBuffer[12] = (hour % 10) + '0';
1766
0
    szBuffer[13] = ':';
1767
0
    szBuffer[14] = ((minute / 10) % 10) + '0';
1768
0
    szBuffer[15] = (minute % 10) + '0';
1769
0
    int nPos;
1770
0
    if (sFormat.ePrecision == OGRISO8601Precision::MINUTE)
1771
0
    {
1772
0
        nPos = 16;
1773
0
    }
1774
0
    else
1775
0
    {
1776
0
        szBuffer[16] = ':';
1777
1778
0
        if (sFormat.ePrecision == OGRISO8601Precision::MILLISECOND ||
1779
0
            (sFormat.ePrecision == OGRISO8601Precision::AUTO &&
1780
0
             OGR_GET_MS(second)))
1781
0
        {
1782
            /* Below is equivalent of the below snprintf(), but hand-made for
1783
             * faster execution. */
1784
            /* snprintf(szBuffer, nMaxSize,
1785
                                   "%04d-%02u-%02uT%02u:%02u:%06.3f%s",
1786
                                   year, month, day, hour, minute, second,
1787
                                   szTimeZone);
1788
            */
1789
0
            int nMilliSecond = static_cast<int>(second * 1000.0f + 0.5f);
1790
0
            szBuffer[22] = (nMilliSecond % 10) + '0';
1791
0
            nMilliSecond /= 10;
1792
0
            szBuffer[21] = (nMilliSecond % 10) + '0';
1793
0
            nMilliSecond /= 10;
1794
0
            szBuffer[20] = (nMilliSecond % 10) + '0';
1795
0
            nMilliSecond /= 10;
1796
0
            szBuffer[19] = '.';
1797
0
            szBuffer[18] = (nMilliSecond % 10) + '0';
1798
0
            nMilliSecond /= 10;
1799
0
            szBuffer[17] = (nMilliSecond % 10) + '0';
1800
0
            nPos = 23;
1801
0
        }
1802
0
        else
1803
0
        {
1804
            /* Below is equivalent of the below snprintf(), but hand-made for
1805
             * faster execution. */
1806
            /* snprintf(szBuffer, nMaxSize,
1807
                                   "%04d-%02u-%02uT%02u:%02u:%02u%s",
1808
                                   year, month, day, hour, minute,
1809
                                   static_cast<GByte>(second), szTimeZone);
1810
            */
1811
0
            int nSecond = static_cast<int>(second + 0.5f);
1812
0
            szBuffer[17] = ((nSecond / 10) % 10) + '0';
1813
0
            szBuffer[18] = (nSecond % 10) + '0';
1814
0
            nPos = 19;
1815
0
        }
1816
0
    }
1817
1818
0
    switch (TZFlag)
1819
0
    {
1820
0
        case 0:  // Unknown time zone
1821
0
        case 1:  // Local time zone (not specified)
1822
0
            break;
1823
1824
0
        case 100:  // GMT
1825
0
            szBuffer[nPos++] = 'Z';
1826
0
            break;
1827
1828
0
        default:  // Offset (in quarter-hour units) from GMT
1829
0
            const int TZOffset = std::abs(TZFlag - 100) * 15;
1830
0
            const int TZHour = TZOffset / 60;
1831
0
            const int TZMinute = TZOffset % 60;
1832
1833
0
            szBuffer[nPos++] = (TZFlag > 100) ? '+' : '-';
1834
0
            szBuffer[nPos++] = ((TZHour / 10) % 10) + '0';
1835
0
            szBuffer[nPos++] = (TZHour % 10) + '0';
1836
0
            szBuffer[nPos++] = ':';
1837
0
            szBuffer[nPos++] = ((TZMinute / 10) % 10) + '0';
1838
0
            szBuffer[nPos++] = (TZMinute % 10) + '0';
1839
0
    }
1840
1841
0
    szBuffer[nPos] = 0;
1842
1843
0
    return nPos;
1844
0
}
1845
1846
/************************************************************************/
1847
/*                 OGRGetXML_UTF8_EscapedString()                       */
1848
/************************************************************************/
1849
1850
char *OGRGetXML_UTF8_EscapedString(const char *pszString)
1851
4.39M
{
1852
4.39M
    char *pszEscaped = nullptr;
1853
4.39M
    if (!CPLIsUTF8(pszString, -1) &&
1854
931k
        CPLTestBool(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
1855
931k
    {
1856
931k
        static bool bFirstTime = true;
1857
931k
        if (bFirstTime)
1858
1
        {
1859
1
            bFirstTime = false;
1860
1
            CPLError(CE_Warning, CPLE_AppDefined,
1861
1
                     "%s is not a valid UTF-8 string. Forcing it to ASCII.  "
1862
1
                     "If you still want the original string and change the XML "
1863
1
                     "file encoding afterwards, you can define "
1864
1
                     "OGR_FORCE_ASCII=NO as configuration option.  "
1865
1
                     "This warning won't be issued anymore",
1866
1
                     pszString);
1867
1
        }
1868
931k
        else
1869
931k
        {
1870
931k
            CPLDebug("OGR",
1871
931k
                     "%s is not a valid UTF-8 string. Forcing it to ASCII",
1872
931k
                     pszString);
1873
931k
        }
1874
931k
        char *pszTemp = CPLForceToASCII(pszString, -1, '?');
1875
931k
        pszEscaped = CPLEscapeString(pszTemp, -1, CPLES_XML);
1876
931k
        CPLFree(pszTemp);
1877
931k
    }
1878
3.46M
    else
1879
3.46M
        pszEscaped = CPLEscapeString(pszString, -1, CPLES_XML);
1880
4.39M
    return pszEscaped;
1881
4.39M
}
1882
1883
/************************************************************************/
1884
/*                        OGRCompareDate()                              */
1885
/************************************************************************/
1886
1887
int OGRCompareDate(const OGRField *psFirstTuple, const OGRField *psSecondTuple)
1888
0
{
1889
    // TODO: We ignore TZFlag.
1890
1891
0
    if (psFirstTuple->Date.Year < psSecondTuple->Date.Year)
1892
0
        return -1;
1893
0
    else if (psFirstTuple->Date.Year > psSecondTuple->Date.Year)
1894
0
        return 1;
1895
1896
0
    if (psFirstTuple->Date.Month < psSecondTuple->Date.Month)
1897
0
        return -1;
1898
0
    else if (psFirstTuple->Date.Month > psSecondTuple->Date.Month)
1899
0
        return 1;
1900
1901
0
    if (psFirstTuple->Date.Day < psSecondTuple->Date.Day)
1902
0
        return -1;
1903
0
    else if (psFirstTuple->Date.Day > psSecondTuple->Date.Day)
1904
0
        return 1;
1905
1906
0
    if (psFirstTuple->Date.Hour < psSecondTuple->Date.Hour)
1907
0
        return -1;
1908
0
    else if (psFirstTuple->Date.Hour > psSecondTuple->Date.Hour)
1909
0
        return 1;
1910
1911
0
    if (psFirstTuple->Date.Minute < psSecondTuple->Date.Minute)
1912
0
        return -1;
1913
0
    else if (psFirstTuple->Date.Minute > psSecondTuple->Date.Minute)
1914
0
        return 1;
1915
1916
0
    if (psFirstTuple->Date.Second < psSecondTuple->Date.Second)
1917
0
        return -1;
1918
0
    else if (psFirstTuple->Date.Second > psSecondTuple->Date.Second)
1919
0
        return 1;
1920
1921
0
    return 0;
1922
0
}
1923
1924
/************************************************************************/
1925
/*                        OGRFastAtof()                                 */
1926
/************************************************************************/
1927
1928
// On Windows, CPLAtof() is very slow if the number is followed by other long
1929
// content.  Just extract the number into a short string before calling
1930
// CPLAtof() on it.
1931
static double OGRCallAtofOnShortString(const char *pszStr)
1932
893k
{
1933
893k
    const char *p = pszStr;
1934
893k
    while (*p == ' ' || *p == '\t')
1935
170
        ++p;
1936
1937
893k
    char szTemp[128] = {};
1938
893k
    int nCounter = 0;
1939
10.5M
    while (*p == '+' || *p == '-' || (*p >= '0' && *p <= '9') || *p == '.' ||
1940
1.99M
           (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D'))
1941
9.66M
    {
1942
9.66M
        szTemp[nCounter++] = *(p++);
1943
9.66M
        if (nCounter == 127)
1944
24.5k
            return CPLAtof(pszStr);
1945
9.66M
    }
1946
869k
    szTemp[nCounter] = '\0';
1947
869k
    return CPLAtof(szTemp);
1948
893k
}
1949
1950
/** Same contract as CPLAtof, except than it doesn't always call the
1951
 *  system CPLAtof() that may be slow on some platforms. For simple but
1952
 *  common strings, it'll use a faster implementation (up to 20x faster
1953
 *  than CPLAtof() on MS runtime libraries) that has no guaranty to return
1954
 *  exactly the same floating point number.
1955
 */
1956
1957
double OGRFastAtof(const char *pszStr)
1958
1.81M
{
1959
1.81M
    double dfVal = 0;
1960
1.81M
    double dfSign = 1.0;
1961
1.81M
    const char *p = pszStr;
1962
1963
1.81M
    constexpr double adfTenPower[] = {
1964
1.81M
        1e0,  1e1,  1e2,  1e3,  1e4,  1e5,  1e6,  1e7,  1e8,  1e9,  1e10,
1965
1.81M
        1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21,
1966
1.81M
        1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31};
1967
1968
1.81M
    while (*p == ' ' || *p == '\t')
1969
3.88k
        ++p;
1970
1971
1.81M
    if (*p == '+')
1972
1.88k
        ++p;
1973
1.81M
    else if (*p == '-')
1974
109k
    {
1975
109k
        dfSign = -1.0;
1976
109k
        ++p;
1977
109k
    }
1978
1979
17.4M
    while (true)
1980
17.4M
    {
1981
17.4M
        if (*p >= '0' && *p <= '9')
1982
15.6M
        {
1983
15.6M
            dfVal = dfVal * 10.0 + (*p - '0');
1984
15.6M
            ++p;
1985
15.6M
        }
1986
1.81M
        else if (*p == '.')
1987
429k
        {
1988
429k
            ++p;
1989
429k
            break;
1990
429k
        }
1991
1.38M
        else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
1992
734k
            return OGRCallAtofOnShortString(pszStr);
1993
651k
        else
1994
651k
            return dfSign * dfVal;
1995
17.4M
    }
1996
1997
429k
    unsigned int countFractionnal = 0;
1998
8.05M
    while (true)
1999
8.05M
    {
2000
8.05M
        if (*p >= '0' && *p <= '9')
2001
7.63M
        {
2002
7.63M
            dfVal = dfVal * 10.0 + (*p - '0');
2003
7.63M
            ++countFractionnal;
2004
7.63M
            ++p;
2005
7.63M
        }
2006
429k
        else if (*p == 'e' || *p == 'E' || *p == 'd' || *p == 'D')
2007
139k
            return OGRCallAtofOnShortString(pszStr);
2008
289k
        else
2009
289k
        {
2010
289k
            if (countFractionnal < CPL_ARRAYSIZE(adfTenPower))
2011
269k
                return dfSign * (dfVal / adfTenPower[countFractionnal]);
2012
19.6k
            else
2013
19.6k
                return OGRCallAtofOnShortString(pszStr);
2014
289k
        }
2015
8.05M
    }
2016
429k
}
2017
2018
/**
2019
 * Check that panPermutation is a permutation of [0, nSize-1].
2020
 * @param panPermutation an array of nSize elements.
2021
 * @param nSize size of the array.
2022
 * @return OGRERR_NONE if panPermutation is a permutation of [0, nSize - 1].
2023
 */
2024
OGRErr OGRCheckPermutation(const int *panPermutation, int nSize)
2025
0
{
2026
0
    OGRErr eErr = OGRERR_NONE;
2027
0
    int *panCheck = static_cast<int *>(CPLCalloc(nSize, sizeof(int)));
2028
0
    for (int i = 0; i < nSize; ++i)
2029
0
    {
2030
0
        if (panPermutation[i] < 0 || panPermutation[i] >= nSize)
2031
0
        {
2032
0
            CPLError(CE_Failure, CPLE_IllegalArg, "Bad value for element %d",
2033
0
                     i);
2034
0
            eErr = OGRERR_FAILURE;
2035
0
            break;
2036
0
        }
2037
0
        if (panCheck[panPermutation[i]] != 0)
2038
0
        {
2039
0
            CPLError(CE_Failure, CPLE_IllegalArg,
2040
0
                     "Array is not a permutation of [0,%d]", nSize - 1);
2041
0
            eErr = OGRERR_FAILURE;
2042
0
            break;
2043
0
        }
2044
0
        panCheck[panPermutation[i]] = 1;
2045
0
    }
2046
0
    CPLFree(panCheck);
2047
0
    return eErr;
2048
0
}
2049
2050
OGRErr OGRReadWKBGeometryType(const unsigned char *pabyData,
2051
                              OGRwkbVariant eWkbVariant,
2052
                              OGRwkbGeometryType *peGeometryType)
2053
4.09M
{
2054
4.09M
    if (!peGeometryType)
2055
0
        return OGRERR_FAILURE;
2056
2057
    /* -------------------------------------------------------------------- */
2058
    /*      Get the byte order byte.                                        */
2059
    /* -------------------------------------------------------------------- */
2060
4.09M
    int nByteOrder = DB2_V72_FIX_BYTE_ORDER(*pabyData);
2061
4.09M
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
2062
7.14k
        return OGRERR_CORRUPT_DATA;
2063
4.09M
    OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
2064
2065
    /* -------------------------------------------------------------------- */
2066
    /*      Get the geometry type.                                          */
2067
    /* -------------------------------------------------------------------- */
2068
4.09M
    bool bIsOldStyle3D = false;
2069
4.09M
    bool bIsOldStyleMeasured = false;
2070
4.09M
    int iRawType = 0;
2071
2072
4.09M
    memcpy(&iRawType, pabyData + 1, 4);
2073
4.09M
    if (OGR_SWAP(eByteOrder))
2074
3.89M
    {
2075
3.89M
        CPL_SWAP32PTR(&iRawType);
2076
3.89M
    }
2077
2078
    // Test for M bit in PostGIS WKB, see ogrgeometry.cpp:4956.
2079
4.09M
    if (0x40000000 & iRawType)
2080
672k
    {
2081
672k
        iRawType &= ~0x40000000;
2082
672k
        bIsOldStyleMeasured = true;
2083
672k
    }
2084
    // Old-style OGC z-bit is flipped? Tests also Z bit in PostGIS WKB.
2085
4.09M
    if (wkb25DBitInternalUse & iRawType)
2086
617k
    {
2087
        // Clean off top 3 bytes.
2088
617k
        iRawType &= 0x000000FF;
2089
617k
        bIsOldStyle3D = true;
2090
617k
    }
2091
2092
    // ISO SQL/MM Part3 draft -> Deprecated.
2093
    // See http://jtc1sc32.org/doc/N1101-1150/32N1107-WD13249-3--spatial.pdf
2094
4.09M
    if (iRawType == 1000001)
2095
3.57k
        iRawType = wkbCircularString;
2096
4.08M
    else if (iRawType == 1000002)
2097
750
        iRawType = wkbCompoundCurve;
2098
4.08M
    else if (iRawType == 1000003)
2099
3.27k
        iRawType = wkbCurvePolygon;
2100
4.08M
    else if (iRawType == 1000004)
2101
967
        iRawType = wkbMultiCurve;
2102
4.08M
    else if (iRawType == 1000005)
2103
1.72k
        iRawType = wkbMultiSurface;
2104
4.08M
    else if (iRawType == 2000001)
2105
1.44k
        iRawType = wkbPointZM;
2106
4.07M
    else if (iRawType == 2000002)
2107
2.59k
        iRawType = wkbLineStringZM;
2108
4.07M
    else if (iRawType == 2000003)
2109
1.47k
        iRawType = wkbCircularStringZM;
2110
4.07M
    else if (iRawType == 2000004)
2111
2.30k
        iRawType = wkbCompoundCurveZM;
2112
4.07M
    else if (iRawType == 2000005)
2113
8.57k
        iRawType = wkbPolygonZM;
2114
4.06M
    else if (iRawType == 2000006)
2115
2.10k
        iRawType = wkbCurvePolygonZM;
2116
4.06M
    else if (iRawType == 2000007)
2117
1.47k
        iRawType = wkbMultiPointZM;
2118
4.06M
    else if (iRawType == 2000008)
2119
2.02k
        iRawType = wkbMultiCurveZM;
2120
4.05M
    else if (iRawType == 2000009)
2121
4.95k
        iRawType = wkbMultiLineStringZM;
2122
4.05M
    else if (iRawType == 2000010)
2123
965
        iRawType = wkbMultiSurfaceZM;
2124
4.05M
    else if (iRawType == 2000011)
2125
921
        iRawType = wkbMultiPolygonZM;
2126
4.05M
    else if (iRawType == 2000012)
2127
10.7k
        iRawType = wkbGeometryCollectionZM;
2128
4.04M
    else if (iRawType == 3000001)
2129
77
        iRawType = wkbPoint25D;
2130
4.04M
    else if (iRawType == 3000002)
2131
352
        iRawType = wkbLineString25D;
2132
4.04M
    else if (iRawType == 3000003)
2133
5.36k
        iRawType = wkbCircularStringZ;
2134
4.03M
    else if (iRawType == 3000004)
2135
2.24k
        iRawType = wkbCompoundCurveZ;
2136
4.03M
    else if (iRawType == 3000005)
2137
445
        iRawType = wkbPolygon25D;
2138
4.03M
    else if (iRawType == 3000006)
2139
1.92k
        iRawType = wkbCurvePolygonZ;
2140
4.03M
    else if (iRawType == 3000007)
2141
252
        iRawType = wkbMultiPoint25D;
2142
4.03M
    else if (iRawType == 3000008)
2143
5.56k
        iRawType = wkbMultiCurveZ;
2144
4.02M
    else if (iRawType == 3000009)
2145
114
        iRawType = wkbMultiLineString25D;
2146
4.02M
    else if (iRawType == 3000010)
2147
1.20k
        iRawType = wkbMultiSurfaceZ;
2148
4.02M
    else if (iRawType == 3000011)
2149
249
        iRawType = wkbMultiPolygon25D;
2150
4.02M
    else if (iRawType == 3000012)
2151
424
        iRawType = wkbGeometryCollection25D;
2152
4.02M
    else if (iRawType == 4000001)
2153
3.84k
        iRawType = wkbPointM;
2154
4.01M
    else if (iRawType == 4000002)
2155
339k
        iRawType = wkbLineStringM;
2156
3.67M
    else if (iRawType == 4000003)
2157
797k
        iRawType = wkbCircularStringM;
2158
2.88M
    else if (iRawType == 4000004)
2159
8.01k
        iRawType = wkbCompoundCurveM;
2160
2.87M
    else if (iRawType == 4000005)
2161
7.51k
        iRawType = wkbPolygonM;
2162
2.86M
    else if (iRawType == 4000006)
2163
94.7k
        iRawType = wkbCurvePolygonM;
2164
2.77M
    else if (iRawType == 4000007)
2165
10.8k
        iRawType = wkbMultiPointM;
2166
2.76M
    else if (iRawType == 4000008)
2167
3.14k
        iRawType = wkbMultiCurveM;
2168
2.75M
    else if (iRawType == 4000009)
2169
27.3k
        iRawType = wkbMultiLineStringM;
2170
2.73M
    else if (iRawType == 4000010)
2171
3.48k
        iRawType = wkbMultiSurfaceM;
2172
2.72M
    else if (iRawType == 4000011)
2173
1.47k
        iRawType = wkbMultiPolygonM;
2174
2.72M
    else if (iRawType == 4000012)
2175
8.64k
        iRawType = wkbGeometryCollectionM;
2176
2177
    // Sometimes the Z flag is in the 2nd byte?
2178
4.09M
    if (iRawType & (wkb25DBitInternalUse >> 16))
2179
605k
    {
2180
        // Clean off top 3 bytes.
2181
605k
        iRawType &= 0x000000FF;
2182
605k
        bIsOldStyle3D = true;
2183
605k
    }
2184
2185
4.09M
    if (eWkbVariant == wkbVariantPostGIS1)
2186
0
    {
2187
0
        if (iRawType == POSTGIS15_CURVEPOLYGON)
2188
0
            iRawType = wkbCurvePolygon;
2189
0
        else if (iRawType == POSTGIS15_MULTICURVE)
2190
0
            iRawType = wkbMultiCurve;
2191
0
        else if (iRawType == POSTGIS15_MULTISURFACE)
2192
0
            iRawType = wkbMultiSurface;
2193
0
    }
2194
2195
    // ISO SQL/MM style types are between 1-17, 1001-1017, 2001-2017, and
2196
    // 3001-3017.
2197
4.09M
    if (!((iRawType > 0 && iRawType <= 17) ||
2198
1.91M
          (iRawType > 1000 && iRawType <= 1017) ||
2199
1.89M
          (iRawType > 2000 && iRawType <= 2017) ||
2200
584k
          (iRawType > 3000 && iRawType <= 3017)))
2201
542k
    {
2202
542k
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WKB type %d",
2203
542k
                 iRawType);
2204
542k
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
2205
542k
    }
2206
2207
3.54M
    if (bIsOldStyle3D)
2208
1.12M
    {
2209
1.12M
        iRawType += 1000;
2210
1.12M
    }
2211
3.54M
    if (bIsOldStyleMeasured)
2212
615k
    {
2213
615k
        iRawType += 2000;
2214
615k
    }
2215
2216
    // Convert to OGRwkbGeometryType value.
2217
3.54M
    if (iRawType >= 1001 && iRawType <= 1007)
2218
543k
    {
2219
543k
        iRawType -= 1000;
2220
543k
        iRawType |= wkb25DBitInternalUse;
2221
543k
    }
2222
2223
3.54M
    *peGeometryType = static_cast<OGRwkbGeometryType>(iRawType);
2224
2225
3.54M
    return OGRERR_NONE;
2226
4.09M
}
2227
2228
/************************************************************************/
2229
/*                      OGRReadWKTGeometryType()                        */
2230
/************************************************************************/
2231
2232
OGRErr OGRReadWKTGeometryType(const char *pszWKT,
2233
                              OGRwkbGeometryType *peGeometryType)
2234
0
{
2235
0
    if (!peGeometryType)
2236
0
        return OGRERR_FAILURE;
2237
2238
0
    OGRwkbGeometryType eGeomType = wkbUnknown;
2239
0
    if (STARTS_WITH_CI(pszWKT, "POINT"))
2240
0
        eGeomType = wkbPoint;
2241
0
    else if (STARTS_WITH_CI(pszWKT, "LINESTRING"))
2242
0
        eGeomType = wkbLineString;
2243
0
    else if (STARTS_WITH_CI(pszWKT, "POLYGON"))
2244
0
        eGeomType = wkbPolygon;
2245
0
    else if (STARTS_WITH_CI(pszWKT, "MULTIPOINT"))
2246
0
        eGeomType = wkbMultiPoint;
2247
0
    else if (STARTS_WITH_CI(pszWKT, "MULTILINESTRING"))
2248
0
        eGeomType = wkbMultiLineString;
2249
0
    else if (STARTS_WITH_CI(pszWKT, "MULTIPOLYGON"))
2250
0
        eGeomType = wkbMultiPolygon;
2251
0
    else if (STARTS_WITH_CI(pszWKT, "GEOMETRYCOLLECTION"))
2252
0
        eGeomType = wkbGeometryCollection;
2253
0
    else if (STARTS_WITH_CI(pszWKT, "CIRCULARSTRING"))
2254
0
        eGeomType = wkbCircularString;
2255
0
    else if (STARTS_WITH_CI(pszWKT, "COMPOUNDCURVE"))
2256
0
        eGeomType = wkbCompoundCurve;
2257
0
    else if (STARTS_WITH_CI(pszWKT, "CURVEPOLYGON"))
2258
0
        eGeomType = wkbCurvePolygon;
2259
0
    else if (STARTS_WITH_CI(pszWKT, "MULTICURVE"))
2260
0
        eGeomType = wkbMultiCurve;
2261
0
    else if (STARTS_WITH_CI(pszWKT, "MULTISURFACE"))
2262
0
        eGeomType = wkbMultiSurface;
2263
0
    else if (STARTS_WITH_CI(pszWKT, "POLYHEDRALSURFACE"))
2264
0
        eGeomType = wkbPolyhedralSurface;
2265
0
    else if (STARTS_WITH_CI(pszWKT, "TIN"))
2266
0
        eGeomType = wkbTIN;
2267
0
    else
2268
0
        return OGRERR_UNSUPPORTED_GEOMETRY_TYPE;
2269
2270
0
    if (strstr(pszWKT, " ZM"))
2271
0
        eGeomType = OGR_GT_SetModifier(eGeomType, true, true);
2272
0
    else if (strstr(pszWKT, " Z"))
2273
0
        eGeomType = OGR_GT_SetModifier(eGeomType, true, false);
2274
0
    else if (strstr(pszWKT, " M"))
2275
0
        eGeomType = OGR_GT_SetModifier(eGeomType, false, true);
2276
2277
0
    *peGeometryType = eGeomType;
2278
2279
0
    return OGRERR_NONE;
2280
0
}
2281
2282
/************************************************************************/
2283
/*                        OGRFormatFloat()                              */
2284
/************************************************************************/
2285
2286
int OGRFormatFloat(char *pszBuffer, int nBufferLen, float fVal, int nPrecision,
2287
                   char chConversionSpecifier)
2288
0
{
2289
    // So to have identical cross platform representation.
2290
0
    if (std::isinf(fVal))
2291
0
        return CPLsnprintf(pszBuffer, nBufferLen, (fVal > 0) ? "inf" : "-inf");
2292
0
    if (std::isnan(fVal))
2293
0
        return CPLsnprintf(pszBuffer, nBufferLen, "nan");
2294
2295
0
    int nSize = 0;
2296
0
    char szFormatting[32] = {};
2297
0
    constexpr int MAX_SIGNIFICANT_DIGITS_FLOAT32 = 8;
2298
0
    const int nInitialSignificantFigures =
2299
0
        nPrecision >= 0 ? nPrecision : MAX_SIGNIFICANT_DIGITS_FLOAT32;
2300
2301
0
    CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
2302
0
                nInitialSignificantFigures, chConversionSpecifier);
2303
0
    nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting,
2304
0
                        static_cast<double>(fVal));
2305
0
    const char *pszDot = strchr(pszBuffer, '.');
2306
2307
    // Try to avoid 0.34999999 or 0.15000001 rounding issues by
2308
    // decreasing a bit precision.
2309
0
    if (nInitialSignificantFigures >= 8 && pszDot != nullptr &&
2310
0
        (strstr(pszDot, "99999") != nullptr ||
2311
0
         strstr(pszDot, "00000") != nullptr))
2312
0
    {
2313
0
        const CPLString osOriBuffer(pszBuffer, nSize);
2314
2315
0
        bool bOK = false;
2316
0
        for (int i = 1; i <= 3; i++)
2317
0
        {
2318
0
            CPLsnprintf(szFormatting, sizeof(szFormatting), "%%.%d%c",
2319
0
                        nInitialSignificantFigures - i, chConversionSpecifier);
2320
0
            nSize = CPLsnprintf(pszBuffer, nBufferLen, szFormatting,
2321
0
                                static_cast<double>(fVal));
2322
0
            pszDot = strchr(pszBuffer, '.');
2323
0
            if (pszDot != nullptr && strstr(pszDot, "99999") == nullptr &&
2324
0
                strstr(pszDot, "00000") == nullptr &&
2325
0
                static_cast<float>(CPLAtof(pszBuffer)) == fVal)
2326
0
            {
2327
0
                bOK = true;
2328
0
                break;
2329
0
            }
2330
0
        }
2331
0
        if (!bOK)
2332
0
        {
2333
0
            memcpy(pszBuffer, osOriBuffer.c_str(), osOriBuffer.size() + 1);
2334
0
            nSize = static_cast<int>(osOriBuffer.size());
2335
0
        }
2336
0
    }
2337
2338
0
    if (nSize + 2 < static_cast<int>(nBufferLen) &&
2339
0
        strchr(pszBuffer, '.') == nullptr && strchr(pszBuffer, 'e') == nullptr)
2340
0
    {
2341
0
        nSize += CPLsnprintf(pszBuffer + nSize, nBufferLen - nSize, ".0");
2342
0
    }
2343
2344
0
    return nSize;
2345
0
}
2346
2347
int OGR_GET_MS(float fSec)
2348
12.4k
{
2349
12.4k
    if (std::isnan(fSec))
2350
505
        return 0;
2351
11.9k
    if (fSec >= 999)
2352
3.37k
        return 999;
2353
8.60k
    if (fSec <= 0)
2354
6.20k
        return 0;
2355
2.39k
    const float fValue = (fSec - static_cast<int>(fSec)) * 1000 + 0.5f;
2356
2.39k
    return static_cast<int>(fValue);
2357
8.60k
}
2358
2359
/************************************************************************/
2360
/*                    OGRDuplicateCharacter()                           */
2361
/************************************************************************/
2362
2363
std::string OGRDuplicateCharacter(const std::string &osStr, char ch)
2364
0
{
2365
0
    char aszReplacement[] = {ch, ch, 0};
2366
0
    return CPLString(osStr).replaceAll(ch, aszReplacement);
2367
0
}