Coverage Report

Created: 2025-06-13 06:29

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