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