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

OSGeo / gdal / 15666580815

15 Jun 2025 07:21PM UTC coverage: 71.05% (-0.003%) from 71.053%
15666580815

Pull #12573

github

web-flow
Merge b7130346e into 29fe65bd6
Pull Request #12573: gdal raster/vector reproject: enhance completion for --dst-crs to take into extent of input dataset

114 of 117 new or added lines in 12 files covered. (97.44%)

20431 existing lines in 41 files now uncovered.

571796 of 804780 relevant lines covered (71.05%)

250533.47 hits per line

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

76.69
/gcore/gdal_misc.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Free standing functions for GDAL.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13

14
#include "cpl_port.h"
15

16
#include <cctype>
17
#include <cerrno>
18
#include <clocale>
19
#include <cmath>
20
#include <cstddef>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <fcntl.h>
25

26
#include <algorithm>
27
#include <iostream>
28
#include <limits>
29
#include <string>
30

31
#include "cpl_conv.h"
32
#include "cpl_error.h"
33
#include "cpl_float.h"
34
#include "cpl_json.h"
35
#include "cpl_minixml.h"
36
#include "cpl_multiproc.h"
37
#include "cpl_string.h"
38
#include "cpl_vsi.h"
39
#ifdef EMBED_RESOURCE_FILES
40
#include "embedded_resources.h"
41
#endif
42
#include "gdal_version_full/gdal_version.h"
43
#include "gdal.h"
44
#include "gdal_mdreader.h"
45
#include "gdal_priv.h"
46
#include "gdal_priv_templates.hpp"
47
#include "ogr_core.h"
48
#include "ogr_spatialref.h"
49
#include "ogr_geos.h"
50

51
#include "proj.h"
52

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

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

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

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

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

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

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

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

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

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

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

114
        case GDT_Unknown:
×
115
        case GDT_TypeCount:
116
            break;
×
117
    }
118
    return 0;
×
119
}
120

121
/************************************************************************/
122
/*                         GDALDataTypeUnion()                          */
123
/************************************************************************/
124

125
/**
126
 * \brief Return the smallest data type that can fully express both input data
127
 * types.
128
 *
129
 * @param eType1 first data type.
130
 * @param eType2 second data type.
131
 *
132
 * @return a data type able to express eType1 and eType2.
133
 */
134

135
GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
106,876✔
136
                                           GDALDataType eType2)
137

138
{
139
    if (eType1 == GDT_Unknown)
106,876✔
140
        return eType2;
2,412✔
141
    if (eType2 == GDT_Unknown)
104,464✔
142
        return eType1;
×
143

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

238
    return 64;
7✔
239
}
240

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

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

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

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

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

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

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

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

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

379
{
380
    switch (eDataType)
248,004,000✔
381
    {
382
        case GDT_Byte:
80,923,000✔
383
        case GDT_Int8:
384
            return 1;
80,923,000✔
385

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

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

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

405
        case GDT_CFloat64:
584,831✔
406
            return 16;
584,831✔
407

408
        case GDT_Unknown:
17,736✔
409
        case GDT_TypeCount:
410
            break;
17,736✔
411
    }
412
    return 0;
7,357✔
413
}
414

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

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

429
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
13,475✔
430

431
{
432
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
13,475✔
433
}
434

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

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

451
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
4,114,610✔
452

453
{
454
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
4,114,610✔
455
}
456

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

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

469
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
1,223,250✔
470

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

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

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

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

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

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

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

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

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

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

556
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
361,679✔
557

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

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

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

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

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

599
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
907,275✔
600
{
601
    switch (eDataType)
907,275✔
602
    {
603
        case GDT_Byte:
771,100✔
604
        case GDT_UInt16:
605
        case GDT_UInt32:
606
        case GDT_UInt64:
607
            return FALSE;
771,100✔
608

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

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

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

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

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

650
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
355,927✔
651
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
355,926✔
652

653
    if (GDALDataTypeIsInteger(eTypeTo))
355,926✔
654
    {
655
        // E.g. float32 -> int32
656
        if (GDALDataTypeIsFloating(eTypeFrom))
353,996✔
657
            return TRUE;
5,159✔
658

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

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

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

675
        return FALSE;
348,408✔
676
    }
677

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

687
    if (eTypeTo == GDT_Float32 &&
1,931✔
688
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
794✔
689
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
762✔
690
         eTypeFrom == GDT_Float64))
691
    {
692
        return TRUE;
96✔
693
    }
694

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

701
    return FALSE;
1,803✔
702
}
703

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

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

722
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
100,667✔
723

724
{
725
    switch (eDataType)
100,667✔
726
    {
727
        case GDT_Unknown:
5,303✔
728
            return "Unknown";
5,303✔
729

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

814
    return GDT_Unknown;
32✔
815
}
816

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1148
    return GARIO_ERROR;
×
1149
}
1150

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1399
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1,999✔
1400

1401
{
1402
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1,999✔
1403
                      GCI_Undefined);
1404

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

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

1419
    return GCI_Undefined;
4✔
1420
}
1421

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

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

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

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

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

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

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

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

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

1510
    GDALRasterBand *poBand;
1511

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

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

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

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

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

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

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

1543
        return FALSE;
×
1544
    }
1545

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

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

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

1556
    int nBlockSampleRate = 1;
×
1557

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

1563
    int nActualSamples = 0;
×
1564

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

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

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

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

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

1586
        int iRemainder = 0;
×
1587

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

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

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

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

1705
            iRemainder = iX - iXValid;
×
1706
        }
1707

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

1711
    return nActualSamples;
×
1712
}
1713

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

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

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

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

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

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

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

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

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

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

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

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

1824
} /* namespace gdal */
1825

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1915
    return pasReturn;
726✔
1916
}
1917

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

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

1948
/**/
1949
/**/
1950

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

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

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

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

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

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

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

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

1991
    return osTarget;
68✔
1992
}
1993

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

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

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

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

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

2023
    int nLines = CSLCount(papszLines);
×
2024

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2156
                nCoordinateCount++;
×
2157
            }
2158
        }
2159

2160
        CSLDestroy(papszTok);
×
2161
    }
2162

2163
    CSLDestroy(papszLines);
×
2164

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

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

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

2199
    return TRUE;
×
2200
}
2201

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

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

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

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

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

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

2238
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
×
2239

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2393
    return TRUE;
14✔
2394
}
2395

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

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

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

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

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

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

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

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

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

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

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

2471
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
×
2472

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

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

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

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

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

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

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

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

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

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

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

2561
        CSLDestroy(papszLines);
67✔
2562

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

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

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

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

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

2621
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
29,193✔
2622
                       double *padfGeoTransform, CSLConstList papszSiblingFiles,
2623
                       char **ppszWorldFileNameOut)
2624
{
2625
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
29,193✔
2626
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
29,193✔
2627

2628
    if (ppszWorldFileNameOut)
29,193✔
2629
        *ppszWorldFileNameOut = nullptr;
27,369✔
2630

2631
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
29,193✔
2632
        return FALSE;
202✔
2633

2634
    /* -------------------------------------------------------------------- */
2635
    /*      If we aren't given an extension, try both the unix and          */
2636
    /*      windows style extensions.                                       */
2637
    /* -------------------------------------------------------------------- */
2638
    if (pszExtension == nullptr)
28,991✔
2639
    {
2640
        const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename);
14,014✔
2641

2642
        if (oBaseExt.length() < 2)
7,007✔
2643
            return FALSE;
149✔
2644

2645
        // windows version - first + last + 'w'
2646
        char szDerivedExtension[100] = {'\0'};
6,858✔
2647
        szDerivedExtension[0] = oBaseExt[0];
6,858✔
2648
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
6,858✔
2649
        szDerivedExtension[2] = 'w';
6,858✔
2650
        szDerivedExtension[3] = '\0';
6,858✔
2651

2652
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
6,858✔
2653
                               padfGeoTransform, papszSiblingFiles,
2654
                               ppszWorldFileNameOut))
6,858✔
2655
            return TRUE;
52✔
2656

2657
        // unix version - extension + 'w'
2658
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
6,806✔
2659
            return FALSE;
×
2660

2661
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
6,806✔
2662
                 oBaseExt.c_str());
2663
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
6,806✔
2664
                                  padfGeoTransform, papszSiblingFiles,
2665
                                  ppszWorldFileNameOut);
6,806✔
2666
    }
2667

2668
    /* -------------------------------------------------------------------- */
2669
    /*      Skip the leading period in the extension if there is one.       */
2670
    /* -------------------------------------------------------------------- */
2671
    if (*pszExtension == '.')
21,984✔
2672
        pszExtension++;
1,413✔
2673

2674
    /* -------------------------------------------------------------------- */
2675
    /*      Generate upper and lower case versions of the extension.        */
2676
    /* -------------------------------------------------------------------- */
2677
    char szExtUpper[32] = {'\0'};
21,984✔
2678
    char szExtLower[32] = {'\0'};
21,984✔
2679
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
21,984✔
2680
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
21,984✔
2681

2682
    for (int i = 0; szExtUpper[i] != '\0'; i++)
95,263✔
2683
    {
2684
        szExtUpper[i] = static_cast<char>(
73,279✔
2685
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
73,280✔
2686
        szExtLower[i] = static_cast<char>(
73,279✔
2687
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
73,279✔
2688
    }
2689

2690
    std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
43,967✔
2691

2692
    if (papszSiblingFiles &&
42,893✔
2693
        // cppcheck-suppress knownConditionTrueFalse
2694
        GDALCanReliablyUseSiblingFileList(osTFW.c_str()))
20,909✔
2695
    {
2696
        const int iSibling =
2697
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str()));
20,909✔
2698
        if (iSibling >= 0)
20,909✔
2699
        {
2700
            CPLString osTFWFilename = pszBaseFilename;
65✔
2701
            osTFWFilename.resize(strlen(pszBaseFilename) -
130✔
2702
                                 strlen(CPLGetFilename(pszBaseFilename)));
65✔
2703
            osTFWFilename += papszSiblingFiles[iSibling];
65✔
2704
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
65✔
2705
            {
2706
                if (ppszWorldFileNameOut)
65✔
2707
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
62✔
2708
                return TRUE;
65✔
2709
            }
2710
        }
2711
        return FALSE;
20,844✔
2712
    }
