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