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

OSGeo / gdal / 13836648005

13 Mar 2025 02:09PM UTC coverage: 70.436% (-0.01%) from 70.446%
13836648005

push

github

web-flow
New Transform type: Homography (#11949)

Add new transform type, Homography.
Add functions to compute homography from a list of GCPs.
Add functions to serialize and deserialize a homography
Automatically select homography transfrom when there are 4 or 5 GCPs present.

Fixes #11940

231 of 274 new or added lines in 2 files covered. (84.31%)

16257 existing lines in 42 files now uncovered.

553736 of 786159 relevant lines covered (70.44%)

221595.72 hits per line

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

76.84
/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[],
3,461✔
58
                             const int panBits[])
59
{
60
    if (pabFloating[0] != pabFloating[1])
3,461✔
61
    {
62
        const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
467✔
63
        const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
467✔
64

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

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

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

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

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

89
        case GDT_UInt16:
1,250✔
90
        case GDT_Int16:
91
        case GDT_Float16:
92
        case GDT_CInt16:
93
        case GDT_CFloat16:
94
            return 16;
1,250✔
95

96
        case GDT_UInt32:
952✔
97
        case GDT_Int32:
98
        case GDT_Float32:
99
        case GDT_CInt32:
100
        case GDT_CFloat32:
101
            return 32;
952✔
102

103
        case GDT_Float64:
449✔
104
        case GDT_CFloat64:
105
        case GDT_UInt64:
106
        case GDT_Int64:
107
            return 64;
449✔
108

109
        case GDT_Unknown:
×
110
        case GDT_TypeCount:
111
            break;
×
112
    }
113
    return 0;
×
114
}
115

116
/************************************************************************/
117
/*                         GDALDataTypeUnion()                          */
118
/************************************************************************/
119

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

130
GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
3,462✔
131
                                           GDALDataType eType2)
132

133
{
134
    if (eType1 == GDT_Unknown)
3,462✔
135
        return eType2;
1✔
136
    if (eType2 == GDT_Unknown)
3,461✔
137
        return eType1;
×
138

139
    const int panBits[] = {GetDataTypeElementSizeBits(eType1),
3,461✔
140
                           GetDataTypeElementSizeBits(eType2)};
3,461✔
141

142
    if (panBits[0] == 0 || panBits[1] == 0)
3,461✔
143
        return GDT_Unknown;
×
144

145
    const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
3,461✔
146
                              CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
3,461✔
147

148
    const bool bSigned = pabSigned[0] || pabSigned[1];
3,461✔
149
    const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
3,461✔
150
                                CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
3,461✔
151
    const bool bFloating = pabFloating[0] || pabFloating[1];
3,461✔
152
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
6,490✔
153
                          CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
3,029✔
154

155
    const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
3,461✔
156

157
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
3,461✔
158
}
159

160
/************************************************************************/
161
/*                        GDALDataTypeUnionWithValue()                  */
162
/************************************************************************/
163

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

253
    const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex);
27✔
254
    return GDALDataTypeUnion(eDT, eDT2);
27✔
255
}
256

257
/************************************************************************/
258
/*                        GetMinBitsForValue()                          */
259
/************************************************************************/
260
static int GetMinBitsForValue(double dValue)
27✔
261
{
262
    if (round(dValue) == dValue)
27✔
263
    {
264
        if (dValue <= cpl::NumericLimits<GByte>::max() &&
26✔
265
            dValue >= cpl::NumericLimits<GByte>::lowest())
9✔
266
            return 8;
4✔
267

268
        if (dValue <= cpl::NumericLimits<GInt8>::max() &&
18✔
269
            dValue >= cpl::NumericLimits<GInt8>::lowest())
5✔
270
            return 8;
3✔
271

272
        if (dValue <= cpl::NumericLimits<GInt16>::max() &&
14✔
273
            dValue >= cpl::NumericLimits<GInt16>::lowest())
4✔
274
            return 16;
3✔
275

276
        if (dValue <= cpl::NumericLimits<GUInt16>::max() &&
9✔
277
            dValue >= cpl::NumericLimits<GUInt16>::lowest())
2✔
278
            return 16;
1✔
279

280
        if (dValue <= cpl::NumericLimits<GInt32>::max() &&
8✔
281
            dValue >= cpl::NumericLimits<GInt32>::lowest())
2✔
282
            return 32;
2✔
283

284
        if (dValue <= cpl::NumericLimits<GUInt32>::max() &&
5✔
285
            dValue >= cpl::NumericLimits<GUInt32>::lowest())
1✔
286
            return 32;
1✔
287

288
        if (dValue <=
3✔
289
                static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) &&
5✔
290
            dValue >= static_cast<double>(
2✔
291
                          cpl::NumericLimits<std::uint64_t>::lowest()))
2✔
292
            return 64;
2✔
293
    }
294
    else if (static_cast<float>(dValue) == dValue)
10✔
295
    {
296
        return 32;
4✔
297
    }
298

299
    return 64;
7✔
300
}
301

302
/************************************************************************/
303
/*                        GDALFindDataType()                            */
304
/************************************************************************/
305

306
/**
307
 * \brief Finds the smallest data type able to support the given
308
 *  requirements
309
 *
310
 * @param nBits number of bits necessary
311
 * @param bSigned if negative values are necessary
312
 * @param bFloating if non-integer values necessary
313
 * @param bComplex if complex values are necessary
314
 *
315
 * @return a best fit GDALDataType for supporting the requirements
316
 * @since GDAL 2.3
317
 */
318
GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
3,510✔
319
                                          int bComplex)
320
{
321
    if (!bFloating)
3,510✔
322
    {
323
        if (!bComplex)
2,821✔
324
        {
325
            if (!bSigned)
2,345✔
326
            {
327
                if (nBits <= 8)
1,998✔
328
                    return GDT_Byte;
1,789✔
329
                if (nBits <= 16)
209✔
330
                    return GDT_UInt16;
121✔
331
                if (nBits <= 32)
88✔
332
                    return GDT_UInt32;
66✔
333
                if (nBits <= 64)
22✔
334
                    return GDT_UInt64;
22✔
335
                return GDT_Float64;
×
336
            }
337
            else  // bSigned
338
            {
339
                if (nBits <= 8)
347✔
340
                    return GDT_Int8;
7✔
341
                if (nBits <= 16)
340✔
342
                    return GDT_Int16;
159✔
343
                if (nBits <= 32)
181✔
344
                    return GDT_Int32;
117✔
345
                if (nBits <= 64)
64✔
346
                    return GDT_Int64;
46✔
347
                return GDT_Float64;
18✔
348
            }
349
        }
350
        else  // bComplex
351
        {
352
            if (!bSigned)
476✔
353
            {
354
                // We don't have complex unsigned data types, so
355
                // return a large-enough complex signed type
356

357
                // Do not choose CInt16 for backward compatibility
358
                // if (nBits <= 15)
359
                //     return GDT_CInt16;
360
                if (nBits <= 31)
3✔
361
                    return GDT_CInt32;
3✔
362
                return GDT_CFloat64;
×
363
            }
364
            else  // bSigned
365
            {
366
                if (nBits <= 16)
473✔
367
                    return GDT_CInt16;
357✔
368
                if (nBits <= 32)
116✔
369
                    return GDT_CInt32;
89✔
370
                return GDT_CFloat64;
27✔
371
            }
372
        }
373
    }
374
    else  // bFloating
375
    {
376
        if (!bComplex)
689✔
377
        {
378
            // Do not choose Float16 since is not supported everywhere
379
            // if (nBits <= 16)
380
            //     return GDT_Float16;
381
            if (nBits <= 32)
356✔
382
                return GDT_Float32;
193✔
383
            return GDT_Float64;
163✔
384
        }
385
        else  // bComplex
386
        {
387
            // Do not choose Float16 since is not supported everywhere
388
            // if (nBits <= 16)
389
            //     return GDT_CFloat16;
390
            if (nBits <= 32)
333✔
391
                return GDT_CFloat32;
144✔
392
            return GDT_CFloat64;
189✔
393
        }
394
    }
395
}
396

397
/************************************************************************/
398
/*                        GDALFindDataTypeForValue()                    */
399
/************************************************************************/
400

401
/**
402
 * \brief Finds the smallest data type able to support the provided value
403
 *
404
 * @param dValue value to support
405
 * @param bComplex is the value complex
406
 *
407
 * @return a best fit GDALDataType for supporting the value
408
 * @since GDAL 2.3
409
 */
410
GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
27✔
411
{
412
    const bool bFloating =
413
        round(dValue) != dValue ||
44✔
414
        dValue >
415
            static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) ||
43✔
416
        dValue <
417
            static_cast<double>(cpl::NumericLimits<std::int64_t>::lowest());
16✔
418
    const bool bSigned = bFloating || dValue < 0;
27✔
419
    const int nBits = GetMinBitsForValue(dValue);
27✔
420

421
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
27✔
422
}
423

424
/************************************************************************/
425
/*                        GDALGetDataTypeSizeBytes()                    */
426
/************************************************************************/
427

428
/**
429
 * \brief Get data type size in <b>bytes</b>.
430
 *
431
 * Returns the size of a GDT_* type in bytes.  In contrast,
432
 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
433
 *
434
 * @param eDataType type, such as GDT_Byte.
435
 * @return the number of bytes or zero if it is not recognised.
436
 */
437

438
int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
243,334,000✔
439

440
{
441
    switch (eDataType)
243,334,000✔
442
    {
443
        case GDT_Byte:
76,852,400✔
444
        case GDT_Int8:
445
            return 1;
76,852,400✔
446

447
        case GDT_UInt16:
51,860,800✔
448
        case GDT_Int16:
449
        case GDT_Float16:
450
            return 2;
51,860,800✔
451

452
        case GDT_UInt32:
77,438,400✔
453
        case GDT_Int32:
454
        case GDT_Float32:
455
        case GDT_CInt16:
456
        case GDT_CFloat16:
457
            return 4;
77,438,400✔
458

459
        case GDT_Float64:
36,857,300✔
460
        case GDT_CInt32:
461
        case GDT_CFloat32:
462
        case GDT_UInt64:
463
        case GDT_Int64:
464
            return 8;
36,857,300✔
465

466
        case GDT_CFloat64:
381,585✔
467
            return 16;
381,585✔
468

469
        case GDT_Unknown:
16,182✔
470
        case GDT_TypeCount:
471
            break;
16,182✔
472
    }
UNCOV
473
    return 0;
×
474
}
475

476
/************************************************************************/
477
/*                        GDALGetDataTypeSizeBits()                     */
478
/************************************************************************/
479

480
/**
481
 * \brief Get data type size in <b>bits</b>.
482
 *
483
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!  Use
484
 * GDALGetDataTypeSizeBytes() for bytes.
485
 *
486
 * @param eDataType type, such as GDT_Byte.
487
 * @return the number of bits or zero if it is not recognised.
488
 */
489

490
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
10,772✔
491

492
{
493
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
10,772✔
494
}
495

496
/************************************************************************/
497
/*                        GDALGetDataTypeSize()                         */
498
/************************************************************************/
499

500
/**
501
 * \brief Get data type size in bits.  <b>Deprecated</b>.
502
 *
503
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
504
 *
505
 * Use GDALGetDataTypeSizeBytes() for bytes.
506
 * Use GDALGetDataTypeSizeBits() for bits.
507
 *
508
 * @param eDataType type, such as GDT_Byte.
509
 * @return the number of bits or zero if it is not recognised.
510
 */
511

512
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
3,222,960✔
513

514
{
515
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
3,222,960✔
516
}
517

518
/************************************************************************/
519
/*                       GDALDataTypeIsComplex()                        */
520
/************************************************************************/
521

522
/**
523
 * \brief Is data type complex?
524
 *
525
 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
526
 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
527
 * component.
528
 */
529

530
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
695,883✔
531

532
{
533
    switch (eDataType)
695,883✔
534
    {
535
        case GDT_CInt16:
7,770✔
536
        case GDT_CInt32:
537
        case GDT_CFloat16:
538
        case GDT_CFloat32:
539
        case GDT_CFloat64:
540
            return TRUE;
7,770✔
541

542
        case GDT_Byte:
688,112✔
543
        case GDT_Int8:
544
        case GDT_Int16:
545
        case GDT_UInt16:
546
        case GDT_Int32:
547
        case GDT_UInt32:
548
        case GDT_Int64:
549
        case GDT_UInt64:
550
        case GDT_Float16:
551
        case GDT_Float32:
552
        case GDT_Float64:
553
            return FALSE;
688,112✔
554

555
        case GDT_Unknown:
7✔
556
        case GDT_TypeCount:
557
            break;
7✔
558
    }
559
    return FALSE;
1✔
560
}
561

562
/************************************************************************/
563
/*                       GDALDataTypeIsFloating()                       */
564
/************************************************************************/
565

566
/**
567
 * \brief Is data type floating? (might be complex)
568
 *
569
 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float16,
570
 * GDT_Float64, GDT_CFloat16, GDT_CFloat32, GDT_CFloat64)
571
 * @since GDAL 2.3
572
 */
573

574
int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
58,464✔
575
{
576
    switch (eDataType)
58,464✔
577
    {
578
        case GDT_Float16:
5,945✔
579
        case GDT_Float32:
580
        case GDT_Float64:
581
        case GDT_CFloat16:
582
        case GDT_CFloat32:
583
        case GDT_CFloat64:
584
            return TRUE;
5,945✔
585

586
        case GDT_Byte:
52,518✔
587
        case GDT_Int8:
588
        case GDT_Int16:
589
        case GDT_UInt16:
590
        case GDT_Int32:
591
        case GDT_UInt32:
592
        case GDT_Int64:
593
        case GDT_UInt64:
594
        case GDT_CInt16:
595
        case GDT_CInt32:
596
            return FALSE;
52,518✔
597

598
        case GDT_Unknown:
1✔
599
        case GDT_TypeCount:
600
            break;
1✔
601
    }
602
    return FALSE;
1✔
603
}
604