2713

2714
    /* -------------------------------------------------------------------- */
2715
    /*      Try lower case, then upper case.                                */
2716
    /* -------------------------------------------------------------------- */
2717

2718
    VSIStatBufL sStatBuf;
2719
    bool bGotTFW =
2720
        VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,075✔
2721

2722
    if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str()))
1,075✔
2723
    {
2724
        osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper);
1,073✔
2725
        bGotTFW =
1,073✔
2726
            VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,073✔
2727
    }
2728

2729
    if (!bGotTFW)
1,075✔
2730
        return FALSE;
1,073✔
2731

2732
    /* -------------------------------------------------------------------- */
2733
    /*      We found the file, now load and parse it.                       */
2734
    /* -------------------------------------------------------------------- */
2735
    if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform))
2✔
2736
    {
2737
        if (ppszWorldFileNameOut)
2✔
2738
            *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str());
1✔
2739
        return TRUE;
2✔
2740
    }
2741
    return FALSE;
×
2742
}
2743

2744
/************************************************************************/
2745
/*                         GDALWriteWorldFile()                         */
2746
/*                                                                      */
2747
/*      Helper function for translator implementer wanting              */
2748
/*      support for ESRI world files.                                   */
2749
/************************************************************************/
2750

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

2778
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
13✔
2779
                                   const char *pszExtension,
2780
                                   double *padfGeoTransform)
2781

2782
{
2783
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
13✔
2784
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
13✔
2785
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
13✔
2786

2787
    /* -------------------------------------------------------------------- */
2788
    /*      Prepare the text to write to the file.                          */
2789
    /* -------------------------------------------------------------------- */
2790
    CPLString osTFWText;
26✔
2791

2792
    osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2793
                     padfGeoTransform[1], padfGeoTransform[4],
13✔
2794
                     padfGeoTransform[2], padfGeoTransform[5],
13✔
2795
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
13✔
2796
                         0.5 * padfGeoTransform[2],
13✔
2797
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
13✔
2798
                         0.5 * padfGeoTransform[5]);
13✔
2799

2800
    /* -------------------------------------------------------------------- */
2801
    /*      Update extension, and write to disk.                            */
2802
    /* -------------------------------------------------------------------- */
2803
    const std::string osTFW =
2804
        CPLResetExtensionSafe(pszBaseFilename, pszExtension);
26✔
2805
    VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt");
13✔
2806
    if (fpTFW == nullptr)
13✔
2807
        return FALSE;
×
2808

2809
    const int bRet =
2810
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
13✔
2811
    if (VSIFCloseL(fpTFW) != 0)
13✔
2812
        return FALSE;
×
2813

2814
    return bRet;
13✔
2815
}
2816

2817
/************************************************************************/
2818
/*                          GDALVersionInfo()                           */
2819
/************************************************************************/
2820

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

2848
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
4,861✔
2849

2850
{
2851
    /* -------------------------------------------------------------------- */
2852
    /*      Try to capture as much build information as practical.          */
2853
    /* -------------------------------------------------------------------- */
2854
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
4,861✔
2855
    {
2856
        CPLString osBuildInfo;
1,434✔
2857

2858
#define STRINGIFY_HELPER(x) #x
2859
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2860

2861
#ifdef ESRI_BUILD
2862
        osBuildInfo += "ESRI_BUILD=YES\n";
2863
#endif
2864
#ifdef PAM_ENABLED
2865
        osBuildInfo += "PAM_ENABLED=YES\n";
2866
#endif
2867
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
717✔
2868
#ifdef HAVE_CURL
2869
        osBuildInfo += "CURL_ENABLED=YES\n";
717✔
2870
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
717✔
2871
#endif
2872
#ifdef HAVE_GEOS
2873
        osBuildInfo += "GEOS_ENABLED=YES\n";
717✔
2874
#ifdef GEOS_CAPI_VERSION
2875
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
717✔
2876
#endif
2877
#endif
2878
        osBuildInfo +=
2879
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2880
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
717✔
2881
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
717✔
2882
        osBuildInfo += proj_info().version;
717✔
2883
        osBuildInfo += '\n';
717✔
2884

2885
#ifdef __VERSION__
2886
#ifdef __clang_version__
2887
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2888
#elif defined(__GNUC__)
2889
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
717✔
2890
#elif defined(__INTEL_COMPILER)
2891
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2892
#else
2893
        // STRINGIFY() as we're not sure if its a int or a string
2894
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2895
#endif
2896
#elif defined(_MSC_FULL_VER)
2897
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2898
#elif defined(__INTEL_COMPILER)
2899
        osBuildInfo +=
2900
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2901
#endif
2902
#ifdef CMAKE_UNITY_BUILD
2903
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2904
#endif
2905
#ifdef EMBED_RESOURCE_FILES
2906
        osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2907
#endif
2908
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2909
        osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2910
#endif
2911

2912
#undef STRINGIFY_HELPER
2913
#undef STRINGIFY
2914

2915
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
717✔
2916
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
717✔
2917
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
717✔
2918
    }
2919

2920
    /* -------------------------------------------------------------------- */
2921
    /*      LICENSE is a special case. We try to find and read the          */
2922
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2923
    /* -------------------------------------------------------------------- */
2924
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
4,144✔
2925
    {
2926
#if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2927
        return GDALGetEmbeddedLicense();
2928
#else
2929
        char *pszResultLicence =
2930
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
4✔
2931
        if (pszResultLicence != nullptr)
4✔
2932
        {
2933
            return pszResultLicence;
×
2934
        }
2935

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

2961
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4✔
2962
        }
2963
#endif
2964

2965
#ifdef EMBED_RESOURCE_FILES
2966
        if (!fp)
2967
        {
2968
            return GDALGetEmbeddedLicense();
2969
        }
2970
#endif
2971

2972
        if (!pszResultLicence)
4✔
2973
        {
2974
            pszResultLicence =
2975
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
×
2976
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
2977
                          "contain additional details.\n");
2978
        }
2979

2980
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
4✔
2981
        return pszResultLicence;
4✔
2982
#endif
2983
    }
2984

2985
    /* -------------------------------------------------------------------- */
2986
    /*      All other strings are fairly small.                             */
2987
    /* -------------------------------------------------------------------- */
2988
    CPLString osVersionInfo;
8,280✔
2989

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

3020
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
4,140✔
3021
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
4,140✔
3022
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
4,140✔
3023
}
3024

3025
/************************************************************************/
3026
/*                         GDALCheckVersion()                           */
3027
/************************************************************************/
3028

3029
/** Return TRUE if GDAL library version at runtime matches
3030
   nVersionMajor.nVersionMinor.
3031

3032
    The purpose of this method is to ensure that calling code will run
3033
    with the GDAL version it is compiled for. It is primarily intended
3034
    for external plugins.
3035

3036
    @param nVersionMajor Major version to be tested against
3037
    @param nVersionMinor Minor version to be tested against
3038
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
3039
   method will issue a failure mentioning the name of the calling component.
3040

3041
    @return TRUE if GDAL library version at runtime matches
3042
    nVersionMajor.nVersionMinor, FALSE otherwise.
3043
  */
3044
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
27,651✔
3045
                                 const char *pszCallingComponentName)
3046
{
3047
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
27,651✔
3048
        nVersionMinor == GDAL_VERSION_MINOR)
3049
        return TRUE;
27,651✔
3050

3051
    if (pszCallingComponentName)
×
3052
    {
3053
        CPLError(CE_Failure, CPLE_AppDefined,
×
3054
                 "%s was compiled against GDAL %d.%d, but "
3055
                 "the current library version is %d.%d",
3056
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
3057
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
3058
    }
3059
    return FALSE;
×
3060
}
3061

3062
/************************************************************************/
3063
/*                            GDALDecToDMS()                            */
3064
/************************************************************************/
3065

3066
/** Translate a decimal degrees value to a DMS string with hemisphere.
3067
 */
3068
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
550✔
3069
                                     int nPrecision)
3070

3071
{
3072
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
550✔
3073
}
3074

3075
/************************************************************************/
3076
/*                         GDALPackedDMSToDec()                         */
3077
/************************************************************************/
3078

3079
/**
3080
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
3081
 *
3082
 * See CPLPackedDMSToDec().
3083
 */
3084

3085
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
4✔
3086

3087
{
3088
    return CPLPackedDMSToDec(dfPacked);
4✔
3089
}
3090

3091
/************************************************************************/
3092
/*                         GDALDecToPackedDMS()                         */
3093
/************************************************************************/
3094

3095
/**
3096
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
3097
 *
3098
 * See CPLDecToPackedDMS().
3099
 */
3100

3101
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
4✔
3102

3103
{
3104
    return CPLDecToPackedDMS(dfDec);
4✔
3105
}
3106

3107
/************************************************************************/
3108
/*                       GDALGCPsToGeoTransform()                       */
3109
/************************************************************************/
3110

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

3141
// TODO(schwehr): Add consts to args.
3142
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
605✔
3143
                                       double *padfGeoTransform, int bApproxOK)
3144

