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

OSGeo / gdal / 12706066811

10 Jan 2025 08:38AM UTC coverage: 70.084% (-2.5%) from 72.549%
12706066811

Pull #11629

github

web-flow
Merge 9418dc48f into 0df468c56
Pull Request #11629: add uv documentation for python package

563296 of 803749 relevant lines covered (70.08%)

223434.74 hits per line

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

77.05
/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_json.h"
34
#include "cpl_minixml.h"
35
#include "cpl_multiproc.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
#ifdef EMBED_RESOURCE_FILES
39
#include "embedded_resources.h"
40
#endif
41
#include "gdal_version_full/gdal_version.h"
42
#include "gdal.h"
43
#include "gdal_mdreader.h"
44
#include "gdal_priv.h"
45
#include "gdal_priv_templates.hpp"
46
#include "ogr_core.h"
47
#include "ogr_spatialref.h"
48
#include "ogr_geos.h"
49

50
#include "proj.h"
51

52
#ifdef HAVE_CURL
53
#include "cpl_curl_priv.h"
54
#endif
55

56
static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[],
3,314✔
57
                             const int panBits[])
58
{
59
    if (pabFloating[0] != pabFloating[1])
3,314✔
60
    {
61
        const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
376✔
62
        const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
376✔
63

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

68
    if (pabSigned[0] != pabSigned[1])
2,938✔
69
    {
70
        const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
438✔
71
        const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
438✔
72

73
        return std::max(panBits[nSignedTypeIndex],
438✔
74
                        2 * panBits[nUnsignedTypeIndex]);
438✔
75
    }
76

77
    return std::max(panBits[0], panBits[1]);
2,500✔
78
}
79

80
static int GetDataTypeElementSizeBits(GDALDataType eDataType)
6,628✔
81
{
82
    switch (eDataType)
6,628✔
83
    {
84
        case GDT_Byte:
4,247✔
85
        case GDT_Int8:
86
            return 8;
4,247✔
87

88
        case GDT_UInt16:
1,113✔
89
        case GDT_Int16:
90
        case GDT_CInt16:
91
            return 16;
1,113✔
92

93
        case GDT_UInt32:
870✔
94
        case GDT_Int32:
95
        case GDT_Float32:
96
        case GDT_CInt32:
97
        case GDT_CFloat32:
98
            return 32;
870✔
99

100
        case GDT_Float64:
398✔
101
        case GDT_CFloat64:
102
        case GDT_UInt64:
103
        case GDT_Int64:
104
            return 64;
398✔
105

106
        case GDT_Unknown:
×
107
        case GDT_TypeCount:
108
            break;
×
109
    }
110
    return 0;
×
111
}
112

113
/************************************************************************/
114
/*                         GDALDataTypeUnion()                          */
115
/************************************************************************/
116

117
/**
118
 * \brief Return the smallest data type that can fully express both input data
119
 * types.
120
 *
121
 * @param eType1 first data type.
122
 * @param eType2 second data type.
123
 *
124
 * @return a data type able to express eType1 and eType2.
125
 */
126

127
GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
3,315✔
128
                                           GDALDataType eType2)
129

130
{
131
    if (eType1 == GDT_Unknown)
3,315✔
132
        return eType2;
1✔
133
    if (eType2 == GDT_Unknown)
3,314✔
134
        return eType1;
×
135

136
    const int panBits[] = {GetDataTypeElementSizeBits(eType1),
3,314✔
137
                           GetDataTypeElementSizeBits(eType2)};
3,314✔
138

139
    if (panBits[0] == 0 || panBits[1] == 0)
3,314✔
140
        return GDT_Unknown;
×
141

142
    const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
3,314✔
143
                              CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
3,314✔
144

145
    const bool bSigned = pabSigned[0] || pabSigned[1];
3,314✔
146
    const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
3,314✔
147
                                CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
3,314✔
148
    const bool bFloating = pabFloating[0] || pabFloating[1];
3,314✔
149
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
6,253✔
150
                          CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
2,939✔
151

152
    const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
3,314✔
153

154
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
3,314✔
155
}
156

157
/************************************************************************/
158
/*                        GDALDataTypeUnionWithValue()                  */
159
/************************************************************************/
160

161
/**
162
 * \brief Union a data type with the one found for a value
163
 *
164
 * @param eDT the first data type
165
 * @param dfValue the value for which to find a data type and union with eDT
166
 * @param bComplex if the value is complex
167
 *
168
 * @return a data type able to express eDT and dfValue.
169
 * @since GDAL 2.3
170
 */
171
GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
304✔
172
                                                    double dfValue,
173
                                                    int bComplex)
174
{
175
    if (!bComplex && !GDALDataTypeIsComplex(eDT))
304✔
176
    {
177
        switch (eDT)
298✔
178
        {
179
            case GDT_Byte:
158✔
180
            {
181
                if (GDALIsValueExactAs<uint8_t>(dfValue))
158✔
182
                    return eDT;
147✔
183
                break;
11✔
184
            }
185
            case GDT_Int8:
2✔
186
            {
187
                if (GDALIsValueExactAs<int8_t>(dfValue))
2✔
188
                    return eDT;
1✔
189
                break;
1✔
190
            }
191
            case GDT_UInt16:
6✔
192
            {
193
                if (GDALIsValueExactAs<uint16_t>(dfValue))
6✔
194
                    return eDT;
5✔
195
                break;
1✔
196
            }
197
            case GDT_Int16:
40✔
198
            {
199
                if (GDALIsValueExactAs<int16_t>(dfValue))
40✔
200
                    return eDT;
39✔
201
                break;
1✔
202
            }
203
            case GDT_UInt32:
2✔
204
            {
205
                if (GDALIsValueExactAs<uint32_t>(dfValue))
2✔
206
                    return eDT;
1✔
207
                break;
1✔
208
            }
209
            case GDT_Int32:
20✔
210
            {
211
                if (GDALIsValueExactAs<int32_t>(dfValue))
20✔
212
                    return eDT;
19✔
213
                break;
1✔
214
            }
215
            case GDT_UInt64:
2✔
216
            {
217
                if (GDALIsValueExactAs<uint64_t>(dfValue))
2✔
218
                    return eDT;
1✔
219
                break;
1✔
220
            }
221
            case GDT_Int64:
2✔
222
            {
223
                if (GDALIsValueExactAs<int64_t>(dfValue))
2✔
224
                    return eDT;
1✔
225
                break;
1✔
226
            }
227
            case GDT_Float32:
55✔
228
            {
229
                if (GDALIsValueExactAs<float>(dfValue))
55✔
230
                    return eDT;
54✔
231
                break;
1✔
232
            }
233
            case GDT_Float64:
10✔
234
            {
235
                return eDT;
10✔
236
            }
237
            case GDT_Unknown:
1✔
238
            case GDT_CInt16:
239
            case GDT_CInt32:
240
            case GDT_CFloat32:
241
            case GDT_CFloat64:
242
            case GDT_TypeCount:
243
                break;
1✔
244
        }
245
    }
246

247
    const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex);
26✔
248
    return GDALDataTypeUnion(eDT, eDT2);
26✔
249
}
250

251
/************************************************************************/
252
/*                        GetMinBitsForValue()                          */
253
/************************************************************************/
254
static int GetMinBitsForValue(double dValue)
26✔
255
{
256
    if (round(dValue) == dValue)
26✔
257
    {
258
        if (dValue <= std::numeric_limits<GByte>::max() &&
26✔
259
            dValue >= std::numeric_limits<GByte>::min())
9✔
260
            return 8;
4✔
261

262
        if (dValue <= std::numeric_limits<GInt8>::max() &&
18✔
263
            dValue >= std::numeric_limits<GInt8>::min())
5✔
264
            return 8;
3✔
265

266
        if (dValue <= std::numeric_limits<GInt16>::max() &&
14✔
267
            dValue >= std::numeric_limits<GInt16>::min())
4✔
268
            return 16;
3✔
269

270
        if (dValue <= std::numeric_limits<GUInt16>::max() &&
9✔
271
            dValue >= std::numeric_limits<GUInt16>::min())
2✔
272
            return 16;
1✔
273

274
        if (dValue <= std::numeric_limits<GInt32>::max() &&
8✔
275
            dValue >= std::numeric_limits<GInt32>::min())
2✔
276
            return 32;
2✔
277

278
        if (dValue <= std::numeric_limits<GUInt32>::max() &&
5✔
279
            dValue >= std::numeric_limits<GUInt32>::min())
1✔
280
            return 32;
1✔
281

282
        if (dValue <= static_cast<double>(
6✔
283
                          std::numeric_limits<std::uint64_t>::max()) &&
5✔
284
            dValue >=
285
                static_cast<double>(std::numeric_limits<std::uint64_t>::min()))
2✔
286
            return 64;
2✔
287
    }
288
    else if (static_cast<float>(dValue) == dValue)
9✔
289
    {
290
        return 32;
4✔
291
    }
292

293
    return 64;
6✔
294
}
295

296
/************************************************************************/
297
/*                        GDALFindDataType()                            */
298
/************************************************************************/
299

300
/**
301
 * \brief Finds the smallest data type able to support the given
302
 *  requirements
303
 *
304
 * @param nBits number of bits necessary
305
 * @param bSigned if negative values are necessary
306
 * @param bFloating if non-integer values necessary
307
 * @param bComplex if complex values are necessary
308
 *
309
 * @return a best fit GDALDataType for supporting the requirements
310
 * @since GDAL 2.3
311
 */
312
GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
3,358✔
313
                                          int bComplex)
314
{
315
    if (bComplex)
3,358✔
316
    {
317
        nBits = std::max(nBits, !bSigned ? 32 : 16);
723✔
318
    }  // we don't have complex unsigned data types, so for a complex uint16,
319
       // promote to complex int32
320
    if (bFloating)
3,358✔
321
    {
322
        nBits = std::max(nBits, 32);
547✔
323
    }
324

325
    if (nBits <= 8)
3,358✔
326
    {
327
        return bSigned ? GDT_Int8 : GDT_Byte;
1,790✔
328
    }
329

330
    if (nBits <= 16)
1,568✔
331
    {
332
        if (bComplex)
661✔
333
            return GDT_CInt16;
358✔
334
        if (bSigned)
303✔
335
            return GDT_Int16;
186✔
336
        return GDT_UInt16;
117✔
337
    }
338

339
    if (nBits <= 32)
907✔
340
    {
341
        if (bFloating)
515✔
342
        {
343
            if (bComplex)
261✔
344
                return GDT_CFloat32;
95✔
345
            return GDT_Float32;
166✔
346
        }
347

348
        if (bComplex)
254✔
349
            return GDT_CInt32;
93✔
350
        if (bSigned)
161✔
351
            return GDT_Int32;
99✔
352
        return GDT_UInt32;
62✔
353
    }
354

355
    if (nBits == 64 && !bFloating && !bComplex)
392✔
356
        return bSigned ? GDT_Int64 : GDT_UInt64;
61✔
357

358
    if (bComplex)
331✔
359
        return GDT_CFloat64;
177✔
360

361
    return GDT_Float64;
154✔
362
}
363

364
/************************************************************************/
365
/*                        GDALFindDataTypeForValue()                    */
366
/************************************************************************/
367

368
/**
369
 * \brief Finds the smallest data type able to support the provided value
370
 *
371
 * @param dValue value to support
372
 * @param bComplex is the value complex
373
 *
374
 * @return a best fit GDALDataType for supporting the value
375
 * @since GDAL 2.3
376
 */
377
GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
26✔
378
{
379
    const bool bFloating =
380
        round(dValue) != dValue ||
43✔
381
        dValue >
382
            static_cast<double>(std::numeric_limits<std::uint64_t>::max()) ||
42✔
383
        dValue <
384
            static_cast<double>(std::numeric_limits<std::int64_t>::lowest());
16✔
385
    const bool bSigned = bFloating || dValue < 0;
26✔
386
    const int nBits = GetMinBitsForValue(dValue);
26✔
387

388
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
26✔
389
}
390

391
/************************************************************************/
392
/*                        GDALGetDataTypeSizeBytes()                    */
393
/************************************************************************/
394

395
/**
396
 * \brief Get data type size in <b>bytes</b>.
397
 *
398
 * Returns the size of a GDT_* type in bytes.  In contrast,
399
 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
400
 *
401
 * @param eDataType type, such as GDT_Byte.
402
 * @return the number of bytes or zero if it is not recognised.
403
 */
404

405
int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
243,454,000✔
406

407
{
408
    switch (eDataType)
243,454,000✔
409
    {
410
        case GDT_Byte:
76,777,200✔
411
        case GDT_Int8:
412
            return 1;
76,777,200✔
413

414
        case GDT_UInt16:
51,906,500✔
415
        case GDT_Int16:
416
            return 2;
51,906,500✔
417

418
        case GDT_UInt32:
77,539,400✔
419
        case GDT_Int32:
420
        case GDT_Float32:
421
        case GDT_CInt16:
422
            return 4;
77,539,400✔
423

424
        case GDT_Float64:
36,852,500✔
425
        case GDT_CInt32:
426
        case GDT_CFloat32:
427
        case GDT_UInt64:
428
        case GDT_Int64:
429
            return 8;
36,852,500✔
430

431
        case GDT_CFloat64:
381,470✔
432
            return 16;
381,470✔
433

434
        case GDT_Unknown:
15,714✔
435
        case GDT_TypeCount:
436
            break;
15,714✔
437
    }
438
    return 0;
×
439
}
440

441
/************************************************************************/
442
/*                        GDALGetDataTypeSizeBits()                     */
443
/************************************************************************/
444

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

455
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
10,672✔
456

457
{
458
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
10,672✔
459
}
460

461
/************************************************************************/
462
/*                        GDALGetDataTypeSize()                         */
463
/************************************************************************/
464

465
/**
466
 * \brief Get data type size in bits.  <b>Deprecated</b>.
467
 *
468
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
469
 *
470
 * Use GDALGetDataTypeSizeBytes() for bytes.
471
 * Use GDALGetDataTypeSizeBits() for bits.
472
 *
473
 * @param eDataType type, such as GDT_Byte.
474
 * @return the number of bits or zero if it is not recognised.
475
 */
476

477
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
3,214,290✔
478

479
{
480
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
3,214,290✔
481
}
482

483
/************************************************************************/
484
/*                       GDALDataTypeIsComplex()                        */
485
/************************************************************************/
486

487
/**
488
 * \brief Is data type complex?
489
 *
490
 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
491
 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
492
 * component.
493
 */
494

495
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
694,633✔
496

497
{
498
    switch (eDataType)
694,633✔
499
    {
500
        case GDT_CInt16:
7,454✔
501
        case GDT_CInt32:
502
        case GDT_CFloat32:
503
        case GDT_CFloat64:
504
            return TRUE;
7,454✔
505

506
        case GDT_Byte:
687,171✔
507
        case GDT_Int8:
508
        case GDT_Int16:
509
        case GDT_UInt16:
510
        case GDT_Int32:
511
        case GDT_UInt32:
512
        case GDT_Int64:
513
        case GDT_UInt64:
514
        case GDT_Float32:
515
        case GDT_Float64:
516
            return FALSE;
687,171✔
517

518
        case GDT_Unknown:
7✔
519
        case GDT_TypeCount:
520
            break;
7✔
521
    }
522
    return FALSE;
8✔
523
}
524

525
/************************************************************************/
526
/*                       GDALDataTypeIsFloating()                       */
527
/************************************************************************/
528

529
/**
530
 * \brief Is data type floating? (might be complex)
531
 *
532
 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float64,
533
 * GDT_CFloat32, GDT_CFloat64)
534
 * @since GDAL 2.3
535
 */
536

537
int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
57,647✔
538
{
539
    switch (eDataType)
57,647✔
540
    {
541
        case GDT_Float32:
5,644✔
542
        case GDT_Float64:
543
        case GDT_CFloat32:
544
        case GDT_CFloat64:
545
            return TRUE;
5,644✔
546

547
        case GDT_Byte:
52,001✔
548
        case GDT_Int8:
549
        case GDT_Int16:
550
        case GDT_UInt16:
551
        case GDT_Int32:
552
        case GDT_UInt32:
553
        case GDT_Int64:
554
        case GDT_UInt64:
555
        case GDT_CInt16:
556
        case GDT_CInt32:
557
            return FALSE;
52,001✔
558

559
        case GDT_Unknown:
1✔
560
        case GDT_TypeCount:
561
            break;
1✔
562
    }
563
    return FALSE;
2✔
564
}
565

566
/************************************************************************/
567
/*                       GDALDataTypeIsInteger()                        */
568
/************************************************************************/
569

570
/**
571
 * \brief Is data type integer? (might be complex)
572
 *
573
 * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
574
 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
575
 * @since GDAL 2.3
576
 */
577

578
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
56,190✔
579

580
{
581
    switch (eDataType)
56,190✔
582
    {
583
        case GDT_Byte:
54,574✔
584
        case GDT_Int8:
585
        case GDT_Int16:
586
        case GDT_UInt16:
587
        case GDT_Int32:
588
        case GDT_UInt32:
589
        case GDT_CInt16:
590
        case GDT_CInt32:
591
        case GDT_UInt64:
592
        case GDT_Int64:
593
            return TRUE;
54,574✔
594

595
        case GDT_Float32:
1,614✔
596
        case GDT_Float64:
597
        case GDT_CFloat32:
598
        case GDT_CFloat64:
599
            return FALSE;
1,614✔
600

601
        case GDT_Unknown:
1✔
602
        case GDT_TypeCount:
603
            break;
1✔
604
    }
605
    return FALSE;
2✔
606
}
607

608
/************************************************************************/
609
/*                       GDALDataTypeIsSigned()                         */
610
/************************************************************************/
611

612
/**
613
 * \brief Is data type signed?
614
 *
615
 * @return TRUE if the passed type is signed.
616
 * @since GDAL 2.3
617
 */
618

619
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
98,273✔
620
{
621
    switch (eDataType)
98,273✔
622
    {
623
        case GDT_Byte:
95,258✔
624
        case GDT_UInt16:
625
        case GDT_UInt32:
626
        case GDT_UInt64:
627
            return FALSE;
95,258✔
628

629
        case GDT_Int8:
3,014✔
630
        case GDT_Int16:
631
        case GDT_Int32:
632
        case GDT_Int64:
633
        case GDT_Float32:
634
        case GDT_Float64:
635
        case GDT_CInt16:
636
        case GDT_CInt32:
637
        case GDT_CFloat32:
638
        case GDT_CFloat64:
639
            return TRUE;
3,014✔
640

641
        case GDT_Unknown:
×
642
        case GDT_TypeCount:
643
            break;
×
644
    }
645
    return FALSE;
1✔
646
}
647

648
/************************************************************************/
649
/*                    GDALDataTypeIsConversionLossy()                   */
650
/************************************************************************/
651

652
/**
653
 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
654
 *
655
 * @param eTypeFrom input datatype
656
 * @param eTypeTo output datatype
657
 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
658
 * @since GDAL 2.3
659
 */
660

661
int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
51,440✔
662
                                              GDALDataType eTypeTo)
663
{
664
    // E.g cfloat32 -> float32
665
    if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
51,440✔
666
        return TRUE;
36✔
667

668
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
51,404✔
669
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
51,403✔
670

671
    if (GDALDataTypeIsInteger(eTypeTo))
51,403✔
672
    {
673
        // E.g. float32 -> int32
674
        if (GDALDataTypeIsFloating(eTypeFrom))
50,248✔
675
            return TRUE;
4,697✔
676

677
        // E.g. Int16 to UInt16
678
        const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
45,552✔
679
        const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
45,550✔
680
        if (bIsFromSigned && !bIsToSigned)
45,549✔
681
            return TRUE;
30✔
682

683
        // E.g UInt32 to UInt16
684
        const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
45,519✔
685
        const int nToSize = GDALGetDataTypeSize(eTypeTo);
45,521✔
686
        if (nFromSize > nToSize)
45,521✔
687
            return TRUE;
28✔
688

689
        // E.g UInt16 to Int16
690
        if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
45,493✔
691
            return TRUE;
9✔
692

693
        return FALSE;
45,484✔
694
    }
695

696
    if (eTypeTo == GDT_Float32 &&
1,155✔
697
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
366✔
698
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
362✔
699
         eTypeFrom == GDT_Float64))
700
    {
701
        return TRUE;
37✔
702
    }
703

704
    if (eTypeTo == GDT_Float64 &&
1,118✔
705
        (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
764✔
706
    {
707
        return TRUE;
4✔
708
    }
709

710
    return FALSE;
1,114✔
711
}
712

713
/************************************************************************/
714
/*                        GDALGetDataTypeName()                         */
715
/************************************************************************/
716

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

731
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
63,170✔
732

733
{
734
    switch (eDataType)
63,170✔
735
    {
736
        case GDT_Unknown:
4,935✔
737
            return "Unknown";
4,935✔
738

739
        case GDT_Byte:
28,277✔
740
            return "Byte";
28,277✔
741

742
        case GDT_Int8:
605✔
743
            return "Int8";
605✔
744

745
        case GDT_UInt16:
4,974✔
746
            return "UInt16";
4,974✔
747

748
        case GDT_Int16:
5,133✔
749
            return "Int16";
5,133✔
750

751
        case GDT_UInt32:
3,843✔
752
            return "UInt32";
3,843✔
753

754
        case GDT_Int32:
3,804✔
755
            return "Int32";
3,804✔
756

757
        case GDT_UInt64:
640✔
758
            return "UInt64";
640✔
759

760
        case GDT_Int64:
622✔
761
            return "Int64";
622✔
762

763
        case GDT_Float32:
3,949✔
764
            return "Float32";
3,949✔
765

766
        case GDT_Float64:
2,003✔
767
            return "Float64";
2,003✔
768

769
        case GDT_CInt16:
1,183✔
770
            return "CInt16";
1,183✔
771

772
        case GDT_CInt32:
1,107✔
773
            return "CInt32";
1,107✔
774

775
        case GDT_CFloat32:
1,128✔
776
            return "CFloat32";
1,128✔
777

778
        case GDT_CFloat64:
967✔
779
            return "CFloat64";
967✔
780

781
        case GDT_TypeCount:
×
782
            break;
×
783
    }
784
    return nullptr;
×
785
}
786

787
/************************************************************************/
788
/*                        GDALGetDataTypeByName()                       */
789
/************************************************************************/
790

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

802
GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
4,778✔
803

804
{
805
    VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
4,778✔
806

807
    for (int iType = 1; iType < GDT_TypeCount; iType++)
14,081✔
808
    {
809
        const auto eType = static_cast<GDALDataType>(iType);
14,049✔
810
        if (GDALGetDataTypeName(eType) != nullptr &&
28,098✔
811
            EQUAL(GDALGetDataTypeName(eType), pszName))
14,049✔
812
        {
813
            return eType;
4,746✔
814
        }
815
    }
816

817
    return GDT_Unknown;
32✔
818
}
819

820
/************************************************************************/
821
/*                      GDALAdjustValueToDataType()                     */
822
/************************************************************************/
823

824
template <class T>
825
static inline void ClampAndRound(double &dfValue, bool &bClamped,
144✔
826
                                 bool &bRounded)
827
{
828
    // TODO(schwehr): Rework this template.  ::min() versus ::lowest.
829

830
    if (dfValue < static_cast<double>(std::numeric_limits<T>::min()))
144✔
831
    {
832
        bClamped = true;
6✔
833
        dfValue = static_cast<double>(std::numeric_limits<T>::min());
6✔
834
    }
835
    else if (dfValue > static_cast<double>(std::numeric_limits<T>::max()))
138✔
836
    {
837
        bClamped = true;
16✔
838
        dfValue = static_cast<double>(std::numeric_limits<T>::max());
16✔
839
    }
840
    else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
122✔
841
    {
842
        bRounded = true;
8✔
843
        dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
8✔
844
    }
845
}
144✔
846

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

864
double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
198✔
865
                                 int *pbClamped, int *pbRounded)
866
{
867
    bool bClamped = false;
198✔
868
    bool bRounded = false;
198✔
869
    switch (eDT)
198✔
870
    {
871
        case GDT_Byte:
87✔
872
            ClampAndRound<GByte>(dfValue, bClamped, bRounded);
87✔
873
            break;
87✔
874
        case GDT_Int8:
6✔
875
            ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
6✔
876
            break;
6✔
877
        case GDT_Int16:
21✔
878
            ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
21✔
879
            break;
21✔
880
        case GDT_UInt16:
16✔
881
            ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
16✔
882
            break;
16✔
883
        case GDT_Int32:
4✔
884
            ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
4✔
885
            break;
4✔
886
        case GDT_UInt32:
5✔
887
            ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
5✔
888
            break;
5✔
889
        case GDT_Int64:
2✔
890
            ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
2✔
891
            break;
2✔
892
        case GDT_UInt64:
3✔
893
            ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
3✔
894
            break;
3✔
895
        case GDT_Float32:
43✔
896
        {
897
            if (!std::isfinite(dfValue))
43✔
898
                break;
6✔
899

900
            // TODO(schwehr): ::min() versus ::lowest.
901
            // Use ClampAndRound after it has been fixed.
902
            if (dfValue < -std::numeric_limits<float>::max())
37✔
903
            {
904
                bClamped = TRUE;
1✔
905
                dfValue =
1✔
906
                    static_cast<double>(-std::numeric_limits<float>::max());
1✔
907
            }
908
            else if (dfValue > std::numeric_limits<float>::max())
36✔
909
            {
910
                bClamped = TRUE;
1✔
911
                dfValue =
1✔
912
                    static_cast<double>(std::numeric_limits<float>::max());
1✔
913
            }
914
            else
915
            {
916
                // Intentionally loose precision.
917
                // TODO(schwehr): Is the double cast really necessary?
918
                // If so, why?  What will fail?
919
                dfValue = static_cast<double>(static_cast<float>(dfValue));
35✔
920
            }
921
            break;
37✔
922
        }
923
        case GDT_Float64:
11✔
924
        case GDT_CInt16:
925
        case GDT_CInt32:
926
        case GDT_CFloat32:
927
        case GDT_CFloat64:
928
        case GDT_Unknown:
929
        case GDT_TypeCount:
930
            break;
11✔
931
    }
932
    if (pbClamped)
198✔
933
        *pbClamped = bClamped;
197✔
934
    if (pbRounded)
198✔
935
        *pbRounded = bRounded;
197✔
936
    return dfValue;
198✔
937
}
938

939
/************************************************************************/
940
/*                         GDALIsValueExactAs()                         */
941
/************************************************************************/
942

943
/**
944
 * \brief Check whether the provided value can be exactly represented in a
945
 * data type.
946
 *
947
 * Only implemented for non-complex data types
948
 *
949
 * @param dfValue value to check.
950
 * @param eDT target data type.
951
 *
952
 * @return true if the provided value can be exactly represented in the
953
 * data type.
954
 * @since GDAL 3.10
955
 */
956
bool GDALIsValueExactAs(double dfValue, GDALDataType eDT)
249✔
957
{
958
    switch (eDT)
249✔
959
    {
960
        case GDT_Byte:
137✔
961
            return GDALIsValueExactAs<uint8_t>(dfValue);
137✔
962
        case GDT_Int8:
5✔
963
            return GDALIsValueExactAs<int8_t>(dfValue);
5✔
964
        case GDT_UInt16:
18✔
965
            return GDALIsValueExactAs<uint16_t>(dfValue);
18✔
966
        case GDT_Int16:
29✔
967
            return GDALIsValueExactAs<int16_t>(dfValue);
29✔
968
        case GDT_UInt32:
5✔
969
            return GDALIsValueExactAs<uint32_t>(dfValue);
5✔
970
        case GDT_Int32:
14✔
971
            return GDALIsValueExactAs<int32_t>(dfValue);
14✔
972
        case GDT_UInt64:
5✔
973
            return GDALIsValueExactAs<uint64_t>(dfValue);
5✔
974
        case GDT_Int64:
5✔
975
            return GDALIsValueExactAs<int64_t>(dfValue);
5✔
976
        case GDT_Float32:
22✔
977
            return GDALIsValueExactAs<float>(dfValue);
22✔
978
        case GDT_Float64:
8✔
979
            return true;
8✔
980
        case GDT_Unknown:
1✔
981
        case GDT_CInt16:
982
        case GDT_CInt32:
983
        case GDT_CFloat32:
984
        case GDT_CFloat64:
985
        case GDT_TypeCount:
986
            break;
1✔
987
    }
988
    return true;
1✔
989
}
990

991
/************************************************************************/
992
/*                        GDALGetNonComplexDataType()                */
993
/************************************************************************/
994
/**
995
 * \brief Return the base data type for the specified input.
996
 *
997
 * If the input data type is complex this function returns the base type
998
 * i.e. the data type of the real and imaginary parts (non-complex).
999
 * If the input data type is already non-complex, then it is returned
1000
 * unchanged.
1001
 *
1002
 * @param eDataType type, such as GDT_CFloat32.
1003
 *
1004
 * @return GDAL data type.
1005
 */
1006
GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
102,875✔
1007
{
1008
    switch (eDataType)
102,875✔
1009
    {
1010
        case GDT_CInt16:
91✔
1011
            return GDT_Int16;
91✔
1012
        case GDT_CInt32:
23✔
1013
            return GDT_Int32;
23✔
1014
        case GDT_CFloat32:
64✔
1015
            return GDT_Float32;
64✔
1016
        case GDT_CFloat64:
60✔
1017
            return GDT_Float64;
60✔
1018

1019
        case GDT_Byte:
102,637✔
1020
        case GDT_UInt16:
1021
        case GDT_UInt32:
1022
        case GDT_UInt64:
1023
        case GDT_Int8:
1024
        case GDT_Int16:
1025
        case GDT_Int32:
1026
        case GDT_Int64:
1027
        case GDT_Float32:
1028
        case GDT_Float64:
1029
            break;
102,637✔
1030

1031
        case GDT_Unknown:
×
1032
        case GDT_TypeCount:
1033
            break;
×
1034
    }
1035
    return eDataType;
102,637✔
1036
}
1037

1038
/************************************************************************/
1039
/*                        GDALGetAsyncStatusTypeByName()                */
1040
/************************************************************************/
1041
/**
1042
 * Get AsyncStatusType by symbolic name.
1043
 *
1044
 * Returns a data type corresponding to the given symbolic name. This
1045
 * function is opposite to the GDALGetAsyncStatusTypeName().
1046
 *
1047
 * @param pszName string containing the symbolic name of the type.
1048
 *
1049
 * @return GDAL AsyncStatus type.
1050
 */
1051
GDALAsyncStatusType CPL_DLL CPL_STDCALL
1052
GDALGetAsyncStatusTypeByName(const char *pszName)
×
1053
{
1054
    VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
×
1055

1056
    for (int iType = 0; iType < GARIO_TypeCount; iType++)
×
1057
    {
1058
        const auto eType = static_cast<GDALAsyncStatusType>(iType);
×
1059
        if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
×
1060
            EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
×
1061
        {
1062
            return eType;
×
1063
        }
1064
    }
1065

1066
    return GARIO_ERROR;
×
1067
}
1068

1069
/************************************************************************/
1070
/*                        GDALGetAsyncStatusTypeName()                 */
1071
/************************************************************************/
1072

1073
/**
1074
 * Get name of AsyncStatus data type.
1075
 *
1076
 * Returns a symbolic name for the AsyncStatus data type.  This is essentially
1077
 * the enumerated item name with the GARIO_ prefix removed.  So
1078
 * GARIO_COMPLETE returns "COMPLETE".  The returned strings are static strings
1079
 * and should not be modified or freed by the application.  These strings are
1080
 * useful for reporting datatypes in debug statements, errors and other user
1081
 * output.
1082
 *
1083
 * @param eAsyncStatusType type to get name of.
1084
 * @return string corresponding to type.
1085
 */
1086

1087
const char *CPL_STDCALL
1088
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
×
1089

1090
{
1091
    switch (eAsyncStatusType)
×
1092
    {
1093
        case GARIO_PENDING:
×
1094
            return "PENDING";
×
1095

1096
        case GARIO_UPDATE:
×
1097
            return "UPDATE";
×
1098

1099
        case GARIO_ERROR:
×
1100
            return "ERROR";
×
1101

1102
        case GARIO_COMPLETE:
×
1103
            return "COMPLETE";
×
1104

1105
        default:
×
1106
            return nullptr;
×
1107
    }
1108
}
1109

1110
/************************************************************************/
1111
/*                  GDALGetPaletteInterpretationName()                  */
1112
/************************************************************************/
1113

1114
/**
1115
 * \brief Get name of palette interpretation
1116
 *
1117
 * Returns a symbolic name for the palette interpretation.  This is the
1118
 * the enumerated item name with the GPI_ prefix removed.  So GPI_Gray returns
1119
 * "Gray".  The returned strings are static strings and should not be modified
1120
 * or freed by the application.
1121
 *
1122
 * @param eInterp palette interpretation to get name of.
1123
 * @return string corresponding to palette interpretation.
1124
 */
1125

1126
const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
9✔
1127

1128
{
1129
    switch (eInterp)
9✔
1130
    {
1131
        case GPI_Gray:
×
1132
            return "Gray";
×
1133

1134
        case GPI_RGB:
9✔
1135
            return "RGB";
9✔
1136

1137
        case GPI_CMYK:
×
1138
            return "CMYK";
×
1139

1140
        case GPI_HLS:
×
1141
            return "HLS";
×
1142

1143
        default:
×
1144
            return "Unknown";
×
1145
    }
1146
}
1147

1148
/************************************************************************/
1149
/*                   GDALGetColorInterpretationName()                   */
1150
/************************************************************************/
1151

1152
/**
1153
 * \brief Get name of color interpretation
1154
 *
1155
 * Returns a symbolic name for the color interpretation.  This is derived from
1156
 * the enumerated item name with the GCI_ prefix removed, but there are some
1157
 * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
1158
 * The returned strings are static strings and should not be modified
1159
 * or freed by the application.
1160
 *
1161
 * @param eInterp color interpretation to get name of.
1162
 * @return string corresponding to color interpretation
1163
 *         or NULL pointer if invalid enumerator given.
1164
 */
1165

1166
const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
9,841✔
1167

1168
{
1169
    static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1);
1170
    static_assert(GCI_NIRBand == GCI_IR_Start);
1171
    static_assert(GCI_SAR_Start == GCI_IR_End + 1);
1172
    static_assert(GCI_Max == GCI_SAR_End);
1173

1174
    switch (eInterp)
9,841✔
1175
    {
1176
        case GCI_Undefined:
2,075✔
1177
            break;
2,075✔
1178

1179
        case GCI_GrayIndex:
2,473✔
1180
            return "Gray";
2,473✔
1181

1182
        case GCI_PaletteIndex:
1,063✔
1183
            return "Palette";
1,063✔
1184

1185
        case GCI_RedBand:
1,178✔
1186
            return "Red";
1,178✔
1187

1188
        case GCI_GreenBand:
888✔
1189
            return "Green";
888✔
1190

1191
        case GCI_BlueBand:
615✔
1192
            return "Blue";
615✔
1193

1194
        case GCI_AlphaBand:
331✔
1195
            return "Alpha";
331✔
1196

1197
        case GCI_HueBand:
112✔
1198
            return "Hue";
112✔
1199

1200
        case GCI_SaturationBand:
111✔
1201
            return "Saturation";
111✔
1202

1203
        case GCI_LightnessBand:
110✔
1204
            return "Lightness";
110✔
1205

1206
        case GCI_CyanBand:
117✔
1207
            return "Cyan";
117✔
1208

1209
        case GCI_MagentaBand:
99✔
1210
            return "Magenta";
99✔
1211

1212
        case GCI_YellowBand:
81✔
1213
            return "Yellow";
81✔
1214

1215
        case GCI_BlackBand:
62✔
1216
            return "Black";
62✔
1217

1218
        case GCI_YCbCr_YBand:
38✔
1219
            return "YCbCr_Y";
38✔
1220

1221
        case GCI_YCbCr_CbBand:
35✔
1222
            return "YCbCr_Cb";
35✔
1223

1224
        case GCI_YCbCr_CrBand:
32✔
1225
            return "YCbCr_Cr";
32✔
1226

1227
        case GCI_PanBand:
30✔
1228
            return "Pan";
30✔
1229

1230
        case GCI_CoastalBand:
39✔
1231
            return "Coastal";
39✔
1232

1233
        case GCI_RedEdgeBand:
29✔
1234
            return "RedEdge";
29✔
1235

1236
        case GCI_NIRBand:
34✔
1237
            return "NIR";
34✔
1238

1239
        case GCI_SWIRBand:
26✔
1240
            return "SWIR";
26✔
1241

1242
        case GCI_MWIRBand:
23✔
1243
            return "MWIR";
23✔
1244

1245
        case GCI_LWIRBand:
22✔
1246
            return "LWIR";
22✔
1247

1248
        case GCI_TIRBand:
21✔
1249
            return "TIR";
21✔
1250

1251
        case GCI_OtherIRBand:
22✔
1252
            return "OtherIR";
22✔
1253

1254
        case GCI_IR_Reserved_1:
19✔
1255
            return "IR_Reserved_1";
19✔
1256

1257
        case GCI_IR_Reserved_2:
18✔
1258
            return "IR_Reserved_2";
18✔
1259

1260
        case GCI_IR_Reserved_3:
17✔
1261
            return "IR_Reserved_3";
17✔
1262

1263
        case GCI_IR_Reserved_4:
16✔
1264
            return "IR_Reserved_4";
16✔
1265

1266
        case GCI_SAR_Ka_Band:
15✔
1267
            return "SAR_Ka";
15✔
1268

1269
        case GCI_SAR_K_Band:
14✔
1270
            return "SAR_K";
14✔
1271

1272
        case GCI_SAR_Ku_Band:
13✔
1273
            return "SAR_Ku";
13✔
1274

1275
        case GCI_SAR_X_Band:
12✔
1276
            return "SAR_X";
12✔
1277

1278
        case GCI_SAR_C_Band:
11✔
1279
            return "SAR_C";
11✔
1280

1281
        case GCI_SAR_S_Band:
10✔
1282
            return "SAR_S";
10✔
1283

1284
        case GCI_SAR_L_Band:
9✔
1285
            return "SAR_L";
9✔
1286

1287
        case GCI_SAR_P_Band:
8✔
1288
            return "SAR_P";
8✔
1289

1290
        case GCI_SAR_Reserved_1:
7✔
1291
            return "SAR_Reserved_1";
7✔
1292

1293
        case GCI_SAR_Reserved_2:
6✔
1294
            return "SAR_Reserved_2";
6✔
1295
    }
1296
    return "Undefined";
2,075✔
1297
}
1298

1299
/************************************************************************/
1300
/*                GDALGetColorInterpretationByName()                    */
1301
/************************************************************************/
1302

1303
/**
1304
 * \brief Get color interpretation by symbolic name.
1305
 *
1306
 * Returns a color interpretation corresponding to the given symbolic name. This
1307
 * function is opposite to the GDALGetColorInterpretationName().
1308
 *
1309
 * @param pszName string containing the symbolic name of the color
1310
 * interpretation.
1311
 *
1312
 * @return GDAL color interpretation.
1313
 *
1314
 * @since GDAL 1.7.0
1315
 */
1316

1317
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1,908✔
1318

1319
{
1320
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1,908✔
1321
                      GCI_Undefined);
1322

1323
    for (int iType = 0; iType <= GCI_Max; iType++)
8,468✔
1324
    {
1325
        if (EQUAL(GDALGetColorInterpretationName(
8,464✔
1326
                      static_cast<GDALColorInterp>(iType)),
1327
                  pszName))
1328
        {
1329
            return static_cast<GDALColorInterp>(iType);
1,904✔
1330
        }
1331
    }
1332

1333
    // Accept British English spelling
1334
    if (EQUAL(pszName, "grey"))
4✔
1335
        return GCI_GrayIndex;
×
1336

1337
    return GCI_Undefined;
4✔
1338
}
1339

1340
/************************************************************************/
1341
/*                  GDALGetColorInterpFromSTACCommonName()              */
1342
/************************************************************************/
1343

1344
static const struct
1345
{
1346
    const char *pszName;
1347
    GDALColorInterp eInterp;
1348
} asSTACCommonNames[] = {
1349
    {"pan", GCI_PanBand},
1350
    {"coastal", GCI_CoastalBand},
1351
    {"blue", GCI_BlueBand},
1352
    {"green", GCI_GreenBand},
1353
    {"green05", GCI_GreenBand},  // no exact match
1354
    {"yellow", GCI_YellowBand},
1355
    {"red", GCI_RedBand},
1356
    {"rededge", GCI_RedEdgeBand},
1357
    {"rededge071", GCI_RedEdgeBand},  // no exact match
1358
    {"rededge075", GCI_RedEdgeBand},  // no exact match
1359
    {"rededge078", GCI_RedEdgeBand},  // no exact match
1360
    {"nir", GCI_NIRBand},
1361
    {"nir08", GCI_NIRBand},   // no exact match
1362
    {"nir09", GCI_NIRBand},   // no exact match
1363
    {"cirrus", GCI_NIRBand},  // no exact match
1364
    {nullptr,
1365
     GCI_SWIRBand},  // so that GDALGetSTACCommonNameFromColorInterp returns null on GCI_SWIRBand
1366
    {"swir16", GCI_SWIRBand},  // no exact match
1367
    {"swir22", GCI_SWIRBand},  // no exact match
1368
    {"lwir", GCI_LWIRBand},
1369
    {"lwir11", GCI_LWIRBand},  // no exact match
1370
    {"lwir12", GCI_LWIRBand},  // no exact match
1371
};
1372

1373
/** Get color interpreetation from STAC eo:common_name
1374
 *
1375
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1376
 *
1377
 * @since GDAL 3.10
1378
 */
1379
GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName)
21✔
1380
{
1381

1382
    for (const auto &sAssoc : asSTACCommonNames)
84✔
1383
    {
1384
        if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName))
84✔
1385
            return sAssoc.eInterp;
21✔
1386
    }
1387
    return GCI_Undefined;
×
1388
}
1389

1390
/************************************************************************/
1391
/*                  GDALGetSTACCommonNameFromColorInterp()              */
1392
/************************************************************************/
1393

1394
/** Get STAC eo:common_name from GDAL color interpretation
1395
 *
1396
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1397
 *
1398
 * @return nullptr if there is no match
1399
 *
1400
 * @since GDAL 3.10
1401
 */
1402
const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp)
95✔
1403
{
1404
    for (const auto &sAssoc : asSTACCommonNames)
1,705✔
1405
    {
1406
        if (eInterp == sAssoc.eInterp)
1,632✔
1407
            return sAssoc.pszName;
22✔
1408
    }
1409
    return nullptr;
73✔
1410
}
1411

1412
/************************************************************************/
1413
/*                     GDALGetRandomRasterSample()                      */
1414
/************************************************************************/
1415

1416
/** Undocumented
1417
 * @param hBand undocumented.
1418
 * @param nSamples undocumented.
1419
 * @param pafSampleBuf undocumented.
1420
 * @return undocumented
1421
 */
1422
int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
×
1423
                                          float *pafSampleBuf)
1424

1425
{
1426
    VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
×
1427

1428
    GDALRasterBand *poBand;
1429

1430
    poBand = GDALRasterBand::FromHandle(
×
1431
        GDALGetRasterSampleOverview(hBand, nSamples));
1432
    CPLAssert(nullptr != poBand);
×
1433

1434
    /* -------------------------------------------------------------------- */
1435
    /*      Figure out the ratio of blocks we will read to get an           */
1436
    /*      approximate value.                                              */
1437
    /* -------------------------------------------------------------------- */
1438
    int bGotNoDataValue = FALSE;
×
1439

1440
    double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
×
1441

1442
    int nBlockXSize = 0;
×
1443
    int nBlockYSize = 0;
×
1444
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
×
1445

1446
    const int nBlocksPerRow =
1447
        (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
×
1448
    const int nBlocksPerColumn =
1449
        (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
×
1450

1451
    const GIntBig nBlockPixels =
×
1452
        static_cast<GIntBig>(nBlockXSize) * nBlockYSize;
×
1453
    const GIntBig nBlockCount =
×
1454
        static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn;
×
1455

1456
    if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
×
1457
        nBlockCount == 0)
1458
    {
1459
        CPLError(CE_Failure, CPLE_AppDefined,
×
1460
                 "GDALGetRandomRasterSample(): returning because band"
1461
                 " appears degenerate.");
1462

1463
        return FALSE;
×
1464
    }
1465

1466
    int nSampleRate = static_cast<int>(
1467
        std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
×
1468

1469
    if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
×
1470
        nSampleRate--;
×
1471

1472
    while (nSampleRate > 1 &&
×
1473
           ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
×
1474
        nSampleRate--;
×
1475

1476
    int nBlockSampleRate = 1;
×
1477

1478
    if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
×
1479
        nBlockSampleRate = static_cast<int>(std::max<GIntBig>(
×
1480
            1,
×
1481
            nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1))));
×
1482

1483
    int nActualSamples = 0;
×
1484

1485
    for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount;
×
1486
         iSampleBlock += nSampleRate)
×
1487
    {
1488

1489
        const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow);
×
1490
        const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow);
×
1491

1492
        GDALRasterBlock *const poBlock =
1493
            poBand->GetLockedBlockRef(iXBlock, iYBlock);
×
1494
        if (poBlock == nullptr)
×
1495
            continue;
×
1496
        void *pDataRef = poBlock->GetDataRef();
×
1497

1498
        int iXValid = nBlockXSize;
×
1499
        if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
×
1500
            iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
×
1501

1502
        int iYValid = nBlockYSize;
×
1503
        if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
×
1504
            iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
×
1505

1506
        int iRemainder = 0;
×
1507

1508
        for (int iY = 0; iY < iYValid; iY++)
×
1509
        {
1510
            int iX = iRemainder;  // Used after for.
×
1511
            for (; iX < iXValid; iX += nBlockSampleRate)
×
1512
            {
1513
                double dfValue = 0.0;
×
1514
                const int iOffset = iX + iY * nBlockXSize;
×
1515

1516
                switch (poBlock->GetDataType())
×
1517
                {
1518
                    case GDT_Byte:
×
1519
                        dfValue =
×
1520
                            reinterpret_cast<const GByte *>(pDataRef)[iOffset];
×
1521
                        break;
×
1522
                    case GDT_Int8:
×
1523
                        dfValue =
×
1524
                            reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
×
1525
                        break;
×
1526
                    case GDT_UInt16:
×
1527
                        dfValue = reinterpret_cast<const GUInt16 *>(
×
1528
                            pDataRef)[iOffset];
×
1529
                        break;
×
1530
                    case GDT_Int16:
×
1531
                        dfValue =
×
1532
                            reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
×
1533
                        break;
×
1534
                    case GDT_UInt32:
×
1535
                        dfValue = reinterpret_cast<const GUInt32 *>(
×
1536
                            pDataRef)[iOffset];
×
1537
                        break;
×
1538
                    case GDT_Int32:
×
1539
                        dfValue =
×
1540
                            reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
×
1541
                        break;
×
1542
                    case GDT_UInt64:
×
1543
                        dfValue = static_cast<double>(
×
1544
                            reinterpret_cast<const std::uint64_t *>(
1545
                                pDataRef)[iOffset]);
×
1546
                        break;
×
1547
                    case GDT_Int64:
×
1548
                        dfValue = static_cast<double>(
×
1549
                            reinterpret_cast<const std::int64_t *>(
1550
                                pDataRef)[iOffset]);
×
1551
                        break;
×
1552
                    case GDT_Float32:
×
1553
                        dfValue =
×
1554
                            reinterpret_cast<const float *>(pDataRef)[iOffset];
×
1555
                        break;
×
1556
                    case GDT_Float64:
×
1557
                        dfValue =
×
1558
                            reinterpret_cast<const double *>(pDataRef)[iOffset];
×
1559
                        break;
×
1560
                    case GDT_CInt16:
×
1561
                    {
1562
                        // TODO(schwehr): Clean up casts.
1563
                        const double dfReal = reinterpret_cast<const GInt16 *>(
×
1564
                            pDataRef)[iOffset * 2];
×
1565
                        const double dfImag = reinterpret_cast<const GInt16 *>(
×
1566
                            pDataRef)[iOffset * 2 + 1];
×
1567
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1568
                        break;
×
1569
                    }
1570
                    case GDT_CInt32:
×
1571
                    {
1572
                        const double dfReal = reinterpret_cast<const GInt32 *>(
×
1573
                            pDataRef)[iOffset * 2];
×
1574
                        const double dfImag = reinterpret_cast<const GInt32 *>(
×
1575
                            pDataRef)[iOffset * 2 + 1];
×
1576
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1577
                        break;
×
1578
                    }
1579
                    case GDT_CFloat32:
×
1580
                    {
1581
                        const double dfReal = reinterpret_cast<const float *>(
×
1582
                            pDataRef)[iOffset * 2];
×
1583
                        const double dfImag = reinterpret_cast<const float *>(
×
1584
                            pDataRef)[iOffset * 2 + 1];
×
1585
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1586
                        break;
×
1587
                    }
1588
                    case GDT_CFloat64:
×
1589
                    {
1590
                        const double dfReal = reinterpret_cast<const double *>(
×
1591
                            pDataRef)[iOffset * 2];
×
1592
                        const double dfImag = reinterpret_cast<const double *>(
×
1593
                            pDataRef)[iOffset * 2 + 1];
×
1594
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1595
                        break;
×
1596
                    }
1597
                    case GDT_Unknown:
×
1598
                    case GDT_TypeCount:
1599
                        CPLAssert(false);
×
1600
                }
1601

1602
                if (bGotNoDataValue && dfValue == dfNoDataValue)
×
1603
                    continue;
×
1604

1605
                if (nActualSamples < nSamples)
×
1606
                    pafSampleBuf[nActualSamples++] =
×
1607
                        static_cast<float>(dfValue);
×
1608
            }
1609

1610
            iRemainder = iX - iXValid;
×
1611
        }
1612

1613
        poBlock->DropLock();
×
1614
    }
1615

1616
    return nActualSamples;
×
1617
}
1618

1619
/************************************************************************/
1620
/*                             gdal::GCP                                */
1621
/************************************************************************/
1622

1623
namespace gdal
1624
{
1625
/** Constructor. */
1626
GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
25,493✔
1627
         double dfX, double dfY, double dfZ)
25,493✔
1628
    : gcp{CPLStrdup(pszId ? pszId : ""),
25,493✔
1629
          CPLStrdup(pszInfo ? pszInfo : ""),
25,493✔
1630
          dfPixel,
1631
          dfLine,
1632
          dfX,
1633
          dfY,
1634
          dfZ}
25,493✔
1635
{
1636
    static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1637
}
25,493✔
1638

1639
/** Destructor. */
1640
GCP::~GCP()
313,908✔
1641
{
1642
    CPLFree(gcp.pszId);
156,954✔
1643
    CPLFree(gcp.pszInfo);
156,954✔
1644
}
156,954✔
1645

1646
/** Constructor from a C GDAL_GCP instance. */
1647
GCP::GCP(const GDAL_GCP &other)
106,933✔
1648
    : gcp{CPLStrdup(other.pszId),
106,933✔
1649
          CPLStrdup(other.pszInfo),
213,866✔
1650
          other.dfGCPPixel,
106,933✔
1651
          other.dfGCPLine,
106,933✔
1652
          other.dfGCPX,
106,933✔
1653
          other.dfGCPY,
106,933✔
1654
          other.dfGCPZ}
106,933✔
1655
{
1656
}
106,933✔
1657

1658
/** Copy constructor. */
1659
GCP::GCP(const GCP &other) : GCP(other.gcp)
37,953✔
1660
{
1661
}
37,953✔
1662

1663
/** Move constructor. */
1664
GCP::GCP(GCP &&other)
24,528✔
1665
    : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
24,528✔
1666
          other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
24,528✔
1667
          other.gcp.dfGCPZ}
24,528✔
1668
{
1669
    other.gcp.pszId = nullptr;
24,528✔
1670
    other.gcp.pszInfo = nullptr;
24,528✔
1671
}
24,528✔
1672

1673
/** Copy assignment operator. */
1674
GCP &GCP::operator=(const GCP &other)
1✔
1675
{
1676
    if (this != &other)
1✔
1677
    {
1678
        CPLFree(gcp.pszId);
1✔
1679
        CPLFree(gcp.pszInfo);
1✔
1680
        gcp = other.gcp;
1✔
1681
        gcp.pszId = CPLStrdup(other.gcp.pszId);
1✔
1682
        gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1✔
1683
    }
1684
    return *this;
1✔
1685
}
1686

1687
/** Move assignment operator. */
1688
GCP &GCP::operator=(GCP &&other)
1✔
1689
{
1690
    if (this != &other)
1✔
1691
    {
1692
        CPLFree(gcp.pszId);
1✔
1693
        CPLFree(gcp.pszInfo);
1✔
1694
        gcp = other.gcp;
1✔
1695
        other.gcp.pszId = nullptr;
1✔
1696
        other.gcp.pszInfo = nullptr;
1✔
1697
    }
1698
    return *this;
1✔
1699
}
1700

1701
/** Set the 'id' member of the GCP. */
1702
void GCP::SetId(const char *pszId)
24,497✔
1703
{
1704
    CPLFree(gcp.pszId);
24,497✔
1705
    gcp.pszId = CPLStrdup(pszId ? pszId : "");
24,497✔
1706
}
24,497✔
1707

1708
/** Set the 'info' member of the GCP. */
1709
void GCP::SetInfo(const char *pszInfo)
24,497✔
1710
{
1711
    CPLFree(gcp.pszInfo);
24,497✔
1712
    gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
24,497✔
1713
}
24,497✔
1714

1715
/** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1716
/*static */
1717
const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
692✔
1718
{
1719
    return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
692✔
1720
}
1721

1722
/** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1723
/*static*/
1724
std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
337✔
1725
{
1726
    return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
337✔
1727
}
1728

1729
} /* namespace gdal */
1730

1731
/************************************************************************/
1732
/*                            GDALInitGCPs()                            */
1733
/************************************************************************/
1734

1735
/** Initialize an array of GCPs.
1736
 *
1737
 * Numeric values are initialized to 0 and strings to the empty string ""
1738
 * allocated with CPLStrdup()
1739
 * An array initialized with GDALInitGCPs() must be de-initialized with
1740
 * GDALDeinitGCPs().
1741
 *
1742
 * @param nCount number of GCPs in psGCP
1743
 * @param psGCP array of GCPs of size nCount.
1744
 */
1745
void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1,323✔
1746

1747
{
1748
    if (nCount > 0)
1,323✔
1749
    {
1750
        VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
677✔
1751
    }
1752

1753
    for (int iGCP = 0; iGCP < nCount; iGCP++)
6,099✔
1754
    {
1755
        memset(psGCP, 0, sizeof(GDAL_GCP));
4,776✔
1756
        psGCP->pszId = CPLStrdup("");
4,776✔
1757
        psGCP->pszInfo = CPLStrdup("");
4,776✔
1758
        psGCP++;
4,776✔
1759
    }
1760
}
1761

1762
/************************************************************************/
1763
/*                           GDALDeinitGCPs()                           */
1764
/************************************************************************/
1765

1766
/** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1767
 *
1768
 * @param nCount number of GCPs in psGCP
1769
 * @param psGCP array of GCPs of size nCount.
1770
 */
1771
void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1,432✔
1772

1773
{
1774
    if (nCount > 0)
1,432✔
1775
    {
1776
        VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
479✔
1777
    }
1778

1779
    for (int iGCP = 0; iGCP < nCount; iGCP++)
6,621✔
1780
    {
1781
        CPLFree(psGCP->pszId);
5,189✔
1782
        CPLFree(psGCP->pszInfo);
5,189✔
1783
        psGCP++;
5,189✔
1784
    }
1785
}
1786

1787
/************************************************************************/
1788
/*                         GDALDuplicateGCPs()                          */
1789
/************************************************************************/
1790

1791
/** Duplicate an array of GCPs
1792
 *
1793
 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1794
 *
1795
 * @param nCount number of GCPs in psGCP
1796
 * @param pasGCPList array of GCPs of size nCount.
1797
 */
1798
GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
732✔
1799

1800
{
1801
    GDAL_GCP *pasReturn =
1802
        static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
732✔
1803
    GDALInitGCPs(nCount, pasReturn);
732✔
1804

1805
    for (int iGCP = 0; iGCP < nCount; iGCP++)
3,964✔
1806
    {
1807
        CPLFree(pasReturn[iGCP].pszId);
3,232✔
1808
        pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
3,232✔
1809

1810
        CPLFree(pasReturn[iGCP].pszInfo);
3,232✔
1811
        pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
3,232✔
1812

1813
        pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
3,232✔
1814
        pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
3,232✔
1815
        pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
3,232✔
1816
        pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
3,232✔
1817
        pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
3,232✔
1818
    }
1819

1820
    return pasReturn;
732✔
1821
}
1822

1823
/************************************************************************/
1824
/*                       GDALFindAssociatedFile()                       */
1825
/************************************************************************/
1826

1827
/**
1828
 * \brief Find file with alternate extension.
1829
 *
1830
 * Finds the file with the indicated extension, substituting it in place
1831
 * of the extension of the base filename.  Generally used to search for
1832
 * associated files like world files .RPB files, etc.  If necessary, the
1833
 * extension will be tried in both upper and lower case.  If a sibling file
1834
 * list is available it will be used instead of doing VSIStatExL() calls to
1835
 * probe the file system.
1836
 *
1837
 * Note that the result is a dynamic CPLString so this method should not
1838
 * be used in a situation where there could be cross heap issues.  It is
1839
 * generally imprudent for application built on GDAL to use this function
1840
 * unless they are sure they will always use the same runtime heap as GDAL.
1841
 *
1842
 * @param pszBaseFilename the filename relative to which to search.
1843
 * @param pszExt the target extension in either upper or lower case.
1844
 * @param papszSiblingFiles the list of files in the same directory as
1845
 * pszBaseFilename or NULL if they are not known.
1846
 * @param nFlags special options controlling search.  None defined yet, just
1847
 * pass 0.
1848
 *
1849
 * @return an empty string if the target is not found, otherwise the target
1850
 * file with similar path style as the pszBaseFilename.
1851
 */
1852

1853
/**/
1854
/**/
1855

1856
CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
37,382✔
1857
                                 const char *pszExt,
1858
                                 CSLConstList papszSiblingFiles,
1859
                                 CPL_UNUSED int nFlags)
1860

1861
{
1862
    CPLString osTarget = CPLResetExtension(pszBaseFilename, pszExt);
74,764✔
1863

1864
    if (papszSiblingFiles == nullptr ||
74,500✔
1865
        // cppcheck-suppress knownConditionTrueFalse
1866
        !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
37,118✔
1867
    {
1868
        VSIStatBufL sStatBuf;
1869

1870
        if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
264✔
1871
        {
1872
            CPLString osAltExt = pszExt;
249✔
1873

1874
            if (islower(static_cast<unsigned char>(pszExt[0])))
249✔
1875
                osAltExt = osAltExt.toupper();
×
1876
            else
1877
                osAltExt = osAltExt.tolower();
249✔
1878

1879
            osTarget = CPLResetExtension(pszBaseFilename, osAltExt);
249✔
1880

1881
            if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
249✔
1882
                return "";
247✔
1883
        }
1884
    }
1885
    else
1886
    {
1887
        const int iSibling =
1888
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
37,118✔
1889
        if (iSibling < 0)
37,118✔
1890
            return "";
37,067✔
1891

1892
        osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
51✔
1893
        osTarget += papszSiblingFiles[iSibling];
51✔
1894
    }
1895

1896
    return osTarget;
68✔
1897
}
1898

1899
/************************************************************************/
1900
/*                         GDALLoadOziMapFile()                         */
1901
/************************************************************************/
1902

1903
/** Helper function for translator implementer wanting support for OZI .map
1904
 *
1905
 * @param pszFilename filename of .tab file
1906
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1907
 * @param ppszWKT output pointer to a string that will be allocated with
1908
 * CPLMalloc().
1909
 * @param pnGCPCount output pointer to GCP count.
1910
 * @param ppasGCPs outputer pointer to an array of GCPs.
1911
 * @return TRUE in case of success, FALSE otherwise.
1912
 */
1913
int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
×
1914
                                   double *padfGeoTransform, char **ppszWKT,
1915
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
1916

1917
{
1918
    VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
×
1919
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
×
1920
    VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
×
1921
    VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
×
1922

1923
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
×
1924

1925
    if (!papszLines)
×
1926
        return FALSE;
×
1927

1928
    int nLines = CSLCount(papszLines);
×
1929

1930
    // Check the OziExplorer Map file signature
1931
    if (nLines < 5 ||
×
1932
        !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
×
1933
    {
1934
        CPLError(CE_Failure, CPLE_AppDefined,
×
1935
                 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
1936
                 "format.",
1937
                 pszFilename);
1938
        CSLDestroy(papszLines);
×
1939
        return FALSE;
×
1940
    }
1941

1942
    OGRSpatialReference oSRS;
×
1943
    OGRErr eErr = OGRERR_NONE;
×
1944

1945
    /* The Map Scale Factor has been introduced recently on the 6th line */
1946
    /* and is a trick that is used to just change that line without changing */
1947
    /* the rest of the MAP file but providing an imagery that is smaller or
1948
     * larger */
1949
    /* so we have to correct the pixel/line values read in the .MAP file so they
1950
     */
1951
    /* match the actual imagery dimension. Well, this is a bad summary of what
1952
     */
1953
    /* is explained at
1954
     * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
1955
    double dfMSF = 1;
×
1956

1957
    for (int iLine = 5; iLine < nLines; iLine++)
×
1958
    {
1959
        if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
×
1960
        {
1961
            dfMSF = CPLAtof(papszLines[iLine] + 4);
×
1962
            if (dfMSF <= 0.01) /* Suspicious values */
×
1963
            {
1964
                CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
×
1965
                dfMSF = 1;
×
1966
            }
1967
        }
1968
    }
1969

1970
    eErr = oSRS.importFromOzi(papszLines);
×
1971
    if (eErr == OGRERR_NONE)
×
1972
    {
1973
        if (ppszWKT != nullptr)
×
1974
            oSRS.exportToWkt(ppszWKT);
×
1975
    }
1976

1977
    int nCoordinateCount = 0;
×
1978
    // TODO(schwehr): Initialize asGCPs.
1979
    GDAL_GCP asGCPs[30];
1980

1981
    // Iterate all lines in the MAP-file
1982
    for (int iLine = 5; iLine < nLines; iLine++)
×
1983
    {
1984
        char **papszTok = CSLTokenizeString2(
×
1985
            papszLines[iLine], ",",
×
1986
            CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
1987

1988
        if (CSLCount(papszTok) < 12)
×
1989
        {
1990
            CSLDestroy(papszTok);
×
1991
            continue;
×
1992
        }
1993

1994
        if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
×
1995
            !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
×
1996
            nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
1997
        {
1998
            bool bReadOk = false;
×
1999
            double dfLon = 0.0;
×
2000
            double dfLat = 0.0;
×
2001

2002
            if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
×
2003
                !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
×
2004
            {
2005
                // Set geographical coordinates of the pixels
2006
                dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
×
2007
                dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
×
2008
                if (EQUAL(papszTok[11], "W"))
×
2009
                    dfLon = -dfLon;
×
2010
                if (EQUAL(papszTok[8], "S"))
×
2011
                    dfLat = -dfLat;
×
2012

2013
                // Transform from the geographical coordinates into projected
2014
                // coordinates.
2015
                if (eErr == OGRERR_NONE)
×
2016
                {
2017
                    OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
×
2018

2019
                    if (poLongLat)
×
2020
                    {
2021
                        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
2022
                        poLongLat->SetAxisMappingStrategy(
×
2023
                            OAMS_TRADITIONAL_GIS_ORDER);
2024

2025
                        OGRCoordinateTransformation *poTransform =
2026
                            OGRCreateCoordinateTransformation(poLongLat, &oSRS);
×
2027
                        if (poTransform)
×
2028
                        {
2029
                            bReadOk = CPL_TO_BOOL(
×
2030
                                poTransform->Transform(1, &dfLon, &dfLat));
2031
                            delete poTransform;
×
2032
                        }
2033
                        delete poLongLat;
×
2034
                    }
2035
                }
×
2036
            }
2037
            else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
×
2038
            {
2039
                // Set cartesian coordinates of the pixels.
2040
                dfLon = CPLAtofM(papszTok[14]);
×
2041
                dfLat = CPLAtofM(papszTok[15]);
×
2042
                bReadOk = true;
×
2043

2044
                // if ( EQUAL(papszTok[16], "S") )
2045
                //     dfLat = -dfLat;
2046
            }
2047

2048
            if (bReadOk)
×
2049
            {
2050
                GDALInitGCPs(1, asGCPs + nCoordinateCount);
×
2051

2052
                // Set pixel/line part
2053
                asGCPs[nCoordinateCount].dfGCPPixel =
×
2054
                    CPLAtofM(papszTok[2]) / dfMSF;
×
2055
                asGCPs[nCoordinateCount].dfGCPLine =
×
2056
                    CPLAtofM(papszTok[3]) / dfMSF;
×
2057

2058
                asGCPs[nCoordinateCount].dfGCPX = dfLon;
×
2059
                asGCPs[nCoordinateCount].dfGCPY = dfLat;
×
2060

2061
                nCoordinateCount++;
×
2062
            }
2063
        }
2064

2065
        CSLDestroy(papszTok);
×
2066
    }
2067

2068
    CSLDestroy(papszLines);
×
2069

2070
    if (nCoordinateCount == 0)
×
2071
    {
2072
        CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
×
2073
                 pszFilename);
2074
        return FALSE;
×
2075
    }
2076

2077
    /* -------------------------------------------------------------------- */
2078
    /*      Try to convert the GCPs into a geotransform definition, if      */
2079
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2080
    /* -------------------------------------------------------------------- */
2081
    if (!GDALGCPsToGeoTransform(
×
2082
            nCoordinateCount, asGCPs, padfGeoTransform,
2083
            CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
×
2084
    {
2085
        if (pnGCPCount && ppasGCPs)
×
2086
        {
2087
            CPLDebug(
×
2088
                "GDAL",
2089
                "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
2090
                "first order geotransform.  Using points as GCPs.",
2091
                pszFilename);
2092

2093
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2094
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2095
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2096
            *pnGCPCount = nCoordinateCount;
×
2097
        }
2098
    }
2099
    else
2100
    {
2101
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
×
2102
    }
2103

2104
    return TRUE;
×
2105
}
2106

2107
/************************************************************************/
2108
/*                       GDALReadOziMapFile()                           */
2109
/************************************************************************/
2110

2111
/** Helper function for translator implementer wanting support for OZI .map
2112
 *
2113
 * @param pszBaseFilename filename whose basename will help building the .map
2114
 * filename.
2115
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2116
 * @param ppszWKT output pointer to a string that will be allocated with
2117
 * CPLMalloc().
2118
 * @param pnGCPCount output pointer to GCP count.
2119
 * @param ppasGCPs outputer pointer to an array of GCPs.
2120
 * @return TRUE in case of success, FALSE otherwise.
2121
 */
2122
int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
×
2123
                                   double *padfGeoTransform, char **ppszWKT,
2124
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
2125

2126
{
2127
    /* -------------------------------------------------------------------- */
2128
    /*      Try lower case, then upper case.                                */
2129
    /* -------------------------------------------------------------------- */
2130
    const char *pszOzi = CPLResetExtension(pszBaseFilename, "map");
×
2131

2132
    VSILFILE *fpOzi = VSIFOpenL(pszOzi, "rt");
×
2133

2134
    if (fpOzi == nullptr && VSIIsCaseSensitiveFS(pszOzi))
×
2135
    {
2136
        pszOzi = CPLResetExtension(pszBaseFilename, "MAP");
×
2137
        fpOzi = VSIFOpenL(pszOzi, "rt");
×
2138
    }
2139

2140
    if (fpOzi == nullptr)
×
2141
        return FALSE;
×
2142

2143
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
×
2144

2145
    /* -------------------------------------------------------------------- */
2146
    /*      We found the file, now load and parse it.                       */
2147
    /* -------------------------------------------------------------------- */
2148
    return GDALLoadOziMapFile(pszOzi, padfGeoTransform, ppszWKT, pnGCPCount,
×
2149
                              ppasGCPs);
×
2150
}
2151

2152
/************************************************************************/
2153
/*                         GDALLoadTabFile()                            */
2154
/*                                                                      */
2155
/************************************************************************/
2156

2157
/** Helper function for translator implementer wanting support for MapInfo
2158
 * .tab files.
2159
 *
2160
 * @param pszFilename filename of .tab
2161
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2162
 * @param ppszWKT output pointer to a string that will be allocated with
2163
 * CPLMalloc().
2164
 * @param pnGCPCount output pointer to GCP count.
2165
 * @param ppasGCPs outputer pointer to an array of GCPs.
2166
 * @return TRUE in case of success, FALSE otherwise.
2167
 */
2168
int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
14✔
2169
                                double *padfGeoTransform, char **ppszWKT,
2170
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2171

2172
{
2173
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
14✔
2174

2175
    if (!papszLines)
14✔
2176
        return FALSE;
×
2177

2178
    char **papszTok = nullptr;
14✔
2179
    bool bTypeRasterFound = false;
14✔
2180
    bool bInsideTableDef = false;
14✔
2181
    int nCoordinateCount = 0;
14✔
2182
    GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
2183
    const int numLines = CSLCount(papszLines);
14✔
2184

2185
    // Iterate all lines in the TAB-file
2186
    for (int iLine = 0; iLine < numLines; iLine++)
196✔
2187
    {
2188
        CSLDestroy(papszTok);
182✔
2189
        papszTok =
2190
            CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
182✔
2191

2192
        if (CSLCount(papszTok) < 2)
182✔
2193
            continue;
28✔
2194

2195
        // Did we find table definition
2196
        if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
154✔
2197
        {
2198
            bInsideTableDef = TRUE;
14✔
2199
        }
2200
        else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
140✔
2201
        {
2202
            // Only RASTER-type will be handled
2203
            if (EQUAL(papszTok[1], "RASTER"))
14✔
2204
            {
2205
                bTypeRasterFound = true;
14✔
2206
            }
2207
            else
2208
            {
2209
                CSLDestroy(papszTok);
×
2210
                CSLDestroy(papszLines);
×
2211
                return FALSE;
×
2212
            }
2213
        }
2214
        else if (bTypeRasterFound && bInsideTableDef &&
84✔
2215
                 CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
210✔
2216
                 nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2217
        {
2218
            GDALInitGCPs(1, asGCPs + nCoordinateCount);
56✔
2219

2220
            asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
56✔
2221
            asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
56✔
2222
            asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
56✔
2223
            asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
56✔
2224
            if (papszTok[5] != nullptr)
56✔
2225
            {
2226
                CPLFree(asGCPs[nCoordinateCount].pszId);
56✔
2227
                asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
56✔
2228
            }
2229

2230
            nCoordinateCount++;
56✔
2231
        }
2232
        else if (bTypeRasterFound && bInsideTableDef &&
70✔
2233
                 EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
28✔
2234
        {
2235
            OGRSpatialReference oSRS;
28✔
2236

2237
            if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
14✔
2238
                oSRS.exportToWkt(ppszWKT);
28✔
2239
        }
2240
        else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
70✔
2241
                 EQUAL(papszTok[1], "degree"))
14✔
2242
        {
2243
            /*
2244
            ** If we have units of "degree", but a projected coordinate
2245
            ** system we need to convert it to geographic.  See to01_02.TAB.
2246
            */
2247
            if (ppszWKT != nullptr && *ppszWKT != nullptr &&
×
2248
                STARTS_WITH_CI(*ppszWKT, "PROJCS"))
×
2249
            {
2250
                OGRSpatialReference oSRS;
×
2251
                oSRS.importFromWkt(*ppszWKT);
×
2252

2253
                OGRSpatialReference oSRSGeogCS;
×
2254
                oSRSGeogCS.CopyGeogCSFrom(&oSRS);
×
2255
                CPLFree(*ppszWKT);
×
2256

2257
                oSRSGeogCS.exportToWkt(ppszWKT);
×
2258
            }
2259
        }
2260
    }
2261

2262
    CSLDestroy(papszTok);
14✔
2263
    CSLDestroy(papszLines);
14✔
2264

2265
    if (nCoordinateCount == 0)
14✔
2266
    {
2267
        CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
×
2268
                 pszFilename);
2269
        return FALSE;
×
2270
    }
2271

2272
    /* -------------------------------------------------------------------- */
2273
    /*      Try to convert the GCPs into a geotransform definition, if      */
2274
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2275
    /* -------------------------------------------------------------------- */
2276
    if (!GDALGCPsToGeoTransform(
14✔
2277
            nCoordinateCount, asGCPs, padfGeoTransform,
2278
            CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
14✔
2279
    {
2280
        if (pnGCPCount && ppasGCPs)
×
2281
        {
2282
            CPLDebug("GDAL",
×
2283
                     "GDALLoadTabFile(%s) found file, was not able to derive a "
2284
                     "first order geotransform.  Using points as GCPs.",
2285
                     pszFilename);
2286

2287
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2288
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2289
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2290
            *pnGCPCount = nCoordinateCount;
×
2291
        }
2292
    }
2293
    else
2294
    {
2295
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
14✔
2296
    }
2297

2298
    return TRUE;
14✔
2299
}
2300

2301
/************************************************************************/
2302
/*                         GDALReadTabFile()                            */
2303
/************************************************************************/
2304

2305
/** Helper function for translator implementer wanting support for MapInfo
2306
 * .tab files.
2307
 *
2308
 * @param pszBaseFilename filename whose basename will help building the .tab
2309
 * filename.
2310
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2311
 * @param ppszWKT output pointer to a string that will be allocated with
2312
 * CPLMalloc().
2313
 * @param pnGCPCount output pointer to GCP count.
2314
 * @param ppasGCPs outputer pointer to an array of GCPs.
2315
 * @return TRUE in case of success, FALSE otherwise.
2316
 */
2317
int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
×
2318
                                double *padfGeoTransform, char **ppszWKT,
2319
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2320

2321
{
2322
    return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
×
2323
                            pnGCPCount, ppasGCPs, nullptr, nullptr);
×
2324
}
2325

2326
int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
3,692✔
2327
                     char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2328
                     CSLConstList papszSiblingFiles, char **ppszTabFileNameOut)
2329
{
2330
    if (ppszTabFileNameOut)
3,692✔
2331
        *ppszTabFileNameOut = nullptr;
3,692✔
2332

2333
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
3,692✔
2334
        return FALSE;
×
2335

2336
    const char *pszTAB = CPLResetExtension(pszBaseFilename, "tab");
3,692✔
2337

2338
    if (papszSiblingFiles &&
7,335✔
2339
        // cppcheck-suppress knownConditionTrueFalse
2340
        GDALCanReliablyUseSiblingFileList(pszTAB))
3,643✔
2341
    {
2342
        int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB));
3,643✔
2343
        if (iSibling >= 0)
3,643✔
2344
        {
2345
            CPLString osTabFilename = pszBaseFilename;
14✔
2346
            osTabFilename.resize(strlen(pszBaseFilename) -
28✔
2347
                                 strlen(CPLGetFilename(pszBaseFilename)));
14✔
2348
            osTabFilename += papszSiblingFiles[iSibling];
14✔
2349
            if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
14✔
2350
                                pnGCPCount, ppasGCPs))
14✔
2351
            {
2352
                if (ppszTabFileNameOut)
14✔
2353
                    *ppszTabFileNameOut = CPLStrdup(osTabFilename);
14✔
2354
                return TRUE;
14✔
2355
            }
2356
        }
2357
        return FALSE;
3,629✔
2358
    }
2359

2360
    /* -------------------------------------------------------------------- */
2361
    /*      Try lower case, then upper case.                                */
2362
    /* -------------------------------------------------------------------- */
2363

2364
    VSILFILE *fpTAB = VSIFOpenL(pszTAB, "rt");
49✔
2365

2366
    if (fpTAB == nullptr && VSIIsCaseSensitiveFS(pszTAB))
49✔
2367
    {
2368
        pszTAB = CPLResetExtension(pszBaseFilename, "TAB");
49✔
2369
        fpTAB = VSIFOpenL(pszTAB, "rt");
49✔
2370
    }
2371

2372
    if (fpTAB == nullptr)
49✔
2373
        return FALSE;
49✔
2374

2375
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
×
2376

2377
    /* -------------------------------------------------------------------- */
2378
    /*      We found the file, now load and parse it.                       */
2379
    /* -------------------------------------------------------------------- */
2380
    if (GDALLoadTabFile(pszTAB, padfGeoTransform, ppszWKT, pnGCPCount,
×
2381
                        ppasGCPs))
×
2382
    {
2383
        if (ppszTabFileNameOut)
×
2384
            *ppszTabFileNameOut = CPLStrdup(pszTAB);
×
2385
        return TRUE;
×
2386
    }
2387
    return FALSE;
×
2388
}
2389

2390
/************************************************************************/
2391
/*                         GDALLoadWorldFile()                          */
2392
/************************************************************************/
2393

2394
/**
2395
 * \brief Read ESRI world file.
2396
 *
2397
 * This function reads an ESRI style world file, and formats a geotransform
2398
 * from its contents.
2399
 *
2400
 * The world file contains an affine transformation with the parameters
2401
 * in a different order than in a geotransform array.
2402
 *
2403
 * <ul>
2404
 * <li> geotransform[1] : width of pixel
2405
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2406
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2407
 * <li> geotransform[5] : height of pixel (but negative)
2408
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2409
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2410
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2411
 * pixel.
2412
 * </ul>
2413
 *
2414
 * @param pszFilename the world file name.
2415
 * @param padfGeoTransform the six double array into which the
2416
 * geotransformation should be placed.
2417
 *
2418
 * @return TRUE on success or FALSE on failure.
2419
 */
2420

2421
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
67✔
2422
                                  double *padfGeoTransform)
2423

2424
{
2425
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
67✔
2426
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
67✔
2427

2428
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
67✔
2429

2430
    if (!papszLines)
67✔
2431
        return FALSE;
×
2432

2433
    double world[6] = {0.0};
67✔
2434
    // reads the first 6 non-empty lines
2435
    int nLines = 0;
67✔
2436
    const int nLinesCount = CSLCount(papszLines);
67✔
2437
    for (int i = 0;
469✔
2438
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
469✔
2439
         ++i)
2440
    {
2441
        CPLString line(papszLines[i]);
402✔
2442
        if (line.Trim().empty())
402✔
2443
            continue;
×
2444

2445
        world[nLines] = CPLAtofM(line);
402✔
2446
        ++nLines;
402✔
2447
    }
2448

2449
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
67✔
2450
        (world[3] != 0.0 || world[1] != 0.0))
67✔
2451
    {
2452
        padfGeoTransform[0] = world[4];
67✔
2453
        padfGeoTransform[1] = world[0];
67✔
2454
        padfGeoTransform[2] = world[2];
67✔
2455
        padfGeoTransform[3] = world[5];
67✔
2456
        padfGeoTransform[4] = world[1];
67✔
2457
        padfGeoTransform[5] = world[3];
67✔
2458

2459
        // correct for center of pixel vs. top left of pixel
2460
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
67✔
2461
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
67✔
2462
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
67✔
2463
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
67✔
2464

2465
        CSLDestroy(papszLines);
67✔
2466

2467
        return TRUE;
67✔
2468
    }
2469
    else
2470
    {
2471
        CPLDebug("GDAL",
×
2472
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2473
                 pszFilename);
2474
        CSLDestroy(papszLines);
×
2475
        return FALSE;
×
2476
    }
2477
}
2478

2479
/************************************************************************/
2480
/*                         GDALReadWorldFile()                          */
2481
/************************************************************************/
2482

2483
/**
2484
 * \brief Read ESRI world file.
2485
 *
2486
 * This function reads an ESRI style world file, and formats a geotransform
2487
 * from its contents.  It does the same as GDALLoadWorldFile() function, but
2488
 * it will form the filename for the worldfile from the filename of the raster
2489
 * file referred and the suggested extension.  If no extension is provided,
2490
 * the code will internally try the unix style and windows style world file
2491
 * extensions (eg. for .tif these would be .tfw and .tifw).
2492
 *
2493
 * The world file contains an affine transformation with the parameters
2494
 * in a different order than in a geotransform array.
2495
 *
2496
 * <ul>
2497
 * <li> geotransform[1] : width of pixel
2498
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2499
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2500
 * <li> geotransform[5] : height of pixel (but negative)
2501
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2502
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2503
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2504
 * pixel.
2505
 * </ul>
2506
 *
2507
 * @param pszBaseFilename the target raster file.
2508
 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2509
 * from the pszBaseFilename
2510
 * @param padfGeoTransform the six double array into which the
2511
 * geotransformation should be placed.
2512
 *
2513
 * @return TRUE on success or FALSE on failure.
2514
 */
2515

2516
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
909✔
2517
                                  const char *pszExtension,
2518
                                  double *padfGeoTransform)
2519

2520
{
2521
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
909✔
2522
                              nullptr, nullptr);
909✔
2523
}
2524

2525
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
19,429✔
2526
                       double *padfGeoTransform, CSLConstList papszSiblingFiles,
2527
                       char **ppszWorldFileNameOut)
2528
{
2529
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
19,429✔
2530
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
19,429✔
2531

2532
    if (ppszWorldFileNameOut)
19,429✔
2533
        *ppszWorldFileNameOut = nullptr;
17,534✔
2534

2535
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
19,429✔
2536
        return FALSE;
202✔
2537

2538
    /* -------------------------------------------------------------------- */
2539
    /*      If we aren't given an extension, try both the unix and          */
2540
    /*      windows style extensions.                                       */
2541
    /* -------------------------------------------------------------------- */
2542
    if (pszExtension == nullptr)
19,227✔
2543
    {
2544
        const std::string oBaseExt = CPLGetExtension(pszBaseFilename);
9,162✔
2545

2546
        if (oBaseExt.length() < 2)
4,581✔
2547
            return FALSE;
229✔
2548

2549
        // windows version - first + last + 'w'
2550
        char szDerivedExtension[100] = {'\0'};
4,352✔
2551
        szDerivedExtension[0] = oBaseExt[0];
4,352✔
2552
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
4,352✔
2553
        szDerivedExtension[2] = 'w';
4,352✔
2554
        szDerivedExtension[3] = '\0';
4,352✔
2555

2556
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
4,352✔
2557
                               padfGeoTransform, papszSiblingFiles,
2558
                               ppszWorldFileNameOut))
4,352✔
2559
            return TRUE;
52✔
2560

2561
        // unix version - extension + 'w'
2562
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
4,300✔
2563
            return FALSE;
×
2564

2565
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
4,300✔
2566
                 oBaseExt.c_str());
2567
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
4,300✔
2568
                                  padfGeoTransform, papszSiblingFiles,
2569
                                  ppszWorldFileNameOut);
4,300✔
2570
    }
2571

2572
    /* -------------------------------------------------------------------- */
2573
    /*      Skip the leading period in the extension if there is one.       */
2574
    /* -------------------------------------------------------------------- */
2575
    if (*pszExtension == '.')
14,646✔
2576
        pszExtension++;
1,167✔
2577

2578
    /* -------------------------------------------------------------------- */
2579
    /*      Generate upper and lower case versions of the extension.        */
2580
    /* -------------------------------------------------------------------- */
2581
    char szExtUpper[32] = {'\0'};
14,646✔
2582
    char szExtLower[32] = {'\0'};
14,646✔
2583
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
14,646✔
2584
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
14,646✔
2585

2586
    for (int i = 0; szExtUpper[i] != '\0'; i++)
63,407✔
2587
    {
2588
        szExtUpper[i] = static_cast<char>(
48,761✔
2589
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
48,761✔
2590
        szExtLower[i] = static_cast<char>(
48,761✔
2591
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
48,761✔
2592
    }
2593

2594
    const char *pszTFW = CPLResetExtension(pszBaseFilename, szExtLower);
14,646✔
2595

2596
    if (papszSiblingFiles &&
28,139✔
2597
        // cppcheck-suppress knownConditionTrueFalse
2598
        GDALCanReliablyUseSiblingFileList(pszTFW))
13,493✔
2599
    {
2600
        const int iSibling =
2601
            CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW));
13,493✔
2602
        if (iSibling >= 0)
13,493✔
2603
        {
2604
            CPLString osTFWFilename = pszBaseFilename;
65✔
2605
            osTFWFilename.resize(strlen(pszBaseFilename) -
130✔
2606
                                 strlen(CPLGetFilename(pszBaseFilename)));
65✔
2607
            osTFWFilename += papszSiblingFiles[iSibling];
65✔
2608
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
65✔
2609
            {
2610
                if (ppszWorldFileNameOut)
65✔
2611
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
62✔
2612
                return TRUE;
65✔
2613
            }
2614
        }
2615
        return FALSE;
13,428✔
2616
    }
2617

2618
    /* -------------------------------------------------------------------- */
2619
    /*      Try lower case, then upper case.                                */
2620
    /* -------------------------------------------------------------------- */
2621

2622
    VSIStatBufL sStatBuf;
2623
    bool bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,153✔
2624

2625
    if (!bGotTFW && VSIIsCaseSensitiveFS(pszTFW))
1,153✔
2626
    {
2627
        pszTFW = CPLResetExtension(pszBaseFilename, szExtUpper);
1,151✔
2628
        bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,151✔
2629
    }
2630

2631
    if (!bGotTFW)
1,153✔
2632
        return FALSE;
1,151✔
2633

2634
    /* -------------------------------------------------------------------- */
2635
    /*      We found the file, now load and parse it.                       */
2636
    /* -------------------------------------------------------------------- */
2637
    if (GDALLoadWorldFile(pszTFW, padfGeoTransform))
2✔
2638
    {
2639
        if (ppszWorldFileNameOut)
2✔
2640
            *ppszWorldFileNameOut = CPLStrdup(pszTFW);
1✔
2641
        return TRUE;
2✔
2642
    }
2643
    return FALSE;
×
2644
}
2645

2646
/************************************************************************/
2647
/*                         GDALWriteWorldFile()                         */
2648
/*                                                                      */
2649
/*      Helper function for translator implementer wanting              */
2650
/*      support for ESRI world files.                                   */
2651
/************************************************************************/
2652

2653
/**
2654
 * \brief Write ESRI world file.
2655
 *
2656
 * This function writes an ESRI style world file from the passed geotransform.
2657
 *
2658
 * The world file contains an affine transformation with the parameters
2659
 * in a different order than in a geotransform array.
2660
 *
2661
 * <ul>
2662
 * <li> geotransform[1] : width of pixel
2663
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2664
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2665
 * <li> geotransform[5] : height of pixel (but negative)
2666
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2667
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2668
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2669
 * pixel.
2670
 * </ul>
2671
 *
2672
 * @param pszBaseFilename the target raster file.
2673
 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2674
 * @param padfGeoTransform the six double array from which the
2675
 * geotransformation should be read.
2676
 *
2677
 * @return TRUE on success or FALSE on failure.
2678
 */
2679

2680
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
13✔
2681
                                   const char *pszExtension,
2682
                                   double *padfGeoTransform)
2683

2684
{
2685
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
13✔
2686
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
13✔
2687
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
13✔
2688

2689
    /* -------------------------------------------------------------------- */
2690
    /*      Prepare the text to write to the file.                          */
2691
    /* -------------------------------------------------------------------- */
2692
    CPLString osTFWText;
26✔
2693

2694
    osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2695
                     padfGeoTransform[1], padfGeoTransform[4],
13✔
2696
                     padfGeoTransform[2], padfGeoTransform[5],
13✔
2697
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
13✔
2698
                         0.5 * padfGeoTransform[2],
13✔
2699
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
13✔
2700
                         0.5 * padfGeoTransform[5]);
13✔
2701

2702
    /* -------------------------------------------------------------------- */
2703
    /*      Update extension, and write to disk.                            */
2704
    /* -------------------------------------------------------------------- */
2705
    const char *pszTFW = CPLResetExtension(pszBaseFilename, pszExtension);
13✔
2706
    VSILFILE *const fpTFW = VSIFOpenL(pszTFW, "wt");
13✔
2707
    if (fpTFW == nullptr)
13✔
2708
        return FALSE;
×
2709

2710
    const int bRet =
2711
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
13✔
2712
    if (VSIFCloseL(fpTFW) != 0)
13✔
2713
        return FALSE;
×
2714

2715
    return bRet;
13✔
2716
}
2717

2718
/************************************************************************/
2719
/*                          GDALVersionInfo()                           */
2720
/************************************************************************/
2721

2722
/**
2723
 * \brief Get runtime version information.
2724
 *
2725
 * Available pszRequest values:
2726
 * <ul>
2727
 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
2728
 * "30603000", e.g for GDAL 3.6.3.0</li>
2729
 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2730
 * string. i.e. "20230312".</li>
2731
 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2732
 * <li> "--version": Returns one line version message suitable for
2733
 * use in response to --version requests.  i.e. "GDAL 3.6.3, released
2734
 * 2023/03/12"</li>
2735
 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2736
 * the GDAL_DATA directory.
2737
 * </li>
2738
 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2739
 * with information on build time options.</li>
2740
 * </ul>
2741
 *
2742
 * @param pszRequest the type of version info desired, as listed above.
2743
 *
2744
 * @return an internal string containing the requested information.
2745
 */
2746

2747
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
4,884✔
2748

2749
{
2750
    /* -------------------------------------------------------------------- */
2751
    /*      Try to capture as much build information as practical.          */
2752
    /* -------------------------------------------------------------------- */
2753
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
4,884✔
2754
    {
2755
        CPLString osBuildInfo;
1,332✔
2756

2757
#define STRINGIFY_HELPER(x) #x
2758
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2759

2760
#ifdef ESRI_BUILD
2761
        osBuildInfo += "ESRI_BUILD=YES\n";
2762
#endif
2763
#ifdef PAM_ENABLED
2764
        osBuildInfo += "PAM_ENABLED=YES\n";
2765
#endif
2766
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
666✔
2767
#ifdef HAVE_CURL
2768
        osBuildInfo += "CURL_ENABLED=YES\n";
666✔
2769
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
666✔
2770
#endif
2771
#ifdef HAVE_GEOS
2772
        osBuildInfo += "GEOS_ENABLED=YES\n";
666✔
2773
#ifdef GEOS_CAPI_VERSION
2774
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
666✔
2775
#endif
2776
#endif
2777
        osBuildInfo +=
2778
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2779
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
666✔
2780
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
666✔
2781
        osBuildInfo += proj_info().version;
666✔
2782
        osBuildInfo += '\n';
666✔
2783

2784
#ifdef __VERSION__
2785
#ifdef __clang_version__
2786
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2787
#elif defined(__GNUC__)
2788
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
666✔
2789
#elif defined(__INTEL_COMPILER)
2790
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2791
#else
2792
        // STRINGIFY() as we're not sure if its a int or a string
2793
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2794
#endif
2795
#elif defined(_MSC_FULL_VER)
2796
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2797
#elif defined(__INTEL_COMPILER)
2798
        osBuildInfo +=
2799
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2800
#endif
2801
#ifdef CMAKE_UNITY_BUILD
2802
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2803
#endif
2804
#ifdef EMBED_RESOURCE_FILES
2805
        osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2806
#endif
2807
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2808
        osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2809
#endif
2810

2811
#undef STRINGIFY_HELPER
2812
#undef STRINGIFY
2813

2814
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
666✔
2815
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
666✔
2816
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
666✔
2817
    }
2818

2819
    /* -------------------------------------------------------------------- */
2820
    /*      LICENSE is a special case. We try to find and read the          */
2821
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2822
    /* -------------------------------------------------------------------- */
2823
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
4,218✔
2824
    {
2825
#if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2826
        return GDALGetEmbeddedLicense();
2827
#else
2828
        char *pszResultLicence =
2829
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
4✔
2830
        if (pszResultLicence != nullptr)
4✔
2831
        {
2832
            return pszResultLicence;
×
2833
        }
2834

2835
        VSILFILE *fp = nullptr;
4✔
2836
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
2837
#ifdef EMBED_RESOURCE_FILES
2838
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2839
#endif
2840
        const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
4✔
2841
        if (pszFilename != nullptr)
4✔
2842
            fp = VSIFOpenL(pszFilename, "r");
4✔
2843
        if (fp != nullptr)
4✔
2844
        {
2845
            if (VSIFSeekL(fp, 0, SEEK_END) == 0)
4✔
2846
            {
2847
                // TODO(schwehr): Handle if VSITellL returns a value too large
2848
                // for size_t.
2849
                const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
4✔
2850
                if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
4✔
2851
                {
2852
                    pszResultLicence =
2853
                        static_cast<char *>(VSICalloc(1, nLength));
4✔
2854
                    if (pszResultLicence)
4✔
2855
                        CPL_IGNORE_RET_VAL(
4✔
2856
                            VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
4✔
2857
                }
2858
            }
2859

2860
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4✔
2861
        }
2862
#endif
2863

2864
#ifdef EMBED_RESOURCE_FILES
2865
        if (!fp)
2866
        {
2867
            return GDALGetEmbeddedLicense();
2868
        }
2869
#endif
2870

2871
        if (!pszResultLicence)
4✔
2872
        {
2873
            pszResultLicence =
2874
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
×
2875
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
2876
                          "contain additional details.\n");
2877
        }
2878

2879
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
4✔
2880
        return pszResultLicence;
4✔
2881
#endif
2882
    }
2883

2884
    /* -------------------------------------------------------------------- */
2885
    /*      All other strings are fairly small.                             */
2886
    /* -------------------------------------------------------------------- */
2887
    CPLString osVersionInfo;
8,428✔
2888

2889
    if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
4,214✔
2890
        osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
2,629✔
2891
    else if (EQUAL(pszRequest, "RELEASE_DATE"))
1,585✔
2892
        osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
1✔
2893
    else if (EQUAL(pszRequest, "RELEASE_NAME"))
1,584✔
2894
        osVersionInfo.Printf(GDAL_RELEASE_NAME);
1,235✔
2895
    else  // --version
2896
    {
2897
        osVersionInfo.Printf("GDAL %s, released %d/%02d/%02d",
2898
                             GDAL_RELEASE_NAME, GDAL_RELEASE_DATE / 10000,
2899
                             (GDAL_RELEASE_DATE % 10000) / 100,
2900
                             GDAL_RELEASE_DATE % 100);
349✔
2901
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
2902
        // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
2903
        // also true for CLang
2904
        osVersionInfo += " (debug build)";
349✔
2905
#elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
2906
        // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
2907
        // In release mode, the compiler generates an error if you specify
2908
        // _ITERATOR_DEBUG_LEVEL as 2.
2909
        osVersionInfo += " (debug build)";
2910
#endif
2911
    }
2912

2913
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
4,214✔
2914
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
4,214✔
2915
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
4,214✔
2916
}
2917

2918
/************************************************************************/
2919
/*                         GDALCheckVersion()                           */
2920
/************************************************************************/
2921

2922
/** Return TRUE if GDAL library version at runtime matches
2923
   nVersionMajor.nVersionMinor.
2924

2925
    The purpose of this method is to ensure that calling code will run
2926
    with the GDAL version it is compiled for. It is primarily intended
2927
    for external plugins.
2928

2929
    @param nVersionMajor Major version to be tested against
2930
    @param nVersionMinor Minor version to be tested against
2931
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
2932
   method will issue a failure mentioning the name of the calling component.
2933

2934
    @return TRUE if GDAL library version at runtime matches
2935
    nVersionMajor.nVersionMinor, FALSE otherwise.
2936
  */
2937
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
29,695✔
2938
                                 const char *pszCallingComponentName)
2939
{
2940
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
29,695✔
2941
        nVersionMinor == GDAL_VERSION_MINOR)
2942
        return TRUE;
29,695✔
2943

2944
    if (pszCallingComponentName)
×
2945
    {
2946
        CPLError(CE_Failure, CPLE_AppDefined,
×
2947
                 "%s was compiled against GDAL %d.%d, but "
2948
                 "the current library version is %d.%d",
2949
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
2950
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
2951
    }
2952
    return FALSE;
×
2953
}
2954

2955
/************************************************************************/
2956
/*                            GDALDecToDMS()                            */
2957
/************************************************************************/
2958

2959
/** Translate a decimal degrees value to a DMS string with hemisphere.
2960
 */
2961
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
550✔
2962
                                     int nPrecision)
2963

2964
{
2965
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
550✔
2966
}
2967

2968
/************************************************************************/
2969
/*                         GDALPackedDMSToDec()                         */
2970
/************************************************************************/
2971

2972
/**
2973
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2974
 *
2975
 * See CPLPackedDMSToDec().
2976
 */
2977

2978
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
4✔
2979

2980
{
2981
    return CPLPackedDMSToDec(dfPacked);
4✔
2982
}
2983

2984
/************************************************************************/
2985
/*                         GDALDecToPackedDMS()                         */
2986
/************************************************************************/
2987

2988
/**
2989
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2990
 *
2991
 * See CPLDecToPackedDMS().
2992
 */
2993

2994
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
4✔
2995

2996
{
2997
    return CPLDecToPackedDMS(dfDec);
4✔
2998
}
2999

3000
/************************************************************************/
3001
/*                       GDALGCPsToGeoTransform()                       */
3002
/************************************************************************/
3003

3004
/**
3005
 * \brief Generate Geotransform from GCPs.
3006
 *
3007
 * Given a set of GCPs perform first order fit as a geotransform.
3008
 *
3009
 * Due to imprecision in the calculations the fit algorithm will often
3010
 * return non-zero rotational coefficients even if given perfectly non-rotated
3011
 * inputs.  A special case has been implemented for corner corner coordinates
3012
 * given in TL, TR, BR, BL order.  So when using this to get a geotransform
3013
 * from 4 corner coordinates, pass them in this order.
3014
 *
3015
 * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
3016
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
3017
 * set to YES, then bApproxOK will be overridden with TRUE.
3018
 * Starting with GDAL 2.2.2, when exact fit is asked, the
3019
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
3020
 * give the maximum error threshold in pixel. The default is 0.25.
3021
 *
3022
 * @param nGCPCount the number of GCPs being passed in.
3023
 * @param pasGCPs the list of GCP structures.
3024
 * @param padfGeoTransform the six double array in which the affine
3025
 * geotransformation will be returned.
3026
 * @param bApproxOK If FALSE the function will fail if the geotransform is not
3027
 * essentially an exact fit (within 0.25 pixel) for all GCPs.
3028
 *
3029
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
3030
 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
3031
 * and the fit is poor.
3032
 */
3033

3034
// TODO(schwehr): Add consts to args.
3035
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
606✔
3036
                                       double *padfGeoTransform, int bApproxOK)
3037

3038
{
3039
    double dfPixelThreshold = 0.25;
606✔
3040
    if (!bApproxOK)
606✔
3041
    {
3042
        bApproxOK = CPLTestBool(
594✔
3043
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3044
        if (!bApproxOK)
594✔
3045
        {
3046
            // coverity[tainted_data]
3047
            dfPixelThreshold = CPLAtof(CPLGetConfigOption(
594✔
3048
                "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
3049
        }
3050
    }
3051

3052
    /* -------------------------------------------------------------------- */
3053
    /*      Recognise a few special cases.                                  */
3054
    /* -------------------------------------------------------------------- */
3055
    if (nGCPCount < 2)
606✔
3056
        return FALSE;
15✔
3057

3058
    if (nGCPCount == 2)
591✔
3059
    {
3060
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
1✔
3061
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
1✔
3062
            return FALSE;
×
3063

3064
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
1✔
3065
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
1✔
3066
        padfGeoTransform[2] = 0.0;
1✔
3067

3068
        padfGeoTransform[4] = 0.0;
1✔
3069
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
1✔
3070
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
1✔
3071

3072
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
1✔
3073
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
1✔
3074
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
1✔
3075

3076
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
1✔
3077
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
1✔
3078
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
1✔
3079

3080
        return TRUE;
1✔
3081
    }
3082

3083
    /* -------------------------------------------------------------------- */
3084
    /*      Special case of 4 corner coordinates of a non-rotated           */
3085
    /*      image.  The points must be in TL-TR-BR-BL order for now.        */
3086
    /*      This case helps avoid some imprecision in the general           */
3087
    /*      calculations.                                                   */
3088
    /* -------------------------------------------------------------------- */
3089
    if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
590✔
3090
        pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
289✔
3091
        pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
289✔
3092
        pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
288✔
3093
        pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
288✔
3094
        pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
285✔
3095
        pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
285✔
3096
        pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
259✔
3097
        pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
257✔
3098
        pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
257✔
3099
        pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
257✔
3100
        pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
183✔
3101
    {
3102
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
183✔
3103
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
183✔
3104
        padfGeoTransform[2] = 0.0;
183✔
3105
        padfGeoTransform[4] = 0.0;
183✔
3106
        padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
183✔
3107
                              (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
183✔
3108

3109
        padfGeoTransform[0] =
183✔
3110
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
183✔
3111
        padfGeoTransform[3] =
183✔
3112
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
183✔
3113
        return TRUE;
183✔
3114
    }
3115

3116
    /* -------------------------------------------------------------------- */
3117
    /*      Compute source and destination ranges so we can normalize       */
3118
    /*      the values to make the least squares computation more stable.   */
3119
    /* -------------------------------------------------------------------- */
3120
    double min_pixel = pasGCPs[0].dfGCPPixel;
407✔
3121
    double max_pixel = pasGCPs[0].dfGCPPixel;
407✔
3122
    double min_line = pasGCPs[0].dfGCPLine;
407✔
3123
    double max_line = pasGCPs[0].dfGCPLine;
407✔
3124
    double min_geox = pasGCPs[0].dfGCPX;
407✔
3125
    double max_geox = pasGCPs[0].dfGCPX;
407✔
3126
    double min_geoy = pasGCPs[0].dfGCPY;
407✔
3127
    double max_geoy = pasGCPs[0].dfGCPY;
407✔
3128

3129
    for (int i = 1; i < nGCPCount; ++i)
1,731✔
3130
    {
3131
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
1,324✔
3132
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
1,324✔
3133
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
1,324✔
3134
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
1,324✔
3135
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
1,324✔
3136
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
1,324✔
3137
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
1,324✔
3138
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
1,324✔
3139
    }
3140

3141
    double EPS = 1.0e-12;
407✔
3142

3143
    if (std::abs(max_pixel - min_pixel) < EPS ||
811✔
3144
        std::abs(max_line - min_line) < EPS ||
808✔
3145
        std::abs(max_geox - min_geox) < EPS ||
1,215✔
3146
        std::abs(max_geoy - min_geoy) < EPS)
330✔
3147
    {
3148
        return FALSE;  // degenerate in at least one dimension.
77✔
3149
    }
3150

3151
    double pl_normalize[6], geo_normalize[6];
3152

3153
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
330✔
3154
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
330✔
3155
    pl_normalize[2] = 0.0;
330✔
3156
    pl_normalize[3] = -min_line / (max_line - min_line);
330✔
3157
    pl_normalize[4] = 0.0;
330✔
3158
    pl_normalize[5] = 1.0 / (max_line - min_line);
330✔
3159

3160
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
330✔
3161
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
330✔
3162
    geo_normalize[2] = 0.0;
330✔
3163
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
330✔
3164
    geo_normalize[4] = 0.0;
330✔
3165
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
330✔
3166

3167
    /* -------------------------------------------------------------------- */
3168
    /* In the general case, do a least squares error approximation by       */
3169
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
3170
    /* -------------------------------------------------------------------- */
3171

3172
    double sum_x = 0.0;
330✔
3173
    double sum_y = 0.0;
330✔
3174
    double sum_xy = 0.0;
330✔
3175
    double sum_xx = 0.0;
330✔
3176
    double sum_yy = 0.0;
330✔
3177
    double sum_Lon = 0.0;
330✔
3178
    double sum_Lonx = 0.0;
330✔
3179
    double sum_Lony = 0.0;
330✔
3180
    double sum_Lat = 0.0;
330✔
3181
    double sum_Latx = 0.0;
330✔
3182
    double sum_Laty = 0.0;
330✔
3183

3184
    for (int i = 0; i < nGCPCount; ++i)
1,753✔
3185
    {
3186
        double pixel, line, geox, geoy;
3187

3188
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
1,423✔
3189
                              pasGCPs[i].dfGCPLine, &pixel, &line);
1,423✔
3190
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
1,423✔
3191
                              pasGCPs[i].dfGCPY, &geox, &geoy);
1,423✔
3192

3193
        sum_x += pixel;
1,423✔
3194
        sum_y += line;
1,423✔
3195
        sum_xy += pixel * line;
1,423✔
3196
        sum_xx += pixel * pixel;
1,423✔
3197
        sum_yy += line * line;
1,423✔
3198
        sum_Lon += geox;
1,423✔
3199
        sum_Lonx += geox * pixel;
1,423✔
3200
        sum_Lony += geox * line;
1,423✔
3201
        sum_Lat += geoy;
1,423✔
3202
        sum_Latx += geoy * pixel;
1,423✔
3203
        sum_Laty += geoy * line;
1,423✔
3204
    }
3205

3206
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
330✔
3207
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
330✔
3208
                           sum_x * sum_x * sum_yy;
330✔
3209

3210
    /* -------------------------------------------------------------------- */
3211
    /*      If the divisor is zero, there is no valid solution.             */
3212
    /* -------------------------------------------------------------------- */
3213
    if (divisor == 0.0)
330✔
3214
        return FALSE;
×
3215

3216
    /* -------------------------------------------------------------------- */
3217
    /*      Compute top/left origin.                                        */
3218
    /* -------------------------------------------------------------------- */
3219
    double gt_normalized[6] = {0.0};
330✔
3220
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
330✔
3221
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
330✔
3222
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
330✔
3223
                       divisor;
3224

3225
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
330✔
3226
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
330✔
3227
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
330✔
3228
                       divisor;
3229

3230
    /* -------------------------------------------------------------------- */
3231
    /*      Compute X related coefficients.                                 */
3232
    /* -------------------------------------------------------------------- */
3233
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
330✔
3234
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
330✔
3235
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
330✔
3236
                       divisor;
3237

3238
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
330✔
3239
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
330✔
3240
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
330✔
3241
                       divisor;
3242

3243
    /* -------------------------------------------------------------------- */
3244
    /*      Compute Y related coefficients.                                 */
3245
    /* -------------------------------------------------------------------- */
3246
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
330✔
3247
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
330✔
3248
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
330✔
3249
                       divisor;
3250

3251
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
330✔
3252
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
330✔
3253
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
330✔
3254
                       divisor;
3255

3256
    /* -------------------------------------------------------------------- */
3257
    /*      Compose the resulting transformation with the normalization     */
3258
    /*      geotransformations.                                             */
3259
    /* -------------------------------------------------------------------- */
3260
    double gt1p2[6] = {0.0};
330✔
3261
    double inv_geo_normalize[6] = {0.0};
330✔
3262
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
330✔
3263
        return FALSE;
×
3264

3265
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
330✔
3266
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
330✔
3267

3268
    /* -------------------------------------------------------------------- */
3269
    /*      Now check if any of the input points fit this poorly.           */
3270
    /* -------------------------------------------------------------------- */
3271
    if (!bApproxOK)
330✔
3272
    {
3273
        // FIXME? Not sure if it is the more accurate way of computing
3274
        // pixel size
3275
        double dfPixelSize =
3276
            0.5 *
3277
            (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
321✔
3278
             std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
321✔
3279
        if (dfPixelSize == 0.0)
321✔
3280
        {
3281
            CPLDebug("GDAL", "dfPixelSize = 0");
×
3282
            return FALSE;
×
3283
        }
3284

3285
        for (int i = 0; i < nGCPCount; i++)
1,673✔
3286
        {
3287
            const double dfErrorX =
1,360✔
3288
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
1,360✔
3289
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
1,360✔
3290
                 padfGeoTransform[0]) -
1,360✔
3291
                pasGCPs[i].dfGCPX;
1,360✔
3292
            const double dfErrorY =
1,360✔
3293
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
1,360✔
3294
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
1,360✔
3295
                 padfGeoTransform[3]) -
1,360✔
3296
                pasGCPs[i].dfGCPY;
1,360✔
3297

3298
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
2,717✔
3299
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
1,357✔
3300
            {
3301
                CPLDebug("GDAL",
8✔
3302
                         "dfErrorX/dfPixelSize = %.2f, "
3303
                         "dfErrorY/dfPixelSize = %.2f",
3304
                         std::abs(dfErrorX) / dfPixelSize,
8✔
3305
                         std::abs(dfErrorY) / dfPixelSize);
8✔
3306
                return FALSE;
8✔
3307
            }
3308
        }
3309
    }
3310

3311
    return TRUE;
322✔
3312
}
3313

3314
/************************************************************************/
3315
/*                      GDALComposeGeoTransforms()                      */
3316
/************************************************************************/
3317

3318
/**
3319
 * \brief Compose two geotransforms.
3320
 *
3321
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3322
 * being applied to a point.
3323
 *
3324
 * @param padfGT1 the first geotransform, six values.
3325
 * @param padfGT2 the second geotransform, six values.
3326
 * @param padfGTOut the output geotransform, six values, may safely be the same
3327
 * array as padfGT1 or padfGT2.
3328
 */
3329

3330
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
660✔
3331
                              double *padfGTOut)
3332

3333
{
3334
    double gtwrk[6] = {0.0};
660✔
3335
    // We need to think of the geotransform in a more normal form to do
3336
    // the matrix multiple:
3337
    //
3338
    //  __                     __
3339
    //  | gt[1]   gt[2]   gt[0] |
3340
    //  | gt[4]   gt[5]   gt[3] |
3341
    //  |  0.0     0.0     1.0  |
3342
    //  --                     --
3343
    //
3344
    // Then we can use normal matrix multiplication to produce the
3345
    // composed transformation.  I don't actually reform the matrix
3346
    // explicitly which is why the following may seem kind of spagettish.
3347

3348
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
660✔
3349
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
660✔
3350
    gtwrk[0] =
660✔
3351
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
660✔
3352

3353
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
660✔
3354
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
660✔
3355
    gtwrk[3] =
660✔
3356
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
660✔
3357
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
660✔
3358
}
660✔
3359

3360
/************************************************************************/
3361
/*                      StripIrrelevantOptions()                        */
3362
/************************************************************************/
3363

3364
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
8✔
3365
{
3366
    if (psCOL == nullptr)
8✔
3367
        return;
×
3368
    if (nOptions == 0)
8✔
3369
        nOptions = GDAL_OF_RASTER;
5✔
3370
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
8✔
3371
        return;
×
3372

3373
    CPLXMLNode *psPrev = nullptr;
8✔
3374
    for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
175✔
3375
    {
3376
        if (psIter->eType == CXT_Element)
167✔
3377
        {
3378
            CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
167✔
3379
            bool bStrip = false;
167✔
3380
            if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
167✔
3381
                psScope->psChild->pszValue &&
35✔
3382
                EQUAL(psScope->psChild->pszValue, "vector"))
35✔
3383
            {
3384
                bStrip = true;
1✔
3385
            }
3386
            else if (nOptions == GDAL_OF_VECTOR && psScope &&
166✔
3387
                     psScope->psChild && psScope->psChild->pszValue &&
35✔
3388
                     EQUAL(psScope->psChild->pszValue, "raster"))
35✔
3389
            {
3390
                bStrip = true;
33✔
3391
            }
3392
            if (psScope)
167✔
3393
            {
3394
                CPLRemoveXMLChild(psIter, psScope);
70✔
3395
                CPLDestroyXMLNode(psScope);
70✔
3396
            }
3397

3398
            CPLXMLNode *psNext = psIter->psNext;
167✔
3399
            if (bStrip)
167✔
3400
            {
3401
                if (psPrev)
34✔
3402
                    psPrev->psNext = psNext;
13✔
3403
                else if (psCOL->psChild == psIter)
21✔
3404
                    psCOL->psChild = psNext;
21✔
3405
                psIter->psNext = nullptr;
34✔
3406
                CPLDestroyXMLNode(psIter);
34✔
3407
                psIter = psNext;
34✔
3408
            }
3409
            else
3410
            {
3411
                psPrev = psIter;
133✔
3412
                psIter = psNext;
133✔
3413
            }
3414
        }
3415
        else
3416
        {
3417
            psIter = psIter->psNext;
×
3418
        }
3419
    }
3420
}
3421

3422
/************************************************************************/
3423
/*                    GDALGeneralCmdLineProcessor()                     */
3424
/************************************************************************/
3425

3426
/**
3427
 * \brief General utility option processing.
3428
 *
3429
 * This function is intended to provide a variety of generic commandline
3430
 * options for all GDAL commandline utilities.  It takes care of the following
3431
 * commandline options:
3432
 *
3433
 *  --version: report version of GDAL in use.
3434
 *  --build: report build info about GDAL in use.
3435
 *  --license: report GDAL license info.
3436
 *  --formats: report all format drivers configured. Can be used with -json since 3.10
3437
 *  --format [format]: report details of one format driver.
3438
 *  --optfile filename: expand an option file into the argument list.
3439
 *  --config key value: set system configuration option.
3440
 *  --config key=value: set system configuration option (since GDAL 3.9)
3441
 *  --debug [on/off/value]: set debug level.
3442
 *  --mempreload dir: preload directory contents into /vsimem
3443
 *  --pause: Pause for user input (allows time to attach debugger)
3444
 *  --locale [locale]: Install a locale using setlocale() (debugging)
3445
 *  --help-general: report detailed help on general options.
3446
 *
3447
 * The argument array is replaced "in place" and should be freed with
3448
 * CSLDestroy() when no longer needed.  The typical usage looks something
3449
 * like the following.  Note that the formats should be registered so that
3450
 * the --formats and --format options will work properly.
3451
 *
3452
 *  int main( int argc, char ** argv )
3453
 *  {
3454
 *    GDALAllRegister();
3455
 *
3456
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3457
 *    if( argc < 1 )
3458
 *        exit( -argc );
3459
 *
3460
 * @param nArgc number of values in the argument list.
3461
 * @param ppapszArgv pointer to the argument list array (will be updated in
3462
 * place).
3463
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3464
 *                 to determine which drivers should be displayed by --formats.
3465
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3466
 *
3467
 * @return updated nArgc argument count.  Return of 0 requests terminate
3468
 * without error, return of -1 requests exit with error code.
3469
 */
3470

3471
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
1,312✔
3472
                                            int nOptions)
3473

3474
{
3475
    CPLStringList aosReturn;
2,624✔
3476
    int iArg;
3477
    char **papszArgv = *ppapszArgv;
1,312✔
3478

3479
    /* -------------------------------------------------------------------- */
3480
    /*      Preserve the program name.                                      */
3481
    /* -------------------------------------------------------------------- */
3482
    aosReturn.AddString(papszArgv[0]);
1,312✔
3483

3484
    /* ==================================================================== */
3485
    /*      Loop over all arguments.                                        */
3486
    /* ==================================================================== */
3487

3488
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3489
    // detects and warns about a unknown config option.
3490
    for (iArg = 1; iArg < nArgc; iArg++)
8,223✔
3491
    {
3492
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
6,913✔
3493
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
39✔
3494
        {
3495
            if (iArg + 1 >= nArgc)
×
3496
            {
3497
                CPLError(CE_Failure, CPLE_AppDefined,
×
3498
                         "--config option given without a key=value argument.");
3499
                return -1;
×
3500
            }
3501

3502
            const char *pszArg = papszArgv[iArg + 1];
×
3503
            if (strchr(pszArg, '=') != nullptr)
×
3504
            {
3505
                char *pszKey = nullptr;
×
3506
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
×
3507
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
×
3508
                {
3509
                    CPLSetConfigOption(pszKey, pszValue);
×
3510
                }
3511
                CPLFree(pszKey);
×
3512
                ++iArg;
×
3513
            }
3514
            else
3515
            {
3516
                if (iArg + 2 >= nArgc)
×
3517
                {
3518
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3519
                             "--config option given without a key and value "
3520
                             "argument.");
3521
                    return -1;
×
3522
                }
3523

3524
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
×
3525
                    CPLSetConfigOption(papszArgv[iArg + 1],
×
3526
                                       papszArgv[iArg + 2]);
×
3527

3528
                iArg += 2;
×
3529
            }
×
3530
        }
3531
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,913✔
3532
        {
3533
            if (iArg + 1 >= nArgc)
15✔
3534
            {
3535
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3536
                         "--debug option given without debug level.");
3537
                return -1;
2✔
3538
            }
3539

3540
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
13✔
3541
            iArg += 1;
13✔
3542
        }
3543
    }
3544

3545
    for (iArg = 1; iArg < nArgc; iArg++)
8,032✔
3546
    {
3547
        /* --------------------------------------------------------------------
3548
         */
3549
        /*      --version */
3550
        /* --------------------------------------------------------------------
3551
         */
3552
        if (EQUAL(papszArgv[iArg], "--version"))
6,810✔
3553
        {
3554
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
63✔
3555
            return 0;
63✔
3556
        }
3557

3558
        /* --------------------------------------------------------------------
3559
         */
3560
        /*      --build */
3561
        /* --------------------------------------------------------------------
3562
         */
3563
        else if (EQUAL(papszArgv[iArg], "--build"))
6,747✔
3564
        {
3565
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
1✔
3566
            return 0;
1✔
3567
        }
3568

3569
        /* --------------------------------------------------------------------
3570
         */
3571
        /*      --license */
3572
        /* --------------------------------------------------------------------
3573
         */
3574
        else if (EQUAL(papszArgv[iArg], "--license"))
6,746✔
3575
        {
3576
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
1✔
3577
            return 0;
1✔
3578
        }
3579

3580
        /* --------------------------------------------------------------------
3581
         */
3582
        /*      --config */
3583
        /* --------------------------------------------------------------------
3584
         */
3585
        else if (EQUAL(papszArgv[iArg], "--config"))
6,745✔
3586
        {
3587
            if (iArg + 1 >= nArgc)
45✔
3588
            {
3589
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3590
                         "--config option given without a key=value argument.");
3591
                return -1;
2✔
3592
            }
3593

3594
            const char *pszArg = papszArgv[iArg + 1];
43✔
3595
            if (strchr(pszArg, '=') != nullptr)
43✔
3596
            {
3597
                char *pszKey = nullptr;
2✔
3598
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
2✔
3599
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
2✔
3600
                {
3601
                    CPLSetConfigOption(pszKey, pszValue);
2✔
3602
                }
3603
                CPLFree(pszKey);
2✔
3604
                ++iArg;
2✔
3605
            }
3606
            else
3607
            {
3608
                if (iArg + 2 >= nArgc)
41✔
3609
                {
3610
                    CPLError(CE_Failure, CPLE_AppDefined,
2✔
3611
                             "--config option given without a key and value "
3612
                             "argument.");
3613
                    return -1;
2✔
3614
                }
3615

3616
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
39✔
3617
                    CPLSetConfigOption(papszArgv[iArg + 1],
39✔
3618
                                       papszArgv[iArg + 2]);
39✔
3619

3620
                iArg += 2;
39✔
3621
            }
3622
        }
3623

3624
        /* --------------------------------------------------------------------
3625
         */
3626
        /*      --mempreload */
3627
        /* --------------------------------------------------------------------
3628
         */
3629
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
6,700✔
3630
        {
3631
            if (iArg + 1 >= nArgc)
4✔
3632
            {
3633
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3634
                         "--mempreload option given without directory path.");
3635
                return -1;
2✔
3636
            }
3637

3638
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
2✔
3639
            if (CSLCount(papszFiles) == 0)
2✔
3640
            {
3641
                CPLError(CE_Failure, CPLE_AppDefined,
×
3642
                         "--mempreload given invalid or empty directory.");
3643
                return -1;
×
3644
            }
3645

3646
            for (int i = 0; papszFiles[i] != nullptr; i++)
497✔
3647
            {
3648
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
495✔
3649
                    continue;
76✔
3650

3651
                CPLString osOldPath, osNewPath;
491✔
3652
                osOldPath = CPLFormFilename(papszArgv[iArg + 1], papszFiles[i],
491✔
3653
                                            nullptr);
491✔
3654
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
491✔
3655

3656
                VSIStatBufL sStatBuf;
3657
                if (VSIStatL(osOldPath, &sStatBuf) != 0 ||
982✔
3658
                    VSI_ISDIR(sStatBuf.st_mode))
491✔
3659
                {
3660
                    CPLDebug("VSI", "Skipping preload of %s.",
72✔
3661
                             osOldPath.c_str());
3662
                    continue;
72✔
3663
                }
3664

3665
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
419✔
3666
                         osNewPath.c_str());
3667

3668
                if (CPLCopyFile(osNewPath, osOldPath) != 0)
419✔
3669
                {
3670
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3671
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3672
                    return -1;
×
3673
                }
3674
            }
3675

3676
            CSLDestroy(papszFiles);
2✔
3677
            iArg += 1;
2✔
3678
        }
3679

3680
        /* --------------------------------------------------------------------
3681
         */
3682
        /*      --debug */
3683
        /* --------------------------------------------------------------------
3684
         */
3685
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,696✔
3686
        {
3687
            if (iArg + 1 >= nArgc)
13✔
3688
            {
3689
                CPLError(CE_Failure, CPLE_AppDefined,
×
3690
                         "--debug option given without debug level.");
3691
                return -1;
×
3692
            }
3693

3694
            iArg += 1;
13✔
3695
        }
3696

3697
        /* --------------------------------------------------------------------
3698
         */
3699
        /*      --optfile */
3700
        /* --------------------------------------------------------------------
3701
         */
3702
        else if (EQUAL(papszArgv[iArg], "--optfile"))
6,683✔
3703
        {
3704
            if (iArg + 1 >= nArgc)
11✔
3705
            {
3706
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3707
                         "--optfile option given without filename.");
3708
                return -1;
5✔
3709
            }
3710

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

3713
            if (fpOptFile == nullptr)
9✔
3714
            {
3715
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
3716
                         "Unable to open optfile '%s'.\n%s",
3717
                         papszArgv[iArg + 1], VSIStrerror(errno));
2✔
3718
                return -1;
2✔
3719
            }
3720

3721
            const char *pszLine;
3722
            CPLStringList aosArgvOptfile;
7✔
3723
            // dummy value as first argument to please
3724
            // GDALGeneralCmdLineProcessor()
3725
            aosArgvOptfile.AddString("");
7✔
3726
            bool bHasOptfile = false;
7✔
3727
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
23✔
3728
            {
3729
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
16✔
3730
                    continue;
3✔
3731

3732
                char **papszTokens = CSLTokenizeString(pszLine);
13✔
3733
                for (int i = 0;
13✔
3734
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
45✔
3735
                {
3736
                    if (EQUAL(papszTokens[i], "--optfile"))
32✔
3737
                    {
3738
                        // To avoid potential recursion
3739
                        CPLError(CE_Warning, CPLE_AppDefined,
×
3740
                                 "--optfile not supported in a option file");
3741
                        bHasOptfile = true;
×
3742
                    }
3743
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
32✔
3744
                    papszTokens[i] = nullptr;
32✔
3745
                }
3746
                CSLDestroy(papszTokens);
13✔
3747
            }
3748

3749
            VSIFCloseL(fpOptFile);
7✔
3750

3751
            char **papszArgvOptfile = aosArgvOptfile.StealList();
7✔
3752
            if (!bHasOptfile)
7✔
3753
            {
3754
                char **papszArgvOptfileBefore = papszArgvOptfile;
7✔
3755
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
7✔
3756
                                                &papszArgvOptfile,
3757
                                                nOptions) < 0)
7✔
3758
                {
3759
                    CSLDestroy(papszArgvOptfile);
1✔
3760
                    return -1;
1✔
3761
                }
3762
                CSLDestroy(papszArgvOptfileBefore);
6✔
3763
            }
3764

3765
            char **papszIter = papszArgvOptfile + 1;
6✔
3766
            while (*papszIter)
36✔
3767
            {
3768
                aosReturn.AddString(*papszIter);
30✔
3769
                ++papszIter;
30✔
3770
            }
3771
            CSLDestroy(papszArgvOptfile);
6✔
3772

3773
            iArg += 1;
6✔
3774
        }
3775

3776
        /* --------------------------------------------------------------------
3777
         */
3778
        /*      --formats */
3779
        /* --------------------------------------------------------------------
3780
         */
3781
        else if (EQUAL(papszArgv[iArg], "--formats") ||
6,672✔
3782
                 EQUAL(papszArgv[iArg], "--drivers"))
6,667✔
3783
        {
3784
            if (nOptions == 0)
5✔
3785
                nOptions = GDAL_OF_RASTER;
2✔
3786

3787
            bool bJSON = EQUAL(papszArgv[iArg], "--drivers");
5✔
3788
            for (int i = 1; i < nArgc; i++)
10✔
3789
            {
3790
                if (strcmp(papszArgv[i], "-json") == 0 ||
7✔
3791
                    strcmp(papszArgv[i], "--json") == 0)
5✔
3792
                {
3793
                    bJSON = true;
2✔
3794
                    break;
2✔
3795
                }
3796
            }
3797

3798
            if (bJSON)
5✔
3799
            {
3800
                auto poDM = GetGDALDriverManager();
2✔
3801
                CPLJSONArray oArray;
2✔
3802
                const int nDriverCount = poDM->GetDriverCount();
2✔
3803
                for (int iDr = 0; iDr < nDriverCount; ++iDr)
480✔
3804
                {
3805
                    auto poDriver = poDM->GetDriver(iDr);
478✔
3806
                    CSLConstList papszMD = poDriver->GetMetadata();
478✔
3807

3808
                    if (nOptions == GDAL_OF_RASTER &&
717✔
3809
                        !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
239✔
3810
                        continue;
222✔
3811
                    if (nOptions == GDAL_OF_VECTOR &&
641✔
3812
                        !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
239✔
3813
                        continue;
146✔
3814
                    if (nOptions == GDAL_OF_GNM &&
256✔
3815
                        !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3816
                        continue;
×
3817
                    if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
256✔
3818
                        !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER,
×
3819
                                      false))
3820
                        continue;
×
3821

3822
                    CPLJSONObject oJDriver;
512✔
3823
                    oJDriver.Set("short_name", poDriver->GetDescription());
256✔
3824
                    if (const char *pszLongName =
256✔
3825
                            CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
256✔
3826
                        oJDriver.Set("long_name", pszLongName);
256✔
3827
                    CPLJSONArray oJScopes;
512✔
3828
                    if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
256✔
3829
                        oJScopes.Add("raster");
182✔
3830
                    if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
256✔
3831
                        oJScopes.Add("multidimensional_raster");
15✔
3832
                    if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
256✔
3833
                        oJScopes.Add("vector");
112✔
3834
                    oJDriver.Add("scopes", oJScopes);
256✔
3835
                    CPLJSONArray oJCaps;
512✔
3836
                    if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
256✔
3837
                        oJCaps.Add("open");
254✔
3838
                    if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
256✔
3839
                        oJCaps.Add("create");
114✔
3840
                    if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
256✔
3841
                        oJCaps.Add("create_copy");
73✔
3842
                    if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
256✔
3843
                        oJCaps.Add("virtual_io");
212✔
3844
                    oJDriver.Add("capabilities", oJCaps);
256✔
3845

3846
                    if (const char *pszExtensions = CSLFetchNameValueDef(
256✔
3847
                            papszMD, GDAL_DMD_EXTENSIONS,
3848
                            CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3849
                    {
3850
                        const CPLStringList aosExt(
3851
                            CSLTokenizeString2(pszExtensions, " ", 0));
334✔
3852
                        CPLJSONArray oJExts;
167✔
3853
                        for (int i = 0; i < aosExt.size(); ++i)
390✔
3854
                        {
3855
                            oJExts.Add(aosExt[i]);
223✔
3856
                        }
3857
                        oJDriver.Add("file_extensions", oJExts);
167✔
3858
                    }
3859

3860
                    oArray.Add(oJDriver);
256✔
3861
                }
3862
                printf(/*ok*/
2✔
3863
                       "%s\n",
3864
                       oArray.Format(CPLJSONObject::PrettyFormat::Pretty)
4✔
3865
                           .c_str());
3866

3867
                return 0;
2✔
3868
            }
3869

3870
            printf(/*ok*/
3✔
3871
                   "Supported Formats: (ro:read-only, rw:read-write, +:update, "
3872
                   "v:virtual-I/O s:subdatasets)\n");
3873
            for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
720✔
3874
            {
3875
                GDALDriverH hDriver = GDALGetDriver(iDr);
717✔
3876

3877
                const char *pszRFlag = "", *pszWFlag, *pszVirtualIO,
717✔
3878
                           *pszSubdatasets;
3879
                CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
717✔
3880

3881
                if (nOptions == GDAL_OF_RASTER &&
956✔
3882
                    !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
239✔
3883
                    continue;
368✔
3884
                if (nOptions == GDAL_OF_VECTOR &&
1,119✔
3885
                    !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
478✔
3886
                    continue;
292✔
3887
                if (nOptions == GDAL_OF_GNM &&
349✔
3888
                    !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3889
                    continue;
×
3890
                if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
349✔
3891
                    !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
×
3892
                    continue;
×
3893

3894
                if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
349✔
3895
                    pszRFlag = "r";
346✔
3896

3897
                if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
349✔
3898
                    pszWFlag = "w+";
171✔
3899
                else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
178✔
3900
                    pszWFlag = "w";
36✔
3901
                else
3902
                    pszWFlag = "o";
142✔
3903

3904
                if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
349✔
3905
                    pszVirtualIO = "v";
278✔
3906
                else
3907
                    pszVirtualIO = "";
71✔
3908

3909
                if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
349✔
3910
                    pszSubdatasets = "s";
45✔
3911
                else
3912
                    pszSubdatasets = "";
304✔
3913

3914
                CPLString osKind;
698✔
3915
                if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
349✔
3916
                    osKind = "raster";
201✔
3917
                if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
349✔
3918
                {
3919
                    if (!osKind.empty())
18✔
3920
                        osKind += ',';
18✔
3921
                    osKind += "multidimensional raster";
18✔
3922
                }
3923
                if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
349✔
3924
                {
3925
                    if (!osKind.empty())
205✔
3926
                        osKind += ',';
57✔
3927
                    osKind += "vector";
205✔
3928
                }
3929
                if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
349✔
3930
                {
3931
                    if (!osKind.empty())
×
3932
                        osKind += ',';
×
3933
                    osKind += "geography network";
×
3934
                }
3935
                if (osKind.empty())
349✔
3936
                    osKind = "unknown kind";
×
3937

3938
                std::string osExtensions;
698✔
3939
                if (const char *pszExtensions = CSLFetchNameValueDef(
349✔
3940
                        papszMD, GDAL_DMD_EXTENSIONS,
3941
                        CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3942
                {
3943
                    const CPLStringList aosExt(
3944
                        CSLTokenizeString2(pszExtensions, " ", 0));
458✔
3945
                    for (int i = 0; i < aosExt.size(); ++i)
549✔
3946
                    {
3947
                        if (i == 0)
320✔
3948
                            osExtensions = " (*.";
227✔
3949
                        else
3950
                            osExtensions += ", *.";
93✔
3951
                        osExtensions += aosExt[i];
320✔
3952
                    }
3953
                    if (!osExtensions.empty())
229✔
3954
                        osExtensions += ')';
227✔
3955
                }
3956

3957
                printf("  %s -%s- (%s%s%s%s): %s%s\n", /*ok*/
349✔
3958
                       GDALGetDriverShortName(hDriver), osKind.c_str(),
3959
                       pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
3960
                       GDALGetDriverLongName(hDriver), osExtensions.c_str());
3961
            }
3962

3963
            return 0;
3✔
3964
        }
3965

3966
        /* --------------------------------------------------------------------
3967
         */
3968
        /*      --format */
3969
        /* --------------------------------------------------------------------
3970
         */
3971
        else if (EQUAL(papszArgv[iArg], "--format"))
6,667✔
3972
        {
3973
            GDALDriverH hDriver;
3974
            char **papszMD;
3975

3976
            if (iArg + 1 >= nArgc)
5✔
3977
            {
3978
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
3979
                         "--format option given without a format code.");
3980
                return -1;
1✔
3981
            }
3982

3983
            hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
4✔
3984
            if (hDriver == nullptr)
4✔
3985
            {
3986
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
3987
                         "--format option given with format '%s', but that "
3988
                         "format not\nrecognised.  Use the --formats option "
3989
                         "to get a list of available formats,\n"
3990
                         "and use the short code (i.e. GTiff or HFA) as the "
3991
                         "format identifier.\n",
3992
                         papszArgv[iArg + 1]);
1✔
3993
                return -1;
1✔
3994
            }
3995

3996
            printf("Format Details:\n"); /*ok*/
3✔
3997
            printf(/*ok*/ "  Short Name: %s\n",
3✔
3998
                   GDALGetDriverShortName(hDriver));
3999
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
3✔
4000

4001
            papszMD = GDALGetMetadata(hDriver, nullptr);
3✔
4002
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3✔
4003
                printf("  Supports: Raster\n"); /*ok*/
3✔
4004
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3✔
4005
                printf("  Supports: Multidimensional raster\n"); /*ok*/
×
4006
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3✔
4007
                printf("  Supports: Vector\n"); /*ok*/
2✔
4008
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3✔
4009
                printf("  Supports: Geography Network\n"); /*ok*/
×
4010

4011
            const char *pszExt =
4012
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
3✔
4013
            if (pszExt != nullptr)
3✔
4014
                printf("  Extension%s: %s\n", /*ok*/
3✔
4015
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
3✔
4016

4017
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
3✔
4018
                printf("  Mime Type: %s\n", /*ok*/
1✔
4019
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4020
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
3✔
4021
                printf("  Help Topic: %s\n", /*ok*/
3✔
4022
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4023

4024
            if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
3✔
4025
                printf("  Supports: Raster subdatasets\n"); /*ok*/
3✔
4026
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3✔
4027
                printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
3✔
4028
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3✔
4029
                printf(/*ok*/
3✔
4030
                       "  Supports: Create() - Create writable dataset.\n");
4031
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
3✔
4032
                printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
×
4033
                              "multidimensional dataset.\n");
4034
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3✔
4035
                printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
3✔
4036
                              "copying "
4037
                              "another.\n");
4038
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3✔
4039
                printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
3✔
4040
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
3✔
4041
                printf("  Creation Datatypes: %s\n", /*ok*/
3✔
4042
                       CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
4043
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
3✔
4044
                printf("  Creation Field Datatypes: %s\n", /*ok*/
2✔
4045
                       CSLFetchNameValue(papszMD,
4046
                                         GDAL_DMD_CREATIONFIELDDATATYPES));
4047
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
3✔
4048
                printf("  Creation Field Data Sub-types: %s\n", /*ok*/
2✔
4049
                       CSLFetchNameValue(papszMD,
4050
                                         GDAL_DMD_CREATIONFIELDDATASUBTYPES));
4051
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
3✔
4052
                printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
2✔
4053
                              "constraint.\n");
4054
            if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
3✔
4055
                printf(/*ok*/
2✔
4056
                       "  Supports: Creating fields with UNIQUE constraint.\n");
4057
            if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
3✔
4058
                printf(/*ok*/
2✔
4059
                       "  Supports: Creating fields with DEFAULT values.\n");
4060
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
3✔
4061
                /*ok*/ printf(
2✔
4062
                    "  Supports: Creating geometry fields with NOT NULL "
4063
                    "constraint.\n");
4064
            if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
3✔
4065
                             false))
4066
                /*ok*/ printf("  Supports: Writing geometries with given "
2✔
4067
                              "coordinate precision\n");
4068
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
3✔
4069
                printf("  Supports: Reading feature styles.\n"); /*ok*/
×
4070
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
3✔
4071
                printf("  Supports: Writing feature styles.\n"); /*ok*/
×
4072
            if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
3✔
4073
                printf("  Supports: Coordinate epoch.\n"); /*ok*/
1✔
4074
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
3✔
4075
                printf("  Supports: Multiple vector layers.\n"); /*ok*/
2✔
4076
            if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
3✔
4077
                printf("  Supports: Reading field domains.\n"); /*ok*/
2✔
4078
            if (CSLFetchNameValue(papszMD,
3✔
4079
                                  GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
3✔
4080
                printf("  Creation field domain types: %s\n", /*ok*/
2✔
4081
                       CSLFetchNameValue(papszMD,
4082
                                         GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
4083
            if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
3✔
4084
                printf("  Supported SQL dialects: %s\n", /*ok*/
2✔
4085
                       CSLFetchNameValue(papszMD,
4086
                                         GDAL_DMD_SUPPORTED_SQL_DIALECTS));
4087

4088
            for (const char *key :
24✔
4089
                 {GDAL_DMD_CREATIONOPTIONLIST,
4090
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4091
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4092
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4093
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4094
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4095
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4096
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
27✔
4097
            {
4098
                if (CSLFetchNameValue(papszMD, key))
24✔
4099
                {
4100
                    CPLXMLNode *psCOL =
4101
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
5✔
4102
                    StripIrrelevantOptions(psCOL, nOptions);
5✔
4103
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
5✔
4104

4105
                    CPLDestroyXMLNode(psCOL);
5✔
4106

4107
                    printf("\n%s\n", pszFormattedXML); /*ok*/
5✔
4108
                    CPLFree(pszFormattedXML);
5✔
4109
                }
4110
            }
4111

4112
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
3✔
4113
                printf("  Connection prefix: %s\n", /*ok*/
×
4114
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4115

4116
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
3✔
4117
            {
4118
                CPLXMLNode *psCOL = CPLParseXMLString(
3✔
4119
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4120
                StripIrrelevantOptions(psCOL, nOptions);
3✔
4121
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
3✔
4122

4123
                CPLDestroyXMLNode(psCOL);
3✔
4124

4125
                printf("%s\n", pszFormattedXML); /*ok*/
3✔
4126
                CPLFree(pszFormattedXML);
3✔
4127
            }
4128

4129
            bool bFirstOtherOption = true;
3✔
4130
            for (char **papszIter = papszMD; papszIter && *papszIter;
112✔
4131
                 ++papszIter)
4132
            {
4133
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
109✔
4134
                    !STARTS_WITH(*papszIter, "DMD_") &&
49✔
4135
                    !STARTS_WITH(*papszIter, "DS_") &&
14✔
4136
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
12✔
4137
                {
4138
                    if (bFirstOtherOption)
12✔
4139
                        printf("  Other metadata items:\n"); /*ok*/
3✔
4140
                    bFirstOtherOption = false;
12✔
4141
                    printf("    %s\n", *papszIter); /*ok*/
12✔
4142
                }
4143
            }
4144

4145
            return 0;
3✔
4146
        }
4147

4148
        /* --------------------------------------------------------------------
4149
         */
4150
        /*      --help-general */
4151
        /* --------------------------------------------------------------------
4152
         */
4153
        else if (EQUAL(papszArgv[iArg], "--help-general"))
6,662✔
4154
        {
4155
            printf("Generic GDAL utility command options:\n");       /*ok*/
2✔
4156
            printf("  --version: report version of GDAL in use.\n"); /*ok*/
2✔
4157
            /*ok*/ printf(
2✔
4158
                "  --build: report detailed information about GDAL in "
4159
                "use.\n");
4160
            printf("  --license: report GDAL license info.\n"); /*ok*/
2✔
4161
            printf(                                             /*ok*/
2✔
4162
                   "  --formats: report all configured format drivers.\n"); /*ok*/
4163
            printf("  --format [<format>]: details of one format.\n"); /*ok*/
2✔
4164
            /*ok*/ printf(
2✔
4165
                "  --optfile filename: expand an option file into the "
4166
                "argument list.\n");
4167
            printf(/*ok*/
2✔
4168
                   "  --config <key> <value> or --config <key>=<value>: set "
4169
                   "system configuration option.\n");               /*ok*/
4170
            printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
2✔
4171
            /*ok*/ printf(                                          /*ok*/
2✔
4172
                          "  --pause: wait for user input, time to attach "
4173
                          "debugger\n");
4174
            printf("  --locale [<locale>]: install locale for debugging " /*ok*/
2✔
4175
                   "(i.e. en_US.UTF-8)\n");
4176
            printf("  --help-general: report detailed help on general " /*ok*/
2✔
4177
                   "options.\n");
4178

4179
            return 0;
2✔
4180
        }
4181

4182
        /* --------------------------------------------------------------------
4183
         */
4184
        /*      --locale */
4185
        /* --------------------------------------------------------------------
4186
         */
4187
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
6,660✔
4188
        {
4189
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
2✔
4190
        }
4191

4192
        /* --------------------------------------------------------------------
4193
         */
4194
        /*      --pause */
4195
        /* --------------------------------------------------------------------
4196
         */
4197
        else if (EQUAL(papszArgv[iArg], "--pause"))
6,658✔
4198
        {
4199
            std::cout << "Hit <ENTER> to Continue." << std::endl;
×
4200
            std::cin.clear();
×
4201
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
×
4202
        }
4203

4204
        /* --------------------------------------------------------------------
4205
         */
4206
        /*      Carry through unrecognized options. */
4207
        /* --------------------------------------------------------------------
4208
         */
4209
        else
4210
        {
4211
            aosReturn.AddString(papszArgv[iArg]);
6,658✔
4212
        }
4213
    }
4214

4215
    const int nSize = aosReturn.size();
1,222✔
4216
    *ppapszArgv = aosReturn.StealList();
1,222✔
4217

4218
    return nSize;
1,222✔
4219
}
4220

4221
/************************************************************************/
4222
/*                          _FetchDblFromMD()                           */
4223
/************************************************************************/
4224

4225
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
1,660✔
4226
                            double *padfTarget, int nCount, double dfDefault)
4227

4228
{
4229
    char szFullKey[200];
4230

4231
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
1,660✔
4232

4233
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
1,660✔
4234

4235
    for (int i = 0; i < nCount; i++)
9,628✔
4236
        padfTarget[i] = dfDefault;
7,968✔
4237

4238
    if (pszValue == nullptr)
1,660✔
4239
        return false;
408✔
4240

4241
    if (nCount == 1)
1,252✔
4242
    {
4243
        *padfTarget = CPLAtofM(pszValue);
920✔
4244
        return true;
920✔
4245
    }
4246

4247
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
332✔
4248

4249
    if (CSLCount(papszTokens) != nCount)
332✔
4250
    {
4251
        CSLDestroy(papszTokens);
×
4252
        return false;
×
4253
    }
4254

4255
    for (int i = 0; i < nCount; i++)
6,972✔
4256
        padfTarget[i] = CPLAtofM(papszTokens[i]);
6,640✔
4257

4258
    CSLDestroy(papszTokens);
332✔
4259

4260
    return true;
332✔
4261
}
4262

4263
/************************************************************************/
4264
/*                         GDALExtractRPCInfo()                         */
4265
/************************************************************************/
4266

4267
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4268
 *
4269
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4270
 *
4271
 * @param papszMD Dictionary of metadata representing RPC
4272
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4273
 * @return TRUE in case of success. FALSE in case of failure.
4274
 */
4275
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
×
4276

4277
{
4278
    GDALRPCInfoV2 sRPC;
4279
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
×
4280
        return FALSE;
×
4281
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
×
4282
    return TRUE;
×
4283
}
4284

4285
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4286
 *
4287
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4288
 *
4289
 * @param papszMD Dictionary of metadata representing RPC
4290
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4291
 * @return TRUE in case of success. FALSE in case of failure.
4292
 */
4293
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
83✔
4294

4295
{
4296
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
83✔
4297
        return FALSE;
×
4298

4299
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
83✔
4300
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
83✔
4301
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
249✔
4302
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
83✔
4303
    {
4304
        CPLError(CE_Failure, CPLE_AppDefined,
×
4305
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
4306
        return FALSE;
×
4307
    }
4308

4309
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
83✔
4310
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
83✔
4311
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
83✔
4312
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
83✔
4313
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
83✔
4314
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
83✔
4315
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
83✔
4316
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
83✔
4317
                    1.0);
4318
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
83✔
4319
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
83✔
4320
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
83✔
4321
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
83✔
4322

4323
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
83✔
4324
                    0.0);
4325
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
83✔
4326
                    0.0);
4327
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
83✔
4328
                    0.0);
4329
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
83✔
4330
                    0.0);
4331

4332
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
83✔
4333
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
83✔
4334
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
83✔
4335
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
83✔
4336

4337
    return TRUE;
83✔
4338
}
4339

4340
/************************************************************************/
4341
/*                     GDALFindAssociatedAuxFile()                      */
4342
/************************************************************************/
4343

4344
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
10,082✔
4345
                                       GDALAccess eAccess,
4346
                                       GDALDataset *poDependentDS)
4347

4348
{
4349
    const char *pszAuxSuffixLC = "aux";
10,082✔
4350
    const char *pszAuxSuffixUC = "AUX";
10,082✔
4351

4352
    if (EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC))
10,082✔
4353
        return nullptr;
35✔
4354

4355
    /* -------------------------------------------------------------------- */
4356
    /*      Don't even try to look for an .aux file if we don't have a      */
4357
    /*      path of any kind.                                               */
4358
    /* -------------------------------------------------------------------- */
4359
    if (strlen(pszBasename) == 0)
10,047✔
4360
        return nullptr;
38✔
4361

4362
    /* -------------------------------------------------------------------- */
4363
    /*      We didn't find that, so try and find a corresponding aux        */
4364
    /*      file.  Check that we are the dependent file of the aux          */
4365
    /*      file, or if we aren't verify that the dependent file does       */
4366
    /*      not exist, likely mean it is us but some sort of renaming       */
4367
    /*      has occurred.                                                   */
4368
    /* -------------------------------------------------------------------- */
4369
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
20,018✔
4370
    CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC);
10,009✔
4371
    GDALDataset *poODS = nullptr;
10,009✔
4372
    GByte abyHeader[32];
4373

4374
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
10,009✔
4375

4376
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,009✔
4377
    {
4378
        // Can't found file with lower case suffix. Try the upper case one.
4379
        osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC);
9,959✔
4380
        fp = VSIFOpenL(osAuxFilename, "rb");
9,959✔
4381
    }
4382

4383
    if (fp != nullptr)
10,009✔
4384
    {
4385
        if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
100✔
4386
            STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
50✔
4387
                           "EHFA_HEADER_TAG"))
4388
        {
4389
            /* Avoid causing failure in opening of main file from SWIG bindings
4390
             */
4391
            /* when auxiliary file cannot be opened (#3269) */
4392
            CPLTurnFailureIntoWarning(TRUE);
20✔
4393
            if (poDependentDS != nullptr && poDependentDS->GetShared())
20✔
4394
                poODS = GDALDataset::FromHandle(
×
4395
                    GDALOpenShared(osAuxFilename, eAccess));
4396
            else
4397
                poODS =
4398
                    GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
20✔
4399
            CPLTurnFailureIntoWarning(FALSE);
20✔
4400
        }
4401
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
50✔
4402
    }
4403

4404
    /* -------------------------------------------------------------------- */
4405
    /*      Try replacing extension with .aux                               */
4406
    /* -------------------------------------------------------------------- */
4407
    if (poODS != nullptr)
10,009✔
4408
    {
4409
        const char *pszDep =
4410
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
20✔
4411
        if (pszDep == nullptr)
20✔
4412
        {
4413
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
×
4414
                     osAuxFilename.c_str());
4415
            GDALClose(poODS);
×
4416
            poODS = nullptr;
×
4417
        }
4418
        else if (!EQUAL(pszDep, osJustFile))
20✔
4419
        {
4420
            VSIStatBufL sStatBuf;
4421

4422
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4423
            {
4424
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4425
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4426
                GDALClose(poODS);
×
4427
                poODS = nullptr;
×
4428
            }
4429
            else
4430
            {
4431
                CPLDebug("AUX",
×
4432
                         "%s is for file %s, not %s, but since\n"
4433
                         "%s does not exist, we will use .aux file as our own.",
4434
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4435
                         pszDep);
4436
            }
4437
        }
4438

4439
        /* --------------------------------------------------------------------
4440
         */
4441
        /*      Confirm that the aux file matches the configuration of the */
4442
        /*      dependent dataset. */
4443
        /* --------------------------------------------------------------------
4444
         */
4445
        if (poODS != nullptr && poDependentDS != nullptr &&
40✔
4446
            (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
20✔
4447
             poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
20✔
4448
             poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
17✔
4449
        {
4450
            CPLDebug("AUX",
3✔
4451
                     "Ignoring aux file %s as its raster configuration\n"
4452
                     "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4453
                     "x %dB)",
4454
                     osAuxFilename.c_str(), poODS->GetRasterXSize(),
4455
                     poODS->GetRasterYSize(), poODS->GetRasterCount(),
4456
                     poDependentDS->GetRasterXSize(),
4457
                     poDependentDS->GetRasterYSize(),
4458
                     poDependentDS->GetRasterCount());
4459

4460
            GDALClose(poODS);
3✔
4461
            poODS = nullptr;
3✔
4462
        }
4463
    }
4464

4465
    /* -------------------------------------------------------------------- */
4466
    /*      Try appending .aux to the end of the filename.                  */
4467
    /* -------------------------------------------------------------------- */
4468
    if (poODS == nullptr)
10,009✔
4469
    {
4470
        osAuxFilename = pszBasename;
9,992✔
4471
        osAuxFilename += ".";
9,992✔
4472
        osAuxFilename += pszAuxSuffixLC;
9,992✔
4473
        fp = VSIFOpenL(osAuxFilename, "rb");
9,992✔
4474
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
9,992✔
4475
        {
4476
            // Can't found file with lower case suffix. Try the upper case one.
4477
            osAuxFilename = pszBasename;
9,969✔
4478
            osAuxFilename += ".";
9,969✔
4479
            osAuxFilename += pszAuxSuffixUC;
9,969✔
4480
            fp = VSIFOpenL(osAuxFilename, "rb");
9,969✔
4481
        }
4482

4483
        if (fp != nullptr)
9,992✔
4484
        {
4485
            if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
46✔
4486
                STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
23✔
4487
                               "EHFA_HEADER_TAG"))
4488
            {
4489
                /* Avoid causing failure in opening of main file from SWIG
4490
                 * bindings */
4491
                /* when auxiliary file cannot be opened (#3269) */
4492
                CPLTurnFailureIntoWarning(TRUE);
×
4493
                if (poDependentDS != nullptr && poDependentDS->GetShared())
×
4494
                    poODS = GDALDataset::FromHandle(
×
4495
                        GDALOpenShared(osAuxFilename, eAccess));
4496
                else
4497
                    poODS = GDALDataset::FromHandle(
×
4498
                        GDALOpen(osAuxFilename, eAccess));
4499
                CPLTurnFailureIntoWarning(FALSE);
×
4500
            }
4501
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
23✔
4502
        }
4503

4504
        if (poODS != nullptr)
9,992✔
4505
        {
4506
            const char *pszDep =
4507
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
×
4508
            if (pszDep == nullptr)
×
4509
            {
4510
                CPLDebug("AUX",
×
4511
                         "Found %s but it has no dependent file, ignoring.",
4512
                         osAuxFilename.c_str());
4513
                GDALClose(poODS);
×
4514
                poODS = nullptr;
×
4515
            }
4516
            else if (!EQUAL(pszDep, osJustFile))
×
4517
            {
4518
                VSIStatBufL sStatBuf;
4519

4520
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4521
                {
4522
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4523
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4524
                    GDALClose(poODS);
×
4525
                    poODS = nullptr;
×
4526
                }
4527
                else
4528
                {
4529
                    CPLDebug(
×
4530
                        "AUX",
4531
                        "%s is for file %s, not %s, but since\n"
4532
                        "%s does not exist, we will use .aux file as our own.",
4533
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4534
                        pszDep);
4535
                }
4536
            }
4537
        }
4538
    }
4539

4540
    /* -------------------------------------------------------------------- */
4541
    /*      Confirm that the aux file matches the configuration of the      */
4542
    /*      dependent dataset.                                              */
4543
    /* -------------------------------------------------------------------- */
4544
    if (poODS != nullptr && poDependentDS != nullptr &&
10,026✔
4545
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
17✔
4546
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
17✔
4547
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
17✔
4548
    {
4549
        CPLDebug(
×
4550
            "AUX",
4551
            "Ignoring aux file %s as its raster configuration\n"
4552
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4553
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4554
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4555
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4556
            poDependentDS->GetRasterCount());
4557

4558
        GDALClose(poODS);
×
4559
        poODS = nullptr;
×
4560
    }
4561

4562
    return poODS;
10,009✔
4563
}
4564

4565
/************************************************************************/
4566
/* Infrastructure to check that dataset characteristics are valid       */
4567
/************************************************************************/
4568

4569
CPL_C_START
4570

4571
/**
4572
 * \brief Return TRUE if the dataset dimensions are valid.
4573
 *
4574
 * @param nXSize raster width
4575
 * @param nYSize raster height
4576
 *
4577
 * @since GDAL 1.7.0
4578
 */
4579
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
4,653✔
4580
{
4581
    if (nXSize <= 0 || nYSize <= 0)
4,653✔
4582
    {
4583
        CPLError(CE_Failure, CPLE_AppDefined,
8✔
4584
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4585
        return FALSE;
8✔
4586
    }
4587
    return TRUE;
4,645✔
4588
}
4589

4590
/**
4591
 * \brief Return TRUE if the band count is valid.
4592
 *
4593
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4594
 * the band count will be compared to the maximum number of band allowed.
4595
 * If not defined, the maximum number allowed is 65536.
4596
 *
4597
 * @param nBands the band count
4598
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4599
 *
4600
 * @since GDAL 1.7.0
4601
 */
4602

4603
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
6,783✔
4604
{
4605
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
6,783✔
4606
    {
4607
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
12✔
4608
                 nBands);
4609
        return FALSE;
12✔
4610
    }
4611
    const char *pszMaxBandCount =
4612
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
6,771✔
4613
    /* coverity[tainted_data] */
4614
    int nMaxBands = atoi(pszMaxBandCount);
6,771✔
4615
    if (nBands > nMaxBands)
6,771✔
4616
    {
4617
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
4618
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4619
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4620
                 "legitimate number.",
4621
                 nBands, nMaxBands);
4622
        return FALSE;
2✔
4623
    }
4624
    return TRUE;
6,769✔
4625
}
4626

4627
CPL_C_END
4628

4629
/************************************************************************/
4630
/*                     GDALSerializeGCPListToXML()                      */
4631
/************************************************************************/
4632

4633
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
24✔
4634
                               const std::vector<gdal::GCP> &asGCPs,
4635
                               const OGRSpatialReference *poGCP_SRS)
4636
{
4637
    CPLString oFmt;
48✔
4638

4639
    CPLXMLNode *psPamGCPList =
4640
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
24✔
4641

4642
    CPLXMLNode *psLastChild = nullptr;
24✔
4643

4644
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
24✔
4645
    {
4646
        char *pszWKT = nullptr;
9✔
4647
        poGCP_SRS->exportToWkt(&pszWKT);
9✔
4648
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
9✔
4649
        CPLFree(pszWKT);
9✔
4650
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
9✔
4651
        CPLString osMapping;
9✔
4652
        for (size_t i = 0; i < mapping.size(); ++i)
27✔
4653
        {
4654
            if (!osMapping.empty())
18✔
4655
                osMapping += ",";
9✔
4656
            osMapping += CPLSPrintf("%d", mapping[i]);
18✔
4657
        }
4658
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
9✔
4659
                       osMapping.c_str());
4660

4661
        psLastChild = psPamGCPList->psChild->psNext;
9✔
4662
    }
4663

4664
    for (const gdal::GCP &gcp : asGCPs)
21,936✔
4665
    {
4666
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
21,912✔
4667

4668
        if (psLastChild == nullptr)
21,912✔
4669
            psPamGCPList->psChild = psXMLGCP;
15✔
4670
        else
4671
            psLastChild->psNext = psXMLGCP;
21,897✔
4672
        psLastChild = psXMLGCP;
21,912✔
4673

4674
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
21,912✔
4675

4676
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
21,912✔
4677
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
×
4678

4679
        CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
21,912✔
4680

4681
        CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
21,912✔
4682

4683
        CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
21,912✔
4684

4685
        CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
21,912✔
4686

4687
        /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
4688
         * back */
4689
        if (gcp.Z() != 0.0)
21,912✔
4690
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
21,860✔
4691
    }
4692
}
24✔
4693

4694
/************************************************************************/
4695
/*                     GDALDeserializeGCPListFromXML()                  */
4696
/************************************************************************/
4697

4698
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
95✔
4699
                                   std::vector<gdal::GCP> &asGCPs,
4700
                                   OGRSpatialReference **ppoGCP_SRS)
4701
{
4702
    if (ppoGCP_SRS)
95✔
4703
    {
4704
        const char *pszRawProj =
4705
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
80✔
4706

4707
        *ppoGCP_SRS = nullptr;
80✔
4708
        if (pszRawProj && pszRawProj[0])
80✔
4709
        {
4710
            *ppoGCP_SRS = new OGRSpatialReference();
62✔
4711
            (*ppoGCP_SRS)
4712
                ->SetFromUserInput(
62✔
4713
                    pszRawProj,
4714
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4715

4716
            const char *pszMapping =
4717
                CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
62✔
4718
            if (pszMapping)
62✔
4719
            {
4720
                char **papszTokens =
4721
                    CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14✔
4722
                std::vector<int> anMapping;
28✔
4723
                for (int i = 0; papszTokens && papszTokens[i]; i++)
42✔
4724
                {
4725
                    anMapping.push_back(atoi(papszTokens[i]));
28✔
4726
                }
4727
                CSLDestroy(papszTokens);
14✔
4728
                (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
14✔
4729
            }
4730
            else
4731
            {
4732
                (*ppoGCP_SRS)
4733
                    ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
48✔
4734
            }
4735
        }
4736
    }
4737

4738
    asGCPs.clear();
95✔
4739
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
24,671✔
4740
         psXMLGCP = psXMLGCP->psNext)
24,576✔
4741
    {
4742
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
24,576✔
4743
            continue;
86✔
4744

4745
        gdal::GCP gcp;
48,980✔
4746
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
24,490✔
4747
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
24,490✔
4748

4749
        const auto ParseDoubleValue =
4750
            [psXMLGCP](const char *pszParameter, double &dfVal)
97,960✔
4751
        {
4752
            const char *pszVal =
4753
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
97,960✔
4754
            if (!pszVal)
97,960✔
4755
            {
4756
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
×
4757
                         pszParameter);
4758
                return false;
×
4759
            }
4760
            char *endptr = nullptr;
97,960✔
4761
            dfVal = CPLStrtod(pszVal, &endptr);
97,960✔
4762
            if (endptr == pszVal)
97,960✔
4763
            {
4764
                CPLError(CE_Failure, CPLE_AppDefined,
×
4765
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4766
                return false;
×
4767
            }
4768
            return true;
97,960✔
4769
        };
24,490✔
4770

4771
        bool bOK = true;
24,490✔
4772
        if (!ParseDoubleValue("Pixel", gcp.Pixel()))
24,490✔
4773
            bOK = false;
×
4774
        if (!ParseDoubleValue("Line", gcp.Line()))
24,490✔
4775
            bOK = false;
×
4776
        if (!ParseDoubleValue("X", gcp.X()))
24,490✔
4777
            bOK = false;
×
4778
        if (!ParseDoubleValue("Y", gcp.Y()))
24,490✔
4779
            bOK = false;
×
4780
        const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
24,490✔
4781
        if (pszZ == nullptr)
24,490✔
4782
        {
4783
            // Note: GDAL 1.10.1 and older generated #GCPZ,
4784
            // but could not read it back.
4785
            pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
2,422✔
4786
        }
4787
        char *endptr = nullptr;
24,490✔
4788
        gcp.Z() = CPLStrtod(pszZ, &endptr);
24,490✔
4789
        if (endptr == pszZ)
24,490✔
4790
        {
4791
            CPLError(CE_Failure, CPLE_AppDefined,
×
4792
                     "GCP#Z=%s is an invalid value", pszZ);
4793
            bOK = false;
×
4794
        }
4795

4796
        if (bOK)
24,490✔
4797
        {
4798
            asGCPs.emplace_back(std::move(gcp));
24,490✔
4799
        }
4800
    }
4801
}
95✔
4802

4803
/************************************************************************/
4804
/*                   GDALSerializeOpenOptionsToXML()                    */
4805
/************************************************************************/
4806

4807
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
2,611✔
4808
                                   CSLConstList papszOpenOptions)
4809
{
4810
    if (papszOpenOptions != nullptr)
2,611✔
4811
    {
4812
        CPLXMLNode *psOpenOptions =
4813
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
5✔
4814
        CPLXMLNode *psLastChild = nullptr;
5✔
4815

4816
        for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
10✔
4817
             papszIter++)
4818
        {
4819
            const char *pszRawValue;
4820
            char *pszKey = nullptr;
5✔
4821
            CPLXMLNode *psOOI;
4822

4823
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
5✔
4824

4825
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
5✔
4826
            if (psLastChild == nullptr)
5✔
4827
                psOpenOptions->psChild = psOOI;
5✔
4828
            else
4829
                psLastChild->psNext = psOOI;
×
4830
            psLastChild = psOOI;
5✔
4831

4832
            CPLSetXMLValue(psOOI, "#key", pszKey);
5✔
4833
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
5✔
4834

4835
            CPLFree(pszKey);
5✔
4836
        }
4837
    }
4838
}
2,611✔
4839

4840
/************************************************************************/
4841
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4842
/************************************************************************/
4843

4844
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
3,844✔
4845
{
4846
    char **papszOpenOptions = nullptr;
3,844✔
4847
    const CPLXMLNode *psOpenOptions =
4848
        CPLGetXMLNode(psParentNode, "OpenOptions");
3,844✔
4849
    if (psOpenOptions != nullptr)
3,844✔
4850
    {
4851
        const CPLXMLNode *psOOI;
4852
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
34✔
4853
             psOOI = psOOI->psNext)
17✔
4854
        {
4855
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
17✔
4856
                psOOI->psChild == nullptr ||
17✔
4857
                psOOI->psChild->psNext == nullptr ||
17✔
4858
                psOOI->psChild->eType != CXT_Attribute ||
17✔
4859
                psOOI->psChild->psChild == nullptr)
17✔
4860
                continue;
×
4861

4862
            char *pszName = psOOI->psChild->psChild->pszValue;
17✔
4863
            char *pszValue = psOOI->psChild->psNext->pszValue;
17✔
4864
            if (pszName != nullptr && pszValue != nullptr)
17✔
4865
                papszOpenOptions =
4866
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
17✔
4867
        }
4868
    }
4869
    return papszOpenOptions;
3,844✔
4870
}
4871

4872
/************************************************************************/
4873
/*                    GDALRasterIOGetResampleAlg()                      */
4874
/************************************************************************/
4875

4876
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
2,241✔
4877
{
4878
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
2,241✔
4879
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
2,241✔
4880
        eResampleAlg = GRIORA_NearestNeighbour;
18✔
4881
    else if (EQUAL(pszResampling, "BILINEAR"))
2,223✔
4882
        eResampleAlg = GRIORA_Bilinear;
2,054✔
4883
    else if (EQUAL(pszResampling, "CUBIC"))
169✔
4884
        eResampleAlg = GRIORA_Cubic;
114✔
4885
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
55✔
4886
        eResampleAlg = GRIORA_CubicSpline;
4✔
4887
    else if (EQUAL(pszResampling, "LANCZOS"))
51✔
4888
        eResampleAlg = GRIORA_Lanczos;
1✔
4889
    else if (EQUAL(pszResampling, "AVERAGE"))
50✔
4890
        eResampleAlg = GRIORA_Average;
43✔
4891
    else if (EQUAL(pszResampling, "RMS"))
7✔
4892
        eResampleAlg = GRIORA_RMS;
1✔
4893
    else if (EQUAL(pszResampling, "MODE"))
6✔
4894
        eResampleAlg = GRIORA_Mode;
5✔
4895
    else if (EQUAL(pszResampling, "GAUSS"))
1✔
4896
        eResampleAlg = GRIORA_Gauss;
1✔
4897
    else
4898
        CPLError(CE_Warning, CPLE_NotSupported,
×
4899
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
4900
    return eResampleAlg;
2,241✔
4901
}
4902

4903
/************************************************************************/
4904
/*                    GDALRasterIOGetResampleAlgStr()                   */
4905
/************************************************************************/
4906

4907
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
1✔
4908
{
4909
    switch (eResampleAlg)
1✔
4910
    {
4911
        case GRIORA_NearestNeighbour:
×
4912
            return "NearestNeighbour";
×
4913
        case GRIORA_Bilinear:
×
4914
            return "Bilinear";
×
4915
        case GRIORA_Cubic:
1✔
4916
            return "Cubic";
1✔
4917
        case GRIORA_CubicSpline:
×
4918
            return "CubicSpline";
×
4919
        case GRIORA_Lanczos:
×
4920
            return "Lanczos";
×
4921
        case GRIORA_Average:
×
4922
            return "Average";
×
4923
        case GRIORA_RMS:
×
4924
            return "RMS";
×
4925
        case GRIORA_Mode:
×
4926
            return "Mode";
×
4927
        case GRIORA_Gauss:
×
4928
            return "Gauss";
×
4929
        default:
×
4930
            CPLAssert(false);
×
4931
            return "Unknown";
4932
    }
4933
}
4934

4935
/************************************************************************/
4936
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
4937
/************************************************************************/
4938

4939
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
4,461,020✔
4940
                                        int nXSize, int nYSize, int nBufXSize,
4941
                                        int nBufYSize)
4942
{
4943
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
4,461,020✔
4944
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
370,046✔
4945
    {
4946
        const char *pszResampling =
4947
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
367,912✔
4948
        if (pszResampling != nullptr)
367,912✔
4949
        {
4950
            psExtraArg->eResampleAlg =
1✔
4951
                GDALRasterIOGetResampleAlg(pszResampling);
1✔
4952
        }
4953
    }
4954
}
4,461,020✔
4955

4956
/************************************************************************/
4957
/*                     GDALCanFileAcceptSidecarFile()                   */
4958
/************************************************************************/
4959

4960
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
105,654✔
4961
{
4962
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
105,654✔
4963
        return FALSE;
×
4964
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
4965
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
105,654✔
4966
        return FALSE;
402✔
4967
    return TRUE;
105,252✔
4968
}
4969

4970
/************************************************************************/
4971
/*                   GDALCanReliablyUseSiblingFileList()                */
4972
/************************************************************************/
4973

4974
/* Try to address https://github.com/OSGeo/gdal/issues/2903 */
4975
/* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
4976
/*   (normalization form decomposed). The filesystem takes care of converting */
4977
/*   precomposed form as often coming from user interface to this NFD variant */
4978
/*   See
4979
 * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
4980
 */
4981
/*   And readdir() will return such NFD variant encoding. Consequently comparing
4982
 */
4983
/*   the user filename with ones with readdir() is not reliable */
4984
/* - APFS preserves both case and normalization of the filename on disk in all
4985
 */
4986
/*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
4987
 */
4988
/*   the case-insensitive and case-sensitive variants, using a hash-based native
4989
 */
4990
/*   normalization scheme. APFS preserves the normalization of the filename and
4991
 */
4992
/*   uses hashes of the normalized form of the filename to provide normalization
4993
 */
4994
/*   insensitivity. */
4995
/*   From
4996
 * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
4997
 */
4998
/*   Issues might still arise if the file has been created using one of the
4999
 * UTF-8 */
5000
/*   encoding (likely the decomposed one if using MacOS specific API), but the
5001
 */
5002
/*   string passed to GDAL for opening would be with another one (likely the
5003
 * precomposed one) */
5004
bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
118,689✔
5005
{
5006
#ifdef __APPLE__
5007
    for (int i = 0; pszFilename[i] != 0; ++i)
5008
    {
5009
        if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
5010
        {
5011
            // non-ASCII character found
5012

5013
            // if this is a network storage, assume no issue
5014
            if (!VSIIsLocal(pszFilename))
5015
            {
5016
                return true;
5017
            }
5018
            return false;
5019
        }
5020
    }
5021
    return true;
5022
#else
5023
    (void)pszFilename;
5024
    return true;
118,689✔
5025
#endif
5026
}
5027

5028
/************************************************************************/
5029
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
5030
/************************************************************************/
5031

5032
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
1,140✔
5033
{
5034
    const auto kMaxFloat = std::numeric_limits<float>::max();
1,140✔
5035
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
1,140✔
5036
        return -kMaxFloat;
33✔
5037
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
1,107✔
5038
        return kMaxFloat;
8✔
5039
    return dfVal;
1,099✔
5040
}
5041

5042
/************************************************************************/
5043
/*                        GDALCopyNoDataValue()                         */
5044
/************************************************************************/
5045

5046
void GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand)
2,734✔
5047
{
5048

5049
    int bSuccess;
5050
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
2,734✔
5051
    const auto eDstDataType = poDstBand->GetRasterDataType();
2,734✔
5052
    if (eSrcDataType == GDT_Int64)
2,734✔
5053
    {
5054
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
3✔
5055
        if (bSuccess)
3✔
5056
        {
5057
            if (eDstDataType == GDT_Int64)
3✔
5058
            {
5059
                poDstBand->SetNoDataValueAsInt64(nNoData);
3✔
5060
            }
5061
            else if (eDstDataType == GDT_UInt64)
×
5062
            {
5063
                if (nNoData >= 0)
×
5064
                    poDstBand->SetNoDataValueAsUInt64(
×
5065
                        static_cast<uint64_t>(nNoData));
×
5066
            }
5067
            else if (nNoData ==
×
5068
                     static_cast<int64_t>(static_cast<double>(nNoData)))
×
5069
            {
5070
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
5071
            }
5072
        }
5073
    }
5074
    else if (eSrcDataType == GDT_UInt64)
2,731✔
5075
    {
5076
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
3✔
5077
        if (bSuccess)
3✔
5078
        {
5079
            if (eDstDataType == GDT_UInt64)
3✔
5080
            {
5081
                poDstBand->SetNoDataValueAsUInt64(nNoData);
3✔
5082
            }
5083
            else if (eDstDataType == GDT_Int64)
×
5084
            {
5085
                if (nNoData <
×
5086
                    static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
×
5087
                {
5088
                    poDstBand->SetNoDataValueAsInt64(
×
5089
                        static_cast<int64_t>(nNoData));
×
5090
                }
5091
            }
5092
            else if (nNoData ==
×
5093
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
×
5094
            {
5095
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
5096
            }
5097
        }
5098
    }
5099
    else
5100
    {
5101
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2,728✔
5102
        if (bSuccess)
2,728✔
5103
        {
5104
            if (eDstDataType == GDT_Int64)
170✔
5105
            {
5106
                if (dfNoData >= static_cast<double>(
×
5107
                                    std::numeric_limits<int64_t>::min()) &&
×
5108
                    dfNoData <= static_cast<double>(
×
5109
                                    std::numeric_limits<int64_t>::max()) &&
×
5110
                    dfNoData ==
5111
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
×
5112
                {
5113
                    poDstBand->SetNoDataValueAsInt64(
×
5114
                        static_cast<int64_t>(dfNoData));
×
5115
                }
5116
            }
5117
            else if (eDstDataType == GDT_UInt64)
170✔
5118
            {
5119
                if (dfNoData >= static_cast<double>(
×
5120
                                    std::numeric_limits<uint64_t>::min()) &&
×
5121
                    dfNoData <= static_cast<double>(
×
5122
                                    std::numeric_limits<uint64_t>::max()) &&
×
5123
                    dfNoData ==
5124
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
×
5125
                {
5126
                    poDstBand->SetNoDataValueAsInt64(
×
5127
                        static_cast<uint64_t>(dfNoData));
×
5128
                }
5129
            }
5130
            else
5131
            {
5132
                poDstBand->SetNoDataValue(dfNoData);
170✔
5133
            }
5134
        }
5135
    }
5136
}
2,734✔
5137

5138
/************************************************************************/
5139
/*                     GDALGetNoDataValueCastToDouble()                 */
5140
/************************************************************************/
5141

5142
double GDALGetNoDataValueCastToDouble(int64_t nVal)
2✔
5143
{
5144
    const double dfVal = static_cast<double>(nVal);
2✔
5145
    if (static_cast<int64_t>(dfVal) != nVal)
2✔
5146
    {
5147
        CPLError(CE_Warning, CPLE_AppDefined,
×
5148
                 "GetNoDataValue() returns an approximate value of the "
5149
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5150
                 "GetNoDataValueAsInt64() instead",
5151
                 static_cast<GIntBig>(nVal));
5152
    }
5153
    return dfVal;
2✔
5154
}
5155

5156
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
2✔
5157
{
5158
    const double dfVal = static_cast<double>(nVal);
2✔
5159
    if (static_cast<uint64_t>(dfVal) != nVal)
2✔
5160
    {
5161
        CPLError(CE_Warning, CPLE_AppDefined,
×
5162
                 "GetNoDataValue() returns an approximate value of the "
5163
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5164
                 "GetNoDataValueAsUInt64() instead",
5165
                 static_cast<GUIntBig>(nVal));
5166
    }
5167
    return dfVal;
2✔
5168
}
5169

5170
/************************************************************************/
5171
/*                GDALGetCompressionFormatForJPEG()                     */
5172
/************************************************************************/
5173

5174
//! @cond Doxygen_Suppress
5175
std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
23✔
5176
{
5177
    std::string osRet;
23✔
5178
    const auto nSavedPos = VSIFTellL(fp);
23✔
5179
    GByte abyMarkerHeader[4];
5180
    if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
23✔
5181
        VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
23✔
5182
        abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
46✔
5183
    {
5184
        osRet = "JPEG";
23✔
5185
        bool bHasAPP14Adobe = false;
23✔
5186
        GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
23✔
5187
        int nNumComponents = 0;
23✔
5188
        while (true)
5189
        {
5190
            const auto nCurPos = VSIFTellL(fp);
171✔
5191
            if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
171✔
5192
                break;
×
5193
            if (abyMarkerHeader[0] != 0xFF)
171✔
5194
                break;
×
5195
            const GByte markerType = abyMarkerHeader[1];
171✔
5196
            const size_t nMarkerSize =
171✔
5197
                abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
171✔
5198
            if (nMarkerSize < 2)
171✔
5199
                break;
×
5200
            if (markerType >= 0xC0 && markerType <= 0xCF &&
171✔
5201
                markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
23✔
5202
            {
5203
                switch (markerType)
23✔
5204
                {
5205
                    case 0xC0:
21✔
5206
                        osRet += ";frame_type=SOF0_baseline";
21✔
5207
                        break;
21✔
5208
                    case 0xC1:
2✔
5209
                        osRet += ";frame_type=SOF1_extended_sequential";
2✔
5210
                        break;
2✔
5211
                    case 0xC2:
×
5212
                        osRet += ";frame_type=SOF2_progressive_huffman";
×
5213
                        break;
×
5214
                    case 0xC3:
×
5215
                        osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
5216
                                 "supported=no";
×
5217
                        break;
×
5218
                    case 0xC5:
×
5219
                        osRet += ";frame_type="
5220
                                 "SOF5_differential_sequential_huffman;"
5221
                                 "libjpeg_supported=no";
×
5222
                        break;
×
5223
                    case 0xC6:
×
5224
                        osRet += ";frame_type=SOF6_differential_progressive_"
5225
                                 "huffman;libjpeg_supported=no";
×
5226
                        break;
×
5227
                    case 0xC7:
×
5228
                        osRet += ";frame_type="
5229
                                 "SOF7_differential_lossless_huffman;"
5230
                                 "libjpeg_supported=no";
×
5231
                        break;
×
5232
                    case 0xC9:
×
5233
                        osRet += ";frame_type="
5234
                                 "SOF9_extended_sequential_arithmetic";
×
5235
                        break;
×
5236
                    case 0xCA:
×
5237
                        osRet += ";frame_type=SOF10_progressive_arithmetic";
×
5238
                        break;
×
5239
                    case 0xCB:
×
5240
                        osRet += ";frame_type="
5241
                                 "SOF11_lossless_arithmetic;libjpeg_"
5242
                                 "supported=no";
×
5243
                        break;
×
5244
                    case 0xCD:
×
5245
                        osRet += ";frame_type=SOF13_differential_sequential_"
5246
                                 "arithmetic;libjpeg_supported=no";
×
5247
                        break;
×
5248
                    case 0xCE:
×
5249
                        osRet += ";frame_type=SOF14_differential_progressive_"
5250
                                 "arithmetic;libjpeg_supported=no";
×
5251
                        break;
×
5252
                    case 0xCF:
×
5253
                        osRet += ";frame_type=SOF15_differential_lossless_"
5254
                                 "arithmetic;libjpeg_supported=no";
×
5255
                        break;
×
5256
                    default:
×
5257
                        break;
×
5258
                }
5259
                GByte abySegmentBegin[6];
5260
                if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
23✔
5261
                              fp) != 1)
23✔
5262
                    break;
×
5263
                osRet += ";bit_depth=";
23✔
5264
                osRet += CPLSPrintf("%d", abySegmentBegin[0]);
23✔
5265
                nNumComponents = abySegmentBegin[5];
23✔
5266
                osRet += ";num_components=";
23✔
5267
                osRet += CPLSPrintf("%d", nNumComponents);
23✔
5268
                if (nNumComponents == 3)
23✔
5269
                {
5270
                    GByte abySegmentNext[3 * 3];
5271
                    if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
13✔
5272
                                  fp) != 1)
13✔
5273
                        break;
×
5274
                    if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
13✔
5275
                        abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
×
5276
                        abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
×
5277
                    {
5278
                        // no subsampling
5279
                        osRet += ";subsampling=4:4:4";
×
5280
                    }
5281
                    else if (abySegmentNext[0] == 1 &&
13✔
5282
                             abySegmentNext[1] == 0x22 &&
11✔
5283
                             abySegmentNext[3] == 2 &&
11✔
5284
                             abySegmentNext[4] == 0x11 &&
11✔
5285
                             abySegmentNext[6] == 3 &&
11✔
5286
                             abySegmentNext[7] == 0x11)
11✔
5287
                    {
5288
                        // classic subsampling
5289
                        osRet += ";subsampling=4:2:0";
11✔
5290
                    }
5291
                    else if (abySegmentNext[0] == 1 &&
2✔
5292
                             abySegmentNext[1] == 0x21 &&
×
5293
                             abySegmentNext[3] == 2 &&
×
5294
                             abySegmentNext[4] == 0x11 &&
×
5295
                             abySegmentNext[6] == 3 &&
×
5296
                             abySegmentNext[7] == 0x11)
×
5297
                    {
5298
                        osRet += ";subsampling=4:2:2";
×
5299
                    }
5300
                }
23✔
5301
            }
5302
            else if (markerType == 0xEE && nMarkerSize == 14)
148✔
5303
            {
5304
                if (VSIFReadL(abyAPP14AdobeMarkerData,
1✔
5305
                              sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
2✔
5306
                    memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
1✔
5307
                        0)
5308
                {
5309
                    bHasAPP14Adobe = true;
1✔
5310
                }
5311
            }
5312
            else if (markerType == 0xDA)
147✔
5313
            {
5314
                // Start of scan
5315
                break;
23✔
5316
            }
5317
            VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
148✔
5318
        }
148✔
5319
        std::string osColorspace;
46✔
5320
        if (bHasAPP14Adobe)
23✔
5321
        {
5322
            if (abyAPP14AdobeMarkerData[11] == 0)
1✔
5323
            {
5324
                if (nNumComponents == 3)
1✔
5325
                    osColorspace = "RGB";
×
5326
                else if (nNumComponents == 4)
1✔
5327
                    osColorspace = "CMYK";
1✔
5328
            }
5329
            else if (abyAPP14AdobeMarkerData[11] == 1)
×
5330
            {
5331
                osColorspace = "YCbCr";
×
5332
            }
5333
            else if (abyAPP14AdobeMarkerData[11] == 2)
×
5334
            {
5335
                osColorspace = "YCCK";
×
5336
            }
5337
        }
5338
        else
5339
        {
5340
            if (nNumComponents == 3)
22✔
5341
                osColorspace = "YCbCr";
13✔
5342
            else if (nNumComponents == 4)
9✔
5343
                osColorspace = "CMYK";
1✔
5344
        }
5345
        osRet += ";colorspace=";
23✔
5346
        if (!osColorspace.empty())
23✔
5347
            osRet += osColorspace;
15✔
5348
        else
5349
            osRet += "unknown";
8✔
5350
    }
5351
    if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
23✔
5352
    {
5353
        CPLError(CE_Failure, CPLE_AppDefined,
×
5354
                 "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
5355
    }
5356
    return osRet;
46✔
5357
}
5358

5359
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
16✔
5360
                                            size_t nBufferSize)
5361
{
5362
    VSILFILE *fp = VSIFileFromMemBuffer(
16✔
5363
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5364
        false);
5365
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
16✔
5366
    VSIFCloseL(fp);
16✔
5367
    return osRet;
16✔
5368
}
5369

5370
//! @endcond
5371

5372
/************************************************************************/
5373
/*                      GDALGetNoDataReplacementValue()                 */
5374
/************************************************************************/
5375

5376
/**
5377
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5378
 *        is out of range for the specified data type (dt).
5379
 *        For UInt64 and Int64 data type this function cannot reliably trusted
5380
 *        because their nodata values might not always be representable exactly
5381
 *        as a double, in particular the maximum absolute value for those types
5382
 *        is 2^53.
5383
 *
5384
 * The replacement value is a value that can be used in a computation
5385
 * whose result would match by accident the nodata value, whereas it is
5386
 * meant to be valid. For example, for a dataset with a nodata value of 0,
5387
 * when averaging -1 and 1, one would get normally a value of 0. The
5388
 * replacement nodata value can then be substituted to that 0 value to still
5389
 * get a valid value, as close as practical to the true value, while being
5390
 * different from the nodata value.
5391
 *
5392
 * @param dt Data type
5393
 * @param dfNoDataValue The no data value
5394

5395
 * @since GDAL 3.9
5396
 */
5397
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
203✔
5398
{
5399

5400
    // The logic here is to check if the value is out of range for the
5401
    // specified data type and return a replacement value if it is, return
5402
    // 0 otherwise.
5403
    double dfReplacementVal = dfNoDataValue;
203✔
5404
    if (dt == GDT_Byte)
203✔
5405
    {
5406
        if (GDALClampDoubleValue(dfNoDataValue,
63✔
5407
                                 std::numeric_limits<uint8_t>::lowest(),
63✔
5408
                                 std::numeric_limits<uint8_t>::max()))
63✔
5409
        {
5410
            return 0;
2✔
5411
        }
5412
        if (dfNoDataValue == std::numeric_limits<unsigned char>::max())
61✔
5413
            dfReplacementVal = std::numeric_limits<unsigned char>::max() - 1;
3✔
5414
        else
5415
            dfReplacementVal = dfNoDataValue + 1;
58✔
5416
    }
5417
    else if (dt == GDT_Int8)
140✔
5418
    {
5419
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5420
                                 std::numeric_limits<int8_t>::lowest(),
5✔
5421
                                 std::numeric_limits<int8_t>::max()))
5✔
5422
        {
5423
            return 0;
2✔
5424
        }
5425
        if (dfNoDataValue == std::numeric_limits<GInt8>::max())
3✔
5426
            dfReplacementVal = std::numeric_limits<GInt8>::max() - 1;
1✔
5427
        else
5428
            dfReplacementVal = dfNoDataValue + 1;
2✔
5429
    }
5430
    else if (dt == GDT_UInt16)
135✔
5431
    {
5432
        if (GDALClampDoubleValue(dfNoDataValue,
6✔
5433
                                 std::numeric_limits<uint16_t>::lowest(),
6✔
5434
                                 std::numeric_limits<uint16_t>::max()))
6✔
5435
        {
5436
            return 0;
2✔
5437
        }
5438
        if (dfNoDataValue == std::numeric_limits<GUInt16>::max())
4✔
5439
            dfReplacementVal = std::numeric_limits<GUInt16>::max() - 1;
1✔
5440
        else
5441
            dfReplacementVal = dfNoDataValue + 1;
3✔
5442
    }
5443
    else if (dt == GDT_Int16)
129✔
5444
    {
5445
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5446
                                 std::numeric_limits<int16_t>::lowest(),
5✔
5447
                                 std::numeric_limits<int16_t>::max()))
5✔
5448
        {
5449
            return 0;
2✔
5450
        }
5451
        if (dfNoDataValue == std::numeric_limits<GInt16>::max())
3✔
5452
            dfReplacementVal = std::numeric_limits<GInt16>::max() - 1;
1✔
5453
        else
5454
            dfReplacementVal = dfNoDataValue + 1;
2✔
5455
    }
5456
    else if (dt == GDT_UInt32)
124✔
5457
    {
5458
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5459
                                 std::numeric_limits<uint32_t>::lowest(),
5460
                                 std::numeric_limits<uint32_t>::max()))
5461
        {
5462
            return 0;
2✔
5463
        }
5464
        if (dfNoDataValue == std::numeric_limits<GUInt32>::max())
3✔
5465
            dfReplacementVal = std::numeric_limits<GUInt32>::max() - 1;
1✔
5466
        else
5467
            dfReplacementVal = dfNoDataValue + 1;
2✔
5468
    }
5469
    else if (dt == GDT_Int32)
119✔
5470
    {
5471
        if (GDALClampDoubleValue(dfNoDataValue,
7✔
5472
                                 std::numeric_limits<int32_t>::lowest(),
5473
                                 std::numeric_limits<int32_t>::max()))
5474
        {
5475
            return 0;
2✔
5476
        }
5477
        if (dfNoDataValue == std::numeric_limits<int32_t>::max())
5✔
5478
            dfReplacementVal = std::numeric_limits<int32_t>::max() - 1;
1✔
5479
        else
5480
            dfReplacementVal = dfNoDataValue + 1;
4✔
5481
    }
5482
    else if (dt == GDT_UInt64)
112✔
5483
    {
5484
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5485
        // so we take the next lower value representable as a double 18446744073709549567
5486
        static const double dfMaxUInt64Value{
5487
            std::nextafter(
5488
                static_cast<double>(std::numeric_limits<uint64_t>::max()), 0) -
5489
            1};
5490

5491
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5492
                                 std::numeric_limits<uint64_t>::lowest(),
5493
                                 std::numeric_limits<uint64_t>::max()))
5494
        {
5495
            return 0;
2✔
5496
        }
5497

5498
        if (dfNoDataValue >=
3✔
5499
            static_cast<double>(std::numeric_limits<uint64_t>::max()))
3✔
5500
            dfReplacementVal = dfMaxUInt64Value;
1✔
5501
        else
5502
            dfReplacementVal = dfNoDataValue + 1;
2✔
5503
    }
5504
    else if (dt == GDT_Int64)
107✔
5505
    {
5506
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5507
        // so we take the next lower value representable as a double 9223372036854774784
5508
        static const double dfMaxInt64Value{
5509
            std::nextafter(
5510
                static_cast<double>(std::numeric_limits<int64_t>::max()), 0) -
5511
            1};
5512

5513
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5514
                                 std::numeric_limits<int64_t>::lowest(),
5515
                                 std::numeric_limits<int64_t>::max()))
5516
        {
5517
            return 0;
2✔
5518
        }
5519

5520
        if (dfNoDataValue >=
3✔
5521
            static_cast<double>(std::numeric_limits<int64_t>::max()))
3✔
5522
            dfReplacementVal = dfMaxInt64Value;
1✔
5523
        else
5524
            dfReplacementVal = dfNoDataValue + 1;
2✔
5525
    }
5526
    else if (dt == GDT_Float32)
102✔
5527
    {
5528

5529
        if (GDALClampDoubleValue(dfNoDataValue,
33✔
5530
                                 std::numeric_limits<float>::lowest(),
5531
                                 std::numeric_limits<float>::max()))
5532
        {
5533
            return 0;
4✔
5534
        }
5535

5536
        if (dfNoDataValue == std::numeric_limits<float>::max())
29✔
5537
        {
5538
            dfReplacementVal =
1✔
5539
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
1✔
5540
        }
5541
        else
5542
        {
5543
            dfReplacementVal =
28✔
5544
                std::nextafter(static_cast<float>(dfNoDataValue),
28✔
5545
                               std::numeric_limits<float>::max());
5546
        }
5547
    }
5548
    else if (dt == GDT_Float64)
69✔
5549
    {
5550
        if (GDALClampDoubleValue(dfNoDataValue,
69✔
5551
                                 std::numeric_limits<double>::lowest(),
5552
                                 std::numeric_limits<double>::max()))
5553
        {
5554
            return 0;
2✔
5555
        }
5556

5557
        if (dfNoDataValue == std::numeric_limits<double>::max())
67✔
5558
        {
5559
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0f);
2✔
5560
        }
5561
        else
5562
        {
5563
            dfReplacementVal = std::nextafter(
65✔
5564
                dfNoDataValue, std::numeric_limits<double>::max());
5565
        }
5566
    }
5567

5568
    return dfReplacementVal;
181✔
5569
}
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