605
/************************************************************************/
606
/*                       GDALDataTypeIsInteger()                        */
607
/************************************************************************/
608

609
/**
610
 * \brief Is data type integer? (might be complex)
611
 *
612
 * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
613
 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
614
 * @since GDAL 2.3
615
 */
616

617
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
56,752✔
618

619
{
620
    switch (eDataType)
56,752✔
621
    {
622
        case GDT_Byte:
55,021✔
623
        case GDT_Int8:
624
        case GDT_Int16:
625
        case GDT_UInt16:
626
        case GDT_Int32:
627
        case GDT_UInt32:
628
        case GDT_CInt16:
629
        case GDT_CInt32:
630
        case GDT_UInt64:
631
        case GDT_Int64:
632
            return TRUE;
55,021✔
633

634
        case GDT_Float16:
1,730✔
635
        case GDT_Float32:
636
        case GDT_Float64:
637
        case GDT_CFloat16:
638
        case GDT_CFloat32:
639
        case GDT_CFloat64:
640
            return FALSE;
1,730✔
641

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

649
/************************************************************************/
650
/*                       GDALDataTypeIsSigned()                         */
651
/************************************************************************/
652

653
/**
654
 * \brief Is data type signed?
655
 *
656
 * @return TRUE if the passed type is signed.
657
 * @since GDAL 2.3
658
 */
659

660
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
99,469✔
661
{
662
    switch (eDataType)
99,469✔
663
    {
664
        case GDT_Byte:
96,097✔
665
        case GDT_UInt16:
666
        case GDT_UInt32:
667
        case GDT_UInt64:
668
            return FALSE;
96,097✔
669

670
        case GDT_Int8:
3,372✔
671
        case GDT_Int16:
672
        case GDT_Int32:
673
        case GDT_Int64:
674
        case GDT_Float16:
675
        case GDT_Float32:
676
        case GDT_Float64:
677
        case GDT_CInt16:
678
        case GDT_CInt32:
679
        case GDT_CFloat16:
680
        case GDT_CFloat32:
681
        case GDT_CFloat64:
682
            return TRUE;
3,372✔
683

684
        case GDT_Unknown:
×
685
        case GDT_TypeCount:
686
            break;
×
687
    }
688
    return FALSE;
×
689
}
690

691
/************************************************************************/
692
/*                    GDALDataTypeIsConversionLossy()                   */
693
/************************************************************************/
694

695
/**
696
 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
697
 *
698
 * @param eTypeFrom input datatype
699
 * @param eTypeTo output datatype
700
 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
701
 * @since GDAL 2.3
702
 */
703

704
int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
51,927✔
705
                                              GDALDataType eTypeTo)
706
{
707
    // E.g cfloat32 -> float32
708
    if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
51,927✔
709
        return TRUE;
36✔
710

711
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
51,891✔
712
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
51,891✔
713

714
    if (GDALDataTypeIsInteger(eTypeTo))
51,891✔
715
    {
716
        // E.g. float32 -> int32
717
        if (GDALDataTypeIsFloating(eTypeFrom))
50,632✔
718
            return TRUE;
4,695✔
719

720
        // E.g. Int16 to UInt16
721
        const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
45,937✔
722
        const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
45,937✔
723
        if (bIsFromSigned && !bIsToSigned)
45,937✔
724
            return TRUE;
30✔
725

726
        // E.g UInt32 to UInt16
727
        const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
45,907✔
728
        const int nToSize = GDALGetDataTypeSize(eTypeTo);
45,907✔
729
        if (nFromSize > nToSize)
45,907✔
730
            return TRUE;
28✔
731

732
        // E.g UInt16 to Int16
733
        if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
45,879✔
734
            return TRUE;
9✔
735

736
        return FALSE;
45,870✔
737
    }
738

739
    if (eTypeTo == GDT_Float16 &&
1,259✔
740
        (eTypeFrom == GDT_Int16 || eTypeFrom == GDT_UInt16 ||
1✔
741
         eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
1✔
742
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
1✔
743
         eTypeFrom == GDT_Float32 || eTypeFrom == GDT_Float64))
1✔
744
    {
745
        return TRUE;
×
746
    }
747

748
    if (eTypeTo == GDT_Float32 &&
1,259✔
749
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
367✔
750
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
363✔
751
         eTypeFrom == GDT_Float64))
752
    {
753
        return TRUE;
37✔
754
    }
755

756
    if (eTypeTo == GDT_Float64 &&
1,222✔
757
        (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
866✔
758
    {
759
        return TRUE;
4✔
760
    }
761

762
    return FALSE;
1,218✔
763
}
764

765
/************************************************************************/
766
/*                        GDALGetDataTypeName()                         */
767
/************************************************************************/
768

769
/**
770
 * \brief Get name of data type.
771
 *
772
 * Returns a symbolic name for the data type.  This is essentially the
773
 * the enumerated item name with the GDT_ prefix removed.  So GDT_Byte returns
774
 * "Byte".  The returned strings are static strings and should not be modified
775
 * or freed by the application.  These strings are useful for reporting
776
 * datatypes in debug statements, errors and other user output.
777
 *
778
 * @param eDataType type to get name of.
779
 * @return string corresponding to existing data type
780
 *         or NULL pointer if invalid type given.
781
 */
782

783
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
62,957✔
784

785
{
786
    switch (eDataType)
62,957✔
787
    {
788
        case GDT_Unknown:
5,020✔
789
            return "Unknown";
5,020✔
790

791
        case GDT_Byte:
27,875✔
792
            return "Byte";
27,875✔
793

794
        case GDT_Int8:
553✔
795
            return "Int8";
553✔
796

797
        case GDT_UInt16:
5,039✔
798
            return "UInt16";
5,039✔
799

800
        case GDT_Int16:
5,196✔
801
            return "Int16";
5,196✔
802

803
        case GDT_UInt32:
3,936✔
804
            return "UInt32";
3,936✔
805

806
        case GDT_Int32:
3,910✔
807
            return "Int32";
3,910✔
808

809
        case GDT_UInt64:
590✔
810
            return "UInt64";
590✔
811

812
        case GDT_Int64:
570✔
813
            return "Int64";
570✔
814

815
        case GDT_Float16:
155✔
816
            return "Float16";
155✔
817

818
        case GDT_Float32:
3,921✔
819
            return "Float32";
3,921✔
820

821
        case GDT_Float64:
2,103✔
822
            return "Float64";
2,103✔
823

824
        case GDT_CInt16:
1,071✔
825
            return "CInt16";
1,071✔
826

827
        case GDT_CInt32:
1,005✔
828
            return "CInt32";
1,005✔
829

830
        case GDT_CFloat16:
129✔
831
            return "CFloat16";
129✔
832

833
        case GDT_CFloat32:
1,018✔
834
            return "CFloat32";
1,018✔
835

836
        case GDT_CFloat64:
867✔
837
            return "CFloat64";
867✔
838

839
        case GDT_TypeCount:
×
840
            break;
×
841
    }
842
    return nullptr;
×
843
}
844

845
/************************************************************************/
846
/*                        GDALGetDataTypeByName()                       */
847
/************************************************************************/
848

849
/**
850
 * \brief Get data type by symbolic name.
851
 *
852
 * Returns a data type corresponding to the given symbolic name. This
853
 * function is opposite to the GDALGetDataTypeName().
854
 *
855
 * @param pszName string containing the symbolic name of the type.
856
 *
857
 * @return GDAL data type.
858
 */
859

860
GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
4,855✔
861

862
{
863
    VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
4,855✔
864

865
    for (int iType = 1; iType < GDT_TypeCount; iType++)
14,868✔
866
    {
867
        const auto eType = static_cast<GDALDataType>(iType);
14,836✔
868
        if (GDALGetDataTypeName(eType) != nullptr &&
29,672✔
869
            EQUAL(GDALGetDataTypeName(eType), pszName))
14,836✔
870
        {
871
            return eType;
4,823✔
872
        }
873
    }
874

875
    return GDT_Unknown;
32✔
876
}
877

878
/************************************************************************/
879
/*                      GDALAdjustValueToDataType()                     */
880
/************************************************************************/
881

882
template <class T>
883
static inline void ClampAndRound(double &dfValue, bool &bClamped,
160✔
884
                                 bool &bRounded)
885
{
886
    if (dfValue < static_cast<double>(cpl::NumericLimits<T>::lowest()))
160✔
887
    {
888
        bClamped = true;
6✔
889
        dfValue = static_cast<double>(cpl::NumericLimits<T>::lowest());
6✔
890
    }
891
    else if (dfValue > static_cast<double>(cpl::NumericLimits<T>::max()))
154✔
892
    {
893
        bClamped = true;
16✔
894
        dfValue = static_cast<double>(cpl::NumericLimits<T>::max());
16✔
895
    }
896
    else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
138✔
897
    {
898
        bRounded = true;
8✔
899
        dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
8✔
900
    }
901
}
160✔
902

903
/**
904
 * \brief Adjust a value to the output data type
905
 *
906
 * Adjustment consist in clamping to minimum/maximum values of the data type
907
 * and rounding for integral types.
908
 *
909
 * @param eDT target data type.
910
 * @param dfValue value to adjust.
911
 * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
912
 * been made, or NULL
913
 * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
914
 * been made, or NULL
915
 *
916
 * @return adjusted value
917
 * @since GDAL 2.1
918
 */
919

920
double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
240✔
921
                                 int *pbClamped, int *pbRounded)
922
{
923
    bool bClamped = false;
240✔
924
    bool bRounded = false;
240✔
925
    switch (eDT)
240✔
926
    {
927
        case GDT_Byte:
89✔
928
            ClampAndRound<GByte>(dfValue, bClamped, bRounded);
89✔
929
            break;
89✔
930
        case GDT_Int8:
8✔
931
            ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
8✔
932
            break;
8✔
933
        case GDT_Int16:
23✔
934
            ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
23✔
935
            break;
23✔
936
        case GDT_UInt16:
18✔
937
            ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
18✔
938
            break;
18✔
939
        case GDT_Int32:
6✔
940
            ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
6✔
941
            break;
6✔
942
        case GDT_UInt32:
7✔
943
            ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
7✔
944
            break;
7✔
945
        case GDT_Int64:
4✔
946
            ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
4✔
947
            break;
4✔
948
        case GDT_UInt64:
5✔
949
            ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
5✔
950
            break;
5✔
951
        case GDT_Float16:
8✔
952
        {
953
            if (!std::isfinite(dfValue))
8✔
954
                break;
3✔
955

956
            // TODO: Use ClampAndRound
957
            if (dfValue < cpl::NumericLimits<GFloat16>::lowest())
5✔
958
            {
959
                bClamped = TRUE;
1✔
960
                dfValue =
1✔
961
                    static_cast<double>(cpl::NumericLimits<GFloat16>::lowest());
1✔
962
            }
963
            else if (dfValue > cpl::NumericLimits<GFloat16>::max())
4✔
964
            {
965
                bClamped = TRUE;
1✔
966
                dfValue =
1✔
967
                    static_cast<double>(cpl::NumericLimits<GFloat16>::max());
1✔
968
            }
969
            else
970
            {
971
                // Intentionally lose precision.
972
                // TODO(schwehr): Is the double cast really necessary?
973
                // If so, why?  What will fail?
974
                dfValue = static_cast<double>(static_cast<GFloat16>(dfValue));
3✔
975
            }
976
            break;
5✔
977
        }
978
        case GDT_Float32:
45✔
979
        {
980
            if (!std::isfinite(dfValue))
45✔
981
                break;
6✔
982

983
            // TODO: Use ClampAndRound
984
            if (dfValue < cpl::NumericLimits<float>::lowest())
39✔
985
            {
986
                bClamped = TRUE;
1✔
987
                dfValue =
1✔
988
                    static_cast<double>(cpl::NumericLimits<float>::lowest());
1✔
989
            }
990
            else if (dfValue > cpl::NumericLimits<float>::max())
38✔
991
            {
992
                bClamped = TRUE;
1✔
993
                dfValue = static_cast<double>(cpl::NumericLimits<float>::max());
1✔
994
            }
995
            else
996
            {
997
                // Intentionally lose precision.
998
                // TODO(schwehr): Is the double cast really necessary?
999
                // If so, why?  What will fail?
1000
                dfValue = static_cast<double>(static_cast<float>(dfValue));
37✔
1001
            }
1002
            break;
39✔
1003
        }
1004
        case GDT_Float64:
27✔
1005
        case GDT_CInt16:
1006
        case GDT_CInt32:
1007
        case GDT_CFloat16:
1008
        case GDT_CFloat32:
1009
        case GDT_CFloat64:
1010
        case GDT_Unknown:
1011
        case GDT_TypeCount:
1012
            break;
27✔
1013
    }
1014
    if (pbClamped)
240✔
1015
        *pbClamped = bClamped;
239✔
1016
    if (pbRounded)
240✔
1017
        *pbRounded = bRounded;
239✔
1018
    return dfValue;
240✔
1019
}
1020

1021
/************************************************************************/
1022
/*                         GDALIsValueExactAs()                         */
1023
/************************************************************************/
1024

1025
/**
1026
 * \brief Check whether the provided value can be exactly represented in a
1027
 * data type.
1028
 *
1029
 * Only implemented for non-complex data types
1030
 *
1031
 * @param dfValue value to check.
1032
 * @param eDT target data type.
1033
 *
1034
 * @return true if the provided value can be exactly represented in the
1035
 * data type.
1036
 * @since GDAL 3.10
1037
 */
1038
bool GDALIsValueExactAs(double dfValue, GDALDataType eDT)
235✔
1039
{
1040
    switch (eDT)
235✔
1041
    {
1042
        case GDT_Byte:
128✔
1043
            return GDALIsValueExactAs<uint8_t>(dfValue);
128✔
1044
        case GDT_Int8:
5✔
1045
            return GDALIsValueExactAs<int8_t>(dfValue);
5✔
1046
        case GDT_UInt16:
18✔
1047
            return GDALIsValueExactAs<uint16_t>(dfValue);
18✔
1048
        case GDT_Int16:
24✔
1049
            return GDALIsValueExactAs<int16_t>(dfValue);
24✔
1050
        case GDT_UInt32:
5✔
1051
            return GDALIsValueExactAs<uint32_t>(dfValue);
5✔
1052
        case GDT_Int32:
14✔
1053
            return GDALIsValueExactAs<int32_t>(dfValue);
14✔
1054
        case GDT_UInt64:
5✔
1055
            return GDALIsValueExactAs<uint64_t>(dfValue);
5✔
1056
        case GDT_Int64:
5✔
1057
            return GDALIsValueExactAs<int64_t>(dfValue);
5✔
1058
        case GDT_Float16:
×
1059
            return GDALIsValueExactAs<GFloat16>(dfValue);
×
1060
        case GDT_Float32:
22✔
1061
            return GDALIsValueExactAs<float>(dfValue);
22✔
1062
        case GDT_Float64:
8✔
1063
            return true;
8✔
1064
        case GDT_Unknown:
1✔
1065
        case GDT_CInt16:
1066
        case GDT_CInt32:
1067
        case GDT_CFloat16:
1068
        case GDT_CFloat32:
1069
        case GDT_CFloat64:
1070
        case GDT_TypeCount:
1071
            break;
1✔
1072
    }
1073
    return true;
1✔
1074
}
1075

1076
/************************************************************************/
1077
/*                        GDALGetNonComplexDataType()                */
1078
/************************************************************************/
1079
/**
1080
 * \brief Return the base data type for the specified input.
1081
 *
1082
 * If the input data type is complex this function returns the base type
1083
 * i.e. the data type of the real and imaginary parts (non-complex).
1084
 * If the input data type is already non-complex, then it is returned
1085
 * unchanged.
1086
 *
1087
 * @param eDataType type, such as GDT_CFloat32.
1088
 *
1089
 * @return GDAL data type.
1090
 */
1091
GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
103,853✔
1092
{
1093
    switch (eDataType)
103,853✔
1094
    {
1095
        case GDT_CInt16:
91✔
1096
            return GDT_Int16;
91✔
1097
        case GDT_CInt32:
23✔
1098
            return GDT_Int32;
23✔
1099
        case GDT_CFloat16:
×
1100
            return GDT_Float16;
×
1101
        case GDT_CFloat32:
65✔
1102
            return GDT_Float32;
65✔
1103
        case GDT_CFloat64:
60✔
1104
            return GDT_Float64;
60✔
1105

1106
        case GDT_Byte:
103,614✔
1107
        case GDT_UInt16:
1108
        case GDT_UInt32:
1109
        case GDT_UInt64:
1110
        case GDT_Int8:
1111
        case GDT_Int16:
1112
        case GDT_Int32:
1113
        case GDT_Int64:
1114
        case GDT_Float16:
1115
        case GDT_Float32:
1116
        case GDT_Float64:
1117
            break;
103,614✔
1118

1119
        case GDT_Unknown:
×
1120
        case GDT_TypeCount:
1121
            break;
×
1122
    }
1123
    return eDataType;
103,614✔
1124
}
1125

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

1144
    for (int iType = 0; iType < GARIO_TypeCount; iType++)
×
1145
    {
1146
        const auto eType = static_cast<GDALAsyncStatusType>(iType);
×
1147
        if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
×
1148
            EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
×
1149
        {
1150
            return eType;
×
1151
        }
1152
    }
1153

1154
    return GARIO_ERROR;
×
1155
}
1156

