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