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