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