Coverage Report

Created: 2025-08-28 06:57

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