• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 16038479760

03 Jul 2025 12:12AM UTC coverage: 71.106% (-0.004%) from 71.11%
16038479760

Pull #12692

github

web-flow
Merge efeee3602 into b5d2a80d4
Pull Request #12692: C/C++/Python band algebra: add gdal.abs(), sqrt(), log(), log10() and pow()

80 of 87 new or added lines in 3 files covered. (91.95%)

8848 existing lines in 54 files now uncovered.

574863 of 808463 relevant lines covered (71.11%)

255001.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.62
/gcore/gdal_misc.cpp
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[],
104,833✔
58
                             const int panBits[])
59
{
60
    if (pabFloating[0] != pabFloating[1])
104,833✔
61
    {
62
        const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
512✔
63
        const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
512✔
64

65
        return std::max(panBits[nFloatingTypeIndex],
512✔
66
                        2 * panBits[nNotFloatingTypeIndex]);
512✔
67
    }
68

69
    if (pabSigned[0] != pabSigned[1])
104,321✔
70
    {
71
        if (!pabSigned[0] && panBits[0] < panBits[1])
430✔
72
            return panBits[1];
329✔
73
        if (!pabSigned[1] && panBits[1] < panBits[0])
101✔
74
            return panBits[0];
23✔
75

76
        const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
78✔
77
        const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
78✔
78

79
        return std::max(panBits[nSignedTypeIndex],
78✔
80
                        2 * panBits[nUnsignedTypeIndex]);
78✔
81
    }
82

83
    return std::max(panBits[0], panBits[1]);
103,891✔
84
}
85

86
static int GetNonComplexDataTypeElementSizeBits(GDALDataType eDataType)
209,666✔
87
{
88
    switch (eDataType)
209,666✔
89
    {
90
        case GDT_Byte:
74,809✔
91
        case GDT_Int8:
92
            return 8;
74,809✔
93

94
        case GDT_UInt16:
132,439✔
95
        case GDT_Int16:
96
        case GDT_Float16:
97
        case GDT_CInt16:
98
        case GDT_CFloat16:
99
            return 16;
132,439✔
100

101
        case GDT_UInt32:
1,476✔
102
        case GDT_Int32:
103
        case GDT_Float32:
104
        case GDT_CInt32:
105
        case GDT_CFloat32:
106
            return 32;
1,476✔
107

108
        case GDT_Float64:
942✔
109
        case GDT_CFloat64:
110
        case GDT_UInt64:
111
        case GDT_Int64:
112
            return 64;
942✔
113

114
        case GDT_Unknown:
×
115
        case GDT_TypeCount:
116
            break;
×
117
    }
118
    return 0;
×
119
}
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,
107,746✔
136
                                           GDALDataType eType2)
137

138
{
139
    if (eType1 == GDT_Unknown)
107,746✔
140
        return eType2;
2,913✔
141
    if (eType2 == GDT_Unknown)
104,833✔
142
        return eType1;
×
143

144
    const int panBits[] = {GetNonComplexDataTypeElementSizeBits(eType1),
104,833✔
145
                           GetNonComplexDataTypeElementSizeBits(eType2)};
104,833✔
146

147
    if (panBits[0] == 0 || panBits[1] == 0)
104,833✔
148
        return GDT_Unknown;
×
149

150
    const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
104,833✔
151
                              CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
104,833✔
152

153
    const bool bSigned = pabSigned[0] || pabSigned[1];
104,833✔
154
    const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
104,833✔
155
                                CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
104,833✔
156
    const bool bFloating = pabFloating[0] || pabFloating[1];
104,833✔
157
    const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
104,833✔
158
    const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
209,194✔
159
                            CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
104,361✔
160

161
    return GDALFindDataType(nBits, bSigned, bFloating, bIsComplex);
104,833✔
162
}
163

164
/************************************************************************/
165
/*                        GDALDataTypeUnionWithValue()                  */
166
/************************************************************************/
167

168
/**
169
 * \brief Union a data type with the one found for a value
170
 *
171
 * @param eDT the first data type
172
 * @param dfValue the value for which to find a data type and union with eDT
173
 * @param bComplex if the value is complex
174
 *
175
 * @return a data type able to express eDT and dfValue.
176
 * @since GDAL 2.3
177
 */
178
GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
360✔
179
                                                    double dfValue,
180
                                                    int bComplex)
181
{
182
    if (!bComplex && !GDALDataTypeIsComplex(eDT) && eDT != GDT_Unknown)
360✔
183
    {
184
        // Do not return `GDT_Float16` because that type is not supported everywhere
185
        const auto eDTMod = eDT == GDT_Float16 ? GDT_Float32 : eDT;
337✔
186
        if (GDALIsValueExactAs(dfValue, eDTMod))
337✔
187
        {
188
            return eDTMod;
315✔
189
        }
190
    }
191

192
    const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex);
45✔
193
    return GDALDataTypeUnion(eDT, eDT2);
45✔
194
}
195

196
/************************************************************************/
197
/*                        GetMinBitsForValue()                          */
198
/************************************************************************/
199
static int GetMinBitsForValue(double dValue)
45✔
200
{
201
    if (round(dValue) == dValue)
45✔
202
    {
203
        if (dValue <= cpl::NumericLimits<GByte>::max() &&
42✔
204
            dValue >= cpl::NumericLimits<GByte>::lowest())
17✔
205
            return 8;
12✔
206

207
        if (dValue <= cpl::NumericLimits<GInt8>::max() &&
18✔
208
            dValue >= cpl::NumericLimits<GInt8>::lowest())
5✔
209
            return 8;
3✔
210

211
        if (dValue <= cpl::NumericLimits<GInt16>::max() &&
14✔
212
            dValue >= cpl::NumericLimits<GInt16>::lowest())
4✔
213
            return 16;
3✔
214

215
        if (dValue <= cpl::NumericLimits<GUInt16>::max() &&
9✔
216
            dValue >= cpl::NumericLimits<GUInt16>::lowest())
2✔
217
            return 16;
1✔
218

219
        if (dValue <= cpl::NumericLimits<GInt32>::max() &&
8✔
220
            dValue >= cpl::NumericLimits<GInt32>::lowest())
2✔
221
            return 32;
2✔
222

223
        if (dValue <= cpl::NumericLimits<GUInt32>::max() &&
5✔
224
            dValue >= cpl::NumericLimits<GUInt32>::lowest())
1✔
225
            return 32;
1✔
226

227
        if (dValue <=
3✔
228
                static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) &&
5✔
229
            dValue >= static_cast<double>(
2✔
230
                          cpl::NumericLimits<std::uint64_t>::lowest()))
2✔
231
            return 64;
2✔
232
    }
233
    else if (static_cast<float>(dValue) == dValue)
20✔
234
    {
235
        return 32;
13✔
236
    }
237

238
    return 64;
8✔
239
}
240

241
/************************************************************************/
242
/*                        GDALFindDataType()                            */
243
/************************************************************************/
244

245
/**
246
 * \brief Finds the smallest data type able to support the given
247
 *  requirements
248
 *
249
 * @param nBits number of bits necessary
250
 * @param bSigned if negative values are necessary
251
 * @param bFloating if non-integer values necessary
252
 * @param bComplex if complex values are necessary
253
 *
254
 * @return a best fit GDALDataType for supporting the requirements
255
 * @since GDAL 2.3
256
 */
257
GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
104,900✔
258
                                          int bComplex)
259
{
260
    if (!bFloating)
104,900✔
261
    {
262
        if (!bComplex)
103,698✔
263
        {
264
            if (!bSigned)
103,202✔
265
            {
266
                if (nBits <= 8)
70,041✔
267
                    return GDT_Byte;
37,041✔
268
                if (nBits <= 16)
33,000✔
269
                    return GDT_UInt16;
32,898✔
270
                if (nBits <= 32)
102✔
271
                    return GDT_UInt32;
73✔
272
                if (nBits <= 64)
29✔
273
                    return GDT_UInt64;
29✔
274
                return GDT_Float64;
×
275
            }
276
            else  // bSigned
277
            {
278
                if (nBits <= 8)
33,161✔
279
                    return GDT_Int8;
14✔
280
                if (nBits <= 16)
33,147✔
281
                    return GDT_Int16;
32,946✔
282
                if (nBits <= 32)
201✔
283
                    return GDT_Int32;
130✔
284
                if (nBits <= 64)
71✔
285
                    return GDT_Int64;
53✔
286
                return GDT_Float64;
18✔
287
            }
288
        }
289
        else  // bComplex
290
        {
291
            if (!bSigned)
496✔
292
            {
293
                // We don't have complex unsigned data types, so
294
                // return a large-enough complex signed type
295

296
                // Do not choose CInt16 for backward compatibility
297
                // if (nBits <= 15)
298
                //     return GDT_CInt16;
299
                if (nBits <= 31)
3✔
300
                    return GDT_CInt32;
3✔
301
                return GDT_CFloat64;
×
302
            }
303
            else  // bSigned
304
            {
305
                if (nBits <= 16)
493✔
306
                    return GDT_CInt16;
367✔
307
                if (nBits <= 32)
126✔
308
                    return GDT_CInt32;
99✔
309
                return GDT_CFloat64;
27✔
310
            }
311
        }
312
    }
313
    else  // bFloating
314
    {
315
        if (!bComplex)
1,202✔
316
        {
317
            // Do not choose Float16 since is not supported everywhere
318
            // if (nBits <= 16)
319
            //     return GDT_Float16;
320
            if (nBits <= 32)
842✔
321
                return GDT_Float32;
438✔
322
            return GDT_Float64;
404✔
323
        }
324
        else  // bComplex
325
        {
326
            // Do not choose Float16 since is not supported everywhere
327
            // if (nBits <= 16)
328
            //     return GDT_CFloat16;
329
            if (nBits <= 32)
360✔
330
                return GDT_CFloat32;
160✔
331
            return GDT_CFloat64;
200✔
332
        }
333
    }
334
}
335

336
/************************************************************************/
337
/*                        GDALFindDataTypeForValue()                    */
338
/************************************************************************/
339

340
/**
341
 * \brief Finds the smallest data type able to support the provided value
342
 *
343
 * @param dValue value to support
344
 * @param bComplex is the value complex
345
 *
346
 * @return a best fit GDALDataType for supporting the value
347
 * @since GDAL 2.3
348
 */
349
GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
45✔
350
{
351
    const bool bFloating =
352
        round(dValue) != dValue ||
70✔
353
        dValue >
354
            static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) ||
69✔
355
        dValue <
356
            static_cast<double>(cpl::NumericLimits<std::int64_t>::lowest());
24✔
357
    const bool bSigned = bFloating || dValue < 0;
45✔
358
    const int nBits = GetMinBitsForValue(dValue);
45✔
359

360
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
45✔
361
}
362

363
/************************************************************************/
364
/*                        GDALGetDataTypeSizeBytes()                    */
365
/************************************************************************/
366

367
/**
368
 * \brief Get data type size in <b>bytes</b>.
369
 *
370
 * Returns the size of a GDT_* type in bytes.  In contrast,
371
 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
372
 *
373
 * @param eDataType type, such as GDT_Byte.
374
 * @return the number of bytes or zero if it is not recognised.
375
 */
376

377
int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
248,426,000✔
378

379
{
380
    switch (eDataType)
248,426,000✔
381
    {
382
        case GDT_Byte:
81,187,800✔
383
        case GDT_Int8:
384
            return 1;
81,187,800✔
385

386
        case GDT_UInt16:
52,297,300✔
387
        case GDT_Int16:
388
        case GDT_Float16:
389
            return 2;
52,297,300✔
390

391
        case GDT_UInt32:
77,443,400✔
392
        case GDT_Int32:
393
        case GDT_Float32:
394
        case GDT_CInt16:
395
        case GDT_CFloat16:
396
            return 4;
77,443,400✔
397

398
        case GDT_Float64:
36,911,100✔
399
        case GDT_CInt32:
400
        case GDT_CFloat32:
401
        case GDT_UInt64:
402
        case GDT_Int64:
403
            return 8;
36,911,100✔
404

405
        case GDT_CFloat64:
587,367✔
406
            return 16;
587,367✔
407

408
        case GDT_Unknown:
17,741✔
409
        case GDT_TypeCount:
410
            break;
17,741✔
411
    }
412
    return 0;
×
413
}
414

415
/************************************************************************/
416
/*                        GDALGetDataTypeSizeBits()                     */
417
/************************************************************************/
418

419
/**
420
 * \brief Get data type size in <b>bits</b>.
421
 *
422
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!  Use
423
 * GDALGetDataTypeSizeBytes() for bytes.
424
 *
425
 * @param eDataType type, such as GDT_Byte.
426
 * @return the number of bits or zero if it is not recognised.
427
 */
428

429
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
3,206,650✔
430

431
{
432
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
3,206,650✔
433
}
434

435
/************************************************************************/
436
/*                        GDALGetDataTypeSize()                         */
437
/************************************************************************/
438

439
/**
440
 * \brief Get data type size in bits.  <b>Deprecated</b>.
441
 *
442
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
443
 *
444
 * Use GDALGetDataTypeSizeBytes() for bytes.
445
 * Use GDALGetDataTypeSizeBits() for bits.
446
 *
447
 * @param eDataType type, such as GDT_Byte.
448
 * @return the number of bits or zero if it is not recognised.
449
 */
450

451
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
1,024✔
452

453
{
454
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
1,024✔
455
}
456

457
/************************************************************************/
458
/*                       GDALDataTypeIsComplex()                        */
459
/************************************************************************/
460

461
/**
462
 * \brief Is data type complex?
463
 *
464
 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
465
 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
466
 * component.
467
 */
468

469
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
1,225,390✔
470

471
{
472
    switch (eDataType)
1,225,390✔
473
    {
474
        case GDT_CInt16:
14,745✔
475
        case GDT_CInt32:
476
        case GDT_CFloat16:
477
        case GDT_CFloat32:
478
        case GDT_CFloat64:
479
            return TRUE;
14,745✔
480

481
        case GDT_Byte:
1,210,620✔
482
        case GDT_Int8:
483
        case GDT_Int16:
484
        case GDT_UInt16:
485
        case GDT_Int32:
486
        case GDT_UInt32:
487
        case GDT_Int64:
488
        case GDT_UInt64:
489
        case GDT_Float16:
490
        case GDT_Float32:
491
        case GDT_Float64:
492
            return FALSE;
1,210,620✔
493

494
        case GDT_Unknown:
23✔
495
        case GDT_TypeCount:
496
            break;
23✔
497
    }
498
    return FALSE;
27✔
499
}
500

501
/************************************************************************/
502
/*                       GDALDataTypeIsFloating()                       */
503
/************************************************************************/
504

505
/**
506
 * \brief Is data type floating? (might be complex)
507
 *
508
 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float16,
509
 * GDT_Float64, GDT_CFloat16, GDT_CFloat32, GDT_CFloat64)
510
 * @since GDAL 2.3
511
 */
512

513
int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
564,947✔
514
{
515
    switch (eDataType)
564,947✔
516
    {
517
        case GDT_Float16:
7,384✔
518
        case GDT_Float32:
519
        case GDT_Float64:
520
        case GDT_CFloat16:
521
        case GDT_CFloat32:
522
        case GDT_CFloat64:
523
            return TRUE;
7,384✔
524

525
        case GDT_Byte:
557,562✔
526
        case GDT_Int8:
527
        case GDT_Int16:
528
        case GDT_UInt16:
529
        case GDT_Int32:
530
        case GDT_UInt32:
531
        case GDT_Int64:
532
        case GDT_UInt64:
533
        case GDT_CInt16:
534
        case GDT_CInt32:
535
            return FALSE;
557,562✔
536

537
        case GDT_Unknown:
1✔
538
        case GDT_TypeCount:
539
            break;
1✔
540
    }
541
    return FALSE;
1✔
542
}
543

544
/************************************************************************/
545
/*                       GDALDataTypeIsInteger()                        */
546
/************************************************************************/
547

548
/**
549
 * \brief Is data type integer? (might be complex)
550
 *
551
 * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
552
 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
553
 * @since GDAL 2.3
554
 */
555

556
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
362,612✔
557

558
{
559
    switch (eDataType)
362,612✔
560
    {
561
        case GDT_Byte:
359,723✔
562
        case GDT_Int8:
563
        case GDT_Int16:
564
        case GDT_UInt16:
565
        case GDT_Int32:
566
        case GDT_UInt32:
567
        case GDT_CInt16:
568
        case GDT_CInt32:
569
        case GDT_UInt64:
570
        case GDT_Int64:
571
            return TRUE;
359,723✔
572

573
        case GDT_Float16:
2,886✔
574
        case GDT_Float32:
575
        case GDT_Float64:
576
        case GDT_CFloat16:
577
        case GDT_CFloat32:
578
        case GDT_CFloat64:
579
            return FALSE;
2,886✔
580

581
        case GDT_Unknown:
1✔
582
        case GDT_TypeCount:
583
            break;
1✔
584
    }
585
    return FALSE;
3✔
586
}
587

588
/************************************************************************/
589
/*                       GDALDataTypeIsSigned()                         */
590
/************************************************************************/
591

592
/**
593
 * \brief Is data type signed?
594
 *
595
 * @return TRUE if the passed type is signed.
596
 * @since GDAL 2.3
597
 */
598

599
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
908,731✔
600
{
601
    switch (eDataType)
908,731✔
602
    {
603
        case GDT_Byte:
772,157✔
604
        case GDT_UInt16:
605
        case GDT_UInt32:
606
        case GDT_UInt64:
607
            return FALSE;
772,157✔
608

609
        case GDT_Int8:
136,574✔
610
        case GDT_Int16:
611
        case GDT_Int32:
612
        case GDT_Int64:
613
        case GDT_Float16:
614
        case GDT_Float32:
615
        case GDT_Float64:
616
        case GDT_CInt16:
617
        case GDT_CInt32:
618
        case GDT_CFloat16:
619
        case GDT_CFloat32:
620
        case GDT_CFloat64:
621
            return TRUE;
136,574✔
622

623
        case GDT_Unknown:
×
624
        case GDT_TypeCount:
625
            break;
×
626
    }
UNCOV
627
    return FALSE;
×
628
}
629

630
/************************************************************************/
631
/*                    GDALDataTypeIsConversionLossy()                   */
632
/************************************************************************/
633

634
/**
635
 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
636
 *
637
 * @param eTypeFrom input datatype
638
 * @param eTypeTo output datatype
639
 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
640
 * @since GDAL 2.3
641
 */
642

643
int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
357,292✔
644
                                              GDALDataType eTypeTo)
645
{
646
    // E.g cfloat32 -> float32
647
    if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
357,292✔
648
        return TRUE;
526✔
649

650
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
356,766✔
651
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
356,766✔
652

653
    if (GDALDataTypeIsInteger(eTypeTo))
356,766✔
654
    {
655
        // E.g. float32 -> int32
656
        if (GDALDataTypeIsFloating(eTypeFrom))
354,369✔
657
            return TRUE;
5,173✔
658

659
        // E.g. Int16 to UInt16
660
        const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
349,196✔
661
        const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
349,196✔
662
        if (bIsFromSigned && !bIsToSigned)
349,196✔
663
            return TRUE;
194✔
664

665
        // E.g UInt32 to UInt16
666
        const int nFromSize = GDALGetDataTypeSizeBits(eTypeFrom);
349,002✔
667
        const int nToSize = GDALGetDataTypeSizeBits(eTypeTo);
349,002✔
668
        if (nFromSize > nToSize)
349,002✔
669
            return TRUE;
196✔
670

671
        // E.g UInt16 to Int16
672
        if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
348,806✔
673
            return TRUE;
37✔
674

675
        return FALSE;
348,769✔
676
    }
677

678
    if (eTypeTo == GDT_Float16 &&
2,397✔
679
        (eTypeFrom == GDT_Int16 || eTypeFrom == GDT_UInt16 ||
×
680
         eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
×
681
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
×
682
         eTypeFrom == GDT_Float32 || eTypeFrom == GDT_Float64))
×
683
    {
684
        return TRUE;
×
685
    }
686

687
    if (eTypeTo == GDT_Float32 &&
2,397✔
688
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
883✔
689
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
851✔
690
         eTypeFrom == GDT_Float64))
691
    {
692
        return TRUE;
108✔
693
    }
694

695
    if (eTypeTo == GDT_Float64 &&
2,289✔
696
        (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
1,469✔
697
    {
698
        return TRUE;
32✔
699
    }
700

701
    return FALSE;
2,257✔
702
}
703

704
/************************************************************************/
705
/*                        GDALGetDataTypeName()                         */
706
/************************************************************************/
707

708
/**
709
 * \brief Get name of data type.
710
 *
711
 * Returns a symbolic name for the data type.  This is essentially the
712
 * the enumerated item name with the GDT_ prefix removed.  So GDT_Byte returns
713
 * "Byte".  The returned strings are static strings and should not be modified
714
 * or freed by the application.  These strings are useful for reporting
715
 * datatypes in debug statements, errors and other user output.
716
 *
717
 * @param eDataType type to get name of.
718
 * @return string corresponding to existing data type
719
 *         or NULL pointer if invalid type given.
720
 */
721

722
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
102,167✔
723

724
{
725
    switch (eDataType)
102,167✔
726
    {
727
        case GDT_Unknown:
5,313✔
728
            return "Unknown";
5,313✔
729

730
        case GDT_Byte:
34,998✔
731
            return "Byte";
34,998✔
732

733
        case GDT_Int8:
1,019✔
734
            return "Int8";
1,019✔
735

736
        case GDT_UInt16:
10,924✔
737
            return "UInt16";
10,924✔
738

739
        case GDT_Int16:
10,408✔
740
            return "Int16";
10,408✔
741

742
        case GDT_UInt32:
8,455✔
743
            return "UInt32";
8,455✔
744

745
        case GDT_Int32:
7,879✔
746
            return "Int32";
7,879✔
747

748
        case GDT_UInt64:
1,280✔
749
            return "UInt64";
1,280✔
750

751
        case GDT_Int64:
1,156✔
752
            return "Int64";
1,156✔
753

754
        case GDT_Float16:
491✔
755
            return "Float16";
491✔
756

757
        case GDT_Float32:
7,344✔
758
            return "Float32";
7,344✔
759

760
        case GDT_Float64:
4,676✔
761
            return "Float64";
4,676✔
762

763
        case GDT_CInt16:
2,217✔
764
            return "CInt16";
2,217✔
765

766
        case GDT_CInt32:
2,038✔
767
            return "CInt32";
2,038✔
768

769
        case GDT_CFloat16:
353✔
770
            return "CFloat16";
353✔
771

772
        case GDT_CFloat32:
1,942✔
773
            return "CFloat32";
1,942✔
774

775
        case GDT_CFloat64:
1,674✔
776
            return "CFloat64";
1,674✔
777

778
        case GDT_TypeCount:
×
779
            break;
×
780
    }
781
    return nullptr;
×
782
}
783

784
/************************************************************************/
785
/*                        GDALGetDataTypeByName()                       */
786
/************************************************************************/
787

788
/**
789
 * \brief Get data type by symbolic name.
790
 *
791
 * Returns a data type corresponding to the given symbolic name. This
792
 * function is opposite to the GDALGetDataTypeName().
793
 *
794
 * @param pszName string containing the symbolic name of the type.
795
 *
796
 * @return GDAL data type.
797
 */
798

799
GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
8,242✔
800

801
{
802
    VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
8,242✔
803

804
    for (int iType = 1; iType < GDT_TypeCount; iType++)
32,886✔
805
    {
806
        const auto eType = static_cast<GDALDataType>(iType);
32,854✔
807
        if (GDALGetDataTypeName(eType) != nullptr &&
65,708✔
808
            EQUAL(GDALGetDataTypeName(eType), pszName))
32,854✔
809
        {
810
            return eType;
8,210✔
811
        }
812
    }
813

814
    return GDT_Unknown;
32✔
815
}
816

817
/************************************************************************/
818
/*                      GDALAdjustValueToDataType()                     */
819
/************************************************************************/
820

821
template <class T>
822
static inline void ClampAndRound(double &dfValue, bool &bClamped,
73✔
823
                                 bool &bRounded)
824
{
825
    if (dfValue < static_cast<double>(cpl::NumericLimits<T>::lowest()))
73✔
826
    {
827
        bClamped = true;
6✔
828
        dfValue = static_cast<double>(cpl::NumericLimits<T>::lowest());
6✔
829
    }
830
    else if (dfValue > static_cast<double>(cpl::NumericLimits<T>::max()))
67✔
831
    {
832
        bClamped = true;
4✔
833
        dfValue = static_cast<double>(cpl::NumericLimits<T>::max());
4✔
834
    }
835
    else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
63✔
836
    {
837
        bRounded = true;
8✔
838
        dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
8✔
839
    }
840
}
73✔
841

842
/**
843
 * \brief Adjust a value to the output data type
844
 *
845
 * Adjustment consist in clamping to minimum/maximum values of the data type
846
 * and rounding for integral types.
847
 *
848
 * @param eDT target data type.
849
 * @param dfValue value to adjust.
850
 * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
851
 * been made, or NULL
852
 * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
853
 * been made, or NULL
854
 *
855
 * @return adjusted value
856
 * @since GDAL 2.1
857
 */
858

859
double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
117✔
860
                                 int *pbClamped, int *pbRounded)