1157
/************************************************************************/
1158
/*                        GDALGetAsyncStatusTypeName()                 */
1159
/************************************************************************/
1160

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

1175
const char *CPL_STDCALL
1176
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
×
1177

1178
{
1179
    switch (eAsyncStatusType)
×
1180
    {
1181
        case GARIO_PENDING:
×
1182
            return "PENDING";
×
1183

1184
        case GARIO_UPDATE:
×
1185
            return "UPDATE";
×
1186

1187
        case GARIO_ERROR:
×
1188
            return "ERROR";
×
1189

1190
        case GARIO_COMPLETE:
×
1191
            return "COMPLETE";
×
1192

1193
        default:
×
1194
            return nullptr;
×
1195
    }
1196
}
1197

1198
/************************************************************************/
1199
/*                  GDALGetPaletteInterpretationName()                  */
1200
/************************************************************************/
1201

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

1214
const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
9✔
1215

1216
{
1217
    switch (eInterp)
9✔
1218
    {
1219
        case GPI_Gray:
×
1220
            return "Gray";
×
1221

1222
        case GPI_RGB:
9✔
1223
            return "RGB";
9✔
1224

1225
        case GPI_CMYK:
×
1226
            return "CMYK";
×
1227

1228
        case GPI_HLS:
×
1229
            return "HLS";
×
1230

1231
        default:
×
1232
            return "Unknown";
×
1233
    }
1234
}
1235

1236
/************************************************************************/
1237
/*                   GDALGetColorInterpretationName()                   */
1238
/************************************************************************/
1239

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

1254
const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
9,763✔
1255

1256
{
1257
    static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1);
1258
    static_assert(GCI_NIRBand == GCI_IR_Start);
1259
    static_assert(GCI_SAR_Start == GCI_IR_End + 1);
1260
    static_assert(GCI_Max == GCI_SAR_End);
1261

1262
    switch (eInterp)
9,763✔
1263
    {
1264
        case GCI_Undefined:
2,044✔
1265
            break;
2,044✔
1266

1267
        case GCI_GrayIndex:
2,439✔
1268
            return "Gray";
2,439✔
1269

1270
        case GCI_PaletteIndex:
1,059✔
1271
            return "Palette";
1,059✔
1272

1273
        case GCI_RedBand:
1,174✔
1274
            return "Red";
1,174✔
1275

1276
        case GCI_GreenBand:
885✔
1277
            return "Green";
885✔
1278

1279
        case GCI_BlueBand:
613✔
1280
            return "Blue";
613✔
1281

1282
        case GCI_AlphaBand:
331✔
1283
            return "Alpha";
331✔
1284

1285
        case GCI_HueBand:
112✔
1286
            return "Hue";
112✔
1287

1288
        case GCI_SaturationBand:
111✔
1289
            return "Saturation";
111✔
1290

1291
        case GCI_LightnessBand:
110✔
1292
            return "Lightness";
110✔
1293

1294
        case GCI_CyanBand:
117✔
1295
            return "Cyan";
117✔
1296

1297
        case GCI_MagentaBand:
99✔
1298
            return "Magenta";
99✔
1299

1300
        case GCI_YellowBand:
81✔
1301
            return "Yellow";
81✔
1302

1303
        case GCI_BlackBand:
62✔
1304
            return "Black";
62✔
1305

1306
        case GCI_YCbCr_YBand:
38✔
1307
            return "YCbCr_Y";
38✔
1308

1309
        case GCI_YCbCr_CbBand:
35✔
1310
            return "YCbCr_Cb";
35✔
1311

1312
        case GCI_YCbCr_CrBand:
32✔
1313
            return "YCbCr_Cr";
32✔
1314

1315
        case GCI_PanBand:
30✔
1316
            return "Pan";
30✔
1317

1318
        case GCI_CoastalBand:
39✔
1319
            return "Coastal";
39✔
1320

1321
        case GCI_RedEdgeBand:
29✔
1322
            return "RedEdge";
29✔
1323

1324
        case GCI_NIRBand:
34✔
1325
            return "NIR";
34✔
1326

1327
        case GCI_SWIRBand:
26✔
1328
            return "SWIR";
26✔
1329

1330
        case GCI_MWIRBand:
23✔
1331
            return "MWIR";
23✔
1332

1333
        case GCI_LWIRBand:
22✔
1334
            return "LWIR";
22✔
1335

1336
        case GCI_TIRBand:
21✔
1337
            return "TIR";
21✔
1338

1339
        case GCI_OtherIRBand:
22✔
1340
            return "OtherIR";
22✔
1341

1342
        case GCI_IR_Reserved_1:
19✔
1343
            return "IR_Reserved_1";
19✔
1344

1345
        case GCI_IR_Reserved_2:
18✔
1346
            return "IR_Reserved_2";
18✔
1347

1348
        case GCI_IR_Reserved_3:
17✔
1349
            return "IR_Reserved_3";
17✔
1350

1351
        case GCI_IR_Reserved_4:
16✔
1352
            return "IR_Reserved_4";
16✔
1353

1354
        case GCI_SAR_Ka_Band:
15✔
1355
            return "SAR_Ka";
15✔
1356

1357
        case GCI_SAR_K_Band:
14✔
1358
            return "SAR_K";
14✔
1359

1360
        case GCI_SAR_Ku_Band:
13✔
1361
            return "SAR_Ku";
13✔
1362

1363
        case GCI_SAR_X_Band:
12✔
1364
            return "SAR_X";
12✔
1365

1366
        case GCI_SAR_C_Band:
11✔
1367
            return "SAR_C";
11✔
1368

1369
        case GCI_SAR_S_Band:
10✔
1370
            return "SAR_S";
10✔
1371

1372
        case GCI_SAR_L_Band:
9✔
1373
            return "SAR_L";
9✔
1374

1375
        case GCI_SAR_P_Band:
8✔
1376
            return "SAR_P";
8✔
1377

1378
        case GCI_SAR_Reserved_1:
7✔
1379
            return "SAR_Reserved_1";
7✔
1380

1381
        case GCI_SAR_Reserved_2:
6✔
1382
            return "SAR_Reserved_2";
6✔
1383
    }
1384
    return "Undefined";
2,044✔
1385
}
1386

1387
/************************************************************************/
1388
/*                GDALGetColorInterpretationByName()                    */
1389
/************************************************************************/
1390

1391
/**
1392
 * \brief Get color interpretation by symbolic name.
1393
 *
1394
 * Returns a color interpretation corresponding to the given symbolic name. This
1395
 * function is opposite to the GDALGetColorInterpretationName().
1396
 *
1397
 * @param pszName string containing the symbolic name of the color
1398
 * interpretation.
1399
 *
1400
 * @return GDAL color interpretation.
1401
 *
1402
 * @since GDAL 1.7.0
1403
 */
1404

1405
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1,884✔
1406

1407
{
1408
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1,884✔
1409
                      GCI_Undefined);
1410

1411
    for (int iType = 0; iType <= GCI_Max; iType++)
8,411✔
1412
    {
1413
        if (EQUAL(GDALGetColorInterpretationName(
8,407✔
1414
                      static_cast<GDALColorInterp>(iType)),
1415
                  pszName))
1416
        {
1417
            return static_cast<GDALColorInterp>(iType);
1,880✔
1418
        }
1419
    }
1420

1421
    // Accept British English spelling
1422
    if (EQUAL(pszName, "grey"))
4✔
1423
        return GCI_GrayIndex;
×
1424

1425
    return GCI_Undefined;
4✔
1426
}
1427

1428
/************************************************************************/
1429
/*                  GDALGetColorInterpFromSTACCommonName()              */
1430
/************************************************************************/
1431

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

1461
/** Get color interpreetation from STAC eo:common_name
1462
 *
1463
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1464
 *
1465
 * @since GDAL 3.10
1466
 */
1467
GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName)
22✔
1468
{
1469

1470
    for (const auto &sAssoc : asSTACCommonNames)
86✔
1471
    {
1472
        if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName))
86✔
1473
            return sAssoc.eInterp;
22✔
1474
    }
1475
    return GCI_Undefined;
×
1476
}
1477

1478
/************************************************************************/
1479
/*                  GDALGetSTACCommonNameFromColorInterp()              */
1480
/************************************************************************/
1481

1482
/** Get STAC eo:common_name from GDAL color interpretation
1483
 *
1484
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1485
 *
1486
 * @return nullptr if there is no match
1487
 *
1488
 * @since GDAL 3.10
1489
 */
1490
const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp)
95✔
1491
{
1492
    for (const auto &sAssoc : asSTACCommonNames)
1,705✔
1493
    {
1494
        if (eInterp == sAssoc.eInterp)
1,632✔
1495
            return sAssoc.pszName;
22✔
1496
    }
1497
    return nullptr;
73✔
1498
}
1499

1500
/************************************************************************/
1501
/*                     GDALGetRandomRasterSample()                      */
1502
/************************************************************************/
1503

1504
/** Undocumented
1505
 * @param hBand undocumented.
1506
 * @param nSamples undocumented.
1507
 * @param pafSampleBuf undocumented.
1508
 * @return undocumented
1509
 */
1510
int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
×
1511
                                          float *pafSampleBuf)
1512

