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