861
{
862
    bool bClamped = false;
117✔
863
    bool bRounded = false;
117✔
864
    switch (eDT)
117✔
865
    {
866
        case GDT_Byte:
30✔
867
            ClampAndRound<GByte>(dfValue, bClamped, bRounded);
30✔
868
            break;
30✔
869
        case GDT_Int8:
8✔
870
            ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
8✔
871
            break;
8✔
872
        case GDT_Int16:
11✔
873
            ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
11✔
874
            break;
11✔
875
        case GDT_UInt16:
6✔
876
            ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
6✔
877
            break;
6✔
878
        case GDT_Int32:
4✔
879
            ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
4✔
880
            break;
4✔
881
        case GDT_UInt32:
5✔
882
            ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
5✔
883
            break;
5✔
884
        case GDT_Int64:
4✔
885
            ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
4✔
886
            break;
4✔
887
        case GDT_UInt64:
5✔
888
            ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
5✔
889
            break;
5✔
890
        case GDT_Float16:
8✔
891
        {
892
            if (!std::isfinite(dfValue))
8✔
893
                break;
3✔
894

895
            // TODO: Use ClampAndRound
896
            if (dfValue < cpl::NumericLimits<GFloat16>::lowest())
5✔
897
            {
898
                bClamped = TRUE;
1✔
899
                dfValue =
1✔
900
                    static_cast<double>(cpl::NumericLimits<GFloat16>::lowest());
1✔
901
            }
902
            else if (dfValue > cpl::NumericLimits<GFloat16>::max())
4✔
903
            {
904
                bClamped = TRUE;
1✔
905
                dfValue =
1✔
906
                    static_cast<double>(cpl::NumericLimits<GFloat16>::max());
1✔
907
            }
908
            else
909
            {
910
                // Intentionally lose precision.
911
                // TODO(schwehr): Is the double cast really necessary?
912
                // If so, why?  What will fail?
913
                dfValue = static_cast<double>(static_cast<GFloat16>(dfValue));
3✔
914
            }
915
            break;
5✔
916
        }
917
        case GDT_Float32:
27✔
918
        {
919
            if (!std::isfinite(dfValue))
27✔
920
                break;
4✔
921

922
            // TODO: Use ClampAndRound
923
            if (dfValue < cpl::NumericLimits<float>::lowest())
23✔
924
            {
925
                bClamped = TRUE;
1✔
926
                dfValue =
1✔
927
                    static_cast<double>(cpl::NumericLimits<float>::lowest());
1✔
928
            }
929
            else if (dfValue > cpl::NumericLimits<float>::max())
22✔
930
            {
931
                bClamped = TRUE;
1✔
932
                dfValue = static_cast<double>(cpl::NumericLimits<float>::max());
1✔
933
            }
934
            else
935
            {
936
                // Intentionally lose precision.
937
                // TODO(schwehr): Is the double cast really necessary?
938
                // If so, why?  What will fail?
939
                dfValue = static_cast<double>(static_cast<float>(dfValue));
21✔
940
            }
941
            break;
23✔
942
        }
943
        case GDT_Float64:
9✔
944
        case GDT_CInt16:
945
        case GDT_CInt32:
946
        case GDT_CFloat16:
947
        case GDT_CFloat32:
948
        case GDT_CFloat64:
949
        case GDT_Unknown:
950
        case GDT_TypeCount:
951
            break;
9✔
952
    }
953
    if (pbClamped)
117✔
954
        *pbClamped = bClamped;
116✔
955
    if (pbRounded)
117✔
956
        *pbRounded = bRounded;
116✔
957
    return dfValue;
117✔
958
}
959

960
/************************************************************************/
961
/*                         GDALIsValueExactAs()                         */
962
/************************************************************************/
963

964
/**
965
 * \brief Check whether the provided value can be exactly represented in a
966
 * data type.
967
 *
968
 * Only implemented for non-complex data types
969
 *
970
 * @param dfValue value to check.
971
 * @param eDT target data type.
972
 *
973
 * @return true if the provided value can be exactly represented in the
974
 * data type.
975
 * @since GDAL 3.10
976
 */
977
bool GDALIsValueExactAs(double dfValue, GDALDataType eDT)
2,820✔
978
{
979
    switch (eDT)
2,820✔
980
    {
981
        case GDT_Byte:
385✔
982
            return GDALIsValueExactAs<uint8_t>(dfValue);
385✔
983
        case GDT_Int8:
7✔
984
            return GDALIsValueExactAs<int8_t>(dfValue);
7✔
985
        case GDT_UInt16:
71✔
986
            return GDALIsValueExactAs<uint16_t>(dfValue);
71✔
987
        case GDT_Int16:
94✔
988
            return GDALIsValueExactAs<int16_t>(dfValue);
94✔
989
        case GDT_UInt32:
11✔
990
            return GDALIsValueExactAs<uint32_t>(dfValue);
11✔
991
        case GDT_Int32:
38✔
992
            return GDALIsValueExactAs<int32_t>(dfValue);
38✔
993
        case GDT_UInt64:
9✔
994
            return GDALIsValueExactAs<uint64_t>(dfValue);
9✔
995
        case GDT_Int64:
9✔
996
            return GDALIsValueExactAs<int64_t>(dfValue);
9✔
997
        case GDT_Float16:
×
998
            return GDALIsValueExactAs<GFloat16>(dfValue);
×
999
        case GDT_Float32:
126✔
1000
            return GDALIsValueExactAs<float>(dfValue);
126✔
1001
        case GDT_Float64:
1,048✔
1002
            return true;
1,048✔
1003
        case GDT_Unknown:
1,022✔
1004
        case GDT_CInt16:
1005
        case GDT_CInt32:
1006
        case GDT_CFloat16:
1007
        case GDT_CFloat32:
1008
        case GDT_CFloat64:
1009
        case GDT_TypeCount:
1010
            break;
1,022✔
1011
    }
1012
    return true;
1,022✔
1013
}
1014

1015
/************************************************************************/
1016
/*                         GDALIsValueInRangeOf()                       */
1017
/************************************************************************/
1018

1019
/**
1020
 * \brief Check whether the provided value can be represented in the range
1021
 * of the data type, possibly with rounding.
1022
 *
1023
 * Only implemented for non-complex data types
1024
 *
1025
 * @param dfValue value to check.
1026
 * @param eDT target data type.
1027
 *
1028
 * @return true if the provided value can be represented in the range
1029
 * of the data type, possibly with rounding.
1030
 * @since GDAL 3.11
1031
 */
1032
bool GDALIsValueInRangeOf(double dfValue, GDALDataType eDT)
18✔
1033
{
1034
    switch (eDT)
18✔
1035
    {
1036
        case GDT_Byte:
2✔
1037
            return GDALIsValueInRange<uint8_t>(dfValue);
2✔
1038
        case GDT_Int8:
1✔
1039
            return GDALIsValueInRange<int8_t>(dfValue);
1✔
1040
        case GDT_UInt16:
1✔
1041
            return GDALIsValueInRange<uint16_t>(dfValue);
1✔
1042
        case GDT_Int16:
1✔
1043
            return GDALIsValueInRange<int16_t>(dfValue);
1✔
1044
        case GDT_UInt32:
1✔
1045
            return GDALIsValueInRange<uint32_t>(dfValue);
1✔
1046
        case GDT_Int32:
1✔
1047
            return GDALIsValueInRange<int32_t>(dfValue);
1✔
1048
        case GDT_UInt64:
1✔
1049
            return GDALIsValueInRange<uint64_t>(dfValue);
1✔
1050
        case GDT_Int64:
1✔
1051
            return GDALIsValueInRange<int64_t>(dfValue);
1✔
1052
        case GDT_Float16:
1✔
1053
            return GDALIsValueInRange<GFloat16>(dfValue);
1✔
1054
        case GDT_Float32:
1✔
1055
            return GDALIsValueInRange<float>(dfValue);
1✔
1056
        case GDT_Float64:
1✔
1057
            return true;
1✔
1058
        case GDT_Unknown:
6✔
1059
        case GDT_CInt16:
1060
        case GDT_CInt32:
1061
        case GDT_CFloat16:
1062
        case GDT_CFloat32:
1063
        case GDT_CFloat64:
1064
        case GDT_TypeCount:
1065
            break;
6✔
1066
    }
1067
    return true;
6✔
1068
}
1069

1070
/************************************************************************/
1071
/*                        GDALGetNonComplexDataType()                   */
1072
/************************************************************************/
1073
/**
1074
 * \brief Return the base data type for the specified input.
1075
 *
1076
 * If the input data type is complex this function returns the base type
1077
 * i.e. the data type of the real and imaginary parts (non-complex).
1078
 * If the input data type is already non-complex, then it is returned
1079
 * unchanged.
1080
 *
1081
 * @param eDataType type, such as GDT_CFloat32.
1082
 *
1083
 * @return GDAL data type.
1084
 */
1085
GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
713,637✔
1086
{
1087
    switch (eDataType)
713,637✔
1088
    {
1089
        case GDT_CInt16:
124✔
1090
            return GDT_Int16;
124✔
1091
        case GDT_CInt32:
51✔
1092
            return GDT_Int32;
51✔
1093
        case GDT_CFloat16:
14✔
1094
            return GDT_Float16;
14✔
1095
        case GDT_CFloat32:
97✔
1096
            return GDT_Float32;
97✔
1097
        case GDT_CFloat64:
89✔
1098
            return GDT_Float64;
89✔
1099

1100
        case GDT_Byte:
713,262✔
1101
        case GDT_UInt16:
1102
        case GDT_UInt32:
1103
        case GDT_UInt64:
1104
        case GDT_Int8:
1105
        case GDT_Int16:
1106
        case GDT_Int32:
1107
        case GDT_Int64:
1108
        case GDT_Float16:
1109
        case GDT_Float32:
1110
        case GDT_Float64:
1111
            break;
713,262✔
1112

1113
        case GDT_Unknown:
×
1114
        case GDT_TypeCount:
1115
            break;
×
1116
    }
1117
    return eDataType;
713,262✔
1118
}
1119

1120
/************************************************************************/
1121
/*                        GDALGetAsyncStatusTypeByName()                */
1122
/************************************************************************/
1123
/**
1124
 * Get AsyncStatusType by symbolic name.
1125
 *
1126
 * Returns a data type corresponding to the given symbolic name. This
1127
 * function is opposite to the GDALGetAsyncStatusTypeName().
1128
 *
1129
 * @param pszName string containing the symbolic name of the type.
1130
 *
1131
 * @return GDAL AsyncStatus type.
1132
 */
1133
GDALAsyncStatusType CPL_DLL CPL_STDCALL
1134
GDALGetAsyncStatusTypeByName(const char *pszName)
×
1135
{
1136
    VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
×
1137

1138
    for (int iType = 0; iType < GARIO_TypeCount; iType++)
×
1139
    {
1140
        const auto eType = static_cast<GDALAsyncStatusType>(iType);
×
1141
        if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
×
1142
            EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
×
1143
        {
1144
            return eType;
×
1145
        }
1146
    }
1147

1148
    return GARIO_ERROR;
×
1149
}
1150

1151
/************************************************************************/
1152
/*                        GDALGetAsyncStatusTypeName()                 */
1153
/************************************************************************/
1154

1155
/**
1156
 * Get name of AsyncStatus data type.
1157
 *
1158
 * Returns a symbolic name for the AsyncStatus data type.  This is essentially
1159
 * the enumerated item name with the GARIO_ prefix removed.  So
1160
 * GARIO_COMPLETE returns "COMPLETE".  The returned strings are static strings
1161
 * and should not be modified or freed by the application.  These strings are
1162
 * useful for reporting datatypes in debug statements, errors and other user
1163
 * output.
1164
 *
1165
 * @param eAsyncStatusType type to get name of.
1166
 * @return string corresponding to type.
1167
 */
1168

1169
const char *CPL_STDCALL
1170
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
×
1171

1172
{
1173
    switch (eAsyncStatusType)
×
1174
    {
1175
        case GARIO_PENDING:
×
1176
            return "PENDING";
×
1177

1178
        case GARIO_UPDATE:
×
1179
            return "UPDATE";
×
1180

1181
        case GARIO_ERROR:
×
1182
            return "ERROR";
×
1183

1184
        case GARIO_COMPLETE:
×
1185
            return "COMPLETE";
×
1186

1187
        default:
×
1188
            return nullptr;
×
1189
    }
1190
}
1191

1192
/************************************************************************/
1193
/*                  GDALGetPaletteInterpretationName()                  */
1194
/************************************************************************/
1195

1196
/**
1197
 * \brief Get name of palette interpretation
1198
 *
1199
 * Returns a symbolic name for the palette interpretation.  This is the
1200
 * the enumerated item name with the GPI_ prefix removed.  So GPI_Gray returns
1201
 * "Gray".  The returned strings are static strings and should not be modified
1202
 * or freed by the application.
1203
 *
1204
 * @param eInterp palette interpretation to get name of.
1205
 * @return string corresponding to palette interpretation.
1206
 */
1207

1208
const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
10✔
1209

1210
{
1211
    switch (eInterp)
10✔
1212
    {
1213
        case GPI_Gray:
×
1214
            return "Gray";
×
1215

1216
        case GPI_RGB:
10✔
1217
            return "RGB";
10✔
1218

1219
        case GPI_CMYK:
×
1220
            return "CMYK";
×
1221

1222
        case GPI_HLS:
×
1223
            return "HLS";
×
1224

1225
        default:
×
1226
            return "Unknown";
×
1227
    }
1228
}
1229

1230
/************************************************************************/
1231
/*                   GDALGetColorInterpretationName()                   */
1232
/************************************************************************/
1233

1234
/**
1235
 * \brief Get name of color interpretation
1236
 *
1237
 * Returns a symbolic name for the color interpretation.  This is derived from
1238
 * the enumerated item name with the GCI_ prefix removed, but there are some
1239
 * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
1240
 * The returned strings are static strings and should not be modified
1241
 * or freed by the application.
1242
 *
1243
 * @param eInterp color interpretation to get name of.
1244
 * @return string corresponding to color interpretation
1245
 *         or NULL pointer if invalid enumerator given.
1246
 */
1247

1248
const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
10,338✔
1249

1250
{
1251
    static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1);
1252
    static_assert(GCI_NIRBand == GCI_IR_Start);
1253
    static_assert(GCI_SAR_Start == GCI_IR_End + 1);
1254
    static_assert(GCI_Max == GCI_SAR_End);
1255

1256
    switch (eInterp)
10,338✔
1257
    {
1258
        case GCI_Undefined:
2,174✔
1259
            break;
2,174✔
1260

1261
        case GCI_GrayIndex:
2,601✔
1262
            return "Gray";
2,601✔
1263

1264
        case GCI_PaletteIndex:
1,132✔
1265
            return "Palette";
1,132✔
1266

1267
        case GCI_RedBand:
1,259✔
1268
            return "Red";
1,259✔
1269

1270
        case GCI_GreenBand:
949✔
1271
            return "Green";
949✔
1272

1273
        case GCI_BlueBand:
657✔
1274
            return "Blue";
657✔
1275

1276
        case GCI_AlphaBand:
348✔
1277
            return "Alpha";
348✔
1278

1279
        case GCI_HueBand:
112✔
1280
            return "Hue";
112✔
1281

1282
        case GCI_SaturationBand:
111✔
1283
            return "Saturation";
111✔
1284

1285
        case GCI_LightnessBand:
110✔
1286
            return "Lightness";
110✔
1287

1288
        case GCI_CyanBand:
117✔
1289
            return "Cyan";
117✔
1290

1291
        case GCI_MagentaBand:
99✔
1292
            return "Magenta";
99✔
1293

1294
        case GCI_YellowBand:
81✔
1295
            return "Yellow";
81✔
1296

1297
        case GCI_BlackBand:
62✔
1298
            return "Black";
62✔
1299

1300
        case GCI_YCbCr_YBand:
38✔
1301
            return "YCbCr_Y";
38✔
1302

1303
        case GCI_YCbCr_CbBand:
35✔
1304
            return "YCbCr_Cb";
35✔
1305

1306
        case GCI_YCbCr_CrBand:
32✔
1307
            return "YCbCr_Cr";
32✔
1308

1309
        case GCI_PanBand:
30✔
1310
            return "Pan";
30✔
1311

1312
        case GCI_CoastalBand:
39✔
1313
            return "Coastal";
39✔
1314

1315
        case GCI_RedEdgeBand:
29✔
1316
            return "RedEdge";
29✔
1317

1318
        case GCI_NIRBand:
34✔
1319
            return "NIR";
34✔
1320

1321
        case GCI_SWIRBand:
26✔
1322
            return "SWIR";
26✔
1323

1324
        case GCI_MWIRBand:
23✔
1325
            return "MWIR";
23✔
1326

1327
        case GCI_LWIRBand:
22✔
1328
            return "LWIR";
22✔
1329

1330
        case GCI_TIRBand:
21✔
1331
            return "TIR";
21✔
1332

1333
        case GCI_OtherIRBand:
22✔
1334
            return "OtherIR";
22✔
1335

1336
        case GCI_IR_Reserved_1:
19✔
1337
            return "IR_Reserved_1";
19✔
1338

1339
        case GCI_IR_Reserved_2:
18✔
1340
            return "IR_Reserved_2";
18✔
1341

1342
        case GCI_IR_Reserved_3:
17✔
1343
            return "IR_Reserved_3";
17✔
1344

1345
        case GCI_IR_Reserved_4:
16✔
1346
            return "IR_Reserved_4";
16✔
1347

1348
        case GCI_SAR_Ka_Band:
15✔
1349
            return "SAR_Ka";
15✔
1350

1351
        case GCI_SAR_K_Band:
14✔
1352
            return "SAR_K";
14✔
1353

1354
        case GCI_SAR_Ku_Band:
13✔
1355
            return "SAR_Ku";
13✔
1356

1357
        case GCI_SAR_X_Band:
12✔
1358
            return "SAR_X";
12✔
1359

1360
        case GCI_SAR_C_Band:
11✔
1361
            return "SAR_C";
11✔
1362

1363
        case GCI_SAR_S_Band:
10✔
1364
            return "SAR_S";
10✔
1365

1366
        case GCI_SAR_L_Band:
9✔
1367
            return "SAR_L";
9✔
1368

1369
        case GCI_SAR_P_Band:
8✔
1370
            return "SAR_P";
8✔
1371

1372
        case GCI_SAR_Reserved_1:
7✔
1373
            return "SAR_Reserved_1";
7✔
1374

1375
        case GCI_SAR_Reserved_2:
6✔
1376
            return "SAR_Reserved_2";
6✔
1377
    }
1378
    return "Undefined";
2,174✔
1379
}
1380

1381
/************************************************************************/
1382
/*                GDALGetColorInterpretationByName()                    */
1383
/************************************************************************/
1384

1385
/**
1386
 * \brief Get color interpretation by symbolic name.
1387
 *
1388
 * Returns a color interpretation corresponding to the given symbolic name. This
1389
 * function is opposite to the GDALGetColorInterpretationName().
1390
 *
1391
 * @param pszName string containing the symbolic name of the color
1392
 * interpretation.
1393
 *
1394
 * @return GDAL color interpretation.
1395
 *
1396
 * @since GDAL 1.7.0
1397
 */
1398

1399
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
2,005✔
1400

1401
{
1402
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
2,005✔
1403
                      GCI_Undefined);
1404

1405
    for (int iType = 0; iType <= GCI_Max; iType++)
8,894✔
1406
    {
1407
        if (EQUAL(GDALGetColorInterpretationName(
8,890✔
1408
                      static_cast<GDALColorInterp>(iType)),
1409
                  pszName))
1410
        {
1411
            return static_cast<GDALColorInterp>(iType);
2,001✔
1412
        }
1413
    }
1414

1415
    // Accept British English spelling
1416
    if (EQUAL(pszName, "grey"))
4✔
1417
        return GCI_GrayIndex;
×
1418

1419
    return GCI_Undefined;
4✔
1420
}
1421

1422
/************************************************************************/
1423
/*                  GDALGetColorInterpFromSTACCommonName()              */
1424
/************************************************************************/
1425

1426
static const struct
1427
{
1428
    const char *pszName;
1429
    GDALColorInterp eInterp;
1430
} asSTACCommonNames[] = {
1431
    {"pan", GCI_PanBand},
1432
    {"coastal", GCI_CoastalBand},
1433
    {"blue", GCI_BlueBand},
1434
    {"green", GCI_GreenBand},
1435
    {"green05", GCI_GreenBand},  // no exact match
1436
    {"yellow", GCI_YellowBand},
1437
    {"red", GCI_RedBand},
1438
    {"rededge", GCI_RedEdgeBand},
1439
    {"rededge071", GCI_RedEdgeBand},  // no exact match
1440
    {"rededge075", GCI_RedEdgeBand},  // no exact match
1441
    {"rededge078", GCI_RedEdgeBand},  // no exact match
1442
    {"nir", GCI_NIRBand},
1443
    {"nir08", GCI_NIRBand},   // no exact match
1444
    {"nir09", GCI_NIRBand},   // no exact match
1445
    {"cirrus", GCI_NIRBand},  // no exact match
1446
    {nullptr,
1447
     GCI_SWIRBand},  // so that GDALGetSTACCommonNameFromColorInterp returns null on GCI_SWIRBand
1448
    {"swir16", GCI_SWIRBand},  // no exact match
1449
    {"swir22", GCI_SWIRBand},  // no exact match
1450
    {"lwir", GCI_LWIRBand},
1451
    {"lwir11", GCI_LWIRBand},  // no exact match
1452
    {"lwir12", GCI_LWIRBand},  // no exact match
1453
};
1454

1455
/** Get color interpreetation from STAC eo:common_name
1456
 *
1457
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1458
 *
1459
 * @since GDAL 3.10
1460
 */
1461
GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName)
22✔
1462
{
1463

1464
    for (const auto &sAssoc : asSTACCommonNames)
86✔
1465
    {
1466
        if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName))
86✔
1467
            return sAssoc.eInterp;
22✔
1468
    }
1469
    return GCI_Undefined;
×
1470
}
1471

1472
/************************************************************************/
1473
/*                  GDALGetSTACCommonNameFromColorInterp()              */
1474
/************************************************************************/
1475

1476
/** Get STAC eo:common_name from GDAL color interpretation
1477
 *
1478
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1479
 *
1480
 * @return nullptr if there is no match
1481
 *
1482
 * @since GDAL 3.10
1483
 */
1484
const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp)
105✔
1485
{
1486
    for (const auto &sAssoc : asSTACCommonNames)
1,925✔
1487
    {
1488
        if (eInterp == sAssoc.eInterp)
1,842✔
1489
            return sAssoc.pszName;
22✔
1490
    }
1491
    return nullptr;
83✔
1492
}
1493

1494
/************************************************************************/
1495
/*                     GDALGetRandomRasterSample()                      */
1496
/************************************************************************/
1497

1498
/** Undocumented
1499
 * @param hBand undocumented.
1500
 * @param nSamples undocumented.
1501
 * @param pafSampleBuf undocumented.
1502
 * @return undocumented
1503
 */
1504
int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
×
1505
                                          float *pafSampleBuf)
1506

1507
{
1508
    VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
×
1509

1510
    GDALRasterBand *poBand;
1511

1512
    poBand = GDALRasterBand::FromHandle(
×
1513
        GDALGetRasterSampleOverview(hBand, nSamples));
1514
    CPLAssert(nullptr != poBand);
×
1515

1516
    /* -------------------------------------------------------------------- */
1517
    /*      Figure out the ratio of blocks we will read to get an           */
1518
    /*      approximate value.                                              */
1519
    /* -------------------------------------------------------------------- */
1520
    int bGotNoDataValue = FALSE;
×
1521

1522
    double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
×
1523

1524
    int nBlockXSize = 0;
×
1525
    int nBlockYSize = 0;
×
1526
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
×
1527

1528
    const int nBlocksPerRow = DIV_ROUND_UP(poBand->GetXSize(), nBlockXSize);
×
1529
    const int nBlocksPerColumn = DIV_ROUND_UP(poBand->GetYSize(), nBlockYSize);
×
1530

1531
    const GIntBig nBlockPixels =
×
1532
        static_cast<GIntBig>(nBlockXSize) * nBlockYSize;
×
1533
    const GIntBig nBlockCount =
×
1534
        static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn;
×
1535

1536
    if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
×
1537
        nBlockCount == 0)
1538
    {
1539
        CPLError(CE_Failure, CPLE_AppDefined,
×
1540
                 "GDALGetRandomRasterSample(): returning because band"
1541
                 " appears degenerate.");
1542

1543
        return FALSE;
×
1544
    }
1545

1546
    int nSampleRate = static_cast<int>(
1547
        std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
×
1548

1549
    if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
×
1550
        nSampleRate--;
×
1551

1552
    while (nSampleRate > 1 &&
×
1553
           ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
×
1554
        nSampleRate--;
×
1555

1556
    int nBlockSampleRate = 1;
×
1557

1558
    if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
×
1559
        nBlockSampleRate = static_cast<int>(std::max<GIntBig>(
×
1560
            1,
×
1561
            nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1))));
×
1562

1563
    int nActualSamples = 0;
×
1564

1565
    for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount;
×
1566
         iSampleBlock += nSampleRate)
×
1567
    {
1568

1569
        const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow);
×
1570
        const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow);
×
1571

1572
        GDALRasterBlock *const poBlock =
1573
            poBand->GetLockedBlockRef(iXBlock, iYBlock);
×
1574
        if (poBlock == nullptr)
×
1575
            continue;
×
1576
        void *pDataRef = poBlock->GetDataRef();
×
1577

1578
        int iXValid = nBlockXSize;
