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