1513
{
1514
    VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
×
1515

1516
    GDALRasterBand *poBand;
1517

1518
    poBand = GDALRasterBand::FromHandle(
×
1519
        GDALGetRasterSampleOverview(hBand, nSamples));
1520
    CPLAssert(nullptr != poBand);
×
1521

1522
    /* -------------------------------------------------------------------- */
1523
    /*      Figure out the ratio of blocks we will read to get an           */
1524
    /*      approximate value.                                              */
1525
    /* -------------------------------------------------------------------- */
1526
    int bGotNoDataValue = FALSE;
×
1527

1528
    double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
×
1529

1530
    int nBlockXSize = 0;
×
1531
    int nBlockYSize = 0;
×
1532
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
×
1533

1534
    const int nBlocksPerRow =
1535
        (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
×
1536
    const int nBlocksPerColumn =
1537
        (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
×
1538

1539
    const GIntBig nBlockPixels =
×
1540
        static_cast<GIntBig>(nBlockXSize) * nBlockYSize;
×
1541
    const GIntBig nBlockCount =
×
1542
        static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn;
×
1543

1544
    if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
×
1545
        nBlockCount == 0)
1546
    {
1547
        CPLError(CE_Failure, CPLE_AppDefined,
×
1548
                 "GDALGetRandomRasterSample(): returning because band"
1549
                 " appears degenerate.");
1550

1551
        return FALSE;
×
1552
    }
1553

1554
    int nSampleRate = static_cast<int>(
1555
        std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
×
1556

1557
    if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
×
1558
        nSampleRate--;
×
1559

1560
    while (nSampleRate > 1 &&
×
1561
           ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
×
1562
        nSampleRate--;
×
1563

1564
    int nBlockSampleRate = 1;
×
1565

1566
    if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
×
1567
        nBlockSampleRate = static_cast<int>(std::max<GIntBig>(
×
1568
            1,
×
1569
            nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1))));
×
1570

1571
    int nActualSamples = 0;
×
1572

1573
    for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount;
×
1574
         iSampleBlock += nSampleRate)
×
1575
    {
1576

1577
        const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow);
×
1578
        const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow);
×
1579

1580
        GDALRasterBlock *const poBlock =
1581
            poBand->GetLockedBlockRef(iXBlock, iYBlock);
×
1582
        if (poBlock == nullptr)
×
1583
            continue;
×
1584
        void *pDataRef = poBlock->GetDataRef();
×
1585

1586
        int iXValid = nBlockXSize;
×
1587
        if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
×
1588
            iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
×
1589

1590
        int iYValid = nBlockYSize;
×
1591
        if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
×
1592
            iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
×
1593

1594
        int iRemainder = 0;
×
1595

1596
        for (int iY = 0; iY < iYValid; iY++)
×
1597
        {
1598
            int iX = iRemainder;  // Used after for.
×
1599
            for (; iX < iXValid; iX += nBlockSampleRate)
×
1600
            {
1601
                double dfValue = 0.0;
×
1602
                const int iOffset = iX + iY * nBlockXSize;
×
1603

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

1705
                if (bGotNoDataValue && dfValue == dfNoDataValue)
×
1706
                    continue;
×
1707

1708
                if (nActualSamples < nSamples)
×
1709
                    pafSampleBuf[nActualSamples++] =
×
1710
                        static_cast<float>(dfValue);
×
1711
            }
1712

1713
            iRemainder = iX - iXValid;
×
1714
        }
1715

1716
        poBlock->DropLock();
×
1717
    }
1718

1719
    return nActualSamples;
×
1720
}
1721

1722
/************************************************************************/
1723
/*                             gdal::GCP                                */
1724
/************************************************************************/
1725

1726
namespace gdal
1727
{
1728
/** Constructor. */
1729
GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
25,453✔
1730
         double dfX, double dfY, double dfZ)
25,453✔
1731
    : gcp{CPLStrdup(pszId ? pszId : ""),
25,453✔
1732
          CPLStrdup(pszInfo ? pszInfo : ""),
25,453✔
1733
          dfPixel,
1734
          dfLine,
1735
          dfX,
1736
          dfY,
1737
          dfZ}
25,453✔
1738
{
1739
    static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1740
}
25,453✔
1741

1742
/** Destructor. */
1743
GCP::~GCP()
313,482✔
1744
{
1745
    CPLFree(gcp.pszId);
156,741✔
1746
    CPLFree(gcp.pszInfo);
156,741✔
1747
}
156,741✔
1748

1749
/** Constructor from a C GDAL_GCP instance. */
1750
GCP::GCP(const GDAL_GCP &other)
106,802✔
1751
    : gcp{CPLStrdup(other.pszId),
106,802✔
1752
          CPLStrdup(other.pszInfo),
213,604✔
1753
          other.dfGCPPixel,
106,802✔
1754
          other.dfGCPLine,
106,802✔
1755
          other.dfGCPX,
106,802✔
1756
          other.dfGCPY,
106,802✔
1757
          other.dfGCPZ}
106,802✔
1758
{
1759
}
106,802✔
1760

1761
/** Copy constructor. */
1762
GCP::GCP(const GCP &other) : GCP(other.gcp)
37,929✔
1763
{
1764
}
37,929✔
1765

1766
/** Move constructor. */
1767
GCP::GCP(GCP &&other)
24,486✔
1768
    : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
24,486✔
1769
          other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
24,486✔
1770
          other.gcp.dfGCPZ}
24,486✔
1771
{
1772
    other.gcp.pszId = nullptr;
24,486✔
1773
    other.gcp.pszInfo = nullptr;
24,486✔
1774
}
24,486✔
1775

1776
/** Copy assignment operator. */
1777
GCP &GCP::operator=(const GCP &other)
1✔
1778
{
1779
    if (this != &other)
1✔
1780
    {
1781
        CPLFree(gcp.pszId);
1✔
1782
        CPLFree(gcp.pszInfo);
1✔
1783
        gcp = other.gcp;
1✔
1784
        gcp.pszId = CPLStrdup(other.gcp.pszId);
1✔
1785
        gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1✔
1786
    }
1787
    return *this;
1✔
1788
}
1789

1790
/** Move assignment operator. */
1791
GCP &GCP::operator=(GCP &&other)
1✔
1792
{
1793
    if (this != &other)
1✔
1794
    {
1795
        CPLFree(gcp.pszId);
1✔
1796
        CPLFree(gcp.pszInfo);
1✔
1797
        gcp = other.gcp;
1✔
1798
        other.gcp.pszId = nullptr;
1✔
1799
        other.gcp.pszInfo = nullptr;
1✔
1800
    }
1801
    return *this;
1✔
1802
}
1803

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

1811
/** Set the 'info' member of the GCP. */
1812
void GCP::SetInfo(const char *pszInfo)
24,455✔
1813
{
1814
    CPLFree(gcp.pszInfo);
24,455✔
1815
    gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
24,455✔
1816
}
24,455✔
1817

1818
/** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1819
/*static */
1820
const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
677✔
1821
{
1822
    return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
677✔
1823
}
1824

1825
/** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1826
/*static*/
1827
std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
308✔
1828
{
1829
    return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
308✔
1830
}
1831

1832
} /* namespace gdal */
1833

1834
/************************************************************************/
1835
/*                            GDALInitGCPs()                            */
1836
/************************************************************************/
1837

1838
/** Initialize an array of GCPs.
1839
 *
1840
 * Numeric values are initialized to 0 and strings to the empty string ""
1841
 * allocated with CPLStrdup()
1842
 * An array initialized with GDALInitGCPs() must be de-initialized with
1843
 * GDALDeinitGCPs().
1844
 *
1845
 * @param nCount number of GCPs in psGCP
1846
 * @param psGCP array of GCPs of size nCount.
1847
 */
1848
void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1,254✔
1849

1850
{
1851
    if (nCount > 0)
1,254✔
1852
    {
1853
        VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
613✔
1854
    }
1855

1856
    for (int iGCP = 0; iGCP < nCount; iGCP++)
5,969✔
1857
    {
1858
        memset(psGCP, 0, sizeof(GDAL_GCP));
4,715✔
1859
        psGCP->pszId = CPLStrdup("");
4,715✔
1860
        psGCP->pszInfo = CPLStrdup("");
4,715✔
1861
        psGCP++;
4,715✔
1862
    }
1863
}
1864

1865
/************************************************************************/
1866
/*                           GDALDeinitGCPs()                           */
1867
/************************************************************************/
1868

1869
/** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1870
 *
1871
 * @param nCount number of GCPs in psGCP
1872
 * @param psGCP array of GCPs of size nCount.
1873
 */
1874
void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1,370✔
1875

1876
{
1877
    if (nCount > 0)
1,370✔
1878
    {
1879
        VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
461✔
1880
    }
1881

1882
    for (int iGCP = 0; iGCP < nCount; iGCP++)
6,468✔
1883
    {
1884
        CPLFree(psGCP->pszId);
5,098✔
1885
        CPLFree(psGCP->pszInfo);
5,098✔
1886
        psGCP++;
5,098✔
1887
    }
1888
}
1889

1890
/************************************************************************/
1891
/*                         GDALDuplicateGCPs()                          */
1892
/************************************************************************/
1893

1894
/** Duplicate an array of GCPs
1895
 *
1896
 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1897
 *
1898
 * @param nCount number of GCPs in psGCP
1899
 * @param pasGCPList array of GCPs of size nCount.
1900
 */
1901
GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
726✔
1902

1903
{
1904
    GDAL_GCP *pasReturn =
1905
        static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
726✔
1906
    GDALInitGCPs(nCount, pasReturn);
726✔
1907

1908
    for (int iGCP = 0; iGCP < nCount; iGCP++)
3,954✔
1909
    {
1910
        CPLFree(pasReturn[iGCP].pszId);
3,228✔
1911
        pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
3,228✔
1912

1913
        CPLFree(pasReturn[iGCP].pszInfo);
3,228✔
1914
        pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
3,228✔
1915

1916
        pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
3,228✔
1917
        pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
3,228✔
1918
        pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
3,228✔
1919
        pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
3,228✔
1920
        pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
3,228✔
1921
    }
1922

1923
    return pasReturn;
726✔
1924
}
1925

1926
/************************************************************************/
1927
/*                       GDALFindAssociatedFile()                       */
1928
/************************************************************************/
1929

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

1956
/**/
1957
/**/
1958

1959
CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
37,641✔
1960
                                 const char *pszExt,
1961
                                 CSLConstList papszSiblingFiles,
1962
                                 CPL_UNUSED int nFlags)
1963

1964
{
1965
    CPLString osTarget = CPLResetExtensionSafe(pszBaseFilename, pszExt);
75,282✔
1966

1967
    if (papszSiblingFiles == nullptr ||
75,018✔
1968
        // cppcheck-suppress knownConditionTrueFalse
1969
        !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
37,377✔
1970
    {
1971
        VSIStatBufL sStatBuf;
1972

1973
        if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
264✔
1974
        {
1975
            CPLString osAltExt = pszExt;
249✔
1976

1977
            if (islower(static_cast<unsigned char>(pszExt[0])))
249✔
1978
                osAltExt = osAltExt.toupper();
×
1979
            else
1980
                osAltExt = osAltExt.tolower();
249✔
1981

1982
            osTarget = CPLResetExtensionSafe(pszBaseFilename, osAltExt);
249✔
1983

1984
            if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
249✔
1985
                return "";
247✔
1986
        }
1987
    }
1988
    else
1989
    {
1990
        const int iSibling =
1991
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
37,377✔
1992
        if (iSibling < 0)
37,377✔
1993
            return "";
37,326✔
1994

1995
        osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
51✔
1996
        osTarget += papszSiblingFiles[iSibling];
51✔
1997
    }
1998

1999
    return osTarget;
68✔
2000
}
2001

2002
/************************************************************************/
2003
/*                         GDALLoadOziMapFile()                         */
2004
/************************************************************************/
2005

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

2020
{
2021
    VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
×
2022
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
×
2023
    VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
×
2024
    VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
×
2025

2026
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
×
2027

2028
    if (!papszLines)
×
2029
        return FALSE;
×
2030

2031
    int nLines = CSLCount(papszLines);
×
2032

2033
    // Check the OziExplorer Map file signature
2034
    if (nLines < 5 ||
×
2035
        !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
×
2036
    {
2037
        CPLError(CE_Failure, CPLE_AppDefined,
×
2038
                 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
2039
                 "format.",
2040
                 pszFilename);
2041
        CSLDestroy(papszLines);
×
2042
        return FALSE;
×
2043
    }
2044

2045
    OGRSpatialReference oSRS;
×
2046
    OGRErr eErr = OGRERR_NONE;
×
2047

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

2060
    for (int iLine = 5; iLine < nLines; iLine++)
×
2061
    {
2062
        if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
×
2063
        {
2064
            dfMSF = CPLAtof(papszLines[iLine] + 4);
×
2065
            if (dfMSF <= 0.01) /* Suspicious values */
×
2066
            {
2067
                CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
×
2068
                dfMSF = 1;
×
2069
            }
2070
        }
2071
    }
2072

2073
    eErr = oSRS.importFromOzi(papszLines);
×
2074
    if (eErr == OGRERR_NONE)
×
2075
    {
2076
        if (ppszWKT != nullptr)
×
2077
            oSRS.exportToWkt(ppszWKT);
×
2078
    }
2079

2080
    int nCoordinateCount = 0;
×
2081
    // TODO(schwehr): Initialize asGCPs.
2082
    GDAL_GCP asGCPs[30];
2083

2084
    // Iterate all lines in the MAP-file
2085
    for (int iLine = 5; iLine < nLines; iLine++)
×
2086
    {
2087
        char **papszTok = CSLTokenizeString2(
×
2088
            papszLines[iLine], ",",
×
2089
            CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
2090

2091
        if (CSLCount(papszTok) < 12)
×
2092
        {
2093
            CSLDestroy(papszTok);
×
2094
            continue;
×
2095
        }
2096

2097
        if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
×
2098
            !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
×
2099
            nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2100
        {
2101
            bool bReadOk = false;
×
2102
            double dfLon = 0.0;
×
2103
            double dfLat = 0.0;
×
2104

2105
            if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
×
2106
                !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
×
2107
            {
2108
                // Set geographical coordinates of the pixels
2109
                dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
×
2110
                dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
×
2111
                if (EQUAL(papszTok[11], "W"))
×
2112
                    dfLon = -dfLon;
×
2113
                if (EQUAL(papszTok[8], "S"))
×
2114
                    dfLat = -dfLat;
×
2115

2116
                // Transform from the geographical coordinates into projected
2117
                // coordinates.
2118
                if (eErr == OGRERR_NONE)
×
2119
                {
2120
                    OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
×
2121

2122
                    if (poLongLat)
×
2123
                    {
2124
                        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
2125
                        poLongLat->SetAxisMappingStrategy(
×
2126
                            OAMS_TRADITIONAL_GIS_ORDER);
2127

2128
                        OGRCoordinateTransformation *poTransform =
2129
                            OGRCreateCoordinateTransformation(poLongLat, &oSRS);
×
2130
                        if (poTransform)
×
2131
                        {
2132
                            bReadOk = CPL_TO_BOOL(
×
2133
                                poTransform->Transform(1, &dfLon, &dfLat));
2134
                            delete poTransform;
×
2135
                        }
2136
                        delete poLongLat;
×
2137
                    }
2138
                }
×
2139
            }
2140
            else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
×
2141
            {
2142
                // Set cartesian coordinates of the pixels.
2143
                dfLon = CPLAtofM(papszTok[14]);
×
2144
                dfLat = CPLAtofM(papszTok[15]);
×
2145
                bReadOk = true;
×
2146

2147
                // if ( EQUAL(papszTok[16], "S") )
2148
                //     dfLat = -dfLat;
2149
            }
2150

2151
            if (bReadOk)
×
2152
            {
2153
                GDALInitGCPs(1, asGCPs + nCoordinateCount);
×
2154

2155
                // Set pixel/line part
2156
                asGCPs[nCoordinateCount].dfGCPPixel =
×
2157
                    CPLAtofM(papszTok[2]) / dfMSF;
×
2158
                asGCPs[nCoordinateCount].dfGCPLine =
×
2159
                    CPLAtofM(papszTok[3]) / dfMSF;
×
2160

2161
                asGCPs[nCoordinateCount].dfGCPX = dfLon;
×
2162
                asGCPs[nCoordinateCount].dfGCPY = dfLat;
×
2163

2164
                nCoordinateCount++;
×
2165
            }
2166
        }
2167

2168
        CSLDestroy(papszTok);
×
2169
    }
2170

2171
    CSLDestroy(papszLines);
×
2172

2173
    if (nCoordinateCount == 0)
×
2174
    {
2175
        CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
×
2176
                 pszFilename);
2177
        return FALSE;
×
2178
    }
2179

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

2196
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2197
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2198
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2199
            *pnGCPCount = nCoordinateCount;
×
2200
        }
2201
    }
2202
    else
2203
    {
2204
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
×
2205
    }
2206

2207
    return TRUE;
×
2208
}
2209

