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