3145
{
3146
    double dfPixelThreshold = 0.25;
605✔
3147
    if (!bApproxOK)
605✔
3148
    {
3149
        bApproxOK = CPLTestBool(
591✔
3150
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3151
        if (!bApproxOK)
591✔
3152
        {
3153
            dfPixelThreshold = std::clamp(
591✔
3154
                CPLAtof(CPLGetConfigOption(
591✔
3155
                    "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25")),
3156
                0.0, std::numeric_limits<double>::max());
1,182✔
3157
        }
3158
    }
3159

3160
    /* -------------------------------------------------------------------- */
3161
    /*      Recognise a few special cases.                                  */
3162
    /* -------------------------------------------------------------------- */
3163
    if (nGCPCount < 2)
605✔
3164
        return FALSE;
16✔
3165

3166
    if (nGCPCount == 2)
589✔
3167
    {
3168
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
2✔
3169
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
2✔
3170
            return FALSE;
×
3171

3172
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
2✔
3173
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
2✔
3174
        padfGeoTransform[2] = 0.0;
2✔
3175

3176
        padfGeoTransform[4] = 0.0;
2✔
3177
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
2✔
3178
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
2✔
3179

3180
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
2✔
3181
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
2✔
3182
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
2✔
3183

3184
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
2✔
3185
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
2✔
3186
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
2✔
3187

3188
        return TRUE;
2✔
3189
    }
3190

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

3217
        padfGeoTransform[0] =
184✔
3218
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
184✔
3219
        padfGeoTransform[3] =
184✔
3220
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
184✔
3221
        return TRUE;
184✔
3222
    }
3223

3224
    /* -------------------------------------------------------------------- */
3225
    /*      Compute source and destination ranges so we can normalize       */
3226
    /*      the values to make the least squares computation more stable.   */
3227
    /* -------------------------------------------------------------------- */
3228
    double min_pixel = pasGCPs[0].dfGCPPixel;
403✔
3229
    double max_pixel = pasGCPs[0].dfGCPPixel;
403✔
3230
    double min_line = pasGCPs[0].dfGCPLine;
403✔
3231
    double max_line = pasGCPs[0].dfGCPLine;
403✔
3232
    double min_geox = pasGCPs[0].dfGCPX;
403✔
3233
    double max_geox = pasGCPs[0].dfGCPX;
403✔
3234
    double min_geoy = pasGCPs[0].dfGCPY;
403✔
3235
    double max_geoy = pasGCPs[0].dfGCPY;
403✔
3236

3237
    for (int i = 1; i < nGCPCount; ++i)
1,710✔
3238
    {
3239
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
1,307✔
3240
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
1,307✔
3241
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
1,307✔
3242
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
1,307✔
3243
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
1,307✔
3244
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
1,307✔
3245
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
1,307✔
3246
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
1,307✔
3247
    }
3248

3249
    double EPS = 1.0e-12;
403✔
3250

3251
    if (std::abs(max_pixel - min_pixel) < EPS ||
803✔
3252
        std::abs(max_line - min_line) < EPS ||
800✔
3253
        std::abs(max_geox - min_geox) < EPS ||
1,203✔
3254
        std::abs(max_geoy - min_geoy) < EPS)
325✔
3255
    {
3256
        return FALSE;  // degenerate in at least one dimension.
78✔
3257
    }
3258

3259
    double pl_normalize[6], geo_normalize[6];
3260

3261
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
325✔
3262
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
325✔
3263
    pl_normalize[2] = 0.0;
325✔
3264
    pl_normalize[3] = -min_line / (max_line - min_line);
325✔
3265
    pl_normalize[4] = 0.0;
325✔
3266
    pl_normalize[5] = 1.0 / (max_line - min_line);
325✔
3267

3268
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
325✔
3269
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
325✔
3270
    geo_normalize[2] = 0.0;
325✔
3271
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
325✔
3272
    geo_normalize[4] = 0.0;
325✔
3273
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
325✔
3274

3275
    /* -------------------------------------------------------------------- */
3276
    /* In the general case, do a least squares error approximation by       */
3277
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
3278
    /* -------------------------------------------------------------------- */
3279

3280
    double sum_x = 0.0;
325✔
3281
    double sum_y = 0.0;
325✔
3282
    double sum_xy = 0.0;
325✔
3283
    double sum_xx = 0.0;
325✔
3284
    double sum_yy = 0.0;
325✔
3285
    double sum_Lon = 0.0;
325✔
3286
    double sum_Lonx = 0.0;
325✔
3287
    double sum_Lony = 0.0;
325✔
3288
    double sum_Lat = 0.0;
325✔
3289
    double sum_Latx = 0.0;
325✔
3290
    double sum_Laty = 0.0;
325✔
3291

3292
    for (int i = 0; i < nGCPCount; ++i)
1,723✔
3293
    {
3294
        double pixel, line, geox, geoy;
3295

3296
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
1,398✔
3297
                              pasGCPs[i].dfGCPLine, &pixel, &line);
1,398✔
3298
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
1,398✔
3299
                              pasGCPs[i].dfGCPY, &geox, &geoy);
1,398✔
3300

3301
        sum_x += pixel;
1,398✔
3302
        sum_y += line;
1,398✔
3303
        sum_xy += pixel * line;
1,398✔
3304
        sum_xx += pixel * pixel;
1,398✔
3305
        sum_yy += line * line;
1,398✔
3306
        sum_Lon += geox;
1,398✔
3307
        sum_Lonx += geox * pixel;
1,398✔
3308
        sum_Lony += geox * line;
1,398✔
3309
        sum_Lat += geoy;
1,398✔
3310
        sum_Latx += geoy * pixel;
1,398✔
3311
        sum_Laty += geoy * line;
1,398✔
3312
    }
3313

3314
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3315
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
325✔
3316
                           sum_x * sum_x * sum_yy;
325✔
3317

3318
    /* -------------------------------------------------------------------- */
3319
    /*      If the divisor is zero, there is no valid solution.             */
3320
    /* -------------------------------------------------------------------- */
3321
    if (divisor == 0.0)
325✔
3322
        return FALSE;
×
3323

3324
    /* -------------------------------------------------------------------- */
3325
    /*      Compute top/left origin.                                        */
3326
    /* -------------------------------------------------------------------- */
3327
    double gt_normalized[6] = {0.0};
325✔
3328
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3329
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3330
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
325✔
3331
                       divisor;
3332

3333
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
325✔
3334
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3335
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
325✔
3336
                       divisor;
3337

3338
    /* -------------------------------------------------------------------- */
3339
    /*      Compute X related coefficients.                                 */
3340
    /* -------------------------------------------------------------------- */
3341
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3342
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
325✔
3343
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
325✔
3344
                       divisor;
3345

3346
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
325✔
3347
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
325✔
3348
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
325✔
3349
                       divisor;
3350

3351
    /* -------------------------------------------------------------------- */
3352
    /*      Compute Y related coefficients.                                 */
3353
    /* -------------------------------------------------------------------- */
3354
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
325✔
3355
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
325✔
3356
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
325✔
3357
                       divisor;
3358

3359
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
325✔
3360
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
325✔
3361
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
325✔
3362
                       divisor;
3363

3364
    /* -------------------------------------------------------------------- */
3365
    /*      Compose the resulting transformation with the normalization     */
3366
    /*      geotransformations.                                             */
3367
    /* -------------------------------------------------------------------- */
3368
    double gt1p2[6] = {0.0};
325✔
3369
    double inv_geo_normalize[6] = {0.0};
325✔
3370
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
325✔
3371
        return FALSE;
×
3372

3373
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
325✔
3374
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
325✔
3375

3376
    // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
3377
    if (std::abs(padfGeoTransform[1]) <= 1e-15 ||
649✔
3378
        std::abs(padfGeoTransform[5]) <= 1e-15)
324✔
3379
    {
3380
        return FALSE;
2✔
3381
    }
3382

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

3400
        for (int i = 0; i < nGCPCount; i++)
1,643✔
3401
        {
3402
            const double dfErrorX =
1,335✔
3403
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
1,335✔
3404
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
1,335✔
3405
                 padfGeoTransform[0]) -
1,335✔
3406
                pasGCPs[i].dfGCPX;
1,335✔
3407
            const double dfErrorY =
1,335✔
3408
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
1,335✔
3409
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
1,335✔
3410
                 padfGeoTransform[3]) -
1,335✔
3411
                pasGCPs[i].dfGCPY;
1,335✔
3412

3413
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
2,667✔
3414
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
1,332✔
3415
            {
3416
                CPLDebug("GDAL",
6✔
3417
                         "dfErrorX/dfPixelSize = %.2f, "
3418
                         "dfErrorY/dfPixelSize = %.2f",
3419
                         std::abs(dfErrorX) / dfPixelSize,
6✔
3420
                         std::abs(dfErrorY) / dfPixelSize);
6✔
3421
                return FALSE;
6✔
3422
            }
3423
        }
3424
    }
3425

3426
    return TRUE;
317✔
3427
}
3428

3429
/************************************************************************/
3430
/*                      GDALComposeGeoTransforms()                      */
3431
/************************************************************************/
3432

3433
/**
3434
 * \brief Compose two geotransforms.
3435
 *
3436
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3437
 * being applied to a point.
3438
 *
3439
 * @param padfGT1 the first geotransform, six values.
3440
 * @param padfGT2 the second geotransform, six values.
3441
 * @param padfGTOut the output geotransform, six values, may safely be the same
3442
 * array as padfGT1 or padfGT2.
3443
 */
3444

3445
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
650✔
3446
                              double *padfGTOut)
3447

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

3463
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
650✔
3464
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
650✔
3465
    gtwrk[0] =
650✔
3466
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
650✔
3467

3468
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
650✔
3469
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
650✔
3470
    gtwrk[3] =
650✔
3471
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
650✔
3472
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
650✔
3473
}
650✔
3474

3475
/************************************************************************/
3476
/*                      StripIrrelevantOptions()                        */
3477
/************************************************************************/
3478

3479
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
10✔
3480
{
3481
    if (psCOL == nullptr)
10✔
3482
        return;
×
3483
    if (nOptions == 0)
10✔
3484
        nOptions = GDAL_OF_RASTER;
5✔
3485
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
10✔
3486
        return;
2✔
3487

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

3513
            CPLXMLNode *psNext = psIter->psNext;
167✔
3514
            if (bStrip)
167✔
3515
            {
3516
                if (psPrev)
34✔
3517
                    psPrev->psNext = psNext;
13✔
3518
                else if (psCOL->psChild == psIter)
21✔
3519
                    psCOL->psChild = psNext;
21✔
3520
                psIter->psNext = nullptr;
34✔
3521
                CPLDestroyXMLNode(psIter);
34✔
3522
                psIter = psNext;
34✔
3523
            }
3524
            else
3525
            {
3526
                psPrev = psIter;
133✔
3527
                psIter = psNext;
133✔
3528
            }
3529
        }
3530
        else
3531
        {
3532
            psIter = psIter->psNext;
×
3533
        }
3534
    }
3535
}
3536

3537
/************************************************************************/
3538
/*                         GDALPrintDriverList()                        */
3539
/************************************************************************/
3540