2210
/************************************************************************/
2211
/*                       GDALReadOziMapFile()                           */
2212
/************************************************************************/
2213

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

2229
{
2230
    /* -------------------------------------------------------------------- */
2231
    /*      Try lower case, then upper case.                                */
2232
    /* -------------------------------------------------------------------- */
2233
    std::string osOzi = CPLResetExtensionSafe(pszBaseFilename, "map");
×
2234

2235
    VSILFILE *fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
×
2236

2237
    if (fpOzi == nullptr && VSIIsCaseSensitiveFS(osOzi.c_str()))
×
2238
    {
2239
        osOzi = CPLResetExtensionSafe(pszBaseFilename, "MAP");
×
2240
        fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
×
2241
    }
2242

2243
    if (fpOzi == nullptr)
×
2244
        return FALSE;
×
2245

2246
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
×
2247

2248
    /* -------------------------------------------------------------------- */
2249
    /*      We found the file, now load and parse it.                       */
2250
    /* -------------------------------------------------------------------- */
2251
    return GDALLoadOziMapFile(osOzi.c_str(), padfGeoTransform, ppszWKT,
×
2252
                              pnGCPCount, ppasGCPs);
×
2253
}
2254

2255
/************************************************************************/
2256
/*                         GDALLoadTabFile()                            */
2257
/*                                                                      */
2258
/************************************************************************/
2259

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

2275
{
2276
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
14✔
2277

2278
    if (!papszLines)
14✔
2279
        return FALSE;
×
2280

2281
    char **papszTok = nullptr;
14✔
2282
    bool bTypeRasterFound = false;
14✔
2283
    bool bInsideTableDef = false;
14✔
2284
    int nCoordinateCount = 0;
14✔
2285
    GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
2286
    const int numLines = CSLCount(papszLines);
14✔
2287

2288
    // Iterate all lines in the TAB-file
2289
    for (int iLine = 0; iLine < numLines; iLine++)
196✔
2290
    {
2291
        CSLDestroy(papszTok);
182✔
2292
        papszTok =
2293
            CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
182✔
2294

2295
        if (CSLCount(papszTok) < 2)
182✔
2296
            continue;
28✔
2297

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

2323
            asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
56✔
2324
            asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
56✔
2325
            asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
56✔
2326
            asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
56✔
2327
            if (papszTok[5] != nullptr)
56✔
2328
            {
2329
                CPLFree(asGCPs[nCoordinateCount].pszId);
56✔
2330
                asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
56✔
2331
            }
2332

2333
            nCoordinateCount++;
56✔
2334
        }
2335
        else if (bTypeRasterFound && bInsideTableDef &&
70✔
2336
                 EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
28✔
2337
        {
2338
            OGRSpatialReference oSRS;
28✔
2339

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

2356
                OGRSpatialReference oSRSGeogCS;
×
2357
                oSRSGeogCS.CopyGeogCSFrom(&oSRS);
×
2358
                CPLFree(*ppszWKT);
×
2359

2360
                oSRSGeogCS.exportToWkt(ppszWKT);
×
2361
            }
2362
        }
2363
    }
2364

2365
    CSLDestroy(papszTok);
14✔
2366
    CSLDestroy(papszLines);
14✔
2367

2368
    if (nCoordinateCount == 0)
14✔
2369
    {
2370
        CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
×
2371
                 pszFilename);
2372
        return FALSE;
×
2373
    }
2374

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

2390
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2391
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2392
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2393
            *pnGCPCount = nCoordinateCount;
×
2394
        }
2395
    }
2396
    else
2397
    {
2398
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
14✔
2399
    }
2400

2401
    return TRUE;
14✔
2402
}
2403

2404
/************************************************************************/
2405
/*                         GDALReadTabFile()                            */
2406
/************************************************************************/
2407

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

2424
{
2425
    return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
×
2426
                            pnGCPCount, ppasGCPs, nullptr, nullptr);
×
2427
}
2428

2429
int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
3,788✔
2430
                     char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2431
                     CSLConstList papszSiblingFiles, char **ppszTabFileNameOut)
2432
{
2433
    if (ppszTabFileNameOut)
3,788✔
2434
        *ppszTabFileNameOut = nullptr;
3,788✔
2435

2436
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
3,788✔
2437
        return FALSE;
×
2438

2439
    std::string osTAB = CPLResetExtensionSafe(pszBaseFilename, "tab");
7,576✔
2440

2441
    if (papszSiblingFiles &&
7,526✔
2442
        // cppcheck-suppress knownConditionTrueFalse
2443
        GDALCanReliablyUseSiblingFileList(osTAB.c_str()))
3,738✔
2444
    {
2445
        int iSibling =
2446
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTAB.c_str()));
3,739✔
2447
        if (iSibling >= 0)
3,739✔
2448
        {
2449
            CPLString osTabFilename = pszBaseFilename;
14✔
2450
            osTabFilename.resize(strlen(pszBaseFilename) -
28✔
2451
                                 strlen(CPLGetFilename(pszBaseFilename)));
14✔
2452
            osTabFilename += papszSiblingFiles[iSibling];
14✔
2453
            if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
14✔
2454
                                pnGCPCount, ppasGCPs))
14✔
2455
            {
2456
                if (ppszTabFileNameOut)
14✔
2457
                    *ppszTabFileNameOut = CPLStrdup(osTabFilename);
14✔
2458
                return TRUE;
14✔
2459
            }
2460
        }
2461
        return FALSE;
3,725✔
2462
    }
2463

2464
    /* -------------------------------------------------------------------- */
2465
    /*      Try lower case, then upper case.                                */
2466
    /* -------------------------------------------------------------------- */
2467

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

2470
    if (fpTAB == nullptr && VSIIsCaseSensitiveFS(osTAB.c_str()))
49✔
2471
    {
2472
        osTAB = CPLResetExtensionSafe(pszBaseFilename, "TAB");
49✔
2473
        fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
49✔
2474
    }
2475

2476
    if (fpTAB == nullptr)
49✔
2477
        return FALSE;
49✔
2478

2479
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
×
2480

2481
    /* -------------------------------------------------------------------- */
2482
    /*      We found the file, now load and parse it.                       */
2483
    /* -------------------------------------------------------------------- */
2484
    if (GDALLoadTabFile(osTAB.c_str(), padfGeoTransform, ppszWKT, pnGCPCount,
×
2485
                        ppasGCPs))
×
2486
    {
2487
        if (ppszTabFileNameOut)
×
2488
            *ppszTabFileNameOut = CPLStrdup(osTAB.c_str());
×
2489
        return TRUE;
×
2490
    }
2491
    return FALSE;
×
2492
}
2493

2494
/************************************************************************/
2495
/*                         GDALLoadWorldFile()                          */
2496
/************************************************************************/
2497

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

2525
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
67✔
2526
                                  double *padfGeoTransform)
2527

2528
{
2529
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
67✔
2530
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
67✔
2531

2532
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
67✔
2533

2534
    if (!papszLines)
67✔
2535
        return FALSE;
×
2536

2537
    double world[6] = {0.0};
67✔
2538
    // reads the first 6 non-empty lines
2539
    int nLines = 0;
67✔
2540
    const int nLinesCount = CSLCount(papszLines);
67✔
2541
    for (int i = 0;
469✔
2542
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
469✔
2543
         ++i)
2544
    {
2545
        CPLString line(papszLines[i]);
402✔
2546
        if (line.Trim().empty())
402✔
2547
            continue;
×
2548

2549
        world[nLines] = CPLAtofM(line);
402✔
2550
        ++nLines;
402✔
2551
    }
2552

2553
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
67✔
2554
        (world[3] != 0.0 || world[1] != 0.0))
67✔
2555
    {
2556
        padfGeoTransform[0] = world[4];
67✔
2557
        padfGeoTransform[1] = world[0];
67✔
2558
        padfGeoTransform[2] = world[2];
67✔
2559
        padfGeoTransform[3] = world[5];
67✔
2560
        padfGeoTransform[4] = world[1];
67✔
2561
        padfGeoTransform[5] = world[3];
67✔
2562

2563
        // correct for center of pixel vs. top left of pixel
2564
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
67✔
2565
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
67✔
2566
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
67✔
2567
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
67✔
2568

2569
        CSLDestroy(papszLines);
67✔
2570

2571
        return TRUE;
67✔
2572
    }
2573
    else
2574
    {
2575
        CPLDebug("GDAL",
×
2576
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2577
                 pszFilename);
2578
        CSLDestroy(papszLines);
×
2579
        return FALSE;
×
2580
    }
2581
}
2582

2583
/************************************************************************/
2584
/*                         GDALReadWorldFile()                          */
2585
/************************************************************************/
2586

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

2620
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
806✔
2621
                                  const char *pszExtension,
2622
                                  double *padfGeoTransform)
2623

2624
{
2625
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
806✔
2626
                              nullptr, nullptr);
806✔
2627
}
2628

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

2636
    if (ppszWorldFileNameOut)
19,800✔
2637
        *ppszWorldFileNameOut = nullptr;
18,006✔
2638

2639
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
19,800✔
2640
        return FALSE;
202✔
2641

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

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

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

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

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

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

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

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

2690
    for (int i = 0; szExtUpper[i] != '\0'; i++)