×
1579
        if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
×
1580
            iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
×
1581

1582
        int iYValid = nBlockYSize;
×
1583
        if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
×
1584
            iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
×
1585

1586
        int iRemainder = 0;
×
1587

1588
        for (int iY = 0; iY < iYValid; iY++)
×
1589
        {
1590
            int iX = iRemainder;  // Used after for.
×
1591
            for (; iX < iXValid; iX += nBlockSampleRate)
×
1592
            {
1593
                double dfValue = 0.0;
×
1594
                const int iOffset = iX + iY * nBlockXSize;
×
1595

1596
                switch (poBlock->GetDataType())
×
1597
                {
1598
                    case GDT_Byte:
×
1599
                        dfValue =
×
1600
                            reinterpret_cast<const GByte *>(pDataRef)[iOffset];
×
1601
                        break;
×
1602
                    case GDT_Int8:
×
1603
                        dfValue =
×
1604
                            reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
×
1605
                        break;
×
1606
                    case GDT_UInt16:
×
1607
                        dfValue = reinterpret_cast<const GUInt16 *>(
×
1608
                            pDataRef)[iOffset];
×
1609
                        break;
×
1610
                    case GDT_Int16:
×
1611
                        dfValue =
×
1612
                            reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
×
1613
                        break;
×
1614
                    case GDT_UInt32:
×
1615
                        dfValue = reinterpret_cast<const GUInt32 *>(
×
1616
                            pDataRef)[iOffset];
×
1617
                        break;
×
1618
                    case GDT_Int32:
×
1619
                        dfValue =
×
1620
                            reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
×
1621
                        break;
×
1622
                    case GDT_UInt64:
×
1623
                        dfValue = static_cast<double>(
×
1624
                            reinterpret_cast<const std::uint64_t *>(
1625
                                pDataRef)[iOffset]);
×
1626
                        break;
×
1627
                    case GDT_Int64:
×
1628
                        dfValue = static_cast<double>(
×
1629
                            reinterpret_cast<const std::int64_t *>(
1630
                                pDataRef)[iOffset]);
×
1631
                        break;
×
1632
                    case GDT_Float16:
×
1633
                        dfValue = reinterpret_cast<const GFloat16 *>(
1634
                            pDataRef)[iOffset];
×
1635
                        break;
×
1636
                    case GDT_Float32:
×
1637
                        dfValue =
×
1638
                            reinterpret_cast<const float *>(pDataRef)[iOffset];
×
1639
                        break;
×
1640
                    case GDT_Float64:
×
1641
                        dfValue =
×
1642
                            reinterpret_cast<const double *>(pDataRef)[iOffset];
×
1643
                        break;
×
1644
                    case GDT_CInt16:
×
1645
                    {
1646
                        // TODO(schwehr): Clean up casts.
1647
                        const double dfReal = reinterpret_cast<const GInt16 *>(
×
1648
                            pDataRef)[iOffset * 2];
×
1649
                        const double dfImag = reinterpret_cast<const GInt16 *>(
×
1650
                            pDataRef)[iOffset * 2 + 1];
×
1651
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1652
                        break;
×
1653
                    }
1654
                    case GDT_CInt32:
×
1655
                    {
1656
                        const double dfReal = reinterpret_cast<const GInt32 *>(
×
1657
                            pDataRef)[iOffset * 2];
×
1658
                        const double dfImag = reinterpret_cast<const GInt32 *>(
×
1659
                            pDataRef)[iOffset * 2 + 1];
×
1660
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1661
                        break;
×
1662
                    }
1663
                    case GDT_CFloat16:
×
1664
                    {
1665
                        const double dfReal =
1666
                            reinterpret_cast<const GFloat16 *>(
1667
                                pDataRef)[iOffset * 2];
×
1668
                        const double dfImag =
1669
                            reinterpret_cast<const GFloat16 *>(
1670
                                pDataRef)[iOffset * 2 + 1];
×
1671
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1672
                        break;
×
1673
                    }
1674
                    case GDT_CFloat32:
×
1675
                    {
1676
                        const double dfReal = reinterpret_cast<const float *>(
×
1677
                            pDataRef)[iOffset * 2];
×
1678
                        const double dfImag = reinterpret_cast<const float *>(
×
1679
                            pDataRef)[iOffset * 2 + 1];
×
1680
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1681
                        break;
×
1682
                    }
1683
                    case GDT_CFloat64:
×
1684
                    {
1685
                        const double dfReal = reinterpret_cast<const double *>(
×
1686
                            pDataRef)[iOffset * 2];
×
1687
                        const double dfImag = reinterpret_cast<const double *>(
×
1688
                            pDataRef)[iOffset * 2 + 1];
×
1689
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1690
                        break;
×
1691
                    }
1692
                    case GDT_Unknown:
×
1693
                    case GDT_TypeCount:
1694
                        CPLAssert(false);
×
1695
                }
1696

1697
                if (bGotNoDataValue && dfValue == dfNoDataValue)
×
1698
                    continue;
×
1699

1700
                if (nActualSamples < nSamples)
×
1701
                    pafSampleBuf[nActualSamples++] =
×
1702
                        static_cast<float>(dfValue);
×
1703
            }
1704

1705
            iRemainder = iX - iXValid;
×
1706
        }
1707

1708
        poBlock->DropLock();
×
1709
    }
1710

1711
    return nActualSamples;
×
1712
}
1713

1714
/************************************************************************/
1715
/*                             gdal::GCP                                */
1716
/************************************************************************/
1717

1718
namespace gdal
1719
{
1720
/** Constructor. */
1721
GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
25,488✔
1722
         double dfX, double dfY, double dfZ)
25,488✔
1723
    : gcp{CPLStrdup(pszId ? pszId : ""),
25,488✔
1724
          CPLStrdup(pszInfo ? pszInfo : ""),
25,488✔
1725
          dfPixel,
1726
          dfLine,
1727
          dfX,
1728
          dfY,
1729
          dfZ}
25,488✔
1730
{
1731
    static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1732
}
25,488✔
1733

1734
/** Destructor. */
1735
GCP::~GCP()
313,680✔
1736
{
1737
    CPLFree(gcp.pszId);
156,840✔
1738
    CPLFree(gcp.pszInfo);
156,840✔
1739
}
156,840✔
1740

1741
/** Constructor from a C GDAL_GCP instance. */
1742
GCP::GCP(const GDAL_GCP &other)
106,860✔
1743
    : gcp{CPLStrdup(other.pszId),
106,860✔
1744
          CPLStrdup(other.pszInfo),
213,720✔
1745
          other.dfGCPPixel,
106,860✔
1746
          other.dfGCPLine,
106,860✔
1747
          other.dfGCPX,
106,860✔
1748
          other.dfGCPY,
106,860✔
1749
          other.dfGCPZ}
106,860✔
1750
{
1751
}
106,860✔
1752

1753
/** Copy constructor. */
1754
GCP::GCP(const GCP &other) : GCP(other.gcp)
37,980✔
1755
{
1756
}
37,980✔
1757

1758
/** Move constructor. */
1759
GCP::GCP(GCP &&other)
24,492✔
1760
    : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
24,492✔
1761
          other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
24,492✔
1762
          other.gcp.dfGCPZ}
24,492✔
1763
{
1764
    other.gcp.pszId = nullptr;
24,492✔
1765
    other.gcp.pszInfo = nullptr;
24,492✔
1766
}
24,492✔
1767

1768
/** Copy assignment operator. */
1769
GCP &GCP::operator=(const GCP &other)
1✔
1770
{
1771
    if (this != &other)
1✔
1772
    {
1773
        CPLFree(gcp.pszId);
1✔
1774
        CPLFree(gcp.pszInfo);
1✔
1775
        gcp = other.gcp;
1✔
1776
        gcp.pszId = CPLStrdup(other.gcp.pszId);
1✔
1777
        gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1✔
1778
    }
1779
    return *this;
1✔
1780
}
1781

1782
/** Move assignment operator. */
1783
GCP &GCP::operator=(GCP &&other)
1✔
1784
{
1785
    if (this != &other)
1✔
1786
    {
1787
        CPLFree(gcp.pszId);
1✔
1788
        CPLFree(gcp.pszInfo);
1✔
1789
        gcp = other.gcp;
1✔
1790
        other.gcp.pszId = nullptr;
1✔
1791
        other.gcp.pszInfo = nullptr;
1✔
1792
    }
1793
    return *this;
1✔
1794
}
1795

1796
/** Set the 'id' member of the GCP. */
1797
void GCP::SetId(const char *pszId)
24,455✔
1798
{
1799
    CPLFree(gcp.pszId);
24,455✔
1800
    gcp.pszId = CPLStrdup(pszId ? pszId : "");
24,455✔
1801
}
24,455✔
1802

1803
/** Set the 'info' member of the GCP. */
1804
void GCP::SetInfo(const char *pszInfo)
24,455✔
1805
{
1806
    CPLFree(gcp.pszInfo);
24,455✔
1807
    gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
24,455✔
1808
}
24,455✔
1809

1810
/** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1811
/*static */
1812
const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
712✔
1813
{
1814
    return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
712✔
1815
}
1816

1817
/** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1818
/*static*/
1819
std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
315✔
1820
{
1821
    return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
315✔
1822
}
1823

1824
} /* namespace gdal */
1825

1826
/************************************************************************/
1827
/*                            GDALInitGCPs()                            */
1828
/************************************************************************/
1829

1830
/** Initialize an array of GCPs.
1831
 *
1832
 * Numeric values are initialized to 0 and strings to the empty string ""
1833
 * allocated with CPLStrdup()
1834
 * An array initialized with GDALInitGCPs() must be de-initialized with
1835
 * GDALDeinitGCPs().
1836
 *
1837
 * @param nCount number of GCPs in psGCP
1838
 * @param psGCP array of GCPs of size nCount.
1839
 */
1840
void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1,216✔
1841

1842
{
1843
    if (nCount > 0)
1,216✔
1844
    {
1845
        VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
575✔
1846
    }
1847

1848
    for (int iGCP = 0; iGCP < nCount; iGCP++)
5,873✔
1849
    {
1850
        memset(psGCP, 0, sizeof(GDAL_GCP));
4,657✔
1851
        psGCP->pszId = CPLStrdup("");
4,657✔
1852
        psGCP->pszInfo = CPLStrdup("");
4,657✔
1853
        psGCP++;
4,657✔
1854
    }
1855
}
1856

1857
/************************************************************************/
1858
/*                           GDALDeinitGCPs()                           */
1859
/************************************************************************/
1860

1861
/** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1862
 *
1863
 * @param nCount number of GCPs in psGCP
1864
 * @param psGCP array of GCPs of size nCount.
1865
 */
1866
void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1,355✔
1867

1868
{
1869
    if (nCount > 0)
1,355✔
1870
    {
1871
        VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
443✔
1872
    }
1873

1874
    for (int iGCP = 0; iGCP < nCount; iGCP++)
6,395✔
1875
    {
1876
        CPLFree(psGCP->pszId);
5,040✔
1877
        CPLFree(psGCP->pszInfo);
5,040✔
1878
        psGCP++;
5,040✔
1879
    }
1880
}
1881

1882
/************************************************************************/
1883
/*                         GDALDuplicateGCPs()                          */
1884
/************************************************************************/
1885

1886
/** Duplicate an array of GCPs
1887
 *
1888
 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1889
 *
1890
 * @param nCount number of GCPs in psGCP
1891
 * @param pasGCPList array of GCPs of size nCount.
1892
 */
1893
GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
717✔
1894

1895
{
1896
    GDAL_GCP *pasReturn =
1897
        static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
717✔
1898
    GDALInitGCPs(nCount, pasReturn);
717✔
1899

1900
    for (int iGCP = 0; iGCP < nCount; iGCP++)
3,916✔
1901
    {
1902
        CPLFree(pasReturn[iGCP].pszId);
3,199✔
1903
        pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
3,199✔
1904

1905
        CPLFree(pasReturn[iGCP].pszInfo);
3,199✔
1906
        pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
3,199✔
1907

1908
        pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
3,199✔
1909
        pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
3,199✔
1910
        pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
3,199✔
1911
        pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
3,199✔
1912
        pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
3,199✔
1913
    }
1914

1915
    return pasReturn;
717✔
1916
}
1917

1918
/************************************************************************/
1919
/*                       GDALFindAssociatedFile()                       */
1920
/************************************************************************/
1921

1922
/**
1923
 * \brief Find file with alternate extension.
1924
 *
1925
 * Finds the file with the indicated extension, substituting it in place
1926
 * of the extension of the base filename.  Generally used to search for
1927
 * associated files like world files .RPB files, etc.  If necessary, the
1928
 * extension will be tried in both upper and lower case.  If a sibling file
1929
 * list is available it will be used instead of doing VSIStatExL() calls to
1930
 * probe the file system.
1931
 *
1932
 * Note that the result is a dynamic CPLString so this method should not
1933
 * be used in a situation where there could be cross heap issues.  It is
1934
 * generally imprudent for application built on GDAL to use this function
1935
 * unless they are sure they will always use the same runtime heap as GDAL.
1936
 *
1937
 * @param pszBaseFilename the filename relative to which to search.
1938
 * @param pszExt the target extension in either upper or lower case.
1939
 * @param papszSiblingFiles the list of files in the same directory as
1940
 * pszBaseFilename or NULL if they are not known.
1941
 * @param nFlags special options controlling search.  None defined yet, just
1942
 * pass 0.
1943
 *
1944
 * @return an empty string if the target is not found, otherwise the target
1945
 * file with similar path style as the pszBaseFilename.
1946
 */
1947

1948
/**/
1949
/**/
1950

1951
CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
39,307✔
1952
                                 const char *pszExt,
1953
                                 CSLConstList papszSiblingFiles,
1954
                                 CPL_UNUSED int nFlags)
1955

1956
{
1957
    CPLString osTarget = CPLResetExtensionSafe(pszBaseFilename, pszExt);
78,614✔
1958

1959
    if (papszSiblingFiles == nullptr ||
78,343✔
1960
        // cppcheck-suppress knownConditionTrueFalse
1961
        !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
39,036✔
1962
    {
1963
        VSIStatBufL sStatBuf;
1964

1965
        if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
271✔
1966
        {
1967
            CPLString osAltExt = pszExt;
256✔
1968

1969
            if (islower(static_cast<unsigned char>(pszExt[0])))
256✔
1970
                osAltExt = osAltExt.toupper();
×
1971
            else
1972
                osAltExt = osAltExt.tolower();
256✔
1973

1974
            osTarget = CPLResetExtensionSafe(pszBaseFilename, osAltExt);
256✔
1975

1976
            if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
256✔
1977
                return "";
254✔
1978
        }
1979
    }
1980
    else
1981
    {
1982
        const int iSibling =
1983
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
39,036✔
1984
        if (iSibling < 0)
39,036✔
1985
            return "";
38,985✔
1986

1987
        osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
51✔
1988
        osTarget += papszSiblingFiles[iSibling];
51✔
1989
    }
1990

1991
    return osTarget;
68✔
1992
}
1993

1994
/************************************************************************/
1995
/*                         GDALLoadOziMapFile()                         */
1996
/************************************************************************/
1997

1998
/** Helper function for translator implementer wanting support for OZI .map
1999
 *
2000
 * @param pszFilename filename of .tab file
2001
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2002
 * @param ppszWKT output pointer to a string that will be allocated with
2003
 * CPLMalloc().
2004
 * @param pnGCPCount output pointer to GCP count.
2005
 * @param ppasGCPs outputer pointer to an array of GCPs.
2006
 * @return TRUE in case of success, FALSE otherwise.
2007
 */
2008
int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
×
2009
                                   double *padfGeoTransform, char **ppszWKT,
2010
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
2011

2012
{
2013
    VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
×
2014
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
×
2015
    VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
×
2016
    VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
×
2017

2018
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
×
2019

2020
    if (!papszLines)
×
2021
        return FALSE;
×
2022

2023
    int nLines = CSLCount(papszLines);
×
2024

2025
    // Check the OziExplorer Map file signature
2026
    if (nLines < 5 ||
×
2027
        !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
×
2028
    {
2029
        CPLError(CE_Failure, CPLE_AppDefined,
×
2030
                 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
2031
                 "format.",
2032
                 pszFilename);
2033
        CSLDestroy(papszLines);
×
2034
        return FALSE;
×
2035
    }
2036

2037
    OGRSpatialReference oSRS;
×
2038
    OGRErr eErr = OGRERR_NONE;
×
2039

2040
    /* The Map Scale Factor has been introduced recently on the 6th line */
2041
    /* and is a trick that is used to just change that line without changing */
2042
    /* the rest of the MAP file but providing an imagery that is smaller or
2043
     * larger */
2044
    /* so we have to correct the pixel/line values read in the .MAP file so they
2045
     */
2046
    /* match the actual imagery dimension. Well, this is a bad summary of what
2047
     */
2048
    /* is explained at
2049
     * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
2050
    double dfMSF = 1;
×
2051

2052
    for (int iLine = 5; iLine < nLines; iLine++)
×
2053
    {
2054
        if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
×
2055
        {
2056
            dfMSF = CPLAtof(papszLines[iLine] + 4);
×
2057
            if (dfMSF <= 0.01) /* Suspicious values */
×
2058
            {
2059
                CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
×
2060
                dfMSF = 1;
×
2061
            }
2062
        }
2063
    }
2064

2065
    eErr = oSRS.importFromOzi(papszLines);
×
2066
    if (eErr == OGRERR_NONE)
×
2067
    {
2068
        if (ppszWKT != nullptr)
×
2069
            oSRS.exportToWkt(ppszWKT);
×
2070
    }
2071

2072
    int nCoordinateCount = 0;
×
2073
    // TODO(schwehr): Initialize asGCPs.
2074
    GDAL_GCP asGCPs[30];
2075

2076
    // Iterate all lines in the MAP-file
2077
    for (int iLine = 5; iLine < nLines; iLine++)
×
2078
    {
2079
        char **papszTok = CSLTokenizeString2(
×
2080
            papszLines[iLine], ",",
×
2081
            CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
2082

2083
        if (CSLCount(papszTok) < 12)
×
2084
        {
2085
            CSLDestroy(papszTok);
×
2086
            continue;
×
2087
        }
2088

2089
        if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
×
2090
            !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
×
2091
            nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2092
        {
2093
            bool bReadOk = false;
×
2094
            double dfLon = 0.0;
×
2095
            double dfLat = 0.0;
×
2096

2097
            if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
×
2098
                !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
×
2099
            {
2100
                // Set geographical coordinates of the pixels
2101
                dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
×
2102
                dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
×
2103
                if (EQUAL(papszTok[11], "W"))
×
2104
                    dfLon = -dfLon;
×
2105
                if (EQUAL(papszTok[8], "S"))
×
2106
                    dfLat = -dfLat;
×
2107

2108
                // Transform from the geographical coordinates into projected
2109
                // coordinates.
2110
                if (eErr == OGRERR_NONE)
×
2111
                {
2112
                    OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
×
2113

2114
                    if (poLongLat)
×
2115
                    {
2116
                        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
2117
                        poLongLat->SetAxisMappingStrategy(
×
2118
                            OAMS_TRADITIONAL_GIS_ORDER);
2119

2120
                        OGRCoordinateTransformation *poTransform =
2121
                            OGRCreateCoordinateTransformation(poLongLat, &oSRS);
×
2122
                        if (poTransform)
×
2123
                        {
2124
                            bReadOk = CPL_TO_BOOL(
×
2125
                                poTransform->Transform(1, &dfLon, &dfLat));
2126
                            delete poTransform;
×
2127
                        }
2128
                        delete poLongLat;
×
2129
                    }
2130
                }
×
2131
            }
2132
            else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
×
2133
            {
2134
                // Set cartesian coordinates of the pixels.
2135
                dfLon = CPLAtofM(papszTok[14]);
×
2136
                dfLat = CPLAtofM(papszTok[15]);
×
2137
                bReadOk = true;
×
2138

2139
                // if ( EQUAL(papszTok[16], "S") )
2140
                //     dfLat = -dfLat;
2141
            }
2142

2143
            if (bReadOk)
×
2144
            {
2145
                GDALInitGCPs(1, asGCPs + nCoordinateCount);
×
2146

2147
                // Set pixel/line part
2148
                asGCPs[nCoordinateCount].dfGCPPixel =
×
2149
                    CPLAtofM(papszTok[2]) / dfMSF;
×
2150
                asGCPs[nCoordinateCount].dfGCPLine =
×
2151
                    CPLAtofM(papszTok[3]) / dfMSF;
×
2152

2153
                asGCPs[nCoordinateCount].dfGCPX = dfLon;
×
2154
                asGCPs[nCoordinateCount].dfGCPY = dfLat;
×
2155

2156
                nCoordinateCount++;
×
2157
            }
2158
        }
2159

2160
        CSLDestroy(papszTok);
×
2161
    }
2162

2163
    CSLDestroy(papszLines);
×
2164

2165
    if (nCoordinateCount == 0)
×
2166
    {
2167
        CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
×
2168
                 pszFilename);
2169
        return FALSE;
×
2170
    }
2171

2172
    /* -------------------------------------------------------------------- */
2173
    /*      Try to convert the GCPs into a geotransform definition, if      */
2174
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2175
    /* -------------------------------------------------------------------- */
2176
    if (!GDALGCPsToGeoTransform(
×
2177
            nCoordinateCount, asGCPs, padfGeoTransform,
2178
            CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
×
2179
    {
2180
        if (pnGCPCount && ppasGCPs)
×
2181
        {
2182
            CPLDebug(
×
2183
                "GDAL",
2184
                "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
2185
                "first order geotransform.  Using points as GCPs.",
2186
                pszFilename);
2187

2188
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2189
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2190
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2191
            *pnGCPCount = nCoordinateCount;
×
2192
        }
2193
    }
2194
    else
2195
    {
2196
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
×
2197
    }
2198

2199
    return TRUE;
×
2200
}
2201

2202
/************************************************************************/
2203
/*                       GDALReadOziMapFile()                           */
2204
/************************************************************************/
2205

2206
/** Helper function for translator implementer wanting support for OZI .map
2207
 *
2208
 * @param pszBaseFilename filename whose basename will help building the .map
2209
 * filename.
2210
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2211
 * @param ppszWKT output pointer to a string that will be allocated with
2212
 * CPLMalloc().
2213
 * @param pnGCPCount output pointer to GCP count.
2214
 * @param ppasGCPs outputer pointer to an array of GCPs.
2215
 * @return TRUE in case of success, FALSE otherwise.
2216
 */
2217
int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
×
2218
                                   double *padfGeoTransform, char **ppszWKT,
2219
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
2220

2221
{
2222
    /* -------------------------------------------------------------------- */
2223
    /*      Try lower case, then upper case.                                */
2224
    /* -------------------------------------------------------------------- */
2225
    std::string osOzi = CPLResetExtensionSafe(pszBaseFilename, "map");
×
2226

2227
    VSILFILE *fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
×
2228

2229
    if (fpOzi == nullptr && VSIIsCaseSensitiveFS(osOzi.c_str()))
×
2230
    {
2231
        osOzi = CPLResetExtensionSafe(pszBaseFilename, "MAP");
×
2232
        fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
×
2233
    }
2234

2235
    if (fpOzi == nullptr)
×
2236
        return FALSE;
×
2237

2238
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
×
2239

2240
    /* -------------------------------------------------------------------- */
2241
    /*      We found the file, now load and parse it.                       */
2242
    /* -------------------------------------------------------------------- */
2243
    return GDALLoadOziMapFile(osOzi.c_str(), padfGeoTransform, ppszWKT,
×
2244
                              pnGCPCount, ppasGCPs);
×
2245
}
2246

2247
/************************************************************************/
2248
/*                         GDALLoadTabFile()                            */
2249
/*                                                                      */
2250
/************************************************************************/
2251

2252
/** Helper function for translator implementer wanting support for MapInfo
2253
 * .tab files.
2254
 *
2255
 * @param pszFilename filename of .tab
2256
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2257
 * @param ppszWKT output pointer to a string that will be allocated with
2258
 * CPLMalloc().
2259
 * @param pnGCPCount output pointer to GCP count.
2260
 * @param ppasGCPs outputer pointer to an array of GCPs.
2261
 * @return TRUE in case of success, FALSE otherwise.
2262
 */
2263
int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
14✔
2264
                                double *padfGeoTransform, char **ppszWKT,
2265
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2266

2267
{
2268
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
14✔
2269

2270
    if (!papszLines)
14✔
2271
        return FALSE;
×
2272

2273
    char **papszTok = nullptr;
14✔
2274
    bool bTypeRasterFound = false;
14✔
2275
    bool bInsideTableDef = false;
14✔
2276
    int nCoordinateCount = 0;
14✔
2277
    GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
2278
    const int numLines = CSLCount(papszLines);
14✔
2279

2280
    // Iterate all lines in the TAB-file
2281
    for (int iLine = 0; iLine < numLines; iLine++)
196✔
2282
    {
2283
        CSLDestroy(papszTok);
182✔
2284
        papszTok =
2285
            CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
182✔
2286

2287
        if (CSLCount(papszTok) < 2)
182✔
2288
            continue;
28✔
2289

2290
        // Did we find table definition
2291
        if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
154✔
2292
        {
2293
            bInsideTableDef = TRUE;
14✔
2294
        }
2295
        else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
140✔
2296
        {
2297
            // Only RASTER-type will be handled
2298
            if (EQUAL(papszTok[1], "RASTER"))
14✔
2299
            {
2300
                bTypeRasterFound = true;
14✔
2301
            }
2302
            else
2303
            {
2304
                CSLDestroy(papszTok);
×
2305
                CSLDestroy(papszLines);
×
2306
                return FALSE;
×
2307
            }
2308
        }
2309
        else if (bTypeRasterFound && bInsideTableDef &&
84✔
2310
                 CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
210✔
2311
                 nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2312
        {
2313
            GDALInitGCPs(1, asGCPs + nCoordinateCount);
56✔
2314

2315
            asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
56✔
2316
            asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
56✔
2317
            asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
56✔
2318
            asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
56✔
2319
            if (papszTok[5] != nullptr)
56✔
2320
            {
2321
                CPLFree(asGCPs[nCoordinateCount].pszId);
56✔
2322
                asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
56✔
2323
            }
2324

2325
            nCoordinateCount++;
56✔
2326
        }
2327
        else if (bTypeRasterFound && bInsideTableDef &&
70✔
2328
                 EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
28✔
2329
        {
2330
            OGRSpatialReference oSRS;
28✔
2331

2332
            if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
14✔
2333
                oSRS.exportToWkt(ppszWKT);
28✔
2334
        }
2335
        else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
70✔
2336
                 EQUAL(papszTok[1], "degree"))
14✔
2337
        {
2338
            /*
2339
            ** If we have units of "degree", but a projected coordinate
2340
            ** system we need to convert it to geographic.  See to01_02.TAB.
2341
            */
2342
            if (ppszWKT != nullptr && *ppszWKT != nullptr &&
×
2343
                STARTS_WITH_CI(*ppszWKT, "PROJCS"))
×
2344
            {
2345
                OGRSpatialReference oSRS;
×
2346
                oSRS.importFromWkt(*ppszWKT);
×
2347

2348
                OGRSpatialReference oSRSGeogCS;
×
2349
                oSRSGeogCS.CopyGeogCSFrom(&oSRS);
×
2350
                CPLFree(*ppszWKT);
×
2351

2352
                oSRSGeogCS.exportToWkt(ppszWKT);
×
2353
            }
2354
        }
2355
    }
2356

2357
    CSLDestroy(papszTok);
14✔
2358
    CSLDestroy(papszLines);
14✔
2359

2360
    if (nCoordinateCount == 0)
14✔
2361
    {
2362
        CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
×
2363
                 pszFilename);
2364
        return FALSE;
×
2365
    }
2366

2367
    /* -------------------------------------------------------------------- */
2368
    /*      Try to convert the GCPs into a geotransform definition, if      */
2369
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2370
    /* -------------------------------------------------------------------- */
2371
    if (!GDALGCPsToGeoTransform(
14✔
2372
            nCoordinateCount, asGCPs, padfGeoTransform,
2373
            CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
14✔
2374
    {
2375
        if (pnGCPCount && ppasGCPs)
×
2376
        {
2377
            CPLDebug("GDAL",
×
2378
                     "GDALLoadTabFile(%s) found file, was not able to derive a "
2379
                     "first order geotransform.  Using points as GCPs.",
2380
                     pszFilename);
2381

2382
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2383
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2384
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2385
            *pnGCPCount = nCoordinateCount;
×
2386
        }
2387
    }
2388
    else
2389
    {
2390
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
14✔
2391
    }
2392

2393
    return TRUE;
14✔
2394
}
2395

2396
/************************************************************************/
2397
/*                         GDALReadTabFile()                            */
2398
/************************************************************************/
2399

2400
/** Helper function for translator implementer wanting support for MapInfo
2401
 * .tab files.
2402
 *
2403
 * @param pszBaseFilename filename whose basename will help building the .tab
2404
 * filename.
2405
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2406
 * @param ppszWKT output pointer to a string that will be allocated with
2407
 * CPLMalloc().
2408
 * @param pnGCPCount output pointer to GCP count.
2409
 * @param ppasGCPs outputer pointer to an array of GCPs.
2410
 * @return TRUE in case of success, FALSE otherwise.
2411
 */
2412
int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
×
2413
                                double *padfGeoTransform, char **ppszWKT,
2414
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2415

2416
{
2417
    return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
×
2418
                            pnGCPCount, ppasGCPs, nullptr, nullptr);
×
2419
}
2420

2421
int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
5,979✔
2422
                     char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2423
                     CSLConstList papszSiblingFiles, char **ppszTabFileNameOut)
2424
{
2425
    if (ppszTabFileNameOut)
5,979✔
2426
        *ppszTabFileNameOut = nullptr;
5,979✔
2427

2428
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
5,979✔
2429
        return FALSE;
×
2430

2431
    std::string osTAB = CPLResetExtensionSafe(pszBaseFilename, "tab");
11,958✔
2432

2433
    if (papszSiblingFiles &&
11,909✔
2434
        // cppcheck-suppress knownConditionTrueFalse
2435
        GDALCanReliablyUseSiblingFileList(osTAB.c_str()))
5,930✔
2436
    {
2437
        int iSibling =
2438
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTAB.c_str()));
5,930✔
2439
        if (iSibling >= 0)
5,930✔
2440
        {
2441
            CPLString osTabFilename = pszBaseFilename;
14✔
2442
            osTabFilename.resize(strlen(pszBaseFilename) -
28✔
2443
                                 strlen(CPLGetFilename(pszBaseFilename)));
14✔
2444
            osTabFilename += papszSiblingFiles[iSibling];
14✔
2445
            if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
14✔
2446
                                pnGCPCount, ppasGCPs))
14✔
2447
            {
2448
                if (ppszTabFileNameOut)
14✔
2449
                    *ppszTabFileNameOut = CPLStrdup(osTabFilename);
14✔
2450
                return TRUE;
14✔
2451
            }
2452
        }
