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