64,731✔
2691
    {
2692
        szExtUpper[i] = static_cast<char>(
49,793✔
2693
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
49,794✔
2694
        szExtLower[i] = static_cast<char>(
49,793✔
2695
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
49,793✔
2696
    }
2697

2698
    std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
29,875✔
2699

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

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

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

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

2737
    if (!bGotTFW)
1,049✔
2738
        return FALSE;
1,047✔
2739

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

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

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

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

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

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

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

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

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

2822
    return bRet;
13✔
2823
}
2824

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

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

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

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

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

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

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

2920
#undef STRINGIFY_HELPER
2921
#undef STRINGIFY
2922

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3093
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
4✔
3094

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

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

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

3109
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
4✔
3110

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

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

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

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

3153
{
3154
    double dfPixelThreshold = 0.25;
603✔
3155
    if (!bApproxOK)
603✔
3156
    {
3157
        bApproxOK = CPLTestBool(
589✔
3158
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3159
        if (!bApproxOK)
589✔
3160
        {
3161
            // coverity[tainted_data]
3162
            dfPixelThreshold = CPLAtof(CPLGetConfigOption(
589✔
3163
                "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
3164
        }
3165
    }
3166

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

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

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

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

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

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

3195
        return TRUE;
2✔
3196
    }
3197

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

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

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

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

3256
    double EPS = 1.0e-12;
401✔
3257

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3380
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
323✔
3381
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
323✔
3382

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

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

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

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

3433
    return TRUE;
315✔
3434
}
3435

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

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

3452
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
646✔
3453
                              double *padfGTOut)
3454

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

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

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

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

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

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

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

3544
/************************************************************************/
3545
/*                    GDALGeneralCmdLineProcessor()                     */
3546
/************************************************************************/
3547

3548
/**
3549
 * \brief General utility option processing.
3550
 *
3551
 * This function is intended to provide a variety of generic commandline
3552
 * options for all GDAL commandline utilities.  It takes care of the following
3553
 * commandline options:
3554
 *
3555
 *  --version: report version of GDAL in use.
3556
 *  --build: report build info about GDAL in use.
3557
 *  --license: report GDAL license info.
3558
 *  --formats: report all format drivers configured. Can be used with -json since 3.10
3559
 *  --format [format]: report details of one format driver.
3560
 *  --optfile filename: expand an option file into the argument list.
3561
 *  --config key value: set system configuration option.
3562
 *  --config key=value: set system configuration option (since GDAL 3.9)
3563
 *  --debug [on/off/value]: set debug level.
3564
 *  --mempreload dir: preload directory contents into /vsimem
3565
 *  --pause: Pause for user input (allows time to attach debugger)
3566
 *  --locale [locale]: Install a locale using setlocale() (debugging)
3567
 *  --help-general: report detailed help on general options.
3568
 *
3569
 * The argument array is replaced "in place" and should be freed with
3570
 * CSLDestroy() when no longer needed.  The typical usage looks something
3571
 * like the following.  Note that the formats should be registered so that
3572
 * the --formats and --format options will work properly.
3573
 *
3574
 *  int main( int argc, char ** argv )
3575
 *  {
3576
 *    GDALAllRegister();
3577
 *
3578
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3579
 *    if( argc < 1 )
3580
 *        exit( -argc );
3581
 *
3582
 * @param nArgc number of values in the argument list.
3583
 * @param ppapszArgv pointer to the argument list array (will be updated in
3584
 * place).
3585
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3586
 *                 to determine which drivers should be displayed by --formats.
3587
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3588
 *
3589
 * @return updated nArgc argument count.  Return of 0 requests terminate
3590
 * without error, return of -1 requests exit with error code.
3591
 */
3592

3593
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
1,293✔
3594
                                            int nOptions)
3595

3596
{
3597
    CPLStringList aosReturn;
2,586✔
3598
    int iArg;
3599
    char **papszArgv = *ppapszArgv;
1,293✔
3600

3601
    /* -------------------------------------------------------------------- */
3602
    /*      Preserve the program name.                                      */
3603
    /* -------------------------------------------------------------------- */
3604
    aosReturn.AddString(papszArgv[0]);
1,293✔
3605

3606
    /* ==================================================================== */
3607
    /*      Loop over all arguments.                                        */
3608
    /* ==================================================================== */
3609

3610
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3611
    // detects and warns about a unknown config option.
3612
    for (iArg = 1; iArg < nArgc; iArg++)
8,147✔
3613
    {
3614
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
6,856✔
3615
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
38✔
3616
        {
3617
            if (iArg + 1 >= nArgc)
×
3618
            {
3619
                CPLError(CE_Failure, CPLE_AppDefined,
×
3620
                         "--config option given without a key=value argument.");
3621
                return -1;
×
3622
            }
3623

3624
            const char *pszArg = papszArgv[iArg + 1];
×
3625
            if (strchr(pszArg, '=') != nullptr)
×
3626
            {
3627
                char *pszKey = nullptr;
×
3628
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
×
3629
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
×
3630
                {
3631
                    CPLSetConfigOption(pszKey, pszValue);
×
3632
                }
3633
                CPLFree(pszKey);
×
3634
                ++iArg;
×
3635
            }
3636
            else
3637
            {
3638
                if (iArg + 2 >= nArgc)
×
3639
                {
3640
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3641
                             "--config option given without a key and value "
3642
                             "argument.");
3643
                    return -1;
×
3644
                }
3645

3646
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
×
3647
                    CPLSetConfigOption(papszArgv[iArg + 1],
×
3648
                                       papszArgv[iArg + 2]);
×
3649

3650
                iArg += 2;
×
3651
            }
×
3652
        }
3653
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,856✔
3654
        {
3655
            if (iArg + 1 >= nArgc)
15✔
3656
            {
3657
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3658
                         "--debug option given without debug level.");
3659
                return -1;
2✔
3660
            }
3661

3662
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
13✔
3663
            iArg += 1;
13✔
3664
        }
3665
    }
3666

3667
    for (iArg = 1; iArg < nArgc; iArg++)
7,958✔
3668
    {
3669
        /* --------------------------------------------------------------------
3670
         */
3671
        /*      --version */
3672
        /* --------------------------------------------------------------------
3673
         */
3674
        if (EQUAL(papszArgv[iArg], "--version"))
6,755✔
3675
        {
3676
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
63✔
3677
            return 0;
63✔
3678
        }
3679

3680
        /* --------------------------------------------------------------------
3681
         */
3682
        /*      --build */
3683
        /* --------------------------------------------------------------------
3684
         */
3685
        else if (EQUAL(papszArgv[iArg], "--build"))
6,692✔
3686
        {
3687
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
1✔
3688
            return 0;
1✔
3689
        }
3690

3691
        /* --------------------------------------------------------------------
3692
         */
3693
        /*      --license */
3694
        /* --------------------------------------------------------------------
3695
         */
3696
        else if (EQUAL(papszArgv[iArg], "--license"))
6,691✔
3697
        {
3698
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
1✔
3699
            return 0;
1✔
3700
        }
3701

3702
        /* --------------------------------------------------------------------
3703
         */
3704
        /*      --config */
3705
        /* --------------------------------------------------------------------
3706
         */
3707
        else if (EQUAL(papszArgv[iArg], "--config"))
6,690✔
3708
        {
3709
            if (iArg + 1 >= nArgc)
44✔
3710
            {
3711
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3712
                         "--config option given without a key=value argument.");
3713
                return -1;
2✔
3714
            }
3715

3716
            const char *pszArg = papszArgv[iArg + 1];
42✔
3717
            if (strchr(pszArg, '=') != nullptr)
42✔
3718
            {
3719
                char *pszKey = nullptr;
2✔
3720
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
2✔
3721
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
2✔
3722
                {
3723
                    CPLSetConfigOption(pszKey, pszValue);
2✔
3724
                }
3725
                CPLFree(pszKey);
2✔
3726
                ++iArg;
2✔
3727
            }
3728
            else
3729
            {
3730
                if (iArg + 2 >= nArgc)
40✔
3731
                {
3732
                    CPLError(CE_Failure, CPLE_AppDefined,
2✔
3733
                             "--config option given without a key and value "
3734
                             "argument.");
3735
                    return -1;
2✔
3736
                }
3737

3738
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
38✔
3739
                    CPLSetConfigOption(papszArgv[iArg + 1],
38✔
3740
                                       papszArgv[iArg + 2]);
38✔
3741

3742
                iArg += 2;
38✔
3743
            }
3744
        }
3745

3746
        /* --------------------------------------------------------------------
3747
         */
3748
        /*      --mempreload */
3749
        /* --------------------------------------------------------------------
3750
         */
3751
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
6,646✔
3752
        {
3753
            if (iArg + 1 >= nArgc)
4✔
3754
            {
3755
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3756
                         "--mempreload option given without directory path.");
3757
                return -1;
2✔
3758
            }
3759

3760
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
2✔
3761
            if (CSLCount(papszFiles) == 0)
2✔
3762
            {
3763
                CPLError(CE_Failure, CPLE_AppDefined,
×
3764
                         "--mempreload given invalid or empty directory.");
3765
                return -1;
×
3766
            }
3767

3768
            for (int i = 0; papszFiles[i] != nullptr; i++)
493✔
3769
            {
3770
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
491✔
3771
                    continue;
72✔
3772

3773
                std::string osOldPath;
487✔
3774
                CPLString osNewPath;
487✔
3775
                osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
487✔
3776
                                                papszFiles[i], nullptr);
487✔
3777
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
487✔
3778

3779
                VSIStatBufL sStatBuf;
3780
                if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
974✔
3781
                    VSI_ISDIR(sStatBuf.st_mode))
487✔
3782
                {
3783
                    CPLDebug("VSI", "Skipping preload of %s.",
68✔
3784
                             osOldPath.c_str());
3785
                    continue;
68✔
3786
                }
3787

3788
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
419✔
3789
                         osNewPath.c_str());
3790

3791
                if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
419✔
3792
                {
3793
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3794
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3795
                    return -1;
×
3796
                }
3797
            }
3798

3799
            CSLDestroy(papszFiles);
2✔
3800
            iArg += 1;
2✔
3801
        }
3802

3803
        /* --------------------------------------------------------------------
3804
         */
3805
        /*      --debug */
3806
        /* --------------------------------------------------------------------
3807
         */
3808
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,642✔
3809
        {
3810
            if (iArg + 1 >= nArgc)
13✔
3811
            {
3812
                CPLError(CE_Failure, CPLE_AppDefined,
×
3813
                         "--debug option given without debug level.");
3814
                return -1;
×
3815
            }
3816

3817
            iArg += 1;
13✔
3818
        }
3819

3820
        /* --------------------------------------------------------------------
3821
         */
3822
        /*      --optfile */
3823
        /* --------------------------------------------------------------------
3824
         */
3825
        else if (EQUAL(papszArgv[iArg], "--optfile"))
6,629✔
3826
        {
3827
            if (iArg + 1 >= nArgc)
11✔
3828
            {
3829
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3830
                         "--optfile option given without filename.");
3831
                return -1;
5✔
3832
            }
3833

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

3836
            if (fpOptFile == nullptr)
9✔
3837
            {
3838
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
3839
                         "Unable to open optfile '%s'.\n%s",
3840
                         papszArgv[iArg + 1], VSIStrerror(errno));
2✔
3841
                return -1;
2✔
3842
            }
3843

3844
            const char *pszLine;
3845
            CPLStringList aosArgvOptfile;
7✔
3846
            // dummy value as first argument to please
3847
            // GDALGeneralCmdLineProcessor()
3848
            aosArgvOptfile.AddString("");
7✔
3849
            bool bHasOptfile = false;
7✔
3850
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
23✔
3851
            {
3852
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
16✔
3853
                    continue;
3✔
3854

3855
                char **papszTokens = CSLTokenizeString(pszLine);
13✔
3856
                for (int i = 0;
13✔
3857
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
45✔
3858
                {
3859
                    if (EQUAL(papszTokens[i], "--optfile"))
32✔
3860
                    {
3861
                        // To avoid potential recursion
3862
                        CPLError(CE_Warning, CPLE_AppDefined,
×
3863
                                 "--optfile not supported in a option file");
3864
                        bHasOptfile = true;
×
3865
                    }
3866
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
32✔
3867
                    papszTokens[i] = nullptr;
32✔
3868
                }
3869
                CSLDestroy(papszTokens);
13✔
3870
            }
3871

3872
            VSIFCloseL(fpOptFile);
7✔
3873

3874
            char **papszArgvOptfile = aosArgvOptfile.StealList();
7✔
3875
            if (!bHasOptfile)
7✔
3876
            {
3877
                char **papszArgvOptfileBefore = papszArgvOptfile;
7✔
3878
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
7✔
3879
                                                &papszArgvOptfile,
3880
                                                nOptions) < 0)
7✔
3881
                {
3882
                    CSLDestroy(papszArgvOptfile);
1✔
3883
                    return -1;
1✔
3884
                }
3885
                CSLDestroy(papszArgvOptfileBefore);
6✔
3886
            }
3887

3888
            char **papszIter = papszArgvOptfile + 1;
6✔
3889
            while (*papszIter)
36✔
3890
            {
3891
                aosReturn.AddString(*papszIter);
30✔
3892
                ++papszIter;
30✔
3893
            }
3894
            CSLDestroy(papszArgvOptfile);
6✔
3895

3896
            iArg += 1;
6✔
3897
        }
3898

3899
        /* --------------------------------------------------------------------
3900
         */
3901
        /*      --formats */
3902
        /* --------------------------------------------------------------------
3903
         */
3904
        else if (EQUAL(papszArgv[iArg], "--formats") ||
6,618✔
3905
                 EQUAL(papszArgv[iArg], "--drivers"))