2453
        return FALSE;
5,916✔
2454
    }
2455

2456
    /* -------------------------------------------------------------------- */
2457
    /*      Try lower case, then upper case.                                */
2458
    /* -------------------------------------------------------------------- */
2459

2460
    VSILFILE *fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
49✔
2461

2462
    if (fpTAB == nullptr && VSIIsCaseSensitiveFS(osTAB.c_str()))
49✔
2463
    {
2464
        osTAB = CPLResetExtensionSafe(pszBaseFilename, "TAB");
49✔
2465
        fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
49✔
2466
    }
2467

2468
    if (fpTAB == nullptr)
49✔
2469
        return FALSE;
49✔
2470

2471
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
×
2472

2473
    /* -------------------------------------------------------------------- */
2474
    /*      We found the file, now load and parse it.                       */
2475
    /* -------------------------------------------------------------------- */
2476
    if (GDALLoadTabFile(osTAB.c_str(), padfGeoTransform, ppszWKT, pnGCPCount,
×
2477
                        ppasGCPs))
×
2478
    {
2479
        if (ppszTabFileNameOut)
×
2480
            *ppszTabFileNameOut = CPLStrdup(osTAB.c_str());
×
2481
        return TRUE;
×
2482
    }
2483
    return FALSE;
×
2484
}
2485

2486
/************************************************************************/
2487
/*                         GDALLoadWorldFile()                          */
2488
/************************************************************************/
2489

2490
/**
2491
 * \brief Read ESRI world file.
2492
 *
2493
 * This function reads an ESRI style world file, and formats a geotransform
2494
 * from its contents.
2495
 *
2496
 * The world file contains an affine transformation with the parameters
2497
 * in a different order than in a geotransform array.
2498
 *
2499
 * <ul>
2500
 * <li> geotransform[1] : width of pixel
2501
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2502
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2503
 * <li> geotransform[5] : height of pixel (but negative)
2504
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2505
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2506
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2507
 * pixel.
2508
 * </ul>
2509
 *
2510
 * @param pszFilename the world file name.
2511
 * @param padfGeoTransform the six double array into which the
2512
 * geotransformation should be placed.
2513
 *
2514
 * @return TRUE on success or FALSE on failure.
2515
 */
2516

2517
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
67✔
2518
                                  double *padfGeoTransform)
2519

2520
{
2521
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
67✔
2522
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
67✔
2523

2524
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
67✔
2525

2526
    if (!papszLines)
67✔
2527
        return FALSE;
×
2528

2529
    double world[6] = {0.0};
67✔
2530
    // reads the first 6 non-empty lines
2531
    int nLines = 0;
67✔
2532
    const int nLinesCount = CSLCount(papszLines);
67✔
2533
    for (int i = 0;
469✔
2534
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
469✔
2535
         ++i)
2536
    {
2537
        CPLString line(papszLines[i]);
402✔
2538
        if (line.Trim().empty())
402✔
2539
            continue;
×
2540

2541
        world[nLines] = CPLAtofM(line);
402✔
2542
        ++nLines;
402✔
2543
    }
2544

2545
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
67✔
2546
        (world[3] != 0.0 || world[1] != 0.0))
67✔
2547
    {
2548
        padfGeoTransform[0] = world[4];
67✔
2549
        padfGeoTransform[1] = world[0];
67✔
2550
        padfGeoTransform[2] = world[2];
67✔
2551
        padfGeoTransform[3] = world[5];
67✔
2552
        padfGeoTransform[4] = world[1];
67✔
2553
        padfGeoTransform[5] = world[3];
67✔
2554

2555
        // correct for center of pixel vs. top left of pixel
2556
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
67✔
2557
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
67✔
2558
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
67✔
2559
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
67✔
2560

2561
        CSLDestroy(papszLines);
67✔
2562

2563
        return TRUE;
67✔
2564
    }
2565
    else
2566
    {
2567
        CPLDebug("GDAL",
×
2568
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2569
                 pszFilename);
2570
        CSLDestroy(papszLines);
×
2571
        return FALSE;
×
2572
    }
2573
}
2574

2575
/************************************************************************/
2576
/*                         GDALReadWorldFile()                          */
2577
/************************************************************************/
2578

2579
/**
2580
 * \brief Read ESRI world file.
2581
 *
2582
 * This function reads an ESRI style world file, and formats a geotransform
2583
 * from its contents.  It does the same as GDALLoadWorldFile() function, but
2584
 * it will form the filename for the worldfile from the filename of the raster
2585
 * file referred and the suggested extension.  If no extension is provided,
2586
 * the code will internally try the unix style and windows style world file
2587
 * extensions (eg. for .tif these would be .tfw and .tifw).
2588
 *
2589
 * The world file contains an affine transformation with the parameters
2590
 * in a different order than in a geotransform array.
2591
 *
2592
 * <ul>
2593
 * <li> geotransform[1] : width of pixel
2594
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2595
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2596
 * <li> geotransform[5] : height of pixel (but negative)
2597
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2598
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2599
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2600
 * pixel.
2601
 * </ul>
2602
 *
2603
 * @param pszBaseFilename the target raster file.
2604
 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2605
 * from the pszBaseFilename
2606
 * @param padfGeoTransform the six double array into which the
2607
 * geotransformation should be placed.
2608
 *
2609
 * @return TRUE on success or FALSE on failure.
2610
 */
2611

2612
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
821✔
2613
                                  const char *pszExtension,
2614
                                  double *padfGeoTransform)
2615

2616
{
2617
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
821✔
2618
                              nullptr, nullptr);
821✔
2619
}
2620

2621
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
13,682✔
2622
                       GDALGeoTransform &gt, CSLConstList papszSiblingFiles,
2623
                       char **ppszWorldFileNameOut)
2624
{
2625
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, gt.data(),
13,682✔
2626
                              papszSiblingFiles, ppszWorldFileNameOut);
13,683✔
2627
}
2628

2629
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
28,638✔
2630
                       double *padfGeoTransform, CSLConstList papszSiblingFiles,
2631
                       char **ppszWorldFileNameOut)
2632
{
2633
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
28,638✔
2634
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
28,638✔
2635

2636
    if (ppszWorldFileNameOut)
28,638✔
2637
        *ppszWorldFileNameOut = nullptr;
26,814✔
2638

2639
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
28,638✔
2640
        return FALSE;
202✔
2641

2642
    /* -------------------------------------------------------------------- */
2643
    /*      If we aren't given an extension, try both the unix and          */
2644
    /*      windows style extensions.                                       */
2645
    /* -------------------------------------------------------------------- */
2646
    if (pszExtension == nullptr)
28,438✔
2647
    {
2648
        const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename);
13,737✔
2649

2650
        if (oBaseExt.length() < 2)
6,868✔
2651
            return FALSE;
149✔
2652

2653
        // windows version - first + last + 'w'
2654
        char szDerivedExtension[100] = {'\0'};
6,720✔
2655
        szDerivedExtension[0] = oBaseExt[0];
6,720✔
2656
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
6,720✔
2657
        szDerivedExtension[2] = 'w';
6,719✔
2658
        szDerivedExtension[3] = '\0';
6,719✔
2659

2660
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
6,719✔
2661
                               padfGeoTransform, papszSiblingFiles,
2662
                               ppszWorldFileNameOut))
6,720✔
2663
            return TRUE;
52✔
2664

2665
        // unix version - extension + 'w'
2666
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
6,668✔
2667
            return FALSE;
×
2668

2669
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
6,668✔
2670
                 oBaseExt.c_str());
2671
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
6,668✔
2672
                                  padfGeoTransform, papszSiblingFiles,
2673
                                  ppszWorldFileNameOut);
6,668✔
2674
    }
2675

2676
    /* -------------------------------------------------------------------- */
2677
    /*      Skip the leading period in the extension if there is one.       */
2678
    /* -------------------------------------------------------------------- */
2679
    if (*pszExtension == '.')
21,570✔
2680
        pszExtension++;
1,145✔
2681

2682
    /* -------------------------------------------------------------------- */
2683
    /*      Generate upper and lower case versions of the extension.        */
2684
    /* -------------------------------------------------------------------- */
2685
    char szExtUpper[32] = {'\0'};
21,570✔
2686
    char szExtLower[32] = {'\0'};
21,570✔
2687
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
21,570✔
2688
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
21,569✔
2689

2690
    for (int i = 0; szExtUpper[i] != '\0'; i++)
93,461✔
2691
    {
2692
        szExtUpper[i] = static_cast<char>(
71,895✔
2693
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
71,892✔
2694
        szExtLower[i] = static_cast<char>(
71,892✔
2695
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
71,895✔
2696
    }
2697

2698
    std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
43,138✔
2699

2700
    if (papszSiblingFiles &&
42,032✔
2701
        // cppcheck-suppress knownConditionTrueFalse
2702
        GDALCanReliablyUseSiblingFileList(osTFW.c_str()))
20,465✔
2703
    {
2704
        const int iSibling =
2705
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str()));
20,463✔
2706
        if (iSibling >= 0)
20,465✔
2707
        {
2708
            CPLString osTFWFilename = pszBaseFilename;
65✔
2709
            osTFWFilename.resize(strlen(pszBaseFilename) -
130✔
2710
                                 strlen(CPLGetFilename(pszBaseFilename)));
65✔
2711
            osTFWFilename += papszSiblingFiles[iSibling];
65✔
2712
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
65✔
2713
            {
2714
                if (ppszWorldFileNameOut)
65✔
2715
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
62✔
2716
                return TRUE;
65✔
2717
            }
2718
        }
2719
        return FALSE;
20,400✔
2720
    }
2721

2722
    /* -------------------------------------------------------------------- */
2723
    /*      Try lower case, then upper case.                                */
2724
    /* -------------------------------------------------------------------- */
2725

2726
    VSIStatBufL sStatBuf;
2727
    bool bGotTFW =
2728
        VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,104✔
2729

2730
    if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str()))
1,104✔
2731
    {
2732
        osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper);
1,102✔
2733
        bGotTFW =
1,102✔
2734
            VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,102✔
2735
    }
2736

2737
    if (!bGotTFW)
1,104✔
2738
        return FALSE;
1,102✔
2739

2740
    /* -------------------------------------------------------------------- */
2741
    /*      We found the file, now load and parse it.                       */
2742
    /* -------------------------------------------------------------------- */
2743
    if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform))
2✔
2744
    {
2745
        if (ppszWorldFileNameOut)
2✔
2746
            *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str());
1✔
2747
        return TRUE;
2✔
2748
    }
2749
    return FALSE;
×
2750
}
2751

2752
/************************************************************************/
2753
/*                         GDALWriteWorldFile()                         */
2754
/*                                                                      */
2755
/*      Helper function for translator implementer wanting              */
2756
/*      support for ESRI world files.                                   */
2757
/************************************************************************/
2758

2759
/**
2760
 * \brief Write ESRI world file.
2761
 *
2762
 * This function writes an ESRI style world file from the passed geotransform.
2763
 *
2764
 * The world file contains an affine transformation with the parameters
2765
 * in a different order than in a geotransform array.
2766
 *
2767
 * <ul>
2768
 * <li> geotransform[1] : width of pixel
2769
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2770
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2771
 * <li> geotransform[5] : height of pixel (but negative)
2772
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2773
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2774
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2775
 * pixel.
2776
 * </ul>
2777
 *
2778
 * @param pszBaseFilename the target raster file.
2779
 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2780
 * @param padfGeoTransform the six double array from which the
2781
 * geotransformation should be read.
2782
 *
2783
 * @return TRUE on success or FALSE on failure.
2784
 */
2785

2786
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
13✔
2787
                                   const char *pszExtension,
2788
                                   double *padfGeoTransform)
2789

2790
{
2791
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
13✔
2792
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
13✔
2793
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
13✔
2794

2795
    /* -------------------------------------------------------------------- */
2796
    /*      Prepare the text to write to the file.                          */
2797
    /* -------------------------------------------------------------------- */
2798
    CPLString osTFWText;
26✔
2799

2800
    osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2801
                     padfGeoTransform[1], padfGeoTransform[4],
13✔
2802
                     padfGeoTransform[2], padfGeoTransform[5],
13✔
2803
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
13✔
2804
                         0.5 * padfGeoTransform[2],
13✔
2805
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
13✔
2806
                         0.5 * padfGeoTransform[5]);
13✔
2807

2808
    /* -------------------------------------------------------------------- */
2809
    /*      Update extension, and write to disk.                            */
2810
    /* -------------------------------------------------------------------- */
2811
    const std::string osTFW =
2812
        CPLResetExtensionSafe(pszBaseFilename, pszExtension);
26✔
2813
    VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt");
13✔
2814
    if (fpTFW == nullptr)
13✔
2815
        return FALSE;
×
2816

2817
    const int bRet =
2818
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
13✔
2819
    if (VSIFCloseL(fpTFW) != 0)
13✔
2820
        return FALSE;
×
2821

2822
    return bRet;
13✔
2823
}
2824

2825
/************************************************************************/
2826
/*                          GDALVersionInfo()                           */
2827
/************************************************************************/
2828

2829
/**
2830
 * \brief Get runtime version information.
2831
 *
2832
 * Available pszRequest values:
2833
 * <ul>
2834
 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
2835
 * "30603000", e.g for GDAL 3.6.3.0</li>
2836
 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2837
 * string. i.e. "20230312".</li>
2838
 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2839
 * <li> "RELEASE_NICKNAME": (>= 3.11) Returns the GDAL_RELEASE_NICKNAME.
2840
 * (may be empty)</li>
2841
 * <li> "\--version": Returns one line version message suitable for
2842
 * use in response to \--version requests.  i.e. "GDAL 3.6.3, released
2843
 * 2023/03/12"</li>
2844
 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2845
 * the GDAL_DATA directory.
2846
 * </li>
2847
 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2848
 * with information on build time options.</li>
2849
 * </ul>
2850
 *
2851
 * @param pszRequest the type of version info desired, as listed above.
2852
 *
2853
 * @return an internal string containing the requested information.
2854
 */
2855

2856
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
4,876✔
2857

2858
{
2859
    /* -------------------------------------------------------------------- */
2860
    /*      Try to capture as much build information as practical.          */
2861
    /* -------------------------------------------------------------------- */
2862
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
4,876✔
2863
    {
2864
        CPLString osBuildInfo;
1,450✔
2865

2866
#define STRINGIFY_HELPER(x) #x
2867
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2868

2869
#ifdef ESRI_BUILD
2870
        osBuildInfo += "ESRI_BUILD=YES\n";
2871
#endif
2872
#ifdef PAM_ENABLED
2873
        osBuildInfo += "PAM_ENABLED=YES\n";
2874
#endif
2875
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
725✔
2876
#ifdef HAVE_CURL
2877
        osBuildInfo += "CURL_ENABLED=YES\n";
725✔
2878
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
725✔
2879
#endif
2880
#ifdef HAVE_GEOS
2881
        osBuildInfo += "GEOS_ENABLED=YES\n";
725✔
2882
#ifdef GEOS_CAPI_VERSION
2883
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
725✔
2884
#endif
2885
#endif
2886
        osBuildInfo +=
2887
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2888
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
725✔
2889
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
725✔
2890
        osBuildInfo += proj_info().version;
725✔
2891
        osBuildInfo += '\n';
725✔
2892

2893
#ifdef __VERSION__
2894
#ifdef __clang_version__
2895
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2896
#elif defined(__GNUC__)
2897
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
725✔
2898
#elif defined(__INTEL_COMPILER)
2899
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2900
#else
2901
        // STRINGIFY() as we're not sure if its a int or a string
2902
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2903
#endif
2904
#elif defined(_MSC_FULL_VER)
2905
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2906
#elif defined(__INTEL_COMPILER)
2907
        osBuildInfo +=
2908
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2909
#endif
2910
#ifdef CMAKE_UNITY_BUILD
2911
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2912
#endif
2913
#ifdef EMBED_RESOURCE_FILES
2914
        osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2915
#endif
2916
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2917
        osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2918
#endif
2919

2920
#undef STRINGIFY_HELPER
2921
#undef STRINGIFY
2922

2923
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
725✔
2924
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
725✔
2925
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
725✔
2926
    }
2927

2928
    /* -------------------------------------------------------------------- */
2929
    /*      LICENSE is a special case. We try to find and read the          */
2930
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2931
    /* -------------------------------------------------------------------- */
2932
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
4,151✔
2933
    {
2934
#if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2935
        return GDALGetEmbeddedLicense();
2936
#else
2937
        char *pszResultLicence =
2938
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
4✔
2939
        if (pszResultLicence != nullptr)
4✔
2940
        {
2941
            return pszResultLicence;
×
2942
        }
2943

2944
        VSILFILE *fp = nullptr;
4✔
2945
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
2946
#ifdef EMBED_RESOURCE_FILES
2947
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2948
#endif
2949
        const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
4✔
2950
        if (pszFilename != nullptr)
4✔
2951
            fp = VSIFOpenL(pszFilename, "r");
4✔
2952
        if (fp != nullptr)
4✔
2953
        {
2954
            if (VSIFSeekL(fp, 0, SEEK_END) == 0)
4✔
2955
            {
2956
                // TODO(schwehr): Handle if VSITellL returns a value too large
2957
                // for size_t.
2958
                const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
4✔
2959
                if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
4✔
2960
                {
2961
                    pszResultLicence =
2962
                        static_cast<char *>(VSICalloc(1, nLength));
4✔
2963
                    if (pszResultLicence)
4✔
2964
                        CPL_IGNORE_RET_VAL(
4✔
2965
                            VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
4✔
2966
                }
2967
            }
2968

2969
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4✔
2970
        }
2971
#endif
2972

2973
#ifdef EMBED_RESOURCE_FILES
2974
        if (!fp)
2975
        {
2976
            return GDALGetEmbeddedLicense();
2977
        }
2978
#endif
2979

2980
        if (!pszResultLicence)
4✔
2981
        {
2982
            pszResultLicence =
2983
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
×
2984
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
2985
                          "contain additional details.\n");
2986
        }
2987

2988
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
4✔
2989
        return pszResultLicence;
4✔
2990
#endif
2991
    }
2992

2993
    /* -------------------------------------------------------------------- */
2994
    /*      All other strings are fairly small.                             */
2995
    /* -------------------------------------------------------------------- */
2996
    CPLString osVersionInfo;
8,294✔
2997

2998
    if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
4,147✔
2999
        osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
2,571✔
3000
    else if (EQUAL(pszRequest, "RELEASE_DATE"))
1,576✔
3001
        osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
1✔
3002
    else if (EQUAL(pszRequest, "RELEASE_NAME"))
1,575✔
3003
        osVersionInfo.Printf(GDAL_RELEASE_NAME);
1,217✔
3004
    else if (EQUAL(pszRequest, "RELEASE_NICKNAME"))
358✔
3005
        osVersionInfo.Printf("%s", GDAL_RELEASE_NICKNAME);
×
3006
    else  // --version
3007
    {
3008
        osVersionInfo = "GDAL " GDAL_RELEASE_NAME;
358✔
3009
        if (GDAL_RELEASE_NICKNAME[0])
358✔
3010
        {
3011
            osVersionInfo += " \"" GDAL_RELEASE_NICKNAME "\"";
×
3012
        }
3013
        osVersionInfo += CPLString().Printf(
716✔
3014
            ", released %d/%02d/%02d", GDAL_RELEASE_DATE / 10000,
3015
            (GDAL_RELEASE_DATE % 10000) / 100, GDAL_RELEASE_DATE % 100);
358✔
3016
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
3017
        // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
3018
        // also true for CLang
3019
        osVersionInfo += " (debug build)";
358✔
3020
#elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
3021
        // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
3022
        // In release mode, the compiler generates an error if you specify
3023
        // _ITERATOR_DEBUG_LEVEL as 2.
3024
        osVersionInfo += " (debug build)";
3025
#endif
3026
    }
3027

3028
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
4,147✔
3029
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
4,147✔
3030
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
4,147✔
3031
}
3032

