Coverage Report

Created: 2025-12-31 06:48

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