6,613✔
3906
        {
3907
            if (nOptions == 0)
5✔
3908
                nOptions = GDAL_OF_RASTER;
2✔
3909

3910
            bool bJSON = EQUAL(papszArgv[iArg], "--drivers");
5✔
3911
            for (int i = 1; i < nArgc; i++)
10✔
3912
            {
3913
                if (strcmp(papszArgv[i], "-json") == 0 ||
7✔
3914
                    strcmp(papszArgv[i], "--json") == 0)
5✔
3915
                {
3916
                    bJSON = true;
2✔
3917
                    break;
2✔
3918
                }
3919
            }
3920

3921
            if (bJSON)
5✔
3922
            {
3923
                auto poDM = GetGDALDriverManager();
2✔
3924
                CPLJSONArray oArray;
2✔
3925
                const int nDriverCount = poDM->GetDriverCount();
2✔
3926
                for (int iDr = 0; iDr < nDriverCount; ++iDr)
440✔
3927
                {
3928
                    auto poDriver = poDM->GetDriver(iDr);
438✔
3929
                    CSLConstList papszMD = poDriver->GetMetadata();
438✔
3930

3931
                    if (nOptions == GDAL_OF_RASTER &&
657✔
3932
                        !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
219✔
3933
                        continue;
202✔
3934
                    if (nOptions == GDAL_OF_VECTOR &&
587✔
3935
                        !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
219✔
3936
                        continue;
132✔
3937
                    if (nOptions == GDAL_OF_GNM &&
236✔
3938
                        !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3939
                        continue;
×
3940
                    if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
236✔
3941
                        !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER,
×
3942
                                      false))
3943
                        continue;
×
3944

3945
                    CPLJSONObject oJDriver;
472✔
3946
                    oJDriver.Set("short_name", poDriver->GetDescription());
236✔
3947
                    if (const char *pszLongName =
236✔
3948
                            CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
236✔
3949
                        oJDriver.Set("long_name", pszLongName);
236✔
3950
                    CPLJSONArray oJScopes;
472✔
3951
                    if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
236✔
3952
                        oJScopes.Add("raster");
168✔
3953
                    if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
236✔
3954
                        oJScopes.Add("multidimensional_raster");
15✔
3955
                    if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
236✔
3956
                        oJScopes.Add("vector");
106✔
3957
                    oJDriver.Add("scopes", oJScopes);
236✔
3958
                    CPLJSONArray oJCaps;
472✔
3959
                    if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
236✔
3960
                        oJCaps.Add("open");
234✔
3961
                    if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
236✔
3962
                        oJCaps.Add("create");
98✔
3963
                    if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
236✔
3964
                        oJCaps.Add("create_copy");
63✔
3965
                    if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
236✔
3966
                        oJCaps.Add("update");
36✔
3967
                    if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
236✔
3968
                        oJCaps.Add("virtual_io");
193✔
3969
                    oJDriver.Add("capabilities", oJCaps);
236✔
3970

3971
                    if (const char *pszExtensions = CSLFetchNameValueDef(
236✔
3972
                            papszMD, GDAL_DMD_EXTENSIONS,
3973
                            CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3974
                    {
3975
                        const CPLStringList aosExt(
3976
                            CSLTokenizeString2(pszExtensions, " ", 0));
312✔
3977
                        CPLJSONArray oJExts;
156✔
3978
                        for (int i = 0; i < aosExt.size(); ++i)
368✔
3979
                        {
3980
                            oJExts.Add(aosExt[i]);
212✔
3981
                        }
3982
                        oJDriver.Add("file_extensions", oJExts);
156✔
3983
                    }
3984

3985
                    oArray.Add(oJDriver);
236✔
3986
                }
3987
                printf(/*ok*/
2✔
3988
                       "%s\n",
3989
                       oArray.Format(CPLJSONObject::PrettyFormat::Pretty)
4✔
3990
                           .c_str());
3991

3992
                return 0;
2✔
3993
            }
3994

3995
            printf(/*ok*/
3✔
3996
                   "Supported Formats: (ro:read-only, rw:read-write, "
3997
                   "+:write from scratch, u:update, "
3998
                   "v:virtual-I/O s:subdatasets)\n");
3999
            for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
660✔
4000
            {
4001
                GDALDriverH hDriver = GDALGetDriver(iDr);
657✔
4002

4003
                const char *pszRFlag = "", *pszWFlag, *pszVirtualIO,
657✔
4004
                           *pszSubdatasets;
4005
                CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
657✔
4006

4007
                if (nOptions == GDAL_OF_RASTER &&
876✔
4008
                    !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
219✔
4009
                    continue;
334✔
4010
                if (nOptions == GDAL_OF_VECTOR &&
1,025✔
4011
                    !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
438✔
4012
                    continue;
264✔
4013
                if (nOptions == GDAL_OF_GNM &&
323✔
4014
                    !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
4015
                    continue;
×
4016
                if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
323✔
4017
                    !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
×
4018
                    continue;
×
4019

4020
                if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
323✔
4021
                    pszRFlag = "r";
320✔
4022

4023
                if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
323✔
4024
                    pszWFlag = "w+";
152✔
4025
                else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
171✔
4026
                    pszWFlag = "w";
29✔
4027
                else
4028
                    pszWFlag = "o";
142✔
4029

4030
                const char *pszUpdate = "";
323✔
4031
                if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
323✔
4032
                    pszUpdate = "u";
55✔
4033

4034
                if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
323✔
4035
                    pszVirtualIO = "v";
254✔
4036
                else
4037
                    pszVirtualIO = "";
69✔
4038

4039
                if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
323✔
4040
                    pszSubdatasets = "s";
44✔
4041
                else
4042
                    pszSubdatasets = "";
279✔
4043

4044
                CPLString osKind;
646✔
4045
                if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
323✔
4046
                    osKind = "raster";
187✔
4047
                if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
323✔
4048
                {
4049
                    if (!osKind.empty())
18✔
4050
                        osKind += ',';
18✔
4051
                    osKind += "multidimensional raster";
18✔
4052
                }
4053
                if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
323✔
4054
                {
4055
                    if (!osKind.empty())
193✔
4056
                        osKind += ',';
57✔
4057
                    osKind += "vector";
193✔
4058
                }
4059
                if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
323✔
4060
                {
4061
                    if (!osKind.empty())
×
4062
                        osKind += ',';
×
4063
                    osKind += "geography network";
×
4064
                }
4065
                if (osKind.empty())
323✔
4066
                    osKind = "unknown kind";
×
4067

4068
                std::string osExtensions;
646✔
4069
                if (const char *pszExtensions = CSLFetchNameValueDef(
323✔
4070
                        papszMD, GDAL_DMD_EXTENSIONS,
4071
                        CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
4072
                {
4073
                    const CPLStringList aosExt(
4074
                        CSLTokenizeString2(pszExtensions, " ", 0));
432✔
4075
                    for (int i = 0; i < aosExt.size(); ++i)
522✔
4076
                    {
4077
                        if (i == 0)
306✔
4078
                            osExtensions = " (*.";
215✔
4079
                        else
4080
                            osExtensions += ", *.";
91✔
4081
                        osExtensions += aosExt[i];
306✔
4082
                    }
4083
                    if (!osExtensions.empty())
216✔
4084
                        osExtensions += ')';
215✔
4085
                }
4086

4087
                printf("  %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/
323✔
4088
                       GDALGetDriverShortName(hDriver), osKind.c_str(),
4089
                       pszRFlag, pszWFlag, pszUpdate, pszVirtualIO,
4090
                       pszSubdatasets, GDALGetDriverLongName(hDriver),
4091
                       osExtensions.c_str());
4092
            }
4093

4094
            return 0;
3✔
4095
        }
4096

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

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

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

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

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

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

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

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

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

4241
                    CPLDestroyXMLNode(psCOL);
5✔
4242

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

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

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

4259
                CPLDestroyXMLNode(psCOL);
3✔
4260

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

4265
            bool bFirstOtherOption = true;
3✔
4266
            for (char **papszIter = papszMD; papszIter && *papszIter;
118✔
4267
                 ++papszIter)
4268
            {
4269
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
115✔
4270
                    !STARTS_WITH(*papszIter, "DMD_") &&
52✔
4271
                    !STARTS_WITH(*papszIter, "DS_") &&
14✔
4272
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
12✔
4273
                {
4274
                    if (bFirstOtherOption)
12✔
4275
                        printf("  Other metadata items:\n"); /*ok*/
3✔
4276
                    bFirstOtherOption = false;
12✔
4277
                    printf("    %s\n", *papszIter); /*ok*/
12✔
4278
                }
4279
            }
4280

4281
            return 0;
3✔
4282
        }
4283

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

4315
            return 0;
2✔
4316
        }
4317

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

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

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

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

4354
    return nSize;
1,203✔
4355
}
4356

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

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

4364
{
4365
    char szFullKey[200];
4366

4367
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
1,660✔
4368

4369
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
1,660✔
4370

4371
    for (int i = 0; i < nCount; i++)
9,628✔
4372
        padfTarget[i] = dfDefault;
7,968✔
4373

4374
    if (pszValue == nullptr)
1,660✔
4375
        return false;
408✔
4376

4377
    if (nCount == 1)
1,252✔
4378
    {
4379
        *padfTarget = CPLAtofM(pszValue);
920✔
4380
        return true;
920✔
4381
    }
4382

4383
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
332✔
4384

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

4391
    for (int i = 0; i < nCount; i++)
6,972✔
4392
        padfTarget[i] = CPLAtofM(papszTokens[i]);
6,640✔
4393

4394
    CSLDestroy(papszTokens);
332✔
4395

4396
    return true;
332✔
4397
}
4398

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

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

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

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

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

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

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

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

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

4473
    return TRUE;
83✔
4474
}
4475

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

4480
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
9,838✔
4481
                                       GDALAccess eAccess,
4482
                                       GDALDataset *poDependentDS)
4483

4484
{
4485
    const char *pszAuxSuffixLC = "aux";
9,838✔
4486
    const char *pszAuxSuffixUC = "AUX";
9,838✔
4487

4488
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
9,838✔
4489
        return nullptr;
35✔
4490

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

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

4511
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
9,762✔
4512

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

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

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

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

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

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

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

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

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

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

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

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

4697
    return poODS;
9,762✔
4698
}
4699

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

4704
CPL_C_START
4705

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

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

4738
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
6,665✔
4739
{
4740
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
6,665✔
4741
    {
4742
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
6✔
4743
                 nBands);
4744
        return FALSE;
6✔
4745
    }
4746
    const char *pszMaxBandCount =
4747
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
6,659✔
4748
    /* coverity[tainted_data] */
4749
    int nMaxBands = atoi(pszMaxBandCount);
6,659✔
4750
    if (nBands > nMaxBands)
6,659✔
4751
    {
4752
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
4753
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4754
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4755
                 "legitimate number.",
4756
                 nBands, nMaxBands);
4757
        return FALSE;
2✔
4758
    }
4759
    return TRUE;
6,657✔
4760
}
4761

4762
CPL_C_END
4763

4764
/************************************************************************/
4765
/*                     GDALSerializeGCPListToXML()                      */
4766
/************************************************************************/
4767

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

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

4777
    CPLXMLNode *psLastChild = nullptr;
16✔
4778

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

4796
        psLastChild = psPamGCPList->psChild->psNext;
9✔
4797
    }
4798

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

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

4809
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
21,880✔
4810

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

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

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

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

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

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

4829
/************************************************************************/
4830
/*                     GDALDeserializeGCPListFromXML()                  */
4831
/************************************************************************/
4832

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

4842
        *ppoGCP_SRS = nullptr;
77✔
4843
        if (pszRawProj && pszRawProj[0])
77✔
4844
        {
4845
            *ppoGCP_SRS = new OGRSpatialReference();
61✔
4846
            (*ppoGCP_SRS)
4847
                ->SetFromUserInput(
61✔
4848
                    pszRawProj,
4849
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4850

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

4873
    asGCPs.clear();
85✔
4874
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
24,616✔
4875
         psXMLGCP = psXMLGCP->psNext)
24,531✔
4876
    {
4877
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
24,531✔
4878
            continue;
83✔
4879

4880
        gdal::GCP gcp;
48,896✔
4881
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
24,448✔
4882
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
24,448✔
4883

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

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

4931
        if (bOK)
24,448✔
4932
        {
4933
            asGCPs.emplace_back(std::move(gcp));
24,448✔
4934
        }
4935
    }
4936
}
85✔
4937

4938
/************************************************************************/
4939
/*                   GDALSerializeOpenOptionsToXML()                    */
4940
/************************************************************************/
4941

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

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

4958
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
5✔
4959

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

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

4970
            CPLFree(pszKey);
5✔
4971
        }
4972
    }
4973
}
2,615✔
4974

4975
/************************************************************************/
4976
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4977
/************************************************************************/
4978

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

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

5007
/************************************************************************/
5008
/*                    GDALRasterIOGetResampleAlg()                      */
5009
/************************************************************************/
5010

5011
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
2,244✔
5012
{
5013
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
2,244✔
5014
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
2,244✔
5015
        eResampleAlg = GRIORA_NearestNeighbour;
19✔
5016
    else if (EQUAL(pszResampling, "BILINEAR"))
2,225✔
5017
        eResampleAlg = GRIORA_Bilinear;
2,054✔
5018
    else if (EQUAL(pszResampling, "CUBIC"))
171✔
5019
        eResampleAlg = GRIORA_Cubic;
116✔
5020
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
55✔
5021
        eResampleAlg = GRIORA_CubicSpline;
4✔
5022
    else if (EQUAL(pszResampling, "LANCZOS"))
51✔
5023
        eResampleAlg = GRIORA_Lanczos;
1✔
5024
    else if (EQUAL(pszResampling, "AVERAGE"))
50✔
5025
        eResampleAlg = GRIORA_Average;
43✔
5026
    else if (EQUAL(pszResampling, "RMS"))
7✔
5027
        eResampleAlg = GRIORA_RMS;
1✔
5028
    else if (EQUAL(pszResampling, "MODE"))
6✔
5029
        eResampleAlg = GRIORA_Mode;
5✔
5030
    else if (EQUAL(pszResampling, "GAUSS"))
1✔
5031
        eResampleAlg = GRIORA_Gauss;
1✔
5032
    else
5033
        CPLError(CE_Warning, CPLE_NotSupported,
×
5034
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
5035
    return eResampleAlg;
2,244✔
5036
}
5037

5038
/************************************************************************/
5039
/*                    GDALRasterIOGetResampleAlgStr()                   */
5040
/************************************************************************/
5041

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

5070
/************************************************************************/
5071
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
5072
/************************************************************************/
5073

5074
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
4,468,630✔
5075
                                        int nXSize, int nYSize, int nBufXSize,
5076
                                        int nBufYSize)
5077
{
5078
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
4,468,630✔
5079
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
370,283✔
5080
    {
5081
        const char *pszResampling =
5082
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
367,923✔
5083
        if (pszResampling != nullptr)
367,923✔
5084
        {
5085
            psExtraArg->eResampleAlg =
1✔
5086
                GDALRasterIOGetResampleAlg(pszResampling);
1✔
5087
        }
5088
    }
5089
}
4,468,630✔
5090

5091
/************************************************************************/
5092
/*                     GDALCanFileAcceptSidecarFile()                   */
5093
/************************************************************************/
5094

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

5105
/************************************************************************/
5106
/*                   GDALCanReliablyUseSiblingFileList()                */
5107
/************************************************************************/
5108

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

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

5163
/************************************************************************/
5164
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
5165
/************************************************************************/
5166

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

5177
/************************************************************************/
5178
/*                        GDALCopyNoDataValue()                         */
5179
/************************************************************************/
5180