3033
/************************************************************************/
3034
/*                         GDALCheckVersion()                           */
3035
/************************************************************************/
3036

3037
/** Return TRUE if GDAL library version at runtime matches
3038
   nVersionMajor.nVersionMinor.
3039

3040
    The purpose of this method is to ensure that calling code will run
3041
    with the GDAL version it is compiled for. It is primarily intended
3042
    for external plugins.
3043

3044
    @param nVersionMajor Major version to be tested against
3045
    @param nVersionMinor Minor version to be tested against
3046
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
3047
   method will issue a failure mentioning the name of the calling component.
3048

3049
    @return TRUE if GDAL library version at runtime matches
3050
    nVersionMajor.nVersionMinor, FALSE otherwise.
3051
  */
3052
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
28,029✔
3053
                                 const char *pszCallingComponentName)
3054
{
3055
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
28,029✔
3056
        nVersionMinor == GDAL_VERSION_MINOR)
3057
        return TRUE;
28,029✔
3058

3059
    if (pszCallingComponentName)
×
3060
    {
3061
        CPLError(CE_Failure, CPLE_AppDefined,
×
3062
                 "%s was compiled against GDAL %d.%d, but "
3063
                 "the current library version is %d.%d",
3064
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
3065
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
3066
    }
3067
    return FALSE;
×
3068
}
3069

3070
/************************************************************************/
3071
/*                            GDALDecToDMS()                            */
3072
/************************************************************************/
3073

3074
/** Translate a decimal degrees value to a DMS string with hemisphere.
3075
 */
3076
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
550✔
3077
                                     int nPrecision)
3078

3079
{
3080
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
550✔
3081
}
3082

3083
/************************************************************************/
3084
/*                         GDALPackedDMSToDec()                         */
3085
/************************************************************************/
3086

3087
/**
3088
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
3089
 *
3090
 * See CPLPackedDMSToDec().
3091
 */
3092

3093
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
4✔
3094

3095
{
3096
    return CPLPackedDMSToDec(dfPacked);
4✔
3097
}
3098

3099
/************************************************************************/
3100
/*                         GDALDecToPackedDMS()                         */
3101
/************************************************************************/
3102

3103
/**
3104
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
3105
 *
3106
 * See CPLDecToPackedDMS().
3107
 */
3108

3109
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
4✔
3110

3111
{
3112
    return CPLDecToPackedDMS(dfDec);
4✔
3113
}
3114

3115
/************************************************************************/
3116
/*                       GDALGCPsToGeoTransform()                       */
3117
/************************************************************************/
3118

3119
/**
3120
 * \brief Generate Geotransform from GCPs.
3121
 *
3122
 * Given a set of GCPs perform first order fit as a geotransform.
3123
 *
3124
 * Due to imprecision in the calculations the fit algorithm will often
3125
 * return non-zero rotational coefficients even if given perfectly non-rotated
3126
 * inputs.  A special case has been implemented for corner corner coordinates
3127
 * given in TL, TR, BR, BL order.  So when using this to get a geotransform
3128
 * from 4 corner coordinates, pass them in this order.
3129
 *
3130
 * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
3131
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
3132
 * set to YES, then bApproxOK will be overridden with TRUE.
3133
 * Starting with GDAL 2.2.2, when exact fit is asked, the
3134
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
3135
 * give the maximum error threshold in pixel. The default is 0.25.
3136
 *
3137
 * @param nGCPCount the number of GCPs being passed in.
3138
 * @param pasGCPs the list of GCP structures.
3139
 * @param padfGeoTransform the six double array in which the affine
3140
 * geotransformation will be returned.
3141
 * @param bApproxOK If FALSE the function will fail if the geotransform is not
3142
 * essentially an exact fit (within 0.25 pixel) for all GCPs.
3143
 *
3144
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
3145
 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
3146
 * and the fit is poor.
3147
 */
3148

3149
// TODO(schwehr): Add consts to args.
3150
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
605✔
3151
                                       double *padfGeoTransform, int bApproxOK)
3152

3153
{
3154
    double dfPixelThreshold = 0.25;
605✔
3155
    if (!bApproxOK)
605✔
3156
    {
3157
        bApproxOK = CPLTestBool(
591✔
3158
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3159
        if (!bApproxOK)
591✔
3160
        {
3161
            dfPixelThreshold = std::clamp(
591✔
3162
                CPLAtof(CPLGetConfigOption(
591✔
3163
                    "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25")),
3164
                0.0, std::numeric_limits<double>::max());
1,182✔
3165
        }
3166
    }
3167

3168
    /* -------------------------------------------------------------------- */
3169
    /*      Recognise a few special cases.                                  */
3170
    /* -------------------------------------------------------------------- */
3171
    if (nGCPCount < 2)
605✔
3172
        return FALSE;
16✔
3173

3174
    if (nGCPCount == 2)
589✔
3175
    {
3176
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
2✔
3177
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
2✔
3178
            return FALSE;
×
3179

3180
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
2✔
3181
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
2✔
3182
        padfGeoTransform[2] = 0.0;
2✔
3183

3184
        padfGeoTransform[4] = 0.0;
2✔
3185
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
2✔
3186
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
2✔
3187

3188
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
2✔
3189
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
2✔
3190
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
2✔
3191

3192
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
2✔
3193
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
2✔
3194
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
2✔
3195

3196
        return TRUE;
2✔
3197
    }
3198

3199
    /* -------------------------------------------------------------------- */
3200
    /*      Special case of 4 corner coordinates of a non-rotated           */
3201
    /*      image.  The points must be in TL-TR-BR-BL order for now.        */
3202
    /*      This case helps avoid some imprecision in the general           */
3203
    /*      calculations.                                                   */
3204
    /* -------------------------------------------------------------------- */
3205
    if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
587✔
3206
        pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
291✔
3207
        pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
291✔
3208
        pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
290✔
3209
        pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
290✔
3210
        pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
287✔
3211
        pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
287✔
3212
        pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
261✔
3213
        pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
259✔
3214
        pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
259✔
3215
        pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
259✔
3216
        pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
184✔
3217
    {
3218
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
184✔
3219
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
184✔
3220
        padfGeoTransform[2] = 0.0;
184✔
3221
        padfGeoTransform[4] = 0.0;
184✔
3222
        padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
184✔
3223
                              (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
184✔
3224

3225
        padfGeoTransform[0] =
184✔
3226
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
184✔
3227
        padfGeoTransform[3] =
184✔
3228
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
184✔
3229
        return TRUE;
184✔
3230
    }
3231

3232
    /* -------------------------------------------------------------------- */
3233
    /*      Compute source and destination ranges so we can normalize       */
3234
    /*      the values to make the least squares computation more stable.   */
3235
    /* -------------------------------------------------------------------- */
3236
    double min_pixel = pasGCPs[0].dfGCPPixel;
403✔
3237
    double max_pixel = pasGCPs[0].dfGCPPixel;
403✔
3238
    double min_line = pasGCPs[0].dfGCPLine;
403✔
3239
    double max_line = pasGCPs[0].dfGCPLine;
403✔
3240
    double min_geox = pasGCPs[0].dfGCPX;
403✔
3241
    double max_geox = pasGCPs[0].dfGCPX;
403✔
3242
    double min_geoy = pasGCPs[0].dfGCPY;
403✔
3243
    double max_geoy = pasGCPs[0].dfGCPY;
403✔
3244

3245
    for (int i = 1; i < nGCPCount; ++i)
1,710✔
3246
    {
3247
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
1,307✔
3248
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
1,307✔
3249
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
1,307✔
3250
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
1,307✔
3251
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
1,307✔
3252
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
1,307✔
3253
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
1,307✔
3254
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
1,307✔
3255
    }
3256

3257
    double EPS = 1.0e-12;
403✔
3258

3259
    if (std::abs(max_pixel - min_pixel) < EPS ||
803✔
3260
        std::abs(max_line - min_line) < EPS ||
800✔
3261
        std::abs(max_geox - min_geox) < EPS ||
1,203✔
3262
        std::abs(max_geoy - min_geoy) < EPS)
325✔
3263
    {
3264
        return FALSE;  // degenerate in at least one dimension.
78✔
3265
    }
3266

3267
    double pl_normalize[6], geo_normalize[6];
3268

3269
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
325✔
3270
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
325✔
3271
    pl_normalize[2] = 0.0;
325✔
3272
    pl_normalize[3] = -min_line / (max_line - min_line);
325✔
3273
    pl_normalize[4] = 0.0;
325✔
3274
    pl_normalize[5] = 1.0 / (max_line - min_line);
325✔
3275

3276
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
325✔
3277
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
325✔
3278
    geo_normalize[2] = 0.0;
325✔
3279
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
325✔
3280
    geo_normalize[4] = 0.0;
325✔
3281
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
325✔
3282

3283
    /* -------------------------------------------------------------------- */
3284
    /* In the general case, do a least squares error approximation by       */
3285
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
3286
    /* -------------------------------------------------------------------- */
3287

3288
    double sum_x = 0.0;
325✔
3289
    double sum_y = 0.0;
325✔
3290
    double sum_xy = 0.0;
325✔
3291
    double sum_xx = 0.0;
325✔
3292
    double sum_yy = 0.0;
325✔
3293
    double sum_Lon = 0.0;
325✔
3294
    double sum_Lonx = 0.0;
325✔
3295
    double sum_Lony = 0.0;
325✔
3296
    double sum_Lat = 0.0;
325✔
3297
    double sum_Latx = 0.0;
325✔
3298
    double sum_Laty = 0.0;
325✔
3299

3300
    for (int i = 0; i < nGCPCount; ++i)
1,723✔
3301
    {
3302
        double pixel, line, geox, geoy;
3303

3304
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
1,398✔
3305
                              pasGCPs[i].dfGCPLine, &pixel, &line);
1,398✔
3306
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
1,398✔
3307
                              pasGCPs[i].dfGCPY, &geox, &geoy);
1,398✔
3308

3309
        sum_x += pixel;
1,398✔
3310
        sum_y += line;
1,398✔
3311
        sum_xy += pixel * line;
1,398✔
3312
        sum_xx += pixel * pixel;
1,398✔
3313
        sum_yy += line * line;
1,398✔
3314
        sum_Lon += geox;
1,398✔
3315
        sum_Lonx += geox * pixel;
1,398✔
3316
        sum_Lony += geox * line;
1,398✔
3317
        sum_Lat += geoy;
1,398✔
3318
        sum_Latx += geoy * pixel;
1,398✔
3319
        sum_Laty += geoy * line;
1,398✔
3320
    }
3321

3322
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3323
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
325✔
3324
                           sum_x * sum_x * sum_yy;
325✔
3325

3326
    /* -------------------------------------------------------------------- */
3327
    /*      If the divisor is zero, there is no valid solution.             */
3328
    /* -------------------------------------------------------------------- */
3329
    if (divisor == 0.0)
325✔
3330
        return FALSE;
×
3331

3332
    /* -------------------------------------------------------------------- */
3333
    /*      Compute top/left origin.                                        */
3334
    /* -------------------------------------------------------------------- */
3335
    double gt_normalized[6] = {0.0};
325✔
3336
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3337
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3338
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
325✔
3339
                       divisor;
3340

3341
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3342
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3343
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
325✔
3344
                       divisor;
3345

3346
    /* -------------------------------------------------------------------- */
3347
    /*      Compute X related coefficients.                                 */
3348
    /* -------------------------------------------------------------------- */
3349
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3350
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
325✔
3351
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
325✔
3352
                       divisor;
3353

3354
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
325✔
3355
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
325✔
3356
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
325✔
3357
                       divisor;
3358

3359
    /* -------------------------------------------------------------------- */
3360
    /*      Compute Y related coefficients.                                 */
3361
    /* -------------------------------------------------------------------- */
3362
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3363
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
325✔
3364
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
325✔
3365
                       divisor;
3366

3367
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
325✔
3368
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
325✔
3369
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
325✔
3370
                       divisor;
3371

3372
    /* -------------------------------------------------------------------- */
3373
    /*      Compose the resulting transformation with the normalization     */
3374
    /*      geotransformations.                                             */
3375
    /* -------------------------------------------------------------------- */
3376
    double gt1p2[6] = {0.0};
325✔
3377
    double inv_geo_normalize[6] = {0.0};
325✔
3378
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
325✔
3379
        return FALSE;
×
3380

3381
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
325✔
3382
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
325✔
3383

3384
    // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
3385
    if (std::abs(padfGeoTransform[1]) <= 1e-15 ||
649✔
3386
        std::abs(padfGeoTransform[5]) <= 1e-15)
324✔
3387
    {
3388
        return FALSE;
2✔
3389
    }
3390

3391
    /* -------------------------------------------------------------------- */
3392
    /*      Now check if any of the input points fit this poorly.           */
3393
    /* -------------------------------------------------------------------- */
3394
    if (!bApproxOK)
323✔
3395
    {
3396
        // FIXME? Not sure if it is the more accurate way of computing
3397
        // pixel size
3398
        double dfPixelSize =
3399
            0.5 *
3400
            (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
314✔
3401
             std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
314✔
3402
        if (dfPixelSize == 0.0)
314✔
3403
        {
3404
            CPLDebug("GDAL", "dfPixelSize = 0");
×
3405
            return FALSE;
×
3406
        }
3407

3408
        for (int i = 0; i < nGCPCount; i++)
1,643✔
3409
        {
3410
            const double dfErrorX =
1,335✔
3411
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
1,335✔
3412
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
1,335✔
3413
                 padfGeoTransform[0]) -
1,335✔
3414
                pasGCPs[i].dfGCPX;
1,335✔
3415
            const double dfErrorY =
1,335✔
3416
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
1,335✔
3417
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
1,335✔
3418
                 padfGeoTransform[3]) -
1,335✔
3419
                pasGCPs[i].dfGCPY;
1,335✔
3420

3421
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
2,667✔
3422
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
1,332✔
3423
            {
3424
                CPLDebug("GDAL",
6✔
3425
                         "dfErrorX/dfPixelSize = %.2f, "
3426
                         "dfErrorY/dfPixelSize = %.2f",
3427
                         std::abs(dfErrorX) / dfPixelSize,
6✔
3428
                         std::abs(dfErrorY) / dfPixelSize);
6✔
3429
                return FALSE;
6✔
3430
            }
3431
        }
3432
    }
3433

3434
    return TRUE;
317✔
3435
}
3436

3437
/************************************************************************/
3438
/*                      GDALComposeGeoTransforms()                      */
3439
/************************************************************************/
3440

3441
/**
3442
 * \brief Compose two geotransforms.
3443
 *
3444
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3445
 * being applied to a point.
3446
 *
3447
 * @param padfGT1 the first geotransform, six values.
3448
 * @param padfGT2 the second geotransform, six values.
3449
 * @param padfGTOut the output geotransform, six values, may safely be the same
3450
 * array as padfGT1 or padfGT2.
3451
 */
3452

3453
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
650✔
3454
                              double *padfGTOut)
3455

3456
{
3457
    double gtwrk[6] = {0.0};
650✔
3458
    // We need to think of the geotransform in a more normal form to do
3459
    // the matrix multiple:
3460
    //
3461
    //  __                     __
3462
    //  | gt[1]   gt[2]   gt[0] |
3463
    //  | gt[4]   gt[5]   gt[3] |
3464
    //  |  0.0     0.0     1.0  |
3465
    //  --                     --
3466
    //
3467
    // Then we can use normal matrix multiplication to produce the
3468
    // composed transformation.  I don't actually reform the matrix
3469
    // explicitly which is why the following may seem kind of spagettish.
3470

3471
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
650✔
3472
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
650✔
3473
    gtwrk[0] =
650✔
3474
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
650✔
3475

3476
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
650✔
3477
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
650✔
3478
    gtwrk[3] =
650✔
3479
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
650✔
3480
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
650✔
3481
}
650✔
3482

3483
/************************************************************************/
3484
/*                      StripIrrelevantOptions()                        */
3485
/************************************************************************/
3486

3487
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
10✔
3488
{
3489
    if (psCOL == nullptr)
10✔
3490
        return;
×
3491
    if (nOptions == 0)
10✔
3492
        nOptions = GDAL_OF_RASTER;
5✔
3493
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
10✔
3494
        return;
2✔
3495

3496
    CPLXMLNode *psPrev = nullptr;
8✔
3497
    for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
175✔
3498
    {
3499
        if (psIter->eType == CXT_Element)
167✔
3500
        {
3501
            CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
167✔
3502
            bool bStrip = false;
167✔
3503
            if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
167✔
3504
                psScope->psChild->pszValue &&
35✔
3505
                EQUAL(psScope->psChild->pszValue, "vector"))
35✔
3506
            {
3507
                bStrip = true;
1✔
3508
            }
3509
            else if (nOptions == GDAL_OF_VECTOR && psScope &&
166✔
3510
                     psScope->psChild && psScope->psChild->pszValue &&
35✔
3511
                     EQUAL(psScope->psChild->pszValue, "raster"))
35✔
3512
            {
3513
                bStrip = true;
33✔
3514
            }
3515
            if (psScope)
167✔
3516
            {
3517
                CPLRemoveXMLChild(psIter, psScope);
70✔
3518
                CPLDestroyXMLNode(psScope);
70✔
3519
            }
3520

3521
            CPLXMLNode *psNext = psIter->psNext;
167✔
3522
            if (bStrip)
167✔
3523
            {
3524
                if (psPrev)
34✔
3525
                    psPrev->psNext = psNext;
13✔
3526
                else if (psCOL->psChild == psIter)
21✔
3527
                    psCOL->psChild = psNext;
21✔
3528
                psIter->psNext = nullptr;
34✔
3529
                CPLDestroyXMLNode(psIter);
34✔
3530
                psIter = psNext;
34✔
3531
            }
3532
            else
3533
            {
3534
                psPrev = psIter;
133✔
3535
                psIter = psNext;
133✔
3536
            }
3537
        }
3538
        else
3539
        {
3540
            psIter = psIter->psNext;
×
3541
        }
3542
    }
3543
}
3544

3545
/************************************************************************/
3546
/*                         GDALPrintDriverList()                        */
3547
/************************************************************************/
3548

