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