5181
void GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand)
2,665✔
5182
{
5183

5184
    int bSuccess;
5185
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
2,665✔
5186
    const auto eDstDataType = poDstBand->GetRasterDataType();
2,665✔
5187
    if (eSrcDataType == GDT_Int64)
2,665✔
5188
    {
5189
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
3✔
5190
        if (bSuccess)
3✔
5191
        {
5192
            if (eDstDataType == GDT_Int64)
3✔
5193
            {
5194
                poDstBand->SetNoDataValueAsInt64(nNoData);
3✔
5195
            }
5196
            else if (eDstDataType == GDT_UInt64)
×
5197
            {
5198
                if (nNoData >= 0)
×
5199
                    poDstBand->SetNoDataValueAsUInt64(
×
5200
                        static_cast<uint64_t>(nNoData));
×
5201
            }
5202
            else if (nNoData ==
×
5203
                     static_cast<int64_t>(static_cast<double>(nNoData)))
×
5204
            {
5205
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
5206
            }
5207
        }
5208
    }
5209
    else if (eSrcDataType == GDT_UInt64)
2,662✔
5210
    {
5211
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
3✔
5212
        if (bSuccess)
3✔
5213
        {
5214
            if (eDstDataType == GDT_UInt64)
3✔
5215
            {
5216
                poDstBand->SetNoDataValueAsUInt64(nNoData);
3✔
5217
            }
5218
            else if (eDstDataType == GDT_Int64)
×
5219
            {
5220
                if (nNoData <
×
5221
                    static_cast<uint64_t>(cpl::NumericLimits<int64_t>::max()))
×
5222
                {
5223
                    poDstBand->SetNoDataValueAsInt64(
×
5224
                        static_cast<int64_t>(nNoData));
×
5225
                }
5226
            }
5227
            else if (nNoData ==
×
5228
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
×
5229
            {
5230
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
5231
            }
5232
        }
5233
    }
5234
    else
5235
    {
5236
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2,659✔
5237
        if (bSuccess)
2,659✔
5238
        {
5239
            if (eDstDataType == GDT_Int64)
184✔
5240
            {
5241
                if (dfNoData >= static_cast<double>(
×
5242
                                    cpl::NumericLimits<int64_t>::lowest()) &&
×
5243
                    dfNoData <= static_cast<double>(
×
5244
                                    cpl::NumericLimits<int64_t>::max()) &&
×
5245
                    dfNoData ==
5246
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
×
5247
                {
5248
                    poDstBand->SetNoDataValueAsInt64(
×
5249
                        static_cast<int64_t>(dfNoData));
×
5250
                }
5251
            }
5252
            else if (eDstDataType == GDT_UInt64)
184✔
5253
            {
5254
                if (dfNoData >= static_cast<double>(
×
5255
                                    cpl::NumericLimits<uint64_t>::lowest()) &&
×
5256
                    dfNoData <= static_cast<double>(
×
5257
                                    cpl::NumericLimits<uint64_t>::max()) &&
×
5258
                    dfNoData ==
5259
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
×
5260
                {
5261
                    poDstBand->SetNoDataValueAsInt64(
×
5262
                        static_cast<uint64_t>(dfNoData));
×
5263
                }
5264
            }
5265
            else
5266
            {
5267
                poDstBand->SetNoDataValue(dfNoData);
184✔
5268
            }
5269
        }
5270
    }
5271
}
2,665✔
5272

5273
/************************************************************************/
5274
/*                     GDALGetNoDataValueCastToDouble()                 */
5275
/************************************************************************/
5276

5277
double GDALGetNoDataValueCastToDouble(int64_t nVal)
2✔
5278
{
5279
    const double dfVal = static_cast<double>(nVal);
2✔
5280
    if (static_cast<int64_t>(dfVal) != nVal)
2✔
5281
    {
5282
        CPLError(CE_Warning, CPLE_AppDefined,
×
5283
                 "GetNoDataValue() returns an approximate value of the "
5284
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5285
                 "GetNoDataValueAsInt64() instead",
5286
                 static_cast<GIntBig>(nVal));
5287
    }
5288
    return dfVal;
2✔
5289
}
5290

5291
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
2✔
5292
{
5293
    const double dfVal = static_cast<double>(nVal);
2✔
5294
    if (static_cast<uint64_t>(dfVal) != nVal)
2✔
5295
    {
5296
        CPLError(CE_Warning, CPLE_AppDefined,
×
5297
                 "GetNoDataValue() returns an approximate value of the "
5298
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5299
                 "GetNoDataValueAsUInt64() instead",
5300
                 static_cast<GUIntBig>(nVal));
5301
    }
5302
    return dfVal;
2✔
5303
}
5304

5305
/************************************************************************/
5306
/*                GDALGetCompressionFormatForJPEG()                     */
5307
/************************************************************************/
5308

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

5494
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
16✔
5495
                                            size_t nBufferSize)
5496
{
5497
    VSILFILE *fp = VSIFileFromMemBuffer(
16✔
5498
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5499
        false);
5500
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
16✔
5501
    VSIFCloseL(fp);
16✔
5502
    return osRet;
16✔
5503
}
5504

5505
//! @endcond
5506

5507
/************************************************************************/
5508
/*                      GDALGetNoDataReplacementValue()                 */
5509
/************************************************************************/
5510

5511
/**
5512
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5513
 *        is out of range for the specified data type (dt).
5514
 *        For UInt64 and Int64 data type this function cannot reliably trusted
5515
 *        because their nodata values might not always be representable exactly
5516
 *        as a double, in particular the maximum absolute value for those types
5517
 *        is 2^53.
5518
 *
5519
 * The replacement value is a value that can be used in a computation
5520
 * whose result would match by accident the nodata value, whereas it is
5521
 * meant to be valid. For example, for a dataset with a nodata value of 0,
5522
 * when averaging -1 and 1, one would get normally a value of 0. The
5523
 * replacement nodata value can then be substituted to that 0 value to still
5524
 * get a valid value, as close as practical to the true value, while being
5525
 * different from the nodata value.
5526
 *
5527
 * @param dt Data type
5528
 * @param dfNoDataValue The no data value
5529

5530
 * @since GDAL 3.9
5531
 */
5532
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
235✔
5533
{
5534

5535
    // The logic here is to check if the value is out of range for the
5536
    // specified data type and return a replacement value if it is, return
5537
    // 0 otherwise.
5538
    double dfReplacementVal = dfNoDataValue;
235✔
5539
    if (dt == GDT_Byte)
235✔
5540
    {
5541
        if (GDALClampDoubleValue(dfNoDataValue,
63✔
5542
                                 cpl::NumericLimits<uint8_t>::lowest(),
63✔
5543
                                 cpl::NumericLimits<uint8_t>::max()))
63✔
5544
        {
5545
            return 0;
2✔
5546
        }
5547
        if (dfNoDataValue == cpl::NumericLimits<unsigned char>::max())
61✔
5548
            dfReplacementVal = cpl::NumericLimits<unsigned char>::max() - 1;
3✔
5549
        else
5550
            dfReplacementVal = dfNoDataValue + 1;
58✔
5551
    }
5552
    else if (dt == GDT_Int8)
172✔
5553
    {
5554
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5555
                                 cpl::NumericLimits<int8_t>::lowest(),
5✔
5556
                                 cpl::NumericLimits<int8_t>::max()))
5✔
5557
        {
5558
            return 0;
2✔
5559
        }
5560
        if (dfNoDataValue == cpl::NumericLimits<GInt8>::max())
3✔
5561
            dfReplacementVal = cpl::NumericLimits<GInt8>::max() - 1;
1✔
5562
        else
5563
            dfReplacementVal = dfNoDataValue + 1;
2✔
5564
    }
5565
    else if (dt == GDT_UInt16)
167✔
5566
    {
5567
        if (GDALClampDoubleValue(dfNoDataValue,
6✔
5568
                                 cpl::NumericLimits<uint16_t>::lowest(),
6✔
5569
                                 cpl::NumericLimits<uint16_t>::max()))
6✔
5570
        {
5571
            return 0;
2✔
5572
        }
5573
        if (dfNoDataValue == cpl::NumericLimits<GUInt16>::max())
4✔
5574
            dfReplacementVal = cpl::NumericLimits<GUInt16>::max() - 1;
1✔
5575
        else
5576
            dfReplacementVal = dfNoDataValue + 1;
3✔
5577
    }
5578
    else if (dt == GDT_Int16)
161✔
5579
    {
5580
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5581
                                 cpl::NumericLimits<int16_t>::lowest(),
5✔
5582
                                 cpl::NumericLimits<int16_t>::max()))
5✔
5583
        {
5584
            return 0;
2✔
5585
        }
5586
        if (dfNoDataValue == cpl::NumericLimits<GInt16>::max())
3✔
5587
            dfReplacementVal = cpl::NumericLimits<GInt16>::max() - 1;
1✔
5588
        else
5589
            dfReplacementVal = dfNoDataValue + 1;
2✔
5590
    }
5591
    else if (dt == GDT_UInt32)
156✔
5592
    {
5593
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5594
                                 cpl::NumericLimits<uint32_t>::lowest(),
5595
                                 cpl::NumericLimits<uint32_t>::max()))
5596
        {
5597
            return 0;
2✔
5598
        }
5599
        if (dfNoDataValue == cpl::NumericLimits<GUInt32>::max())
3✔
5600
            dfReplacementVal = cpl::NumericLimits<GUInt32>::max() - 1;
1✔
5601
        else
5602
            dfReplacementVal = dfNoDataValue + 1;
2✔
5603
    }
5604
    else if (dt == GDT_Int32)
151✔
5605
    {
5606
        if (GDALClampDoubleValue(dfNoDataValue,
7✔
5607
                                 cpl::NumericLimits<int32_t>::lowest(),
5608
                                 cpl::NumericLimits<int32_t>::max()))
5609
        {
5610
            return 0;
2✔
5611
        }
5612
        if (dfNoDataValue == cpl::NumericLimits<int32_t>::max())
5✔
5613
            dfReplacementVal = cpl::NumericLimits<int32_t>::max() - 1;
1✔
5614
        else
5615
            dfReplacementVal = dfNoDataValue + 1;
4✔
5616
    }
5617
    else if (dt == GDT_UInt64)
144✔
5618
    {
5619
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5620
        // so we take the next lower value representable as a double 18446744073709549567
5621
        static const double dfMaxUInt64Value{
5622
            std::nextafter(
5623
                static_cast<double>(cpl::NumericLimits<uint64_t>::max()), 0) -
5624
            1};
5625

5626
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5627
                                 cpl::NumericLimits<uint64_t>::lowest(),
5628
                                 cpl::NumericLimits<uint64_t>::max()))
5629
        {
5630
            return 0;
2✔
5631
        }
5632

5633
        if (dfNoDataValue >=
3✔
5634
            static_cast<double>(cpl::NumericLimits<uint64_t>::max()))
3✔
5635
            dfReplacementVal = dfMaxUInt64Value;
1✔
5636
        else
5637
            dfReplacementVal = dfNoDataValue + 1;
2✔
5638
    }
5639
    else if (dt == GDT_Int64)
139✔
5640
    {
5641
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5642
        // so we take the next lower value representable as a double 9223372036854774784
5643
        static const double dfMaxInt64Value{
5644
            std::nextafter(
5645
                static_cast<double>(cpl::NumericLimits<int64_t>::max()), 0) -
5646
            1};
5647

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

5655
        if (dfNoDataValue >=
3✔
5656
            static_cast<double>(cpl::NumericLimits<int64_t>::max()))
3✔
5657
            dfReplacementVal = dfMaxInt64Value;
1✔
5658
        else
5659
            dfReplacementVal = dfNoDataValue + 1;
2✔
5660
    }
5661
    else if (dt == GDT_Float16)
134✔
5662
    {
5663

5664
        if (GDALClampDoubleValue(dfNoDataValue,
8✔
5665
                                 cpl::NumericLimits<GFloat16>::lowest(),
5666
                                 cpl::NumericLimits<GFloat16>::max()))
5667
        {
5668
            return 0;
4✔
5669
        }
5670

5671
        if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max())
4✔
5672
        {
5673
            using std::nextafter;
5674
            dfReplacementVal =
5675
                nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f));
1✔
5676
        }
5677
        else
5678
        {
5679
            using std::nextafter;
5680
            dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue),
×
5681
                                         cpl::NumericLimits<GFloat16>::max());
3✔
5682
        }
5683
    }
5684
    else if (dt == GDT_Float32)
126✔
5685
    {
5686

5687
        if (GDALClampDoubleValue(dfNoDataValue,
33✔
5688
                                 cpl::NumericLimits<float>::lowest(),
5689
                                 cpl::NumericLimits<float>::max()))
5690
        {
5691
            return 0;
4✔
5692
        }
5693

5694
        if (dfNoDataValue == cpl::NumericLimits<float>::max())
29✔
5695
        {
5696
            dfReplacementVal =
1✔
5697
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
1✔
5698
        }
5699
        else
5700
        {
5701
            dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue),
28✔
5702
                                              cpl::NumericLimits<float>::max());
5703
        }
5704
    }
5705
    else if (dt == GDT_Float64)
93✔
5706
    {
5707
        if (GDALClampDoubleValue(dfNoDataValue,
93✔
5708
                                 cpl::NumericLimits<double>::lowest(),
5709
                                 cpl::NumericLimits<double>::max()))
5710
        {
5711
            return 0;
2✔
5712
        }
5713

5714
        if (dfNoDataValue == cpl::NumericLimits<double>::max())
91✔
5715
        {
5716
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0);
2✔
5717
        }
5718
        else
5719
        {
5720
            dfReplacementVal = std::nextafter(
89✔
5721
                dfNoDataValue, cpl::NumericLimits<double>::max());
5722
        }
5723
    }
5724

5725
    return dfReplacementVal;
209✔
5726
}
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