3549
/** Print on stdout the driver list */
3550
std::string GDALPrintDriverList(int nOptions, bool bJSON)
9✔
3551
{
3552
    if (nOptions == 0)
9✔
3553
        nOptions = GDAL_OF_RASTER;
2✔
3554

3555
    if (bJSON)
9✔
3556
    {
3557
        auto poDM = GetGDALDriverManager();
6✔
3558
        CPLJSONArray oArray;
12✔
3559
        const int nDriverCount = poDM->GetDriverCount();
6✔
3560
        for (int iDr = 0; iDr < nDriverCount; ++iDr)
1,330✔
3561
        {
3562
            auto poDriver = poDM->GetDriver(iDr);
1,324✔
3563
            CSLConstList papszMD = poDriver->GetMetadata();
1,324✔
3564

3565
            if (nOptions == GDAL_OF_RASTER &&
1,765✔
3566
                !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
441✔
3567
                continue;
612✔
3568
            if (nOptions == GDAL_OF_VECTOR &&
1,627✔
3569
                !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
441✔
3570
                continue;
265✔
3571
            if (nOptions == GDAL_OF_GNM &&
921✔
3572
                !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3573
                continue;
×
3574
            if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
1,142✔
3575
                !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
221✔
3576
                continue;
209✔
3577

3578
            CPLJSONObject oJDriver;
1,424✔
3579
            oJDriver.Set("short_name", poDriver->GetDescription());
712✔
3580
            if (const char *pszLongName =
712✔
3581
                    CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
712✔
3582
                oJDriver.Set("long_name", pszLongName);
712✔
3583
            CPLJSONArray oJScopes;
1,424✔
3584
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
712✔
3585
                oJScopes.Add("raster");
509✔
3586
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
712✔
3587
                oJScopes.Add("multidimensional_raster");
56✔
3588
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
712✔
3589
                oJScopes.Add("vector");
310✔
3590
            oJDriver.Add("scopes", oJScopes);
712✔
3591
            CPLJSONArray oJCaps;
1,424✔
3592
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
712✔
3593
                oJCaps.Add("open");
706✔
3594
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
712✔
3595
                oJCaps.Add("create");
290✔
3596
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
712✔
3597
                oJCaps.Add("create_copy");
190✔
3598
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
712✔
3599
                oJCaps.Add("update");
105✔
3600
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
712✔
3601
                oJCaps.Add("virtual_io");
583✔
3602
            oJDriver.Add("capabilities", oJCaps);
712✔
3603

3604
            if (const char *pszExtensions = CSLFetchNameValueDef(
712✔
3605
                    papszMD, GDAL_DMD_EXTENSIONS,
3606
                    CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3607
            {
3608
                const CPLStringList aosExt(
3609
                    CSLTokenizeString2(pszExtensions, " ", 0));
948✔
3610
                CPLJSONArray oJExts;
474✔
3611
                for (int i = 0; i < aosExt.size(); ++i)
1,117✔
3612
                {
3613
                    oJExts.Add(aosExt[i]);
643✔
3614
                }
3615
                oJDriver.Add("file_extensions", oJExts);
474✔
3616
            }
3617

3618
            oArray.Add(oJDriver);
712✔
3619
        }
3620

3621
        return oArray.Format(CPLJSONObject::PrettyFormat::Pretty);
6✔
3622
    }
3623

3624
    std::string ret;
6✔
3625
    ret = "Supported Formats: (ro:read-only, rw:read-write, "
3626
          "+:write from scratch, u:update, "
3627
          "v:virtual-I/O s:subdatasets)\n";
3✔
3628
    for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
663✔
3629
    {
3630
        GDALDriverH hDriver = GDALGetDriver(iDr);
660✔
3631

3632
        const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets;
660✔
3633
        CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
660✔
3634

3635
        if (nOptions == GDAL_OF_RASTER &&
880✔
3636
            !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
220✔
3637
            continue;
333✔
3638
        if (nOptions == GDAL_OF_VECTOR &&
1,031✔
3639
            !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
440✔
3640
            continue;
264✔
3641
        if (nOptions == GDAL_OF_GNM &&
327✔
3642
            !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3643
            continue;
×
3644
        if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
327✔
3645
            !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
×
3646
            continue;
×
3647

3648
        if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
327✔
3649
            pszRFlag = "r";
324✔
3650

3651
        if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
327✔
3652
            pszWFlag = "w+";
153✔
3653
        else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
174✔
3654
            pszWFlag = "w";
29✔
3655
        else
3656
            pszWFlag = "o";
145✔
3657

3658
        const char *pszUpdate = "";
327✔
3659
        if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
327✔
3660
            pszUpdate = "u";
55✔
3661

3662
        if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
327✔
3663
            pszVirtualIO = "v";
258✔
3664
        else
3665
            pszVirtualIO = "";
69✔
3666

3667
        if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
327✔
3668
            pszSubdatasets = "s";
44✔
3669
        else
3670
            pszSubdatasets = "";
283✔
3671

3672
        CPLString osKind;
654✔
3673
        if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
327✔
3674
            osKind = "raster";
193✔
3675
        if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
327✔
3676
        {
3677
            if (!osKind.empty())
20✔
3678
                osKind += ',';
20✔
3679
            osKind += "multidimensional raster";
20✔
3680
        }
3681
        if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
327✔
3682
        {
3683
            if (!osKind.empty())
197✔
3684
                osKind += ',';
63✔
3685
            osKind += "vector";
197✔
3686
        }
3687
        if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
327✔
3688
        {
3689
            if (!osKind.empty())
×
3690
                osKind += ',';
×
3691
            osKind += "geography network";
×
3692
        }
3693
        if (osKind.empty())
327✔
3694
            osKind = "unknown kind";
×
3695

3696
        std::string osExtensions;
654✔
3697
        if (const char *pszExtensions = CSLFetchNameValueDef(
327✔
3698
                papszMD, GDAL_DMD_EXTENSIONS,
3699
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3700
        {
3701
            const CPLStringList aosExt(
3702
                CSLTokenizeString2(pszExtensions, " ", 0));
440✔
3703
            for (int i = 0; i < aosExt.size(); ++i)
530✔
3704
            {
3705
                if (i == 0)
310✔
3706
                    osExtensions = " (*.";
219✔
3707
                else
3708
                    osExtensions += ", *.";
91✔
3709
                osExtensions += aosExt[i];
310✔
3710
            }
3711
            if (!osExtensions.empty())
220✔
3712
                osExtensions += ')';
219✔
3713
        }
3714

3715
        ret += CPLSPrintf("  %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/
3716
                          GDALGetDriverShortName(hDriver), osKind.c_str(),
3717
                          pszRFlag, pszWFlag, pszUpdate, pszVirtualIO,
3718
                          pszSubdatasets, GDALGetDriverLongName(hDriver),
3719
                          osExtensions.c_str());
327✔
3720
    }
3721

3722
    return ret;
3✔
3723
}
3724

3725
/************************************************************************/
3726
/*                    GDALGeneralCmdLineProcessor()                     */
3727
/************************************************************************/
3728

3729
/**
3730
 * \brief General utility option processing.
3731
 *
3732
 * This function is intended to provide a variety of generic commandline
3733
 * options for all GDAL commandline utilities.  It takes care of the following
3734
 * commandline options:
3735
 *
3736
 *  \--version: report version of GDAL in use.
3737
 *  \--build: report build info about GDAL in use.
3738
 *  \--license: report GDAL license info.
3739
 *  \--formats: report all format drivers configured. Can be used with -json since 3.10
3740
 *  \--format [format]: report details of one format driver.
3741
 *  \--optfile filename: expand an option file into the argument list.
3742
 *  \--config key value: set system configuration option.
3743
 *  \--config key=value: set system configuration option (since GDAL 3.9)
3744
 *  \--debug [on/off/value]: set debug level.
3745
 *  \--mempreload dir: preload directory contents into /vsimem
3746
 *  \--pause: Pause for user input (allows time to attach debugger)
3747
 *  \--locale [locale]: Install a locale using setlocale() (debugging)
3748
 *  \--help-general: report detailed help on general options.
3749
 *
3750
 * The argument array is replaced "in place" and should be freed with
3751
 * CSLDestroy() when no longer needed.  The typical usage looks something
3752
 * like the following.  Note that the formats should be registered so that
3753
 * the \--formats and \--format options will work properly.
3754
 *
3755
 *  int main( int argc, char ** argv )
3756
 *  {
3757
 *    GDALAllRegister();
3758
 *
3759
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3760
 *    if( argc < 1 )
3761
 *        exit( -argc );
3762
 *
3763
 * @param nArgc number of values in the argument list.
3764
 * @param ppapszArgv pointer to the argument list array (will be updated in
3765
 * place).
3766
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3767
 *                 to determine which drivers should be displayed by \--formats.
3768
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3769
 *
3770
 * @return updated nArgc argument count.  Return of 0 requests terminate
3771
 * without error, return of -1 requests exit with error code.
3772
 */
3773

3774
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
1,339✔
3775
                                            int nOptions)
3776

3777
{
3778
    CPLStringList aosReturn;
2,678✔
3779
    int iArg;
3780
    char **papszArgv = *ppapszArgv;
1,339✔
3781

3782
    /* -------------------------------------------------------------------- */
3783
    /*      Preserve the program name.                                      */
3784
    /* -------------------------------------------------------------------- */
3785
    aosReturn.AddString(papszArgv[0]);
1,339✔
3786

3787
    /* ==================================================================== */
3788
    /*      Loop over all arguments.                                        */
3789
    /* ==================================================================== */
3790

3791
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3792
    // detects and warns about a unknown config option.
3793
    for (iArg = 1; iArg < nArgc; iArg++)
8,601✔
3794
    {
3795
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
7,264✔
3796
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
67✔
3797
        {
3798
            if (iArg + 1 >= nArgc)
×
3799
            {
3800
                CPLError(CE_Failure, CPLE_AppDefined,
×
3801
                         "--config option given without a key=value argument.");
3802
                return -1;
×
3803
            }
3804

3805
            const char *pszArg = papszArgv[iArg + 1];
×
3806
            if (strchr(pszArg, '=') != nullptr)
×
3807
            {
3808
                char *pszKey = nullptr;
×
3809
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
×
3810
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
×
3811
                {
3812
                    CPLSetConfigOption(pszKey, pszValue);
×
3813
                }
3814
                CPLFree(pszKey);
×
3815
                ++iArg;
×
3816
            }
3817
            else
3818
            {
3819
                // cppcheck-suppress knownConditionTrueFalse
3820
                if (iArg + 2 >= nArgc)
×
3821
                {
3822
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3823
                             "--config option given without a key and value "
3824
                             "argument.");
3825
                    return -1;
×
3826
                }
3827

3828
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
×
3829
                    CPLSetConfigOption(papszArgv[iArg + 1],
×
3830
                                       papszArgv[iArg + 2]);
×
3831

3832
                iArg += 2;
×
3833
            }
×
3834
        }
3835
        else if (EQUAL(papszArgv[iArg], "--debug"))
7,264✔
3836
        {
3837
            if (iArg + 1 >= nArgc)
13✔
3838
            {
3839
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3840
                         "--debug option given without debug level.");
3841
                return -1;
2✔
3842
            }
3843

3844
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
11✔
3845
            iArg += 1;
11✔
3846
        }
3847
    }
3848

3849
    for (iArg = 1; iArg < nArgc; iArg++)
8,378✔
3850
    {
3851
        /* --------------------------------------------------------------------
3852
         */
3853
        /*      --version */
3854
        /* --------------------------------------------------------------------
3855
         */
3856
        if (EQUAL(papszArgv[iArg], "--version"))
7,133✔
3857
        {
3858
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
66✔
3859
            return 0;
66✔
3860
        }
3861

3862
        /* --------------------------------------------------------------------
3863
         */
3864
        /*      --build */
3865
        /* --------------------------------------------------------------------
3866
         */
3867
        else if (EQUAL(papszArgv[iArg], "--build"))
7,067✔
3868
        {
3869
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
1✔
3870
            return 0;
1✔
3871
        }
3872

3873
        /* --------------------------------------------------------------------
3874
         */
3875
        /*      --license */
3876
        /* --------------------------------------------------------------------
3877
         */
3878
        else if (EQUAL(papszArgv[iArg], "--license"))
7,066✔
3879
        {
3880
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
1✔
3881
            return 0;
1✔
3882
        }
3883

3884
        /* --------------------------------------------------------------------
3885
         */
3886
        /*      --config */
3887
        /* --------------------------------------------------------------------
3888
         */
3889
        else if (EQUAL(papszArgv[iArg], "--config"))
7,065✔
3890
        {
3891
            if (iArg + 1 >= nArgc)
73✔
3892
            {
3893
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3894
                         "--config option given without a key=value argument.");
3895
                return -1;
2✔
3896
            }
3897

3898
            const char *pszArg = papszArgv[iArg + 1];
71✔
3899
            if (strchr(pszArg, '=') != nullptr)
71✔
3900
            {
3901
                char *pszKey = nullptr;
31✔
3902
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
31✔
3903
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
31✔
3904
                {
3905
                    CPLSetConfigOption(pszKey, pszValue);
31✔
3906
                }
3907
                CPLFree(pszKey);
31✔
3908
                ++iArg;
31✔
3909
            }
3910
            else
3911
            {
3912
                if (iArg + 2 >= nArgc)
40✔
3913
                {
3914
                    CPLError(CE_Failure, CPLE_AppDefined,
2✔
3915
                             "--config option given without a key and value "
3916
                             "argument.");
3917
                    return -1;
2✔
3918
                }
3919

3920
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
38✔
3921
                    CPLSetConfigOption(papszArgv[iArg + 1],
38✔
3922
                                       papszArgv[iArg + 2]);
38✔
3923

3924
                iArg += 2;
38✔
3925
            }
3926
        }
3927

3928
        /* --------------------------------------------------------------------
3929
         */
3930
        /*      --mempreload */
3931
        /* --------------------------------------------------------------------
3932
         */
3933
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
6,992✔
3934
        {
3935
            if (iArg + 1 >= nArgc)
4✔
3936
            {
3937
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3938
                         "--mempreload option given without directory path.");
3939
                return -1;
2✔
3940
            }
3941

3942
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
2✔
3943
            if (CSLCount(papszFiles) == 0)
2✔
3944
            {
3945
                CPLError(CE_Failure, CPLE_AppDefined,
×
3946
                         "--mempreload given invalid or empty directory.");
3947
                return -1;
×
3948
            }
3949

3950
            for (int i = 0; papszFiles[i] != nullptr; i++)
497✔
3951
            {
3952
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
495✔
3953
                    continue;
72✔
3954

3955
                std::string osOldPath;
491✔
3956
                CPLString osNewPath;
491✔
3957
                osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
491✔
3958
                                                papszFiles[i], nullptr);
491✔
3959
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
491✔
3960

3961
                VSIStatBufL sStatBuf;
3962
                if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
982✔
3963
                    VSI_ISDIR(sStatBuf.st_mode))
491✔
3964
                {
3965
                    CPLDebug("VSI", "Skipping preload of %s.",
68✔
3966
                             osOldPath.c_str());
3967
                    continue;
68✔
3968
                }
3969

3970
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
423✔
3971
                         osNewPath.c_str());
3972

3973
                if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
423✔
3974
                {
3975
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3976
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3977
                    return -1;
×
3978
                }
3979
            }
3980

3981
            CSLDestroy(papszFiles);
2✔
3982
            iArg += 1;
2✔
3983
        }
3984

3985
        /* --------------------------------------------------------------------
3986
         */
3987
        /*      --debug */
3988
        /* --------------------------------------------------------------------
3989
         */
3990
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,988✔
3991
        {
3992
            if (iArg + 1 >= nArgc)
11✔
3993
            {
3994
                CPLError(CE_Failure, CPLE_AppDefined,
×
3995
                         "--debug option given without debug level.");
3996
                return -1;
×
3997
            }
3998

3999
            iArg += 1;
11✔
4000
        }
4001

4002
        /* --------------------------------------------------------------------
4003
         */
4004
        /*      --optfile */
4005
        /* --------------------------------------------------------------------
4006
         */
4007
        else if (EQUAL(papszArgv[iArg], "--optfile"))
6,977✔
4008
        {
4009
            if (iArg + 1 >= nArgc)
11✔
4010
            {
4011
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
4012
                         "--optfile option given without filename.");
4013
                return -1;
5✔
4014
            }
4015

4016
            VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb");
9✔
4017

4018
            if (fpOptFile == nullptr)
9✔
4019
            {
4020
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
4021
                         "Unable to open optfile '%s'.\n%s",
4022
                         papszArgv[iArg + 1], VSIStrerror(errno));
2✔
4023
                return -1;
2✔
4024
            }
4025

4026
            const char *pszLine;
4027
            CPLStringList aosArgvOptfile;
7✔
4028
            // dummy value as first argument to please
4029
            // GDALGeneralCmdLineProcessor()
4030
            aosArgvOptfile.AddString("");
7✔
4031
            bool bHasOptfile = false;
7✔
4032
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
23✔
4033
            {
4034
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
16✔
4035
                    continue;
3✔
4036

4037
                char **papszTokens = CSLTokenizeString(pszLine);
13✔
4038
                for (int i = 0;
13✔
4039
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
45✔
4040
                {
4041
                    if (EQUAL(papszTokens[i], "--optfile"))
32✔
4042
                    {
4043
                        // To avoid potential recursion
4044
                        CPLError(CE_Warning, CPLE_AppDefined,
×
4045
                                 "--optfile not supported in a option file");
4046
                        bHasOptfile = true;
×
4047
                    }
4048
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
32✔
4049
                    papszTokens[i] = nullptr;
32✔
4050
                }
4051
                CSLDestroy(papszTokens);
13✔
4052
            }
4053

4054
            VSIFCloseL(fpOptFile);
7✔
4055

4056
            char **papszArgvOptfile = aosArgvOptfile.StealList();
7✔
4057
            if (!bHasOptfile)
7✔
4058
            {
4059
                char **papszArgvOptfileBefore = papszArgvOptfile;
7✔
4060
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
7✔
4061
                                                &papszArgvOptfile,
4062
                                                nOptions) < 0)
7✔
4063
                {
4064
                    CSLDestroy(papszArgvOptfile);
1✔
4065
                    return -1;
1✔
4066
                }
4067
                CSLDestroy(papszArgvOptfileBefore);
6✔
4068
            }
4069

4070
            char **papszIter = papszArgvOptfile + 1;
6✔
4071
            while (*papszIter)
36✔
4072
            {
4073
                aosReturn.AddString(*papszIter);
30✔
4074
                ++papszIter;
30✔
4075
            }
4076
            CSLDestroy(papszArgvOptfile);
6✔
4077

4078
            iArg += 1;
6✔
4079
        }
4080

4081
        /* --------------------------------------------------------------------
4082
         */
4083
        /*      --formats */
4084
        /* --------------------------------------------------------------------
4085
         */
4086
        else if (EQUAL(papszArgv[iArg], "--formats"))
6,966✔
4087
        {
4088
            bool bJSON = false;
5✔
4089
            for (int i = 1; i < nArgc; i++)
10✔
4090
            {
4091
                if (strcmp(papszArgv[i], "-json") == 0 ||
7✔
4092
                    strcmp(papszArgv[i], "--json") == 0)
5✔
4093
                {
4094
                    bJSON = true;
2✔
4095
                    break;
2✔
4096
                }
4097
            }
4098

4099
            printf("%s", GDALPrintDriverList(nOptions, bJSON).c_str()); /*ok*/
5✔
4100

4101
            return 0;
5✔
4102
        }
4103

4104
        /* --------------------------------------------------------------------
4105
         */
4106
        /*      --format */
4107
        /* --------------------------------------------------------------------
4108
         */
4109
        else if (EQUAL(papszArgv[iArg], "--format"))
6,961✔
4110
        {
4111
            GDALDriverH hDriver;
4112
            char **papszMD;
4113

4114
            if (iArg + 1 >= nArgc)
6✔
4115
            {
4116
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
4117
                         "--format option given without a format code.");
4118
                return -1;
1✔
4119
            }
4120

4121
            hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
5✔
4122
            if (hDriver == nullptr)
5✔
4123
            {
4124
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
4125
                         "--format option given with format '%s', but that "
4126
                         "format not\nrecognised.  Use the --formats option "
4127
                         "to get a list of available formats,\n"
4128
                         "and use the short code (i.e. GTiff or HFA) as the "
4129
                         "format identifier.\n",
4130
                         papszArgv[iArg + 1]);
1✔
4131
                return -1;
1✔
4132
            }
4133

4134
            printf("Format Details:\n"); /*ok*/
4✔
4135
            printf(/*ok*/ "  Short Name: %s\n",
4✔
4136
                   GDALGetDriverShortName(hDriver));
4137
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
4✔
4138

4139
            papszMD = GDALGetMetadata(hDriver, nullptr);
4✔
4140
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
4✔
4141
                printf("  Supports: Raster\n"); /*ok*/
4✔
4142
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
4✔
4143
                printf("  Supports: Multidimensional raster\n"); /*ok*/
1✔
4144
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
4✔
4145
                printf("  Supports: Vector\n"); /*ok*/
3✔
4146
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
4✔
4147
                printf("  Supports: Geography Network\n"); /*ok*/
×
4148

4149
            const char *pszExt =
4150
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
4✔
4151
            if (pszExt != nullptr)
4✔
4152
                printf("  Extension%s: %s\n", /*ok*/
3✔
4153
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
3✔
4154

4155
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
4✔
4156
                printf("  Mime Type: %s\n", /*ok*/
1✔
4157
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4158
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
4✔
4159
                printf("  Help Topic: %s\n", /*ok*/
3✔
4160
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4161

4162
            if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
4✔
4163
                printf("  Supports: Raster subdatasets\n"); /*ok*/
3✔
4164
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
4✔
4165
                printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
4✔
4166
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
4✔
4167
                printf(/*ok*/
4✔
4168
                       "  Supports: Create() - Create writable dataset.\n");
4169
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
4✔
4170
                printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
1✔
4171
                              "multidimensional dataset.\n");
4172
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
4✔
4173
                printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
3✔
4174
                              "copying "
4175
                              "another.\n");
4176
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
4✔
4177
                printf("  Supports: Update\n"); /*ok*/
3✔
4178
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
4✔
4179
                printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
3✔
4180
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
4✔
4181
                printf("  Creation Datatypes: %s\n", /*ok*/
4✔
4182
                       CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
4183
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
4✔
4184
                printf("  Creation Field Datatypes: %s\n", /*ok*/
3✔
4185
                       CSLFetchNameValue(papszMD,
4186
                                         GDAL_DMD_CREATIONFIELDDATATYPES));
4187
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
4✔
4188
                printf("  Creation Field Data Sub-types: %s\n", /*ok*/
2✔
4189
                       CSLFetchNameValue(papszMD,
4190
                                         GDAL_DMD_CREATIONFIELDDATASUBTYPES));
4191
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
4✔
4192
                printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
2✔
4193
                              "constraint.\n");
4194
            if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
4✔
4195
                printf(/*ok*/
2✔
4196
                       "  Supports: Creating fields with UNIQUE constraint.\n");
4197
            if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
4✔
4198
                printf(/*ok*/
2✔
4199
                       "  Supports: Creating fields with DEFAULT values.\n");
4200
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
4✔
4201
                /*ok*/ printf(
2✔
4202
                    "  Supports: Creating geometry fields with NOT NULL "
4203
                    "constraint.\n");
4204
            if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
4✔
4205
                             false))
4206
                /*ok*/ printf("  Supports: Writing geometries with given "
2✔
4207
                              "coordinate precision\n");
4208
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
4✔
4209
                printf("  Supports: Reading feature styles.\n"); /*ok*/
×
4210
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
4✔
4211
                printf("  Supports: Writing feature styles.\n"); /*ok*/
×
4212
            if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
4✔
4213
                printf("  Supports: Coordinate epoch.\n"); /*ok*/
2✔
4214
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
4✔
4215
                printf("  Supports: Multiple vector layers.\n"); /*ok*/
3✔
4216
            if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
4✔
4217
                printf("  Supports: Reading field domains.\n"); /*ok*/
3✔
4218
            if (CSLFetchNameValue(papszMD,
4✔
4219
                                  GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
4✔
4220
                printf("  Creation field domain types: %s\n", /*ok*/
3✔
4221
                       CSLFetchNameValue(papszMD,
4222
                                         GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
4223
            if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
4✔
4224
                printf("  Supported SQL dialects: %s\n", /*ok*/
3✔
4225
                       CSLFetchNameValue(papszMD,
4226
                                         GDAL_DMD_SUPPORTED_SQL_DIALECTS));
4227
            if (CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS))
4✔
4228
                printf("  Supported items for update: %s\n", /*ok*/
3✔
4229
                       CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS));
4230

4231
            for (const char *key :
32✔
4232
                 {GDAL_DMD_CREATIONOPTIONLIST,
4233
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4234
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4235
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4236
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4237
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4238
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4239
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
36✔
4240
            {
4241
                if (CSLFetchNameValue(papszMD, key))
32✔
4242
                {
4243
                    CPLXMLNode *psCOL =
4244
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
7✔
4245
                    StripIrrelevantOptions(psCOL, nOptions);
7✔
4246
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
7✔
4247

4248
                    CPLDestroyXMLNode(psCOL);
7✔
4249

4250
                    printf("\n%s\n", pszFormattedXML); /*ok*/
7✔
4251
                    CPLFree(pszFormattedXML);
7✔
4252
                }
4253
            }
4254

4255
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
4✔
4256
                printf("  Connection prefix: %s\n", /*ok*/
×
4257
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4258

4259
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
4✔
4260
            {
4261
                CPLXMLNode *psCOL = CPLParseXMLString(
3✔
4262
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4263
                StripIrrelevantOptions(psCOL, nOptions);
3✔
4264
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
3✔
4265

4266
                CPLDestroyXMLNode(psCOL);
3✔
4267

4268
                printf("%s\n", pszFormattedXML); /*ok*/
3✔
4269
                CPLFree(pszFormattedXML);
3✔
4270
            }
4271

4272
            bool bFirstOtherOption = true;
4✔
4273
            for (char **papszIter = papszMD; papszIter && *papszIter;
149✔
4274
                 ++papszIter)
4275
            {
4276
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
145✔
4277
                    !STARTS_WITH(*papszIter, "DMD_") &&
62✔
4278
                    !STARTS_WITH(*papszIter, "DS_") &&
16✔
4279
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
13✔
4280
                {
4281
                    if (bFirstOtherOption)
13✔
4282
                        printf("  Other metadata items:\n"); /*ok*/
4✔
4283
                    bFirstOtherOption = false;
13✔
4284
                    printf("    %s\n", *papszIter); /*ok*/
13✔
4285
                }
4286
            }
4287

4288
            return 0;
4✔
4289
        }
4290

4291
        /* --------------------------------------------------------------------
4292
         */
4293
        /*      --help-general */
4294
        /* --------------------------------------------------------------------
4295
         */
4296
        else if (EQUAL(papszArgv[iArg], "--help-general"))
6,955✔
4297
        {
4298
            printf("Generic GDAL utility command options:\n");       /*ok*/
2✔
4299
            printf("  --version: report version of GDAL in use.\n"); /*ok*/
2✔
4300
            /*ok*/ printf(
2✔
4301
                "  --build: report detailed information about GDAL in "
4302
                "use.\n");
4303
            printf("  --license: report GDAL license info.\n"); /*ok*/
2✔
4304
            printf(                                             /*ok*/
2✔
4305
                   "  --formats: report all configured format drivers.\n"); /*ok*/
4306
            printf("  --format [<format>]: details of one format.\n"); /*ok*/
2✔
4307
            /*ok*/ printf(
2✔
4308
                "  --optfile filename: expand an option file into the "
4309
                "argument list.\n");
4310
            printf(/*ok*/
2✔
4311
                   "  --config <key> <value> or --config <key>=<value>: set "
4312
                   "system configuration option.\n");               /*ok*/
4313
            printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
2✔
4314
            /*ok*/ printf(                                          /*ok*/
2✔
4315
                          "  --pause: wait for user input, time to attach "
4316
                          "debugger\n");
4317
            printf("  --locale [<locale>]: install locale for debugging " /*ok*/
2✔
4318
                   "(i.e. en_US.UTF-8)\n");
4319
            printf("  --help-general: report detailed help on general " /*ok*/
2✔
4320
                   "options.\n");
4321

4322
            return 0;
2✔
4323
        }
4324

4325
        /* --------------------------------------------------------------------
4326
         */
4327
        /*      --locale */
4328
        /* --------------------------------------------------------------------
4329
         */
4330
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
6,953✔
4331
        {
4332
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
2✔
4333
        }
4334

4335
        /* --------------------------------------------------------------------
4336
         */
4337
        /*      --pause */
4338
        /* --------------------------------------------------------------------
4339
         */
4340
        else if (EQUAL(papszArgv[iArg], "--pause"))
6,951✔
4341
        {
4342
            std::cout << "Hit <ENTER> to Continue." << std::endl;
×
4343
            std::cin.clear();
×
4344
            std::cin.ignore(cpl::NumericLimits<std::streamsize>::max(), '\n');
×
4345
        }
4346

4347
        /* --------------------------------------------------------------------
4348
         */
4349
        /*      Carry through unrecognized options. */
4350
        /* --------------------------------------------------------------------
4351
         */
4352
        else
4353
        {
4354
            aosReturn.AddString(papszArgv[iArg]);
6,951✔
4355
        }
4356
    }
4357

4358
    const int nSize = aosReturn.size();
1,245✔
4359
    *ppapszArgv = aosReturn.StealList();
1,245✔
4360

4361
    return nSize;
1,245✔
4362
}
4363

4364
/************************************************************************/
4365
/*                          _FetchDblFromMD()                           */
4366
/************************************************************************/
4367

4368
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
1,680✔
4369
                            double *padfTarget, int nCount, double dfDefault)
4370

4371
{
4372
    char szFullKey[200];
4373

4374
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
1,680✔
4375

4376
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
1,680✔
4377

4378
    for (int i = 0; i < nCount; i++)
9,744✔
4379
        padfTarget[i] = dfDefault;
8,064✔
4380

4381
    if (pszValue == nullptr)
1,680✔
4382
        return false;
414✔
4383

4384
    if (nCount == 1)
1,266✔
4385
    {
4386
        *padfTarget = CPLAtofM(pszValue);
930✔
4387
        return true;
930✔
4388
    }
4389

4390
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
336✔
4391

4392
    if (CSLCount(papszTokens) != nCount)
336✔
4393
    {
4394
        CSLDestroy(papszTokens);
×
4395
        return false;
×
4396
    }
4397

4398
    for (int i = 0; i < nCount; i++)
7,056✔
4399
        padfTarget[i] = CPLAtofM(papszTokens[i]);
6,720✔
4400

4401
    CSLDestroy(papszTokens);
336✔
4402

4403
    return true;
336✔
4404
}
4405

4406
/************************************************************************/
4407
/*                         GDALExtractRPCInfo()                         */
4408
/************************************************************************/
4409

4410
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4411
 *
4412
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4413
 *
4414
 * @param papszMD Dictionary of metadata representing RPC
4415
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4416
 * @return TRUE in case of success. FALSE in case of failure.
4417
 */
4418
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
×
4419

4420
{
4421
    GDALRPCInfoV2 sRPC;
4422
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
×
4423
        return FALSE;
×
4424
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
×
4425
    return TRUE;
×
4426
}
4427

4428
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4429
 *
4430
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4431
 *
4432
 * @param papszMD Dictionary of metadata representing RPC
4433
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4434
 * @return TRUE in case of success. FALSE in case of failure.
4435
 */
4436
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
84✔
4437

4438
{
4439
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
84✔
4440
        return FALSE;
×
4441

4442
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
84✔
4443
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
84✔
4444
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
252✔
4445
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
84✔
4446
    {
4447
        CPLError(CE_Failure, CPLE_AppDefined,
×
4448
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
4449
        return FALSE;
×
4450
    }
4451

4452
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
84✔
4453
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
84✔
4454
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
84✔
4455
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
84✔
4456
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
84✔
4457
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
84✔
4458
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
84✔
4459
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
84✔
4460
                    1.0);
4461
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
84✔
4462
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
84✔
4463
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
84✔
4464
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
84✔
4465

4466
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
84✔
4467
                    0.0);
4468
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
84✔
4469
                    0.0);
4470
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
84✔
4471
                    0.0);
4472
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
84✔
4473
                    0.0);
4474

4475
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
84✔
4476
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
84✔
4477
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
84✔
4478
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
84✔
4479

4480
    return TRUE;
84✔
4481
}
4482

4483
/************************************************************************/
4484
/*                     GDALFindAssociatedAuxFile()                      */
4485
/************************************************************************/
4486

