Coverage Report

Created: 2025-11-16 06:25

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