3541
/** Print on stdout the driver list */
3542
std::string GDALPrintDriverList(int nOptions, bool bJSON)
9✔
3543
{
3544
    if (nOptions == 0)
9✔
3545
        nOptions = GDAL_OF_RASTER;
2✔
3546

3547
    if (bJSON)
9✔
3548
    {
3549
        auto poDM = GetGDALDriverManager();
6✔
3550
        CPLJSONArray oArray;
12✔
3551
        const int nDriverCount = poDM->GetDriverCount();
6✔
3552
        for (int iDr = 0; iDr < nDriverCount; ++iDr)
1,330✔
3553
        {
3554
            auto poDriver = poDM->GetDriver(iDr);
1,324✔
3555
            CSLConstList papszMD = poDriver->GetMetadata();
1,324✔
3556

3557
            if (nOptions == GDAL_OF_RASTER &&
1,765✔
3558
                !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
441✔
3559
                continue;
612✔
3560
            if (nOptions == GDAL_OF_VECTOR &&
1,627✔
3561
                !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
441✔
3562
                continue;
265✔
3563
            if (nOptions == GDAL_OF_GNM &&
921✔
3564
                !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3565
                continue;
×
3566
            if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
1,142✔
3567
                !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
221✔
3568
                continue;
209✔
3569

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

3596
            if (const char *pszExtensions = CSLFetchNameValueDef(
712✔
3597
                    papszMD, GDAL_DMD_EXTENSIONS,
3598
                    CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3599
            {
3600
                const CPLStringList aosExt(
3601
                    CSLTokenizeString2(pszExtensions, " ", 0));
948✔
3602
                CPLJSONArray oJExts;
474✔
3603
                for (int i = 0; i < aosExt.size(); ++i)
1,117✔
3604
                {
3605
                    oJExts.Add(aosExt[i]);
643✔
3606
                }
3607
                oJDriver.Add("file_extensions", oJExts);
474✔
3608
            }
3609

3610
            oArray.Add(oJDriver);
712✔
3611
        }
3612

3613
        return oArray.Format(CPLJSONObject::PrettyFormat::Pretty);
6✔
3614
    }
3615

3616
    std::string ret;
6✔
3617
    ret = "Supported Formats: (ro:read-only, rw:read-write, "
3618
          "+:write from scratch, u:update, "
3619
          "v:virtual-I/O s:subdatasets)\n";
3✔
3620
    for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
663✔
3621
    {
3622
        GDALDriverH hDriver = GDALGetDriver(iDr);
660✔
3623

3624
        const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets;
660✔
3625
        CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
660✔
3626

3627
        if (nOptions == GDAL_OF_RASTER &&
880✔
3628
            !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
220✔
3629
            continue;
333✔
3630
        if (nOptions == GDAL_OF_VECTOR &&
1,031✔
3631
            !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
440✔
3632
            continue;
264✔
3633
        if (nOptions == GDAL_OF_GNM &&
327✔
3634
            !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3635
            continue;
×
3636
        if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
327✔
3637
            !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
×
3638
            continue;
×
3639

3640
        if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
327✔
3641
            pszRFlag = "r";
324✔
3642

3643
        if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
327✔
3644
            pszWFlag = "w+";
153✔
3645
        else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
174✔
3646
            pszWFlag = "w";
29✔
3647
        else
3648
            pszWFlag = "o";
145✔
3649

3650
        const char *pszUpdate = "";
327✔
3651
        if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
327✔
3652
            pszUpdate = "u";
55✔
3653

3654
        if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
327✔
3655
            pszVirtualIO = "v";
258✔
3656
        else
3657
            pszVirtualIO = "";
69✔
3658

3659
        if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
327✔
3660
            pszSubdatasets = "s";
44✔
3661
        else
3662
            pszSubdatasets = "";
283✔
3663

3664
        CPLString osKind;
654✔
3665
        if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
327✔
3666
            osKind = "raster";
193✔
3667
        if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
327✔
3668
        {
3669
            if (!osKind.empty())
20✔
3670
                osKind += ',';
20✔
3671
            osKind += "multidimensional raster";
20✔
3672
        }
3673
        if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
327✔
3674
        {
3675
            if (!osKind.empty())
197✔
3676
                osKind += ',';
63✔
3677
            osKind += "vector";
197✔
3678
        }
3679
        if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
327✔
3680
        {
3681
            if (!osKind.empty())
×
3682
                osKind += ',';
×
3683
            osKind += "geography network";
×
3684
        }
3685
        if (osKind.empty())
327✔
3686
            osKind = "unknown kind";
×
3687

3688
        std::string osExtensions;
654✔
3689
        if (const char *pszExtensions = CSLFetchNameValueDef(
327✔
3690
                papszMD, GDAL_DMD_EXTENSIONS,
3691
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3692
        {
3693
            const CPLStringList aosExt(
3694
                CSLTokenizeString2(pszExtensions, " ", 0));
440✔
3695
            for (int i = 0; i < aosExt.size(); ++i)
530✔
3696
            {
3697
                if (i == 0)
310✔
3698
                    osExtensions = " (*.";
219✔
3699
                else
3700
                    osExtensions += ", *.";
91✔
3701
                osExtensions += aosExt[i];
310✔
3702
            }
3703
            if (!osExtensions.empty())
220✔
3704
                osExtensions += ')';
219✔
3705
        }
3706

3707
        ret += CPLSPrintf("  %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/
3708
                          GDALGetDriverShortName(hDriver), osKind.c_str(),
3709
                          pszRFlag, pszWFlag, pszUpdate, pszVirtualIO,
3710
                          pszSubdatasets, GDALGetDriverLongName(hDriver),
3711
                          osExtensions.c_str());
327✔
3712
    }
3713

3714
    return ret;
3✔
3715
}
3716

3717
/************************************************************************/
3718
/*                    GDALGeneralCmdLineProcessor()                     */
3719
/************************************************************************/
3720

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

3766
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
1,314✔
3767
                                            int nOptions)
3768

3769
{
3770
    CPLStringList aosReturn;
2,628✔
3771
    int iArg;
3772
    char **papszArgv = *ppapszArgv;
1,314✔
3773

3774
    /* -------------------------------------------------------------------- */
3775
    /*      Preserve the program name.                                      */
3776
    /* -------------------------------------------------------------------- */
3777
    aosReturn.AddString(papszArgv[0]);
1,314✔
3778

3779
    /* ==================================================================== */
3780
    /*      Loop over all arguments.                                        */
3781
    /* ==================================================================== */
3782

3783
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3784
    // detects and warns about a unknown config option.
3785
    for (iArg = 1; iArg < nArgc; iArg++)
8,118✔
3786
    {
3787
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
6,806✔
3788
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
39✔
3789
        {
3790
            if (iArg + 1 >= nArgc)
×
3791
            {
3792
                CPLError(CE_Failure, CPLE_AppDefined,
×
3793
                         "--config option given without a key=value argument.");
3794
                return -1;
×
3795
            }
3796

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

3820
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
×
3821
                    CPLSetConfigOption(papszArgv[iArg + 1],
×
3822
                                       papszArgv[iArg + 2]);
×
3823

3824
                iArg += 2;
×
3825
            }
×
3826
        }
3827
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,806✔
3828
        {
3829
            if (iArg + 1 >= nArgc)
13✔
3830
            {
3831
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3832
                         "--debug option given without debug level.");
3833
                return -1;
2✔
3834
            }
3835

3836
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
11✔
3837
            iArg += 1;
11✔
3838
        }
3839
    }
3840

3841
    for (iArg = 1; iArg < nArgc; iArg++)
7,925✔
3842
    {
3843
        /* --------------------------------------------------------------------
3844
         */
3845
        /*      --version */
3846
        /* --------------------------------------------------------------------
3847
         */
3848
        if (EQUAL(papszArgv[iArg], "--version"))
6,703✔
3849
        {
3850
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
64✔
3851
            return 0;
64✔
3852
        }
3853

3854
        /* --------------------------------------------------------------------
3855
         */
3856
        /*      --build */
3857
        /* --------------------------------------------------------------------
3858
         */
3859
        else if (EQUAL(papszArgv[iArg], "--build"))
6,639✔
3860
        {
3861
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
1✔
3862
            return 0;
1✔
3863
        }
3864

3865
        /* --------------------------------------------------------------------
3866
         */
3867
        /*      --license */
3868
        /* --------------------------------------------------------------------
3869
         */
3870
        else if (EQUAL(papszArgv[iArg], "--license"))
6,638✔
3871
        {
3872
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
1✔
3873
            return 0;
1✔
3874
        }
3875

3876
        /* --------------------------------------------------------------------
3877
         */
3878
        /*      --config */
3879
        /* --------------------------------------------------------------------
3880
         */
3881
        else if (EQUAL(papszArgv[iArg], "--config"))
6,637✔
3882
        {
3883
            if (iArg + 1 >= nArgc)
45✔
3884
            {
3885
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3886
                         "--config option given without a key=value argument.");
3887
                return -1;
2✔
3888
            }
3889

3890
            const char *pszArg = papszArgv[iArg + 1];
43✔
3891
            if (strchr(pszArg, '=') != nullptr)
43✔
3892
            {
3893
                char *pszKey = nullptr;
3✔
3894
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3✔
3895
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3✔
3896
                {
3897
                    CPLSetConfigOption(pszKey, pszValue);
3✔
3898
                }
3899
                CPLFree(pszKey);
3✔
3900
                ++iArg;
3✔
3901
            }
3902
            else
3903
            {
3904
                if (iArg + 2 >= nArgc)
40✔
3905
                {
3906
                    CPLError(CE_Failure, CPLE_AppDefined,
2✔
3907
                             "--config option given without a key and value "
3908
                             "argument.");
3909
                    return -1;
2✔
3910
                }
3911

3912
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
38✔
3913
                    CPLSetConfigOption(papszArgv[iArg + 1],
38✔
3914
                                       papszArgv[iArg + 2]);
38✔
3915

3916
                iArg += 2;
38✔
3917
            }
3918
        }
3919

3920
        /* --------------------------------------------------------------------
3921
         */
3922
        /*      --mempreload */
3923
        /* --------------------------------------------------------------------
3924
         */
3925
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
6,592✔
3926
        {
3927
            if (iArg + 1 >= nArgc)
4✔
3928
            {
3929
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3930
                         "--mempreload option given without directory path.");
3931
                return -1;
2✔
3932
            }
3933

3934
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
2✔
3935
            if (CSLCount(papszFiles) == 0)
2✔
3936
            {
3937
                CPLError(CE_Failure, CPLE_AppDefined,
×
3938
                         "--mempreload given invalid or empty directory.");
3939
                return -1;
×
3940
            }
3941

3942
            for (int i = 0; papszFiles[i] != nullptr; i++)
495✔
3943
            {
3944
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
493✔
3945
                    continue;
72✔
3946

3947
                std::string osOldPath;
489✔
3948
                CPLString osNewPath;
489✔
3949
                osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
489✔
3950
                                                papszFiles[i], nullptr);
489✔
3951
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
489✔
3952

3953
                VSIStatBufL sStatBuf;
3954
                if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
978✔
3955
                    VSI_ISDIR(sStatBuf.st_mode))
489✔
3956
                {
3957
                    CPLDebug("VSI", "Skipping preload of %s.",
68✔
3958
                             osOldPath.c_str());
3959
                    continue;
68✔
3960
                }
3961

3962
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
421✔
3963
                         osNewPath.c_str());
3964

3965
                if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
421✔
3966
                {
3967
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3968
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3969
                    return -1;
×
3970
                }
3971
            }
3972

3973
            CSLDestroy(papszFiles);
2✔
3974
            iArg += 1;
2✔
3975
        }
3976

3977
        /* --------------------------------------------------------------------
3978
         */
3979
        /*      --debug */
3980
        /* --------------------------------------------------------------------
3981
         */
3982
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,588✔
3983
        {
3984
            if (iArg + 1 >= nArgc)
11✔
3985
            {
3986
                CPLError(CE_Failure, CPLE_AppDefined,
×
3987
                         "--debug option given without debug level.");
3988
                return -1;
×
3989
            }
3990

3991
            iArg += 1;
11✔
3992
        }
3993

3994
        /* --------------------------------------------------------------------
3995
         */
3996
        /*      --optfile */
3997
        /* --------------------------------------------------------------------
3998
         */
3999
        else if (EQUAL(papszArgv[iArg], "--optfile"))
6,577✔
4000
        {
4001
            if (iArg + 1 >= nArgc)
11✔
4002
            {
4003
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
4004
                         "--optfile option given without filename.");
4005
                return -1;
5✔
4006
            }
4007

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

4010
            if (fpOptFile == nullptr)
9✔
4011
            {
4012
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
4013
                         "Unable to open optfile '%s'.\n%s",
4014
                         papszArgv[iArg + 1], VSIStrerror(errno));
2✔
4015
                return -1;
2✔
4016
            }
4017

4018
            const char *pszLine;
4019
            CPLStringList aosArgvOptfile;
7✔
4020
            // dummy value as first argument to please
4021
            // GDALGeneralCmdLineProcessor()
4022
            aosArgvOptfile.AddString("");
7✔
4023
            bool bHasOptfile = false;
7✔
4024
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
23✔
4025
            {
4026
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
16✔
4027
                    continue;
3✔
4028

4029
                char **papszTokens = CSLTokenizeString(pszLine);
13✔
4030
                for (int i = 0;
13✔
4031
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
45✔
4032
                {
4033
                    if (EQUAL(papszTokens[i], "--optfile"))
32✔
4034
                    {
4035
                        // To avoid potential recursion
4036
                        CPLError(CE_Warning, CPLE_AppDefined,
×
4037
                                 "--optfile not supported in a option file");
4038
                        bHasOptfile = true;
×
4039
                    }
4040
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
32✔
4041
                    papszTokens[i] = nullptr;
32✔
4042
                }
4043
                CSLDestroy(papszTokens);
13✔
4044
            }
4045

4046
            VSIFCloseL(fpOptFile);
7✔
4047

4048
            char **papszArgvOptfile = aosArgvOptfile.StealList();
7✔
4049
            if (!bHasOptfile)
7✔
4050
            {
4051
                char **papszArgvOptfileBefore = papszArgvOptfile;
7✔
4052
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
7✔
4053
                                                &papszArgvOptfile,
4054
                                                nOptions) < 0)
7✔
4055
                {
4056
                    CSLDestroy(papszArgvOptfile);
1✔
4057
                    return -1;
1✔
4058
                }
4059
                CSLDestroy(papszArgvOptfileBefore);
6✔
4060
            }
4061

4062
            char **papszIter = papszArgvOptfile + 1;
6✔
4063
            while (*papszIter)
36✔
4064
            {
4065
                aosReturn.AddString(*papszIter);
30✔
4066
                ++papszIter;
30✔
4067
            }
4068
            CSLDestroy(papszArgvOptfile);
6✔
4069

4070
            iArg += 1;
6✔
4071
        }
4072

4073
        /* --------------------------------------------------------------------
4074
         */
4075
        /*      --formats */
4076
        /* --------------------------------------------------------------------
4077
         */
4078
        else if (EQUAL(papszArgv[iArg], "--formats"))
6,566✔
4079
        {
4080
            bool bJSON = false;
5✔
4081
            for (int i = 1; i < nArgc; i++)
10✔
4082
            {
4083
                if (strcmp(papszArgv[i], "-json") == 0 ||
7✔
4084
                    strcmp(papszArgv[i], "--json") == 0)
5✔
4085
                {
4086
                    bJSON = true;
2✔
4087
                    break;
2✔
4088
                }
4089
            }
4090

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

4093
            return 0;
5✔
4094
        }
4095

4096
        /* --------------------------------------------------------------------
4097
         */
4098
        /*      --format */
4099
        /* --------------------------------------------------------------------
4100
         */
4101
        else if (EQUAL(papszArgv[iArg], "--format"))
6,561✔
4102
        {
4103
            GDALDriverH hDriver;
4104
            char **papszMD;
4105

4106
            if (iArg + 1 >= nArgc)
6✔
4107
            {
4108
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
4109
                         "--format option given without a format code.");
4110
                return -1;
1✔
4111
            }
4112

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

4126
            printf("Format Details:\n"); /*ok*/
4✔
4127
            printf(/*ok*/ "  Short Name: %s\n",
4✔
4128
                   GDALGetDriverShortName(hDriver));
4129
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
4✔
4130

4131
            papszMD = GDALGetMetadata(hDriver, nullptr);
4✔
4132
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
4✔
4133
                printf("  Supports: Raster\n"); /*ok*/
4✔
4134
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
4✔
4135
                printf("  Supports: Multidimensional raster\n"); /*ok*/
1✔
4136
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
4✔
4137
                printf("  Supports: Vector\n"); /*ok*/
3✔
4138
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
4✔
4139
                printf("  Supports: Geography Network\n"); /*ok*/
×
4140

4141
            const char *pszExt =
4142
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
4✔
4143
            if (pszExt != nullptr)
4✔
4144
                printf("  Extension%s: %s\n", /*ok*/
3✔
4145
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
3✔
4146

4147
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
4✔
4148
                printf("  Mime Type: %s\n", /*ok*/
1✔
4149
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4150
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
4✔
4151
                printf("  Help Topic: %s\n", /*ok*/
3✔
4152
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4153

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

4223
            for (const char *key :
32✔
4224
                 {GDAL_DMD_CREATIONOPTIONLIST,
4225
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4226
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4227
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4228
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4229
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4230
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4231
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
36✔
4232
            {
4233
                if (CSLFetchNameValue(papszMD, key))
32✔
4234
                {
4235
                    CPLXMLNode *psCOL =
4236
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
7✔
4237
                    StripIrrelevantOptions(psCOL, nOptions);
7✔
4238
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
7✔
4239

4240
                    CPLDestroyXMLNode(psCOL);
7✔
4241

4242
                    printf("\n%s\n", pszFormattedXML); /*ok*/
7✔
4243
                    CPLFree(pszFormattedXML);
7✔
4244
                }
4245
            }
4246

4247
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
4✔
4248
                printf("  Connection prefix: %s\n", /*ok*/
×
4249
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4250

4251
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
4✔
4252
            {
4253
                CPLXMLNode *psCOL = CPLParseXMLString(
3✔
4254
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4255
                StripIrrelevantOptions(psCOL, nOptions);
3✔
4256
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
3✔
4257

4258
                CPLDestroyXMLNode(psCOL);
3✔
4259

4260
                printf("%s\n", pszFormattedXML); /*ok*/
3✔
4261
                CPLFree(pszFormattedXML);
3✔
4262
            }
4263

4264
            bool bFirstOtherOption = true;
4✔
4265
            for (char **papszIter = papszMD; papszIter && *papszIter;
149✔
4266
                 ++papszIter)
4267
            {
4268
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
145✔
4269
                    !STARTS_WITH(*papszIter, "DMD_") &&
62✔
4270
                    !STARTS_WITH(*papszIter, "DS_") &&
16✔
4271
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
13✔
4272
                {
4273
                    if (bFirstOtherOption)
13✔
4274
                        printf("  Other metadata items:\n"); /*ok*/
4✔
4275
                    bFirstOtherOption = false;
13✔
4276
                    printf("    %s\n", *papszIter); /*ok*/
13✔
4277
                }
4278
            }
4279

4280
            return 0;
4✔
4281
        }
4282

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

4314
            return 0;
2✔
4315
        }
4316

4317
        /* --------------------------------------------------------------------
4318
         */
4319
        /*      --locale */
4320
        /* --------------------------------------------------------------------
4321
         */
4322
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
6,553✔
4323
        {
4324
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
2✔
4325
        }
4326

4327
        /* --------------------------------------------------------------------
4328
         */
4329
        /*      --pause */
4330
        /* --------------------------------------------------------------------
4331
         */
4332
        else if (EQUAL(papszArgv[iArg], "--pause"))
6,551✔
4333
        {
4334
            std::cout << "Hit <ENTER> to Continue." << std::endl;
×
4335
            std::cin.clear();
×
4336
            std::cin.ignore(cpl::NumericLimits<std::streamsize>::max(), '\n');
×
4337
        }
4338

4339
        /* --------------------------------------------------------------------
4340
         */
4341
        /*      Carry through unrecognized options. */
4342
        /* --------------------------------------------------------------------
4343
         */
4344
        else
4345
        {
4346
            aosReturn.AddString(papszArgv[iArg]);
6,551✔
4347
        }
4348
    }
4349

4350
    const int nSize = aosReturn.size();
1,222✔
4351
    *ppapszArgv = aosReturn.StealList();
1,222✔
4352

4353
    return nSize;
1,222✔
4354
}
4355

4356
/************************************************************************/
4357
/*                          _FetchDblFromMD()                           */
4358
/************************************************************************/
4359

4360
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
1,680✔
4361
                            double *padfTarget, int nCount, double dfDefault)
4362

4363
{
4364
    char szFullKey[200];
4365

4366
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
1,680✔
4367

4368
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
1,680✔
4369

4370
    for (int i = 0; i < nCount; i++)
9,744✔
4371
        padfTarget[i] = dfDefault;
8,064✔
4372

4373
    if (pszValue == nullptr)
1,680✔
4374
        return false;
414✔
4375

4376
    if (nCount == 1)
1,266✔
4377
    {
4378
        *padfTarget = CPLAtofM(pszValue);
930✔
4379
        return true;
930✔
4380
    }
4381

4382
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
336✔
4383

4384
    if (CSLCount(papszTokens) != nCount)
336✔
4385
    {
4386
        CSLDestroy(papszTokens);
×
4387
        return false;
×
4388
    }
4389

4390
    for (int i = 0; i < nCount; i++)
7,056✔
4391
        padfTarget[i] = CPLAtofM(papszTokens[i]);
6,720✔
4392

4393
    CSLDestroy(papszTokens);
336✔
4394

4395
    return true;
336✔
4396
}
4397

4398
/************************************************************************/
4399
/*                         GDALExtractRPCInfo()                         */
4400
/************************************************************************/
4401

4402
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4403
 *
4404
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4405
 *
4406
 * @param papszMD Dictionary of metadata representing RPC
4407
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4408
 * @return TRUE in case of success. FALSE in case of failure.
4409
 */
4410
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
×
4411

4412
{
4413
    GDALRPCInfoV2 sRPC;
4414
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
×
4415
        return FALSE;
×
4416
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
×
4417
    return TRUE;
×
4418
}
4419

4420
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4421
 *
4422
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4423
 *
4424
 * @param papszMD Dictionary of metadata representing RPC
4425
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4426
 * @return TRUE in case of success. FALSE in case of failure.
4427
 */
4428
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
84✔
4429

4430
{
4431
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
84✔
4432
        return FALSE;
×
4433

4434
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
84✔
4435
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
84✔
4436
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
252✔
4437
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
84✔
4438
    {
4439
        CPLError(CE_Failure, CPLE_AppDefined,
×
4440
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
4441
        return FALSE;
×
4442
    }
4443

4444
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
84✔
4445
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
84✔
4446
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
84✔
4447
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
84✔
4448
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
84✔
4449
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
84✔
4450
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
84✔
4451
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
84✔
4452
                    1.0);
4453
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
84✔
4454
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
84✔
4455
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
84✔
4456
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
84✔
4457

4458
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
84✔
4459
                    0.0);
4460
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
84✔
4461
                    0.0);
4462
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
84✔
4463
                    0.0);
4464
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
84✔
4465
                    0.0);
4466

4467
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
84✔
4468
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
84✔
4469
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
84✔
4470
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
84✔
4471

4472
    return TRUE;
84✔
4473
}
4474

4475
/************************************************************************/
4476
/*                     GDALFindAssociatedAuxFile()                      */
4477
/************************************************************************/
4478

4479
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
10,216✔
4480
                                       GDALAccess eAccess,
4481
                                       GDALDataset *poDependentDS)
4482

4483
{
4484
    const char *pszAuxSuffixLC = "aux";
10,216✔
4485
    const char *pszAuxSuffixUC = "AUX";
10,216✔
4486

4487
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
10,216✔
4488
        return nullptr;
32✔
4489

4490
    /* -------------------------------------------------------------------- */
4491
    /*      Don't even try to look for an .aux file if we don't have a      */
4492
    /*      path of any kind.                                               */
4493
    /* -------------------------------------------------------------------- */
4494
    if (strlen(pszBasename) == 0)
10,184✔
4495
        return nullptr;
55✔
4496

4497
    /* -------------------------------------------------------------------- */
4498
    /*      We didn't find that, so try and find a corresponding aux        */
4499
    /*      file.  Check that we are the dependent file of the aux          */
4500
    /*      file, or if we aren't verify that the dependent file does       */
4501
    /*      not exist, likely mean it is us but some sort of renaming       */
4502
    /*      has occurred.                                                   */
4503
    /* -------------------------------------------------------------------- */
4504
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
20,258✔
4505
    CPLString osAuxFilename =
4506
        CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC);
10,129✔
4507
    GDALDataset *poODS = nullptr;
10,129✔
4508
    GByte abyHeader[32];
4509

4510
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
10,129✔
4511

4512
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,129✔
4513
    {
4514
        // Can't found file with lower case suffix. Try the upper case one.
4515
        osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC);
10,106✔
4516
        fp = VSIFOpenL(osAuxFilename, "rb");
10,106✔
4517
    }
4518

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

4539
    /* -------------------------------------------------------------------- */
4540
    /*      Try replacing extension with .aux                               */
4541
    /* -------------------------------------------------------------------- */
4542
    if (poODS != nullptr)
10,129✔
4543
    {
4544
        const char *pszDep =
4545
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
18✔
4546
        if (pszDep == nullptr)
18✔
4547
        {
4548
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
×
4549
                     osAuxFilename.c_str());
4550
            GDALClose(poODS);
×
4551
            poODS = nullptr;
×
4552
        }
4553
        else if (!EQUAL(pszDep, osJustFile))
18✔
4554
        {
4555
            VSIStatBufL sStatBuf;
4556

4557
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4558
            {
4559
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4560
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4561
                GDALClose(poODS);
×
4562
                poODS = nullptr;
×
4563
            }
4564
            else
4565
            {
4566
                CPLDebug("AUX",
×
4567
                         "%s is for file %s, not %s, but since\n"
4568
                         "%s does not exist, we will use .aux file as our own.",
4569
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4570
                         pszDep);
4571
            }
4572
        }
4573

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

4595
            GDALClose(poODS);
3✔
4596
            poODS = nullptr;
3✔
4597
        }
4598
    }
4599

4600
    /* -------------------------------------------------------------------- */
4601
    /*      Try appending .aux to the end of the filename.                  */
4602
    /* -------------------------------------------------------------------- */
4603
    if (poODS == nullptr)
10,129✔
4604
    {
4605
        osAuxFilename = pszBasename;
10,114✔
4606
        osAuxFilename += ".";
10,114✔
4607
        osAuxFilename += pszAuxSuffixLC;
10,114✔
4608
        fp = VSIFOpenL(osAuxFilename, "rb");
10,114✔
4609
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,114✔
4610
        {
4611
            // Can't found file with lower case suffix. Try the upper case one.
4612
            osAuxFilename = pszBasename;
10,114✔
4613
            osAuxFilename += ".";
10,114✔
4614
            osAuxFilename += pszAuxSuffixUC;
10,114✔
4615
            fp = VSIFOpenL(osAuxFilename, "rb");
10,114✔
4616
        }
4617

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

4638
        if (poODS != nullptr)
10,114✔
4639
        {
4640
            const char *pszDep =
4641
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
×
4642
            if (pszDep == nullptr)
×
4643
            {
4644
                CPLDebug("AUX",
×
4645
                         "Found %s but it has no dependent file, ignoring.",
4646
                         osAuxFilename.c_str());
4647
                GDALClose(poODS);
×
4648
                poODS = nullptr;
×
4649
            }
4650
            else if (!EQUAL(pszDep, osJustFile))
×
4651
            {
4652
                VSIStatBufL sStatBuf;
4653

4654
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4655
                {
4656
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4657
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4658
                    GDALClose(poODS);
×
4659
                    poODS = nullptr;
×
4660
                }
4661
                else
4662
                {
4663
                    CPLDebug(
×
4664
                        "AUX",
4665
                        "%s is for file %s, not %s, but since\n"
4666
                        "%s does not exist, we will use .aux file as our own.",
4667
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4668
                        pszDep);
4669
                }
4670
            }
4671
        }
4672
    }
4673

4674
    /* -------------------------------------------------------------------- */
4675
    /*      Confirm that the aux file matches the configuration of the      */
4676
    /*      dependent dataset.                                              */
4677
    /* -------------------------------------------------------------------- */
4678
    if (poODS != nullptr && poDependentDS != nullptr &&
10,144✔
4679
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
15✔
4680
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
15✔
4681
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
15✔
4682
    {
4683
        CPLDebug(
×
4684
            "AUX",
4685
            "Ignoring aux file %s as its raster configuration\n"
4686
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4687
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4688
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4689
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4690
            poDependentDS->GetRasterCount());
4691

4692
        GDALClose(poODS);
×
4693
        poODS = nullptr;
×
4694
    }
4695

4696
    return poODS;
10,129✔
4697
}
4698

4699
/************************************************************************/
4700
/* Infrastructure to check that dataset characteristics are valid       */
4701
/************************************************************************/
4702

4703
CPL_C_START
4704

4705
/**
4706
 * \brief Return TRUE if the dataset dimensions are valid.
4707
 *
4708
 * @param nXSize raster width
4709
 * @param nYSize raster height
4710
 *
4711
 * @since GDAL 1.7.0
4712
 */
4713
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
5,493✔
4714
{
4715
    if (nXSize <= 0 || nYSize <= 0)
5,493✔
4716
    {
4717
        CPLError(CE_Failure, CPLE_AppDefined,
8✔
4718
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4719
        return FALSE;
8✔
4720
    }
4721
    return TRUE;
5,485✔
4722
}
4723

4724
/**
4725
 * \brief Return TRUE if the band count is valid.
4726
 *
4727
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4728
 * the band count will be compared to the maximum number of band allowed.
4729
 * If not defined, the maximum number allowed is 65536.
4730
 *
4731
 * @param nBands the band count
4732
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4733
 *
4734
 * @since GDAL 1.7.0
4735
 */
4736

4737
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
107,435✔
4738
{
4739
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
107,435✔
4740
    {
4741
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
6✔
4742
                 nBands);
4743
        return FALSE;
6✔
4744
    }
4745
    const char *pszMaxBandCount =
4746
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
107,429✔
4747
    int nMaxBands = std::clamp(atoi(pszMaxBandCount), 0, INT_MAX - 1);
107,429✔
4748
    if (nBands > nMaxBands)
107,429✔
4749
    {
4750
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
4751
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4752
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4753
                 "legitimate number.",
4754
                 nBands, nMaxBands);
4755
        return FALSE;
2✔
4756
    }
4757
    return TRUE;
107,427✔
4758
}
4759

4760
CPL_C_END
4761

4762
/************************************************************************/
4763
/*                     GDALSerializeGCPListToXML()                      */
4764
/************************************************************************/
4765

4766
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
16✔
4767
                               const std::vector<gdal::GCP> &asGCPs,
4768
                               const OGRSpatialReference *poGCP_SRS)
4769
{
4770
    CPLString oFmt;
32✔
4771

4772
    CPLXMLNode *psPamGCPList =
4773
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
16✔
4774

4775
    CPLXMLNode *psLastChild = nullptr;
16✔
4776

4777
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
16✔
4778
    {
4779
        char *pszWKT = nullptr;
9✔
4780
        poGCP_SRS->exportToWkt(&pszWKT);
9✔
4781
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
9✔
4782
        CPLFree(pszWKT);
9✔
4783
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
9✔
4784
        CPLString osMapping;
9✔
4785
        for (size_t i = 0; i < mapping.size(); ++i)
27✔
4786
        {
4787
            if (!osMapping.empty())
18✔
4788
                osMapping += ",";
9✔
4789
            osMapping += CPLSPrintf("%d", mapping[i]);
18✔
4790
        }
4791
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
9✔
4792
                       osMapping.c_str());
4793

4794
        psLastChild = psPamGCPList->psChild->psNext;
9✔
4795
    }
4796

4797
    for (const gdal::GCP &gcp : asGCPs)
21,896✔
4798
    {
4799
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
21,880✔
4800

4801
        if (psLastChild == nullptr)
21,880✔
4802
            psPamGCPList->psChild = psXMLGCP;
7✔
4803
        else
4804
            psLastChild->psNext = psXMLGCP;
21,873✔
4805
        psLastChild = psXMLGCP;
21,880✔
4806

4807
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
21,880✔
4808

4809
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
21,880✔
4810
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
×
4811

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

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

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

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

4820
        /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
4821
         * back */
4822
        if (gcp.Z() != 0.0)
21,880✔
4823
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
21,860✔
4824
    }
4825
}
16✔
4826

4827
/************************************************************************/
4828
/*                     GDALDeserializeGCPListFromXML()                  */
4829
/************************************************************************/
4830

4831
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
84✔
4832
                                   std::vector<gdal::GCP> &asGCPs,
4833
                                   OGRSpatialReference **ppoGCP_SRS)
4834
{
4835
    if (ppoGCP_SRS)
84✔
4836
    {
4837
        const char *pszRawProj =
4838
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
76✔
4839

4840
        *ppoGCP_SRS = nullptr;
76✔
4841
        if (pszRawProj && pszRawProj[0])
76✔
4842
        {
4843
            *ppoGCP_SRS = new OGRSpatialReference();
60✔
4844
            (*ppoGCP_SRS)
4845
                ->SetFromUserInput(
60✔
4846
                    pszRawProj,
4847
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4848

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

4871
    asGCPs.clear();
84✔
4872
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
24,612✔
4873
         psXMLGCP = psXMLGCP->psNext)
24,528✔
4874
    {
4875
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
24,528✔
4876
            continue;
81✔
4877

4878
        gdal::GCP gcp;
48,894✔
4879
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
24,447✔
4880
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
24,447✔
4881

4882
        const auto ParseDoubleValue =
4883
            [psXMLGCP](const char *pszParameter, double &dfVal)
97,788✔
4884
        {
4885
            const char *pszVal =
4886
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
97,788✔
4887
            if (!pszVal)
97,788✔
4888
            {
4889
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
×
4890
                         pszParameter);
4891
                return false;
×
4892
            }
4893
            char *endptr = nullptr;
97,788✔
4894
            dfVal = CPLStrtod(pszVal, &endptr);
97,788✔
4895
            if (endptr == pszVal)
97,788✔
4896
            {
4897
                CPLError(CE_Failure, CPLE_AppDefined,
×
4898
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4899
                return false;
×
4900
            }
4901
            return true;
97,788✔
4902
        };
24,447✔
4903

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

4929
        if (bOK)
24,447✔
4930
        {
4931
            asGCPs.emplace_back(std::move(gcp));
24,447✔
4932
        }
4933
    }
4934
}
84✔
4935

4936
/************************************************************************/
4937
/*                   GDALSerializeOpenOptionsToXML()                    */
4938
/************************************************************************/
4939

4940
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
2,668✔
4941
                                   CSLConstList papszOpenOptions)
4942
{
4943
    if (papszOpenOptions != nullptr)
2,668✔
4944
    {
4945
        CPLXMLNode *psOpenOptions =
4946
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
5✔
4947
        CPLXMLNode *psLastChild = nullptr;
5✔
4948

4949
        for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
10✔
4950
             papszIter++)
4951
        {
4952
            const char *pszRawValue;
4953
            char *pszKey = nullptr;
5✔
4954
            CPLXMLNode *psOOI;
4955

4956
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
5✔
4957

4958
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
5✔
4959
            if (psLastChild == nullptr)
5✔
4960
                psOpenOptions->psChild = psOOI;
5✔
4961
            else
4962
                psLastChild->psNext = psOOI;
×
4963
            psLastChild = psOOI;
5✔
4964

4965
            CPLSetXMLValue(psOOI, "#key", pszKey);
5✔
4966
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
5✔
4967

4968
            CPLFree(pszKey);
5✔
4969
        }
4970
    }
4971
}
2,668✔
4972

4973
/************************************************************************/
4974
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4975
/************************************************************************/
4976

4977
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
104,714✔
4978
{
4979
    char **papszOpenOptions = nullptr;
104,714✔
4980
    const CPLXMLNode *psOpenOptions =
4981
        CPLGetXMLNode(psParentNode, "OpenOptions");
104,714✔
4982
    if (psOpenOptions != nullptr)
104,714✔
4983
    {
4984
        const CPLXMLNode *psOOI;
4985
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
34✔
4986
             psOOI = psOOI->psNext)
17✔
4987
        {
4988
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
17✔
4989
                psOOI->psChild == nullptr ||
17✔
4990
                psOOI->psChild->psNext == nullptr ||
17✔
4991
                psOOI->psChild->eType != CXT_Attribute ||
17✔
4992
                psOOI->psChild->psChild == nullptr)
17✔
4993
                continue;
×
4994

4995
            char *pszName = psOOI->psChild->psChild->pszValue;
17✔
4996
            char *pszValue = psOOI->psChild->psNext->pszValue;
17✔
4997
            if (pszName != nullptr && pszValue != nullptr)
17✔
4998
                papszOpenOptions =
4999
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
17✔
5000
        }
5001
    }
5002
    return papszOpenOptions;
104,714✔
5003
}
5004

5005
/************************************************************************/
5006
/*                    GDALRasterIOGetResampleAlg()                      */
5007
/************************************************************************/
5008

5009
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
203,661✔
5010
{
5011
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
203,661✔
5012
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
203,661✔
5013
        eResampleAlg = GRIORA_NearestNeighbour;
200,939✔
5014
    else if (EQUAL(pszResampling, "BILINEAR"))
2,722✔
5015
        eResampleAlg = GRIORA_Bilinear;
2,082✔
5016
    else if (EQUAL(pszResampling, "CUBIC"))
640✔
5017
        eResampleAlg = GRIORA_Cubic;
575✔
5018
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
65✔
5019
        eResampleAlg = GRIORA_CubicSpline;
4✔
5020
    else if (EQUAL(pszResampling, "LANCZOS"))
61✔
5021
        eResampleAlg = GRIORA_Lanczos;
1✔
5022
    else if (EQUAL(pszResampling, "AVERAGE"))
60✔
5023
        eResampleAlg = GRIORA_Average;
53✔
5024
    else if (EQUAL(pszResampling, "RMS"))
7✔
5025
        eResampleAlg = GRIORA_RMS;
1✔
5026
    else if (EQUAL(pszResampling, "MODE"))
6✔
5027
        eResampleAlg = GRIORA_Mode;
5✔
5028
    else if (EQUAL(pszResampling, "GAUSS"))
1✔
5029
        eResampleAlg = GRIORA_Gauss;
1✔
5030
    else
5031
        CPLError(CE_Warning, CPLE_NotSupported,
×
5032
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
5033
    return eResampleAlg;
203,661✔
5034
}
5035

5036
/************************************************************************/
5037
/*                    GDALRasterIOGetResampleAlgStr()                   */
5038
/************************************************************************/
5039

5040
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
7✔
5041
{
5042
    switch (eResampleAlg)
7✔
5043
    {
5044
        case GRIORA_NearestNeighbour:
×
5045
            return "NearestNeighbour";
×
5046
        case GRIORA_Bilinear:
×
5047
            return "Bilinear";
×
5048
        case GRIORA_Cubic:
7✔
5049
            return "Cubic";
7✔
5050
        case GRIORA_CubicSpline:
×
5051
            return "CubicSpline";
×
5052
        case GRIORA_Lanczos:
×
5053
            return "Lanczos";
×
5054
        case GRIORA_Average:
×
5055
            return "Average";
×
5056
        case GRIORA_RMS:
×
5057
            return "RMS";
×
5058
        case GRIORA_Mode:
×
5059
            return "Mode";
×
5060
        case GRIORA_Gauss:
×
5061
            return "Gauss";
×
5062
        default:
×
5063
            CPLAssert(false);
×
5064
            return "Unknown";
5065
    }
5066
}
5067

5068
/************************************************************************/
5069
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
5070
/************************************************************************/
5071

5072
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
5,197,050✔
5073
                                        int nXSize, int nYSize, int nBufXSize,
5074
                                        int nBufYSize)
5075
{
5076
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
5,197,050✔
5077
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
572,079✔
5078
    {
5079
        const char *pszResampling =
5080
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
569,010✔
5081
        if (pszResampling != nullptr)
569,010✔
5082
        {
5083
            psExtraArg->eResampleAlg =
1✔
5084
                GDALRasterIOGetResampleAlg(pszResampling);
1✔
5085
        }
5086
    }
5087
}
5,197,050✔
5088

5089
/************************************************************************/
5090
/*                     GDALCanFileAcceptSidecarFile()                   */
5091
/************************************************************************/
5092

5093
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
125,297✔
5094
{
5095
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
125,297✔
5096
        return FALSE;
×
5097
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
5098
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
125,297✔
5099
        return FALSE;
402✔
5100
    return TRUE;
124,895✔
5101
}
5102

5103
/************************************************************************/
5104
/*                   GDALCanReliablyUseSiblingFileList()                */
5105
/************************************************************************/
5106

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

5146
            // if this is a network storage, assume no issue
5147
            if (!VSIIsLocal(pszFilename))
5148
            {
5149
                return true;
5150
            }
5151
            return false;
5152
        }
5153
    }
5154
    return true;
5155
#else
5156
    (void)pszFilename;
5157
    return true;
139,450✔
5158
#endif
5159
}
5160

5161
/************************************************************************/
5162
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
5163
/************************************************************************/
5164

5165
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
1,345✔
5166
{
5167
    const auto kMaxFloat = cpl::NumericLimits<float>::max();
1,345✔
5168
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
1,345✔
5169
        return -kMaxFloat;
33✔
5170
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
1,312✔
5171
        return kMaxFloat;
8✔
5172
    return dfVal;
1,304✔
5173
}
5174

5175
/************************************************************************/
5176
/*                        GDALCopyNoDataValue()                         */
5177
/************************************************************************/
5178

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

5293
/************************************************************************/
5294
/*                     GDALGetNoDataValueCastToDouble()                 */
5295
/************************************************************************/
5296

5297
double GDALGetNoDataValueCastToDouble(int64_t nVal)
2✔
5298
{
5299
    const double dfVal = static_cast<double>(nVal);
2✔
5300
    if (static_cast<int64_t>(dfVal) != nVal)
2✔
5301
    {
5302
        CPLError(CE_Warning, CPLE_AppDefined,
×
5303
                 "GetNoDataValue() returns an approximate value of the "
5304
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5305
                 "GetNoDataValueAsInt64() instead",
5306
                 static_cast<GIntBig>(nVal));
5307
    }
5308
    return dfVal;
2✔
5309
}
5310

5311
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
2✔
5312
{
5313
    const double dfVal = static_cast<double>(nVal);
2✔
5314
    if (static_cast<uint64_t>(dfVal) != nVal)
2✔
5315
    {
5316
        CPLError(CE_Warning, CPLE_AppDefined,
×
5317
                 "GetNoDataValue() returns an approximate value of the "
5318
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5319
                 "GetNoDataValueAsUInt64() instead",
5320
                 static_cast<GUIntBig>(nVal));
5321
    }
5322
    return dfVal;
2✔
5323
}
5324

5325
/************************************************************************/
5326
/*                GDALGetCompressionFormatForJPEG()                     */
5327
/************************************************************************/
5328

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

5514
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
16✔
5515
                                            size_t nBufferSize)
5516
{
5517
    VSILFILE *fp = VSIFileFromMemBuffer(
16✔
5518
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5519
        false);
5520
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
16✔
5521
    VSIFCloseL(fp);
16✔
5522
    return osRet;
16✔
5523
}
5524

5525
//! @endcond
5526

5527
/************************************************************************/
5528
/*                      GDALGetNoDataReplacementValue()                 */
5529
/************************************************************************/
5530

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

5550
 * @since GDAL 3.9
5551
 */
5552
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
269✔
5553
{
5554

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

5646
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5647
                                 cpl::NumericLimits<uint64_t>::lowest(),
5648
                                 cpl::NumericLimits<uint64_t>::max()))
5649
        {
5650
            return 0;
2✔
5651
        }
5652

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

5668
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5669
                                 cpl::NumericLimits<int64_t>::lowest(),
5670
                                 cpl::NumericLimits<int64_t>::max()))
5671
        {
5672
            return 0;
2✔
5673
        }
5674

5675
        if (dfNoDataValue >=
3✔
5676
            static_cast<double>(cpl::NumericLimits<int64_t>::max()))
3✔
5677
            dfReplacementVal = dfMaxInt64Value;
1✔
5678
        else
5679
            dfReplacementVal = dfNoDataValue + 1;
2✔
5680
    }
5681
    else if (dt == GDT_Float16)
134✔
5682
    {
5683

5684
        if (GDALClampDoubleValue(dfNoDataValue,
8✔
5685
                                 cpl::NumericLimits<GFloat16>::lowest(),
5686
                                 cpl::NumericLimits<GFloat16>::max()))
5687
        {
5688
            return 0;
4✔
5689
        }
5690

5691
        if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max())
4✔
5692
        {
5693
            using std::nextafter;
5694
            dfReplacementVal =
5695
                nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f));
1✔
5696
        }
5697
        else
5698
        {
5699
            using std::nextafter;
5700
            dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue),
×
5701
                                         cpl::NumericLimits<GFloat16>::max());
3✔
5702
        }
5703
    }
5704
    else if (dt == GDT_Float32)
126✔
5705
    {
5706

5707
        if (GDALClampDoubleValue(dfNoDataValue,
33✔
5708
                                 cpl::NumericLimits<float>::lowest(),
5709
                                 cpl::NumericLimits<float>::max()))
5710
        {
5711
            return 0;
4✔
5712
        }
5713

5714
        if (dfNoDataValue == cpl::NumericLimits<float>::max())
29✔
5715
        {
5716
            dfReplacementVal =
1✔
5717
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
1✔
5718
        }
5719
        else
5720
        {
5721
            dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue),
28✔
5722
                                              cpl::NumericLimits<float>::max());
5723
        }
5724
    }
5725
    else if (dt == GDT_Float64)
93✔
5726
    {
5727
        if (GDALClampDoubleValue(dfNoDataValue,
93✔
5728
                                 cpl::NumericLimits<double>::lowest(),
5729
                                 cpl::NumericLimits<double>::max()))
5730
        {
5731
            return 0;
2✔
5732
        }
5733

5734
        if (dfNoDataValue == cpl::NumericLimits<double>::max())
91✔
5735
        {
5736
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0);
2✔
5737
        }
5738
        else
5739
        {
5740
            dfReplacementVal = std::nextafter(
89✔
5741
                dfNoDataValue, cpl::NumericLimits<double>::max());
5742
        }
5743
    }
5744

5745
    return dfReplacementVal;
243✔
5746
}
5747

5748
/************************************************************************/
5749
/*                        GDALGetCacheDirectory()                       */
5750
/************************************************************************/
5751

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

5773
    if (const char *pszXDG_CACHE_HOME =
315✔
5774
            CPLGetConfigOption("XDG_CACHE_HOME", nullptr))
315✔
5775
    {
5776
        return CPLFormFilenameSafe(pszXDG_CACHE_HOME, "gdal", nullptr);
×
5777
    }
5778

5779
#ifdef _WIN32
5780
    const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
5781
#else
5782
    const char *pszHome = CPLGetConfigOption("HOME", nullptr);
315✔
5783
#endif
5784
    if (pszHome != nullptr)
315✔
5785
    {
5786
        return CPLFormFilenameSafe(pszHome, ".gdal", nullptr);
315✔
5787
    }
5788
    else
5789
    {
5790
        const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
×
5791

5792
        if (pszDir == nullptr)
×
5793
            pszDir = CPLGetConfigOption("TMPDIR", nullptr);
×
5794

5795
        if (pszDir == nullptr)
×
5796
            pszDir = CPLGetConfigOption("TEMP", nullptr);
×
5797

5798
        const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
×
5799
        if (pszUsername == nullptr)
×
5800
            pszUsername = CPLGetConfigOption("USER", nullptr);
×
5801

5802
        if (pszDir != nullptr && pszUsername != nullptr)
×
5803
        {
5804
            return CPLFormFilenameSafe(
5805
                pszDir, CPLSPrintf(".gdal_%s", pszUsername), nullptr);
×
5806
        }
5807
    }
5808
    return std::string();
×
5809
}
5810

5811
/************************************************************************/
5812
/*                      GDALDoesFileOrDatasetExist()                    */
5813
/************************************************************************/
5814

5815
/** Return whether a file already exists.
5816
 */
5817
bool GDALDoesFileOrDatasetExist(const char *pszName, const char **ppszType,
687✔
5818
                                GDALDriver **ppDriver)
5819
{
5820
    {
5821
        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
687✔
5822
        GDALDriverH hDriver = GDALIdentifyDriver(pszName, nullptr);
687✔
5823
        if (hDriver)
687✔
5824
        {
5825
            if (ppszType)
58✔
5826
                *ppszType = "Dataset";
58✔
5827
            if (ppDriver)
58✔
5828
                *ppDriver = GDALDriver::FromHandle(hDriver);
57✔
5829
            return true;
58✔
5830
        }
5831
    }
5832

5833
    VSIStatBufL sStat;
5834
    if (VSIStatL(pszName, &sStat) == 0)
629✔
5835
    {
5836
        if (ppszType)
4✔
5837
            *ppszType = VSI_ISDIR(sStat.st_mode) ? "Directory" : "File";
4✔
5838
        return true;
4✔
5839
    }
5840

5841
    return false;
625✔
5842
}
5843

5844
/************************************************************************/
5845
/*                        GDALRescaleGeoTransform()                     */
5846
/************************************************************************/
5847

5848
/** Rescale a geotransform by multiplying its scale and rotation terms by
5849
 * the provided ratios.
5850
 *
5851
 * This is typically used to compute the geotransform matrix of an overview
5852
 * dataset from the full resolution dataset, where the ratios are the size
5853
 * of the full resolution dataset divided by the size of the overview.
5854
 */
5855
void GDALRescaleGeoTransform(double adfGeoTransform[6], double dfXRatio,
1,745✔
5856
                             double dfYRatio)
5857
{
5858
    adfGeoTransform[1] *= dfXRatio;
1,745✔
5859
    adfGeoTransform[2] *= dfYRatio;
1,745✔
5860
    adfGeoTransform[4] *= dfXRatio;
1,745✔
5861
    adfGeoTransform[5] *= dfYRatio;
1,745✔
5862
}
1,745✔
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