4487
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
10,248✔
4488
                                       GDALAccess eAccess,
4489
                                       GDALDataset *poDependentDS)
4490

4491
{
4492
    const char *pszAuxSuffixLC = "aux";
10,248✔
4493
    const char *pszAuxSuffixUC = "AUX";
10,248✔
4494

4495
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
10,248✔
4496
        return nullptr;
32✔
4497

4498
    /* -------------------------------------------------------------------- */
4499
    /*      Don't even try to look for an .aux file if we don't have a      */
4500
    /*      path of any kind.                                               */
4501
    /* -------------------------------------------------------------------- */
4502
    if (strlen(pszBasename) == 0)
10,216✔
4503
        return nullptr;
58✔
4504

4505
    /* -------------------------------------------------------------------- */
4506
    /*      We didn't find that, so try and find a corresponding aux        */
4507
    /*      file.  Check that we are the dependent file of the aux          */
4508
    /*      file, or if we aren't verify that the dependent file does       */
4509
    /*      not exist, likely mean it is us but some sort of renaming       */
4510
    /*      has occurred.                                                   */
4511
    /* -------------------------------------------------------------------- */
4512
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
20,316✔
4513
    CPLString osAuxFilename =
4514
        CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC);
10,158✔
4515
    GDALDataset *poODS = nullptr;
10,158✔
4516
    GByte abyHeader[32];
4517

4518
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
10,158✔
4519

4520
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,158✔
4521
    {
4522
        // Can't found file with lower case suffix. Try the upper case one.
4523
        osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC);
10,135✔
4524
        fp = VSIFOpenL(osAuxFilename, "rb");
10,135✔
4525
    }
4526

4527
    if (fp != nullptr)
10,158✔
4528
    {
4529
        if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
46✔
4530
            STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
23✔
4531
                           "EHFA_HEADER_TAG"))
4532
        {
4533
            /* Avoid causing failure in opening of main file from SWIG bindings
4534
             */
4535
            /* when auxiliary file cannot be opened (#3269) */
4536
            CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
36✔
4537
            if (poDependentDS != nullptr && poDependentDS->GetShared())
18✔
4538
                poODS = GDALDataset::FromHandle(
×
4539
                    GDALOpenShared(osAuxFilename, eAccess));
4540
            else
4541
                poODS =
4542
                    GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
18✔
4543
        }
4544
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
23✔
4545
    }
4546

4547
    /* -------------------------------------------------------------------- */
4548
    /*      Try replacing extension with .aux                               */
4549
    /* -------------------------------------------------------------------- */
4550
    if (poODS != nullptr)
10,158✔
4551
    {
4552
        const char *pszDep =
4553
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
18✔
4554
        if (pszDep == nullptr)
18✔
4555
        {
4556
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
×
4557
                     osAuxFilename.c_str());
4558
            GDALClose(poODS);
×
4559
            poODS = nullptr;
×
4560
        }
4561
        else if (!EQUAL(pszDep, osJustFile))
18✔
4562
        {
4563
            VSIStatBufL sStatBuf;
4564

4565
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4566
            {
4567
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4568
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4569
                GDALClose(poODS);
×
4570
                poODS = nullptr;
×
4571
            }
4572
            else
4573
            {
4574
                CPLDebug("AUX",
×
4575
                         "%s is for file %s, not %s, but since\n"
4576
                         "%s does not exist, we will use .aux file as our own.",
4577
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4578
                         pszDep);
4579
            }
4580
        }
4581

4582
        /* --------------------------------------------------------------------
4583
         */
4584
        /*      Confirm that the aux file matches the configuration of the */
4585
        /*      dependent dataset. */
4586
        /* --------------------------------------------------------------------
4587
         */
4588
        if (poODS != nullptr && poDependentDS != nullptr &&
36✔
4589
            (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
18✔
4590
             poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
18✔
4591
             poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
15✔
4592
        {
4593
            CPLDebug("AUX",
3✔
4594
                     "Ignoring aux file %s as its raster configuration\n"
4595
                     "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4596
                     "x %dB)",
4597
                     osAuxFilename.c_str(), poODS->GetRasterXSize(),
4598
                     poODS->GetRasterYSize(), poODS->GetRasterCount(),
4599
                     poDependentDS->GetRasterXSize(),
4600
                     poDependentDS->GetRasterYSize(),
4601
                     poDependentDS->GetRasterCount());
4602

4603
            GDALClose(poODS);
3✔
4604
            poODS = nullptr;
3✔
4605
        }
4606
    }
4607

4608
    /* -------------------------------------------------------------------- */
4609
    /*      Try appending .aux to the end of the filename.                  */
4610
    /* -------------------------------------------------------------------- */
4611
    if (poODS == nullptr)
10,158✔
4612
    {
4613
        osAuxFilename = pszBasename;
10,143✔
4614
        osAuxFilename += ".";
10,143✔
4615
        osAuxFilename += pszAuxSuffixLC;
10,143✔
4616
        fp = VSIFOpenL(osAuxFilename, "rb");
10,143✔
4617
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,143✔
4618
        {
4619
            // Can't found file with lower case suffix. Try the upper case one.
4620
            osAuxFilename = pszBasename;
10,143✔
4621
            osAuxFilename += ".";
10,143✔
4622
            osAuxFilename += pszAuxSuffixUC;
10,143✔
4623
            fp = VSIFOpenL(osAuxFilename, "rb");
10,143✔
4624
        }
4625

4626
        if (fp != nullptr)
10,143✔
4627
        {
4628
            if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
×
4629
                STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
×
4630
                               "EHFA_HEADER_TAG"))
4631
            {
4632
                /* Avoid causing failure in opening of main file from SWIG
4633
                 * bindings */
4634
                /* when auxiliary file cannot be opened (#3269) */
4635
                CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
×
4636
                if (poDependentDS != nullptr && poDependentDS->GetShared())
×
4637
                    poODS = GDALDataset::FromHandle(
×
4638
                        GDALOpenShared(osAuxFilename, eAccess));
4639
                else
4640
                    poODS = GDALDataset::FromHandle(
×
4641
                        GDALOpen(osAuxFilename, eAccess));
4642
            }
4643
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
×
4644
        }
4645

4646
        if (poODS != nullptr)
10,143✔
4647
        {
4648
            const char *pszDep =
4649
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
×
4650
            if (pszDep == nullptr)
×
4651
            {
4652
                CPLDebug("AUX",
×
4653
                         "Found %s but it has no dependent file, ignoring.",
4654
                         osAuxFilename.c_str());
4655
                GDALClose(poODS);
×
4656
                poODS = nullptr;
×
4657
            }
4658
            else if (!EQUAL(pszDep, osJustFile))
×
4659
            {
4660
                VSIStatBufL sStatBuf;
4661

4662
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4663
                {
4664
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4665
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4666
                    GDALClose(poODS);
×
4667
                    poODS = nullptr;
×
4668
                }
4669
                else
4670
                {
4671
                    CPLDebug(
×
4672
                        "AUX",
4673
                        "%s is for file %s, not %s, but since\n"
4674
                        "%s does not exist, we will use .aux file as our own.",
4675
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4676
                        pszDep);
4677
                }
4678
            }
4679
        }
4680
    }
4681

4682
    /* -------------------------------------------------------------------- */
4683
    /*      Confirm that the aux file matches the configuration of the      */
4684
    /*      dependent dataset.                                              */
4685
    /* -------------------------------------------------------------------- */
4686
    if (poODS != nullptr && poDependentDS != nullptr &&
10,173✔
4687
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
15✔
4688
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
15✔
4689
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
15✔
4690
    {
4691
        CPLDebug(
×
4692
            "AUX",
4693
            "Ignoring aux file %s as its raster configuration\n"
4694
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4695
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4696
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4697
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4698
            poDependentDS->GetRasterCount());
4699

4700
        GDALClose(poODS);
×
4701
        poODS = nullptr;
×
4702
    }
4703

4704
    return poODS;
10,158✔
4705
}
4706

4707
/************************************************************************/
4708
/* Infrastructure to check that dataset characteristics are valid       */
4709
/************************************************************************/
4710

4711
CPL_C_START
4712

4713
/**
4714
 * \brief Return TRUE if the dataset dimensions are valid.
4715
 *
4716
 * @param nXSize raster width
4717
 * @param nYSize raster height
4718
 *
4719
 * @since GDAL 1.7.0
4720
 */
4721
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
5,536✔
4722
{
4723
    if (nXSize <= 0 || nYSize <= 0)
5,536✔
4724
    {
4725
        CPLError(CE_Failure, CPLE_AppDefined,
8✔
4726
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4727
        return FALSE;
8✔
4728
    }
4729
    return TRUE;
5,528✔
4730
}
4731

4732
/**
4733
 * \brief Return TRUE if the band count is valid.
4734
 *
4735
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4736
 * the band count will be compared to the maximum number of band allowed.
4737
 * If not defined, the maximum number allowed is 65536.
4738
 *
4739
 * @param nBands the band count
4740
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4741
 *
4742
 * @since GDAL 1.7.0
4743
 */
4744

4745
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
107,630✔
4746
{
4747
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
107,630✔
4748
    {
4749
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
6✔
4750
                 nBands);
4751
        return FALSE;
6✔
4752
    }
4753
    const char *pszMaxBandCount =
4754
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
107,624✔
4755
    int nMaxBands = std::clamp(atoi(pszMaxBandCount), 0, INT_MAX - 1);
107,624✔
4756
    if (nBands > nMaxBands)
107,624✔
4757
    {
4758
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
4759
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4760
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4761
                 "legitimate number.",
4762
                 nBands, nMaxBands);
4763
        return FALSE;
2✔
4764
    }
4765
    return TRUE;
107,622✔
4766
}
4767

4768
CPL_C_END
4769

4770
/************************************************************************/
4771
/*                     GDALSerializeGCPListToXML()                      */
4772
/************************************************************************/
4773

4774
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
16✔
4775
                               const std::vector<gdal::GCP> &asGCPs,
4776
                               const OGRSpatialReference *poGCP_SRS)
4777
{
4778
    CPLString oFmt;
32✔
4779

4780
    CPLXMLNode *psPamGCPList =
4781
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
16✔
4782

4783
    CPLXMLNode *psLastChild = nullptr;
16✔
4784

4785
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
16✔
4786
    {
4787
        char *pszWKT = nullptr;
9✔
4788
        poGCP_SRS->exportToWkt(&pszWKT);
9✔
4789
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
9✔
4790
        CPLFree(pszWKT);
9✔
4791
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
9✔
4792
        CPLString osMapping;
9✔
4793
        for (size_t i = 0; i < mapping.size(); ++i)
27✔
4794
        {
4795
            if (!osMapping.empty())
18✔
4796
                osMapping += ",";
9✔
4797
            osMapping += CPLSPrintf("%d", mapping[i]);
18✔
4798
        }
4799
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
9✔
4800
                       osMapping.c_str());
4801

4802
        psLastChild = psPamGCPList->psChild->psNext;
9✔
4803
    }
4804

4805
    for (const gdal::GCP &gcp : asGCPs)
21,896✔
4806
    {
4807
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
21,880✔
4808

4809
        if (psLastChild == nullptr)
21,880✔
4810
            psPamGCPList->psChild = psXMLGCP;
7✔
4811
        else
4812
            psLastChild->psNext = psXMLGCP;
21,873✔
4813
        psLastChild = psXMLGCP;
21,880✔
4814

4815
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
21,880✔
4816

4817
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
21,880✔
4818
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
×
4819

4820
        CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
21,880✔
4821

4822
        CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
21,880✔
4823

4824
        CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
21,880✔
4825

4826
        CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
21,880✔
4827

4828
        /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
4829
         * back */
4830
        if (gcp.Z() != 0.0)
21,880✔
4831
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
21,860✔
4832
    }
4833
}
16✔
4834

4835
/************************************************************************/
4836
/*                     GDALDeserializeGCPListFromXML()                  */
4837
/************************************************************************/
4838

4839
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
84✔
4840
                                   std::vector<gdal::GCP> &asGCPs,
4841
                                   OGRSpatialReference **ppoGCP_SRS)
4842
{
4843
    if (ppoGCP_SRS)
84✔
4844
    {
4845
        const char *pszRawProj =
4846
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
76✔
4847

4848
        *ppoGCP_SRS = nullptr;
76✔
4849
        if (pszRawProj && pszRawProj[0])
76✔
4850
        {
4851
            *ppoGCP_SRS = new OGRSpatialReference();
60✔
4852
            (*ppoGCP_SRS)
4853
                ->SetFromUserInput(
60✔
4854
                    pszRawProj,
4855
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4856

4857
            const char *pszMapping =
4858
                CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
60✔
4859
            if (pszMapping)
60✔
4860
            {
4861
                char **papszTokens =
4862
                    CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
13✔
4863
                std::vector<int> anMapping;
26✔
4864
                for (int i = 0; papszTokens && papszTokens[i]; i++)
39✔
4865
                {
4866
                    anMapping.push_back(atoi(papszTokens[i]));
26✔
4867
                }
4868
                CSLDestroy(papszTokens);
13✔
4869
                (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
13✔
4870
            }
4871
            else
4872
            {
4873
                (*ppoGCP_SRS)
4874
                    ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
47✔
4875
            }
4876
        }
4877
    }
4878

4879
    asGCPs.clear();
84✔
4880
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
24,612✔
4881
         psXMLGCP = psXMLGCP->psNext)
24,528✔
4882
    {
4883
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
24,528✔
4884
            continue;
81✔
4885

4886
        gdal::GCP gcp;
48,894✔
4887
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
24,447✔
4888
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
24,447✔
4889

4890
        const auto ParseDoubleValue =
4891
            [psXMLGCP](const char *pszParameter, double &dfVal)
97,788✔
4892
        {
4893
            const char *pszVal =
4894
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
97,788✔
4895
            if (!pszVal)
97,788✔
4896
            {
4897
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
×
4898
                         pszParameter);
4899
                return false;
×
4900
            }
4901
            char *endptr = nullptr;
97,788✔
4902
            dfVal = CPLStrtod(pszVal, &endptr);
97,788✔
4903
            if (endptr == pszVal)
97,788✔
4904
            {
4905
                CPLError(CE_Failure, CPLE_AppDefined,
×
4906
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4907
                return false;
×
4908
            }
4909
            return true;
97,788✔
4910
        };
24,447✔
4911

4912
        bool bOK = true;
24,447✔
4913
        if (!ParseDoubleValue("Pixel", gcp.Pixel()))
24,447✔
4914
            bOK = false;
×
4915
        if (!ParseDoubleValue("Line", gcp.Line()))
24,447✔
4916
            bOK = false;
×
4917
        if (!ParseDoubleValue("X", gcp.X()))
24,447✔
4918
            bOK = false;
×
4919
        if (!ParseDoubleValue("Y", gcp.Y()))
24,447✔
4920
            bOK = false;
×
4921
        const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
24,447✔
4922
        if (pszZ == nullptr)
24,447✔
4923
        {
4924
            // Note: GDAL 1.10.1 and older generated #GCPZ,
4925
            // but could not read it back.
4926
            pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
2,379✔
4927
        }
4928
        char *endptr = nullptr;
24,447✔
4929
        gcp.Z() = CPLStrtod(pszZ, &endptr);
24,447✔
4930
        if (endptr == pszZ)
24,447✔
4931
        {
4932
            CPLError(CE_Failure, CPLE_AppDefined,
×
4933
                     "GCP#Z=%s is an invalid value", pszZ);
4934
            bOK = false;
×
4935
        }
4936

4937
        if (bOK)
24,447✔
4938
        {
4939
            asGCPs.emplace_back(std::move(gcp));
24,447✔
4940
        }
4941
    }
4942
}
84✔
4943

4944
/************************************************************************/
4945
/*                   GDALSerializeOpenOptionsToXML()                    */
4946
/************************************************************************/
4947

4948
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
2,732✔
4949
                                   CSLConstList papszOpenOptions)
4950
{
4951
    if (papszOpenOptions != nullptr)
2,732✔
4952
    {
4953
        CPLXMLNode *psOpenOptions =
4954
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
5✔
4955
        CPLXMLNode *psLastChild = nullptr;
5✔
4956

4957
        for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
10✔
4958
             papszIter++)
4959
        {
4960
            const char *pszRawValue;
4961
            char *pszKey = nullptr;
5✔
4962
            CPLXMLNode *psOOI;
4963

4964
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
5✔
4965

4966
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
5✔
4967
            if (psLastChild == nullptr)
5✔
4968
                psOpenOptions->psChild = psOOI;
5✔
4969
            else
4970
                psLastChild->psNext = psOOI;
×
4971
            psLastChild = psOOI;
5✔
4972

4973
            CPLSetXMLValue(psOOI, "#key", pszKey);
5✔
4974
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
5✔
4975

4976
            CPLFree(pszKey);
5✔
4977
        }
4978
    }
4979
}
2,732✔
4980

4981
/************************************************************************/
4982
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4983
/************************************************************************/
4984

4985
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
104,904✔
4986
{
4987
    char **papszOpenOptions = nullptr;
104,904✔
4988
    const CPLXMLNode *psOpenOptions =
4989
        CPLGetXMLNode(psParentNode, "OpenOptions");
104,904✔
4990
    if (psOpenOptions != nullptr)
104,904✔
4991
    {
4992
        const CPLXMLNode *psOOI;
4993
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
34✔
4994
             psOOI = psOOI->psNext)
17✔
4995
        {
4996
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
17✔
4997
                psOOI->psChild == nullptr ||
17✔
4998
                psOOI->psChild->psNext == nullptr ||
17✔
4999
                psOOI->psChild->eType != CXT_Attribute ||
17✔
5000
                psOOI->psChild->psChild == nullptr)
17✔
5001
                continue;
×
5002

5003
            char *pszName = psOOI->psChild->psChild->pszValue;
17✔
5004
            char *pszValue = psOOI->psChild->psNext->pszValue;
17✔
5005
            if (pszName != nullptr && pszValue != nullptr)
17✔
5006
                papszOpenOptions =
5007
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
17✔
5008
        }
5009
    }
5010
    return papszOpenOptions;
104,904✔
5011
}
5012

5013
/************************************************************************/
5014
/*                    GDALRasterIOGetResampleAlg()                      */
5015
/************************************************************************/
5016

5017
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
203,682✔
5018
{
5019
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
203,682✔
5020
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
203,682✔
5021
        eResampleAlg = GRIORA_NearestNeighbour;
200,939✔
5022
    else if (EQUAL(pszResampling, "BILINEAR"))
2,743✔
5023
        eResampleAlg = GRIORA_Bilinear;
2,082✔
5024
    else if (EQUAL(pszResampling, "CUBIC"))
661✔
5025
        eResampleAlg = GRIORA_Cubic;
596✔
5026
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
65✔
5027
        eResampleAlg = GRIORA_CubicSpline;
4✔
5028
    else if (EQUAL(pszResampling, "LANCZOS"))
61✔
5029
        eResampleAlg = GRIORA_Lanczos;
1✔
5030
    else if (EQUAL(pszResampling, "AVERAGE"))
60✔
5031
        eResampleAlg = GRIORA_Average;
53✔
5032
    else if (EQUAL(pszResampling, "RMS"))
7✔
5033
        eResampleAlg = GRIORA_RMS;
1✔
5034
    else if (EQUAL(pszResampling, "MODE"))
6✔
5035
        eResampleAlg = GRIORA_Mode;
5✔
5036
    else if (EQUAL(pszResampling, "GAUSS"))
1✔
5037
        eResampleAlg = GRIORA_Gauss;
1✔
5038
    else
5039
        CPLError(CE_Warning, CPLE_NotSupported,
×
5040
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
5041
    return eResampleAlg;
203,682✔
5042
}
5043

5044
/************************************************************************/
5045
/*                    GDALRasterIOGetResampleAlgStr()                   */
5046
/************************************************************************/
5047

5048
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
7✔
5049
{
5050
    switch (eResampleAlg)
7✔
5051
    {
5052
        case GRIORA_NearestNeighbour:
×
5053
            return "NearestNeighbour";
×
5054
        case GRIORA_Bilinear:
×
5055
            return "Bilinear";
×
5056
        case GRIORA_Cubic:
7✔
5057
            return "Cubic";
7✔
5058
        case GRIORA_CubicSpline:
×
5059
            return "CubicSpline";
×
5060
        case GRIORA_Lanczos:
×
5061
            return "Lanczos";
×
5062
        case GRIORA_Average:
×
5063
            return "Average";
×
5064
        case GRIORA_RMS:
×
5065
            return "RMS";
×
5066
        case GRIORA_Mode:
×
5067
            return "Mode";
×
5068
        case GRIORA_Gauss:
×
5069
            return "Gauss";
×
5070
        default:
×
5071
            CPLAssert(false);
×
5072
            return "Unknown";
5073
    }
5074
}
5075

5076
/************************************************************************/
5077
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
5078
/************************************************************************/
5079

5080
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
5,220,360✔
5081
                                        int nXSize, int nYSize, int nBufXSize,
5082
                                        int nBufYSize)
5083
{
5084
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
5,220,360✔
5085
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
573,703✔
5086
    {
5087
        const char *pszResampling =
5088
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
569,010✔
5089
        if (pszResampling != nullptr)
569,010✔
5090
        {
5091
            psExtraArg->eResampleAlg =
1✔
5092
                GDALRasterIOGetResampleAlg(pszResampling);
1✔
5093
        }
5094
    }
5095
}
5,220,360✔
5096

5097
/************************************************************************/
5098
/*                     GDALCanFileAcceptSidecarFile()                   */
5099
/************************************************************************/
5100

5101
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
125,363✔
5102
{
5103
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
125,363✔
5104
        return FALSE;
×
5105
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
5106
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
125,363✔
5107
        return FALSE;
402✔
5108
    return TRUE;
124,961✔
5109
}
5110

5111
/************************************************************************/
5112
/*                   GDALCanReliablyUseSiblingFileList()                */
5113
/************************************************************************/
5114

5115
/* Try to address https://github.com/OSGeo/gdal/issues/2903 */
5116
/* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
5117
/*   (normalization form decomposed). The filesystem takes care of converting */
5118
/*   precomposed form as often coming from user interface to this NFD variant */
5119
/*   See
5120
 * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
5121
 */
5122
/*   And readdir() will return such NFD variant encoding. Consequently comparing
5123
 */
5124
/*   the user filename with ones with readdir() is not reliable */
5125
/* - APFS preserves both case and normalization of the filename on disk in all
5126
 */
5127
/*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
5128
 */
5129
/*   the case-insensitive and case-sensitive variants, using a hash-based native
5130
 */
5131
/*   normalization scheme. APFS preserves the normalization of the filename and
5132
 */
5133
/*   uses hashes of the normalized form of the filename to provide normalization
5134
 */
5135
/*   insensitivity. */
5136
/*   From
5137
 * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
5138
 */
5139
/*   Issues might still arise if the file has been created using one of the
5140
 * UTF-8 */
5141
/*   encoding (likely the decomposed one if using MacOS specific API), but the
5142
 */
5143
/*   string passed to GDAL for opening would be with another one (likely the
5144
 * precomposed one) */
5145
bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
139,861✔
5146
{
5147
#ifdef __APPLE__
5148
    for (int i = 0; pszFilename[i] != 0; ++i)
5149
    {
5150
        if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
5151
        {
5152
            // non-ASCII character found
5153

5154
            // if this is a network storage, assume no issue
5155
            if (!VSIIsLocal(pszFilename))
5156
            {
5157
                return true;
5158
            }
5159
            return false;
5160
        }
5161
    }
5162
    return true;
5163
#else
5164
    (void)pszFilename;
5165
    return true;
139,861✔
5166
#endif
5167
}
5168

5169
/************************************************************************/
5170
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
5171
/************************************************************************/
5172

5173
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
1,390✔
5174
{
5175
    const auto kMaxFloat = cpl::NumericLimits<float>::max();
1,390✔
5176
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
1,390✔
5177
        return -kMaxFloat;
33✔
5178
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
1,357✔
5179
        return kMaxFloat;
8✔
5180
    return dfVal;
1,349✔
5181
}
5182

5183
/************************************************************************/
5184
/*                        GDALCopyNoDataValue()                         */
5185
/************************************************************************/
5186

5187
/** Copy the nodata value from the source band to the target band if
5188
 * it can be exactly represented in the output data type.
5189
 *
5190
 * @param poDstBand Destination band.
5191
 * @param poSrcBand Source band band.
5192
 * @param[out] pbCannotBeExactlyRepresented Pointer to a boolean, or nullptr.
5193
 *             If the value cannot be exactly represented on the output data
5194
 *             type, *pbCannotBeExactlyRepresented will be set to true.
5195
 *
5196
 * @return true if the nodata value was successfully set.
5197
 */
5198
bool GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand,
134,182✔
5199
                         bool *pbCannotBeExactlyRepresented)
