Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Core
4 : * Purpose: Free standing functions for GDAL.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 :
16 : #include <cctype>
17 : #include <cerrno>
18 : #include <clocale>
19 : #include <cmath>
20 : #include <cstddef>
21 : #include <cstdio>
22 : #include <cstdlib>
23 : #include <cstring>
24 : #include <fcntl.h>
25 :
26 : #include <algorithm>
27 : #include <iostream>
28 : #include <limits>
29 : #include <string>
30 :
31 : #include "cpl_conv.h"
32 : #include "cpl_error.h"
33 : #include "cpl_json.h"
34 : #include "cpl_minixml.h"
35 : #include "cpl_multiproc.h"
36 : #include "cpl_string.h"
37 : #include "cpl_vsi.h"
38 : #ifdef EMBED_RESOURCE_FILES
39 : #include "embedded_resources.h"
40 : #endif
41 : #include "gdal_version_full/gdal_version.h"
42 : #include "gdal.h"
43 : #include "gdal_mdreader.h"
44 : #include "gdal_priv.h"
45 : #include "gdal_priv_templates.hpp"
46 : #include "ogr_core.h"
47 : #include "ogr_spatialref.h"
48 : #include "ogr_geos.h"
49 :
50 : #include "proj.h"
51 :
52 : #ifdef HAVE_CURL
53 : #include "cpl_curl_priv.h"
54 : #endif
55 :
56 3352 : static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[],
57 : const int panBits[])
58 : {
59 3352 : if (pabFloating[0] != pabFloating[1])
60 : {
61 376 : const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
62 376 : const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
63 :
64 376 : return std::max(panBits[nFloatingTypeIndex],
65 376 : 2 * panBits[nNotFloatingTypeIndex]);
66 : }
67 :
68 2976 : if (pabSigned[0] != pabSigned[1])
69 : {
70 452 : const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
71 452 : const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
72 :
73 452 : return std::max(panBits[nSignedTypeIndex],
74 452 : 2 * panBits[nUnsignedTypeIndex]);
75 : }
76 :
77 2524 : return std::max(panBits[0], panBits[1]);
78 : }
79 :
80 6704 : static int GetDataTypeElementSizeBits(GDALDataType eDataType)
81 : {
82 6704 : switch (eDataType)
83 : {
84 4281 : case GDT_Byte:
85 : case GDT_Int8:
86 4281 : return 8;
87 :
88 1131 : case GDT_UInt16:
89 : case GDT_Int16:
90 : case GDT_CInt16:
91 1131 : return 16;
92 :
93 894 : case GDT_UInt32:
94 : case GDT_Int32:
95 : case GDT_Float32:
96 : case GDT_CInt32:
97 : case GDT_CFloat32:
98 894 : return 32;
99 :
100 398 : case GDT_Float64:
101 : case GDT_CFloat64:
102 : case GDT_UInt64:
103 : case GDT_Int64:
104 398 : return 64;
105 :
106 0 : case GDT_Unknown:
107 : case GDT_TypeCount:
108 0 : break;
109 : }
110 0 : return 0;
111 : }
112 :
113 : /************************************************************************/
114 : /* GDALDataTypeUnion() */
115 : /************************************************************************/
116 :
117 : /**
118 : * \brief Return the smallest data type that can fully express both input data
119 : * types.
120 : *
121 : * @param eType1 first data type.
122 : * @param eType2 second data type.
123 : *
124 : * @return a data type able to express eType1 and eType2.
125 : */
126 :
127 3353 : GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
128 : GDALDataType eType2)
129 :
130 : {
131 3353 : if (eType1 == GDT_Unknown)
132 1 : return eType2;
133 3352 : if (eType2 == GDT_Unknown)
134 0 : return eType1;
135 :
136 3352 : const int panBits[] = {GetDataTypeElementSizeBits(eType1),
137 3352 : GetDataTypeElementSizeBits(eType2)};
138 :
139 3352 : if (panBits[0] == 0 || panBits[1] == 0)
140 0 : return GDT_Unknown;
141 :
142 3352 : const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
143 3352 : CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
144 :
145 3352 : const bool bSigned = pabSigned[0] || pabSigned[1];
146 3352 : const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
147 3352 : CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
148 3352 : const bool bFloating = pabFloating[0] || pabFloating[1];
149 6329 : const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
150 2977 : CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
151 :
152 3352 : const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
153 :
154 3352 : return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
155 : }
156 :
157 : /************************************************************************/
158 : /* GDALDataTypeUnionWithValue() */
159 : /************************************************************************/
160 :
161 : /**
162 : * \brief Union a data type with the one found for a value
163 : *
164 : * @param eDT the first data type
165 : * @param dfValue the value for which to find a data type and union with eDT
166 : * @param bComplex if the value is complex
167 : *
168 : * @return a data type able to express eDT and dfValue.
169 : * @since GDAL 2.3
170 : */
171 304 : GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
172 : double dfValue,
173 : int bComplex)
174 : {
175 304 : if (!bComplex && !GDALDataTypeIsComplex(eDT))
176 : {
177 298 : switch (eDT)
178 : {
179 158 : case GDT_Byte:
180 : {
181 158 : if (GDALIsValueExactAs<uint8_t>(dfValue))
182 147 : return eDT;
183 11 : break;
184 : }
185 2 : case GDT_Int8:
186 : {
187 2 : if (GDALIsValueExactAs<int8_t>(dfValue))
188 1 : return eDT;
189 1 : break;
190 : }
191 6 : case GDT_UInt16:
192 : {
193 6 : if (GDALIsValueExactAs<uint16_t>(dfValue))
194 5 : return eDT;
195 1 : break;
196 : }
197 40 : case GDT_Int16:
198 : {
199 40 : if (GDALIsValueExactAs<int16_t>(dfValue))
200 39 : return eDT;
201 1 : break;
202 : }
203 2 : case GDT_UInt32:
204 : {
205 2 : if (GDALIsValueExactAs<uint32_t>(dfValue))
206 1 : return eDT;
207 1 : break;
208 : }
209 20 : case GDT_Int32:
210 : {
211 20 : if (GDALIsValueExactAs<int32_t>(dfValue))
212 19 : return eDT;
213 1 : break;
214 : }
215 2 : case GDT_UInt64:
216 : {
217 2 : if (GDALIsValueExactAs<uint64_t>(dfValue))
218 1 : return eDT;
219 1 : break;
220 : }
221 2 : case GDT_Int64:
222 : {
223 2 : if (GDALIsValueExactAs<int64_t>(dfValue))
224 1 : return eDT;
225 1 : break;
226 : }
227 55 : case GDT_Float32:
228 : {
229 55 : if (GDALIsValueExactAs<float>(dfValue))
230 54 : return eDT;
231 1 : break;
232 : }
233 10 : case GDT_Float64:
234 : {
235 10 : return eDT;
236 : }
237 1 : case GDT_Unknown:
238 : case GDT_CInt16:
239 : case GDT_CInt32:
240 : case GDT_CFloat32:
241 : case GDT_CFloat64:
242 : case GDT_TypeCount:
243 1 : break;
244 : }
245 : }
246 :
247 26 : const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex);
248 26 : return GDALDataTypeUnion(eDT, eDT2);
249 : }
250 :
251 : /************************************************************************/
252 : /* GetMinBitsForValue() */
253 : /************************************************************************/
254 26 : static int GetMinBitsForValue(double dValue)
255 : {
256 26 : if (round(dValue) == dValue)
257 : {
258 26 : if (dValue <= std::numeric_limits<GByte>::max() &&
259 9 : dValue >= std::numeric_limits<GByte>::min())
260 4 : return 8;
261 :
262 18 : if (dValue <= std::numeric_limits<GInt8>::max() &&
263 5 : dValue >= std::numeric_limits<GInt8>::min())
264 3 : return 8;
265 :
266 14 : if (dValue <= std::numeric_limits<GInt16>::max() &&
267 4 : dValue >= std::numeric_limits<GInt16>::min())
268 3 : return 16;
269 :
270 9 : if (dValue <= std::numeric_limits<GUInt16>::max() &&
271 2 : dValue >= std::numeric_limits<GUInt16>::min())
272 1 : return 16;
273 :
274 8 : if (dValue <= std::numeric_limits<GInt32>::max() &&
275 2 : dValue >= std::numeric_limits<GInt32>::min())
276 2 : return 32;
277 :
278 5 : if (dValue <= std::numeric_limits<GUInt32>::max() &&
279 1 : dValue >= std::numeric_limits<GUInt32>::min())
280 1 : return 32;
281 :
282 6 : if (dValue <= static_cast<double>(
283 5 : std::numeric_limits<std::uint64_t>::max()) &&
284 : dValue >=
285 2 : static_cast<double>(std::numeric_limits<std::uint64_t>::min()))
286 2 : return 64;
287 : }
288 9 : else if (static_cast<float>(dValue) == dValue)
289 : {
290 4 : return 32;
291 : }
292 :
293 6 : return 64;
294 : }
295 :
296 : /************************************************************************/
297 : /* GDALFindDataType() */
298 : /************************************************************************/
299 :
300 : /**
301 : * \brief Finds the smallest data type able to support the given
302 : * requirements
303 : *
304 : * @param nBits number of bits necessary
305 : * @param bSigned if negative values are necessary
306 : * @param bFloating if non-integer values necessary
307 : * @param bComplex if complex values are necessary
308 : *
309 : * @return a best fit GDALDataType for supporting the requirements
310 : * @since GDAL 2.3
311 : */
312 3396 : GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
313 : int bComplex)
314 : {
315 3396 : if (bComplex)
316 : {
317 723 : nBits = std::max(nBits, !bSigned ? 32 : 16);
318 : } // we don't have complex unsigned data types, so for a complex uint16,
319 : // promote to complex int32
320 3396 : if (bFloating)
321 : {
322 547 : nBits = std::max(nBits, 32);
323 : }
324 :
325 3396 : if (nBits <= 8)
326 : {
327 1800 : return bSigned ? GDT_Int8 : GDT_Byte;
328 : }
329 :
330 1596 : if (nBits <= 16)
331 : {
332 673 : if (bComplex)
333 358 : return GDT_CInt16;
334 315 : if (bSigned)
335 198 : return GDT_Int16;
336 117 : return GDT_UInt16;
337 : }
338 :
339 923 : if (nBits <= 32)
340 : {
341 531 : if (bFloating)
342 : {
343 261 : if (bComplex)
344 95 : return GDT_CFloat32;
345 166 : return GDT_Float32;
346 : }
347 :
348 270 : if (bComplex)
349 93 : return GDT_CInt32;
350 177 : if (bSigned)
351 115 : return GDT_Int32;
352 62 : return GDT_UInt32;
353 : }
354 :
355 392 : if (nBits == 64 && !bFloating && !bComplex)
356 61 : return bSigned ? GDT_Int64 : GDT_UInt64;
357 :
358 331 : if (bComplex)
359 177 : return GDT_CFloat64;
360 :
361 154 : return GDT_Float64;
362 : }
363 :
364 : /************************************************************************/
365 : /* GDALFindDataTypeForValue() */
366 : /************************************************************************/
367 :
368 : /**
369 : * \brief Finds the smallest data type able to support the provided value
370 : *
371 : * @param dValue value to support
372 : * @param bComplex is the value complex
373 : *
374 : * @return a best fit GDALDataType for supporting the value
375 : * @since GDAL 2.3
376 : */
377 26 : GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
378 : {
379 : const bool bFloating =
380 43 : round(dValue) != dValue ||
381 : dValue >
382 42 : static_cast<double>(std::numeric_limits<std::uint64_t>::max()) ||
383 : dValue <
384 16 : static_cast<double>(std::numeric_limits<std::int64_t>::lowest());
385 26 : const bool bSigned = bFloating || dValue < 0;
386 26 : const int nBits = GetMinBitsForValue(dValue);
387 :
388 26 : return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
389 : }
390 :
391 : /************************************************************************/
392 : /* GDALGetDataTypeSizeBytes() */
393 : /************************************************************************/
394 :
395 : /**
396 : * \brief Get data type size in <b>bytes</b>.
397 : *
398 : * Returns the size of a GDT_* type in bytes. In contrast,
399 : * GDALGetDataTypeSize() returns the size in <b>bits</b>.
400 : *
401 : * @param eDataType type, such as GDT_Byte.
402 : * @return the number of bytes or zero if it is not recognised.
403 : */
404 :
405 243510000 : int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
406 :
407 : {
408 243510000 : switch (eDataType)
409 : {
410 76808700 : case GDT_Byte:
411 : case GDT_Int8:
412 76808700 : return 1;
413 :
414 51910200 : case GDT_UInt16:
415 : case GDT_Int16:
416 51910200 : return 2;
417 :
418 77548700 : case GDT_UInt32:
419 : case GDT_Int32:
420 : case GDT_Float32:
421 : case GDT_CInt16:
422 77548700 : return 4;
423 :
424 36857400 : case GDT_Float64:
425 : case GDT_CInt32:
426 : case GDT_CFloat32:
427 : case GDT_UInt64:
428 : case GDT_Int64:
429 36857400 : return 8;
430 :
431 381536 : case GDT_CFloat64:
432 381536 : return 16;
433 :
434 15714 : case GDT_Unknown:
435 : case GDT_TypeCount:
436 15714 : break;
437 : }
438 3011 : return 0;
439 : }
440 :
441 : /************************************************************************/
442 : /* GDALGetDataTypeSizeBits() */
443 : /************************************************************************/
444 :
445 : /**
446 : * \brief Get data type size in <b>bits</b>.
447 : *
448 : * Returns the size of a GDT_* type in bits, <b>not bytes</b>! Use
449 : * GDALGetDataTypeSizeBytes() for bytes.
450 : *
451 : * @param eDataType type, such as GDT_Byte.
452 : * @return the number of bits or zero if it is not recognised.
453 : */
454 :
455 10742 : int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
456 :
457 : {
458 10742 : return GDALGetDataTypeSizeBytes(eDataType) * 8;
459 : }
460 :
461 : /************************************************************************/
462 : /* GDALGetDataTypeSize() */
463 : /************************************************************************/
464 :
465 : /**
466 : * \brief Get data type size in bits. <b>Deprecated</b>.
467 : *
468 : * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
469 : *
470 : * Use GDALGetDataTypeSizeBytes() for bytes.
471 : * Use GDALGetDataTypeSizeBits() for bits.
472 : *
473 : * @param eDataType type, such as GDT_Byte.
474 : * @return the number of bits or zero if it is not recognised.
475 : */
476 :
477 3218340 : int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
478 :
479 : {
480 3218340 : return GDALGetDataTypeSizeBytes(eDataType) * 8;
481 : }
482 :
483 : /************************************************************************/
484 : /* GDALDataTypeIsComplex() */
485 : /************************************************************************/
486 :
487 : /**
488 : * \brief Is data type complex?
489 : *
490 : * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
491 : * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
492 : * component.
493 : */
494 :
495 694807 : int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
496 :
497 : {
498 694807 : switch (eDataType)
499 : {
500 7454 : case GDT_CInt16:
501 : case GDT_CInt32:
502 : case GDT_CFloat32:
503 : case GDT_CFloat64:
504 7454 : return TRUE;
505 :
506 687345 : case GDT_Byte:
507 : case GDT_Int8:
508 : case GDT_Int16:
509 : case GDT_UInt16:
510 : case GDT_Int32:
511 : case GDT_UInt32:
512 : case GDT_Int64:
513 : case GDT_UInt64:
514 : case GDT_Float32:
515 : case GDT_Float64:
516 687345 : return FALSE;
517 :
518 7 : case GDT_Unknown:
519 : case GDT_TypeCount:
520 7 : break;
521 : }
522 8 : return FALSE;
523 : }
524 :
525 : /************************************************************************/
526 : /* GDALDataTypeIsFloating() */
527 : /************************************************************************/
528 :
529 : /**
530 : * \brief Is data type floating? (might be complex)
531 : *
532 : * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float64,
533 : * GDT_CFloat32, GDT_CFloat64)
534 : * @since GDAL 2.3
535 : */
536 :
537 57689 : int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
538 : {
539 57689 : switch (eDataType)
540 : {
541 5644 : case GDT_Float32:
542 : case GDT_Float64:
543 : case GDT_CFloat32:
544 : case GDT_CFloat64:
545 5644 : return TRUE;
546 :
547 52044 : case GDT_Byte:
548 : case GDT_Int8:
549 : case GDT_Int16:
550 : case GDT_UInt16:
551 : case GDT_Int32:
552 : case GDT_UInt32:
553 : case GDT_Int64:
554 : case GDT_UInt64:
555 : case GDT_CInt16:
556 : case GDT_CInt32:
557 52044 : return FALSE;
558 :
559 1 : case GDT_Unknown:
560 : case GDT_TypeCount:
561 1 : break;
562 : }
563 1 : return FALSE;
564 : }
565 :
566 : /************************************************************************/
567 : /* GDALDataTypeIsInteger() */
568 : /************************************************************************/
569 :
570 : /**
571 : * \brief Is data type integer? (might be complex)
572 : *
573 : * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
574 : * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
575 : * @since GDAL 2.3
576 : */
577 :
578 56209 : int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
579 :
580 : {
581 56209 : switch (eDataType)
582 : {
583 54539 : case GDT_Byte:
584 : case GDT_Int8:
585 : case GDT_Int16:
586 : case GDT_UInt16:
587 : case GDT_Int32:
588 : case GDT_UInt32:
589 : case GDT_CInt16:
590 : case GDT_CInt32:
591 : case GDT_UInt64:
592 : case GDT_Int64:
593 54539 : return TRUE;
594 :
595 1668 : case GDT_Float32:
596 : case GDT_Float64:
597 : case GDT_CFloat32:
598 : case GDT_CFloat64:
599 1668 : return FALSE;
600 :
601 1 : case GDT_Unknown:
602 : case GDT_TypeCount:
603 1 : break;
604 : }
605 2 : return FALSE;
606 : }
607 :
608 : /************************************************************************/
609 : /* GDALDataTypeIsSigned() */
610 : /************************************************************************/
611 :
612 : /**
613 : * \brief Is data type signed?
614 : *
615 : * @return TRUE if the passed type is signed.
616 : * @since GDAL 2.3
617 : */
618 :
619 98285 : int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
620 : {
621 98285 : switch (eDataType)
622 : {
623 95229 : case GDT_Byte:
624 : case GDT_UInt16:
625 : case GDT_UInt32:
626 : case GDT_UInt64:
627 95229 : return FALSE;
628 :
629 3056 : case GDT_Int8:
630 : case GDT_Int16:
631 : case GDT_Int32:
632 : case GDT_Int64:
633 : case GDT_Float32:
634 : case GDT_Float64:
635 : case GDT_CInt16:
636 : case GDT_CInt32:
637 : case GDT_CFloat32:
638 : case GDT_CFloat64:
639 3056 : return TRUE;
640 :
641 0 : case GDT_Unknown:
642 : case GDT_TypeCount:
643 0 : break;
644 : }
645 0 : return FALSE;
646 : }
647 :
648 : /************************************************************************/
649 : /* GDALDataTypeIsConversionLossy() */
650 : /************************************************************************/
651 :
652 : /**
653 : * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
654 : *
655 : * @param eTypeFrom input datatype
656 : * @param eTypeTo output datatype
657 : * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
658 : * @since GDAL 2.3
659 : */
660 :
661 51448 : int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
662 : GDALDataType eTypeTo)
663 : {
664 : // E.g cfloat32 -> float32
665 51448 : if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
666 36 : return TRUE;
667 :
668 51411 : eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
669 51411 : eTypeTo = GDALGetNonComplexDataType(eTypeTo);
670 :
671 51410 : if (GDALDataTypeIsInteger(eTypeTo))
672 : {
673 : // E.g. float32 -> int32
674 50215 : if (GDALDataTypeIsFloating(eTypeFrom))
675 4697 : return TRUE;
676 :
677 : // E.g. Int16 to UInt16
678 45518 : const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
679 45518 : const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
680 45517 : if (bIsFromSigned && !bIsToSigned)
681 30 : return TRUE;
682 :
683 : // E.g UInt32 to UInt16
684 45487 : const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
685 45488 : const int nToSize = GDALGetDataTypeSize(eTypeTo);
686 45487 : if (nFromSize > nToSize)
687 28 : return TRUE;
688 :
689 : // E.g UInt16 to Int16
690 45459 : if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
691 9 : return TRUE;
692 :
693 45450 : return FALSE;
694 : }
695 :
696 1197 : if (eTypeTo == GDT_Float32 &&
697 366 : (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
698 362 : eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
699 : eTypeFrom == GDT_Float64))
700 : {
701 37 : return TRUE;
702 : }
703 :
704 1160 : if (eTypeTo == GDT_Float64 &&
705 806 : (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
706 : {
707 4 : return TRUE;
708 : }
709 :
710 1156 : return FALSE;
711 : }
712 :
713 : /************************************************************************/
714 : /* GDALGetDataTypeName() */
715 : /************************************************************************/
716 :
717 : /**
718 : * \brief Get name of data type.
719 : *
720 : * Returns a symbolic name for the data type. This is essentially the
721 : * the enumerated item name with the GDT_ prefix removed. So GDT_Byte returns
722 : * "Byte". The returned strings are static strings and should not be modified
723 : * or freed by the application. These strings are useful for reporting
724 : * datatypes in debug statements, errors and other user output.
725 : *
726 : * @param eDataType type to get name of.
727 : * @return string corresponding to existing data type
728 : * or NULL pointer if invalid type given.
729 : */
730 :
731 63667 : const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
732 :
733 : {
734 63667 : switch (eDataType)
735 : {
736 4952 : case GDT_Unknown:
737 4952 : return "Unknown";
738 :
739 28401 : case GDT_Byte:
740 28401 : return "Byte";
741 :
742 602 : case GDT_Int8:
743 602 : return "Int8";
744 :
745 5029 : case GDT_UInt16:
746 5029 : return "UInt16";
747 :
748 5206 : case GDT_Int16:
749 5206 : return "Int16";
750 :
751 3898 : case GDT_UInt32:
752 3898 : return "UInt32";
753 :
754 3873 : case GDT_Int32:
755 3873 : return "Int32";
756 :
757 637 : case GDT_UInt64:
758 637 : return "UInt64";
759 :
760 619 : case GDT_Int64:
761 619 : return "Int64";
762 :
763 4011 : case GDT_Float32:
764 4011 : return "Float32";
765 :
766 2066 : case GDT_Float64:
767 2066 : return "Float64";
768 :
769 1180 : case GDT_CInt16:
770 1180 : return "CInt16";
771 :
772 1104 : case GDT_CInt32:
773 1104 : return "CInt32";
774 :
775 1125 : case GDT_CFloat32:
776 1125 : return "CFloat32";
777 :
778 964 : case GDT_CFloat64:
779 964 : return "CFloat64";
780 :
781 0 : case GDT_TypeCount:
782 0 : break;
783 : }
784 0 : return nullptr;
785 : }
786 :
787 : /************************************************************************/
788 : /* GDALGetDataTypeByName() */
789 : /************************************************************************/
790 :
791 : /**
792 : * \brief Get data type by symbolic name.
793 : *
794 : * Returns a data type corresponding to the given symbolic name. This
795 : * function is opposite to the GDALGetDataTypeName().
796 : *
797 : * @param pszName string containing the symbolic name of the type.
798 : *
799 : * @return GDAL data type.
800 : */
801 :
802 4801 : GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
803 :
804 : {
805 4801 : VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
806 :
807 14268 : for (int iType = 1; iType < GDT_TypeCount; iType++)
808 : {
809 14236 : const auto eType = static_cast<GDALDataType>(iType);
810 28472 : if (GDALGetDataTypeName(eType) != nullptr &&
811 14236 : EQUAL(GDALGetDataTypeName(eType), pszName))
812 : {
813 4769 : return eType;
814 : }
815 : }
816 :
817 32 : return GDT_Unknown;
818 : }
819 :
820 : /************************************************************************/
821 : /* GDALAdjustValueToDataType() */
822 : /************************************************************************/
823 :
824 : template <class T>
825 144 : static inline void ClampAndRound(double &dfValue, bool &bClamped,
826 : bool &bRounded)
827 : {
828 : // TODO(schwehr): Rework this template. ::min() versus ::lowest.
829 :
830 144 : if (dfValue < static_cast<double>(std::numeric_limits<T>::min()))
831 : {
832 6 : bClamped = true;
833 6 : dfValue = static_cast<double>(std::numeric_limits<T>::min());
834 : }
835 138 : else if (dfValue > static_cast<double>(std::numeric_limits<T>::max()))
836 : {
837 16 : bClamped = true;
838 16 : dfValue = static_cast<double>(std::numeric_limits<T>::max());
839 : }
840 122 : else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
841 : {
842 8 : bRounded = true;
843 8 : dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
844 : }
845 144 : }
846 :
847 : /**
848 : * \brief Adjust a value to the output data type
849 : *
850 : * Adjustment consist in clamping to minimum/maximum values of the data type
851 : * and rounding for integral types.
852 : *
853 : * @param eDT target data type.
854 : * @param dfValue value to adjust.
855 : * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
856 : * been made, or NULL
857 : * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
858 : * been made, or NULL
859 : *
860 : * @return adjusted value
861 : * @since GDAL 2.1
862 : */
863 :
864 212 : double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
865 : int *pbClamped, int *pbRounded)
866 : {
867 212 : bool bClamped = false;
868 212 : bool bRounded = false;
869 212 : switch (eDT)
870 : {
871 87 : case GDT_Byte:
872 87 : ClampAndRound<GByte>(dfValue, bClamped, bRounded);
873 87 : break;
874 6 : case GDT_Int8:
875 6 : ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
876 6 : break;
877 21 : case GDT_Int16:
878 21 : ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
879 21 : break;
880 16 : case GDT_UInt16:
881 16 : ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
882 16 : break;
883 4 : case GDT_Int32:
884 4 : ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
885 4 : break;
886 5 : case GDT_UInt32:
887 5 : ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
888 5 : break;
889 2 : case GDT_Int64:
890 2 : ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
891 2 : break;
892 3 : case GDT_UInt64:
893 3 : ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
894 3 : break;
895 43 : case GDT_Float32:
896 : {
897 43 : if (!std::isfinite(dfValue))
898 6 : break;
899 :
900 : // TODO(schwehr): ::min() versus ::lowest.
901 : // Use ClampAndRound after it has been fixed.
902 37 : if (dfValue < -std::numeric_limits<float>::max())
903 : {
904 1 : bClamped = TRUE;
905 1 : dfValue =
906 1 : static_cast<double>(-std::numeric_limits<float>::max());
907 : }
908 36 : else if (dfValue > std::numeric_limits<float>::max())
909 : {
910 1 : bClamped = TRUE;
911 1 : dfValue =
912 1 : static_cast<double>(std::numeric_limits<float>::max());
913 : }
914 : else
915 : {
916 : // Intentionally loose precision.
917 : // TODO(schwehr): Is the double cast really necessary?
918 : // If so, why? What will fail?
919 35 : dfValue = static_cast<double>(static_cast<float>(dfValue));
920 : }
921 37 : break;
922 : }
923 25 : case GDT_Float64:
924 : case GDT_CInt16:
925 : case GDT_CInt32:
926 : case GDT_CFloat32:
927 : case GDT_CFloat64:
928 : case GDT_Unknown:
929 : case GDT_TypeCount:
930 25 : break;
931 : }
932 212 : if (pbClamped)
933 211 : *pbClamped = bClamped;
934 212 : if (pbRounded)
935 211 : *pbRounded = bRounded;
936 212 : return dfValue;
937 : }
938 :
939 : /************************************************************************/
940 : /* GDALIsValueExactAs() */
941 : /************************************************************************/
942 :
943 : /**
944 : * \brief Check whether the provided value can be exactly represented in a
945 : * data type.
946 : *
947 : * Only implemented for non-complex data types
948 : *
949 : * @param dfValue value to check.
950 : * @param eDT target data type.
951 : *
952 : * @return true if the provided value can be exactly represented in the
953 : * data type.
954 : * @since GDAL 3.10
955 : */
956 249 : bool GDALIsValueExactAs(double dfValue, GDALDataType eDT)
957 : {
958 249 : switch (eDT)
959 : {
960 137 : case GDT_Byte:
961 137 : return GDALIsValueExactAs<uint8_t>(dfValue);
962 5 : case GDT_Int8:
963 5 : return GDALIsValueExactAs<int8_t>(dfValue);
964 18 : case GDT_UInt16:
965 18 : return GDALIsValueExactAs<uint16_t>(dfValue);
966 29 : case GDT_Int16:
967 29 : return GDALIsValueExactAs<int16_t>(dfValue);
968 5 : case GDT_UInt32:
969 5 : return GDALIsValueExactAs<uint32_t>(dfValue);
970 14 : case GDT_Int32:
971 14 : return GDALIsValueExactAs<int32_t>(dfValue);
972 5 : case GDT_UInt64:
973 5 : return GDALIsValueExactAs<uint64_t>(dfValue);
974 5 : case GDT_Int64:
975 5 : return GDALIsValueExactAs<int64_t>(dfValue);
976 22 : case GDT_Float32:
977 22 : return GDALIsValueExactAs<float>(dfValue);
978 8 : case GDT_Float64:
979 8 : return true;
980 1 : case GDT_Unknown:
981 : case GDT_CInt16:
982 : case GDT_CInt32:
983 : case GDT_CFloat32:
984 : case GDT_CFloat64:
985 : case GDT_TypeCount:
986 1 : break;
987 : }
988 1 : return true;
989 : }
990 :
991 : /************************************************************************/
992 : /* GDALGetNonComplexDataType() */
993 : /************************************************************************/
994 : /**
995 : * \brief Return the base data type for the specified input.
996 : *
997 : * If the input data type is complex this function returns the base type
998 : * i.e. the data type of the real and imaginary parts (non-complex).
999 : * If the input data type is already non-complex, then it is returned
1000 : * unchanged.
1001 : *
1002 : * @param eDataType type, such as GDT_CFloat32.
1003 : *
1004 : * @return GDAL data type.
1005 : */
1006 102890 : GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
1007 : {
1008 102890 : switch (eDataType)
1009 : {
1010 91 : case GDT_CInt16:
1011 91 : return GDT_Int16;
1012 23 : case GDT_CInt32:
1013 23 : return GDT_Int32;
1014 64 : case GDT_CFloat32:
1015 64 : return GDT_Float32;
1016 60 : case GDT_CFloat64:
1017 60 : return GDT_Float64;
1018 :
1019 102651 : case GDT_Byte:
1020 : case GDT_UInt16:
1021 : case GDT_UInt32:
1022 : case GDT_UInt64:
1023 : case GDT_Int8:
1024 : case GDT_Int16:
1025 : case GDT_Int32:
1026 : case GDT_Int64:
1027 : case GDT_Float32:
1028 : case GDT_Float64:
1029 102651 : break;
1030 :
1031 0 : case GDT_Unknown:
1032 : case GDT_TypeCount:
1033 0 : break;
1034 : }
1035 102652 : return eDataType;
1036 : }
1037 :
1038 : /************************************************************************/
1039 : /* GDALGetAsyncStatusTypeByName() */
1040 : /************************************************************************/
1041 : /**
1042 : * Get AsyncStatusType by symbolic name.
1043 : *
1044 : * Returns a data type corresponding to the given symbolic name. This
1045 : * function is opposite to the GDALGetAsyncStatusTypeName().
1046 : *
1047 : * @param pszName string containing the symbolic name of the type.
1048 : *
1049 : * @return GDAL AsyncStatus type.
1050 : */
1051 : GDALAsyncStatusType CPL_DLL CPL_STDCALL
1052 0 : GDALGetAsyncStatusTypeByName(const char *pszName)
1053 : {
1054 0 : VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
1055 :
1056 0 : for (int iType = 0; iType < GARIO_TypeCount; iType++)
1057 : {
1058 0 : const auto eType = static_cast<GDALAsyncStatusType>(iType);
1059 0 : if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
1060 0 : EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
1061 : {
1062 0 : return eType;
1063 : }
1064 : }
1065 :
1066 0 : return GARIO_ERROR;
1067 : }
1068 :
1069 : /************************************************************************/
1070 : /* GDALGetAsyncStatusTypeName() */
1071 : /************************************************************************/
1072 :
1073 : /**
1074 : * Get name of AsyncStatus data type.
1075 : *
1076 : * Returns a symbolic name for the AsyncStatus data type. This is essentially
1077 : * the enumerated item name with the GARIO_ prefix removed. So
1078 : * GARIO_COMPLETE returns "COMPLETE". The returned strings are static strings
1079 : * and should not be modified or freed by the application. These strings are
1080 : * useful for reporting datatypes in debug statements, errors and other user
1081 : * output.
1082 : *
1083 : * @param eAsyncStatusType type to get name of.
1084 : * @return string corresponding to type.
1085 : */
1086 :
1087 : const char *CPL_STDCALL
1088 0 : GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
1089 :
1090 : {
1091 0 : switch (eAsyncStatusType)
1092 : {
1093 0 : case GARIO_PENDING:
1094 0 : return "PENDING";
1095 :
1096 0 : case GARIO_UPDATE:
1097 0 : return "UPDATE";
1098 :
1099 0 : case GARIO_ERROR:
1100 0 : return "ERROR";
1101 :
1102 0 : case GARIO_COMPLETE:
1103 0 : return "COMPLETE";
1104 :
1105 0 : default:
1106 0 : return nullptr;
1107 : }
1108 : }
1109 :
1110 : /************************************************************************/
1111 : /* GDALGetPaletteInterpretationName() */
1112 : /************************************************************************/
1113 :
1114 : /**
1115 : * \brief Get name of palette interpretation
1116 : *
1117 : * Returns a symbolic name for the palette interpretation. This is the
1118 : * the enumerated item name with the GPI_ prefix removed. So GPI_Gray returns
1119 : * "Gray". The returned strings are static strings and should not be modified
1120 : * or freed by the application.
1121 : *
1122 : * @param eInterp palette interpretation to get name of.
1123 : * @return string corresponding to palette interpretation.
1124 : */
1125 :
1126 9 : const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
1127 :
1128 : {
1129 9 : switch (eInterp)
1130 : {
1131 0 : case GPI_Gray:
1132 0 : return "Gray";
1133 :
1134 9 : case GPI_RGB:
1135 9 : return "RGB";
1136 :
1137 0 : case GPI_CMYK:
1138 0 : return "CMYK";
1139 :
1140 0 : case GPI_HLS:
1141 0 : return "HLS";
1142 :
1143 0 : default:
1144 0 : return "Unknown";
1145 : }
1146 : }
1147 :
1148 : /************************************************************************/
1149 : /* GDALGetColorInterpretationName() */
1150 : /************************************************************************/
1151 :
1152 : /**
1153 : * \brief Get name of color interpretation
1154 : *
1155 : * Returns a symbolic name for the color interpretation. This is derived from
1156 : * the enumerated item name with the GCI_ prefix removed, but there are some
1157 : * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
1158 : * The returned strings are static strings and should not be modified
1159 : * or freed by the application.
1160 : *
1161 : * @param eInterp color interpretation to get name of.
1162 : * @return string corresponding to color interpretation
1163 : * or NULL pointer if invalid enumerator given.
1164 : */
1165 :
1166 9844 : const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
1167 :
1168 : {
1169 : static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1);
1170 : static_assert(GCI_NIRBand == GCI_IR_Start);
1171 : static_assert(GCI_SAR_Start == GCI_IR_End + 1);
1172 : static_assert(GCI_Max == GCI_SAR_End);
1173 :
1174 9844 : switch (eInterp)
1175 : {
1176 2073 : case GCI_Undefined:
1177 2073 : break;
1178 :
1179 2478 : case GCI_GrayIndex:
1180 2478 : return "Gray";
1181 :
1182 1063 : case GCI_PaletteIndex:
1183 1063 : return "Palette";
1184 :
1185 1178 : case GCI_RedBand:
1186 1178 : return "Red";
1187 :
1188 888 : case GCI_GreenBand:
1189 888 : return "Green";
1190 :
1191 615 : case GCI_BlueBand:
1192 615 : return "Blue";
1193 :
1194 331 : case GCI_AlphaBand:
1195 331 : return "Alpha";
1196 :
1197 112 : case GCI_HueBand:
1198 112 : return "Hue";
1199 :
1200 111 : case GCI_SaturationBand:
1201 111 : return "Saturation";
1202 :
1203 110 : case GCI_LightnessBand:
1204 110 : return "Lightness";
1205 :
1206 117 : case GCI_CyanBand:
1207 117 : return "Cyan";
1208 :
1209 99 : case GCI_MagentaBand:
1210 99 : return "Magenta";
1211 :
1212 81 : case GCI_YellowBand:
1213 81 : return "Yellow";
1214 :
1215 62 : case GCI_BlackBand:
1216 62 : return "Black";
1217 :
1218 38 : case GCI_YCbCr_YBand:
1219 38 : return "YCbCr_Y";
1220 :
1221 35 : case GCI_YCbCr_CbBand:
1222 35 : return "YCbCr_Cb";
1223 :
1224 32 : case GCI_YCbCr_CrBand:
1225 32 : return "YCbCr_Cr";
1226 :
1227 30 : case GCI_PanBand:
1228 30 : return "Pan";
1229 :
1230 39 : case GCI_CoastalBand:
1231 39 : return "Coastal";
1232 :
1233 29 : case GCI_RedEdgeBand:
1234 29 : return "RedEdge";
1235 :
1236 34 : case GCI_NIRBand:
1237 34 : return "NIR";
1238 :
1239 26 : case GCI_SWIRBand:
1240 26 : return "SWIR";
1241 :
1242 23 : case GCI_MWIRBand:
1243 23 : return "MWIR";
1244 :
1245 22 : case GCI_LWIRBand:
1246 22 : return "LWIR";
1247 :
1248 21 : case GCI_TIRBand:
1249 21 : return "TIR";
1250 :
1251 22 : case GCI_OtherIRBand:
1252 22 : return "OtherIR";
1253 :
1254 19 : case GCI_IR_Reserved_1:
1255 19 : return "IR_Reserved_1";
1256 :
1257 18 : case GCI_IR_Reserved_2:
1258 18 : return "IR_Reserved_2";
1259 :
1260 17 : case GCI_IR_Reserved_3:
1261 17 : return "IR_Reserved_3";
1262 :
1263 16 : case GCI_IR_Reserved_4:
1264 16 : return "IR_Reserved_4";
1265 :
1266 15 : case GCI_SAR_Ka_Band:
1267 15 : return "SAR_Ka";
1268 :
1269 14 : case GCI_SAR_K_Band:
1270 14 : return "SAR_K";
1271 :
1272 13 : case GCI_SAR_Ku_Band:
1273 13 : return "SAR_Ku";
1274 :
1275 12 : case GCI_SAR_X_Band:
1276 12 : return "SAR_X";
1277 :
1278 11 : case GCI_SAR_C_Band:
1279 11 : return "SAR_C";
1280 :
1281 10 : case GCI_SAR_S_Band:
1282 10 : return "SAR_S";
1283 :
1284 9 : case GCI_SAR_L_Band:
1285 9 : return "SAR_L";
1286 :
1287 8 : case GCI_SAR_P_Band:
1288 8 : return "SAR_P";
1289 :
1290 7 : case GCI_SAR_Reserved_1:
1291 7 : return "SAR_Reserved_1";
1292 :
1293 6 : case GCI_SAR_Reserved_2:
1294 6 : return "SAR_Reserved_2";
1295 : }
1296 2073 : return "Undefined";
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* GDALGetColorInterpretationByName() */
1301 : /************************************************************************/
1302 :
1303 : /**
1304 : * \brief Get color interpretation by symbolic name.
1305 : *
1306 : * Returns a color interpretation corresponding to the given symbolic name. This
1307 : * function is opposite to the GDALGetColorInterpretationName().
1308 : *
1309 : * @param pszName string containing the symbolic name of the color
1310 : * interpretation.
1311 : *
1312 : * @return GDAL color interpretation.
1313 : *
1314 : * @since GDAL 1.7.0
1315 : */
1316 :
1317 1906 : GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1318 :
1319 : {
1320 1906 : VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1321 : GCI_Undefined);
1322 :
1323 8464 : for (int iType = 0; iType <= GCI_Max; iType++)
1324 : {
1325 8460 : if (EQUAL(GDALGetColorInterpretationName(
1326 : static_cast<GDALColorInterp>(iType)),
1327 : pszName))
1328 : {
1329 1902 : return static_cast<GDALColorInterp>(iType);
1330 : }
1331 : }
1332 :
1333 : // Accept British English spelling
1334 4 : if (EQUAL(pszName, "grey"))
1335 0 : return GCI_GrayIndex;
1336 :
1337 4 : return GCI_Undefined;
1338 : }
1339 :
1340 : /************************************************************************/
1341 : /* GDALGetColorInterpFromSTACCommonName() */
1342 : /************************************************************************/
1343 :
1344 : static const struct
1345 : {
1346 : const char *pszName;
1347 : GDALColorInterp eInterp;
1348 : } asSTACCommonNames[] = {
1349 : {"pan", GCI_PanBand},
1350 : {"coastal", GCI_CoastalBand},
1351 : {"blue", GCI_BlueBand},
1352 : {"green", GCI_GreenBand},
1353 : {"green05", GCI_GreenBand}, // no exact match
1354 : {"yellow", GCI_YellowBand},
1355 : {"red", GCI_RedBand},
1356 : {"rededge", GCI_RedEdgeBand},
1357 : {"rededge071", GCI_RedEdgeBand}, // no exact match
1358 : {"rededge075", GCI_RedEdgeBand}, // no exact match
1359 : {"rededge078", GCI_RedEdgeBand}, // no exact match
1360 : {"nir", GCI_NIRBand},
1361 : {"nir08", GCI_NIRBand}, // no exact match
1362 : {"nir09", GCI_NIRBand}, // no exact match
1363 : {"cirrus", GCI_NIRBand}, // no exact match
1364 : {nullptr,
1365 : GCI_SWIRBand}, // so that GDALGetSTACCommonNameFromColorInterp returns null on GCI_SWIRBand
1366 : {"swir16", GCI_SWIRBand}, // no exact match
1367 : {"swir22", GCI_SWIRBand}, // no exact match
1368 : {"lwir", GCI_LWIRBand},
1369 : {"lwir11", GCI_LWIRBand}, // no exact match
1370 : {"lwir12", GCI_LWIRBand}, // no exact match
1371 : };
1372 :
1373 : /** Get color interpreetation from STAC eo:common_name
1374 : *
1375 : * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1376 : *
1377 : * @since GDAL 3.10
1378 : */
1379 21 : GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName)
1380 : {
1381 :
1382 84 : for (const auto &sAssoc : asSTACCommonNames)
1383 : {
1384 84 : if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName))
1385 21 : return sAssoc.eInterp;
1386 : }
1387 0 : return GCI_Undefined;
1388 : }
1389 :
1390 : /************************************************************************/
1391 : /* GDALGetSTACCommonNameFromColorInterp() */
1392 : /************************************************************************/
1393 :
1394 : /** Get STAC eo:common_name from GDAL color interpretation
1395 : *
1396 : * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1397 : *
1398 : * @return nullptr if there is no match
1399 : *
1400 : * @since GDAL 3.10
1401 : */
1402 95 : const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp)
1403 : {
1404 1705 : for (const auto &sAssoc : asSTACCommonNames)
1405 : {
1406 1632 : if (eInterp == sAssoc.eInterp)
1407 22 : return sAssoc.pszName;
1408 : }
1409 73 : return nullptr;
1410 : }
1411 :
1412 : /************************************************************************/
1413 : /* GDALGetRandomRasterSample() */
1414 : /************************************************************************/
1415 :
1416 : /** Undocumented
1417 : * @param hBand undocumented.
1418 : * @param nSamples undocumented.
1419 : * @param pafSampleBuf undocumented.
1420 : * @return undocumented
1421 : */
1422 0 : int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
1423 : float *pafSampleBuf)
1424 :
1425 : {
1426 0 : VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
1427 :
1428 : GDALRasterBand *poBand;
1429 :
1430 0 : poBand = GDALRasterBand::FromHandle(
1431 : GDALGetRasterSampleOverview(hBand, nSamples));
1432 0 : CPLAssert(nullptr != poBand);
1433 :
1434 : /* -------------------------------------------------------------------- */
1435 : /* Figure out the ratio of blocks we will read to get an */
1436 : /* approximate value. */
1437 : /* -------------------------------------------------------------------- */
1438 0 : int bGotNoDataValue = FALSE;
1439 :
1440 0 : double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
1441 :
1442 0 : int nBlockXSize = 0;
1443 0 : int nBlockYSize = 0;
1444 0 : poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1445 :
1446 : const int nBlocksPerRow =
1447 0 : (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
1448 : const int nBlocksPerColumn =
1449 0 : (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
1450 :
1451 0 : const GIntBig nBlockPixels =
1452 0 : static_cast<GIntBig>(nBlockXSize) * nBlockYSize;
1453 0 : const GIntBig nBlockCount =
1454 0 : static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn;
1455 :
1456 0 : if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
1457 : nBlockCount == 0)
1458 : {
1459 0 : CPLError(CE_Failure, CPLE_AppDefined,
1460 : "GDALGetRandomRasterSample(): returning because band"
1461 : " appears degenerate.");
1462 :
1463 0 : return FALSE;
1464 : }
1465 :
1466 : int nSampleRate = static_cast<int>(
1467 0 : std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
1468 :
1469 0 : if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
1470 0 : nSampleRate--;
1471 :
1472 0 : while (nSampleRate > 1 &&
1473 0 : ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
1474 0 : nSampleRate--;
1475 :
1476 0 : int nBlockSampleRate = 1;
1477 :
1478 0 : if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
1479 0 : nBlockSampleRate = static_cast<int>(std::max<GIntBig>(
1480 0 : 1,
1481 0 : nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1))));
1482 :
1483 0 : int nActualSamples = 0;
1484 :
1485 0 : for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount;
1486 0 : iSampleBlock += nSampleRate)
1487 : {
1488 :
1489 0 : const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow);
1490 0 : const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow);
1491 :
1492 : GDALRasterBlock *const poBlock =
1493 0 : poBand->GetLockedBlockRef(iXBlock, iYBlock);
1494 0 : if (poBlock == nullptr)
1495 0 : continue;
1496 0 : void *pDataRef = poBlock->GetDataRef();
1497 :
1498 0 : int iXValid = nBlockXSize;
1499 0 : if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
1500 0 : iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
1501 :
1502 0 : int iYValid = nBlockYSize;
1503 0 : if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
1504 0 : iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
1505 :
1506 0 : int iRemainder = 0;
1507 :
1508 0 : for (int iY = 0; iY < iYValid; iY++)
1509 : {
1510 0 : int iX = iRemainder; // Used after for.
1511 0 : for (; iX < iXValid; iX += nBlockSampleRate)
1512 : {
1513 0 : double dfValue = 0.0;
1514 0 : const int iOffset = iX + iY * nBlockXSize;
1515 :
1516 0 : switch (poBlock->GetDataType())
1517 : {
1518 0 : case GDT_Byte:
1519 0 : dfValue =
1520 0 : reinterpret_cast<const GByte *>(pDataRef)[iOffset];
1521 0 : break;
1522 0 : case GDT_Int8:
1523 0 : dfValue =
1524 0 : reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
1525 0 : break;
1526 0 : case GDT_UInt16:
1527 0 : dfValue = reinterpret_cast<const GUInt16 *>(
1528 0 : pDataRef)[iOffset];
1529 0 : break;
1530 0 : case GDT_Int16:
1531 0 : dfValue =
1532 0 : reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
1533 0 : break;
1534 0 : case GDT_UInt32:
1535 0 : dfValue = reinterpret_cast<const GUInt32 *>(
1536 0 : pDataRef)[iOffset];
1537 0 : break;
1538 0 : case GDT_Int32:
1539 0 : dfValue =
1540 0 : reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
1541 0 : break;
1542 0 : case GDT_UInt64:
1543 0 : dfValue = static_cast<double>(
1544 : reinterpret_cast<const std::uint64_t *>(
1545 0 : pDataRef)[iOffset]);
1546 0 : break;
1547 0 : case GDT_Int64:
1548 0 : dfValue = static_cast<double>(
1549 : reinterpret_cast<const std::int64_t *>(
1550 0 : pDataRef)[iOffset]);
1551 0 : break;
1552 0 : case GDT_Float32:
1553 0 : dfValue =
1554 0 : reinterpret_cast<const float *>(pDataRef)[iOffset];
1555 0 : break;
1556 0 : case GDT_Float64:
1557 0 : dfValue =
1558 0 : reinterpret_cast<const double *>(pDataRef)[iOffset];
1559 0 : break;
1560 0 : case GDT_CInt16:
1561 : {
1562 : // TODO(schwehr): Clean up casts.
1563 0 : const double dfReal = reinterpret_cast<const GInt16 *>(
1564 0 : pDataRef)[iOffset * 2];
1565 0 : const double dfImag = reinterpret_cast<const GInt16 *>(
1566 0 : pDataRef)[iOffset * 2 + 1];
1567 0 : dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1568 0 : break;
1569 : }
1570 0 : case GDT_CInt32:
1571 : {
1572 0 : const double dfReal = reinterpret_cast<const GInt32 *>(
1573 0 : pDataRef)[iOffset * 2];
1574 0 : const double dfImag = reinterpret_cast<const GInt32 *>(
1575 0 : pDataRef)[iOffset * 2 + 1];
1576 0 : dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1577 0 : break;
1578 : }
1579 0 : case GDT_CFloat32:
1580 : {
1581 0 : const double dfReal = reinterpret_cast<const float *>(
1582 0 : pDataRef)[iOffset * 2];
1583 0 : const double dfImag = reinterpret_cast<const float *>(
1584 0 : pDataRef)[iOffset * 2 + 1];
1585 0 : dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1586 0 : break;
1587 : }
1588 0 : case GDT_CFloat64:
1589 : {
1590 0 : const double dfReal = reinterpret_cast<const double *>(
1591 0 : pDataRef)[iOffset * 2];
1592 0 : const double dfImag = reinterpret_cast<const double *>(
1593 0 : pDataRef)[iOffset * 2 + 1];
1594 0 : dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1595 0 : break;
1596 : }
1597 0 : case GDT_Unknown:
1598 : case GDT_TypeCount:
1599 0 : CPLAssert(false);
1600 : }
1601 :
1602 0 : if (bGotNoDataValue && dfValue == dfNoDataValue)
1603 0 : continue;
1604 :
1605 0 : if (nActualSamples < nSamples)
1606 0 : pafSampleBuf[nActualSamples++] =
1607 0 : static_cast<float>(dfValue);
1608 : }
1609 :
1610 0 : iRemainder = iX - iXValid;
1611 : }
1612 :
1613 0 : poBlock->DropLock();
1614 : }
1615 :
1616 0 : return nActualSamples;
1617 : }
1618 :
1619 : /************************************************************************/
1620 : /* gdal::GCP */
1621 : /************************************************************************/
1622 :
1623 : namespace gdal
1624 : {
1625 : /** Constructor. */
1626 25459 : GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
1627 25459 : double dfX, double dfY, double dfZ)
1628 25459 : : gcp{CPLStrdup(pszId ? pszId : ""),
1629 25459 : CPLStrdup(pszInfo ? pszInfo : ""),
1630 : dfPixel,
1631 : dfLine,
1632 : dfX,
1633 : dfY,
1634 25459 : dfZ}
1635 : {
1636 : static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1637 25459 : }
1638 :
1639 : /** Destructor. */
1640 313764 : GCP::~GCP()
1641 : {
1642 156882 : CPLFree(gcp.pszId);
1643 156882 : CPLFree(gcp.pszInfo);
1644 156882 : }
1645 :
1646 : /** Constructor from a C GDAL_GCP instance. */
1647 106913 : GCP::GCP(const GDAL_GCP &other)
1648 106913 : : gcp{CPLStrdup(other.pszId),
1649 213826 : CPLStrdup(other.pszInfo),
1650 106913 : other.dfGCPPixel,
1651 106913 : other.dfGCPLine,
1652 106913 : other.dfGCPX,
1653 106913 : other.dfGCPY,
1654 106913 : other.dfGCPZ}
1655 : {
1656 106913 : }
1657 :
1658 : /** Copy constructor. */
1659 37933 : GCP::GCP(const GCP &other) : GCP(other.gcp)
1660 : {
1661 37933 : }
1662 :
1663 : /** Move constructor. */
1664 24510 : GCP::GCP(GCP &&other)
1665 24510 : : gcp{other.gcp.pszId, other.gcp.pszInfo, other.gcp.dfGCPPixel,
1666 24510 : other.gcp.dfGCPLine, other.gcp.dfGCPX, other.gcp.dfGCPY,
1667 24510 : other.gcp.dfGCPZ}
1668 : {
1669 24510 : other.gcp.pszId = nullptr;
1670 24510 : other.gcp.pszInfo = nullptr;
1671 24510 : }
1672 :
1673 : /** Copy assignment operator. */
1674 1 : GCP &GCP::operator=(const GCP &other)
1675 : {
1676 1 : if (this != &other)
1677 : {
1678 1 : CPLFree(gcp.pszId);
1679 1 : CPLFree(gcp.pszInfo);
1680 1 : gcp = other.gcp;
1681 1 : gcp.pszId = CPLStrdup(other.gcp.pszId);
1682 1 : gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1683 : }
1684 1 : return *this;
1685 : }
1686 :
1687 : /** Move assignment operator. */
1688 1 : GCP &GCP::operator=(GCP &&other)
1689 : {
1690 1 : if (this != &other)
1691 : {
1692 1 : CPLFree(gcp.pszId);
1693 1 : CPLFree(gcp.pszInfo);
1694 1 : gcp = other.gcp;
1695 1 : other.gcp.pszId = nullptr;
1696 1 : other.gcp.pszInfo = nullptr;
1697 : }
1698 1 : return *this;
1699 : }
1700 :
1701 : /** Set the 'id' member of the GCP. */
1702 24479 : void GCP::SetId(const char *pszId)
1703 : {
1704 24479 : CPLFree(gcp.pszId);
1705 24479 : gcp.pszId = CPLStrdup(pszId ? pszId : "");
1706 24479 : }
1707 :
1708 : /** Set the 'info' member of the GCP. */
1709 24479 : void GCP::SetInfo(const char *pszInfo)
1710 : {
1711 24479 : CPLFree(gcp.pszInfo);
1712 24479 : gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
1713 24479 : }
1714 :
1715 : /** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1716 : /*static */
1717 682 : const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
1718 : {
1719 682 : return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
1720 : }
1721 :
1722 : /** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1723 : /*static*/
1724 337 : std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
1725 : {
1726 337 : return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
1727 : }
1728 :
1729 : } /* namespace gdal */
1730 :
1731 : /************************************************************************/
1732 : /* GDALInitGCPs() */
1733 : /************************************************************************/
1734 :
1735 : /** Initialize an array of GCPs.
1736 : *
1737 : * Numeric values are initialized to 0 and strings to the empty string ""
1738 : * allocated with CPLStrdup()
1739 : * An array initialized with GDALInitGCPs() must be de-initialized with
1740 : * GDALDeinitGCPs().
1741 : *
1742 : * @param nCount number of GCPs in psGCP
1743 : * @param psGCP array of GCPs of size nCount.
1744 : */
1745 1320 : void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1746 :
1747 : {
1748 1320 : if (nCount > 0)
1749 : {
1750 674 : VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
1751 : }
1752 :
1753 6084 : for (int iGCP = 0; iGCP < nCount; iGCP++)
1754 : {
1755 4764 : memset(psGCP, 0, sizeof(GDAL_GCP));
1756 4764 : psGCP->pszId = CPLStrdup("");
1757 4764 : psGCP->pszInfo = CPLStrdup("");
1758 4764 : psGCP++;
1759 : }
1760 : }
1761 :
1762 : /************************************************************************/
1763 : /* GDALDeinitGCPs() */
1764 : /************************************************************************/
1765 :
1766 : /** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1767 : *
1768 : * @param nCount number of GCPs in psGCP
1769 : * @param psGCP array of GCPs of size nCount.
1770 : */
1771 1420 : void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1772 :
1773 : {
1774 1420 : if (nCount > 0)
1775 : {
1776 470 : VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
1777 : }
1778 :
1779 6567 : for (int iGCP = 0; iGCP < nCount; iGCP++)
1780 : {
1781 5147 : CPLFree(psGCP->pszId);
1782 5147 : CPLFree(psGCP->pszInfo);
1783 5147 : psGCP++;
1784 : }
1785 : }
1786 :
1787 : /************************************************************************/
1788 : /* GDALDuplicateGCPs() */
1789 : /************************************************************************/
1790 :
1791 : /** Duplicate an array of GCPs
1792 : *
1793 : * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1794 : *
1795 : * @param nCount number of GCPs in psGCP
1796 : * @param pasGCPList array of GCPs of size nCount.
1797 : */
1798 730 : GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
1799 :
1800 : {
1801 : GDAL_GCP *pasReturn =
1802 730 : static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
1803 730 : GDALInitGCPs(nCount, pasReturn);
1804 :
1805 3954 : for (int iGCP = 0; iGCP < nCount; iGCP++)
1806 : {
1807 3224 : CPLFree(pasReturn[iGCP].pszId);
1808 3224 : pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
1809 :
1810 3224 : CPLFree(pasReturn[iGCP].pszInfo);
1811 3224 : pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
1812 :
1813 3224 : pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
1814 3224 : pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
1815 3224 : pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
1816 3224 : pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
1817 3224 : pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
1818 : }
1819 :
1820 730 : return pasReturn;
1821 : }
1822 :
1823 : /************************************************************************/
1824 : /* GDALFindAssociatedFile() */
1825 : /************************************************************************/
1826 :
1827 : /**
1828 : * \brief Find file with alternate extension.
1829 : *
1830 : * Finds the file with the indicated extension, substituting it in place
1831 : * of the extension of the base filename. Generally used to search for
1832 : * associated files like world files .RPB files, etc. If necessary, the
1833 : * extension will be tried in both upper and lower case. If a sibling file
1834 : * list is available it will be used instead of doing VSIStatExL() calls to
1835 : * probe the file system.
1836 : *
1837 : * Note that the result is a dynamic CPLString so this method should not
1838 : * be used in a situation where there could be cross heap issues. It is
1839 : * generally imprudent for application built on GDAL to use this function
1840 : * unless they are sure they will always use the same runtime heap as GDAL.
1841 : *
1842 : * @param pszBaseFilename the filename relative to which to search.
1843 : * @param pszExt the target extension in either upper or lower case.
1844 : * @param papszSiblingFiles the list of files in the same directory as
1845 : * pszBaseFilename or NULL if they are not known.
1846 : * @param nFlags special options controlling search. None defined yet, just
1847 : * pass 0.
1848 : *
1849 : * @return an empty string if the target is not found, otherwise the target
1850 : * file with similar path style as the pszBaseFilename.
1851 : */
1852 :
1853 : /**/
1854 : /**/
1855 :
1856 37655 : CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
1857 : const char *pszExt,
1858 : CSLConstList papszSiblingFiles,
1859 : CPL_UNUSED int nFlags)
1860 :
1861 : {
1862 75309 : CPLString osTarget = CPLResetExtensionSafe(pszBaseFilename, pszExt);
1863 :
1864 75045 : if (papszSiblingFiles == nullptr ||
1865 : // cppcheck-suppress knownConditionTrueFalse
1866 37390 : !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
1867 : {
1868 : VSIStatBufL sStatBuf;
1869 :
1870 264 : if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
1871 : {
1872 249 : CPLString osAltExt = pszExt;
1873 :
1874 249 : if (islower(static_cast<unsigned char>(pszExt[0])))
1875 0 : osAltExt = osAltExt.toupper();
1876 : else
1877 249 : osAltExt = osAltExt.tolower();
1878 :
1879 249 : osTarget = CPLResetExtensionSafe(pszBaseFilename, osAltExt);
1880 :
1881 249 : if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
1882 247 : return "";
1883 : }
1884 : }
1885 : else
1886 : {
1887 : const int iSibling =
1888 37391 : CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
1889 37391 : if (iSibling < 0)
1890 37340 : return "";
1891 :
1892 51 : osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
1893 51 : osTarget += papszSiblingFiles[iSibling];
1894 : }
1895 :
1896 68 : return osTarget;
1897 : }
1898 :
1899 : /************************************************************************/
1900 : /* GDALLoadOziMapFile() */
1901 : /************************************************************************/
1902 :
1903 : /** Helper function for translator implementer wanting support for OZI .map
1904 : *
1905 : * @param pszFilename filename of .tab file
1906 : * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1907 : * @param ppszWKT output pointer to a string that will be allocated with
1908 : * CPLMalloc().
1909 : * @param pnGCPCount output pointer to GCP count.
1910 : * @param ppasGCPs outputer pointer to an array of GCPs.
1911 : * @return TRUE in case of success, FALSE otherwise.
1912 : */
1913 0 : int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
1914 : double *padfGeoTransform, char **ppszWKT,
1915 : int *pnGCPCount, GDAL_GCP **ppasGCPs)
1916 :
1917 : {
1918 0 : VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
1919 0 : VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
1920 0 : VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
1921 0 : VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
1922 :
1923 0 : char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
1924 :
1925 0 : if (!papszLines)
1926 0 : return FALSE;
1927 :
1928 0 : int nLines = CSLCount(papszLines);
1929 :
1930 : // Check the OziExplorer Map file signature
1931 0 : if (nLines < 5 ||
1932 0 : !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
1933 : {
1934 0 : CPLError(CE_Failure, CPLE_AppDefined,
1935 : "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
1936 : "format.",
1937 : pszFilename);
1938 0 : CSLDestroy(papszLines);
1939 0 : return FALSE;
1940 : }
1941 :
1942 0 : OGRSpatialReference oSRS;
1943 0 : OGRErr eErr = OGRERR_NONE;
1944 :
1945 : /* The Map Scale Factor has been introduced recently on the 6th line */
1946 : /* and is a trick that is used to just change that line without changing */
1947 : /* the rest of the MAP file but providing an imagery that is smaller or
1948 : * larger */
1949 : /* so we have to correct the pixel/line values read in the .MAP file so they
1950 : */
1951 : /* match the actual imagery dimension. Well, this is a bad summary of what
1952 : */
1953 : /* is explained at
1954 : * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
1955 0 : double dfMSF = 1;
1956 :
1957 0 : for (int iLine = 5; iLine < nLines; iLine++)
1958 : {
1959 0 : if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
1960 : {
1961 0 : dfMSF = CPLAtof(papszLines[iLine] + 4);
1962 0 : if (dfMSF <= 0.01) /* Suspicious values */
1963 : {
1964 0 : CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
1965 0 : dfMSF = 1;
1966 : }
1967 : }
1968 : }
1969 :
1970 0 : eErr = oSRS.importFromOzi(papszLines);
1971 0 : if (eErr == OGRERR_NONE)
1972 : {
1973 0 : if (ppszWKT != nullptr)
1974 0 : oSRS.exportToWkt(ppszWKT);
1975 : }
1976 :
1977 0 : int nCoordinateCount = 0;
1978 : // TODO(schwehr): Initialize asGCPs.
1979 : GDAL_GCP asGCPs[30];
1980 :
1981 : // Iterate all lines in the MAP-file
1982 0 : for (int iLine = 5; iLine < nLines; iLine++)
1983 : {
1984 0 : char **papszTok = CSLTokenizeString2(
1985 0 : papszLines[iLine], ",",
1986 : CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
1987 :
1988 0 : if (CSLCount(papszTok) < 12)
1989 : {
1990 0 : CSLDestroy(papszTok);
1991 0 : continue;
1992 : }
1993 :
1994 0 : if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
1995 0 : !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
1996 : nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
1997 : {
1998 0 : bool bReadOk = false;
1999 0 : double dfLon = 0.0;
2000 0 : double dfLat = 0.0;
2001 :
2002 0 : if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
2003 0 : !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
2004 : {
2005 : // Set geographical coordinates of the pixels
2006 0 : dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
2007 0 : dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
2008 0 : if (EQUAL(papszTok[11], "W"))
2009 0 : dfLon = -dfLon;
2010 0 : if (EQUAL(papszTok[8], "S"))
2011 0 : dfLat = -dfLat;
2012 :
2013 : // Transform from the geographical coordinates into projected
2014 : // coordinates.
2015 0 : if (eErr == OGRERR_NONE)
2016 : {
2017 0 : OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
2018 :
2019 0 : if (poLongLat)
2020 : {
2021 0 : oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2022 0 : poLongLat->SetAxisMappingStrategy(
2023 : OAMS_TRADITIONAL_GIS_ORDER);
2024 :
2025 : OGRCoordinateTransformation *poTransform =
2026 0 : OGRCreateCoordinateTransformation(poLongLat, &oSRS);
2027 0 : if (poTransform)
2028 : {
2029 0 : bReadOk = CPL_TO_BOOL(
2030 : poTransform->Transform(1, &dfLon, &dfLat));
2031 0 : delete poTransform;
2032 : }
2033 0 : delete poLongLat;
2034 : }
2035 0 : }
2036 : }
2037 0 : else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
2038 : {
2039 : // Set cartesian coordinates of the pixels.
2040 0 : dfLon = CPLAtofM(papszTok[14]);
2041 0 : dfLat = CPLAtofM(papszTok[15]);
2042 0 : bReadOk = true;
2043 :
2044 : // if ( EQUAL(papszTok[16], "S") )
2045 : // dfLat = -dfLat;
2046 : }
2047 :
2048 0 : if (bReadOk)
2049 : {
2050 0 : GDALInitGCPs(1, asGCPs + nCoordinateCount);
2051 :
2052 : // Set pixel/line part
2053 0 : asGCPs[nCoordinateCount].dfGCPPixel =
2054 0 : CPLAtofM(papszTok[2]) / dfMSF;
2055 0 : asGCPs[nCoordinateCount].dfGCPLine =
2056 0 : CPLAtofM(papszTok[3]) / dfMSF;
2057 :
2058 0 : asGCPs[nCoordinateCount].dfGCPX = dfLon;
2059 0 : asGCPs[nCoordinateCount].dfGCPY = dfLat;
2060 :
2061 0 : nCoordinateCount++;
2062 : }
2063 : }
2064 :
2065 0 : CSLDestroy(papszTok);
2066 : }
2067 :
2068 0 : CSLDestroy(papszLines);
2069 :
2070 0 : if (nCoordinateCount == 0)
2071 : {
2072 0 : CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
2073 : pszFilename);
2074 0 : return FALSE;
2075 : }
2076 :
2077 : /* -------------------------------------------------------------------- */
2078 : /* Try to convert the GCPs into a geotransform definition, if */
2079 : /* possible. Otherwise we will need to use them as GCPs. */
2080 : /* -------------------------------------------------------------------- */
2081 0 : if (!GDALGCPsToGeoTransform(
2082 : nCoordinateCount, asGCPs, padfGeoTransform,
2083 0 : CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
2084 : {
2085 0 : if (pnGCPCount && ppasGCPs)
2086 : {
2087 0 : CPLDebug(
2088 : "GDAL",
2089 : "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
2090 : "first order geotransform. Using points as GCPs.",
2091 : pszFilename);
2092 :
2093 0 : *ppasGCPs = static_cast<GDAL_GCP *>(
2094 0 : CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
2095 0 : memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
2096 0 : *pnGCPCount = nCoordinateCount;
2097 : }
2098 : }
2099 : else
2100 : {
2101 0 : GDALDeinitGCPs(nCoordinateCount, asGCPs);
2102 : }
2103 :
2104 0 : return TRUE;
2105 : }
2106 :
2107 : /************************************************************************/
2108 : /* GDALReadOziMapFile() */
2109 : /************************************************************************/
2110 :
2111 : /** Helper function for translator implementer wanting support for OZI .map
2112 : *
2113 : * @param pszBaseFilename filename whose basename will help building the .map
2114 : * filename.
2115 : * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2116 : * @param ppszWKT output pointer to a string that will be allocated with
2117 : * CPLMalloc().
2118 : * @param pnGCPCount output pointer to GCP count.
2119 : * @param ppasGCPs outputer pointer to an array of GCPs.
2120 : * @return TRUE in case of success, FALSE otherwise.
2121 : */
2122 0 : int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
2123 : double *padfGeoTransform, char **ppszWKT,
2124 : int *pnGCPCount, GDAL_GCP **ppasGCPs)
2125 :
2126 : {
2127 : /* -------------------------------------------------------------------- */
2128 : /* Try lower case, then upper case. */
2129 : /* -------------------------------------------------------------------- */
2130 0 : std::string osOzi = CPLResetExtensionSafe(pszBaseFilename, "map");
2131 :
2132 0 : VSILFILE *fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
2133 :
2134 0 : if (fpOzi == nullptr && VSIIsCaseSensitiveFS(osOzi.c_str()))
2135 : {
2136 0 : osOzi = CPLResetExtensionSafe(pszBaseFilename, "MAP");
2137 0 : fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
2138 : }
2139 :
2140 0 : if (fpOzi == nullptr)
2141 0 : return FALSE;
2142 :
2143 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
2144 :
2145 : /* -------------------------------------------------------------------- */
2146 : /* We found the file, now load and parse it. */
2147 : /* -------------------------------------------------------------------- */
2148 0 : return GDALLoadOziMapFile(osOzi.c_str(), padfGeoTransform, ppszWKT,
2149 0 : pnGCPCount, ppasGCPs);
2150 : }
2151 :
2152 : /************************************************************************/
2153 : /* GDALLoadTabFile() */
2154 : /* */
2155 : /************************************************************************/
2156 :
2157 : /** Helper function for translator implementer wanting support for MapInfo
2158 : * .tab files.
2159 : *
2160 : * @param pszFilename filename of .tab
2161 : * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2162 : * @param ppszWKT output pointer to a string that will be allocated with
2163 : * CPLMalloc().
2164 : * @param pnGCPCount output pointer to GCP count.
2165 : * @param ppasGCPs outputer pointer to an array of GCPs.
2166 : * @return TRUE in case of success, FALSE otherwise.
2167 : */
2168 14 : int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
2169 : double *padfGeoTransform, char **ppszWKT,
2170 : int *pnGCPCount, GDAL_GCP **ppasGCPs)
2171 :
2172 : {
2173 14 : char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
2174 :
2175 14 : if (!papszLines)
2176 0 : return FALSE;
2177 :
2178 14 : char **papszTok = nullptr;
2179 14 : bool bTypeRasterFound = false;
2180 14 : bool bInsideTableDef = false;
2181 14 : int nCoordinateCount = 0;
2182 : GDAL_GCP asGCPs[256]; // TODO(schwehr): Initialize.
2183 14 : const int numLines = CSLCount(papszLines);
2184 :
2185 : // Iterate all lines in the TAB-file
2186 196 : for (int iLine = 0; iLine < numLines; iLine++)
2187 : {
2188 182 : CSLDestroy(papszTok);
2189 : papszTok =
2190 182 : CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
2191 :
2192 182 : if (CSLCount(papszTok) < 2)
2193 28 : continue;
2194 :
2195 : // Did we find table definition
2196 154 : if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
2197 : {
2198 14 : bInsideTableDef = TRUE;
2199 : }
2200 140 : else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
2201 : {
2202 : // Only RASTER-type will be handled
2203 14 : if (EQUAL(papszTok[1], "RASTER"))
2204 : {
2205 14 : bTypeRasterFound = true;
2206 : }
2207 : else
2208 : {
2209 0 : CSLDestroy(papszTok);
2210 0 : CSLDestroy(papszLines);
2211 0 : return FALSE;
2212 : }
2213 : }
2214 84 : else if (bTypeRasterFound && bInsideTableDef &&
2215 210 : CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
2216 : nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2217 : {
2218 56 : GDALInitGCPs(1, asGCPs + nCoordinateCount);
2219 :
2220 56 : asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
2221 56 : asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
2222 56 : asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
2223 56 : asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
2224 56 : if (papszTok[5] != nullptr)
2225 : {
2226 56 : CPLFree(asGCPs[nCoordinateCount].pszId);
2227 56 : asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
2228 : }
2229 :
2230 56 : nCoordinateCount++;
2231 : }
2232 70 : else if (bTypeRasterFound && bInsideTableDef &&
2233 28 : EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
2234 : {
2235 28 : OGRSpatialReference oSRS;
2236 :
2237 14 : if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
2238 28 : oSRS.exportToWkt(ppszWKT);
2239 : }
2240 70 : else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
2241 14 : EQUAL(papszTok[1], "degree"))
2242 : {
2243 : /*
2244 : ** If we have units of "degree", but a projected coordinate
2245 : ** system we need to convert it to geographic. See to01_02.TAB.
2246 : */
2247 0 : if (ppszWKT != nullptr && *ppszWKT != nullptr &&
2248 0 : STARTS_WITH_CI(*ppszWKT, "PROJCS"))
2249 : {
2250 0 : OGRSpatialReference oSRS;
2251 0 : oSRS.importFromWkt(*ppszWKT);
2252 :
2253 0 : OGRSpatialReference oSRSGeogCS;
2254 0 : oSRSGeogCS.CopyGeogCSFrom(&oSRS);
2255 0 : CPLFree(*ppszWKT);
2256 :
2257 0 : oSRSGeogCS.exportToWkt(ppszWKT);
2258 : }
2259 : }
2260 : }
2261 :
2262 14 : CSLDestroy(papszTok);
2263 14 : CSLDestroy(papszLines);
2264 :
2265 14 : if (nCoordinateCount == 0)
2266 : {
2267 0 : CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
2268 : pszFilename);
2269 0 : return FALSE;
2270 : }
2271 :
2272 : /* -------------------------------------------------------------------- */
2273 : /* Try to convert the GCPs into a geotransform definition, if */
2274 : /* possible. Otherwise we will need to use them as GCPs. */
2275 : /* -------------------------------------------------------------------- */
2276 14 : if (!GDALGCPsToGeoTransform(
2277 : nCoordinateCount, asGCPs, padfGeoTransform,
2278 14 : CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
2279 : {
2280 0 : if (pnGCPCount && ppasGCPs)
2281 : {
2282 0 : CPLDebug("GDAL",
2283 : "GDALLoadTabFile(%s) found file, was not able to derive a "
2284 : "first order geotransform. Using points as GCPs.",
2285 : pszFilename);
2286 :
2287 0 : *ppasGCPs = static_cast<GDAL_GCP *>(
2288 0 : CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
2289 0 : memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
2290 0 : *pnGCPCount = nCoordinateCount;
2291 : }
2292 : }
2293 : else
2294 : {
2295 14 : GDALDeinitGCPs(nCoordinateCount, asGCPs);
2296 : }
2297 :
2298 14 : return TRUE;
2299 : }
2300 :
2301 : /************************************************************************/
2302 : /* GDALReadTabFile() */
2303 : /************************************************************************/
2304 :
2305 : /** Helper function for translator implementer wanting support for MapInfo
2306 : * .tab files.
2307 : *
2308 : * @param pszBaseFilename filename whose basename will help building the .tab
2309 : * filename.
2310 : * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2311 : * @param ppszWKT output pointer to a string that will be allocated with
2312 : * CPLMalloc().
2313 : * @param pnGCPCount output pointer to GCP count.
2314 : * @param ppasGCPs outputer pointer to an array of GCPs.
2315 : * @return TRUE in case of success, FALSE otherwise.
2316 : */
2317 0 : int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
2318 : double *padfGeoTransform, char **ppszWKT,
2319 : int *pnGCPCount, GDAL_GCP **ppasGCPs)
2320 :
2321 : {
2322 0 : return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
2323 0 : pnGCPCount, ppasGCPs, nullptr, nullptr);
2324 : }
2325 :
2326 3744 : int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
2327 : char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2328 : CSLConstList papszSiblingFiles, char **ppszTabFileNameOut)
2329 : {
2330 3744 : if (ppszTabFileNameOut)
2331 3744 : *ppszTabFileNameOut = nullptr;
2332 :
2333 3744 : if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
2334 0 : return FALSE;
2335 :
2336 7488 : std::string osTAB = CPLResetExtensionSafe(pszBaseFilename, "tab");
2337 :
2338 7439 : if (papszSiblingFiles &&
2339 : // cppcheck-suppress knownConditionTrueFalse
2340 3695 : GDALCanReliablyUseSiblingFileList(osTAB.c_str()))
2341 : {
2342 : int iSibling =
2343 3695 : CSLFindString(papszSiblingFiles, CPLGetFilename(osTAB.c_str()));
2344 3695 : if (iSibling >= 0)
2345 : {
2346 14 : CPLString osTabFilename = pszBaseFilename;
2347 28 : osTabFilename.resize(strlen(pszBaseFilename) -
2348 14 : strlen(CPLGetFilename(pszBaseFilename)));
2349 14 : osTabFilename += papszSiblingFiles[iSibling];
2350 14 : if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
2351 14 : pnGCPCount, ppasGCPs))
2352 : {
2353 14 : if (ppszTabFileNameOut)
2354 14 : *ppszTabFileNameOut = CPLStrdup(osTabFilename);
2355 14 : return TRUE;
2356 : }
2357 : }
2358 3681 : return FALSE;
2359 : }
2360 :
2361 : /* -------------------------------------------------------------------- */
2362 : /* Try lower case, then upper case. */
2363 : /* -------------------------------------------------------------------- */
2364 :
2365 49 : VSILFILE *fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
2366 :
2367 49 : if (fpTAB == nullptr && VSIIsCaseSensitiveFS(osTAB.c_str()))
2368 : {
2369 49 : osTAB = CPLResetExtensionSafe(pszBaseFilename, "TAB");
2370 49 : fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
2371 : }
2372 :
2373 49 : if (fpTAB == nullptr)
2374 49 : return FALSE;
2375 :
2376 0 : CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
2377 :
2378 : /* -------------------------------------------------------------------- */
2379 : /* We found the file, now load and parse it. */
2380 : /* -------------------------------------------------------------------- */
2381 0 : if (GDALLoadTabFile(osTAB.c_str(), padfGeoTransform, ppszWKT, pnGCPCount,
2382 0 : ppasGCPs))
2383 : {
2384 0 : if (ppszTabFileNameOut)
2385 0 : *ppszTabFileNameOut = CPLStrdup(osTAB.c_str());
2386 0 : return TRUE;
2387 : }
2388 0 : return FALSE;
2389 : }
2390 :
2391 : /************************************************************************/
2392 : /* GDALLoadWorldFile() */
2393 : /************************************************************************/
2394 :
2395 : /**
2396 : * \brief Read ESRI world file.
2397 : *
2398 : * This function reads an ESRI style world file, and formats a geotransform
2399 : * from its contents.
2400 : *
2401 : * The world file contains an affine transformation with the parameters
2402 : * in a different order than in a geotransform array.
2403 : *
2404 : * <ul>
2405 : * <li> geotransform[1] : width of pixel
2406 : * <li> geotransform[4] : rotational coefficient, zero for north up images.
2407 : * <li> geotransform[2] : rotational coefficient, zero for north up images.
2408 : * <li> geotransform[5] : height of pixel (but negative)
2409 : * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2410 : * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2411 : * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2412 : * pixel.
2413 : * </ul>
2414 : *
2415 : * @param pszFilename the world file name.
2416 : * @param padfGeoTransform the six double array into which the
2417 : * geotransformation should be placed.
2418 : *
2419 : * @return TRUE on success or FALSE on failure.
2420 : */
2421 :
2422 67 : int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
2423 : double *padfGeoTransform)
2424 :
2425 : {
2426 67 : VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
2427 67 : VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
2428 :
2429 67 : char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
2430 :
2431 67 : if (!papszLines)
2432 0 : return FALSE;
2433 :
2434 67 : double world[6] = {0.0};
2435 : // reads the first 6 non-empty lines
2436 67 : int nLines = 0;
2437 67 : const int nLinesCount = CSLCount(papszLines);
2438 469 : for (int i = 0;
2439 469 : i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
2440 : ++i)
2441 : {
2442 402 : CPLString line(papszLines[i]);
2443 402 : if (line.Trim().empty())
2444 0 : continue;
2445 :
2446 402 : world[nLines] = CPLAtofM(line);
2447 402 : ++nLines;
2448 : }
2449 :
2450 67 : if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
2451 67 : (world[3] != 0.0 || world[1] != 0.0))
2452 : {
2453 67 : padfGeoTransform[0] = world[4];
2454 67 : padfGeoTransform[1] = world[0];
2455 67 : padfGeoTransform[2] = world[2];
2456 67 : padfGeoTransform[3] = world[5];
2457 67 : padfGeoTransform[4] = world[1];
2458 67 : padfGeoTransform[5] = world[3];
2459 :
2460 : // correct for center of pixel vs. top left of pixel
2461 67 : padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
2462 67 : padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
2463 67 : padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
2464 67 : padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
2465 :
2466 67 : CSLDestroy(papszLines);
2467 :
2468 67 : return TRUE;
2469 : }
2470 : else
2471 : {
2472 0 : CPLDebug("GDAL",
2473 : "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2474 : pszFilename);
2475 0 : CSLDestroy(papszLines);
2476 0 : return FALSE;
2477 : }
2478 : }
2479 :
2480 : /************************************************************************/
2481 : /* GDALReadWorldFile() */
2482 : /************************************************************************/
2483 :
2484 : /**
2485 : * \brief Read ESRI world file.
2486 : *
2487 : * This function reads an ESRI style world file, and formats a geotransform
2488 : * from its contents. It does the same as GDALLoadWorldFile() function, but
2489 : * it will form the filename for the worldfile from the filename of the raster
2490 : * file referred and the suggested extension. If no extension is provided,
2491 : * the code will internally try the unix style and windows style world file
2492 : * extensions (eg. for .tif these would be .tfw and .tifw).
2493 : *
2494 : * The world file contains an affine transformation with the parameters
2495 : * in a different order than in a geotransform array.
2496 : *
2497 : * <ul>
2498 : * <li> geotransform[1] : width of pixel
2499 : * <li> geotransform[4] : rotational coefficient, zero for north up images.
2500 : * <li> geotransform[2] : rotational coefficient, zero for north up images.
2501 : * <li> geotransform[5] : height of pixel (but negative)
2502 : * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2503 : * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2504 : * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2505 : * pixel.
2506 : * </ul>
2507 : *
2508 : * @param pszBaseFilename the target raster file.
2509 : * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2510 : * from the pszBaseFilename
2511 : * @param padfGeoTransform the six double array into which the
2512 : * geotransformation should be placed.
2513 : *
2514 : * @return TRUE on success or FALSE on failure.
2515 : */
2516 :
2517 909 : int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
2518 : const char *pszExtension,
2519 : double *padfGeoTransform)
2520 :
2521 : {
2522 909 : return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
2523 909 : nullptr, nullptr);
2524 : }
2525 :
2526 19636 : int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
2527 : double *padfGeoTransform, CSLConstList papszSiblingFiles,
2528 : char **ppszWorldFileNameOut)
2529 : {
2530 19636 : VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
2531 19636 : VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
2532 :
2533 19636 : if (ppszWorldFileNameOut)
2534 17742 : *ppszWorldFileNameOut = nullptr;
2535 :
2536 19636 : if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
2537 202 : return FALSE;
2538 :
2539 : /* -------------------------------------------------------------------- */
2540 : /* If we aren't given an extension, try both the unix and */
2541 : /* windows style extensions. */
2542 : /* -------------------------------------------------------------------- */
2543 19434 : if (pszExtension == nullptr)
2544 : {
2545 9266 : const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename);
2546 :
2547 4633 : if (oBaseExt.length() < 2)
2548 229 : return FALSE;
2549 :
2550 : // windows version - first + last + 'w'
2551 4404 : char szDerivedExtension[100] = {'\0'};
2552 4404 : szDerivedExtension[0] = oBaseExt[0];
2553 4404 : szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
2554 4404 : szDerivedExtension[2] = 'w';
2555 4404 : szDerivedExtension[3] = '\0';
2556 :
2557 4404 : if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2558 : padfGeoTransform, papszSiblingFiles,
2559 4404 : ppszWorldFileNameOut))
2560 52 : return TRUE;
2561 :
2562 : // unix version - extension + 'w'
2563 4352 : if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
2564 0 : return FALSE;
2565 :
2566 4352 : snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
2567 : oBaseExt.c_str());
2568 4352 : return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2569 : padfGeoTransform, papszSiblingFiles,
2570 4352 : ppszWorldFileNameOut);
2571 : }
2572 :
2573 : /* -------------------------------------------------------------------- */
2574 : /* Skip the leading period in the extension if there is one. */
2575 : /* -------------------------------------------------------------------- */
2576 14801 : if (*pszExtension == '.')
2577 1167 : pszExtension++;
2578 :
2579 : /* -------------------------------------------------------------------- */
2580 : /* Generate upper and lower case versions of the extension. */
2581 : /* -------------------------------------------------------------------- */
2582 14801 : char szExtUpper[32] = {'\0'};
2583 14801 : char szExtLower[32] = {'\0'};
2584 14801 : CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
2585 14801 : CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
2586 :
2587 64079 : for (int i = 0; szExtUpper[i] != '\0'; i++)
2588 : {
2589 49278 : szExtUpper[i] = static_cast<char>(
2590 49278 : CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
2591 49278 : szExtLower[i] = static_cast<char>(
2592 49278 : CPLTolower(static_cast<unsigned char>(szExtLower[i])));
2593 : }
2594 :
2595 29602 : std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
2596 :
2597 28449 : if (papszSiblingFiles &&
2598 : // cppcheck-suppress knownConditionTrueFalse
2599 13648 : GDALCanReliablyUseSiblingFileList(osTFW.c_str()))
2600 : {
2601 : const int iSibling =
2602 13648 : CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str()));
2603 13648 : if (iSibling >= 0)
2604 : {
2605 65 : CPLString osTFWFilename = pszBaseFilename;
2606 130 : osTFWFilename.resize(strlen(pszBaseFilename) -
2607 65 : strlen(CPLGetFilename(pszBaseFilename)));
2608 65 : osTFWFilename += papszSiblingFiles[iSibling];
2609 65 : if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
2610 : {
2611 65 : if (ppszWorldFileNameOut)
2612 62 : *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
2613 65 : return TRUE;
2614 : }
2615 : }
2616 13583 : return FALSE;
2617 : }
2618 :
2619 : /* -------------------------------------------------------------------- */
2620 : /* Try lower case, then upper case. */
2621 : /* -------------------------------------------------------------------- */
2622 :
2623 : VSIStatBufL sStatBuf;
2624 : bool bGotTFW =
2625 1153 : VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2626 :
2627 1153 : if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str()))
2628 : {
2629 1151 : osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper);
2630 1151 : bGotTFW =
2631 1151 : VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2632 : }
2633 :
2634 1153 : if (!bGotTFW)
2635 1151 : return FALSE;
2636 :
2637 : /* -------------------------------------------------------------------- */
2638 : /* We found the file, now load and parse it. */
2639 : /* -------------------------------------------------------------------- */
2640 2 : if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform))
2641 : {
2642 2 : if (ppszWorldFileNameOut)
2643 1 : *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str());
2644 2 : return TRUE;
2645 : }
2646 0 : return FALSE;
2647 : }
2648 :
2649 : /************************************************************************/
2650 : /* GDALWriteWorldFile() */
2651 : /* */
2652 : /* Helper function for translator implementer wanting */
2653 : /* support for ESRI world files. */
2654 : /************************************************************************/
2655 :
2656 : /**
2657 : * \brief Write ESRI world file.
2658 : *
2659 : * This function writes an ESRI style world file from the passed geotransform.
2660 : *
2661 : * The world file contains an affine transformation with the parameters
2662 : * in a different order than in a geotransform array.
2663 : *
2664 : * <ul>
2665 : * <li> geotransform[1] : width of pixel
2666 : * <li> geotransform[4] : rotational coefficient, zero for north up images.
2667 : * <li> geotransform[2] : rotational coefficient, zero for north up images.
2668 : * <li> geotransform[5] : height of pixel (but negative)
2669 : * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2670 : * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2671 : * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2672 : * pixel.
2673 : * </ul>
2674 : *
2675 : * @param pszBaseFilename the target raster file.
2676 : * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2677 : * @param padfGeoTransform the six double array from which the
2678 : * geotransformation should be read.
2679 : *
2680 : * @return TRUE on success or FALSE on failure.
2681 : */
2682 :
2683 13 : int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
2684 : const char *pszExtension,
2685 : double *padfGeoTransform)
2686 :
2687 : {
2688 13 : VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
2689 13 : VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
2690 13 : VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
2691 :
2692 : /* -------------------------------------------------------------------- */
2693 : /* Prepare the text to write to the file. */
2694 : /* -------------------------------------------------------------------- */
2695 26 : CPLString osTFWText;
2696 :
2697 : osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2698 13 : padfGeoTransform[1], padfGeoTransform[4],
2699 13 : padfGeoTransform[2], padfGeoTransform[5],
2700 13 : padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
2701 13 : 0.5 * padfGeoTransform[2],
2702 13 : padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
2703 13 : 0.5 * padfGeoTransform[5]);
2704 :
2705 : /* -------------------------------------------------------------------- */
2706 : /* Update extension, and write to disk. */
2707 : /* -------------------------------------------------------------------- */
2708 : const std::string osTFW =
2709 26 : CPLResetExtensionSafe(pszBaseFilename, pszExtension);
2710 13 : VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt");
2711 13 : if (fpTFW == nullptr)
2712 0 : return FALSE;
2713 :
2714 : const int bRet =
2715 13 : VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
2716 13 : if (VSIFCloseL(fpTFW) != 0)
2717 0 : return FALSE;
2718 :
2719 13 : return bRet;
2720 : }
2721 :
2722 : /************************************************************************/
2723 : /* GDALVersionInfo() */
2724 : /************************************************************************/
2725 :
2726 : /**
2727 : * \brief Get runtime version information.
2728 : *
2729 : * Available pszRequest values:
2730 : * <ul>
2731 : * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string. i.e.
2732 : * "30603000", e.g for GDAL 3.6.3.0</li>
2733 : * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2734 : * string. i.e. "20230312".</li>
2735 : * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2736 : * <li> "--version": Returns one line version message suitable for
2737 : * use in response to --version requests. i.e. "GDAL 3.6.3, released
2738 : * 2023/03/12"</li>
2739 : * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2740 : * the GDAL_DATA directory.
2741 : * </li>
2742 : * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2743 : * with information on build time options.</li>
2744 : * </ul>
2745 : *
2746 : * @param pszRequest the type of version info desired, as listed above.
2747 : *
2748 : * @return an internal string containing the requested information.
2749 : */
2750 :
2751 4879 : const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
2752 :
2753 : {
2754 : /* -------------------------------------------------------------------- */
2755 : /* Try to capture as much build information as practical. */
2756 : /* -------------------------------------------------------------------- */
2757 4879 : if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
2758 : {
2759 1332 : CPLString osBuildInfo;
2760 :
2761 : #define STRINGIFY_HELPER(x) #x
2762 : #define STRINGIFY(x) STRINGIFY_HELPER(x)
2763 :
2764 : #ifdef ESRI_BUILD
2765 : osBuildInfo += "ESRI_BUILD=YES\n";
2766 : #endif
2767 : #ifdef PAM_ENABLED
2768 : osBuildInfo += "PAM_ENABLED=YES\n";
2769 : #endif
2770 666 : osBuildInfo += "OGR_ENABLED=YES\n"; // Deprecated. Always yes.
2771 : #ifdef HAVE_CURL
2772 666 : osBuildInfo += "CURL_ENABLED=YES\n";
2773 666 : osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
2774 : #endif
2775 : #ifdef HAVE_GEOS
2776 666 : osBuildInfo += "GEOS_ENABLED=YES\n";
2777 : #ifdef GEOS_CAPI_VERSION
2778 666 : osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
2779 : #endif
2780 : #endif
2781 : osBuildInfo +=
2782 : "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2783 666 : PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
2784 666 : osBuildInfo += "PROJ_RUNTIME_VERSION=";
2785 666 : osBuildInfo += proj_info().version;
2786 666 : osBuildInfo += '\n';
2787 :
2788 : #ifdef __VERSION__
2789 : #ifdef __clang_version__
2790 : osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2791 : #elif defined(__GNUC__)
2792 666 : osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
2793 : #elif defined(__INTEL_COMPILER)
2794 : osBuildInfo += "COMPILER=" __VERSION__ "\n";
2795 : #else
2796 : // STRINGIFY() as we're not sure if its a int or a string
2797 : osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2798 : #endif
2799 : #elif defined(_MSC_FULL_VER)
2800 : osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2801 : #elif defined(__INTEL_COMPILER)
2802 : osBuildInfo +=
2803 : "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2804 : #endif
2805 : #ifdef CMAKE_UNITY_BUILD
2806 : osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2807 : #endif
2808 : #ifdef EMBED_RESOURCE_FILES
2809 : osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2810 : #endif
2811 : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2812 : osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2813 : #endif
2814 :
2815 : #undef STRINGIFY_HELPER
2816 : #undef STRINGIFY
2817 :
2818 666 : CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
2819 666 : CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
2820 666 : return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
2821 : }
2822 :
2823 : /* -------------------------------------------------------------------- */
2824 : /* LICENSE is a special case. We try to find and read the */
2825 : /* LICENSE.TXT file from the GDAL_DATA directory and return it */
2826 : /* -------------------------------------------------------------------- */
2827 4213 : if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
2828 : {
2829 : #if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2830 : return GDALGetEmbeddedLicense();
2831 : #else
2832 : char *pszResultLicence =
2833 4 : reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
2834 4 : if (pszResultLicence != nullptr)
2835 : {
2836 0 : return pszResultLicence;
2837 : }
2838 :
2839 4 : VSILFILE *fp = nullptr;
2840 : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
2841 : #ifdef EMBED_RESOURCE_FILES
2842 : CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2843 : #endif
2844 4 : const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
2845 4 : if (pszFilename != nullptr)
2846 4 : fp = VSIFOpenL(pszFilename, "r");
2847 4 : if (fp != nullptr)
2848 : {
2849 4 : if (VSIFSeekL(fp, 0, SEEK_END) == 0)
2850 : {
2851 : // TODO(schwehr): Handle if VSITellL returns a value too large
2852 : // for size_t.
2853 4 : const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
2854 4 : if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
2855 : {
2856 : pszResultLicence =
2857 4 : static_cast<char *>(VSICalloc(1, nLength));
2858 4 : if (pszResultLicence)
2859 4 : CPL_IGNORE_RET_VAL(
2860 4 : VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
2861 : }
2862 : }
2863 :
2864 4 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
2865 : }
2866 : #endif
2867 :
2868 : #ifdef EMBED_RESOURCE_FILES
2869 : if (!fp)
2870 : {
2871 : return GDALGetEmbeddedLicense();
2872 : }
2873 : #endif
2874 :
2875 4 : if (!pszResultLicence)
2876 : {
2877 : pszResultLicence =
2878 0 : CPLStrdup("GDAL/OGR is released under the MIT license.\n"
2879 : "The LICENSE.TXT distributed with GDAL/OGR should\n"
2880 : "contain additional details.\n");
2881 : }
2882 :
2883 4 : CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
2884 4 : return pszResultLicence;
2885 : #endif
2886 : }
2887 :
2888 : /* -------------------------------------------------------------------- */
2889 : /* All other strings are fairly small. */
2890 : /* -------------------------------------------------------------------- */
2891 8418 : CPLString osVersionInfo;
2892 :
2893 4209 : if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
2894 2630 : osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
2895 1579 : else if (EQUAL(pszRequest, "RELEASE_DATE"))
2896 1 : osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
2897 1578 : else if (EQUAL(pszRequest, "RELEASE_NAME"))
2898 1229 : osVersionInfo.Printf(GDAL_RELEASE_NAME);
2899 : else // --version
2900 : {
2901 : osVersionInfo.Printf("GDAL %s, released %d/%02d/%02d",
2902 : GDAL_RELEASE_NAME, GDAL_RELEASE_DATE / 10000,
2903 : (GDAL_RELEASE_DATE % 10000) / 100,
2904 349 : GDAL_RELEASE_DATE % 100);
2905 : #if defined(__GNUC__) && !defined(__OPTIMIZE__)
2906 : // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
2907 : // also true for CLang
2908 349 : osVersionInfo += " (debug build)";
2909 : #elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
2910 : // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
2911 : // In release mode, the compiler generates an error if you specify
2912 : // _ITERATOR_DEBUG_LEVEL as 2.
2913 : osVersionInfo += " (debug build)";
2914 : #endif
2915 : }
2916 :
2917 4209 : CPLFree(CPLGetTLS(CTLS_VERSIONINFO)); // clear old value.
2918 4209 : CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
2919 4209 : return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
2920 : }
2921 :
2922 : /************************************************************************/
2923 : /* GDALCheckVersion() */
2924 : /************************************************************************/
2925 :
2926 : /** Return TRUE if GDAL library version at runtime matches
2927 : nVersionMajor.nVersionMinor.
2928 :
2929 : The purpose of this method is to ensure that calling code will run
2930 : with the GDAL version it is compiled for. It is primarily intended
2931 : for external plugins.
2932 :
2933 : @param nVersionMajor Major version to be tested against
2934 : @param nVersionMinor Minor version to be tested against
2935 : @param pszCallingComponentName If not NULL, in case of version mismatch, the
2936 : method will issue a failure mentioning the name of the calling component.
2937 :
2938 : @return TRUE if GDAL library version at runtime matches
2939 : nVersionMajor.nVersionMinor, FALSE otherwise.
2940 : */
2941 29624 : int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
2942 : const char *pszCallingComponentName)
2943 : {
2944 29624 : if (nVersionMajor == GDAL_VERSION_MAJOR &&
2945 : nVersionMinor == GDAL_VERSION_MINOR)
2946 29624 : return TRUE;
2947 :
2948 0 : if (pszCallingComponentName)
2949 : {
2950 0 : CPLError(CE_Failure, CPLE_AppDefined,
2951 : "%s was compiled against GDAL %d.%d, but "
2952 : "the current library version is %d.%d",
2953 : pszCallingComponentName, nVersionMajor, nVersionMinor,
2954 : GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
2955 : }
2956 0 : return FALSE;
2957 : }
2958 :
2959 : /************************************************************************/
2960 : /* GDALDecToDMS() */
2961 : /************************************************************************/
2962 :
2963 : /** Translate a decimal degrees value to a DMS string with hemisphere.
2964 : */
2965 550 : const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
2966 : int nPrecision)
2967 :
2968 : {
2969 550 : return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
2970 : }
2971 :
2972 : /************************************************************************/
2973 : /* GDALPackedDMSToDec() */
2974 : /************************************************************************/
2975 :
2976 : /**
2977 : * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2978 : *
2979 : * See CPLPackedDMSToDec().
2980 : */
2981 :
2982 4 : double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
2983 :
2984 : {
2985 4 : return CPLPackedDMSToDec(dfPacked);
2986 : }
2987 :
2988 : /************************************************************************/
2989 : /* GDALDecToPackedDMS() */
2990 : /************************************************************************/
2991 :
2992 : /**
2993 : * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2994 : *
2995 : * See CPLDecToPackedDMS().
2996 : */
2997 :
2998 4 : double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
2999 :
3000 : {
3001 4 : return CPLDecToPackedDMS(dfDec);
3002 : }
3003 :
3004 : /************************************************************************/
3005 : /* GDALGCPsToGeoTransform() */
3006 : /************************************************************************/
3007 :
3008 : /**
3009 : * \brief Generate Geotransform from GCPs.
3010 : *
3011 : * Given a set of GCPs perform first order fit as a geotransform.
3012 : *
3013 : * Due to imprecision in the calculations the fit algorithm will often
3014 : * return non-zero rotational coefficients even if given perfectly non-rotated
3015 : * inputs. A special case has been implemented for corner corner coordinates
3016 : * given in TL, TR, BR, BL order. So when using this to get a geotransform
3017 : * from 4 corner coordinates, pass them in this order.
3018 : *
3019 : * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
3020 : * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
3021 : * set to YES, then bApproxOK will be overridden with TRUE.
3022 : * Starting with GDAL 2.2.2, when exact fit is asked, the
3023 : * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
3024 : * give the maximum error threshold in pixel. The default is 0.25.
3025 : *
3026 : * @param nGCPCount the number of GCPs being passed in.
3027 : * @param pasGCPs the list of GCP structures.
3028 : * @param padfGeoTransform the six double array in which the affine
3029 : * geotransformation will be returned.
3030 : * @param bApproxOK If FALSE the function will fail if the geotransform is not
3031 : * essentially an exact fit (within 0.25 pixel) for all GCPs.
3032 : *
3033 : * @return TRUE on success or FALSE if there aren't enough points to prepare a
3034 : * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
3035 : * and the fit is poor.
3036 : */
3037 :
3038 : // TODO(schwehr): Add consts to args.
3039 597 : int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
3040 : double *padfGeoTransform, int bApproxOK)
3041 :
3042 : {
3043 597 : double dfPixelThreshold = 0.25;
3044 597 : if (!bApproxOK)
3045 : {
3046 583 : bApproxOK = CPLTestBool(
3047 : CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3048 583 : if (!bApproxOK)
3049 : {
3050 : // coverity[tainted_data]
3051 583 : dfPixelThreshold = CPLAtof(CPLGetConfigOption(
3052 : "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
3053 : }
3054 : }
3055 :
3056 : /* -------------------------------------------------------------------- */
3057 : /* Recognise a few special cases. */
3058 : /* -------------------------------------------------------------------- */
3059 597 : if (nGCPCount < 2)
3060 15 : return FALSE;
3061 :
3062 582 : if (nGCPCount == 2)
3063 : {
3064 1 : if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
3065 1 : pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
3066 0 : return FALSE;
3067 :
3068 1 : padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3069 1 : (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3070 1 : padfGeoTransform[2] = 0.0;
3071 :
3072 1 : padfGeoTransform[4] = 0.0;
3073 1 : padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
3074 1 : (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
3075 :
3076 1 : padfGeoTransform[0] = pasGCPs[0].dfGCPX -
3077 1 : pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
3078 1 : pasGCPs[0].dfGCPLine * padfGeoTransform[2];
3079 :
3080 1 : padfGeoTransform[3] = pasGCPs[0].dfGCPY -
3081 1 : pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
3082 1 : pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3083 :
3084 1 : return TRUE;
3085 : }
3086 :
3087 : /* -------------------------------------------------------------------- */
3088 : /* Special case of 4 corner coordinates of a non-rotated */
3089 : /* image. The points must be in TL-TR-BR-BL order for now. */
3090 : /* This case helps avoid some imprecision in the general */
3091 : /* calculations. */
3092 : /* -------------------------------------------------------------------- */
3093 581 : if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
3094 288 : pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
3095 288 : pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
3096 287 : pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
3097 287 : pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
3098 284 : pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
3099 284 : pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
3100 258 : pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
3101 256 : pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
3102 256 : pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
3103 256 : pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
3104 182 : pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
3105 : {
3106 182 : padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3107 182 : (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3108 182 : padfGeoTransform[2] = 0.0;
3109 182 : padfGeoTransform[4] = 0.0;
3110 182 : padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
3111 182 : (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
3112 :
3113 182 : padfGeoTransform[0] =
3114 182 : pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
3115 182 : padfGeoTransform[3] =
3116 182 : pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3117 182 : return TRUE;
3118 : }
3119 :
3120 : /* -------------------------------------------------------------------- */
3121 : /* Compute source and destination ranges so we can normalize */
3122 : /* the values to make the least squares computation more stable. */
3123 : /* -------------------------------------------------------------------- */
3124 399 : double min_pixel = pasGCPs[0].dfGCPPixel;
3125 399 : double max_pixel = pasGCPs[0].dfGCPPixel;
3126 399 : double min_line = pasGCPs[0].dfGCPLine;
3127 399 : double max_line = pasGCPs[0].dfGCPLine;
3128 399 : double min_geox = pasGCPs[0].dfGCPX;
3129 399 : double max_geox = pasGCPs[0].dfGCPX;
3130 399 : double min_geoy = pasGCPs[0].dfGCPY;
3131 399 : double max_geoy = pasGCPs[0].dfGCPY;
3132 :
3133 1697 : for (int i = 1; i < nGCPCount; ++i)
3134 : {
3135 1298 : min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
3136 1298 : max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
3137 1298 : min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
3138 1298 : max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
3139 1298 : min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
3140 1298 : max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
3141 1298 : min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
3142 1298 : max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
3143 : }
3144 :
3145 399 : double EPS = 1.0e-12;
3146 :
3147 795 : if (std::abs(max_pixel - min_pixel) < EPS ||
3148 792 : std::abs(max_line - min_line) < EPS ||
3149 1191 : std::abs(max_geox - min_geox) < EPS ||
3150 322 : std::abs(max_geoy - min_geoy) < EPS)
3151 : {
3152 77 : return FALSE; // degenerate in at least one dimension.
3153 : }
3154 :
3155 : double pl_normalize[6], geo_normalize[6];
3156 :
3157 322 : pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
3158 322 : pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
3159 322 : pl_normalize[2] = 0.0;
3160 322 : pl_normalize[3] = -min_line / (max_line - min_line);
3161 322 : pl_normalize[4] = 0.0;
3162 322 : pl_normalize[5] = 1.0 / (max_line - min_line);
3163 :
3164 322 : geo_normalize[0] = -min_geox / (max_geox - min_geox);
3165 322 : geo_normalize[1] = 1.0 / (max_geox - min_geox);
3166 322 : geo_normalize[2] = 0.0;
3167 322 : geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
3168 322 : geo_normalize[4] = 0.0;
3169 322 : geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
3170 :
3171 : /* -------------------------------------------------------------------- */
3172 : /* In the general case, do a least squares error approximation by */
3173 : /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum */
3174 : /* -------------------------------------------------------------------- */
3175 :
3176 322 : double sum_x = 0.0;
3177 322 : double sum_y = 0.0;
3178 322 : double sum_xy = 0.0;
3179 322 : double sum_xx = 0.0;
3180 322 : double sum_yy = 0.0;
3181 322 : double sum_Lon = 0.0;
3182 322 : double sum_Lonx = 0.0;
3183 322 : double sum_Lony = 0.0;
3184 322 : double sum_Lat = 0.0;
3185 322 : double sum_Latx = 0.0;
3186 322 : double sum_Laty = 0.0;
3187 :
3188 1711 : for (int i = 0; i < nGCPCount; ++i)
3189 : {
3190 : double pixel, line, geox, geoy;
3191 :
3192 1389 : GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
3193 1389 : pasGCPs[i].dfGCPLine, &pixel, &line);
3194 1389 : GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
3195 1389 : pasGCPs[i].dfGCPY, &geox, &geoy);
3196 :
3197 1389 : sum_x += pixel;
3198 1389 : sum_y += line;
3199 1389 : sum_xy += pixel * line;
3200 1389 : sum_xx += pixel * pixel;
3201 1389 : sum_yy += line * line;
3202 1389 : sum_Lon += geox;
3203 1389 : sum_Lonx += geox * pixel;
3204 1389 : sum_Lony += geox * line;
3205 1389 : sum_Lat += geoy;
3206 1389 : sum_Latx += geoy * pixel;
3207 1389 : sum_Laty += geoy * line;
3208 : }
3209 :
3210 322 : const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
3211 322 : 2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
3212 322 : sum_x * sum_x * sum_yy;
3213 :
3214 : /* -------------------------------------------------------------------- */
3215 : /* If the divisor is zero, there is no valid solution. */
3216 : /* -------------------------------------------------------------------- */
3217 322 : if (divisor == 0.0)
3218 0 : return FALSE;
3219 :
3220 : /* -------------------------------------------------------------------- */
3221 : /* Compute top/left origin. */
3222 : /* -------------------------------------------------------------------- */
3223 322 : double gt_normalized[6] = {0.0};
3224 322 : gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
3225 322 : sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
3226 322 : sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
3227 : divisor;
3228 :
3229 322 : gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
3230 322 : sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
3231 322 : sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
3232 : divisor;
3233 :
3234 : /* -------------------------------------------------------------------- */
3235 : /* Compute X related coefficients. */
3236 : /* -------------------------------------------------------------------- */
3237 322 : gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
3238 322 : sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
3239 322 : sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
3240 : divisor;
3241 :
3242 322 : gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
3243 322 : sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
3244 322 : sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
3245 : divisor;
3246 :
3247 : /* -------------------------------------------------------------------- */
3248 : /* Compute Y related coefficients. */
3249 : /* -------------------------------------------------------------------- */
3250 322 : gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
3251 322 : sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
3252 322 : sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
3253 : divisor;
3254 :
3255 322 : gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
3256 322 : sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
3257 322 : sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
3258 : divisor;
3259 :
3260 : /* -------------------------------------------------------------------- */
3261 : /* Compose the resulting transformation with the normalization */
3262 : /* geotransformations. */
3263 : /* -------------------------------------------------------------------- */
3264 322 : double gt1p2[6] = {0.0};
3265 322 : double inv_geo_normalize[6] = {0.0};
3266 322 : if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
3267 0 : return FALSE;
3268 :
3269 322 : GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
3270 322 : GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
3271 :
3272 : // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
3273 643 : if (std::abs(padfGeoTransform[1]) <= 1e-15 ||
3274 321 : std::abs(padfGeoTransform[5]) <= 1e-15)
3275 : {
3276 2 : return FALSE;
3277 : }
3278 :
3279 : /* -------------------------------------------------------------------- */
3280 : /* Now check if any of the input points fit this poorly. */
3281 : /* -------------------------------------------------------------------- */
3282 320 : if (!bApproxOK)
3283 : {
3284 : // FIXME? Not sure if it is the more accurate way of computing
3285 : // pixel size
3286 : double dfPixelSize =
3287 : 0.5 *
3288 311 : (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
3289 311 : std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
3290 311 : if (dfPixelSize == 0.0)
3291 : {
3292 0 : CPLDebug("GDAL", "dfPixelSize = 0");
3293 0 : return FALSE;
3294 : }
3295 :
3296 1631 : for (int i = 0; i < nGCPCount; i++)
3297 : {
3298 1326 : const double dfErrorX =
3299 1326 : (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
3300 1326 : pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
3301 1326 : padfGeoTransform[0]) -
3302 1326 : pasGCPs[i].dfGCPX;
3303 1326 : const double dfErrorY =
3304 1326 : (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
3305 1326 : pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
3306 1326 : padfGeoTransform[3]) -
3307 1326 : pasGCPs[i].dfGCPY;
3308 :
3309 2651 : if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
3310 1325 : std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
3311 : {
3312 6 : CPLDebug("GDAL",
3313 : "dfErrorX/dfPixelSize = %.2f, "
3314 : "dfErrorY/dfPixelSize = %.2f",
3315 6 : std::abs(dfErrorX) / dfPixelSize,
3316 6 : std::abs(dfErrorY) / dfPixelSize);
3317 6 : return FALSE;
3318 : }
3319 : }
3320 : }
3321 :
3322 314 : return TRUE;
3323 : }
3324 :
3325 : /************************************************************************/
3326 : /* GDALComposeGeoTransforms() */
3327 : /************************************************************************/
3328 :
3329 : /**
3330 : * \brief Compose two geotransforms.
3331 : *
3332 : * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3333 : * being applied to a point.
3334 : *
3335 : * @param padfGT1 the first geotransform, six values.
3336 : * @param padfGT2 the second geotransform, six values.
3337 : * @param padfGTOut the output geotransform, six values, may safely be the same
3338 : * array as padfGT1 or padfGT2.
3339 : */
3340 :
3341 644 : void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
3342 : double *padfGTOut)
3343 :
3344 : {
3345 644 : double gtwrk[6] = {0.0};
3346 : // We need to think of the geotransform in a more normal form to do
3347 : // the matrix multiple:
3348 : //
3349 : // __ __
3350 : // | gt[1] gt[2] gt[0] |
3351 : // | gt[4] gt[5] gt[3] |
3352 : // | 0.0 0.0 1.0 |
3353 : // -- --
3354 : //
3355 : // Then we can use normal matrix multiplication to produce the
3356 : // composed transformation. I don't actually reform the matrix
3357 : // explicitly which is why the following may seem kind of spagettish.
3358 :
3359 644 : gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
3360 644 : gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
3361 644 : gtwrk[0] =
3362 644 : padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
3363 :
3364 644 : gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
3365 644 : gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
3366 644 : gtwrk[3] =
3367 644 : padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
3368 644 : memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
3369 644 : }
3370 :
3371 : /************************************************************************/
3372 : /* StripIrrelevantOptions() */
3373 : /************************************************************************/
3374 :
3375 8 : static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
3376 : {
3377 8 : if (psCOL == nullptr)
3378 0 : return;
3379 8 : if (nOptions == 0)
3380 5 : nOptions = GDAL_OF_RASTER;
3381 8 : if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
3382 0 : return;
3383 :
3384 8 : CPLXMLNode *psPrev = nullptr;
3385 175 : for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
3386 : {
3387 167 : if (psIter->eType == CXT_Element)
3388 : {
3389 167 : CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
3390 167 : bool bStrip = false;
3391 167 : if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
3392 35 : psScope->psChild->pszValue &&
3393 35 : EQUAL(psScope->psChild->pszValue, "vector"))
3394 : {
3395 1 : bStrip = true;
3396 : }
3397 166 : else if (nOptions == GDAL_OF_VECTOR && psScope &&
3398 35 : psScope->psChild && psScope->psChild->pszValue &&
3399 35 : EQUAL(psScope->psChild->pszValue, "raster"))
3400 : {
3401 33 : bStrip = true;
3402 : }
3403 167 : if (psScope)
3404 : {
3405 70 : CPLRemoveXMLChild(psIter, psScope);
3406 70 : CPLDestroyXMLNode(psScope);
3407 : }
3408 :
3409 167 : CPLXMLNode *psNext = psIter->psNext;
3410 167 : if (bStrip)
3411 : {
3412 34 : if (psPrev)
3413 13 : psPrev->psNext = psNext;
3414 21 : else if (psCOL->psChild == psIter)
3415 21 : psCOL->psChild = psNext;
3416 34 : psIter->psNext = nullptr;
3417 34 : CPLDestroyXMLNode(psIter);
3418 34 : psIter = psNext;
3419 : }
3420 : else
3421 : {
3422 133 : psPrev = psIter;
3423 133 : psIter = psNext;
3424 : }
3425 : }
3426 : else
3427 : {
3428 0 : psIter = psIter->psNext;
3429 : }
3430 : }
3431 : }
3432 :
3433 : /************************************************************************/
3434 : /* GDALGeneralCmdLineProcessor() */
3435 : /************************************************************************/
3436 :
3437 : /**
3438 : * \brief General utility option processing.
3439 : *
3440 : * This function is intended to provide a variety of generic commandline
3441 : * options for all GDAL commandline utilities. It takes care of the following
3442 : * commandline options:
3443 : *
3444 : * --version: report version of GDAL in use.
3445 : * --build: report build info about GDAL in use.
3446 : * --license: report GDAL license info.
3447 : * --formats: report all format drivers configured. Can be used with -json since 3.10
3448 : * --format [format]: report details of one format driver.
3449 : * --optfile filename: expand an option file into the argument list.
3450 : * --config key value: set system configuration option.
3451 : * --config key=value: set system configuration option (since GDAL 3.9)
3452 : * --debug [on/off/value]: set debug level.
3453 : * --mempreload dir: preload directory contents into /vsimem
3454 : * --pause: Pause for user input (allows time to attach debugger)
3455 : * --locale [locale]: Install a locale using setlocale() (debugging)
3456 : * --help-general: report detailed help on general options.
3457 : *
3458 : * The argument array is replaced "in place" and should be freed with
3459 : * CSLDestroy() when no longer needed. The typical usage looks something
3460 : * like the following. Note that the formats should be registered so that
3461 : * the --formats and --format options will work properly.
3462 : *
3463 : * int main( int argc, char ** argv )
3464 : * {
3465 : * GDALAllRegister();
3466 : *
3467 : * argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3468 : * if( argc < 1 )
3469 : * exit( -argc );
3470 : *
3471 : * @param nArgc number of values in the argument list.
3472 : * @param ppapszArgv pointer to the argument list array (will be updated in
3473 : * place).
3474 : * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3475 : * to determine which drivers should be displayed by --formats.
3476 : * If set to 0, GDAL_OF_RASTER is assumed.
3477 : *
3478 : * @return updated nArgc argument count. Return of 0 requests terminate
3479 : * without error, return of -1 requests exit with error code.
3480 : */
3481 :
3482 1310 : int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
3483 : int nOptions)
3484 :
3485 : {
3486 2620 : CPLStringList aosReturn;
3487 : int iArg;
3488 1310 : char **papszArgv = *ppapszArgv;
3489 :
3490 : /* -------------------------------------------------------------------- */
3491 : /* Preserve the program name. */
3492 : /* -------------------------------------------------------------------- */
3493 1310 : aosReturn.AddString(papszArgv[0]);
3494 :
3495 : /* ==================================================================== */
3496 : /* Loop over all arguments. */
3497 : /* ==================================================================== */
3498 :
3499 : // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3500 : // detects and warns about a unknown config option.
3501 8231 : for (iArg = 1; iArg < nArgc; iArg++)
3502 : {
3503 6923 : if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
3504 39 : EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3505 : {
3506 0 : if (iArg + 1 >= nArgc)
3507 : {
3508 0 : CPLError(CE_Failure, CPLE_AppDefined,
3509 : "--config option given without a key=value argument.");
3510 0 : return -1;
3511 : }
3512 :
3513 0 : const char *pszArg = papszArgv[iArg + 1];
3514 0 : if (strchr(pszArg, '=') != nullptr)
3515 : {
3516 0 : char *pszKey = nullptr;
3517 0 : const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3518 0 : if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3519 : {
3520 0 : CPLSetConfigOption(pszKey, pszValue);
3521 : }
3522 0 : CPLFree(pszKey);
3523 0 : ++iArg;
3524 : }
3525 : else
3526 : {
3527 0 : if (iArg + 2 >= nArgc)
3528 : {
3529 0 : CPLError(CE_Failure, CPLE_AppDefined,
3530 : "--config option given without a key and value "
3531 : "argument.");
3532 0 : return -1;
3533 : }
3534 :
3535 0 : if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3536 0 : CPLSetConfigOption(papszArgv[iArg + 1],
3537 0 : papszArgv[iArg + 2]);
3538 :
3539 0 : iArg += 2;
3540 0 : }
3541 : }
3542 6923 : else if (EQUAL(papszArgv[iArg], "--debug"))
3543 : {
3544 15 : if (iArg + 1 >= nArgc)
3545 : {
3546 2 : CPLError(CE_Failure, CPLE_AppDefined,
3547 : "--debug option given without debug level.");
3548 2 : return -1;
3549 : }
3550 :
3551 13 : CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
3552 13 : iArg += 1;
3553 : }
3554 : }
3555 :
3556 8040 : for (iArg = 1; iArg < nArgc; iArg++)
3557 : {
3558 : /* --------------------------------------------------------------------
3559 : */
3560 : /* --version */
3561 : /* --------------------------------------------------------------------
3562 : */
3563 6820 : if (EQUAL(papszArgv[iArg], "--version"))
3564 : {
3565 63 : printf("%s\n", GDALVersionInfo("--version")); /*ok*/
3566 63 : return 0;
3567 : }
3568 :
3569 : /* --------------------------------------------------------------------
3570 : */
3571 : /* --build */
3572 : /* --------------------------------------------------------------------
3573 : */
3574 6757 : else if (EQUAL(papszArgv[iArg], "--build"))
3575 : {
3576 1 : printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
3577 1 : return 0;
3578 : }
3579 :
3580 : /* --------------------------------------------------------------------
3581 : */
3582 : /* --license */
3583 : /* --------------------------------------------------------------------
3584 : */
3585 6756 : else if (EQUAL(papszArgv[iArg], "--license"))
3586 : {
3587 1 : printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
3588 1 : return 0;
3589 : }
3590 :
3591 : /* --------------------------------------------------------------------
3592 : */
3593 : /* --config */
3594 : /* --------------------------------------------------------------------
3595 : */
3596 6755 : else if (EQUAL(papszArgv[iArg], "--config"))
3597 : {
3598 45 : if (iArg + 1 >= nArgc)
3599 : {
3600 2 : CPLError(CE_Failure, CPLE_AppDefined,
3601 : "--config option given without a key=value argument.");
3602 2 : return -1;
3603 : }
3604 :
3605 43 : const char *pszArg = papszArgv[iArg + 1];
3606 43 : if (strchr(pszArg, '=') != nullptr)
3607 : {
3608 2 : char *pszKey = nullptr;
3609 2 : const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3610 2 : if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3611 : {
3612 2 : CPLSetConfigOption(pszKey, pszValue);
3613 : }
3614 2 : CPLFree(pszKey);
3615 2 : ++iArg;
3616 : }
3617 : else
3618 : {
3619 41 : if (iArg + 2 >= nArgc)
3620 : {
3621 2 : CPLError(CE_Failure, CPLE_AppDefined,
3622 : "--config option given without a key and value "
3623 : "argument.");
3624 2 : return -1;
3625 : }
3626 :
3627 39 : if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3628 39 : CPLSetConfigOption(papszArgv[iArg + 1],
3629 39 : papszArgv[iArg + 2]);
3630 :
3631 39 : iArg += 2;
3632 : }
3633 : }
3634 :
3635 : /* --------------------------------------------------------------------
3636 : */
3637 : /* --mempreload */
3638 : /* --------------------------------------------------------------------
3639 : */
3640 6710 : else if (EQUAL(papszArgv[iArg], "--mempreload"))
3641 : {
3642 4 : if (iArg + 1 >= nArgc)
3643 : {
3644 2 : CPLError(CE_Failure, CPLE_AppDefined,
3645 : "--mempreload option given without directory path.");
3646 2 : return -1;
3647 : }
3648 :
3649 2 : char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
3650 2 : if (CSLCount(papszFiles) == 0)
3651 : {
3652 0 : CPLError(CE_Failure, CPLE_AppDefined,
3653 : "--mempreload given invalid or empty directory.");
3654 0 : return -1;
3655 : }
3656 :
3657 497 : for (int i = 0; papszFiles[i] != nullptr; i++)
3658 : {
3659 495 : if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
3660 76 : continue;
3661 :
3662 491 : std::string osOldPath;
3663 491 : CPLString osNewPath;
3664 491 : osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
3665 491 : papszFiles[i], nullptr);
3666 491 : osNewPath.Printf("/vsimem/%s", papszFiles[i]);
3667 :
3668 : VSIStatBufL sStatBuf;
3669 982 : if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
3670 491 : VSI_ISDIR(sStatBuf.st_mode))
3671 : {
3672 72 : CPLDebug("VSI", "Skipping preload of %s.",
3673 : osOldPath.c_str());
3674 72 : continue;
3675 : }
3676 :
3677 419 : CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
3678 : osNewPath.c_str());
3679 :
3680 419 : if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
3681 : {
3682 0 : CPLError(CE_Failure, CPLE_AppDefined,
3683 : "Failed to copy %s to /vsimem", osOldPath.c_str());
3684 0 : return -1;
3685 : }
3686 : }
3687 :
3688 2 : CSLDestroy(papszFiles);
3689 2 : iArg += 1;
3690 : }
3691 :
3692 : /* --------------------------------------------------------------------
3693 : */
3694 : /* --debug */
3695 : /* --------------------------------------------------------------------
3696 : */
3697 6706 : else if (EQUAL(papszArgv[iArg], "--debug"))
3698 : {
3699 13 : if (iArg + 1 >= nArgc)
3700 : {
3701 0 : CPLError(CE_Failure, CPLE_AppDefined,
3702 : "--debug option given without debug level.");
3703 0 : return -1;
3704 : }
3705 :
3706 13 : iArg += 1;
3707 : }
3708 :
3709 : /* --------------------------------------------------------------------
3710 : */
3711 : /* --optfile */
3712 : /* --------------------------------------------------------------------
3713 : */
3714 6693 : else if (EQUAL(papszArgv[iArg], "--optfile"))
3715 : {
3716 11 : if (iArg + 1 >= nArgc)
3717 : {
3718 2 : CPLError(CE_Failure, CPLE_AppDefined,
3719 : "--optfile option given without filename.");
3720 5 : return -1;
3721 : }
3722 :
3723 9 : VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb");
3724 :
3725 9 : if (fpOptFile == nullptr)
3726 : {
3727 4 : CPLError(CE_Failure, CPLE_AppDefined,
3728 : "Unable to open optfile '%s'.\n%s",
3729 2 : papszArgv[iArg + 1], VSIStrerror(errno));
3730 2 : return -1;
3731 : }
3732 :
3733 : const char *pszLine;
3734 7 : CPLStringList aosArgvOptfile;
3735 : // dummy value as first argument to please
3736 : // GDALGeneralCmdLineProcessor()
3737 7 : aosArgvOptfile.AddString("");
3738 7 : bool bHasOptfile = false;
3739 23 : while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
3740 : {
3741 16 : if (pszLine[0] == '#' || strlen(pszLine) == 0)
3742 3 : continue;
3743 :
3744 13 : char **papszTokens = CSLTokenizeString(pszLine);
3745 13 : for (int i = 0;
3746 45 : papszTokens != nullptr && papszTokens[i] != nullptr; i++)
3747 : {
3748 32 : if (EQUAL(papszTokens[i], "--optfile"))
3749 : {
3750 : // To avoid potential recursion
3751 0 : CPLError(CE_Warning, CPLE_AppDefined,
3752 : "--optfile not supported in a option file");
3753 0 : bHasOptfile = true;
3754 : }
3755 32 : aosArgvOptfile.AddStringDirectly(papszTokens[i]);
3756 32 : papszTokens[i] = nullptr;
3757 : }
3758 13 : CSLDestroy(papszTokens);
3759 : }
3760 :
3761 7 : VSIFCloseL(fpOptFile);
3762 :
3763 7 : char **papszArgvOptfile = aosArgvOptfile.StealList();
3764 7 : if (!bHasOptfile)
3765 : {
3766 7 : char **papszArgvOptfileBefore = papszArgvOptfile;
3767 7 : if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
3768 : &papszArgvOptfile,
3769 7 : nOptions) < 0)
3770 : {
3771 1 : CSLDestroy(papszArgvOptfile);
3772 1 : return -1;
3773 : }
3774 6 : CSLDestroy(papszArgvOptfileBefore);
3775 : }
3776 :
3777 6 : char **papszIter = papszArgvOptfile + 1;
3778 36 : while (*papszIter)
3779 : {
3780 30 : aosReturn.AddString(*papszIter);
3781 30 : ++papszIter;
3782 : }
3783 6 : CSLDestroy(papszArgvOptfile);
3784 :
3785 6 : iArg += 1;
3786 : }
3787 :
3788 : /* --------------------------------------------------------------------
3789 : */
3790 : /* --formats */
3791 : /* --------------------------------------------------------------------
3792 : */
3793 6682 : else if (EQUAL(papszArgv[iArg], "--formats") ||
3794 6677 : EQUAL(papszArgv[iArg], "--drivers"))
3795 : {
3796 5 : if (nOptions == 0)
3797 2 : nOptions = GDAL_OF_RASTER;
3798 :
3799 5 : bool bJSON = EQUAL(papszArgv[iArg], "--drivers");
3800 10 : for (int i = 1; i < nArgc; i++)
3801 : {
3802 7 : if (strcmp(papszArgv[i], "-json") == 0 ||
3803 5 : strcmp(papszArgv[i], "--json") == 0)
3804 : {
3805 2 : bJSON = true;
3806 2 : break;
3807 : }
3808 : }
3809 :
3810 5 : if (bJSON)
3811 : {
3812 2 : auto poDM = GetGDALDriverManager();
3813 2 : CPLJSONArray oArray;
3814 2 : const int nDriverCount = poDM->GetDriverCount();
3815 480 : for (int iDr = 0; iDr < nDriverCount; ++iDr)
3816 : {
3817 478 : auto poDriver = poDM->GetDriver(iDr);
3818 478 : CSLConstList papszMD = poDriver->GetMetadata();
3819 :
3820 717 : if (nOptions == GDAL_OF_RASTER &&
3821 239 : !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3822 222 : continue;
3823 641 : if (nOptions == GDAL_OF_VECTOR &&
3824 239 : !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3825 146 : continue;
3826 256 : if (nOptions == GDAL_OF_GNM &&
3827 0 : !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3828 0 : continue;
3829 256 : if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3830 0 : !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER,
3831 : false))
3832 0 : continue;
3833 :
3834 512 : CPLJSONObject oJDriver;
3835 256 : oJDriver.Set("short_name", poDriver->GetDescription());
3836 256 : if (const char *pszLongName =
3837 256 : CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
3838 256 : oJDriver.Set("long_name", pszLongName);
3839 512 : CPLJSONArray oJScopes;
3840 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3841 182 : oJScopes.Add("raster");
3842 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3843 15 : oJScopes.Add("multidimensional_raster");
3844 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3845 112 : oJScopes.Add("vector");
3846 256 : oJDriver.Add("scopes", oJScopes);
3847 512 : CPLJSONArray oJCaps;
3848 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3849 254 : oJCaps.Add("open");
3850 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3851 114 : oJCaps.Add("create");
3852 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3853 73 : oJCaps.Add("create_copy");
3854 256 : if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3855 212 : oJCaps.Add("virtual_io");
3856 256 : oJDriver.Add("capabilities", oJCaps);
3857 :
3858 256 : if (const char *pszExtensions = CSLFetchNameValueDef(
3859 : papszMD, GDAL_DMD_EXTENSIONS,
3860 : CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3861 : {
3862 : const CPLStringList aosExt(
3863 334 : CSLTokenizeString2(pszExtensions, " ", 0));
3864 167 : CPLJSONArray oJExts;
3865 390 : for (int i = 0; i < aosExt.size(); ++i)
3866 : {
3867 223 : oJExts.Add(aosExt[i]);
3868 : }
3869 167 : oJDriver.Add("file_extensions", oJExts);
3870 : }
3871 :
3872 256 : oArray.Add(oJDriver);
3873 : }
3874 2 : printf(/*ok*/
3875 : "%s\n",
3876 4 : oArray.Format(CPLJSONObject::PrettyFormat::Pretty)
3877 : .c_str());
3878 :
3879 2 : return 0;
3880 : }
3881 :
3882 3 : printf(/*ok*/
3883 : "Supported Formats: (ro:read-only, rw:read-write, +:update, "
3884 : "v:virtual-I/O s:subdatasets)\n");
3885 720 : for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
3886 : {
3887 717 : GDALDriverH hDriver = GDALGetDriver(iDr);
3888 :
3889 717 : const char *pszRFlag = "", *pszWFlag, *pszVirtualIO,
3890 : *pszSubdatasets;
3891 717 : CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
3892 :
3893 956 : if (nOptions == GDAL_OF_RASTER &&
3894 239 : !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3895 368 : continue;
3896 1119 : if (nOptions == GDAL_OF_VECTOR &&
3897 478 : !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3898 292 : continue;
3899 349 : if (nOptions == GDAL_OF_GNM &&
3900 0 : !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3901 0 : continue;
3902 349 : if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3903 0 : !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3904 0 : continue;
3905 :
3906 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3907 346 : pszRFlag = "r";
3908 :
3909 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3910 171 : pszWFlag = "w+";
3911 178 : else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3912 36 : pszWFlag = "w";
3913 : else
3914 142 : pszWFlag = "o";
3915 :
3916 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3917 278 : pszVirtualIO = "v";
3918 : else
3919 71 : pszVirtualIO = "";
3920 :
3921 349 : if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
3922 45 : pszSubdatasets = "s";
3923 : else
3924 304 : pszSubdatasets = "";
3925 :
3926 698 : CPLString osKind;
3927 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3928 201 : osKind = "raster";
3929 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3930 : {
3931 18 : if (!osKind.empty())
3932 18 : osKind += ',';
3933 18 : osKind += "multidimensional raster";
3934 : }
3935 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3936 : {
3937 205 : if (!osKind.empty())
3938 57 : osKind += ',';
3939 205 : osKind += "vector";
3940 : }
3941 349 : if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3942 : {
3943 0 : if (!osKind.empty())
3944 0 : osKind += ',';
3945 0 : osKind += "geography network";
3946 : }
3947 349 : if (osKind.empty())
3948 0 : osKind = "unknown kind";
3949 :
3950 698 : std::string osExtensions;
3951 349 : if (const char *pszExtensions = CSLFetchNameValueDef(
3952 : papszMD, GDAL_DMD_EXTENSIONS,
3953 : CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3954 : {
3955 : const CPLStringList aosExt(
3956 458 : CSLTokenizeString2(pszExtensions, " ", 0));
3957 549 : for (int i = 0; i < aosExt.size(); ++i)
3958 : {
3959 320 : if (i == 0)
3960 227 : osExtensions = " (*.";
3961 : else
3962 93 : osExtensions += ", *.";
3963 320 : osExtensions += aosExt[i];
3964 : }
3965 229 : if (!osExtensions.empty())
3966 227 : osExtensions += ')';
3967 : }
3968 :
3969 349 : printf(" %s -%s- (%s%s%s%s): %s%s\n", /*ok*/
3970 : GDALGetDriverShortName(hDriver), osKind.c_str(),
3971 : pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
3972 : GDALGetDriverLongName(hDriver), osExtensions.c_str());
3973 : }
3974 :
3975 3 : return 0;
3976 : }
3977 :
3978 : /* --------------------------------------------------------------------
3979 : */
3980 : /* --format */
3981 : /* --------------------------------------------------------------------
3982 : */
3983 6677 : else if (EQUAL(papszArgv[iArg], "--format"))
3984 : {
3985 : GDALDriverH hDriver;
3986 : char **papszMD;
3987 :
3988 5 : if (iArg + 1 >= nArgc)
3989 : {
3990 1 : CPLError(CE_Failure, CPLE_AppDefined,
3991 : "--format option given without a format code.");
3992 1 : return -1;
3993 : }
3994 :
3995 4 : hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
3996 4 : if (hDriver == nullptr)
3997 : {
3998 1 : CPLError(CE_Failure, CPLE_AppDefined,
3999 : "--format option given with format '%s', but that "
4000 : "format not\nrecognised. Use the --formats option "
4001 : "to get a list of available formats,\n"
4002 : "and use the short code (i.e. GTiff or HFA) as the "
4003 : "format identifier.\n",
4004 1 : papszArgv[iArg + 1]);
4005 1 : return -1;
4006 : }
4007 :
4008 3 : printf("Format Details:\n"); /*ok*/
4009 3 : printf(/*ok*/ " Short Name: %s\n",
4010 : GDALGetDriverShortName(hDriver));
4011 3 : printf(/*ok*/ " Long Name: %s\n", GDALGetDriverLongName(hDriver));
4012 :
4013 3 : papszMD = GDALGetMetadata(hDriver, nullptr);
4014 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
4015 3 : printf(" Supports: Raster\n"); /*ok*/
4016 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
4017 0 : printf(" Supports: Multidimensional raster\n"); /*ok*/
4018 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
4019 2 : printf(" Supports: Vector\n"); /*ok*/
4020 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
4021 0 : printf(" Supports: Geography Network\n"); /*ok*/
4022 :
4023 : const char *pszExt =
4024 3 : CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
4025 3 : if (pszExt != nullptr)
4026 3 : printf(" Extension%s: %s\n", /*ok*/
4027 3 : (strchr(pszExt, ' ') ? "s" : ""), pszExt);
4028 :
4029 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
4030 1 : printf(" Mime Type: %s\n", /*ok*/
4031 : CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4032 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
4033 3 : printf(" Help Topic: %s\n", /*ok*/
4034 : CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4035 :
4036 3 : if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
4037 3 : printf(" Supports: Raster subdatasets\n"); /*ok*/
4038 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
4039 3 : printf(" Supports: Open() - Open existing dataset.\n"); /*ok*/
4040 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
4041 3 : printf(/*ok*/
4042 : " Supports: Create() - Create writable dataset.\n");
4043 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
4044 0 : printf(/*ok*/ " Supports: CreateMultiDimensional() - Create "
4045 : "multidimensional dataset.\n");
4046 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
4047 3 : printf(/*ok*/ " Supports: CreateCopy() - Create dataset by "
4048 : "copying "
4049 : "another.\n");
4050 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
4051 3 : printf(" Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
4052 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
4053 3 : printf(" Creation Datatypes: %s\n", /*ok*/
4054 : CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
4055 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
4056 2 : printf(" Creation Field Datatypes: %s\n", /*ok*/
4057 : CSLFetchNameValue(papszMD,
4058 : GDAL_DMD_CREATIONFIELDDATATYPES));
4059 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
4060 2 : printf(" Creation Field Data Sub-types: %s\n", /*ok*/
4061 : CSLFetchNameValue(papszMD,
4062 : GDAL_DMD_CREATIONFIELDDATASUBTYPES));
4063 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
4064 2 : printf(/*ok*/ " Supports: Creating fields with NOT NULL "
4065 : "constraint.\n");
4066 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
4067 2 : printf(/*ok*/
4068 : " Supports: Creating fields with UNIQUE constraint.\n");
4069 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
4070 2 : printf(/*ok*/
4071 : " Supports: Creating fields with DEFAULT values.\n");
4072 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
4073 2 : /*ok*/ printf(
4074 : " Supports: Creating geometry fields with NOT NULL "
4075 : "constraint.\n");
4076 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
4077 : false))
4078 2 : /*ok*/ printf(" Supports: Writing geometries with given "
4079 : "coordinate precision\n");
4080 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
4081 0 : printf(" Supports: Reading feature styles.\n"); /*ok*/
4082 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
4083 0 : printf(" Supports: Writing feature styles.\n"); /*ok*/
4084 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
4085 1 : printf(" Supports: Coordinate epoch.\n"); /*ok*/
4086 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
4087 2 : printf(" Supports: Multiple vector layers.\n"); /*ok*/
4088 3 : if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
4089 2 : printf(" Supports: Reading field domains.\n"); /*ok*/
4090 3 : if (CSLFetchNameValue(papszMD,
4091 3 : GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
4092 2 : printf(" Creation field domain types: %s\n", /*ok*/
4093 : CSLFetchNameValue(papszMD,
4094 : GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
4095 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
4096 2 : printf(" Supported SQL dialects: %s\n", /*ok*/
4097 : CSLFetchNameValue(papszMD,
4098 : GDAL_DMD_SUPPORTED_SQL_DIALECTS));
4099 :
4100 24 : for (const char *key :
4101 : {GDAL_DMD_CREATIONOPTIONLIST,
4102 : GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4103 : GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4104 : GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4105 : GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4106 : GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4107 : GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4108 27 : GDAL_DS_LAYER_CREATIONOPTIONLIST})
4109 : {
4110 24 : if (CSLFetchNameValue(papszMD, key))
4111 : {
4112 : CPLXMLNode *psCOL =
4113 5 : CPLParseXMLString(CSLFetchNameValue(papszMD, key));
4114 5 : StripIrrelevantOptions(psCOL, nOptions);
4115 5 : char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4116 :
4117 5 : CPLDestroyXMLNode(psCOL);
4118 :
4119 5 : printf("\n%s\n", pszFormattedXML); /*ok*/
4120 5 : CPLFree(pszFormattedXML);
4121 : }
4122 : }
4123 :
4124 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
4125 0 : printf(" Connection prefix: %s\n", /*ok*/
4126 : CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4127 :
4128 3 : if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
4129 : {
4130 3 : CPLXMLNode *psCOL = CPLParseXMLString(
4131 : CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4132 3 : StripIrrelevantOptions(psCOL, nOptions);
4133 3 : char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4134 :
4135 3 : CPLDestroyXMLNode(psCOL);
4136 :
4137 3 : printf("%s\n", pszFormattedXML); /*ok*/
4138 3 : CPLFree(pszFormattedXML);
4139 : }
4140 :
4141 3 : bool bFirstOtherOption = true;
4142 112 : for (char **papszIter = papszMD; papszIter && *papszIter;
4143 : ++papszIter)
4144 : {
4145 109 : if (!STARTS_WITH(*papszIter, "DCAP_") &&
4146 49 : !STARTS_WITH(*papszIter, "DMD_") &&
4147 14 : !STARTS_WITH(*papszIter, "DS_") &&
4148 12 : !STARTS_WITH(*papszIter, "OGR_DRIVER="))
4149 : {
4150 12 : if (bFirstOtherOption)
4151 3 : printf(" Other metadata items:\n"); /*ok*/
4152 12 : bFirstOtherOption = false;
4153 12 : printf(" %s\n", *papszIter); /*ok*/
4154 : }
4155 : }
4156 :
4157 3 : return 0;
4158 : }
4159 :
4160 : /* --------------------------------------------------------------------
4161 : */
4162 : /* --help-general */
4163 : /* --------------------------------------------------------------------
4164 : */
4165 6672 : else if (EQUAL(papszArgv[iArg], "--help-general"))
4166 : {
4167 2 : printf("Generic GDAL utility command options:\n"); /*ok*/
4168 2 : printf(" --version: report version of GDAL in use.\n"); /*ok*/
4169 2 : /*ok*/ printf(
4170 : " --build: report detailed information about GDAL in "
4171 : "use.\n");
4172 2 : printf(" --license: report GDAL license info.\n"); /*ok*/
4173 2 : printf( /*ok*/
4174 : " --formats: report all configured format drivers.\n"); /*ok*/
4175 2 : printf(" --format [<format>]: details of one format.\n"); /*ok*/
4176 2 : /*ok*/ printf(
4177 : " --optfile filename: expand an option file into the "
4178 : "argument list.\n");
4179 2 : printf(/*ok*/
4180 : " --config <key> <value> or --config <key>=<value>: set "
4181 : "system configuration option.\n"); /*ok*/
4182 2 : printf(" --debug [on/off/value]: set debug level.\n"); /*ok*/
4183 2 : /*ok*/ printf( /*ok*/
4184 : " --pause: wait for user input, time to attach "
4185 : "debugger\n");
4186 2 : printf(" --locale [<locale>]: install locale for debugging " /*ok*/
4187 : "(i.e. en_US.UTF-8)\n");
4188 2 : printf(" --help-general: report detailed help on general " /*ok*/
4189 : "options.\n");
4190 :
4191 2 : return 0;
4192 : }
4193 :
4194 : /* --------------------------------------------------------------------
4195 : */
4196 : /* --locale */
4197 : /* --------------------------------------------------------------------
4198 : */
4199 6670 : else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
4200 : {
4201 2 : CPLsetlocale(LC_ALL, papszArgv[++iArg]);
4202 : }
4203 :
4204 : /* --------------------------------------------------------------------
4205 : */
4206 : /* --pause */
4207 : /* --------------------------------------------------------------------
4208 : */
4209 6668 : else if (EQUAL(papszArgv[iArg], "--pause"))
4210 : {
4211 0 : std::cout << "Hit <ENTER> to Continue." << std::endl;
4212 0 : std::cin.clear();
4213 0 : std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
4214 : }
4215 :
4216 : /* --------------------------------------------------------------------
4217 : */
4218 : /* Carry through unrecognized options. */
4219 : /* --------------------------------------------------------------------
4220 : */
4221 : else
4222 : {
4223 6668 : aosReturn.AddString(papszArgv[iArg]);
4224 : }
4225 : }
4226 :
4227 1220 : const int nSize = aosReturn.size();
4228 1220 : *ppapszArgv = aosReturn.StealList();
4229 :
4230 1220 : return nSize;
4231 : }
4232 :
4233 : /************************************************************************/
4234 : /* _FetchDblFromMD() */
4235 : /************************************************************************/
4236 :
4237 1660 : static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
4238 : double *padfTarget, int nCount, double dfDefault)
4239 :
4240 : {
4241 : char szFullKey[200];
4242 :
4243 1660 : snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
4244 :
4245 1660 : const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
4246 :
4247 9628 : for (int i = 0; i < nCount; i++)
4248 7968 : padfTarget[i] = dfDefault;
4249 :
4250 1660 : if (pszValue == nullptr)
4251 408 : return false;
4252 :
4253 1252 : if (nCount == 1)
4254 : {
4255 920 : *padfTarget = CPLAtofM(pszValue);
4256 920 : return true;
4257 : }
4258 :
4259 332 : char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
4260 :
4261 332 : if (CSLCount(papszTokens) != nCount)
4262 : {
4263 0 : CSLDestroy(papszTokens);
4264 0 : return false;
4265 : }
4266 :
4267 6972 : for (int i = 0; i < nCount; i++)
4268 6640 : padfTarget[i] = CPLAtofM(papszTokens[i]);
4269 :
4270 332 : CSLDestroy(papszTokens);
4271 :
4272 332 : return true;
4273 : }
4274 :
4275 : /************************************************************************/
4276 : /* GDALExtractRPCInfo() */
4277 : /************************************************************************/
4278 :
4279 : /** Extract RPC info from metadata, and apply to an RPCInfo structure.
4280 : *
4281 : * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4282 : *
4283 : * @param papszMD Dictionary of metadata representing RPC
4284 : * @param psRPC (output) Pointer to structure to hold the RPC values.
4285 : * @return TRUE in case of success. FALSE in case of failure.
4286 : */
4287 0 : int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
4288 :
4289 : {
4290 : GDALRPCInfoV2 sRPC;
4291 0 : if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
4292 0 : return FALSE;
4293 0 : memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
4294 0 : return TRUE;
4295 : }
4296 :
4297 : /** Extract RPC info from metadata, and apply to an RPCInfo structure.
4298 : *
4299 : * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4300 : *
4301 : * @param papszMD Dictionary of metadata representing RPC
4302 : * @param psRPC (output) Pointer to structure to hold the RPC values.
4303 : * @return TRUE in case of success. FALSE in case of failure.
4304 : */
4305 83 : int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
4306 :
4307 : {
4308 83 : if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
4309 0 : return FALSE;
4310 :
4311 83 : if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
4312 83 : CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
4313 249 : CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
4314 83 : CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
4315 : {
4316 0 : CPLError(CE_Failure, CPLE_AppDefined,
4317 : "Some required RPC metadata missing in GDALExtractRPCInfo()");
4318 0 : return FALSE;
4319 : }
4320 :
4321 83 : _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
4322 83 : _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
4323 83 : _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
4324 83 : _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
4325 83 : _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
4326 83 : _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
4327 83 : _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
4328 83 : _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
4329 : 1.0);
4330 83 : _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
4331 83 : _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
4332 83 : _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
4333 83 : _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
4334 :
4335 83 : _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
4336 : 0.0);
4337 83 : _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
4338 : 0.0);
4339 83 : _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
4340 : 0.0);
4341 83 : _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
4342 : 0.0);
4343 :
4344 83 : _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
4345 83 : _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
4346 83 : _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
4347 83 : _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
4348 :
4349 83 : return TRUE;
4350 : }
4351 :
4352 : /************************************************************************/
4353 : /* GDALFindAssociatedAuxFile() */
4354 : /************************************************************************/
4355 :
4356 10079 : GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
4357 : GDALAccess eAccess,
4358 : GDALDataset *poDependentDS)
4359 :
4360 : {
4361 10079 : const char *pszAuxSuffixLC = "aux";
4362 10079 : const char *pszAuxSuffixUC = "AUX";
4363 :
4364 10079 : if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
4365 35 : return nullptr;
4366 :
4367 : /* -------------------------------------------------------------------- */
4368 : /* Don't even try to look for an .aux file if we don't have a */
4369 : /* path of any kind. */
4370 : /* -------------------------------------------------------------------- */
4371 10044 : if (strlen(pszBasename) == 0)
4372 38 : return nullptr;
4373 :
4374 : /* -------------------------------------------------------------------- */
4375 : /* We didn't find that, so try and find a corresponding aux */
4376 : /* file. Check that we are the dependent file of the aux */
4377 : /* file, or if we aren't verify that the dependent file does */
4378 : /* not exist, likely mean it is us but some sort of renaming */
4379 : /* has occurred. */
4380 : /* -------------------------------------------------------------------- */
4381 20012 : CPLString osJustFile = CPLGetFilename(pszBasename); // without dir
4382 : CPLString osAuxFilename =
4383 10006 : CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC);
4384 10006 : GDALDataset *poODS = nullptr;
4385 : GByte abyHeader[32];
4386 :
4387 10006 : VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
4388 :
4389 10006 : if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4390 : {
4391 : // Can't found file with lower case suffix. Try the upper case one.
4392 9956 : osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC);
4393 9956 : fp = VSIFOpenL(osAuxFilename, "rb");
4394 : }
4395 :
4396 10006 : if (fp != nullptr)
4397 : {
4398 100 : if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4399 50 : STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4400 : "EHFA_HEADER_TAG"))
4401 : {
4402 : /* Avoid causing failure in opening of main file from SWIG bindings
4403 : */
4404 : /* when auxiliary file cannot be opened (#3269) */
4405 20 : CPLTurnFailureIntoWarning(TRUE);
4406 20 : if (poDependentDS != nullptr && poDependentDS->GetShared())
4407 0 : poODS = GDALDataset::FromHandle(
4408 : GDALOpenShared(osAuxFilename, eAccess));
4409 : else
4410 : poODS =
4411 20 : GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
4412 20 : CPLTurnFailureIntoWarning(FALSE);
4413 : }
4414 50 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4415 : }
4416 :
4417 : /* -------------------------------------------------------------------- */
4418 : /* Try replacing extension with .aux */
4419 : /* -------------------------------------------------------------------- */
4420 10006 : if (poODS != nullptr)
4421 : {
4422 : const char *pszDep =
4423 20 : poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4424 20 : if (pszDep == nullptr)
4425 : {
4426 0 : CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
4427 : osAuxFilename.c_str());
4428 0 : GDALClose(poODS);
4429 0 : poODS = nullptr;
4430 : }
4431 20 : else if (!EQUAL(pszDep, osJustFile))
4432 : {
4433 : VSIStatBufL sStatBuf;
4434 :
4435 0 : if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4436 : {
4437 0 : CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4438 : osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4439 0 : GDALClose(poODS);
4440 0 : poODS = nullptr;
4441 : }
4442 : else
4443 : {
4444 0 : CPLDebug("AUX",
4445 : "%s is for file %s, not %s, but since\n"
4446 : "%s does not exist, we will use .aux file as our own.",
4447 : osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4448 : pszDep);
4449 : }
4450 : }
4451 :
4452 : /* --------------------------------------------------------------------
4453 : */
4454 : /* Confirm that the aux file matches the configuration of the */
4455 : /* dependent dataset. */
4456 : /* --------------------------------------------------------------------
4457 : */
4458 40 : if (poODS != nullptr && poDependentDS != nullptr &&
4459 20 : (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4460 20 : poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4461 17 : poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4462 : {
4463 3 : CPLDebug("AUX",
4464 : "Ignoring aux file %s as its raster configuration\n"
4465 : "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4466 : "x %dB)",
4467 : osAuxFilename.c_str(), poODS->GetRasterXSize(),
4468 : poODS->GetRasterYSize(), poODS->GetRasterCount(),
4469 : poDependentDS->GetRasterXSize(),
4470 : poDependentDS->GetRasterYSize(),
4471 : poDependentDS->GetRasterCount());
4472 :
4473 3 : GDALClose(poODS);
4474 3 : poODS = nullptr;
4475 : }
4476 : }
4477 :
4478 : /* -------------------------------------------------------------------- */
4479 : /* Try appending .aux to the end of the filename. */
4480 : /* -------------------------------------------------------------------- */
4481 10006 : if (poODS == nullptr)
4482 : {
4483 9989 : osAuxFilename = pszBasename;
4484 9989 : osAuxFilename += ".";
4485 9989 : osAuxFilename += pszAuxSuffixLC;
4486 9989 : fp = VSIFOpenL(osAuxFilename, "rb");
4487 9989 : if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4488 : {
4489 : // Can't found file with lower case suffix. Try the upper case one.
4490 9966 : osAuxFilename = pszBasename;
4491 9966 : osAuxFilename += ".";
4492 9966 : osAuxFilename += pszAuxSuffixUC;
4493 9966 : fp = VSIFOpenL(osAuxFilename, "rb");
4494 : }
4495 :
4496 9989 : if (fp != nullptr)
4497 : {
4498 46 : if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4499 23 : STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4500 : "EHFA_HEADER_TAG"))
4501 : {
4502 : /* Avoid causing failure in opening of main file from SWIG
4503 : * bindings */
4504 : /* when auxiliary file cannot be opened (#3269) */
4505 0 : CPLTurnFailureIntoWarning(TRUE);
4506 0 : if (poDependentDS != nullptr && poDependentDS->GetShared())
4507 0 : poODS = GDALDataset::FromHandle(
4508 : GDALOpenShared(osAuxFilename, eAccess));
4509 : else
4510 0 : poODS = GDALDataset::FromHandle(
4511 : GDALOpen(osAuxFilename, eAccess));
4512 0 : CPLTurnFailureIntoWarning(FALSE);
4513 : }
4514 23 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4515 : }
4516 :
4517 9989 : if (poODS != nullptr)
4518 : {
4519 : const char *pszDep =
4520 0 : poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4521 0 : if (pszDep == nullptr)
4522 : {
4523 0 : CPLDebug("AUX",
4524 : "Found %s but it has no dependent file, ignoring.",
4525 : osAuxFilename.c_str());
4526 0 : GDALClose(poODS);
4527 0 : poODS = nullptr;
4528 : }
4529 0 : else if (!EQUAL(pszDep, osJustFile))
4530 : {
4531 : VSIStatBufL sStatBuf;
4532 :
4533 0 : if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4534 : {
4535 0 : CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4536 : osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4537 0 : GDALClose(poODS);
4538 0 : poODS = nullptr;
4539 : }
4540 : else
4541 : {
4542 0 : CPLDebug(
4543 : "AUX",
4544 : "%s is for file %s, not %s, but since\n"
4545 : "%s does not exist, we will use .aux file as our own.",
4546 : osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4547 : pszDep);
4548 : }
4549 : }
4550 : }
4551 : }
4552 :
4553 : /* -------------------------------------------------------------------- */
4554 : /* Confirm that the aux file matches the configuration of the */
4555 : /* dependent dataset. */
4556 : /* -------------------------------------------------------------------- */
4557 10023 : if (poODS != nullptr && poDependentDS != nullptr &&
4558 17 : (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4559 17 : poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4560 17 : poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4561 : {
4562 0 : CPLDebug(
4563 : "AUX",
4564 : "Ignoring aux file %s as its raster configuration\n"
4565 : "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4566 : osAuxFilename.c_str(), poODS->GetRasterXSize(),
4567 : poODS->GetRasterYSize(), poODS->GetRasterCount(),
4568 : poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4569 : poDependentDS->GetRasterCount());
4570 :
4571 0 : GDALClose(poODS);
4572 0 : poODS = nullptr;
4573 : }
4574 :
4575 10006 : return poODS;
4576 : }
4577 :
4578 : /************************************************************************/
4579 : /* Infrastructure to check that dataset characteristics are valid */
4580 : /************************************************************************/
4581 :
4582 : CPL_C_START
4583 :
4584 : /**
4585 : * \brief Return TRUE if the dataset dimensions are valid.
4586 : *
4587 : * @param nXSize raster width
4588 : * @param nYSize raster height
4589 : *
4590 : * @since GDAL 1.7.0
4591 : */
4592 4616 : int GDALCheckDatasetDimensions(int nXSize, int nYSize)
4593 : {
4594 4616 : if (nXSize <= 0 || nYSize <= 0)
4595 : {
4596 8 : CPLError(CE_Failure, CPLE_AppDefined,
4597 : "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4598 8 : return FALSE;
4599 : }
4600 4608 : return TRUE;
4601 : }
4602 :
4603 : /**
4604 : * \brief Return TRUE if the band count is valid.
4605 : *
4606 : * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4607 : * the band count will be compared to the maximum number of band allowed.
4608 : * If not defined, the maximum number allowed is 65536.
4609 : *
4610 : * @param nBands the band count
4611 : * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4612 : *
4613 : * @since GDAL 1.7.0
4614 : */
4615 :
4616 6805 : int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
4617 : {
4618 6805 : if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
4619 : {
4620 12 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
4621 : nBands);
4622 12 : return FALSE;
4623 : }
4624 : const char *pszMaxBandCount =
4625 6793 : CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
4626 : /* coverity[tainted_data] */
4627 6793 : int nMaxBands = atoi(pszMaxBandCount);
4628 6793 : if (nBands > nMaxBands)
4629 : {
4630 2 : CPLError(CE_Failure, CPLE_AppDefined,
4631 : "Invalid band count : %d. Maximum allowed currently is %d. "
4632 : "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4633 : "legitimate number.",
4634 : nBands, nMaxBands);
4635 2 : return FALSE;
4636 : }
4637 6791 : return TRUE;
4638 : }
4639 :
4640 : CPL_C_END
4641 :
4642 : /************************************************************************/
4643 : /* GDALSerializeGCPListToXML() */
4644 : /************************************************************************/
4645 :
4646 24 : void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
4647 : const std::vector<gdal::GCP> &asGCPs,
4648 : const OGRSpatialReference *poGCP_SRS)
4649 : {
4650 48 : CPLString oFmt;
4651 :
4652 : CPLXMLNode *psPamGCPList =
4653 24 : CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
4654 :
4655 24 : CPLXMLNode *psLastChild = nullptr;
4656 :
4657 24 : if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
4658 : {
4659 9 : char *pszWKT = nullptr;
4660 9 : poGCP_SRS->exportToWkt(&pszWKT);
4661 9 : CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
4662 9 : CPLFree(pszWKT);
4663 9 : const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
4664 9 : CPLString osMapping;
4665 27 : for (size_t i = 0; i < mapping.size(); ++i)
4666 : {
4667 18 : if (!osMapping.empty())
4668 9 : osMapping += ",";
4669 18 : osMapping += CPLSPrintf("%d", mapping[i]);
4670 : }
4671 9 : CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
4672 : osMapping.c_str());
4673 :
4674 9 : psLastChild = psPamGCPList->psChild->psNext;
4675 : }
4676 :
4677 21936 : for (const gdal::GCP &gcp : asGCPs)
4678 : {
4679 21912 : CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
4680 :
4681 21912 : if (psLastChild == nullptr)
4682 15 : psPamGCPList->psChild = psXMLGCP;
4683 : else
4684 21897 : psLastChild->psNext = psXMLGCP;
4685 21912 : psLastChild = psXMLGCP;
4686 :
4687 21912 : CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
4688 :
4689 21912 : if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
4690 0 : CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
4691 :
4692 21912 : CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
4693 :
4694 21912 : CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
4695 :
4696 21912 : CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
4697 :
4698 21912 : CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
4699 :
4700 : /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
4701 : * back */
4702 21912 : if (gcp.Z() != 0.0)
4703 21860 : CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
4704 : }
4705 24 : }
4706 :
4707 : /************************************************************************/
4708 : /* GDALDeserializeGCPListFromXML() */
4709 : /************************************************************************/
4710 :
4711 91 : void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
4712 : std::vector<gdal::GCP> &asGCPs,
4713 : OGRSpatialReference **ppoGCP_SRS)
4714 : {
4715 91 : if (ppoGCP_SRS)
4716 : {
4717 : const char *pszRawProj =
4718 76 : CPLGetXMLValue(psGCPList, "Projection", nullptr);
4719 :
4720 76 : *ppoGCP_SRS = nullptr;
4721 76 : if (pszRawProj && pszRawProj[0])
4722 : {
4723 60 : *ppoGCP_SRS = new OGRSpatialReference();
4724 : (*ppoGCP_SRS)
4725 60 : ->SetFromUserInput(
4726 : pszRawProj,
4727 : OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4728 :
4729 : const char *pszMapping =
4730 60 : CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
4731 60 : if (pszMapping)
4732 : {
4733 : char **papszTokens =
4734 14 : CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
4735 28 : std::vector<int> anMapping;
4736 42 : for (int i = 0; papszTokens && papszTokens[i]; i++)
4737 : {
4738 28 : anMapping.push_back(atoi(papszTokens[i]));
4739 : }
4740 14 : CSLDestroy(papszTokens);
4741 14 : (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
4742 : }
4743 : else
4744 : {
4745 : (*ppoGCP_SRS)
4746 46 : ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4747 : }
4748 : }
4749 : }
4750 :
4751 91 : asGCPs.clear();
4752 24645 : for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
4753 24554 : psXMLGCP = psXMLGCP->psNext)
4754 : {
4755 24554 : if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
4756 82 : continue;
4757 :
4758 48944 : gdal::GCP gcp;
4759 24472 : gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
4760 24472 : gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
4761 :
4762 : const auto ParseDoubleValue =
4763 97888 : [psXMLGCP](const char *pszParameter, double &dfVal)
4764 : {
4765 : const char *pszVal =
4766 97888 : CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
4767 97888 : if (!pszVal)
4768 : {
4769 0 : CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
4770 : pszParameter);
4771 0 : return false;
4772 : }
4773 97888 : char *endptr = nullptr;
4774 97888 : dfVal = CPLStrtod(pszVal, &endptr);
4775 97888 : if (endptr == pszVal)
4776 : {
4777 0 : CPLError(CE_Failure, CPLE_AppDefined,
4778 : "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4779 0 : return false;
4780 : }
4781 97888 : return true;
4782 24472 : };
4783 :
4784 24472 : bool bOK = true;
4785 24472 : if (!ParseDoubleValue("Pixel", gcp.Pixel()))
4786 0 : bOK = false;
4787 24472 : if (!ParseDoubleValue("Line", gcp.Line()))
4788 0 : bOK = false;
4789 24472 : if (!ParseDoubleValue("X", gcp.X()))
4790 0 : bOK = false;
4791 24472 : if (!ParseDoubleValue("Y", gcp.Y()))
4792 0 : bOK = false;
4793 24472 : const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
4794 24472 : if (pszZ == nullptr)
4795 : {
4796 : // Note: GDAL 1.10.1 and older generated #GCPZ,
4797 : // but could not read it back.
4798 2404 : pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
4799 : }
4800 24472 : char *endptr = nullptr;
4801 24472 : gcp.Z() = CPLStrtod(pszZ, &endptr);
4802 24472 : if (endptr == pszZ)
4803 : {
4804 0 : CPLError(CE_Failure, CPLE_AppDefined,
4805 : "GCP#Z=%s is an invalid value", pszZ);
4806 0 : bOK = false;
4807 : }
4808 :
4809 24472 : if (bOK)
4810 : {
4811 24472 : asGCPs.emplace_back(std::move(gcp));
4812 : }
4813 : }
4814 91 : }
4815 :
4816 : /************************************************************************/
4817 : /* GDALSerializeOpenOptionsToXML() */
4818 : /************************************************************************/
4819 :
4820 2618 : void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
4821 : CSLConstList papszOpenOptions)
4822 : {
4823 2618 : if (papszOpenOptions != nullptr)
4824 : {
4825 : CPLXMLNode *psOpenOptions =
4826 5 : CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
4827 5 : CPLXMLNode *psLastChild = nullptr;
4828 :
4829 10 : for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
4830 : papszIter++)
4831 : {
4832 : const char *pszRawValue;
4833 5 : char *pszKey = nullptr;
4834 : CPLXMLNode *psOOI;
4835 :
4836 5 : pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
4837 :
4838 5 : psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
4839 5 : if (psLastChild == nullptr)
4840 5 : psOpenOptions->psChild = psOOI;
4841 : else
4842 0 : psLastChild->psNext = psOOI;
4843 5 : psLastChild = psOOI;
4844 :
4845 5 : CPLSetXMLValue(psOOI, "#key", pszKey);
4846 5 : CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
4847 :
4848 5 : CPLFree(pszKey);
4849 : }
4850 : }
4851 2618 : }
4852 :
4853 : /************************************************************************/
4854 : /* GDALDeserializeOpenOptionsFromXML() */
4855 : /************************************************************************/
4856 :
4857 3866 : char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
4858 : {
4859 3866 : char **papszOpenOptions = nullptr;
4860 : const CPLXMLNode *psOpenOptions =
4861 3866 : CPLGetXMLNode(psParentNode, "OpenOptions");
4862 3866 : if (psOpenOptions != nullptr)
4863 : {
4864 : const CPLXMLNode *psOOI;
4865 34 : for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
4866 17 : psOOI = psOOI->psNext)
4867 : {
4868 17 : if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
4869 17 : psOOI->psChild == nullptr ||
4870 17 : psOOI->psChild->psNext == nullptr ||
4871 17 : psOOI->psChild->eType != CXT_Attribute ||
4872 17 : psOOI->psChild->psChild == nullptr)
4873 0 : continue;
4874 :
4875 17 : char *pszName = psOOI->psChild->psChild->pszValue;
4876 17 : char *pszValue = psOOI->psChild->psNext->pszValue;
4877 17 : if (pszName != nullptr && pszValue != nullptr)
4878 : papszOpenOptions =
4879 17 : CSLSetNameValue(papszOpenOptions, pszName, pszValue);
4880 : }
4881 : }
4882 3866 : return papszOpenOptions;
4883 : }
4884 :
4885 : /************************************************************************/
4886 : /* GDALRasterIOGetResampleAlg() */
4887 : /************************************************************************/
4888 :
4889 2241 : GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
4890 : {
4891 2241 : GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
4892 2241 : if (STARTS_WITH_CI(pszResampling, "NEAR"))
4893 18 : eResampleAlg = GRIORA_NearestNeighbour;
4894 2223 : else if (EQUAL(pszResampling, "BILINEAR"))
4895 2054 : eResampleAlg = GRIORA_Bilinear;
4896 169 : else if (EQUAL(pszResampling, "CUBIC"))
4897 114 : eResampleAlg = GRIORA_Cubic;
4898 55 : else if (EQUAL(pszResampling, "CUBICSPLINE"))
4899 4 : eResampleAlg = GRIORA_CubicSpline;
4900 51 : else if (EQUAL(pszResampling, "LANCZOS"))
4901 1 : eResampleAlg = GRIORA_Lanczos;
4902 50 : else if (EQUAL(pszResampling, "AVERAGE"))
4903 43 : eResampleAlg = GRIORA_Average;
4904 7 : else if (EQUAL(pszResampling, "RMS"))
4905 1 : eResampleAlg = GRIORA_RMS;
4906 6 : else if (EQUAL(pszResampling, "MODE"))
4907 5 : eResampleAlg = GRIORA_Mode;
4908 1 : else if (EQUAL(pszResampling, "GAUSS"))
4909 1 : eResampleAlg = GRIORA_Gauss;
4910 : else
4911 0 : CPLError(CE_Warning, CPLE_NotSupported,
4912 : "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
4913 2241 : return eResampleAlg;
4914 : }
4915 :
4916 : /************************************************************************/
4917 : /* GDALRasterIOGetResampleAlgStr() */
4918 : /************************************************************************/
4919 :
4920 1 : const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
4921 : {
4922 1 : switch (eResampleAlg)
4923 : {
4924 0 : case GRIORA_NearestNeighbour:
4925 0 : return "NearestNeighbour";
4926 0 : case GRIORA_Bilinear:
4927 0 : return "Bilinear";
4928 1 : case GRIORA_Cubic:
4929 1 : return "Cubic";
4930 0 : case GRIORA_CubicSpline:
4931 0 : return "CubicSpline";
4932 0 : case GRIORA_Lanczos:
4933 0 : return "Lanczos";
4934 0 : case GRIORA_Average:
4935 0 : return "Average";
4936 0 : case GRIORA_RMS:
4937 0 : return "RMS";
4938 0 : case GRIORA_Mode:
4939 0 : return "Mode";
4940 0 : case GRIORA_Gauss:
4941 0 : return "Gauss";
4942 0 : default:
4943 0 : CPLAssert(false);
4944 : return "Unknown";
4945 : }
4946 : }
4947 :
4948 : /************************************************************************/
4949 : /* GDALRasterIOExtraArgSetResampleAlg() */
4950 : /************************************************************************/
4951 :
4952 4466300 : void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
4953 : int nXSize, int nYSize, int nBufXSize,
4954 : int nBufYSize)
4955 : {
4956 4466300 : if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
4957 370112 : psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
4958 : {
4959 : const char *pszResampling =
4960 367912 : CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
4961 367912 : if (pszResampling != nullptr)
4962 : {
4963 1 : psExtraArg->eResampleAlg =
4964 1 : GDALRasterIOGetResampleAlg(pszResampling);
4965 : }
4966 : }
4967 4466300 : }
4968 :
4969 : /************************************************************************/
4970 : /* GDALCanFileAcceptSidecarFile() */
4971 : /************************************************************************/
4972 :
4973 106287 : int GDALCanFileAcceptSidecarFile(const char *pszFilename)
4974 : {
4975 106287 : if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
4976 0 : return FALSE;
4977 : // Do no attempt reading side-car files on /vsisubfile/ (#6241)
4978 106287 : if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
4979 402 : return FALSE;
4980 105885 : return TRUE;
4981 : }
4982 :
4983 : /************************************************************************/
4984 : /* GDALCanReliablyUseSiblingFileList() */
4985 : /************************************************************************/
4986 :
4987 : /* Try to address https://github.com/OSGeo/gdal/issues/2903 */
4988 : /* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
4989 : /* (normalization form decomposed). The filesystem takes care of converting */
4990 : /* precomposed form as often coming from user interface to this NFD variant */
4991 : /* See
4992 : * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
4993 : */
4994 : /* And readdir() will return such NFD variant encoding. Consequently comparing
4995 : */
4996 : /* the user filename with ones with readdir() is not reliable */
4997 : /* - APFS preserves both case and normalization of the filename on disk in all
4998 : */
4999 : /* variants. In macOS High Sierra, APFS is normalization-insensitive in both
5000 : */
5001 : /* the case-insensitive and case-sensitive variants, using a hash-based native
5002 : */
5003 : /* normalization scheme. APFS preserves the normalization of the filename and
5004 : */
5005 : /* uses hashes of the normalized form of the filename to provide normalization
5006 : */
5007 : /* insensitivity. */
5008 : /* From
5009 : * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
5010 : */
5011 : /* Issues might still arise if the file has been created using one of the
5012 : * UTF-8 */
5013 : /* encoding (likely the decomposed one if using MacOS specific API), but the
5014 : */
5015 : /* string passed to GDAL for opening would be with another one (likely the
5016 : * precomposed one) */
5017 119484 : bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
5018 : {
5019 : #ifdef __APPLE__
5020 : for (int i = 0; pszFilename[i] != 0; ++i)
5021 : {
5022 : if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
5023 : {
5024 : // non-ASCII character found
5025 :
5026 : // if this is a network storage, assume no issue
5027 : if (!VSIIsLocal(pszFilename))
5028 : {
5029 : return true;
5030 : }
5031 : return false;
5032 : }
5033 : }
5034 : return true;
5035 : #else
5036 : (void)pszFilename;
5037 119484 : return true;
5038 : #endif
5039 : }
5040 :
5041 : /************************************************************************/
5042 : /* GDALAdjustNoDataCloseToFloatMax() */
5043 : /************************************************************************/
5044 :
5045 1172 : double GDALAdjustNoDataCloseToFloatMax(double dfVal)
5046 : {
5047 1172 : const auto kMaxFloat = std::numeric_limits<float>::max();
5048 1172 : if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
5049 33 : return -kMaxFloat;
5050 1139 : if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
5051 8 : return kMaxFloat;
5052 1131 : return dfVal;
5053 : }
5054 :
5055 : /************************************************************************/
5056 : /* GDALCopyNoDataValue() */
5057 : /************************************************************************/
5058 :
5059 2782 : void GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand)
5060 : {
5061 :
5062 : int bSuccess;
5063 2782 : const auto eSrcDataType = poSrcBand->GetRasterDataType();
5064 2782 : const auto eDstDataType = poDstBand->GetRasterDataType();
5065 2782 : if (eSrcDataType == GDT_Int64)
5066 : {
5067 3 : const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
5068 3 : if (bSuccess)
5069 : {
5070 3 : if (eDstDataType == GDT_Int64)
5071 : {
5072 3 : poDstBand->SetNoDataValueAsInt64(nNoData);
5073 : }
5074 0 : else if (eDstDataType == GDT_UInt64)
5075 : {
5076 0 : if (nNoData >= 0)
5077 0 : poDstBand->SetNoDataValueAsUInt64(
5078 0 : static_cast<uint64_t>(nNoData));
5079 : }
5080 0 : else if (nNoData ==
5081 0 : static_cast<int64_t>(static_cast<double>(nNoData)))
5082 : {
5083 0 : poDstBand->SetNoDataValue(static_cast<double>(nNoData));
5084 : }
5085 : }
5086 : }
5087 2779 : else if (eSrcDataType == GDT_UInt64)
5088 : {
5089 3 : const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
5090 3 : if (bSuccess)
5091 : {
5092 3 : if (eDstDataType == GDT_UInt64)
5093 : {
5094 3 : poDstBand->SetNoDataValueAsUInt64(nNoData);
5095 : }
5096 0 : else if (eDstDataType == GDT_Int64)
5097 : {
5098 0 : if (nNoData <
5099 0 : static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
5100 : {
5101 0 : poDstBand->SetNoDataValueAsInt64(
5102 0 : static_cast<int64_t>(nNoData));
5103 : }
5104 : }
5105 0 : else if (nNoData ==
5106 0 : static_cast<uint64_t>(static_cast<double>(nNoData)))
5107 : {
5108 0 : poDstBand->SetNoDataValue(static_cast<double>(nNoData));
5109 : }
5110 : }
5111 : }
5112 : else
5113 : {
5114 2776 : const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
5115 2776 : if (bSuccess)
5116 : {
5117 194 : if (eDstDataType == GDT_Int64)
5118 : {
5119 0 : if (dfNoData >= static_cast<double>(
5120 0 : std::numeric_limits<int64_t>::min()) &&
5121 0 : dfNoData <= static_cast<double>(
5122 0 : std::numeric_limits<int64_t>::max()) &&
5123 : dfNoData ==
5124 0 : static_cast<double>(static_cast<int64_t>(dfNoData)))
5125 : {
5126 0 : poDstBand->SetNoDataValueAsInt64(
5127 0 : static_cast<int64_t>(dfNoData));
5128 : }
5129 : }
5130 194 : else if (eDstDataType == GDT_UInt64)
5131 : {
5132 0 : if (dfNoData >= static_cast<double>(
5133 0 : std::numeric_limits<uint64_t>::min()) &&
5134 0 : dfNoData <= static_cast<double>(
5135 0 : std::numeric_limits<uint64_t>::max()) &&
5136 : dfNoData ==
5137 0 : static_cast<double>(static_cast<uint64_t>(dfNoData)))
5138 : {
5139 0 : poDstBand->SetNoDataValueAsInt64(
5140 0 : static_cast<uint64_t>(dfNoData));
5141 : }
5142 : }
5143 : else
5144 : {
5145 194 : poDstBand->SetNoDataValue(dfNoData);
5146 : }
5147 : }
5148 : }
5149 2782 : }
5150 :
5151 : /************************************************************************/
5152 : /* GDALGetNoDataValueCastToDouble() */
5153 : /************************************************************************/
5154 :
5155 2 : double GDALGetNoDataValueCastToDouble(int64_t nVal)
5156 : {
5157 2 : const double dfVal = static_cast<double>(nVal);
5158 2 : if (static_cast<int64_t>(dfVal) != nVal)
5159 : {
5160 0 : CPLError(CE_Warning, CPLE_AppDefined,
5161 : "GetNoDataValue() returns an approximate value of the "
5162 : "true nodata value = " CPL_FRMT_GIB ". Use "
5163 : "GetNoDataValueAsInt64() instead",
5164 : static_cast<GIntBig>(nVal));
5165 : }
5166 2 : return dfVal;
5167 : }
5168 :
5169 2 : double GDALGetNoDataValueCastToDouble(uint64_t nVal)
5170 : {
5171 2 : const double dfVal = static_cast<double>(nVal);
5172 2 : if (static_cast<uint64_t>(dfVal) != nVal)
5173 : {
5174 0 : CPLError(CE_Warning, CPLE_AppDefined,
5175 : "GetNoDataValue() returns an approximate value of the "
5176 : "true nodata value = " CPL_FRMT_GUIB ". Use "
5177 : "GetNoDataValueAsUInt64() instead",
5178 : static_cast<GUIntBig>(nVal));
5179 : }
5180 2 : return dfVal;
5181 : }
5182 :
5183 : /************************************************************************/
5184 : /* GDALGetCompressionFormatForJPEG() */
5185 : /************************************************************************/
5186 :
5187 : //! @cond Doxygen_Suppress
5188 23 : std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
5189 : {
5190 23 : std::string osRet;
5191 23 : const auto nSavedPos = VSIFTellL(fp);
5192 : GByte abyMarkerHeader[4];
5193 23 : if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
5194 23 : VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
5195 46 : abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
5196 : {
5197 23 : osRet = "JPEG";
5198 23 : bool bHasAPP14Adobe = false;
5199 23 : GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
5200 23 : int nNumComponents = 0;
5201 : while (true)
5202 : {
5203 171 : const auto nCurPos = VSIFTellL(fp);
5204 171 : if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
5205 0 : break;
5206 171 : if (abyMarkerHeader[0] != 0xFF)
5207 0 : break;
5208 171 : const GByte markerType = abyMarkerHeader[1];
5209 171 : const size_t nMarkerSize =
5210 171 : abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
5211 171 : if (nMarkerSize < 2)
5212 0 : break;
5213 171 : if (markerType >= 0xC0 && markerType <= 0xCF &&
5214 23 : markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
5215 : {
5216 23 : switch (markerType)
5217 : {
5218 21 : case 0xC0:
5219 21 : osRet += ";frame_type=SOF0_baseline";
5220 21 : break;
5221 2 : case 0xC1:
5222 2 : osRet += ";frame_type=SOF1_extended_sequential";
5223 2 : break;
5224 0 : case 0xC2:
5225 0 : osRet += ";frame_type=SOF2_progressive_huffman";
5226 0 : break;
5227 0 : case 0xC3:
5228 : osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
5229 0 : "supported=no";
5230 0 : break;
5231 0 : case 0xC5:
5232 : osRet += ";frame_type="
5233 : "SOF5_differential_sequential_huffman;"
5234 0 : "libjpeg_supported=no";
5235 0 : break;
5236 0 : case 0xC6:
5237 : osRet += ";frame_type=SOF6_differential_progressive_"
5238 0 : "huffman;libjpeg_supported=no";
5239 0 : break;
5240 0 : case 0xC7:
5241 : osRet += ";frame_type="
5242 : "SOF7_differential_lossless_huffman;"
5243 0 : "libjpeg_supported=no";
5244 0 : break;
5245 0 : case 0xC9:
5246 : osRet += ";frame_type="
5247 0 : "SOF9_extended_sequential_arithmetic";
5248 0 : break;
5249 0 : case 0xCA:
5250 0 : osRet += ";frame_type=SOF10_progressive_arithmetic";
5251 0 : break;
5252 0 : case 0xCB:
5253 : osRet += ";frame_type="
5254 : "SOF11_lossless_arithmetic;libjpeg_"
5255 0 : "supported=no";
5256 0 : break;
5257 0 : case 0xCD:
5258 : osRet += ";frame_type=SOF13_differential_sequential_"
5259 0 : "arithmetic;libjpeg_supported=no";
5260 0 : break;
5261 0 : case 0xCE:
5262 : osRet += ";frame_type=SOF14_differential_progressive_"
5263 0 : "arithmetic;libjpeg_supported=no";
5264 0 : break;
5265 0 : case 0xCF:
5266 : osRet += ";frame_type=SOF15_differential_lossless_"
5267 0 : "arithmetic;libjpeg_supported=no";
5268 0 : break;
5269 0 : default:
5270 0 : break;
5271 : }
5272 : GByte abySegmentBegin[6];
5273 23 : if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
5274 23 : fp) != 1)
5275 0 : break;
5276 23 : osRet += ";bit_depth=";
5277 23 : osRet += CPLSPrintf("%d", abySegmentBegin[0]);
5278 23 : nNumComponents = abySegmentBegin[5];
5279 23 : osRet += ";num_components=";
5280 23 : osRet += CPLSPrintf("%d", nNumComponents);
5281 23 : if (nNumComponents == 3)
5282 : {
5283 : GByte abySegmentNext[3 * 3];
5284 13 : if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
5285 13 : fp) != 1)
5286 0 : break;
5287 13 : if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
5288 0 : abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
5289 0 : abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
5290 : {
5291 : // no subsampling
5292 0 : osRet += ";subsampling=4:4:4";
5293 : }
5294 13 : else if (abySegmentNext[0] == 1 &&
5295 11 : abySegmentNext[1] == 0x22 &&
5296 11 : abySegmentNext[3] == 2 &&
5297 11 : abySegmentNext[4] == 0x11 &&
5298 11 : abySegmentNext[6] == 3 &&
5299 11 : abySegmentNext[7] == 0x11)
5300 : {
5301 : // classic subsampling
5302 11 : osRet += ";subsampling=4:2:0";
5303 : }
5304 2 : else if (abySegmentNext[0] == 1 &&
5305 0 : abySegmentNext[1] == 0x21 &&
5306 0 : abySegmentNext[3] == 2 &&
5307 0 : abySegmentNext[4] == 0x11 &&
5308 0 : abySegmentNext[6] == 3 &&
5309 0 : abySegmentNext[7] == 0x11)
5310 : {
5311 0 : osRet += ";subsampling=4:2:2";
5312 : }
5313 23 : }
5314 : }
5315 148 : else if (markerType == 0xEE && nMarkerSize == 14)
5316 : {
5317 1 : if (VSIFReadL(abyAPP14AdobeMarkerData,
5318 2 : sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
5319 1 : memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
5320 : 0)
5321 : {
5322 1 : bHasAPP14Adobe = true;
5323 : }
5324 : }
5325 147 : else if (markerType == 0xDA)
5326 : {
5327 : // Start of scan
5328 23 : break;
5329 : }
5330 148 : VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
5331 148 : }
5332 46 : std::string osColorspace;
5333 23 : if (bHasAPP14Adobe)
5334 : {
5335 1 : if (abyAPP14AdobeMarkerData[11] == 0)
5336 : {
5337 1 : if (nNumComponents == 3)
5338 0 : osColorspace = "RGB";
5339 1 : else if (nNumComponents == 4)
5340 1 : osColorspace = "CMYK";
5341 : }
5342 0 : else if (abyAPP14AdobeMarkerData[11] == 1)
5343 : {
5344 0 : osColorspace = "YCbCr";
5345 : }
5346 0 : else if (abyAPP14AdobeMarkerData[11] == 2)
5347 : {
5348 0 : osColorspace = "YCCK";
5349 : }
5350 : }
5351 : else
5352 : {
5353 22 : if (nNumComponents == 3)
5354 13 : osColorspace = "YCbCr";
5355 9 : else if (nNumComponents == 4)
5356 1 : osColorspace = "CMYK";
5357 : }
5358 23 : osRet += ";colorspace=";
5359 23 : if (!osColorspace.empty())
5360 15 : osRet += osColorspace;
5361 : else
5362 8 : osRet += "unknown";
5363 : }
5364 23 : if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
5365 : {
5366 0 : CPLError(CE_Failure, CPLE_AppDefined,
5367 : "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
5368 : }
5369 46 : return osRet;
5370 : }
5371 :
5372 16 : std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
5373 : size_t nBufferSize)
5374 : {
5375 16 : VSILFILE *fp = VSIFileFromMemBuffer(
5376 : nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5377 : false);
5378 16 : std::string osRet = GDALGetCompressionFormatForJPEG(fp);
5379 16 : VSIFCloseL(fp);
5380 16 : return osRet;
5381 : }
5382 :
5383 : //! @endcond
5384 :
5385 : /************************************************************************/
5386 : /* GDALGetNoDataReplacementValue() */
5387 : /************************************************************************/
5388 :
5389 : /**
5390 : * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5391 : * is out of range for the specified data type (dt).
5392 : * For UInt64 and Int64 data type this function cannot reliably trusted
5393 : * because their nodata values might not always be representable exactly
5394 : * as a double, in particular the maximum absolute value for those types
5395 : * is 2^53.
5396 : *
5397 : * The replacement value is a value that can be used in a computation
5398 : * whose result would match by accident the nodata value, whereas it is
5399 : * meant to be valid. For example, for a dataset with a nodata value of 0,
5400 : * when averaging -1 and 1, one would get normally a value of 0. The
5401 : * replacement nodata value can then be substituted to that 0 value to still
5402 : * get a valid value, as close as practical to the true value, while being
5403 : * different from the nodata value.
5404 : *
5405 : * @param dt Data type
5406 : * @param dfNoDataValue The no data value
5407 :
5408 : * @since GDAL 3.9
5409 : */
5410 227 : double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
5411 : {
5412 :
5413 : // The logic here is to check if the value is out of range for the
5414 : // specified data type and return a replacement value if it is, return
5415 : // 0 otherwise.
5416 227 : double dfReplacementVal = dfNoDataValue;
5417 227 : if (dt == GDT_Byte)
5418 : {
5419 63 : if (GDALClampDoubleValue(dfNoDataValue,
5420 63 : std::numeric_limits<uint8_t>::lowest(),
5421 63 : std::numeric_limits<uint8_t>::max()))
5422 : {
5423 2 : return 0;
5424 : }
5425 61 : if (dfNoDataValue == std::numeric_limits<unsigned char>::max())
5426 3 : dfReplacementVal = std::numeric_limits<unsigned char>::max() - 1;
5427 : else
5428 58 : dfReplacementVal = dfNoDataValue + 1;
5429 : }
5430 164 : else if (dt == GDT_Int8)
5431 : {
5432 5 : if (GDALClampDoubleValue(dfNoDataValue,
5433 5 : std::numeric_limits<int8_t>::lowest(),
5434 5 : std::numeric_limits<int8_t>::max()))
5435 : {
5436 2 : return 0;
5437 : }
5438 3 : if (dfNoDataValue == std::numeric_limits<GInt8>::max())
5439 1 : dfReplacementVal = std::numeric_limits<GInt8>::max() - 1;
5440 : else
5441 2 : dfReplacementVal = dfNoDataValue + 1;
5442 : }
5443 159 : else if (dt == GDT_UInt16)
5444 : {
5445 6 : if (GDALClampDoubleValue(dfNoDataValue,
5446 6 : std::numeric_limits<uint16_t>::lowest(),
5447 6 : std::numeric_limits<uint16_t>::max()))
5448 : {
5449 2 : return 0;
5450 : }
5451 4 : if (dfNoDataValue == std::numeric_limits<GUInt16>::max())
5452 1 : dfReplacementVal = std::numeric_limits<GUInt16>::max() - 1;
5453 : else
5454 3 : dfReplacementVal = dfNoDataValue + 1;
5455 : }
5456 153 : else if (dt == GDT_Int16)
5457 : {
5458 5 : if (GDALClampDoubleValue(dfNoDataValue,
5459 5 : std::numeric_limits<int16_t>::lowest(),
5460 5 : std::numeric_limits<int16_t>::max()))
5461 : {
5462 2 : return 0;
5463 : }
5464 3 : if (dfNoDataValue == std::numeric_limits<GInt16>::max())
5465 1 : dfReplacementVal = std::numeric_limits<GInt16>::max() - 1;
5466 : else
5467 2 : dfReplacementVal = dfNoDataValue + 1;
5468 : }
5469 148 : else if (dt == GDT_UInt32)
5470 : {
5471 5 : if (GDALClampDoubleValue(dfNoDataValue,
5472 : std::numeric_limits<uint32_t>::lowest(),
5473 : std::numeric_limits<uint32_t>::max()))
5474 : {
5475 2 : return 0;
5476 : }
5477 3 : if (dfNoDataValue == std::numeric_limits<GUInt32>::max())
5478 1 : dfReplacementVal = std::numeric_limits<GUInt32>::max() - 1;
5479 : else
5480 2 : dfReplacementVal = dfNoDataValue + 1;
5481 : }
5482 143 : else if (dt == GDT_Int32)
5483 : {
5484 7 : if (GDALClampDoubleValue(dfNoDataValue,
5485 : std::numeric_limits<int32_t>::lowest(),
5486 : std::numeric_limits<int32_t>::max()))
5487 : {
5488 2 : return 0;
5489 : }
5490 5 : if (dfNoDataValue == std::numeric_limits<int32_t>::max())
5491 1 : dfReplacementVal = std::numeric_limits<int32_t>::max() - 1;
5492 : else
5493 4 : dfReplacementVal = dfNoDataValue + 1;
5494 : }
5495 136 : else if (dt == GDT_UInt64)
5496 : {
5497 : // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5498 : // so we take the next lower value representable as a double 18446744073709549567
5499 : static const double dfMaxUInt64Value{
5500 : std::nextafter(
5501 : static_cast<double>(std::numeric_limits<uint64_t>::max()), 0) -
5502 : 1};
5503 :
5504 5 : if (GDALClampDoubleValue(dfNoDataValue,
5505 : std::numeric_limits<uint64_t>::lowest(),
5506 : std::numeric_limits<uint64_t>::max()))
5507 : {
5508 2 : return 0;
5509 : }
5510 :
5511 3 : if (dfNoDataValue >=
5512 3 : static_cast<double>(std::numeric_limits<uint64_t>::max()))
5513 1 : dfReplacementVal = dfMaxUInt64Value;
5514 : else
5515 2 : dfReplacementVal = dfNoDataValue + 1;
5516 : }
5517 131 : else if (dt == GDT_Int64)
5518 : {
5519 : // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5520 : // so we take the next lower value representable as a double 9223372036854774784
5521 : static const double dfMaxInt64Value{
5522 : std::nextafter(
5523 : static_cast<double>(std::numeric_limits<int64_t>::max()), 0) -
5524 : 1};
5525 :
5526 5 : if (GDALClampDoubleValue(dfNoDataValue,
5527 : std::numeric_limits<int64_t>::lowest(),
5528 : std::numeric_limits<int64_t>::max()))
5529 : {
5530 2 : return 0;
5531 : }
5532 :
5533 3 : if (dfNoDataValue >=
5534 3 : static_cast<double>(std::numeric_limits<int64_t>::max()))
5535 1 : dfReplacementVal = dfMaxInt64Value;
5536 : else
5537 2 : dfReplacementVal = dfNoDataValue + 1;
5538 : }
5539 126 : else if (dt == GDT_Float32)
5540 : {
5541 :
5542 33 : if (GDALClampDoubleValue(dfNoDataValue,
5543 : std::numeric_limits<float>::lowest(),
5544 : std::numeric_limits<float>::max()))
5545 : {
5546 4 : return 0;
5547 : }
5548 :
5549 29 : if (dfNoDataValue == std::numeric_limits<float>::max())
5550 : {
5551 1 : dfReplacementVal =
5552 1 : std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
5553 : }
5554 : else
5555 : {
5556 28 : dfReplacementVal =
5557 28 : std::nextafter(static_cast<float>(dfNoDataValue),
5558 : std::numeric_limits<float>::max());
5559 : }
5560 : }
5561 93 : else if (dt == GDT_Float64)
5562 : {
5563 93 : if (GDALClampDoubleValue(dfNoDataValue,
5564 : std::numeric_limits<double>::lowest(),
5565 : std::numeric_limits<double>::max()))
5566 : {
5567 2 : return 0;
5568 : }
5569 :
5570 91 : if (dfNoDataValue == std::numeric_limits<double>::max())
5571 : {
5572 2 : dfReplacementVal = std::nextafter(dfNoDataValue, 0.0f);
5573 : }
5574 : else
5575 : {
5576 89 : dfReplacementVal = std::nextafter(
5577 : dfNoDataValue, std::numeric_limits<double>::max());
5578 : }
5579 : }
5580 :
5581 205 : return dfReplacementVal;
5582 : }
|