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