5200
{
5201
    if (pbCannotBeExactlyRepresented)
134,182✔
5202
        *pbCannotBeExactlyRepresented = false;
101✔
5203
    int bSuccess;
5204
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
134,182✔
5205
    const auto eDstDataType = poDstBand->GetRasterDataType();
134,182✔
5206
    if (eSrcDataType == GDT_Int64)
134,182✔
5207
    {
5208
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
8✔
5209
        if (bSuccess)
8✔
5210
        {
5211
            if (eDstDataType == GDT_Int64)
3✔
5212
            {
5213
                return poDstBand->SetNoDataValueAsInt64(nNoData) == CE_None;
3✔
5214
            }
5215
            else if (eDstDataType == GDT_UInt64)
×
5216
            {
5217
                if (nNoData >= 0)
×
5218
                {
5219
                    return poDstBand->SetNoDataValueAsUInt64(
×
5220
                               static_cast<uint64_t>(nNoData)) == CE_None;
×
5221
                }
5222
            }
5223
            else if (nNoData ==
×
5224
                     static_cast<int64_t>(static_cast<double>(nNoData)))
×
5225
            {
5226
                const double dfValue = static_cast<double>(nNoData);
×
5227
                if (GDALIsValueExactAs(dfValue, eDstDataType))
×
5228
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
×
5229
            }
5230
        }
5231
    }
5232
    else if (eSrcDataType == GDT_UInt64)
134,174✔
5233
    {
5234
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
4✔
5235
        if (bSuccess)
4✔
5236
        {
5237
            if (eDstDataType == GDT_UInt64)
3✔
5238
            {
5239
                return poDstBand->SetNoDataValueAsUInt64(nNoData) == CE_None;
3✔
5240
            }
5241
            else if (eDstDataType == GDT_Int64)
×
5242
            {
5243
                if (nNoData <
×
5244
                    static_cast<uint64_t>(cpl::NumericLimits<int64_t>::max()))
×
5245
                {
5246
                    return poDstBand->SetNoDataValueAsInt64(
×
5247
                               static_cast<int64_t>(nNoData)) == CE_None;
×
5248
                }
5249
            }
5250
            else if (nNoData ==
×
5251
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
×
5252
            {
5253
                const double dfValue = static_cast<double>(nNoData);
×
5254
                if (GDALIsValueExactAs(dfValue, eDstDataType))
×
5255
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
×
5256
            }
5257
        }
5258
    }
5259
    else
5260
    {
5261
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
134,170✔
5262
        if (bSuccess)
134,170✔
5263
        {
5264
            if (eDstDataType == GDT_Int64)
377✔
5265
            {
5266
                if (dfNoData >= static_cast<double>(
×
5267
                                    cpl::NumericLimits<int64_t>::lowest()) &&
×
5268
                    dfNoData <= static_cast<double>(
×
5269
                                    cpl::NumericLimits<int64_t>::max()) &&
×
5270
                    dfNoData ==
5271
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
×
5272
                {
5273
                    return poDstBand->SetNoDataValueAsInt64(
×
5274
                               static_cast<int64_t>(dfNoData)) == CE_None;
×
5275
                }
5276
            }
5277
            else if (eDstDataType == GDT_UInt64)
377✔
5278
            {
5279
                if (dfNoData >= static_cast<double>(
×
5280
                                    cpl::NumericLimits<uint64_t>::lowest()) &&
×
5281
                    dfNoData <= static_cast<double>(
×
5282
                                    cpl::NumericLimits<uint64_t>::max()) &&
×
5283
                    dfNoData ==
5284
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
×
5285
                {
5286
                    return poDstBand->SetNoDataValueAsInt64(
×
5287
                               static_cast<uint64_t>(dfNoData)) == CE_None;
×
5288
                }
5289
            }
5290
            else
5291
            {
5292
                return poDstBand->SetNoDataValue(dfNoData) == CE_None;
377✔
5293
            }
5294
        }
5295
    }
5296
    if (pbCannotBeExactlyRepresented)
133,799✔
5297
        *pbCannotBeExactlyRepresented = true;
×
5298
    return false;
133,799✔
5299
}
5300

5301
/************************************************************************/
5302
/*                     GDALGetNoDataValueCastToDouble()                 */
5303
/************************************************************************/
5304

5305
double GDALGetNoDataValueCastToDouble(int64_t nVal)
2✔
5306
{
5307
    const double dfVal = static_cast<double>(nVal);
2✔
5308
    if (static_cast<int64_t>(dfVal) != nVal)
2✔
5309
    {
5310
        CPLError(CE_Warning, CPLE_AppDefined,
×
5311
                 "GetNoDataValue() returns an approximate value of the "
5312
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5313
                 "GetNoDataValueAsInt64() instead",
5314
                 static_cast<GIntBig>(nVal));
5315
    }
5316
    return dfVal;
2✔
5317
}
5318

5319
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
2✔
5320
{
5321
    const double dfVal = static_cast<double>(nVal);
2✔
5322
    if (static_cast<uint64_t>(dfVal) != nVal)
2✔
5323
    {
5324
        CPLError(CE_Warning, CPLE_AppDefined,
×
5325
                 "GetNoDataValue() returns an approximate value of the "
5326
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5327
                 "GetNoDataValueAsUInt64() instead",
5328
                 static_cast<GUIntBig>(nVal));
5329
    }
5330
    return dfVal;
2✔
5331
}
5332

5333
/************************************************************************/
5334
/*                GDALGetCompressionFormatForJPEG()                     */
5335
/************************************************************************/
5336

5337
//! @cond Doxygen_Suppress
5338
std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
23✔
5339
{
5340
    std::string osRet;
23✔
5341
    const auto nSavedPos = VSIFTellL(fp);
23✔
5342
    GByte abyMarkerHeader[4];
5343
    if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
23✔
5344
        VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
23✔
5345
        abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
46✔
5346
    {
5347
        osRet = "JPEG";
23✔
5348
        bool bHasAPP14Adobe = false;
23✔
5349
        GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
23✔
5350
        int nNumComponents = 0;
23✔
5351
        while (true)
5352
        {
5353
            const auto nCurPos = VSIFTellL(fp);
171✔
5354
            if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
171✔
5355
                break;
×
5356
            if (abyMarkerHeader[0] != 0xFF)
171✔
5357
                break;
×
5358
            const GByte markerType = abyMarkerHeader[1];
171✔
5359
            const size_t nMarkerSize =
171✔
5360
                abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
171✔
5361
            if (nMarkerSize < 2)
171✔
5362
                break;
×
5363
            if (markerType >= 0xC0 && markerType <= 0xCF &&
171✔
5364
                markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
23✔
5365
            {
5366
                switch (markerType)
23✔
5367
                {
5368
                    case 0xC0:
21✔
5369
                        osRet += ";frame_type=SOF0_baseline";
21✔
5370
                        break;
21✔
5371
                    case 0xC1:
2✔
5372
                        osRet += ";frame_type=SOF1_extended_sequential";
2✔
5373
                        break;
2✔
5374
                    case 0xC2:
×
5375
                        osRet += ";frame_type=SOF2_progressive_huffman";
×
5376
                        break;
×
5377
                    case 0xC3:
×
5378
                        osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
5379
                                 "supported=no";
×
5380
                        break;
×
5381
                    case 0xC5:
×
5382
                        osRet += ";frame_type="
5383
                                 "SOF5_differential_sequential_huffman;"
5384
                                 "libjpeg_supported=no";
×
5385
                        break;
×
5386
                    case 0xC6:
×
5387
                        osRet += ";frame_type=SOF6_differential_progressive_"
5388
                                 "huffman;libjpeg_supported=no";
×
5389
                        break;
×
5390
                    case 0xC7:
×
5391
                        osRet += ";frame_type="
5392
                                 "SOF7_differential_lossless_huffman;"
5393
                                 "libjpeg_supported=no";
×
5394
                        break;
×
5395
                    case 0xC9:
×
5396
                        osRet += ";frame_type="
5397
                                 "SOF9_extended_sequential_arithmetic";
×
5398
                        break;
×
5399
                    case 0xCA:
×
5400
                        osRet += ";frame_type=SOF10_progressive_arithmetic";
×
5401
                        break;
×
5402
                    case 0xCB:
×
5403
                        osRet += ";frame_type="
5404
                                 "SOF11_lossless_arithmetic;libjpeg_"
5405
                                 "supported=no";
×
5406
                        break;
×
5407
                    case 0xCD:
×
5408
                        osRet += ";frame_type=SOF13_differential_sequential_"
5409
                                 "arithmetic;libjpeg_supported=no";
×
5410
                        break;
×
5411
                    case 0xCE:
×
5412
                        osRet += ";frame_type=SOF14_differential_progressive_"
5413
                                 "arithmetic;libjpeg_supported=no";
×
5414
                        break;
×
5415
                    case 0xCF:
×
5416
                        osRet += ";frame_type=SOF15_differential_lossless_"
5417
                                 "arithmetic;libjpeg_supported=no";
×
5418
                        break;
×
5419
                    default:
×
5420
                        break;
×
5421
                }
5422
                GByte abySegmentBegin[6];
5423
                if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
23✔
5424
                              fp) != 1)
23✔
5425
                    break;
×
5426
                osRet += ";bit_depth=";
23✔
5427
                osRet += CPLSPrintf("%d", abySegmentBegin[0]);
23✔
5428
                nNumComponents = abySegmentBegin[5];
23✔
5429
                osRet += ";num_components=";
23✔
5430
                osRet += CPLSPrintf("%d", nNumComponents);
23✔
5431
                if (nNumComponents == 3)
23✔
5432
                {
5433
                    GByte abySegmentNext[3 * 3];
5434
                    if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
13✔
5435
                                  fp) != 1)
13✔
5436
                        break;
×
5437
                    if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
13✔
5438
                        abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
×
5439
                        abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
×
5440
                    {
5441
                        // no subsampling
5442
                        osRet += ";subsampling=4:4:4";
×
5443
                    }
5444
                    else if (abySegmentNext[0] == 1 &&
13✔
5445
                             abySegmentNext[1] == 0x22 &&
11✔
5446
                             abySegmentNext[3] == 2 &&
11✔
5447
                             abySegmentNext[4] == 0x11 &&
11✔
5448
                             abySegmentNext[6] == 3 &&
11✔
5449
                             abySegmentNext[7] == 0x11)
11✔
5450
                    {
5451
                        // classic subsampling
5452
                        osRet += ";subsampling=4:2:0";
11✔
5453
                    }
5454
                    else if (abySegmentNext[0] == 1 &&
2✔
5455
                             abySegmentNext[1] == 0x21 &&
×
5456
                             abySegmentNext[3] == 2 &&
×
5457
                             abySegmentNext[4] == 0x11 &&
×
5458
                             abySegmentNext[6] == 3 &&
×
5459
                             abySegmentNext[7] == 0x11)
×
5460
                    {
5461
                        osRet += ";subsampling=4:2:2";
×
5462
                    }
5463
                }
23✔
5464
            }
5465
            else if (markerType == 0xEE && nMarkerSize == 14)
148✔
5466
            {
5467
                if (VSIFReadL(abyAPP14AdobeMarkerData,
1✔
5468
                              sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
2✔
5469
                    memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
1✔
5470
                        0)
5471
                {
5472
                    bHasAPP14Adobe = true;
1✔
5473
                }
5474
            }
5475
            else if (markerType == 0xDA)
147✔
5476
            {
5477
                // Start of scan
5478
                break;
23✔
5479
            }
5480
            VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
148✔
5481
        }
148✔
5482
        std::string osColorspace;
46✔
5483
        if (bHasAPP14Adobe)
23✔
5484
        {
5485
            if (abyAPP14AdobeMarkerData[11] == 0)
1✔
5486
            {
5487
                if (nNumComponents == 3)
1✔
5488
                    osColorspace = "RGB";
×
5489
                else if (nNumComponents == 4)
1✔
5490
                    osColorspace = "CMYK";
1✔
5491
            }
5492
            else if (abyAPP14AdobeMarkerData[11] == 1)
×
5493
            {
5494
                osColorspace = "YCbCr";
×
5495
            }
5496
            else if (abyAPP14AdobeMarkerData[11] == 2)
×
5497
            {
5498
                osColorspace = "YCCK";
×
5499
            }
5500
        }
5501
        else
5502
        {
5503
            if (nNumComponents == 3)
22✔
5504
                osColorspace = "YCbCr";
13✔
5505
            else if (nNumComponents == 4)
9✔
5506
                osColorspace = "CMYK";
1✔
5507
        }
5508
        osRet += ";colorspace=";
23✔
5509
        if (!osColorspace.empty())
23✔
5510
            osRet += osColorspace;
15✔
5511
        else
5512
            osRet += "unknown";
8✔
5513
    }
5514
    if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
23✔
5515
    {
5516
        CPLError(CE_Failure, CPLE_AppDefined,
×
5517
                 "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
5518
    }
5519
    return osRet;
46✔
5520
}
5521

5522
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
16✔
5523
                                            size_t nBufferSize)
5524
{
5525
    VSILFILE *fp = VSIFileFromMemBuffer(
16✔
5526
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5527
        false);
5528
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
16✔
5529
    VSIFCloseL(fp);
16✔
5530
    return osRet;
16✔
5531
}
5532

5533
//! @endcond
5534

5535
/************************************************************************/
5536
/*                      GDALGetNoDataReplacementValue()                 */
5537
/************************************************************************/
5538

5539
/**
5540
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5541
 *        is out of range for the specified data type (dt).
5542
 *        For UInt64 and Int64 data type this function cannot reliably trusted
5543
 *        because their nodata values might not always be representable exactly
5544
 *        as a double, in particular the maximum absolute value for those types
5545
 *        is 2^53.
5546
 *
5547
 * The replacement value is a value that can be used in a computation
5548
 * whose result would match by accident the nodata value, whereas it is
5549
 * meant to be valid. For example, for a dataset with a nodata value of 0,
5550
 * when averaging -1 and 1, one would get normally a value of 0. The
5551
 * replacement nodata value can then be substituted to that 0 value to still
5552
 * get a valid value, as close as practical to the true value, while being
5553
 * different from the nodata value.
5554
 *
5555
 * @param dt Data type
5556
 * @param dfNoDataValue The no data value
5557

5558
 * @since GDAL 3.9
5559
 */
5560
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
269✔
5561
{
5562

5563
    // The logic here is to check if the value is out of range for the
5564
    // specified data type and return a replacement value if it is, return
5565
    // 0 otherwise.
5566
    double dfReplacementVal = dfNoDataValue;
269✔
5567
    if (dt == GDT_Byte)
269✔
5568
    {
5569
        if (GDALClampDoubleValue(dfNoDataValue,
83✔
5570
                                 cpl::NumericLimits<uint8_t>::lowest(),
83✔
5571
                                 cpl::NumericLimits<uint8_t>::max()))
83✔
5572
        {
5573
            return 0;
2✔
5574
        }
5575
        if (dfNoDataValue == cpl::NumericLimits<unsigned char>::max())
81✔
5576
            dfReplacementVal = cpl::NumericLimits<unsigned char>::max() - 1;
3✔
5577
        else
5578
            dfReplacementVal = dfNoDataValue + 1;
78✔
5579
    }
5580
    else if (dt == GDT_Int8)
186✔
5581
    {
5582
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5583
                                 cpl::NumericLimits<int8_t>::lowest(),
5✔
5584
                                 cpl::NumericLimits<int8_t>::max()))
5✔
5585
        {
5586
            return 0;
2✔
5587
        }
5588
        if (dfNoDataValue == cpl::NumericLimits<GInt8>::max())
3✔
5589
            dfReplacementVal = cpl::NumericLimits<GInt8>::max() - 1;
1✔
5590
        else
5591
            dfReplacementVal = dfNoDataValue + 1;
2✔
5592
    }
5593
    else if (dt == GDT_UInt16)
181✔
5594
    {
5595
        if (GDALClampDoubleValue(dfNoDataValue,
6✔
5596
                                 cpl::NumericLimits<uint16_t>::lowest(),
6✔
5597
                                 cpl::NumericLimits<uint16_t>::max()))
6✔
5598
        {
5599
            return 0;
2✔
5600
        }
5601
        if (dfNoDataValue == cpl::NumericLimits<GUInt16>::max())
4✔
5602
            dfReplacementVal = cpl::NumericLimits<GUInt16>::max() - 1;
1✔
5603
        else
5604
            dfReplacementVal = dfNoDataValue + 1;
3✔
5605
    }
5606
    else if (dt == GDT_Int16)
175✔
5607
    {
5608
        if (GDALClampDoubleValue(dfNoDataValue,
19✔
5609
                                 cpl::NumericLimits<int16_t>::lowest(),
19✔
5610
                                 cpl::NumericLimits<int16_t>::max()))
19✔
5611
        {
5612
            return 0;
2✔
5613
        }
5614
        if (dfNoDataValue == cpl::NumericLimits<GInt16>::max())
17✔
5615
            dfReplacementVal = cpl::NumericLimits<GInt16>::max() - 1;
1✔
5616
        else
5617
            dfReplacementVal = dfNoDataValue + 1;
16✔
5618
    }
5619
    else if (dt == GDT_UInt32)
156✔
5620
    {
5621
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5622
                                 cpl::NumericLimits<uint32_t>::lowest(),
5623
                                 cpl::NumericLimits<uint32_t>::max()))
5624
        {
5625
            return 0;
2✔
5626
        }
5627
        if (dfNoDataValue == cpl::NumericLimits<GUInt32>::max())
3✔
5628
            dfReplacementVal = cpl::NumericLimits<GUInt32>::max() - 1;
1✔
5629
        else
5630
            dfReplacementVal = dfNoDataValue + 1;
2✔
5631
    }
5632
    else if (dt == GDT_Int32)
151✔
5633
    {
5634
        if (GDALClampDoubleValue(dfNoDataValue,
7✔
5635
                                 cpl::NumericLimits<int32_t>::lowest(),
5636
                                 cpl::NumericLimits<int32_t>::max()))
5637
        {
5638
            return 0;
2✔
5639
        }
5640
        if (dfNoDataValue == cpl::NumericLimits<int32_t>::max())
5✔
5641
            dfReplacementVal = cpl::NumericLimits<int32_t>::max() - 1;
1✔
5642
        else
5643
            dfReplacementVal = dfNoDataValue + 1;
4✔
5644
    }
5645
    else if (dt == GDT_UInt64)
144✔
5646
    {
5647
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5648
        // so we take the next lower value representable as a double 18446744073709549567
5649
        static const double dfMaxUInt64Value{
5650
            std::nextafter(
5651
                static_cast<double>(cpl::NumericLimits<uint64_t>::max()), 0) -
5652
            1};
5653

5654
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5655
                                 cpl::NumericLimits<uint64_t>::lowest(),
5656
                                 cpl::NumericLimits<uint64_t>::max()))
5657
        {
5658
            return 0;
2✔
5659
        }
5660

5661
        if (dfNoDataValue >=
3✔
5662
            static_cast<double>(cpl::NumericLimits<uint64_t>::max()))
3✔
5663
            dfReplacementVal = dfMaxUInt64Value;
1✔
5664
        else
5665
            dfReplacementVal = dfNoDataValue + 1;
2✔
5666
    }
5667
    else if (dt == GDT_Int64)
139✔
5668
    {
5669
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5670
        // so we take the next lower value representable as a double 9223372036854774784
5671
        static const double dfMaxInt64Value{
5672
            std::nextafter(
5673
                static_cast<double>(cpl::NumericLimits<int64_t>::max()), 0) -
5674
            1};
5675

5676
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5677
                                 cpl::NumericLimits<int64_t>::lowest(),
5678
                                 cpl::NumericLimits<int64_t>::max()))
5679
        {
5680
            return 0;
2✔
5681
        }
5682

5683
        if (dfNoDataValue >=
3✔
5684
            static_cast<double>(cpl::NumericLimits<int64_t>::max()))
3✔
5685
            dfReplacementVal = dfMaxInt64Value;
1✔
5686
        else
5687
            dfReplacementVal = dfNoDataValue + 1;
2✔
5688
    }
5689
    else if (dt == GDT_Float16)
134✔
5690
    {
5691

5692
        if (GDALClampDoubleValue(dfNoDataValue,
8✔
5693
                                 cpl::NumericLimits<GFloat16>::lowest(),
5694
                                 cpl::NumericLimits<GFloat16>::max()))
5695
        {
5696
            return 0;
4✔
5697
        }
5698

5699
        if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max())
4✔
5700
        {
5701
            using std::nextafter;
5702
            dfReplacementVal =
5703
                nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f));
1✔
5704
        }
5705
        else
5706
        {
5707
            using std::nextafter;
5708
            dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue),
×
5709
                                         cpl::NumericLimits<GFloat16>::max());
3✔
5710
        }
5711
    }
5712
    else if (dt == GDT_Float32)
126✔
5713
    {
5714

5715
        if (GDALClampDoubleValue(dfNoDataValue,
33✔
5716
                                 cpl::NumericLimits<float>::lowest(),
5717
                                 cpl::NumericLimits<float>::max()))
5718
        {
5719
            return 0;
4✔
5720
        }
5721

5722
        if (dfNoDataValue == cpl::NumericLimits<float>::max())
29✔
5723
        {
5724
            dfReplacementVal =
1✔
5725
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
1✔
5726
        }
5727
        else
5728
        {
5729
            dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue),
28✔
5730
                                              cpl::NumericLimits<float>::max());
5731
        }
5732
    }
5733
    else if (dt == GDT_Float64)
93✔
5734
    {
5735
        if (GDALClampDoubleValue(dfNoDataValue,
93✔
5736
                                 cpl::NumericLimits<double>::lowest(),
5737
                                 cpl::NumericLimits<double>::max()))
5738
        {
5739
            return 0;
2✔
5740
        }
5741

5742
        if (dfNoDataValue == cpl::NumericLimits<double>::max())
91✔
5743
        {
5744
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0);
2✔
5745
        }
5746
        else
5747
        {
5748
            dfReplacementVal = std::nextafter(
89✔
5749
                dfNoDataValue, cpl::NumericLimits<double>::max());
5750
        }
5751
    }
5752

5753
    return dfReplacementVal;
243✔
5754
}
5755

5756
/************************************************************************/
5757
/*                        GDALGetCacheDirectory()                       */
5758
/************************************************************************/
5759

5760
/** Return the root path of the GDAL cache.
5761
 *
5762
 * If the GDAL_CACHE_DIRECTORY configuration option is set, its value will
5763
 * be returned.
5764
 * Otherwise if the XDG_CACHE_HOME environment variable is set,
5765
 * ${XDG_CACHE_HOME}/.gdal will be returned.
5766
 * Otherwise ${HOME}/.gdal on Unix or$ ${USERPROFILE}/.gdal on Windows will
5767
 * be returned.
5768
 * Otherwise ${CPL_TMPDIR|TMPDIR|TEMP}/.gdal_${USERNAME|USER} will be returned.
5769
 * Otherwise empty string will be returned.
5770
 *
5771
 * @since GDAL 3.11
5772
 */
5773
std::string GDALGetCacheDirectory()
315✔
5774
{
5775
    if (const char *pszGDAL_CACHE_DIRECTORY =
315✔
5776
            CPLGetConfigOption("GDAL_CACHE_DIRECTORY", nullptr))
315✔
5777
    {
5778
        return pszGDAL_CACHE_DIRECTORY;
×
5779
    }
5780

5781
    if (const char *pszXDG_CACHE_HOME =
315✔
5782
            CPLGetConfigOption("XDG_CACHE_HOME", nullptr))
315✔
5783
    {
5784
        return CPLFormFilenameSafe(pszXDG_CACHE_HOME, "gdal", nullptr);
×
5785
    }
5786

5787
#ifdef _WIN32
5788
    const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
5789
#else
5790
    const char *pszHome = CPLGetConfigOption("HOME", nullptr);
315✔
5791
#endif
5792
    if (pszHome != nullptr)
315✔
5793
    {
5794
        return CPLFormFilenameSafe(pszHome, ".gdal", nullptr);
315✔
5795
    }
5796
    else
5797
    {
5798
        const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
×
5799

5800
        if (pszDir == nullptr)
×
5801
            pszDir = CPLGetConfigOption("TMPDIR", nullptr);
×
5802

5803
        if (pszDir == nullptr)
×
5804
            pszDir = CPLGetConfigOption("TEMP", nullptr);
×
5805

5806
        const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
×
5807
        if (pszUsername == nullptr)
×
5808
            pszUsername = CPLGetConfigOption("USER", nullptr);
×
5809

5810
        if (pszDir != nullptr && pszUsername != nullptr)
×
5811
        {
5812
            return CPLFormFilenameSafe(
5813
                pszDir, CPLSPrintf(".gdal_%s", pszUsername), nullptr);
×
5814
        }
5815
    }
5816
    return std::string();
×
5817
}
5818

5819
/************************************************************************/
5820
/*                      GDALDoesFileOrDatasetExist()                    */
5821
/************************************************************************/
5822

5823
/** Return whether a file already exists.
5824
 */
5825
bool GDALDoesFileOrDatasetExist(const char *pszName, const char **ppszType,
700✔
5826
                                GDALDriver **ppDriver)
5827
{
5828
    {
5829
        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
700✔
5830
        GDALDriverH hDriver = GDALIdentifyDriver(pszName, nullptr);
700✔
5831
        if (hDriver)
700✔
5832
        {
5833
            if (ppszType)
58✔
5834
                *ppszType = "Dataset";
58✔
5835
            if (ppDriver)
58✔
5836
                *ppDriver = GDALDriver::FromHandle(hDriver);
57✔
5837
            return true;
58✔
5838
        }
5839
    }
5840

5841
    VSIStatBufL sStat;
5842
    if (VSIStatL(pszName, &sStat) == 0)
642✔
5843
    {
5844
        if (ppszType)
4✔
5845
            *ppszType = VSI_ISDIR(sStat.st_mode) ? "Directory" : "File";
4✔
5846
        return true;
4✔
5847
    }
5848

5849
    return false;
638✔
5850
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc