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