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