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

OSGeo / gdal / 8872387746

29 Apr 2024 02:16AM UTC coverage: 69.076% (+0.003%) from 69.073%
8872387746

Pull #9801

github

web-flow
Bump actions/upload-artifact from 4.3.2 to 4.3.3

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.2 to 4.3.3.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/1746f4ab6...65462800f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #9801: Bump actions/upload-artifact from 4.3.2 to 4.3.3

534153 of 773282 relevant lines covered (69.08%)

205719.18 hits per line

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

75.62
/gcore/gdal_misc.cpp
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Free standing functions for GDAL.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included
19
 * in all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29

30
#include "cpl_port.h"
31

32
#include <cctype>
33
#include <cerrno>
34
#include <clocale>
35
#include <cmath>
36
#include <cstddef>
37
#include <cstdio>
38
#include <cstdlib>
39
#include <cstring>
40
#include <fcntl.h>
41

42
#include <algorithm>
43
#include <iostream>
44
#include <limits>
45
#include <string>
46

47
#include "cpl_conv.h"
48
#include "cpl_error.h"
49
#include "cpl_minixml.h"
50
#include "cpl_multiproc.h"
51
#include "cpl_string.h"
52
#include "cpl_vsi.h"
53
#include "gdal_version_full/gdal_version.h"
54
#include "gdal.h"
55
#include "gdal_mdreader.h"
56
#include "gdal_priv.h"
57
#include "gdal_priv_templates.hpp"
58
#include "ogr_core.h"
59
#include "ogr_spatialref.h"
60
#include "ogr_geos.h"
61

62
#include "proj.h"
63

64
#ifdef HAVE_CURL
65
#include "cpl_curl_priv.h"
66
#endif
67

68
static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[],
3,357✔
69
                             const int panBits[])
70
{
71
    if (pabFloating[0] != pabFloating[1])
3,357✔
72
    {
73
        const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
371✔
74
        const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
371✔
75

76
        return std::max(panBits[nFloatingTypeIndex],
371✔
77
                        2 * panBits[nNotFloatingTypeIndex]);
371✔
78
    }
79

80
    if (pabSigned[0] != pabSigned[1])
2,986✔
81
    {
82
        const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
428✔
83
        const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
428✔
84

85
        return std::max(panBits[nSignedTypeIndex],
428✔
86
                        2 * panBits[nUnsignedTypeIndex]);
428✔
87
    }
88

89
    return std::max(panBits[0], panBits[1]);
2,558✔
90
}
91

92
static int GetDataTypeElementSizeBits(GDALDataType eDataType)
6,714✔
93
{
94
    switch (eDataType)
6,714✔
95
    {
96
        case GDT_Byte:
4,286✔
97
        case GDT_Int8:
98
            return 8;
4,286✔
99

100
        case GDT_UInt16:
1,151✔
101
        case GDT_Int16:
102
        case GDT_CInt16:
103
            return 16;
1,151✔
104

105
        case GDT_UInt32:
876✔
106
        case GDT_Int32:
107
        case GDT_Float32:
108
        case GDT_CInt32:
109
        case GDT_CFloat32:
110
            return 32;
876✔
111

112
        case GDT_Float64:
401✔
113
        case GDT_CFloat64:
114
        case GDT_UInt64:
115
        case GDT_Int64:
116
            return 64;
401✔
117

118
        case GDT_Unknown:
×
119
        case GDT_TypeCount:
120
            break;
×
121
    }
122
    return 0;
×
123
}
124

125
/************************************************************************/
126
/*                         GDALDataTypeUnion()                          */
127
/************************************************************************/
128

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

139
GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
3,357✔
140
                                           GDALDataType eType2)
141

142
{
143
    const int panBits[] = {GetDataTypeElementSizeBits(eType1),
3,357✔
144
                           GetDataTypeElementSizeBits(eType2)};
3,357✔
145

146
    if (panBits[0] == 0 || panBits[1] == 0)
3,357✔
147
        return GDT_Unknown;
×
148

149
    const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
3,357✔
150
                              CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
3,357✔
151

152
    const bool bSigned = pabSigned[0] || pabSigned[1];
3,357✔
153
    const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
3,357✔
154
                                CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
3,357✔
155
    const bool bFloating = pabFloating[0] || pabFloating[1];
3,357✔
156
    const bool bComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
6,339✔
157
                          CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
2,982✔
158

159
    const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
3,357✔
160

161
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
3,357✔
162
}
163

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

168
/**
169
 * \brief Union a data type with the one found for a value
170
 *
171
 * @param eDT the first data type
172
 * @param dValue the value for which to find a data type and union with eDT
173
 * @param bComplex if the value is complex
174
 *
175
 * @return a data type able to express eDT and dValue.
176
 * @since GDAL 2.3
177
 */
178
GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
248✔
179
                                                    double dValue, int bComplex)
180
{
181
    if (eDT == GDT_Float32 && !bComplex && static_cast<float>(dValue) == dValue)
248✔
182
        return eDT;
47✔
183

184
    const GDALDataType eDT2 = GDALFindDataTypeForValue(dValue, bComplex);
201✔
185
    return GDALDataTypeUnion(eDT, eDT2);
201✔
186
}
187

188
/************************************************************************/
189
/*                        GetMinBitsForValue()                          */
190
/************************************************************************/
191
static int GetMinBitsForValue(double dValue)
201✔
192
{
193
    if (round(dValue) == dValue)
201✔
194
    {
195
        if (dValue <= std::numeric_limits<GByte>::max() &&
374✔
196
            dValue >= std::numeric_limits<GByte>::min())
184✔
197
            return 8;
135✔
198

199
        if (dValue <= std::numeric_limits<GInt8>::max() &&
104✔
200
            dValue >= std::numeric_limits<GInt8>::min())
49✔
201
            return 8;
3✔
202

203
        if (dValue <= std::numeric_limits<GInt16>::max() &&
100✔
204
            dValue >= std::numeric_limits<GInt16>::min())
48✔
205
            return 16;
47✔
206

207
        if (dValue <= std::numeric_limits<GUInt16>::max() &&
10✔
208
            dValue >= std::numeric_limits<GUInt16>::min())
5✔
209
            return 16;
4✔
210

211
        if (dValue <= std::numeric_limits<GInt32>::max() &&
2✔
212
            dValue >= std::numeric_limits<GInt32>::min())
1✔
213
            return 32;
1✔
214

215
        if (dValue <= std::numeric_limits<GUInt32>::max() &&
×
216
            dValue >= std::numeric_limits<GUInt32>::min())
×
217
            return 32;
×
218

219
        if (dValue <= static_cast<double>(
×
220
                          std::numeric_limits<std::uint64_t>::max()) &&
×
221
            dValue >=
222
                static_cast<double>(std::numeric_limits<std::uint64_t>::min()))
×
223
            return 64;
×
224
    }
225
    else if (static_cast<float>(dValue) == dValue)
11✔
226
    {
227
        return 32;
4✔
228
    }
229

230
    return 64;
7✔
231
}
232

233
/************************************************************************/
234
/*                        GDALFindDataType()                            */
235
/************************************************************************/
236

237
/**
238
 * \brief Finds the smallest data type able to support the given
239
 *  requirements
240
 *
241
 * @param nBits number of bits necessary
242
 * @param bSigned if negative values are necessary
243
 * @param bFloating if non-integer values necessary
244
 * @param bComplex if complex values are necessary
245
 *
246
 * @return a best fit GDALDataType for supporting the requirements
247
 * @since GDAL 2.3
248
 */
249
GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
3,576✔
250
                                          int bComplex)
251
{
252
    if (bComplex)
3,576✔
253
    {
254
        nBits = std::max(nBits, !bSigned ? 32 : 16);
723✔
255
    }  // we don't have complex unsigned data types, so for a complex uint16,
256
       // promote to complex int32
257
    if (bFloating)
3,576✔
258
    {
259
        nBits = std::max(nBits, 32);
545✔
260
    }
261

262
    if (nBits <= 8)
3,576✔
263
    {
264
        return bSigned ? GDT_Int8 : GDT_Byte;
1,950✔
265
    }
266

267
    if (nBits <= 16)
1,626✔
268
    {
269
        if (bComplex)
712✔
270
            return GDT_CInt16;
358✔
271
        if (bSigned)
354✔
272
            return GDT_Int16;
244✔
273
        return GDT_UInt16;
110✔
274
    }
275

276
    if (nBits <= 32)
914✔
277
    {
278
        if (bFloating)
521✔
279
        {
280
            if (bComplex)
253✔
281
                return GDT_CFloat32;
95✔
282
            return GDT_Float32;
158✔
283
        }
284

285
        if (bComplex)
268✔
286
            return GDT_CInt32;
93✔
287
        if (bSigned)
175✔
288
            return GDT_Int32;
116✔
289
        return GDT_UInt32;
59✔
290
    }
291

292
    if (nBits == 64 && !bFloating && !bComplex)
393✔
293
        return bSigned ? GDT_Int64 : GDT_UInt64;
57✔
294

295
    if (bComplex)
336✔
296
        return GDT_CFloat64;
177✔
297

298
    return GDT_Float64;
159✔
299
}
300

301
/************************************************************************/
302
/*                        GDALFindDataTypeForValue()                    */
303
/************************************************************************/
304

305
/**
306
 * \brief Finds the smallest data type able to support the provided value
307
 *
308
 * @param dValue value to support
309
 * @param bComplex is the value complex
310
 *
311
 * @return a best fit GDALDataType for supporting the value
312
 * @since GDAL 2.3
313
 */
314
GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
201✔
315
{
316
    const bool bFloating = round(dValue) != dValue;
201✔
317
    const bool bSigned = bFloating || dValue < 0;
201✔
318
    const int nBits = GetMinBitsForValue(dValue);
201✔
319

320
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
201✔
321
}
322

323
/************************************************************************/
324
/*                        GDALGetDataTypeSizeBytes()                    */
325
/************************************************************************/
326

327
/**
328
 * \brief Get data type size in <b>bytes</b>.
329
 *
330
 * Returns the size of a GDT_* type in bytes.  In contrast,
331
 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
332
 *
333
 * @param eDataType type, such as GDT_Byte.
334
 * @return the number of bytes or zero if it is not recognised.
335
 */
336

337
int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
232,610,000✔
338

339
{
340
    switch (eDataType)
232,610,000✔
341
    {
342
        case GDT_Byte:
63,332,700✔
343
        case GDT_Int8:
344
            return 1;
63,332,700✔
345

346
        case GDT_UInt16:
51,940,700✔
347
        case GDT_Int16:
348
            return 2;
51,940,700✔
349

350
        case GDT_UInt32:
80,572,700✔
351
        case GDT_Int32:
352
        case GDT_Float32:
353
        case GDT_CInt16:
354
            return 4;
80,572,700✔
355

356
        case GDT_Float64:
36,400,700✔
357
        case GDT_CInt32:
358
        case GDT_CFloat32:
359
        case GDT_UInt64:
360
        case GDT_Int64:
361
            return 8;
36,400,700✔
362

363
        case GDT_CFloat64:
347,727✔
364
            return 16;
347,727✔
365

366
        case GDT_Unknown:
13,580✔
367
        case GDT_TypeCount:
368
            break;
13,580✔
369
    }
370
    return 0;
15,213✔
371
}
372

373
/************************************************************************/
374
/*                        GDALGetDataTypeSizeBits()                     */
375
/************************************************************************/
376

377
/**
378
 * \brief Get data type size in <b>bits</b>.
379
 *
380
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!  Use
381
 * GDALGetDataTypeSizeBytes() for bytes.
382
 *
383
 * @param eDataType type, such as GDT_Byte.
384
 * @return the number of bits or zero if it is not recognised.
385
 */
386

387
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
8,915✔
388

389
{
390
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
8,915✔
391
}
392

393
/************************************************************************/
394
/*                        GDALGetDataTypeSize()                         */
395
/************************************************************************/
396

397
/**
398
 * \brief Get data type size in bits.  <b>Deprecated</b>.
399
 *
400
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
401
 *
402
 * Use GDALGetDataTypeSizeBytes() for bytes.
403
 * Use GDALGetDataTypeSizeBits() for bits.
404
 *
405
 * @param eDataType type, such as GDT_Byte.
406
 * @return the number of bits or zero if it is not recognised.
407
 */
408

409
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
1,195,250✔
410

411
{
412
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
1,195,250✔
413
}
414

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

419
/**
420
 * \brief Is data type complex?
421
 *
422
 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
423
 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
424
 * component.
425
 */
426

427
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
622,617✔
428

429
{
430
    switch (eDataType)
622,617✔
431
    {
432
        case GDT_CInt16:
7,317✔
433
        case GDT_CInt32:
434
        case GDT_CFloat32:
435
        case GDT_CFloat64:
436
            return TRUE;
7,317✔
437

438
        case GDT_Byte:
615,296✔
439
        case GDT_Int8:
440
        case GDT_Int16:
441
        case GDT_UInt16:
442
        case GDT_Int32:
443
        case GDT_UInt32:
444
        case GDT_Int64:
445
        case GDT_UInt64:
446
        case GDT_Float32:
447
        case GDT_Float64:
448
            return FALSE;
615,296✔
449

450
        case GDT_Unknown:
6✔
451
        case GDT_TypeCount:
452
            break;
6✔
453
    }
454
    return FALSE;
4✔
455
}
456

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

461
/**
462
 * \brief Is data type floating? (might be complex)
463
 *
464
 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float64,
465
 * GDT_CFloat32, GDT_CFloat64)
466
 * @since GDAL 2.3
467
 */
468

469
int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
46,340✔
470
{
471
    switch (eDataType)
46,340✔
472
    {
473
        case GDT_Float32:
974✔
474
        case GDT_Float64:
475
        case GDT_CFloat32:
476
        case GDT_CFloat64:
477
            return TRUE;
974✔
478

479
        case GDT_Byte:
45,365✔
480
        case GDT_Int8:
481
        case GDT_Int16:
482
        case GDT_UInt16:
483
        case GDT_Int32:
484
        case GDT_UInt32:
485
        case GDT_Int64:
486
        case GDT_UInt64:
487
        case GDT_CInt16:
488
        case GDT_CInt32:
489
            return FALSE;
45,365✔
490

491
        case GDT_Unknown:
1✔
492
        case GDT_TypeCount:
493
            break;
1✔
494
    }
495
    return FALSE;
1✔
496
}
497

498
/************************************************************************/
499
/*                       GDALDataTypeIsInteger()                        */
500
/************************************************************************/
501

502
/**
503
 * \brief Is data type integer? (might be complex)
504
 *
505
 * @return TRUE if the passed type is integer (one of GDT_Byte, GDT_Int16,
506
 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
507
 * @since GDAL 2.3
508
 */
509

510
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
43,962✔
511

512
{
513
    switch (eDataType)
43,962✔
514
    {
515
        case GDT_Byte:
43,043✔
516
        case GDT_Int8:
517
        case GDT_Int16:
518
        case GDT_UInt16:
519
        case GDT_Int32:
520
        case GDT_UInt32:
521
        case GDT_CInt16:
522
        case GDT_CInt32:
523
        case GDT_UInt64:
524
        case GDT_Int64:
525
            return TRUE;
43,043✔
526

527
        case GDT_Float32:
920✔
528
        case GDT_Float64:
529
        case GDT_CFloat32:
530
        case GDT_CFloat64:
531
            return FALSE;
920✔
532

533
        case GDT_Unknown:
1✔
534
        case GDT_TypeCount:
535
            break;
1✔
536
    }
537
    return FALSE;
×
538
}
539

540
/************************************************************************/
541
/*                       GDALDataTypeIsSigned()                         */
542
/************************************************************************/
543

544
/**
545
 * \brief Is data type signed?
546
 *
547
 * @return TRUE if the passed type is signed.
548
 * @since GDAL 2.3
549
 */
550

551
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
84,915✔
552
{
553
    switch (eDataType)
84,915✔
554
    {
555
        case GDT_Byte:
82,076✔
556
        case GDT_UInt16:
557
        case GDT_UInt32:
558
        case GDT_UInt64:
559
            return FALSE;
82,076✔
560

561
        case GDT_Int8:
2,839✔
562
        case GDT_Int16:
563
        case GDT_Int32:
564
        case GDT_Int64:
565
        case GDT_Float32:
566
        case GDT_Float64:
567
        case GDT_CInt16:
568
        case GDT_CInt32:
569
        case GDT_CFloat32:
570
        case GDT_CFloat64:
571
            return TRUE;
2,839✔
572

573
        case GDT_Unknown:
×
574
        case GDT_TypeCount:
575
            break;
×
576
    }
577
    return FALSE;
×
578
}
579

580
/************************************************************************/
581
/*                    GDALDataTypeIsConversionLossy()                   */
582
/************************************************************************/
583

584
/**
585
 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
586
 *
587
 * @param eTypeFrom input datatype
588
 * @param eTypeTo output datatype
589
 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
590
 * @since GDAL 2.3
591
 */
592

593
int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
39,378✔
594
                                              GDALDataType eTypeTo)
595
{
596
    // E.g cfloat32 -> float32
597
    if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
39,378✔
598
        return TRUE;
36✔
599

600
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
39,342✔
601
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
39,342✔
602

603
    if (GDALDataTypeIsInteger(eTypeTo))
39,342✔
604
    {
605
        // E.g. float32 -> int32
606
        if (GDALDataTypeIsFloating(eTypeFrom))
38,856✔
607
            return TRUE;
28✔
608

609
        // E.g. Int16 to UInt16
610
        const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
38,828✔
611
        const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
38,828✔
612
        if (bIsFromSigned && !bIsToSigned)
38,828✔
613
            return TRUE;
46✔
614

615
        // E.g UInt32 to UInt16
616
        const int nFromSize = GDALGetDataTypeSize(eTypeFrom);
38,782✔
617
        const int nToSize = GDALGetDataTypeSize(eTypeTo);
38,782✔
618
        if (nFromSize > nToSize)
38,782✔
619
            return TRUE;
30✔
620

621
        // E.g UInt16 to Int16
622
        if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
38,752✔
623
            return TRUE;
15✔
624

625
        return FALSE;
38,737✔
626
    }
627

628
    if (eTypeTo == GDT_Float32 &&
486✔
629
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
390✔
630
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
386✔
631
         eTypeFrom == GDT_Float64))
632
    {
633
        return TRUE;
35✔
634
    }
635

636
    if (eTypeTo == GDT_Float64 &&
451✔
637
        (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
71✔
638
    {
639
        return TRUE;
4✔
640
    }
641

642
    return FALSE;
447✔
643
}
644

645
/************************************************************************/
646
/*                        GDALGetDataTypeName()                         */
647
/************************************************************************/
648

649
/**
650
 * \brief Get name of data type.
651
 *
652
 * Returns a symbolic name for the data type.  This is essentially the
653
 * the enumerated item name with the GDT_ prefix removed.  So GDT_Byte returns
654
 * "Byte".  The returned strings are static strings and should not be modified
655
 * or freed by the application.  These strings are useful for reporting
656
 * datatypes in debug statements, errors and other user output.
657
 *
658
 * @param eDataType type to get name of.
659
 * @return string corresponding to existing data type
660
 *         or NULL pointer if invalid type given.
661
 */
662

663
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
57,852✔
664

665
{
666
    switch (eDataType)
57,852✔
667
    {
668
        case GDT_Unknown:
4,581✔
669
            return "Unknown";
4,581✔
670

671
        case GDT_Byte:
25,153✔
672
            return "Byte";
25,153✔
673

674
        case GDT_Int8:
560✔
675
            return "Int8";
560✔
676

677
        case GDT_UInt16:
4,786✔
678
            return "UInt16";
4,786✔
679

680
        case GDT_Int16:
4,934✔
681
            return "Int16";
4,934✔
682

683
        case GDT_UInt32:
3,615✔
684
            return "UInt32";
3,615✔
685

686
        case GDT_Int32:
3,588✔
687
            return "Int32";
3,588✔
688

689
        case GDT_UInt64:
565✔
690
            return "UInt64";
565✔
691

692
        case GDT_Int64:
550✔
693
            return "Int64";
550✔
694

695
        case GDT_Float32:
3,723✔
696
            return "Float32";
3,723✔
697

698
        case GDT_Float64:
1,810✔
699
            return "Float64";
1,810✔
700

701
        case GDT_CInt16:
1,068✔
702
            return "CInt16";
1,068✔
703

704
        case GDT_CInt32:
1,002✔
705
            return "CInt32";
1,002✔
706

707
        case GDT_CFloat32:
1,033✔
708
            return "CFloat32";
1,033✔
709

710
        case GDT_CFloat64:
884✔
711
            return "CFloat64";
884✔
712

713
        case GDT_TypeCount:
×
714
            break;
×
715
    }
716
    return nullptr;
×
717
}
718

719
/************************************************************************/
720
/*                        GDALGetDataTypeByName()                       */
721
/************************************************************************/
722

723
/**
724
 * \brief Get data type by symbolic name.
725
 *
726
 * Returns a data type corresponding to the given symbolic name. This
727
 * function is opposite to the GDALGetDataTypeName().
728
 *
729
 * @param pszName string containing the symbolic name of the type.
730
 *
731
 * @return GDAL data type.
732
 */
733

734
GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
4,522✔
735

736
{
737
    VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
4,522✔
738

739
    for (int iType = 1; iType < GDT_TypeCount; iType++)
12,971✔
740
    {
741
        const auto eType = static_cast<GDALDataType>(iType);
12,940✔
742
        if (GDALGetDataTypeName(eType) != nullptr &&
25,880✔
743
            EQUAL(GDALGetDataTypeName(eType), pszName))
12,940✔
744
        {
745
            return eType;
4,491✔
746
        }
747
    }
748

749
    return GDT_Unknown;
31✔
750
}
751

752
/************************************************************************/
753
/*                      GDALAdjustValueToDataType()                     */
754
/************************************************************************/
755

756
template <class T>
757
static inline void ClampAndRound(double &dfValue, bool &bClamped,
147✔
758
                                 bool &bRounded)
759
{
760
    // TODO(schwehr): Rework this template.  ::min() versus ::lowest.
761

762
    if (dfValue < static_cast<double>(std::numeric_limits<T>::min()))
147✔
763
    {
764
        bClamped = true;
10✔
765
        dfValue = static_cast<double>(std::numeric_limits<T>::min());
10✔
766
    }
767
    else if (dfValue > static_cast<double>(std::numeric_limits<T>::max()))
137✔
768
    {
769
        bClamped = true;
16✔
770
        dfValue = static_cast<double>(std::numeric_limits<T>::max());
16✔
771
    }
772
    else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
121✔
773
    {
774
        bRounded = true;
8✔
775
        dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
8✔
776
    }
777
}
147✔
778

779
/**
780
 * \brief Adjust a value to the output data type
781
 *
782
 * Adjustment consist in clamping to minimum/maximum values of the data type
783
 * and rounding for integral types.
784
 *
785
 * @param eDT target data type.
786
 * @param dfValue value to adjust.
787
 * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
788
 * been made, or NULL
789
 * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
790
 * been made, or NULL
791
 *
792
 * @return adjusted value
793
 * @since GDAL 2.1
794
 */
795

796
double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
198✔
797
                                 int *pbClamped, int *pbRounded)
798
{
799
    bool bClamped = false;
198✔
800
    bool bRounded = false;
198✔
801
    switch (eDT)
198✔
802
    {
803
        case GDT_Byte:
91✔
804
            ClampAndRound<GByte>(dfValue, bClamped, bRounded);
91✔
805
            break;
91✔
806
        case GDT_Int8:
6✔
807
            ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
6✔
808
            break;
6✔
809
        case GDT_Int16:
20✔
810
            ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
20✔
811
            break;
20✔
812
        case GDT_UInt16:
16✔
813
            ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
16✔
814
            break;
16✔
815
        case GDT_Int32:
4✔
816
            ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
4✔
817
            break;
4✔
818
        case GDT_UInt32:
5✔
819
            ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
5✔
820
            break;
5✔
821
        case GDT_Int64:
2✔
822
            ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
2✔
823
            break;
2✔
824
        case GDT_UInt64:
3✔
825
            ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
3✔
826
            break;
3✔
827
        case GDT_Float32:
40✔
828
        {
829
            if (!CPLIsFinite(dfValue))
40✔
830
                break;
4✔
831

832
            // TODO(schwehr): ::min() versus ::lowest.
833
            // Use ClampAndRound after it has been fixed.
834
            if (dfValue < -std::numeric_limits<float>::max())
36✔
835
            {
836
                bClamped = TRUE;
1✔
837
                dfValue =
1✔
838
                    static_cast<double>(-std::numeric_limits<float>::max());
1✔
839
            }
840
            else if (dfValue > std::numeric_limits<float>::max())
35✔
841
            {
842
                bClamped = TRUE;
1✔
843
                dfValue =
1✔
844
                    static_cast<double>(std::numeric_limits<float>::max());
1✔
845
            }
846
            else
847
            {
848
                // Intentionally loose precision.
849
                // TODO(schwehr): Is the double cast really necessary?
850
                // If so, why?  What will fail?
851
                dfValue = static_cast<double>(static_cast<float>(dfValue));
34✔
852
            }
853
            break;
36✔
854
        }
855
        case GDT_Float64:
11✔
856
        case GDT_CInt16:
857
        case GDT_CInt32:
858
        case GDT_CFloat32:
859
        case GDT_CFloat64:
860
        case GDT_Unknown:
861
        case GDT_TypeCount:
862
            break;
11✔
863
    }
864
    if (pbClamped)
198✔
865
        *pbClamped = bClamped;
197✔
866
    if (pbRounded)
198✔
867
        *pbRounded = bRounded;
197✔
868
    return dfValue;
198✔
869
}
870

871
/************************************************************************/
872
/*                        GDALGetNonComplexDataType()                */
873
/************************************************************************/
874
/**
875
 * \brief Return the base data type for the specified input.
876
 *
877
 * If the input data type is complex this function returns the base type
878
 * i.e. the data type of the real and imaginary parts (non-complex).
879
 * If the input data type is already non-complex, then it is returned
880
 * unchanged.
881
 *
882
 * @param eDataType type, such as GDT_CFloat32.
883
 *
884
 * @return GDAL data type.
885
 */
886
GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
78,751✔
887
{
888
    switch (eDataType)
78,751✔
889
    {
890
        case GDT_CInt16:
70✔
891
            return GDT_Int16;
70✔
892
        case GDT_CInt32:
23✔
893
            return GDT_Int32;
23✔
894
        case GDT_CFloat32:
47✔
895
            return GDT_Float32;
47✔
896
        case GDT_CFloat64:
50✔
897
            return GDT_Float64;
50✔
898

899
        case GDT_Byte:
78,561✔
900
        case GDT_UInt16:
901
        case GDT_UInt32:
902
        case GDT_UInt64:
903
        case GDT_Int8:
904
        case GDT_Int16:
905
        case GDT_Int32:
906
        case GDT_Int64:
907
        case GDT_Float32:
908
        case GDT_Float64:
909
            break;
78,561✔
910

911
        case GDT_Unknown:
×
912
        case GDT_TypeCount:
913
            break;
×
914
    }
915
    return eDataType;
78,561✔
916
}
917

918
/************************************************************************/
919
/*                        GDALGetAsyncStatusTypeByName()                */
920
/************************************************************************/
921
/**
922
 * Get AsyncStatusType by symbolic name.
923
 *
924
 * Returns a data type corresponding to the given symbolic name. This
925
 * function is opposite to the GDALGetAsyncStatusTypeName().
926
 *
927
 * @param pszName string containing the symbolic name of the type.
928
 *
929
 * @return GDAL AsyncStatus type.
930
 */
931
GDALAsyncStatusType CPL_DLL CPL_STDCALL
932
GDALGetAsyncStatusTypeByName(const char *pszName)
×
933
{
934
    VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
×
935

936
    for (int iType = 0; iType < GARIO_TypeCount; iType++)
×
937
    {
938
        const auto eType = static_cast<GDALAsyncStatusType>(iType);
×
939
        if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
×
940
            EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
×
941
        {
942
            return eType;
×
943
        }
944
    }
945

946
    return GARIO_ERROR;
×
947
}
948

949
/************************************************************************/
950
/*                        GDALGetAsyncStatusTypeName()                 */
951
/************************************************************************/
952

953
/**
954
 * Get name of AsyncStatus data type.
955
 *
956
 * Returns a symbolic name for the AsyncStatus data type.  This is essentially
957
 * the enumerated item name with the GARIO_ prefix removed.  So
958
 * GARIO_COMPLETE returns "COMPLETE".  The returned strings are static strings
959
 * and should not be modified or freed by the application.  These strings are
960
 * useful for reporting datatypes in debug statements, errors and other user
961
 * output.
962
 *
963
 * @param eAsyncStatusType type to get name of.
964
 * @return string corresponding to type.
965
 */
966

967
const char *CPL_STDCALL
968
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
×
969

970
{
971
    switch (eAsyncStatusType)
×
972
    {
973
        case GARIO_PENDING:
×
974
            return "PENDING";
×
975

976
        case GARIO_UPDATE:
×
977
            return "UPDATE";
×
978

979
        case GARIO_ERROR:
×
980
            return "ERROR";
×
981

982
        case GARIO_COMPLETE:
×
983
            return "COMPLETE";
×
984

985
        default:
×
986
            return nullptr;
×
987
    }
988
}
989

990
/************************************************************************/
991
/*                  GDALGetPaletteInterpretationName()                  */
992
/************************************************************************/
993

994
/**
995
 * \brief Get name of palette interpretation
996
 *
997
 * Returns a symbolic name for the palette interpretation.  This is the
998
 * the enumerated item name with the GPI_ prefix removed.  So GPI_Gray returns
999
 * "Gray".  The returned strings are static strings and should not be modified
1000
 * or freed by the application.
1001
 *
1002
 * @param eInterp palette interpretation to get name of.
1003
 * @return string corresponding to palette interpretation.
1004
 */
1005

1006
const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
9✔
1007

1008
{
1009
    switch (eInterp)
9✔
1010
    {
1011
        case GPI_Gray:
×
1012
            return "Gray";
×
1013

1014
        case GPI_RGB:
9✔
1015
            return "RGB";
9✔
1016

1017
        case GPI_CMYK:
×
1018
            return "CMYK";
×
1019

1020
        case GPI_HLS:
×
1021
            return "HLS";
×
1022

1023
        default:
×
1024
            return "Unknown";
×
1025
    }
1026
}
1027

1028
/************************************************************************/
1029
/*                   GDALGetColorInterpretationName()                   */
1030
/************************************************************************/
1031

1032
/**
1033
 * \brief Get name of color interpretation
1034
 *
1035
 * Returns a symbolic name for the color interpretation.  This is derived from
1036
 * the enumerated item name with the GCI_ prefix removed, but there are some
1037
 * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
1038
 * The returned strings are static strings and should not be modified
1039
 * or freed by the application.
1040
 *
1041
 * @param eInterp color interpretation to get name of.
1042
 * @return string corresponding to color interpretation
1043
 *         or NULL pointer if invalid enumerator given.
1044
 */
1045

1046
const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
8,311✔
1047

1048
{
1049
    switch (eInterp)
8,311✔
1050
    {
1051
        case GCI_Undefined:
2,091✔
1052
            return "Undefined";
2,091✔
1053

1054
        case GCI_GrayIndex:
2,253✔
1055
            return "Gray";
2,253✔
1056

1057
        case GCI_PaletteIndex:
919✔
1058
            return "Palette";
919✔
1059

1060
        case GCI_RedBand:
1,016✔
1061
            return "Red";
1,016✔
1062

1063
        case GCI_GreenBand:
758✔
1064
            return "Green";
758✔
1065

1066
        case GCI_BlueBand:
514✔
1067
            return "Blue";
514✔
1068

1069
        case GCI_AlphaBand:
278✔
1070
            return "Alpha";
278✔
1071

1072
        case GCI_HueBand:
76✔
1073
            return "Hue";
76✔
1074

1075
        case GCI_SaturationBand:
76✔
1076
            return "Saturation";
76✔
1077

1078
        case GCI_LightnessBand:
76✔
1079
            return "Lightness";
76✔
1080

1081
        case GCI_CyanBand:
84✔
1082
            return "Cyan";
84✔
1083

1084
        case GCI_MagentaBand:
67✔
1085
            return "Magenta";
67✔
1086

1087
        case GCI_YellowBand:
50✔
1088
            return "Yellow";
50✔
1089

1090
        case GCI_BlackBand:
32✔
1091
            return "Black";
32✔
1092

1093
        case GCI_YCbCr_YBand:
9✔
1094
            return "YCbCr_Y";
9✔
1095

1096
        case GCI_YCbCr_CbBand:
7✔
1097
            return "YCbCr_Cb";
7✔
1098

1099
        case GCI_YCbCr_CrBand:
5✔
1100
            return "YCbCr_Cr";
5✔
1101

1102
        default:
×
1103
            return "Unknown";
×
1104
    }
1105
}
1106

1107
/************************************************************************/
1108
/*                GDALGetColorInterpretationByName()                    */
1109
/************************************************************************/
1110

1111
/**
1112
 * \brief Get color interpretation by symbolic name.
1113
 *
1114
 * Returns a color interpretation corresponding to the given symbolic name. This
1115
 * function is opposite to the GDALGetColorInterpretationName().
1116
 *
1117
 * @param pszName string containing the symbolic name of the color
1118
 * interpretation.
1119
 *
1120
 * @return GDAL color interpretation.
1121
 *
1122
 * @since GDAL 1.7.0
1123
 */
1124

1125
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1,933✔
1126

1127
{
1128
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1,933✔
1129
                      GCI_Undefined);
1130

1131
    for (int iType = 0; iType <= GCI_Max; iType++)
7,120✔
1132
    {
1133
        if (EQUAL(GDALGetColorInterpretationName(
7,118✔
1134
                      static_cast<GDALColorInterp>(iType)),
1135
                  pszName))
1136
        {
1137
            return static_cast<GDALColorInterp>(iType);
1,931✔
1138
        }
1139
    }
1140

1141
    return GCI_Undefined;
2✔
1142
}
1143

1144
/************************************************************************/
1145
/*                     GDALGetRandomRasterSample()                      */
1146
/************************************************************************/
1147

1148
/** Undocumented
1149
 * @param hBand undocumented.
1150
 * @param nSamples undocumented.
1151
 * @param pafSampleBuf undocumented.
1152
 * @return undocumented
1153
 */
1154
int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
×
1155
                                          float *pafSampleBuf)
1156

1157
{
1158
    VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
×
1159

1160
    GDALRasterBand *poBand;
1161

1162
    poBand = GDALRasterBand::FromHandle(
×
1163
        GDALGetRasterSampleOverview(hBand, nSamples));
1164
    CPLAssert(nullptr != poBand);
×
1165

1166
    /* -------------------------------------------------------------------- */
1167
    /*      Figure out the ratio of blocks we will read to get an           */
1168
    /*      approximate value.                                              */
1169
    /* -------------------------------------------------------------------- */
1170
    int bGotNoDataValue = FALSE;
×
1171

1172
    double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
×
1173

1174
    int nBlockXSize = 0;
×
1175
    int nBlockYSize = 0;
×
1176
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
×
1177

1178
    const int nBlocksPerRow =
1179
        (poBand->GetXSize() + nBlockXSize - 1) / nBlockXSize;
×
1180
    const int nBlocksPerColumn =
1181
        (poBand->GetYSize() + nBlockYSize - 1) / nBlockYSize;
×
1182

1183
    const int nBlockPixels = nBlockXSize * nBlockYSize;
×
1184
    const int nBlockCount = nBlocksPerRow * nBlocksPerColumn;
×
1185

1186
    if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
×
1187
        nBlockCount == 0)
1188
    {
1189
        CPLError(CE_Failure, CPLE_AppDefined,
×
1190
                 "GDALGetRandomRasterSample(): returning because band"
1191
                 " appears degenerate.");
1192

1193
        return FALSE;
×
1194
    }
1195

1196
    int nSampleRate = static_cast<int>(
1197
        std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
×
1198

1199
    if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
×
1200
        nSampleRate--;
×
1201

1202
    while (nSampleRate > 1 &&
×
1203
           ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
×
1204
        nSampleRate--;
×
1205

1206
    int nBlockSampleRate = 1;
×
1207

1208
    if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
×
1209
        nBlockSampleRate =
×
1210
            std::max(1, nBlockPixels /
×
1211
                            (nSamples / ((nBlockCount - 1) / nSampleRate + 1)));
×
1212

1213
    int nActualSamples = 0;
×
1214

1215
    for (int iSampleBlock = 0; iSampleBlock < nBlockCount;
×
1216
         iSampleBlock += nSampleRate)
×
1217
    {
1218

1219
        const int iYBlock = iSampleBlock / nBlocksPerRow;
×
1220
        const int iXBlock = iSampleBlock - nBlocksPerRow * iYBlock;
×
1221

1222
        GDALRasterBlock *const poBlock =
1223
            poBand->GetLockedBlockRef(iXBlock, iYBlock);
×
1224
        if (poBlock == nullptr)
×
1225
            continue;
×
1226
        void *pDataRef = poBlock->GetDataRef();
×
1227

1228
        int iXValid = nBlockXSize;
×
1229
        if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
×
1230
            iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
×
1231

1232
        int iYValid = nBlockYSize;
×
1233
        if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
×
1234
            iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
×
1235

1236
        int iRemainder = 0;
×
1237

1238
        for (int iY = 0; iY < iYValid; iY++)
×
1239
        {
1240
            int iX = iRemainder;  // Used after for.
×
1241
            for (; iX < iXValid; iX += nBlockSampleRate)
×
1242
            {
1243
                double dfValue = 0.0;
×
1244
                const int iOffset = iX + iY * nBlockXSize;
×
1245

1246
                switch (poBlock->GetDataType())
×
1247
                {
1248
                    case GDT_Byte:
×
1249
                        dfValue =
×
1250
                            reinterpret_cast<const GByte *>(pDataRef)[iOffset];
×
1251
                        break;
×
1252
                    case GDT_Int8:
×
1253
                        dfValue =
×
1254
                            reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
×
1255
                        break;
×
1256
                    case GDT_UInt16:
×
1257
                        dfValue = reinterpret_cast<const GUInt16 *>(
×
1258
                            pDataRef)[iOffset];
×
1259
                        break;
×
1260
                    case GDT_Int16:
×
1261
                        dfValue =
×
1262
                            reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
×
1263
                        break;
×
1264
                    case GDT_UInt32:
×
1265
                        dfValue = reinterpret_cast<const GUInt32 *>(
×
1266
                            pDataRef)[iOffset];
×
1267
                        break;
×
1268
                    case GDT_Int32:
×
1269
                        dfValue =
×
1270
                            reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
×
1271
                        break;
×
1272
                    case GDT_UInt64:
×
1273
                        dfValue = static_cast<double>(
×
1274
                            reinterpret_cast<const std::uint64_t *>(
1275
                                pDataRef)[iOffset]);
×
1276
                        break;
×
1277
                    case GDT_Int64:
×
1278
                        dfValue = static_cast<double>(
×
1279
                            reinterpret_cast<const std::int64_t *>(
1280
                                pDataRef)[iOffset]);
×
1281
                        break;
×
1282
                    case GDT_Float32:
×
1283
                        dfValue =
×
1284
                            reinterpret_cast<const float *>(pDataRef)[iOffset];
×
1285
                        break;
×
1286
                    case GDT_Float64:
×
1287
                        dfValue =
×
1288
                            reinterpret_cast<const double *>(pDataRef)[iOffset];
×
1289
                        break;
×
1290
                    case GDT_CInt16:
×
1291
                    {
1292
                        // TODO(schwehr): Clean up casts.
1293
                        const double dfReal = reinterpret_cast<const GInt16 *>(
×
1294
                            pDataRef)[iOffset * 2];
×
1295
                        const double dfImag = reinterpret_cast<const GInt16 *>(
×
1296
                            pDataRef)[iOffset * 2 + 1];
×
1297
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1298
                        break;
×
1299
                    }
1300
                    case GDT_CInt32:
×
1301
                    {
1302
                        const double dfReal = reinterpret_cast<const GInt32 *>(
×
1303
                            pDataRef)[iOffset * 2];
×
1304
                        const double dfImag = reinterpret_cast<const GInt32 *>(
×
1305
                            pDataRef)[iOffset * 2 + 1];
×
1306
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1307
                        break;
×
1308
                    }
1309
                    case GDT_CFloat32:
×
1310
                    {
1311
                        const double dfReal = reinterpret_cast<const float *>(
×
1312
                            pDataRef)[iOffset * 2];
×
1313
                        const double dfImag = reinterpret_cast<const float *>(
×
1314
                            pDataRef)[iOffset * 2 + 1];
×
1315
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1316
                        break;
×
1317
                    }
1318
                    case GDT_CFloat64:
×
1319
                    {
1320
                        const double dfReal = reinterpret_cast<const double *>(
×
1321
                            pDataRef)[iOffset * 2];
×
1322
                        const double dfImag = reinterpret_cast<const double *>(
×
1323
                            pDataRef)[iOffset * 2 + 1];
×
1324
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
×
1325
                        break;
×
1326
                    }
1327
                    case GDT_Unknown:
×
1328
                    case GDT_TypeCount:
1329
                        CPLAssert(false);
×
1330
                }
1331

1332
                if (bGotNoDataValue && dfValue == dfNoDataValue)
×
1333
                    continue;
×
1334

1335
                if (nActualSamples < nSamples)
×
1336
                    pafSampleBuf[nActualSamples++] =
×
1337
                        static_cast<float>(dfValue);
×
1338
            }
1339

1340
            iRemainder = iX - iXValid;
×
1341
        }
1342

1343
        poBlock->DropLock();
×
1344
    }
1345

1346
    return nActualSamples;
×
1347
}
1348

1349
/************************************************************************/
1350
/*                             gdal::GCP                                */
1351
/************************************************************************/
1352

1353
namespace gdal
1354
{
1355
/** Constructor. */
1356
GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
3,566✔
1357
         double dfX, double dfY, double dfZ)
3,566✔
1358
    : gcp{CPLStrdup(pszId ? pszId : ""),
3,566✔
1359
          CPLStrdup(pszInfo ? pszInfo : ""),
3,566✔
1360
          dfPixel,
1361
          dfLine,
1362
          dfX,
1363
          dfY,
1364
          dfZ}
3,566✔
1365
{
1366
    static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1367
}
3,566✔
1368

1369
/** Destructor. */
1370
GCP::~GCP()
29,594✔
1371
{
1372
    CPLFree(gcp.pszId);
14,797✔
1373
    CPLFree(gcp.pszInfo);
14,797✔
1374
}
14,797✔
1375

1376
/** Constructor from a C GDAL_GCP instance. */
1377
GCP::GCP(const GDAL_GCP &other)
8,558✔
1378
    : gcp{CPLStrdup(other.pszId),
8,558✔
1379
          CPLStrdup(other.pszInfo),
17,116✔
1380
          other.dfGCPPixel,
8,558✔
1381
          other.dfGCPLine,
8,558✔
1382
          other.dfGCPX,
8,558✔
1383
          other.dfGCPY,
8,558✔
1384
          other.dfGCPZ}
8,558✔
1385
{
1386
}
8,558✔
1387

1388
/** Copy constructor. */
1389
GCP::GCP(const GCP &other) : GCP(other.gcp)
5,129✔
1390
{
1391
}
5,129✔
1392

1393
/** Move constructor. */
1394
GCP::GCP(GCP &&other)
2,673✔
1395
    : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
2,673✔
1396
          other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
2,673✔
1397
          other.gcp.dfGCPZ}
2,673✔
1398
{
1399
    other.gcp.pszId = nullptr;
2,673✔
1400
    other.gcp.pszInfo = nullptr;
2,673✔
1401
}
2,673✔
1402

1403
/** Copy assignment operator. */
1404
GCP &GCP::operator=(const GCP &other)
1✔
1405
{
1406
    if (this != &other)
1✔
1407
    {
1408
        CPLFree(gcp.pszId);
1✔
1409
        CPLFree(gcp.pszInfo);
1✔
1410
        gcp = other.gcp;
1✔
1411
        gcp.pszId = CPLStrdup(other.gcp.pszId);
1✔
1412
        gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1✔
1413
    }
1414
    return *this;
1✔
1415
}
1416

1417
/** Move assignment operator. */
1418
GCP &GCP::operator=(GCP &&other)
1✔
1419
{
1420
    if (this != &other)
1✔
1421
    {
1422
        CPLFree(gcp.pszId);
1✔
1423
        CPLFree(gcp.pszInfo);
1✔
1424
        gcp = other.gcp;
1✔
1425
        other.gcp.pszId = nullptr;
1✔
1426
        other.gcp.pszInfo = nullptr;
1✔
1427
    }
1428
    return *this;
1✔
1429
}
1430

1431
/** Set the 'id' member of the GCP. */
1432
void GCP::SetId(const char *pszId)
2,643✔
1433
{
1434
    CPLFree(gcp.pszId);
2,643✔
1435
    gcp.pszId = CPLStrdup(pszId ? pszId : "");
2,643✔
1436
}
2,643✔
1437

1438
/** Set the 'info' member of the GCP. */
1439
void GCP::SetInfo(const char *pszInfo)
2,643✔
1440
{
1441
    CPLFree(gcp.pszInfo);
2,643✔
1442
    gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
2,643✔
1443
}
2,643✔
1444

1445
/** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1446
/*static */
1447
const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
285✔
1448
{
1449
    return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
285✔
1450
}
1451

1452
/** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1453
/*static*/
1454
std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
320✔
1455
{
1456
    return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
320✔
1457
}
1458

1459
} /* namespace gdal */
1460

1461
/************************************************************************/
1462
/*                            GDALInitGCPs()                            */
1463
/************************************************************************/
1464

1465
/** Initialize an array of GCPs.
1466
 *
1467
 * Numeric values are initialized to 0 and strings to the empty string ""
1468
 * allocated with CPLStrdup()
1469
 * An array initialized with GDALInitGCPs() must be de-initialized with
1470
 * GDALDeinitGCPs().
1471
 *
1472
 * @param nCount number of GCPs in psGCP
1473
 * @param psGCP array of GCPs of size nCount.
1474
 */
1475
void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1,276✔
1476

1477
{
1478
    if (nCount > 0)
1,276✔
1479
    {
1480
        VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
637✔
1481
    }
1482

1483
    for (int iGCP = 0; iGCP < nCount; iGCP++)
5,906✔
1484
    {
1485
        memset(psGCP, 0, sizeof(GDAL_GCP));
4,630✔
1486
        psGCP->pszId = CPLStrdup("");
4,630✔
1487
        psGCP->pszInfo = CPLStrdup("");
4,630✔
1488
        psGCP++;
4,630✔
1489
    }
1490
}
1491

1492
/************************************************************************/
1493
/*                           GDALDeinitGCPs()                           */
1494
/************************************************************************/
1495

1496
/** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1497
 *
1498
 * @param nCount number of GCPs in psGCP
1499
 * @param psGCP array of GCPs of size nCount.
1500
 */
1501
void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1,331✔
1502

1503
{
1504
    if (nCount > 0)
1,331✔
1505
    {
1506
        VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
429✔
1507
    }
1508

1509
    for (int iGCP = 0; iGCP < nCount; iGCP++)
6,090✔
1510
    {
1511
        CPLFree(psGCP->pszId);
4,759✔
1512
        CPLFree(psGCP->pszInfo);
4,759✔
1513
        psGCP++;
4,759✔
1514
    }
1515
}
1516

1517
/************************************************************************/
1518
/*                         GDALDuplicateGCPs()                          */
1519
/************************************************************************/
1520

1521
/** Duplicate an array of GCPs
1522
 *
1523
 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1524
 *
1525
 * @param nCount number of GCPs in psGCP
1526
 * @param pasGCPList array of GCPs of size nCount.
1527
 */
1528
GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
723✔
1529

1530
{
1531
    GDAL_GCP *pasReturn =
1532
        static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
723✔
1533
    GDALInitGCPs(nCount, pasReturn);
723✔
1534

1535
    for (int iGCP = 0; iGCP < nCount; iGCP++)
3,952✔
1536
    {
1537
        CPLFree(pasReturn[iGCP].pszId);
3,229✔
1538
        pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
3,229✔
1539

1540
        CPLFree(pasReturn[iGCP].pszInfo);
3,229✔
1541
        pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
3,229✔
1542

1543
        pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
3,229✔
1544
        pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
3,229✔
1545
        pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
3,229✔
1546
        pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
3,229✔
1547
        pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
3,229✔
1548
    }
1549

1550
    return pasReturn;
723✔
1551
}
1552

1553
/************************************************************************/
1554
/*                       GDALFindAssociatedFile()                       */
1555
/************************************************************************/
1556

1557
/**
1558
 * \brief Find file with alternate extension.
1559
 *
1560
 * Finds the file with the indicated extension, substituting it in place
1561
 * of the extension of the base filename.  Generally used to search for
1562
 * associated files like world files .RPB files, etc.  If necessary, the
1563
 * extension will be tried in both upper and lower case.  If a sibling file
1564
 * list is available it will be used instead of doing VSIStatExL() calls to
1565
 * probe the file system.
1566
 *
1567
 * Note that the result is a dynamic CPLString so this method should not
1568
 * be used in a situation where there could be cross heap issues.  It is
1569
 * generally imprudent for application built on GDAL to use this function
1570
 * unless they are sure they will always use the same runtime heap as GDAL.
1571
 *
1572
 * @param pszBaseFilename the filename relative to which to search.
1573
 * @param pszExt the target extension in either upper or lower case.
1574
 * @param papszSiblingFiles the list of files in the same directory as
1575
 * pszBaseFilename or NULL if they are not known.
1576
 * @param nFlags special options controlling search.  None defined yet, just
1577
 * pass 0.
1578
 *
1579
 * @return an empty string if the target is not found, otherwise the target
1580
 * file with similar path style as the pszBaseFilename.
1581
 */
1582

1583
/**/
1584
/**/
1585

1586
CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
35,380✔
1587
                                 const char *pszExt,
1588
                                 CSLConstList papszSiblingFiles,
1589
                                 CPL_UNUSED int nFlags)
1590

1591
{
1592
    CPLString osTarget = CPLResetExtension(pszBaseFilename, pszExt);
70,760✔
1593

1594
    if (papszSiblingFiles == nullptr ||
70,552✔
1595
        // cppcheck-suppress knownConditionTrueFalse
1596
        !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
35,172✔
1597
    {
1598
        VSIStatBufL sStatBuf;
1599

1600
        if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
208✔
1601
        {
1602
            CPLString osAltExt = pszExt;
193✔
1603

1604
            if (islower(static_cast<unsigned char>(pszExt[0])))
193✔
1605
                osAltExt.toupper();
×
1606
            else
1607
                osAltExt.tolower();
193✔
1608

1609
            osTarget = CPLResetExtension(pszBaseFilename, osAltExt);
193✔
1610

1611
            if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
193✔
1612
                return "";
191✔
1613
        }
1614
    }
1615
    else
1616
    {
1617
        const int iSibling =
1618
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
35,172✔
1619
        if (iSibling < 0)
35,172✔
1620
            return "";
35,121✔
1621

1622
        osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
51✔
1623
        osTarget += papszSiblingFiles[iSibling];
51✔
1624
    }
1625

1626
    return osTarget;
68✔
1627
}
1628

1629
/************************************************************************/
1630
/*                         GDALLoadOziMapFile()                         */
1631
/************************************************************************/
1632

1633
/** Helper function for translator implementer wanting support for OZI .map
1634
 *
1635
 * @param pszFilename filename of .tab file
1636
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1637
 * @param ppszWKT output pointer to a string that will be allocated with
1638
 * CPLMalloc().
1639
 * @param pnGCPCount output pointer to GCP count.
1640
 * @param ppasGCPs outputer pointer to an array of GCPs.
1641
 * @return TRUE in case of success, FALSE otherwise.
1642
 */
1643
int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
×
1644
                                   double *padfGeoTransform, char **ppszWKT,
1645
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
1646

1647
{
1648
    VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
×
1649
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
×
1650
    VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
×
1651
    VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
×
1652

1653
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
×
1654

1655
    if (!papszLines)
×
1656
        return FALSE;
×
1657

1658
    int nLines = CSLCount(papszLines);
×
1659

1660
    // Check the OziExplorer Map file signature
1661
    if (nLines < 5 ||
×
1662
        !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
×
1663
    {
1664
        CPLError(CE_Failure, CPLE_AppDefined,
×
1665
                 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
1666
                 "format.",
1667
                 pszFilename);
1668
        CSLDestroy(papszLines);
×
1669
        return FALSE;
×
1670
    }
1671

1672
    OGRSpatialReference oSRS;
×
1673
    OGRErr eErr = OGRERR_NONE;
×
1674

1675
    /* The Map Scale Factor has been introduced recently on the 6th line */
1676
    /* and is a trick that is used to just change that line without changing */
1677
    /* the rest of the MAP file but providing an imagery that is smaller or
1678
     * larger */
1679
    /* so we have to correct the pixel/line values read in the .MAP file so they
1680
     */
1681
    /* match the actual imagery dimension. Well, this is a bad summary of what
1682
     */
1683
    /* is explained at
1684
     * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
1685
    double dfMSF = 1;
×
1686

1687
    for (int iLine = 5; iLine < nLines; iLine++)
×
1688
    {
1689
        if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
×
1690
        {
1691
            dfMSF = CPLAtof(papszLines[iLine] + 4);
×
1692
            if (dfMSF <= 0.01) /* Suspicious values */
×
1693
            {
1694
                CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
×
1695
                dfMSF = 1;
×
1696
            }
1697
        }
1698
    }
1699

1700
    eErr = oSRS.importFromOzi(papszLines);
×
1701
    if (eErr == OGRERR_NONE)
×
1702
    {
1703
        if (ppszWKT != nullptr)
×
1704
            oSRS.exportToWkt(ppszWKT);
×
1705
    }
1706

1707
    int nCoordinateCount = 0;
×
1708
    // TODO(schwehr): Initialize asGCPs.
1709
    GDAL_GCP asGCPs[30];
1710

1711
    // Iterate all lines in the MAP-file
1712
    for (int iLine = 5; iLine < nLines; iLine++)
×
1713
    {
1714
        char **papszTok = CSLTokenizeString2(
×
1715
            papszLines[iLine], ",",
×
1716
            CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
1717

1718
        if (CSLCount(papszTok) < 12)
×
1719
        {
1720
            CSLDestroy(papszTok);
×
1721
            continue;
×
1722
        }
1723

1724
        if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
×
1725
            !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
×
1726
            nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
1727
        {
1728
            bool bReadOk = false;
×
1729
            double dfLon = 0.0;
×
1730
            double dfLat = 0.0;
×
1731

1732
            if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
×
1733
                !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
×
1734
            {
1735
                // Set geographical coordinates of the pixels
1736
                dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
×
1737
                dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
×
1738
                if (EQUAL(papszTok[11], "W"))
×
1739
                    dfLon = -dfLon;
×
1740
                if (EQUAL(papszTok[8], "S"))
×
1741
                    dfLat = -dfLat;
×
1742

1743
                // Transform from the geographical coordinates into projected
1744
                // coordinates.
1745
                if (eErr == OGRERR_NONE)
×
1746
                {
1747
                    OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
×
1748

1749
                    if (poLongLat)
×
1750
                    {
1751
                        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
1752
                        poLongLat->SetAxisMappingStrategy(
×
1753
                            OAMS_TRADITIONAL_GIS_ORDER);
1754

1755
                        OGRCoordinateTransformation *poTransform =
1756
                            OGRCreateCoordinateTransformation(poLongLat, &oSRS);
×
1757
                        if (poTransform)
×
1758
                        {
1759
                            bReadOk = CPL_TO_BOOL(
×
1760
                                poTransform->Transform(1, &dfLon, &dfLat));
1761
                            delete poTransform;
×
1762
                        }
1763
                        delete poLongLat;
×
1764
                    }
1765
                }
×
1766
            }
1767
            else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
×
1768
            {
1769
                // Set cartesian coordinates of the pixels.
1770
                dfLon = CPLAtofM(papszTok[14]);
×
1771
                dfLat = CPLAtofM(papszTok[15]);
×
1772
                bReadOk = true;
×
1773

1774
                // if ( EQUAL(papszTok[16], "S") )
1775
                //     dfLat = -dfLat;
1776
            }
1777

1778
            if (bReadOk)
×
1779
            {
1780
                GDALInitGCPs(1, asGCPs + nCoordinateCount);
×
1781

1782
                // Set pixel/line part
1783
                asGCPs[nCoordinateCount].dfGCPPixel =
×
1784
                    CPLAtofM(papszTok[2]) / dfMSF;
×
1785
                asGCPs[nCoordinateCount].dfGCPLine =
×
1786
                    CPLAtofM(papszTok[3]) / dfMSF;
×
1787

1788
                asGCPs[nCoordinateCount].dfGCPX = dfLon;
×
1789
                asGCPs[nCoordinateCount].dfGCPY = dfLat;
×
1790

1791
                nCoordinateCount++;
×
1792
            }
1793
        }
1794

1795
        CSLDestroy(papszTok);
×
1796
    }
1797

1798
    CSLDestroy(papszLines);
×
1799

1800
    if (nCoordinateCount == 0)
×
1801
    {
1802
        CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
×
1803
                 pszFilename);
1804
        return FALSE;
×
1805
    }
1806

1807
    /* -------------------------------------------------------------------- */
1808
    /*      Try to convert the GCPs into a geotransform definition, if      */
1809
    /*      possible.  Otherwise we will need to use them as GCPs.          */
1810
    /* -------------------------------------------------------------------- */
1811
    if (!GDALGCPsToGeoTransform(
×
1812
            nCoordinateCount, asGCPs, padfGeoTransform,
1813
            CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
×
1814
    {
1815
        if (pnGCPCount && ppasGCPs)
×
1816
        {
1817
            CPLDebug(
×
1818
                "GDAL",
1819
                "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
1820
                "first order geotransform.  Using points as GCPs.",
1821
                pszFilename);
1822

1823
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
1824
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
1825
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
1826
            *pnGCPCount = nCoordinateCount;
×
1827
        }
1828
    }
1829
    else
1830
    {
1831
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
×
1832
    }
1833

1834
    return TRUE;
×
1835
}
1836

1837
/************************************************************************/
1838
/*                       GDALReadOziMapFile()                           */
1839
/************************************************************************/
1840

1841
/** Helper function for translator implementer wanting support for OZI .map
1842
 *
1843
 * @param pszBaseFilename filename whose basename will help building the .map
1844
 * filename.
1845
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1846
 * @param ppszWKT output pointer to a string that will be allocated with
1847
 * CPLMalloc().
1848
 * @param pnGCPCount output pointer to GCP count.
1849
 * @param ppasGCPs outputer pointer to an array of GCPs.
1850
 * @return TRUE in case of success, FALSE otherwise.
1851
 */
1852
int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
×
1853
                                   double *padfGeoTransform, char **ppszWKT,
1854
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
1855

1856
{
1857
    /* -------------------------------------------------------------------- */
1858
    /*      Try lower case, then upper case.                                */
1859
    /* -------------------------------------------------------------------- */
1860
    const char *pszOzi = CPLResetExtension(pszBaseFilename, "map");
×
1861

1862
    VSILFILE *fpOzi = VSIFOpenL(pszOzi, "rt");
×
1863

1864
    if (fpOzi == nullptr && VSIIsCaseSensitiveFS(pszOzi))
×
1865
    {
1866
        pszOzi = CPLResetExtension(pszBaseFilename, "MAP");
×
1867
        fpOzi = VSIFOpenL(pszOzi, "rt");
×
1868
    }
1869

1870
    if (fpOzi == nullptr)
×
1871
        return FALSE;
×
1872

1873
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
×
1874

1875
    /* -------------------------------------------------------------------- */
1876
    /*      We found the file, now load and parse it.                       */
1877
    /* -------------------------------------------------------------------- */
1878
    return GDALLoadOziMapFile(pszOzi, padfGeoTransform, ppszWKT, pnGCPCount,
×
1879
                              ppasGCPs);
×
1880
}
1881

1882
/************************************************************************/
1883
/*                         GDALLoadTabFile()                            */
1884
/*                                                                      */
1885
/************************************************************************/
1886

1887
/** Helper function for translator implementer wanting support for MapInfo
1888
 * .tab files.
1889
 *
1890
 * @param pszFilename filename of .tab
1891
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1892
 * @param ppszWKT output pointer to a string that will be allocated with
1893
 * CPLMalloc().
1894
 * @param pnGCPCount output pointer to GCP count.
1895
 * @param ppasGCPs outputer pointer to an array of GCPs.
1896
 * @return TRUE in case of success, FALSE otherwise.
1897
 */
1898
int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
14✔
1899
                                double *padfGeoTransform, char **ppszWKT,
1900
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
1901

1902
{
1903
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
14✔
1904

1905
    if (!papszLines)
14✔
1906
        return FALSE;
×
1907

1908
    char **papszTok = nullptr;
14✔
1909
    bool bTypeRasterFound = false;
14✔
1910
    bool bInsideTableDef = false;
14✔
1911
    int nCoordinateCount = 0;
14✔
1912
    GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
1913
    const int numLines = CSLCount(papszLines);
14✔
1914

1915
    // Iterate all lines in the TAB-file
1916
    for (int iLine = 0; iLine < numLines; iLine++)
196✔
1917
    {
1918
        CSLDestroy(papszTok);
182✔
1919
        papszTok =
1920
            CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
182✔
1921

1922
        if (CSLCount(papszTok) < 2)
182✔
1923
            continue;
28✔
1924

1925
        // Did we find table definition
1926
        if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
154✔
1927
        {
1928
            bInsideTableDef = TRUE;
14✔
1929
        }
1930
        else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
140✔
1931
        {
1932
            // Only RASTER-type will be handled
1933
            if (EQUAL(papszTok[1], "RASTER"))
14✔
1934
            {
1935
                bTypeRasterFound = true;
14✔
1936
            }
1937
            else
1938
            {
1939
                CSLDestroy(papszTok);
×
1940
                CSLDestroy(papszLines);
×
1941
                return FALSE;
×
1942
            }
1943
        }
1944
        else if (bTypeRasterFound && bInsideTableDef &&
84✔
1945
                 CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
210✔
1946
                 nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
1947
        {
1948
            GDALInitGCPs(1, asGCPs + nCoordinateCount);
56✔
1949

1950
            asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
56✔
1951
            asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
56✔
1952
            asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
56✔
1953
            asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
56✔
1954
            if (papszTok[5] != nullptr)
56✔
1955
            {
1956
                CPLFree(asGCPs[nCoordinateCount].pszId);
56✔
1957
                asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
56✔
1958
            }
1959

1960
            nCoordinateCount++;
56✔
1961
        }
1962
        else if (bTypeRasterFound && bInsideTableDef &&
70✔
1963
                 EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
28✔
1964
        {
1965
            OGRSpatialReference oSRS;
28✔
1966

1967
            if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
14✔
1968
                oSRS.exportToWkt(ppszWKT);
28✔
1969
        }
1970
        else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
70✔
1971
                 EQUAL(papszTok[1], "degree"))
14✔
1972
        {
1973
            /*
1974
            ** If we have units of "degree", but a projected coordinate
1975
            ** system we need to convert it to geographic.  See to01_02.TAB.
1976
            */
1977
            if (ppszWKT != nullptr && *ppszWKT != nullptr &&
×
1978
                STARTS_WITH_CI(*ppszWKT, "PROJCS"))
×
1979
            {
1980
                OGRSpatialReference oSRS;
×
1981
                oSRS.importFromWkt(*ppszWKT);
×
1982

1983
                OGRSpatialReference oSRSGeogCS;
×
1984
                oSRSGeogCS.CopyGeogCSFrom(&oSRS);
×
1985
                CPLFree(*ppszWKT);
×
1986

1987
                oSRSGeogCS.exportToWkt(ppszWKT);
×
1988
            }
1989
        }
1990
    }
1991

1992
    CSLDestroy(papszTok);
14✔
1993
    CSLDestroy(papszLines);
14✔
1994

1995
    if (nCoordinateCount == 0)
14✔
1996
    {
1997
        CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
×
1998
                 pszFilename);
1999
        return FALSE;
×
2000
    }
2001

2002
    /* -------------------------------------------------------------------- */
2003
    /*      Try to convert the GCPs into a geotransform definition, if      */
2004
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2005
    /* -------------------------------------------------------------------- */
2006
    if (!GDALGCPsToGeoTransform(
14✔
2007
            nCoordinateCount, asGCPs, padfGeoTransform,
2008
            CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
14✔
2009
    {
2010
        if (pnGCPCount && ppasGCPs)
×
2011
        {
2012
            CPLDebug("GDAL",
×
2013
                     "GDALLoadTabFile(%s) found file, was not able to derive a "
2014
                     "first order geotransform.  Using points as GCPs.",
2015
                     pszFilename);
2016

2017
            *ppasGCPs = static_cast<GDAL_GCP *>(
×
2018
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
×
2019
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
×
2020
            *pnGCPCount = nCoordinateCount;
×
2021
        }
2022
    }
2023
    else
2024
    {
2025
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
14✔
2026
    }
2027

2028
    return TRUE;
14✔
2029
}
2030

2031
/************************************************************************/
2032
/*                         GDALReadTabFile()                            */
2033
/************************************************************************/
2034

2035
/** Helper function for translator implementer wanting support for MapInfo
2036
 * .tab files.
2037
 *
2038
 * @param pszBaseFilename filename whose basename will help building the .tab
2039
 * filename.
2040
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2041
 * @param ppszWKT output pointer to a string that will be allocated with
2042
 * CPLMalloc().
2043
 * @param pnGCPCount output pointer to GCP count.
2044
 * @param ppasGCPs outputer pointer to an array of GCPs.
2045
 * @return TRUE in case of success, FALSE otherwise.
2046
 */
2047
int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
×
2048
                                double *padfGeoTransform, char **ppszWKT,
2049
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2050

2051
{
2052
    return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
×
2053
                            pnGCPCount, ppasGCPs, nullptr, nullptr);
×
2054
}
2055

2056
int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
3,617✔
2057
                     char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2058
                     char **papszSiblingFiles, char **ppszTabFileNameOut)
2059
{
2060
    if (ppszTabFileNameOut)
3,617✔
2061
        *ppszTabFileNameOut = nullptr;
3,617✔
2062

2063
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
3,617✔
2064
        return FALSE;
×
2065

2066
    const char *pszTAB = CPLResetExtension(pszBaseFilename, "tab");
3,617✔
2067

2068
    if (papszSiblingFiles &&
7,188✔
2069
        // cppcheck-suppress knownConditionTrueFalse
2070
        GDALCanReliablyUseSiblingFileList(pszTAB))
3,571✔
2071
    {
2072
        int iSibling = CSLFindString(papszSiblingFiles, CPLGetFilename(pszTAB));
3,571✔
2073
        if (iSibling >= 0)
3,571✔
2074
        {
2075
            CPLString osTabFilename = pszBaseFilename;
14✔
2076
            osTabFilename.resize(strlen(pszBaseFilename) -
28✔
2077
                                 strlen(CPLGetFilename(pszBaseFilename)));
14✔
2078
            osTabFilename += papszSiblingFiles[iSibling];
14✔
2079
            if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
14✔
2080
                                pnGCPCount, ppasGCPs))
14✔
2081
            {
2082
                if (ppszTabFileNameOut)
14✔
2083
                    *ppszTabFileNameOut = CPLStrdup(osTabFilename);
14✔
2084
                return TRUE;
14✔
2085
            }
2086
        }
2087
        return FALSE;
3,557✔
2088
    }
2089

2090
    /* -------------------------------------------------------------------- */
2091
    /*      Try lower case, then upper case.                                */
2092
    /* -------------------------------------------------------------------- */
2093

2094
    VSILFILE *fpTAB = VSIFOpenL(pszTAB, "rt");
46✔
2095

2096
    if (fpTAB == nullptr && VSIIsCaseSensitiveFS(pszTAB))
46✔
2097
    {
2098
        pszTAB = CPLResetExtension(pszBaseFilename, "TAB");
46✔
2099
        fpTAB = VSIFOpenL(pszTAB, "rt");
46✔
2100
    }
2101

2102
    if (fpTAB == nullptr)
46✔
2103
        return FALSE;
46✔
2104

2105
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
×
2106

2107
    /* -------------------------------------------------------------------- */
2108
    /*      We found the file, now load and parse it.                       */
2109
    /* -------------------------------------------------------------------- */
2110
    if (GDALLoadTabFile(pszTAB, padfGeoTransform, ppszWKT, pnGCPCount,
×
2111
                        ppasGCPs))
×
2112
    {
2113
        if (ppszTabFileNameOut)
×
2114
            *ppszTabFileNameOut = CPLStrdup(pszTAB);
×
2115
        return TRUE;
×
2116
    }
2117
    return FALSE;
×
2118
}
2119

2120
/************************************************************************/
2121
/*                         GDALLoadWorldFile()                          */
2122
/************************************************************************/
2123

2124
/**
2125
 * \brief Read ESRI world file.
2126
 *
2127
 * This function reads an ESRI style world file, and formats a geotransform
2128
 * from its contents.
2129
 *
2130
 * The world file contains an affine transformation with the parameters
2131
 * in a different order than in a geotransform array.
2132
 *
2133
 * <ul>
2134
 * <li> geotransform[1] : width of pixel
2135
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2136
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2137
 * <li> geotransform[5] : height of pixel (but negative)
2138
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2139
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2140
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2141
 * pixel.
2142
 * </ul>
2143
 *
2144
 * @param pszFilename the world file name.
2145
 * @param padfGeoTransform the six double array into which the
2146
 * geotransformation should be placed.
2147
 *
2148
 * @return TRUE on success or FALSE on failure.
2149
 */
2150

2151
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
67✔
2152
                                  double *padfGeoTransform)
2153

2154
{
2155
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
67✔
2156
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
67✔
2157

2158
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
67✔
2159

2160
    if (!papszLines)
67✔
2161
        return FALSE;
×
2162

2163
    double world[6] = {0.0};
67✔
2164
    // reads the first 6 non-empty lines
2165
    int nLines = 0;
67✔
2166
    const int nLinesCount = CSLCount(papszLines);
67✔
2167
    for (int i = 0;
469✔
2168
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
469✔
2169
         ++i)
2170
    {
2171
        CPLString line(papszLines[i]);
402✔
2172
        if (line.Trim().empty())
402✔
2173
            continue;
×
2174

2175
        world[nLines] = CPLAtofM(line);
402✔
2176
        ++nLines;
402✔
2177
    }
2178

2179
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
67✔
2180
        (world[3] != 0.0 || world[1] != 0.0))
67✔
2181
    {
2182
        padfGeoTransform[0] = world[4];
67✔
2183
        padfGeoTransform[1] = world[0];
67✔
2184
        padfGeoTransform[2] = world[2];
67✔
2185
        padfGeoTransform[3] = world[5];
67✔
2186
        padfGeoTransform[4] = world[1];
67✔
2187
        padfGeoTransform[5] = world[3];
67✔
2188

2189
        // correct for center of pixel vs. top left of pixel
2190
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
67✔
2191
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
67✔
2192
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
67✔
2193
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
67✔
2194

2195
        CSLDestroy(papszLines);
67✔
2196

2197
        return TRUE;
67✔
2198
    }
2199
    else
2200
    {
2201
        CPLDebug("GDAL",
×
2202
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2203
                 pszFilename);
2204
        CSLDestroy(papszLines);
×
2205
        return FALSE;
×
2206
    }
2207
}
2208

2209
/************************************************************************/
2210
/*                         GDALReadWorldFile()                          */
2211
/************************************************************************/
2212

2213
/**
2214
 * \brief Read ESRI world file.
2215
 *
2216
 * This function reads an ESRI style world file, and formats a geotransform
2217
 * from its contents.  It does the same as GDALLoadWorldFile() function, but
2218
 * it will form the filename for the worldfile from the filename of the raster
2219
 * file referred and the suggested extension.  If no extension is provided,
2220
 * the code will internally try the unix style and windows style world file
2221
 * extensions (eg. for .tif these would be .tfw and .tifw).
2222
 *
2223
 * The world file contains an affine transformation with the parameters
2224
 * in a different order than in a geotransform array.
2225
 *
2226
 * <ul>
2227
 * <li> geotransform[1] : width of pixel
2228
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2229
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2230
 * <li> geotransform[5] : height of pixel (but negative)
2231
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2232
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2233
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2234
 * pixel.
2235
 * </ul>
2236
 *
2237
 * @param pszBaseFilename the target raster file.
2238
 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2239
 * from the pszBaseFilename
2240
 * @param padfGeoTransform the six double array into which the
2241
 * geotransformation should be placed.
2242
 *
2243
 * @return TRUE on success or FALSE on failure.
2244
 */
2245

2246
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
908✔
2247
                                  const char *pszExtension,
2248
                                  double *padfGeoTransform)
2249

2250
{
2251
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
908✔
2252
                              nullptr, nullptr);
908✔
2253
}
2254

2255
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
18,408✔
2256
                       double *padfGeoTransform, char **papszSiblingFiles,
2257
                       char **ppszWorldFileNameOut)
2258
{
2259
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
18,408✔
2260
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
18,408✔
2261

2262
    if (ppszWorldFileNameOut)
18,408✔
2263
        *ppszWorldFileNameOut = nullptr;
16,555✔
2264

2265
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
18,408✔
2266
        return FALSE;
167✔
2267

2268
    /* -------------------------------------------------------------------- */
2269
    /*      If we aren't given an extension, try both the unix and          */
2270
    /*      windows style extensions.                                       */
2271
    /* -------------------------------------------------------------------- */
2272
    if (pszExtension == nullptr)
18,241✔
2273
    {
2274
        const std::string oBaseExt = CPLGetExtension(pszBaseFilename);
8,680✔
2275

2276
        if (oBaseExt.length() < 2)
4,340✔
2277
            return FALSE;
217✔
2278

2279
        // windows version - first + last + 'w'
2280
        char szDerivedExtension[100] = {'\0'};
4,123✔
2281
        szDerivedExtension[0] = oBaseExt[0];
4,123✔
2282
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
4,123✔
2283
        szDerivedExtension[2] = 'w';
4,123✔
2284
        szDerivedExtension[3] = '\0';
4,123✔
2285

2286
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
4,123✔
2287
                               padfGeoTransform, papszSiblingFiles,
2288
                               ppszWorldFileNameOut))
4,123✔
2289
            return TRUE;
52✔
2290

2291
        // unix version - extension + 'w'
2292
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
4,071✔
2293
            return FALSE;
×
2294

2295
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
4,071✔
2296
                 oBaseExt.c_str());
2297
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
4,071✔
2298
                                  padfGeoTransform, papszSiblingFiles,
2299
                                  ppszWorldFileNameOut);
4,071✔
2300
    }
2301

2302
    /* -------------------------------------------------------------------- */
2303
    /*      Skip the leading period in the extension if there is one.       */
2304
    /* -------------------------------------------------------------------- */
2305
    if (*pszExtension == '.')
13,901✔
2306
        pszExtension++;
991✔
2307

2308
    /* -------------------------------------------------------------------- */
2309
    /*      Generate upper and lower case versions of the extension.        */
2310
    /* -------------------------------------------------------------------- */
2311
    char szExtUpper[32] = {'\0'};
13,901✔
2312
    char szExtLower[32] = {'\0'};
13,901✔
2313
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
13,901✔
2314
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
13,901✔
2315

2316
    for (int i = 0; szExtUpper[i] != '\0'; i++)
59,715✔
2317
    {
2318
        szExtUpper[i] = static_cast<char>(
45,814✔
2319
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
45,814✔
2320
        szExtLower[i] = static_cast<char>(
45,814✔
2321
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
45,814✔
2322
    }
2323

2324
    const char *pszTFW = CPLResetExtension(pszBaseFilename, szExtLower);
13,901✔
2325

2326
    if (papszSiblingFiles &&
26,661✔
2327
        // cppcheck-suppress knownConditionTrueFalse
2328
        GDALCanReliablyUseSiblingFileList(pszTFW))
12,760✔
2329
    {
2330
        const int iSibling =
2331
            CSLFindString(papszSiblingFiles, CPLGetFilename(pszTFW));
12,760✔
2332
        if (iSibling >= 0)
12,760✔
2333
        {
2334
            CPLString osTFWFilename = pszBaseFilename;
65✔
2335
            osTFWFilename.resize(strlen(pszBaseFilename) -
130✔
2336
                                 strlen(CPLGetFilename(pszBaseFilename)));
65✔
2337
            osTFWFilename += papszSiblingFiles[iSibling];
65✔
2338
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
65✔
2339
            {
2340
                if (ppszWorldFileNameOut)
65✔
2341
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
62✔
2342
                return TRUE;
65✔
2343
            }
2344
        }
2345
        return FALSE;
12,695✔
2346
    }
2347

2348
    /* -------------------------------------------------------------------- */
2349
    /*      Try lower case, then upper case.                                */
2350
    /* -------------------------------------------------------------------- */
2351

2352
    VSIStatBufL sStatBuf;
2353
    bool bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,141✔
2354

2355
    if (!bGotTFW && VSIIsCaseSensitiveFS(pszTFW))
1,141✔
2356
    {
2357
        pszTFW = CPLResetExtension(pszBaseFilename, szExtUpper);
1,139✔
2358
        bGotTFW = VSIStatExL(pszTFW, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
1,139✔
2359
    }
2360

2361
    if (!bGotTFW)
1,141✔
2362
        return FALSE;
1,139✔
2363

2364
    /* -------------------------------------------------------------------- */
2365
    /*      We found the file, now load and parse it.                       */
2366
    /* -------------------------------------------------------------------- */
2367
    if (GDALLoadWorldFile(pszTFW, padfGeoTransform))
2✔
2368
    {
2369
        if (ppszWorldFileNameOut)
2✔
2370
            *ppszWorldFileNameOut = CPLStrdup(pszTFW);
1✔
2371
        return TRUE;
2✔
2372
    }
2373
    return FALSE;
×
2374
}
2375

2376
/************************************************************************/
2377
/*                         GDALWriteWorldFile()                         */
2378
/*                                                                      */
2379
/*      Helper function for translator implementer wanting              */
2380
/*      support for ESRI world files.                                   */
2381
/************************************************************************/
2382

2383
/**
2384
 * \brief Write ESRI world file.
2385
 *
2386
 * This function writes an ESRI style world file from the passed geotransform.
2387
 *
2388
 * The world file contains an affine transformation with the parameters
2389
 * in a different order than in a geotransform array.
2390
 *
2391
 * <ul>
2392
 * <li> geotransform[1] : width of pixel
2393
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2394
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2395
 * <li> geotransform[5] : height of pixel (but negative)
2396
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2397
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2398
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2399
 * pixel.
2400
 * </ul>
2401
 *
2402
 * @param pszBaseFilename the target raster file.
2403
 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2404
 * @param padfGeoTransform the six double array from which the
2405
 * geotransformation should be read.
2406
 *
2407
 * @return TRUE on success or FALSE on failure.
2408
 */
2409

2410
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
13✔
2411
                                   const char *pszExtension,
2412
                                   double *padfGeoTransform)
2413

2414
{
2415
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
13✔
2416
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
13✔
2417
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
13✔
2418

2419
    /* -------------------------------------------------------------------- */
2420
    /*      Prepare the text to write to the file.                          */
2421
    /* -------------------------------------------------------------------- */
2422
    CPLString osTFWText;
26✔
2423

2424
    osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2425
                     padfGeoTransform[1], padfGeoTransform[4],
13✔
2426
                     padfGeoTransform[2], padfGeoTransform[5],
13✔
2427
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
13✔
2428
                         0.5 * padfGeoTransform[2],
13✔
2429
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
13✔
2430
                         0.5 * padfGeoTransform[5]);
13✔
2431

2432
    /* -------------------------------------------------------------------- */
2433
    /*      Update extension, and write to disk.                            */
2434
    /* -------------------------------------------------------------------- */
2435
    const char *pszTFW = CPLResetExtension(pszBaseFilename, pszExtension);
13✔
2436
    VSILFILE *const fpTFW = VSIFOpenL(pszTFW, "wt");
13✔
2437
    if (fpTFW == nullptr)
13✔
2438
        return FALSE;
×
2439

2440
    const int bRet =
2441
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
13✔
2442
    if (VSIFCloseL(fpTFW) != 0)
13✔
2443
        return FALSE;
×
2444

2445
    return bRet;
13✔
2446
}
2447

2448
/************************************************************************/
2449
/*                          GDALVersionInfo()                           */
2450
/************************************************************************/
2451

2452
/**
2453
 * \brief Get runtime version information.
2454
 *
2455
 * Available pszRequest values:
2456
 * <ul>
2457
 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
2458
 * "30603000", e.g for GDAL 3.6.3.0</li>
2459
 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2460
 * string. i.e. "20230312".</li>
2461
 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2462
 * <li> "--version": Returns one line version message suitable for
2463
 * use in response to --version requests.  i.e. "GDAL 3.6.3, released
2464
 * 2023/03/12"</li>
2465
 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2466
 * the GDAL_DATA directory.
2467
 * </li>
2468
 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2469
 * with information on build time options.</li>
2470
 * </ul>
2471
 *
2472
 * @param pszRequest the type of version info desired, as listed above.
2473
 *
2474
 * @return an internal string containing the requested information.
2475
 */
2476

2477
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
4,443✔
2478

2479
{
2480
    /* -------------------------------------------------------------------- */
2481
    /*      Try to capture as much build information as practical.          */
2482
    /* -------------------------------------------------------------------- */
2483
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
4,443✔
2484
    {
2485
        CPLString osBuildInfo;
994✔
2486

2487
#define STRINGIFY_HELPER(x) #x
2488
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2489

2490
#ifdef ESRI_BUILD
2491
        osBuildInfo += "ESRI_BUILD=YES\n";
2492
#endif
2493
#ifdef PAM_ENABLED
2494
        osBuildInfo += "PAM_ENABLED=YES\n";
497✔
2495
#endif
2496
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
497✔
2497
#ifdef HAVE_CURL
2498
        osBuildInfo += "CURL_ENABLED=YES\n";
497✔
2499
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
497✔
2500
#endif
2501
#ifdef HAVE_GEOS
2502
        osBuildInfo += "GEOS_ENABLED=YES\n";
497✔
2503
#ifdef GEOS_CAPI_VERSION
2504
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
497✔
2505
#endif
2506
#endif
2507
        osBuildInfo +=
2508
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2509
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
497✔
2510
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
497✔
2511
        osBuildInfo += proj_info().version;
497✔
2512
        osBuildInfo += '\n';
497✔
2513

2514
#ifdef __VERSION__
2515
#ifdef __clang_version__
2516
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2517
#elif defined(__GNUC__)
2518
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
497✔
2519
#elif defined(__INTEL_COMPILER)
2520
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2521
#else
2522
        // STRINGIFY() as we're not sure if its a int or a string
2523
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2524
#endif
2525
#elif defined(_MSC_FULL_VER)
2526
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2527
#elif defined(__INTEL_COMPILER)
2528
        osBuildInfo +=
2529
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2530
#endif
2531
#ifdef CMAKE_UNITY_BUILD
2532
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2533
#endif
2534

2535
#undef STRINGIFY_HELPER
2536
#undef STRINGIFY
2537

2538
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
497✔
2539
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
497✔
2540
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
497✔
2541
    }
2542

2543
    /* -------------------------------------------------------------------- */
2544
    /*      LICENSE is a special case. We try to find and read the          */
2545
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2546
    /* -------------------------------------------------------------------- */
2547
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
3,946✔
2548
    {
2549
        char *pszResultLicence =
2550
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
4✔
2551
        if (pszResultLicence != nullptr)
4✔
2552
        {
2553
            return pszResultLicence;
×
2554
        }
2555

2556
        const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
4✔
2557
        VSILFILE *fp = nullptr;
4✔
2558

2559
        if (pszFilename != nullptr)
4✔
2560
            fp = VSIFOpenL(pszFilename, "r");
4✔
2561

2562
        if (fp != nullptr)
4✔
2563
        {
2564
            if (VSIFSeekL(fp, 0, SEEK_END) == 0)
4✔
2565
            {
2566
                // TODO(schwehr): Handle if VSITellL returns a value too large
2567
                // for size_t.
2568
                const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
4✔
2569
                if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
4✔
2570
                {
2571
                    pszResultLicence =
2572
                        static_cast<char *>(VSICalloc(1, nLength));
4✔
2573
                    if (pszResultLicence)
4✔
2574
                        CPL_IGNORE_RET_VAL(
4✔
2575
                            VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
4✔
2576
                }
2577
            }
2578

2579
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4✔
2580
        }
2581

2582
        if (!pszResultLicence)
4✔
2583
        {
2584
            pszResultLicence =
2585
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
×
2586
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
2587
                          "contain additional details.\n");
2588
        }
2589

2590
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
4✔
2591
        return pszResultLicence;
4✔
2592
    }
2593

2594
    /* -------------------------------------------------------------------- */
2595
    /*      All other strings are fairly small.                             */
2596
    /* -------------------------------------------------------------------- */
2597
    CPLString osVersionInfo;
7,884✔
2598

2599
    if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
3,942✔
2600
        osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
2,426✔
2601
    else if (EQUAL(pszRequest, "RELEASE_DATE"))
1,516✔
2602
        osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
1✔
2603
    else if (EQUAL(pszRequest, "RELEASE_NAME"))
1,515✔
2604
        osVersionInfo.Printf(GDAL_RELEASE_NAME);
1,216✔
2605
    else  // --version
2606
    {
2607
        osVersionInfo.Printf("GDAL %s, released %d/%02d/%02d",
2608
                             GDAL_RELEASE_NAME, GDAL_RELEASE_DATE / 10000,
2609
                             (GDAL_RELEASE_DATE % 10000) / 100,
2610
                             GDAL_RELEASE_DATE % 100);
299✔
2611
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
2612
        // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
2613
        // also true for CLang
2614
        osVersionInfo += " (debug build)";
299✔
2615
#elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
2616
        // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
2617
        // In release mode, the compiler generates an error if you specify
2618
        // _ITERATOR_DEBUG_LEVEL as 2.
2619
        osVersionInfo += " (debug build)";
2620
#endif
2621
    }
2622

2623
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
3,942✔
2624
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
3,942✔
2625
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
3,942✔
2626
}
2627

2628
/************************************************************************/
2629
/*                         GDALCheckVersion()                           */
2630
/************************************************************************/
2631

2632
/** Return TRUE if GDAL library version at runtime matches
2633
   nVersionMajor.nVersionMinor.
2634

2635
    The purpose of this method is to ensure that calling code will run
2636
    with the GDAL version it is compiled for. It is primarily intended
2637
    for external plugins.
2638

2639
    @param nVersionMajor Major version to be tested against
2640
    @param nVersionMinor Minor version to be tested against
2641
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
2642
   method will issue a failure mentioning the name of the calling component.
2643

2644
    @return TRUE if GDAL library version at runtime matches
2645
    nVersionMajor.nVersionMinor, FALSE otherwise.
2646
  */
2647
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
25,158✔
2648
                                 const char *pszCallingComponentName)
2649
{
2650
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
25,158✔
2651
        nVersionMinor == GDAL_VERSION_MINOR)
2652
        return TRUE;
25,158✔
2653

2654
    if (pszCallingComponentName)
×
2655
    {
2656
        CPLError(CE_Failure, CPLE_AppDefined,
×
2657
                 "%s was compiled against GDAL %d.%d, but "
2658
                 "the current library version is %d.%d",
2659
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
2660
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
2661
    }
2662
    return FALSE;
×
2663
}
2664

2665
/************************************************************************/
2666
/*                            GDALDecToDMS()                            */
2667
/************************************************************************/
2668

2669
/** Translate a decimal degrees value to a DMS string with hemisphere.
2670
 */
2671
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
500✔
2672
                                     int nPrecision)
2673

2674
{
2675
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
500✔
2676
}
2677

2678
/************************************************************************/
2679
/*                         GDALPackedDMSToDec()                         */
2680
/************************************************************************/
2681

2682
/**
2683
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
2684
 *
2685
 * See CPLPackedDMSToDec().
2686
 */
2687

2688
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
4✔
2689

2690
{
2691
    return CPLPackedDMSToDec(dfPacked);
4✔
2692
}
2693

2694
/************************************************************************/
2695
/*                         GDALDecToPackedDMS()                         */
2696
/************************************************************************/
2697

2698
/**
2699
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
2700
 *
2701
 * See CPLDecToPackedDMS().
2702
 */
2703

2704
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
4✔
2705

2706
{
2707
    return CPLDecToPackedDMS(dfDec);
4✔
2708
}
2709

2710
/************************************************************************/
2711
/*                       GDALGCPsToGeoTransform()                       */
2712
/************************************************************************/
2713

2714
/**
2715
 * \brief Generate Geotransform from GCPs.
2716
 *
2717
 * Given a set of GCPs perform first order fit as a geotransform.
2718
 *
2719
 * Due to imprecision in the calculations the fit algorithm will often
2720
 * return non-zero rotational coefficients even if given perfectly non-rotated
2721
 * inputs.  A special case has been implemented for corner corner coordinates
2722
 * given in TL, TR, BR, BL order.  So when using this to get a geotransform
2723
 * from 4 corner coordinates, pass them in this order.
2724
 *
2725
 * Starting with GDAL 2.2.2, if bApproxOK = FALSE, the
2726
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
2727
 * set to YES, then bApproxOK will be overridden with TRUE.
2728
 * Starting with GDAL 2.2.2, when exact fit is asked, the
2729
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
2730
 * give the maximum error threshold in pixel. The default is 0.25.
2731
 *
2732
 * @param nGCPCount the number of GCPs being passed in.
2733
 * @param pasGCPs the list of GCP structures.
2734
 * @param padfGeoTransform the six double array in which the affine
2735
 * geotransformation will be returned.
2736
 * @param bApproxOK If FALSE the function will fail if the geotransform is not
2737
 * essentially an exact fit (within 0.25 pixel) for all GCPs.
2738
 *
2739
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
2740
 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
2741
 * and the fit is poor.
2742
 */
2743

2744
// TODO(schwehr): Add consts to args.
2745
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
561✔
2746
                                       double *padfGeoTransform, int bApproxOK)
2747

2748
{
2749
    double dfPixelThreshold = 0.25;
561✔
2750
    if (!bApproxOK)
561✔
2751
    {
2752
        bApproxOK = CPLTestBool(
551✔
2753
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
2754
        if (!bApproxOK)
551✔
2755
        {
2756
            // coverity[tainted_data]
2757
            dfPixelThreshold = CPLAtof(CPLGetConfigOption(
551✔
2758
                "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25"));
2759
        }
2760
    }
2761

2762
    /* -------------------------------------------------------------------- */
2763
    /*      Recognise a few special cases.                                  */
2764
    /* -------------------------------------------------------------------- */
2765
    if (nGCPCount < 2)
561✔
2766
        return FALSE;
15✔
2767

2768
    if (nGCPCount == 2)
546✔
2769
    {
2770
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
1✔
2771
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
1✔
2772
            return FALSE;
×
2773

2774
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
1✔
2775
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
1✔
2776
        padfGeoTransform[2] = 0.0;
1✔
2777

2778
        padfGeoTransform[4] = 0.0;
1✔
2779
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
1✔
2780
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
1✔
2781

2782
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
1✔
2783
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
1✔
2784
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
1✔
2785

2786
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
1✔
2787
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
1✔
2788
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
1✔
2789

2790
        return TRUE;
1✔
2791
    }
2792

2793
    /* -------------------------------------------------------------------- */
2794
    /*      Special case of 4 corner coordinates of a non-rotated           */
2795
    /*      image.  The points must be in TL-TR-BR-BL order for now.        */
2796
    /*      This case helps avoid some imprecision in the general           */
2797
    /*      calculations.                                                   */
2798
    /* -------------------------------------------------------------------- */
2799
    if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
545✔
2800
        pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
254✔
2801
        pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
254✔
2802
        pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
253✔
2803
        pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
253✔
2804
        pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
250✔
2805
        pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
250✔
2806
        pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
224✔
2807
        pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
222✔
2808
        pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
222✔
2809
        pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
222✔
2810
        pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
159✔
2811
    {
2812
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
159✔
2813
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
159✔
2814
        padfGeoTransform[2] = 0.0;
159✔
2815
        padfGeoTransform[4] = 0.0;
159✔
2816
        padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
159✔
2817
                              (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
159✔
2818

2819
        padfGeoTransform[0] =
159✔
2820
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
159✔
2821
        padfGeoTransform[3] =
159✔
2822
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
159✔
2823
        return TRUE;
159✔
2824
    }
2825

2826
    /* -------------------------------------------------------------------- */
2827
    /*      Compute source and destination ranges so we can normalize       */
2828
    /*      the values to make the least squares computation more stable.   */
2829
    /* -------------------------------------------------------------------- */
2830
    double min_pixel = pasGCPs[0].dfGCPPixel;
386✔
2831
    double max_pixel = pasGCPs[0].dfGCPPixel;
386✔
2832
    double min_line = pasGCPs[0].dfGCPLine;
386✔
2833
    double max_line = pasGCPs[0].dfGCPLine;
386✔
2834
    double min_geox = pasGCPs[0].dfGCPX;
386✔
2835
    double max_geox = pasGCPs[0].dfGCPX;
386✔
2836
    double min_geoy = pasGCPs[0].dfGCPY;
386✔
2837
    double max_geoy = pasGCPs[0].dfGCPY;
386✔
2838

2839
    for (int i = 1; i < nGCPCount; ++i)
1,647✔
2840
    {
2841
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
1,261✔
2842
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
1,261✔
2843
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
1,261✔
2844
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
1,261✔
2845
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
1,261✔
2846
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
1,261✔
2847
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
1,261✔
2848
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
1,261✔
2849
    }
2850

2851
    double EPS = 1.0e-12;
386✔
2852

2853
    if (std::abs(max_pixel - min_pixel) < EPS ||
769✔
2854
        std::abs(max_line - min_line) < EPS ||
766✔
2855
        std::abs(max_geox - min_geox) < EPS ||
1,152✔
2856
        std::abs(max_geoy - min_geoy) < EPS)
320✔
2857
    {
2858
        return FALSE;  // degenerate in at least one dimension.
66✔
2859
    }
2860

2861
    double pl_normalize[6], geo_normalize[6];
2862

2863
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
320✔
2864
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
320✔
2865
    pl_normalize[2] = 0.0;
320✔
2866
    pl_normalize[3] = -min_line / (max_line - min_line);
320✔
2867
    pl_normalize[4] = 0.0;
320✔
2868
    pl_normalize[5] = 1.0 / (max_line - min_line);
320✔
2869

2870
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
320✔
2871
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
320✔
2872
    geo_normalize[2] = 0.0;
320✔
2873
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
320✔
2874
    geo_normalize[4] = 0.0;
320✔
2875
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
320✔
2876

2877
    /* -------------------------------------------------------------------- */
2878
    /* In the general case, do a least squares error approximation by       */
2879
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
2880
    /* -------------------------------------------------------------------- */
2881

2882
    double sum_x = 0.0;
320✔
2883
    double sum_y = 0.0;
320✔
2884
    double sum_xy = 0.0;
320✔
2885
    double sum_xx = 0.0;
320✔
2886
    double sum_yy = 0.0;
320✔
2887
    double sum_Lon = 0.0;
320✔
2888
    double sum_Lonx = 0.0;
320✔
2889
    double sum_Lony = 0.0;
320✔
2890
    double sum_Lat = 0.0;
320✔
2891
    double sum_Latx = 0.0;
320✔
2892
    double sum_Laty = 0.0;
320✔
2893

2894
    for (int i = 0; i < nGCPCount; ++i)
1,703✔
2895
    {
2896
        double pixel, line, geox, geoy;
2897

2898
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
1,383✔
2899
                              pasGCPs[i].dfGCPLine, &pixel, &line);
1,383✔
2900
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
1,383✔
2901
                              pasGCPs[i].dfGCPY, &geox, &geoy);
1,383✔
2902

2903
        sum_x += pixel;
1,383✔
2904
        sum_y += line;
1,383✔
2905
        sum_xy += pixel * line;
1,383✔
2906
        sum_xx += pixel * pixel;
1,383✔
2907
        sum_yy += line * line;
1,383✔
2908
        sum_Lon += geox;
1,383✔
2909
        sum_Lonx += geox * pixel;
1,383✔
2910
        sum_Lony += geox * line;
1,383✔
2911
        sum_Lat += geoy;
1,383✔
2912
        sum_Latx += geoy * pixel;
1,383✔
2913
        sum_Laty += geoy * line;
1,383✔
2914
    }
2915

2916
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
320✔
2917
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
320✔
2918
                           sum_x * sum_x * sum_yy;
320✔
2919

2920
    /* -------------------------------------------------------------------- */
2921
    /*      If the divisor is zero, there is no valid solution.             */
2922
    /* -------------------------------------------------------------------- */
2923
    if (divisor == 0.0)
320✔
2924
        return FALSE;
×
2925

2926
    /* -------------------------------------------------------------------- */
2927
    /*      Compute top/left origin.                                        */
2928
    /* -------------------------------------------------------------------- */
2929
    double gt_normalized[6] = {0.0};
320✔
2930
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
320✔
2931
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
320✔
2932
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
320✔
2933
                       divisor;
2934

2935
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
320✔
2936
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
320✔
2937
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
320✔
2938
                       divisor;
2939

2940
    /* -------------------------------------------------------------------- */
2941
    /*      Compute X related coefficients.                                 */
2942
    /* -------------------------------------------------------------------- */
2943
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
320✔
2944
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
320✔
2945
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
320✔
2946
                       divisor;
2947

2948
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
320✔
2949
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
320✔
2950
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
320✔
2951
                       divisor;
2952

2953
    /* -------------------------------------------------------------------- */
2954
    /*      Compute Y related coefficients.                                 */
2955
    /* -------------------------------------------------------------------- */
2956
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
320✔
2957
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
320✔
2958
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
320✔
2959
                       divisor;
2960

2961
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
320✔
2962
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
320✔
2963
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
320✔
2964
                       divisor;
2965

2966
    /* -------------------------------------------------------------------- */
2967
    /*      Compose the resulting transformation with the normalization     */
2968
    /*      geotransformations.                                             */
2969
    /* -------------------------------------------------------------------- */
2970
    double gt1p2[6] = {0.0};
320✔
2971
    double inv_geo_normalize[6] = {0.0};
320✔
2972
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
320✔
2973
        return FALSE;
×
2974

2975
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
320✔
2976
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
320✔
2977

2978
    /* -------------------------------------------------------------------- */
2979
    /*      Now check if any of the input points fit this poorly.           */
2980
    /* -------------------------------------------------------------------- */
2981
    if (!bApproxOK)
320✔
2982
    {
2983
        // FIXME? Not sure if it is the more accurate way of computing
2984
        // pixel size
2985
        double dfPixelSize =
2986
            0.5 *
2987
            (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
313✔
2988
             std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
313✔
2989
        if (dfPixelSize == 0.0)
313✔
2990
        {
2991
            CPLDebug("GDAL", "dfPixelSize = 0");
×
2992
            return FALSE;
×
2993
        }
2994

2995
        for (int i = 0; i < nGCPCount; i++)
1,641✔
2996
        {
2997
            const double dfErrorX =
1,334✔
2998
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
1,334✔
2999
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
1,334✔
3000
                 padfGeoTransform[0]) -
1,334✔
3001
                pasGCPs[i].dfGCPX;
1,334✔
3002
            const double dfErrorY =
1,334✔
3003
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
1,334✔
3004
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
1,334✔
3005
                 padfGeoTransform[3]) -
1,334✔
3006
                pasGCPs[i].dfGCPY;
1,334✔
3007

3008
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
2,665✔
3009
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
1,331✔
3010
            {
3011
                CPLDebug("GDAL",
6✔
3012
                         "dfErrorX/dfPixelSize = %.2f, "
3013
                         "dfErrorY/dfPixelSize = %.2f",
3014
                         std::abs(dfErrorX) / dfPixelSize,
6✔
3015
                         std::abs(dfErrorY) / dfPixelSize);
6✔
3016
                return FALSE;
6✔
3017
            }
3018
        }
3019
    }
3020

3021
    return TRUE;
314✔
3022
}
3023

3024
/************************************************************************/
3025
/*                      GDALComposeGeoTransforms()                      */
3026
/************************************************************************/
3027

3028
/**
3029
 * \brief Compose two geotransforms.
3030
 *
3031
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3032
 * being applied to a point.
3033
 *
3034
 * @param padfGT1 the first geotransform, six values.
3035
 * @param padfGT2 the second geotransform, six values.
3036
 * @param padfGTOut the output geotransform, six values, may safely be the same
3037
 * array as padfGT1 or padfGT2.
3038
 */
3039

3040
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
640✔
3041
                              double *padfGTOut)
3042

3043
{
3044
    double gtwrk[6] = {0.0};
640✔
3045
    // We need to think of the geotransform in a more normal form to do
3046
    // the matrix multiple:
3047
    //
3048
    //  __                     __
3049
    //  | gt[1]   gt[2]   gt[0] |
3050
    //  | gt[4]   gt[5]   gt[3] |
3051
    //  |  0.0     0.0     1.0  |
3052
    //  --                     --
3053
    //
3054
    // Then we can use normal matrix multiplication to produce the
3055
    // composed transformation.  I don't actually reform the matrix
3056
    // explicitly which is why the following may seem kind of spagettish.
3057

3058
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
640✔
3059
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
640✔
3060
    gtwrk[0] =
640✔
3061
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
640✔
3062

3063
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
640✔
3064
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
640✔
3065
    gtwrk[3] =
640✔
3066
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
640✔
3067
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
640✔
3068
}
640✔
3069

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

3074
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
8✔
3075
{
3076
    if (psCOL == nullptr)
8✔
3077
        return;
×
3078
    if (nOptions == 0)
8✔
3079
        nOptions = GDAL_OF_RASTER;
5✔
3080
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
8✔
3081
        return;
×
3082

3083
    CPLXMLNode *psPrev = nullptr;
8✔
3084
    for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
173✔
3085
    {
3086
        if (psIter->eType == CXT_Element)
165✔
3087
        {
3088
            CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
165✔
3089
            bool bStrip = false;
165✔
3090
            if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
165✔
3091
                psScope->psChild->pszValue &&
35✔
3092
                EQUAL(psScope->psChild->pszValue, "vector"))
35✔
3093
            {
3094
                bStrip = true;
1✔
3095
            }
3096
            else if (nOptions == GDAL_OF_VECTOR && psScope &&
164✔
3097
                     psScope->psChild && psScope->psChild->pszValue &&
35✔
3098
                     EQUAL(psScope->psChild->pszValue, "raster"))
35✔
3099
            {
3100
                bStrip = true;
33✔
3101
            }
3102
            if (psScope)
165✔
3103
            {
3104
                CPLRemoveXMLChild(psIter, psScope);
70✔
3105
                CPLDestroyXMLNode(psScope);
70✔
3106
            }
3107

3108
            CPLXMLNode *psNext = psIter->psNext;
165✔
3109
            if (bStrip)
165✔
3110
            {
3111
                if (psPrev)
34✔
3112
                    psPrev->psNext = psNext;
13✔
3113
                else if (psCOL->psChild == psIter)
21✔
3114
                    psCOL->psChild = psNext;
21✔
3115
                psIter->psNext = nullptr;
34✔
3116
                CPLDestroyXMLNode(psIter);
34✔
3117
                psIter = psNext;
34✔
3118
            }
3119
            else
3120
            {
3121
                psPrev = psIter;
131✔
3122
                psIter = psNext;
131✔
3123
            }
3124
        }
3125
        else
3126
        {
3127
            psIter = psIter->psNext;
×
3128
        }
3129
    }
3130
}
3131

3132
/************************************************************************/
3133
/*                    GDALGeneralCmdLineProcessor()                     */
3134
/************************************************************************/
3135

3136
/**
3137
 * \brief General utility option processing.
3138
 *
3139
 * This function is intended to provide a variety of generic commandline
3140
 * options for all GDAL commandline utilities.  It takes care of the following
3141
 * commandline options:
3142
 *
3143
 *  --version: report version of GDAL in use.
3144
 *  --build: report build info about GDAL in use.
3145
 *  --license: report GDAL license info.
3146
 *  --formats: report all format drivers configured.
3147
 *  --format [format]: report details of one format driver.
3148
 *  --optfile filename: expand an option file into the argument list.
3149
 *  --config key value: set system configuration option.
3150
 *  --config key=value: set system configuration option (since GDAL 3.9)
3151
 *  --debug [on/off/value]: set debug level.
3152
 *  --mempreload dir: preload directory contents into /vsimem
3153
 *  --pause: Pause for user input (allows time to attach debugger)
3154
 *  --locale [locale]: Install a locale using setlocale() (debugging)
3155
 *  --help-general: report detailed help on general options.
3156
 *
3157
 * The argument array is replaced "in place" and should be freed with
3158
 * CSLDestroy() when no longer needed.  The typical usage looks something
3159
 * like the following.  Note that the formats should be registered so that
3160
 * the --formats and --format options will work properly.
3161
 *
3162
 *  int main( int argc, char ** argv )
3163
 *  {
3164
 *    GDALAllRegister();
3165
 *
3166
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3167
 *    if( argc < 1 )
3168
 *        exit( -argc );
3169
 *
3170
 * @param nArgc number of values in the argument list.
3171
 * @param ppapszArgv pointer to the argument list array (will be updated in
3172
 * place).
3173
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3174
 *                 to determine which drivers should be displayed by --formats.
3175
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3176
 *
3177
 * @return updated nArgc argument count.  Return of 0 requests terminate
3178
 * without error, return of -1 requests exit with error code.
3179
 */
3180

3181
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
1,192✔
3182
                                            int nOptions)
3183

3184
{
3185
    CPLStringList aosReturn;
2,384✔
3186
    int iArg;
3187
    char **papszArgv = *ppapszArgv;
1,192✔
3188

3189
    /* -------------------------------------------------------------------- */
3190
    /*      Preserve the program name.                                      */
3191
    /* -------------------------------------------------------------------- */
3192
    aosReturn.AddString(papszArgv[0]);
1,192✔
3193

3194
    /* ==================================================================== */
3195
    /*      Loop over all arguments.                                        */
3196
    /* ==================================================================== */
3197
    for (iArg = 1; iArg < nArgc; iArg++)
7,282✔
3198
    {
3199
        /* --------------------------------------------------------------------
3200
         */
3201
        /*      --version */
3202
        /* --------------------------------------------------------------------
3203
         */
3204
        if (EQUAL(papszArgv[iArg], "--version"))
6,134✔
3205
        {
3206
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
19✔
3207
            return 0;
19✔
3208
        }
3209

3210
        /* --------------------------------------------------------------------
3211
         */
3212
        /*      --build */
3213
        /* --------------------------------------------------------------------
3214
         */
3215
        else if (EQUAL(papszArgv[iArg], "--build"))
6,115✔
3216
        {
3217
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
1✔
3218
            return 0;
1✔
3219
        }
3220

3221
        /* --------------------------------------------------------------------
3222
         */
3223
        /*      --license */
3224
        /* --------------------------------------------------------------------
3225
         */
3226
        else if (EQUAL(papszArgv[iArg], "--license"))
6,114✔
3227
        {
3228
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
1✔
3229
            return 0;
1✔
3230
        }
3231

3232
        /* --------------------------------------------------------------------
3233
         */
3234
        /*      --config */
3235
        /* --------------------------------------------------------------------
3236
         */
3237
        else if (EQUAL(papszArgv[iArg], "--config"))
6,113✔
3238
        {
3239
            if (iArg + 1 >= nArgc)
43✔
3240
            {
3241
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3242
                         "--config option given without a key=value argument.");
3243
                return -1;
2✔
3244
            }
3245

3246
            const char *pszArg = papszArgv[iArg + 1];
41✔
3247
            if (strchr(pszArg, '=') != nullptr)
41✔
3248
            {
3249
                char *pszKey = nullptr;
1✔
3250
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
1✔
3251
                if (pszKey && pszValue)
1✔
3252
                {
3253
                    CPLSetConfigOption(pszKey, pszValue);
1✔
3254
                }
3255
                CPLFree(pszKey);
1✔
3256
                ++iArg;
1✔
3257
            }
3258
            else
3259
            {
3260
                if (iArg + 2 >= nArgc)
40✔
3261
                {
3262
                    CPLError(CE_Failure, CPLE_AppDefined,
2✔
3263
                             "--config option given without a key and value "
3264
                             "argument.");
3265
                    return -1;
2✔
3266
                }
3267

3268
                CPLSetConfigOption(papszArgv[iArg + 1], papszArgv[iArg + 2]);
38✔
3269

3270
                iArg += 2;
38✔
3271
            }
3272
        }
3273

3274
        /* --------------------------------------------------------------------
3275
         */
3276
        /*      --mempreload */
3277
        /* --------------------------------------------------------------------
3278
         */
3279
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
6,070✔
3280
        {
3281
            if (iArg + 1 >= nArgc)
4✔
3282
            {
3283
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3284
                         "--mempreload option given without directory path.");
3285
                return -1;
2✔
3286
            }
3287

3288
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
2✔
3289
            if (CSLCount(papszFiles) == 0)
2✔
3290
            {
3291
                CPLError(CE_Failure, CPLE_AppDefined,
×
3292
                         "--mempreload given invalid or empty directory.");
3293
                return -1;
×
3294
            }
3295

3296
            for (int i = 0; papszFiles[i] != nullptr; i++)
488✔
3297
            {
3298
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
486✔
3299
                    continue;
71✔
3300

3301
                CPLString osOldPath, osNewPath;
482✔
3302
                osOldPath = CPLFormFilename(papszArgv[iArg + 1], papszFiles[i],
482✔
3303
                                            nullptr);
482✔
3304
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
482✔
3305

3306
                VSIStatBufL sStatBuf;
3307
                if (VSIStatL(osOldPath, &sStatBuf) != 0 ||
964✔
3308
                    VSI_ISDIR(sStatBuf.st_mode))
482✔
3309
                {
3310
                    CPLDebug("VSI", "Skipping preload of %s.",
67✔
3311
                             osOldPath.c_str());
3312
                    continue;
67✔
3313
                }
3314

3315
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
415✔
3316
                         osNewPath.c_str());
3317

3318
                if (CPLCopyFile(osNewPath, osOldPath) != 0)
415✔
3319
                {
3320
                    CPLError(CE_Failure, CPLE_AppDefined,
×
3321
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3322
                    return -1;
×
3323
                }
3324
            }
3325

3326
            CSLDestroy(papszFiles);
2✔
3327
            iArg += 1;
2✔
3328
        }
3329

3330
        /* --------------------------------------------------------------------
3331
         */
3332
        /*      --debug */
3333
        /* --------------------------------------------------------------------
3334
         */
3335
        else if (EQUAL(papszArgv[iArg], "--debug"))
6,066✔
3336
        {
3337
            if (iArg + 1 >= nArgc)
15✔
3338
            {
3339
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3340
                         "--debug option given without debug level.");
3341
                return -1;
2✔
3342
            }
3343

3344
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
13✔
3345
            iArg += 1;
13✔
3346
        }
3347

3348
        /* --------------------------------------------------------------------
3349
         */
3350
        /*      --optfile */
3351
        /* --------------------------------------------------------------------
3352
         */
3353
        else if (EQUAL(papszArgv[iArg], "--optfile"))
6,051✔
3354
        {
3355
            if (iArg + 1 >= nArgc)
11✔
3356
            {
3357
                CPLError(CE_Failure, CPLE_AppDefined,
2✔
3358
                         "--optfile option given without filename.");
3359
                return -1;
5✔
3360
            }
3361

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

3364
            if (fpOptFile == nullptr)
9✔
3365
            {
3366
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
3367
                         "Unable to open optfile '%s'.\n%s",
3368
                         papszArgv[iArg + 1], VSIStrerror(errno));
2✔
3369
                return -1;
2✔
3370
            }
3371

3372
            const char *pszLine;
3373
            CPLStringList aosArgvOptfile;
7✔
3374
            // dummy value as first argument to please
3375
            // GDALGeneralCmdLineProcessor()
3376
            aosArgvOptfile.AddString("");
7✔
3377
            bool bHasOptfile = false;
7✔
3378
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
23✔
3379
            {
3380
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
16✔
3381
                    continue;
3✔
3382

3383
                char **papszTokens = CSLTokenizeString(pszLine);
13✔
3384
                for (int i = 0;
13✔
3385
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
45✔
3386
                {
3387
                    if (EQUAL(papszTokens[i], "--optfile"))
32✔
3388
                    {
3389
                        // To avoid potential recursion
3390
                        CPLError(CE_Warning, CPLE_AppDefined,
×
3391
                                 "--optfile not supported in a option file");
3392
                        bHasOptfile = true;
×
3393
                    }
3394
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
32✔
3395
                    papszTokens[i] = nullptr;
32✔
3396
                }
3397
                CSLDestroy(papszTokens);
13✔
3398
            }
3399

3400
            VSIFCloseL(fpOptFile);
7✔
3401

3402
            char **papszArgvOptfile = aosArgvOptfile.StealList();
7✔
3403
            if (!bHasOptfile)
7✔
3404
            {
3405
                char **papszArgvOptfileBefore = papszArgvOptfile;
7✔
3406
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
7✔
3407
                                                &papszArgvOptfile,
3408
                                                nOptions) < 0)
7✔
3409
                {
3410
                    CSLDestroy(papszArgvOptfile);
1✔
3411
                    return -1;
1✔
3412
                }
3413
                CSLDestroy(papszArgvOptfileBefore);
6✔
3414
            }
3415

3416
            char **papszIter = papszArgvOptfile + 1;
6✔
3417
            while (*papszIter)
36✔
3418
            {
3419
                aosReturn.AddString(*papszIter);
30✔
3420
                ++papszIter;
30✔
3421
            }
3422
            CSLDestroy(papszArgvOptfile);
6✔
3423

3424
            iArg += 1;
6✔
3425
        }
3426

3427
        /* --------------------------------------------------------------------
3428
         */
3429
        /*      --formats */
3430
        /* --------------------------------------------------------------------
3431
         */
3432
        else if (EQUAL(papszArgv[iArg], "--formats"))
6,040✔
3433
        {
3434
            if (nOptions == 0)
3✔
3435
                nOptions = GDAL_OF_RASTER;
1✔
3436

3437
            printf(/*ok*/
3✔
3438
                   "Supported Formats: (ro:read-only, rw:read-write, +:update, "
3439
                   "v:virtual-I/O s:subdatasets)\n");
3440
            for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
702✔
3441
            {
3442
                GDALDriverH hDriver = GDALGetDriver(iDr);
699✔
3443

3444
                const char *pszRFlag = "", *pszWFlag, *pszVirtualIO,
699✔
3445
                           *pszSubdatasets;
3446
                char **papszMD = GDALGetMetadata(hDriver, nullptr);
699✔
3447

3448
                if (nOptions == GDAL_OF_RASTER &&
932✔
3449
                    !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
233✔
3450
                    continue;
359✔
3451
                if (nOptions == GDAL_OF_VECTOR &&
1,092✔
3452
                    !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
466✔
3453
                    continue;
286✔
3454
                if (nOptions == GDAL_OF_GNM &&
340✔
3455
                    !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
×
3456
                    continue;
×
3457
                if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
340✔
3458
                    !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
×
3459
                    continue;
×
3460

3461
                if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
340✔
3462
                    pszRFlag = "r";
337✔
3463

3464
                if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
340✔
3465
                    pszWFlag = "w+";
171✔
3466
                else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
169✔
3467
                    pszWFlag = "w";
35✔
3468
                else
3469
                    pszWFlag = "o";
134✔
3470

3471
                if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
340✔
3472
                    pszVirtualIO = "v";
275✔
3473
                else
3474
                    pszVirtualIO = "";
65✔
3475

3476
                if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
340✔
3477
                    pszSubdatasets = "s";
44✔
3478
                else
3479
                    pszSubdatasets = "";
296✔
3480

3481
                CPLString osKind;
680✔
3482
                if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
340✔
3483
                    osKind = "raster";
198✔
3484
                if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
340✔
3485
                {
3486
                    if (!osKind.empty())
18✔
3487
                        osKind += ',';
18✔
3488
                    osKind += "multidimensional raster";
18✔
3489
                }
3490
                if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
340✔
3491
                {
3492
                    if (!osKind.empty())
199✔
3493
                        osKind += ',';
57✔
3494
                    osKind += "vector";
199✔
3495
                }
3496
                if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
340✔
3497
                {
3498
                    if (!osKind.empty())
×
3499
                        osKind += ',';
×
3500
                    osKind += "geography network";
×
3501
                }
3502
                if (osKind.empty())
340✔
3503
                    osKind = "unknown kind";
×
3504

3505
                std::string osExtensions;
680✔
3506
                if (const char *pszExtensions = CSLFetchNameValueDef(
340✔
3507
                        papszMD, GDAL_DMD_EXTENSIONS,
3508
                        CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3509
                {
3510
                    const CPLStringList aosExt(
3511
                        CSLTokenizeString2(pszExtensions, " ", 0));
452✔
3512
                    for (int i = 0; i < aosExt.size(); ++i)
542✔
3513
                    {
3514
                        if (i == 0)
316✔
3515
                            osExtensions = " (*.";
224✔
3516
                        else
3517
                            osExtensions += ", *.";
92✔
3518
                        osExtensions += aosExt[i];
316✔
3519
                    }
3520
                    if (!osExtensions.empty())
226✔
3521
                        osExtensions += ')';
224✔
3522
                }
3523

3524
                printf("  %s -%s- (%s%s%s%s): %s%s\n", /*ok*/
340✔
3525
                       GDALGetDriverShortName(hDriver), osKind.c_str(),
3526
                       pszRFlag, pszWFlag, pszVirtualIO, pszSubdatasets,
3527
                       GDALGetDriverLongName(hDriver), osExtensions.c_str());
3528
            }
3529

3530
            return 0;
3✔
3531
        }
3532

3533
        /* --------------------------------------------------------------------
3534
         */
3535
        /*      --format */
3536
        /* --------------------------------------------------------------------
3537
         */
3538
        else if (EQUAL(papszArgv[iArg], "--format"))
6,037✔
3539
        {
3540
            GDALDriverH hDriver;
3541
            char **papszMD;
3542

3543
            if (iArg + 1 >= nArgc)
5✔
3544
            {
3545
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
3546
                         "--format option given without a format code.");
3547
                return -1;
1✔
3548
            }
3549

3550
            hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
4✔
3551
            if (hDriver == nullptr)
4✔
3552
            {
3553
                CPLError(CE_Failure, CPLE_AppDefined,
1✔
3554
                         "--format option given with format '%s', but that "
3555
                         "format not\nrecognised.  Use the --formats option "
3556
                         "to get a list of available formats,\n"
3557
                         "and use the short code (i.e. GTiff or HFA) as the "
3558
                         "format identifier.\n",
3559
                         papszArgv[iArg + 1]);
1✔
3560
                return -1;
1✔
3561
            }
3562

3563
            printf("Format Details:\n"); /*ok*/
3✔
3564
            printf(/*ok*/ "  Short Name: %s\n",
3✔
3565
                   GDALGetDriverShortName(hDriver));
3566
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
3✔
3567

3568
            papszMD = GDALGetMetadata(hDriver, nullptr);
3✔
3569
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3✔
3570
                printf("  Supports: Raster\n"); /*ok*/
3✔
3571
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3✔
3572
                printf("  Supports: Multidimensional raster\n"); /*ok*/
×
3573
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3✔
3574
                printf("  Supports: Vector\n"); /*ok*/
2✔
3575
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3✔
3576
                printf("  Supports: Geography Network\n"); /*ok*/
×
3577

3578
            const char *pszExt =
3579
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
3✔
3580
            if (pszExt != nullptr)
3✔
3581
                printf("  Extension%s: %s\n", /*ok*/
3✔
3582
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
3✔
3583

3584
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
3✔
3585
                printf("  Mime Type: %s\n", /*ok*/
1✔
3586
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
3587
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
3✔
3588
                printf("  Help Topic: %s\n", /*ok*/
3✔
3589
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
3590

3591
            if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
3✔
3592
                printf("  Supports: Raster subdatasets\n"); /*ok*/
3✔
3593
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3✔
3594
                printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
3✔
3595
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3✔
3596
                printf(/*ok*/
3✔
3597
                       "  Supports: Create() - Create writable dataset.\n");
3598
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
3✔
3599
                printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
×
3600
                              "multidimensional dataset.\n");
3601
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3✔
3602
                printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
3✔
3603
                              "copying "
3604
                              "another.\n");
3605
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3✔
3606
                printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
3✔
3607
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
3✔
3608
                printf("  Creation Datatypes: %s\n", /*ok*/
3✔
3609
                       CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
3610
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
3✔
3611
                printf("  Creation Field Datatypes: %s\n", /*ok*/
2✔
3612
                       CSLFetchNameValue(papszMD,
3613
                                         GDAL_DMD_CREATIONFIELDDATATYPES));
3614
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
3✔
3615
                printf("  Creation Field Data Sub-types: %s\n", /*ok*/
2✔
3616
                       CSLFetchNameValue(papszMD,
3617
                                         GDAL_DMD_CREATIONFIELDDATASUBTYPES));
3618
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
3✔
3619
                printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
2✔
3620
                              "constraint.\n");
3621
            if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
3✔
3622
                printf(/*ok*/
2✔
3623
                       "  Supports: Creating fields with UNIQUE constraint.\n");
3624
            if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
3✔
3625
                printf(/*ok*/
2✔
3626
                       "  Supports: Creating fields with DEFAULT values.\n");
3627
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
3✔
3628
                /*ok*/ printf(
2✔
3629
                    "  Supports: Creating geometry fields with NOT NULL "
3630
                    "constraint.\n");
3631
            if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
3✔
3632
                             false))
3633
                /*ok*/ printf("  Supports: Writing geometries with given "
2✔
3634
                              "coordinate precision\n");
3635
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
3✔
3636
                printf("  Supports: Reading feature styles.\n"); /*ok*/
×
3637
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
3✔
3638
                printf("  Supports: Writing feature styles.\n"); /*ok*/
×
3639
            if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
3✔
3640
                printf("  Supports: Coordinate epoch.\n"); /*ok*/
1✔
3641
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
3✔
3642
                printf("  Supports: Multiple vector layers.\n"); /*ok*/
2✔
3643
            if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
3✔
3644
                printf("  Supports: Reading field domains.\n"); /*ok*/
2✔
3645
            if (CSLFetchNameValue(papszMD,
3✔
3646
                                  GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
3✔
3647
                printf("  Creation field domain types: %s\n", /*ok*/
2✔
3648
                       CSLFetchNameValue(papszMD,
3649
                                         GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
3650
            if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
3✔
3651
                printf("  Supported SQL dialects: %s\n", /*ok*/
2✔
3652
                       CSLFetchNameValue(papszMD,
3653
                                         GDAL_DMD_SUPPORTED_SQL_DIALECTS));
3654

3655
            for (const char *key :
24✔
3656
                 {GDAL_DMD_CREATIONOPTIONLIST,
3657
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
3658
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
3659
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
3660
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
3661
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
3662
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
3663
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
27✔
3664
            {
3665
                if (CSLFetchNameValue(papszMD, key))
24✔
3666
                {
3667
                    CPLXMLNode *psCOL =
3668
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
5✔
3669
                    StripIrrelevantOptions(psCOL, nOptions);
5✔
3670
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
5✔
3671

3672
                    CPLDestroyXMLNode(psCOL);
5✔
3673

3674
                    printf("\n%s\n", pszFormattedXML); /*ok*/
5✔
3675
                    CPLFree(pszFormattedXML);
5✔
3676
                }
3677
            }
3678

3679
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
3✔
3680
                printf("  Connection prefix: %s\n", /*ok*/
×
3681
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
3682

3683
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
3✔
3684
            {
3685
                CPLXMLNode *psCOL = CPLParseXMLString(
3✔
3686
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
3687
                StripIrrelevantOptions(psCOL, nOptions);
3✔
3688
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
3✔
3689

3690
                CPLDestroyXMLNode(psCOL);
3✔
3691

3692
                printf("%s\n", pszFormattedXML); /*ok*/
3✔
3693
                CPLFree(pszFormattedXML);
3✔
3694
            }
3695

3696
            bool bFirstOtherOption = true;
3✔
3697
            for (char **papszIter = papszMD; papszIter && *papszIter;
112✔
3698
                 ++papszIter)
3699
            {
3700
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
109✔
3701
                    !STARTS_WITH(*papszIter, "DMD_") &&
49✔
3702
                    !STARTS_WITH(*papszIter, "DS_") &&
14✔
3703
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
12✔
3704
                {
3705
                    if (bFirstOtherOption)
12✔
3706
                        printf("  Other metadata items:\n"); /*ok*/
3✔
3707
                    bFirstOtherOption = false;
12✔
3708
                    printf("    %s\n", *papszIter); /*ok*/
12✔
3709
                }
3710
            }
3711

3712
            return 0;
3✔
3713
        }
3714

3715
        /* --------------------------------------------------------------------
3716
         */
3717
        /*      --help-general */
3718
        /* --------------------------------------------------------------------
3719
         */
3720
        else if (EQUAL(papszArgv[iArg], "--help-general"))
6,032✔
3721
        {
3722
            printf("Generic GDAL utility command options:\n");       /*ok*/
2✔
3723
            printf("  --version: report version of GDAL in use.\n"); /*ok*/
2✔
3724
            /*ok*/ printf(
2✔
3725
                "  --build: report detailed information about GDAL in "
3726
                "use.\n");
3727
            printf("  --license: report GDAL license info.\n"); /*ok*/
2✔
3728
            printf(                                             /*ok*/
2✔
3729
                   "  --formats: report all configured format drivers.\n"); /*ok*/
3730
            printf("  --format [<format>]: details of one format.\n"); /*ok*/
2✔
3731
            /*ok*/ printf(
2✔
3732
                "  --optfile filename: expand an option file into the "
3733
                "argument list.\n");
3734
            printf(/*ok*/
2✔
3735
                   "  --config <key> <value> or --config <key>=<value>: set "
3736
                   "system configuration option.\n");               /*ok*/
3737
            printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
2✔
3738
            /*ok*/ printf(                                          /*ok*/
2✔
3739
                          "  --pause: wait for user input, time to attach "
3740
                          "debugger\n");
3741
            printf("  --locale [<locale>]: install locale for debugging " /*ok*/
2✔
3742
                   "(i.e. en_US.UTF-8)\n");
3743
            printf("  --help-general: report detailed help on general " /*ok*/
2✔
3744
                   "options.\n");
3745

3746
            return 0;
2✔
3747
        }
3748

3749
        /* --------------------------------------------------------------------
3750
         */
3751
        /*      --locale */
3752
        /* --------------------------------------------------------------------
3753
         */
3754
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
6,030✔
3755
        {
3756
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
2✔
3757
        }
3758

3759
        /* --------------------------------------------------------------------
3760
         */
3761
        /*      --pause */
3762
        /* --------------------------------------------------------------------
3763
         */
3764
        else if (EQUAL(papszArgv[iArg], "--pause"))
6,028✔
3765
        {
3766
            std::cout << "Hit <ENTER> to Continue." << std::endl;
×
3767
            std::cin.clear();
×
3768
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
×
3769
        }
3770

3771
        /* --------------------------------------------------------------------
3772
         */
3773
        /*      Carry through unrecognized options. */
3774
        /* --------------------------------------------------------------------
3775
         */
3776
        else
3777
        {
3778
            aosReturn.AddString(papszArgv[iArg]);
6,028✔
3779
        }
3780
    }
3781

3782
    const int nSize = aosReturn.size();
1,148✔
3783
    *ppapszArgv = aosReturn.StealList();
1,148✔
3784

3785
    return nSize;
1,148✔
3786
}
3787

3788
/************************************************************************/
3789
/*                          _FetchDblFromMD()                           */
3790
/************************************************************************/
3791

3792
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
1,660✔
3793
                            double *padfTarget, int nCount, double dfDefault)
3794

3795
{
3796
    char szFullKey[200];
3797

3798
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
1,660✔
3799

3800
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
1,660✔
3801

3802
    for (int i = 0; i < nCount; i++)
9,628✔
3803
        padfTarget[i] = dfDefault;
7,968✔
3804

3805
    if (pszValue == nullptr)
1,660✔
3806
        return false;
408✔
3807

3808
    if (nCount == 1)
1,252✔
3809
    {
3810
        *padfTarget = CPLAtofM(pszValue);
920✔
3811
        return true;
920✔
3812
    }
3813

3814
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
332✔
3815

3816
    if (CSLCount(papszTokens) != nCount)
332✔
3817
    {
3818
        CSLDestroy(papszTokens);
×
3819
        return false;
×
3820
    }
3821

3822
    for (int i = 0; i < nCount; i++)
6,972✔
3823
        padfTarget[i] = CPLAtofM(papszTokens[i]);
6,640✔
3824

3825
    CSLDestroy(papszTokens);
332✔
3826

3827
    return true;
332✔
3828
}
3829

3830
/************************************************************************/
3831
/*                         GDALExtractRPCInfo()                         */
3832
/************************************************************************/
3833

3834
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
3835
 *
3836
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
3837
 *
3838
 * @param papszMD Dictionary of metadata representing RPC
3839
 * @param psRPC (output) Pointer to structure to hold the RPC values.
3840
 * @return TRUE in case of success. FALSE in case of failure.
3841
 */
3842
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
×
3843

3844
{
3845
    GDALRPCInfoV2 sRPC;
3846
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
×
3847
        return FALSE;
×
3848
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
×
3849
    return TRUE;
×
3850
}
3851

3852
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
3853
 *
3854
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
3855
 *
3856
 * @param papszMD Dictionary of metadata representing RPC
3857
 * @param psRPC (output) Pointer to structure to hold the RPC values.
3858
 * @return TRUE in case of success. FALSE in case of failure.
3859
 */
3860
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
83✔
3861

3862
{
3863
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
83✔
3864
        return FALSE;
×
3865

3866
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
83✔
3867
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
83✔
3868
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
249✔
3869
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
83✔
3870
    {
3871
        CPLError(CE_Failure, CPLE_AppDefined,
×
3872
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
3873
        return FALSE;
×
3874
    }
3875

3876
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
83✔
3877
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
83✔
3878
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
83✔
3879
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
83✔
3880
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
83✔
3881
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
83✔
3882
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
83✔
3883
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
83✔
3884
                    1.0);
3885
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
83✔
3886
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
83✔
3887
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
83✔
3888
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
83✔
3889

3890
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
83✔
3891
                    0.0);
3892
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
83✔
3893
                    0.0);
3894
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
83✔
3895
                    0.0);
3896
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
83✔
3897
                    0.0);
3898

3899
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
83✔
3900
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
83✔
3901
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
83✔
3902
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
83✔
3903

3904
    return TRUE;
83✔
3905
}
3906

3907
/************************************************************************/
3908
/*                     GDALFindAssociatedAuxFile()                      */
3909
/************************************************************************/
3910

3911
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
10,820✔
3912
                                       GDALAccess eAccess,
3913
                                       GDALDataset *poDependentDS)
3914

3915
{
3916
    const char *pszAuxSuffixLC = "aux";
10,820✔
3917
    const char *pszAuxSuffixUC = "AUX";
10,820✔
3918

3919
    if (EQUAL(CPLGetExtension(pszBasename), pszAuxSuffixLC))
10,820✔
3920
        return nullptr;
34✔
3921

3922
    /* -------------------------------------------------------------------- */
3923
    /*      Don't even try to look for an .aux file if we don't have a      */
3924
    /*      path of any kind.                                               */
3925
    /* -------------------------------------------------------------------- */
3926
    if (strlen(pszBasename) == 0)
10,786✔
3927
        return nullptr;
17✔
3928

3929
    /* -------------------------------------------------------------------- */
3930
    /*      We didn't find that, so try and find a corresponding aux        */
3931
    /*      file.  Check that we are the dependent file of the aux          */
3932
    /*      file, or if we aren't verify that the dependent file does       */
3933
    /*      not exist, likely mean it is us but some sort of renaming       */
3934
    /*      has occurred.                                                   */
3935
    /* -------------------------------------------------------------------- */
3936
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
21,538✔
3937
    CPLString osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixLC);
10,769✔
3938
    GDALDataset *poODS = nullptr;
10,769✔
3939
    GByte abyHeader[32];
3940

3941
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
10,769✔
3942

3943
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,769✔
3944
    {
3945
        // Can't found file with lower case suffix. Try the upper case one.
3946
        osAuxFilename = CPLResetExtension(pszBasename, pszAuxSuffixUC);
10,720✔
3947
        fp = VSIFOpenL(osAuxFilename, "rb");
10,720✔
3948
    }
3949

3950
    if (fp != nullptr)
10,769✔
3951
    {
3952
        if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
98✔
3953
            STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
49✔
3954
                           "EHFA_HEADER_TAG"))
3955
        {
3956
            /* Avoid causing failure in opening of main file from SWIG bindings
3957
             */
3958
            /* when auxiliary file cannot be opened (#3269) */
3959
            CPLTurnFailureIntoWarning(TRUE);
19✔
3960
            if (poDependentDS != nullptr && poDependentDS->GetShared())
19✔
3961
                poODS = GDALDataset::FromHandle(
×
3962
                    GDALOpenShared(osAuxFilename, eAccess));
3963
            else
3964
                poODS =
3965
                    GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
19✔
3966
            CPLTurnFailureIntoWarning(FALSE);
19✔
3967
        }
3968
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
49✔
3969
    }
3970

3971
    /* -------------------------------------------------------------------- */
3972
    /*      Try replacing extension with .aux                               */
3973
    /* -------------------------------------------------------------------- */
3974
    if (poODS != nullptr)
10,769✔
3975
    {
3976
        const char *pszDep =
3977
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
19✔
3978
        if (pszDep == nullptr)
19✔
3979
        {
3980
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
×
3981
                     osAuxFilename.c_str());
3982
            GDALClose(poODS);
×
3983
            poODS = nullptr;
×
3984
        }
3985
        else if (!EQUAL(pszDep, osJustFile))
19✔
3986
        {
3987
            VSIStatBufL sStatBuf;
3988

3989
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
3990
            {
3991
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
3992
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
3993
                GDALClose(poODS);
×
3994
                poODS = nullptr;
×
3995
            }
3996
            else
3997
            {
3998
                CPLDebug("AUX",
×
3999
                         "%s is for file %s, not %s, but since\n"
4000
                         "%s does not exist, we will use .aux file as our own.",
4001
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4002
                         pszDep);
4003
            }
4004
        }
4005

4006
        /* --------------------------------------------------------------------
4007
         */
4008
        /*      Confirm that the aux file matches the configuration of the */
4009
        /*      dependent dataset. */
4010
        /* --------------------------------------------------------------------
4011
         */
4012
        if (poODS != nullptr && poDependentDS != nullptr &&
38✔
4013
            (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
19✔
4014
             poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
19✔
4015
             poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
16✔
4016
        {
4017
            CPLDebug("AUX",
3✔
4018
                     "Ignoring aux file %s as its raster configuration\n"
4019
                     "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4020
                     "x %dB)",
4021
                     osAuxFilename.c_str(), poODS->GetRasterXSize(),
4022
                     poODS->GetRasterYSize(), poODS->GetRasterCount(),
4023
                     poDependentDS->GetRasterXSize(),
4024
                     poDependentDS->GetRasterYSize(),
4025
                     poDependentDS->GetRasterCount());
4026

4027
            GDALClose(poODS);
3✔
4028
            poODS = nullptr;
3✔
4029
        }
4030
    }
4031

4032
    /* -------------------------------------------------------------------- */
4033
    /*      Try appending .aux to the end of the filename.                  */
4034
    /* -------------------------------------------------------------------- */
4035
    if (poODS == nullptr)
10,769✔
4036
    {
4037
        osAuxFilename = pszBasename;
10,753✔
4038
        osAuxFilename += ".";
10,753✔
4039
        osAuxFilename += pszAuxSuffixLC;
10,753✔
4040
        fp = VSIFOpenL(osAuxFilename, "rb");
10,753✔
4041
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
10,753✔
4042
        {
4043
            // Can't found file with lower case suffix. Try the upper case one.
4044
            osAuxFilename = pszBasename;
10,730✔
4045
            osAuxFilename += ".";
10,730✔
4046
            osAuxFilename += pszAuxSuffixUC;
10,730✔
4047
            fp = VSIFOpenL(osAuxFilename, "rb");
10,730✔
4048
        }
4049

4050
        if (fp != nullptr)
10,753✔
4051
        {
4052
            if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
46✔
4053
                STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
23✔
4054
                               "EHFA_HEADER_TAG"))
4055
            {
4056
                /* Avoid causing failure in opening of main file from SWIG
4057
                 * bindings */
4058
                /* when auxiliary file cannot be opened (#3269) */
4059
                CPLTurnFailureIntoWarning(TRUE);
×
4060
                if (poDependentDS != nullptr && poDependentDS->GetShared())
×
4061
                    poODS = GDALDataset::FromHandle(
×
4062
                        GDALOpenShared(osAuxFilename, eAccess));
4063
                else
4064
                    poODS = GDALDataset::FromHandle(
×
4065
                        GDALOpen(osAuxFilename, eAccess));
4066
                CPLTurnFailureIntoWarning(FALSE);
×
4067
            }
4068
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
23✔
4069
        }
4070

4071
        if (poODS != nullptr)
10,753✔
4072
        {
4073
            const char *pszDep =
4074
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
×
4075
            if (pszDep == nullptr)
×
4076
            {
4077
                CPLDebug("AUX",
×
4078
                         "Found %s but it has no dependent file, ignoring.",
4079
                         osAuxFilename.c_str());
4080
                GDALClose(poODS);
×
4081
                poODS = nullptr;
×
4082
            }
4083
            else if (!EQUAL(pszDep, osJustFile))
×
4084
            {
4085
                VSIStatBufL sStatBuf;
4086

4087
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
×
4088
                {
4089
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
×
4090
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4091
                    GDALClose(poODS);
×
4092
                    poODS = nullptr;
×
4093
                }
4094
                else
4095
                {
4096
                    CPLDebug(
×
4097
                        "AUX",
4098
                        "%s is for file %s, not %s, but since\n"
4099
                        "%s does not exist, we will use .aux file as our own.",
4100
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4101
                        pszDep);
4102
                }
4103
            }
4104
        }
4105
    }
4106

4107
    /* -------------------------------------------------------------------- */
4108
    /*      Confirm that the aux file matches the configuration of the      */
4109
    /*      dependent dataset.                                              */
4110
    /* -------------------------------------------------------------------- */
4111
    if (poODS != nullptr && poDependentDS != nullptr &&
10,785✔
4112
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
16✔
4113
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
16✔
4114
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
16✔
4115
    {
4116
        CPLDebug(
×
4117
            "AUX",
4118
            "Ignoring aux file %s as its raster configuration\n"
4119
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4120
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4121
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4122
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4123
            poDependentDS->GetRasterCount());
4124

4125
        GDALClose(poODS);
×
4126
        poODS = nullptr;
×
4127
    }
4128

4129
    return poODS;
10,769✔
4130
}
4131

4132
/************************************************************************/
4133
/* Infrastructure to check that dataset characteristics are valid       */
4134
/************************************************************************/
4135

4136
CPL_C_START
4137

4138
/**
4139
 * \brief Return TRUE if the dataset dimensions are valid.
4140
 *
4141
 * @param nXSize raster width
4142
 * @param nYSize raster height
4143
 *
4144
 * @since GDAL 1.7.0
4145
 */
4146
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
4,531✔
4147
{
4148
    if (nXSize <= 0 || nYSize <= 0)
4,531✔
4149
    {
4150
        CPLError(CE_Failure, CPLE_AppDefined,
8✔
4151
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4152
        return FALSE;
8✔
4153
    }
4154
    return TRUE;
4,523✔
4155
}
4156

4157
/**
4158
 * \brief Return TRUE if the band count is valid.
4159
 *
4160
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4161
 * the band count will be compared to the maximum number of band allowed.
4162
 * If not defined, the maximum number allowed is 65536.
4163
 *
4164
 * @param nBands the band count
4165
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4166
 *
4167
 * @since GDAL 1.7.0
4168
 */
4169

4170
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
3,941✔
4171
{
4172
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
3,941✔
4173
    {
4174
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
12✔
4175
                 nBands);
4176
        return FALSE;
12✔
4177
    }
4178
    const char *pszMaxBandCount =
4179
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
3,929✔
4180
    /* coverity[tainted_data] */
4181
    int nMaxBands = atoi(pszMaxBandCount);
3,929✔
4182
    if (nBands > nMaxBands)
3,929✔
4183
    {
4184
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4185
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4186
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4187
                 "legitimate number.",
4188
                 nBands, nMaxBands);
4189
        return FALSE;
1✔
4190
    }
4191
    return TRUE;
3,928✔
4192
}
4193

4194
CPL_C_END
4195

4196
/************************************************************************/
4197
/*                     GDALSerializeGCPListToXML()                      */
4198
/************************************************************************/
4199

4200
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
22✔
4201
                               const std::vector<gdal::GCP> &asGCPs,
4202
                               const OGRSpatialReference *poGCP_SRS)
4203
{
4204
    CPLString oFmt;
44✔
4205

4206
    CPLXMLNode *psPamGCPList =
4207
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
22✔
4208

4209
    CPLXMLNode *psLastChild = nullptr;
22✔
4210

4211
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
22✔
4212
    {
4213
        char *pszWKT = nullptr;
9✔
4214
        poGCP_SRS->exportToWkt(&pszWKT);
9✔
4215
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
9✔
4216
        CPLFree(pszWKT);
9✔
4217
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
9✔
4218
        CPLString osMapping;
9✔
4219
        for (size_t i = 0; i < mapping.size(); ++i)
27✔
4220
        {
4221
            if (!osMapping.empty())
18✔
4222
                osMapping += ",";
9✔
4223
            osMapping += CPLSPrintf("%d", mapping[i]);
18✔
4224
        }
4225
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
9✔
4226
                       osMapping.c_str());
4227

4228
        psLastChild = psPamGCPList->psChild->psNext;
9✔
4229
    }
4230

4231
    for (const gdal::GCP &gcp : asGCPs)
88✔
4232
    {
4233
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
66✔
4234

4235
        if (psLastChild == nullptr)
66✔
4236
            psPamGCPList->psChild = psXMLGCP;
13✔
4237
        else
4238
            psLastChild->psNext = psXMLGCP;
53✔
4239
        psLastChild = psXMLGCP;
66✔
4240

4241
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
66✔
4242

4243
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
66✔
4244
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
×
4245

4246
        CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
66✔
4247

4248
        CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
66✔
4249

4250
        CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
66✔
4251

4252
        CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
66✔
4253

4254
        /* Note: GDAL 1.10.1 and older generated #GCPZ, but could not read it
4255
         * back */
4256
        if (gcp.Z() != 0.0)
66✔
4257
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
14✔
4258
    }
4259
}
22✔
4260

4261
/************************************************************************/
4262
/*                     GDALDeserializeGCPListFromXML()                  */
4263
/************************************************************************/
4264

4265
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
91✔
4266
                                   std::vector<gdal::GCP> &asGCPs,
4267
                                   OGRSpatialReference **ppoGCP_SRS)
4268
{
4269
    if (ppoGCP_SRS)
91✔
4270
    {
4271
        const char *pszRawProj =
4272
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
76✔
4273

4274
        *ppoGCP_SRS = nullptr;
76✔
4275
        if (pszRawProj && pszRawProj[0])
76✔
4276
        {
4277
            *ppoGCP_SRS = new OGRSpatialReference();
62✔
4278
            (*ppoGCP_SRS)
4279
                ->SetFromUserInput(
62✔
4280
                    pszRawProj,
4281
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4282

4283
            const char *pszMapping =
4284
                CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
62✔
4285
            if (pszMapping)
62✔
4286
            {
4287
                char **papszTokens =
4288
                    CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
14✔
4289
                std::vector<int> anMapping;
28✔
4290
                for (int i = 0; papszTokens && papszTokens[i]; i++)
42✔
4291
                {
4292
                    anMapping.push_back(atoi(papszTokens[i]));
28✔
4293
                }
4294
                CSLDestroy(papszTokens);
14✔
4295
                (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
14✔
4296
            }
4297
            else
4298
            {
4299
                (*ppoGCP_SRS)
4300
                    ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
48✔
4301
            }
4302
        }
4303
    }
4304

4305
    asGCPs.clear();
91✔
4306
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
2,813✔
4307
         psXMLGCP = psXMLGCP->psNext)
2,722✔
4308
    {
4309
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
2,722✔
4310
            continue;
86✔
4311

4312
        gdal::GCP gcp;
5,272✔
4313
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
2,636✔
4314
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
2,636✔
4315

4316
        const auto ParseDoubleValue =
4317
            [psXMLGCP](const char *pszParameter, double &dfVal)
10,544✔
4318
        {
4319
            const char *pszVal =
4320
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
10,544✔
4321
            if (!pszVal)
10,544✔
4322
            {
4323
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
×
4324
                         pszParameter);
4325
                return false;
×
4326
            }
4327
            char *endptr = nullptr;
10,544✔
4328
            dfVal = CPLStrtod(pszVal, &endptr);
10,544✔
4329
            if (endptr == pszVal)
10,544✔
4330
            {
4331
                CPLError(CE_Failure, CPLE_AppDefined,
×
4332
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4333
                return false;
×
4334
            }
4335
            return true;
10,544✔
4336
        };
2,636✔
4337

4338
        bool bOK = true;
2,636✔
4339
        if (!ParseDoubleValue("Pixel", gcp.Pixel()))
2,636✔
4340
            bOK = false;
×
4341
        if (!ParseDoubleValue("Line", gcp.Line()))
2,636✔
4342
            bOK = false;
×
4343
        if (!ParseDoubleValue("X", gcp.X()))
2,636✔
4344
            bOK = false;
×
4345
        if (!ParseDoubleValue("Y", gcp.Y()))
2,636✔
4346
            bOK = false;
×
4347
        const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
2,636✔
4348
        if (pszZ == nullptr)
2,636✔
4349
        {
4350
            // Note: GDAL 1.10.1 and older generated #GCPZ,
4351
            // but could not read it back.
4352
            pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
2,414✔
4353
        }
4354
        char *endptr = nullptr;
2,636✔
4355
        gcp.Z() = CPLStrtod(pszZ, &endptr);
2,636✔
4356
        if (endptr == pszZ)
2,636✔
4357
        {
4358
            CPLError(CE_Failure, CPLE_AppDefined,
×
4359
                     "GCP#Z=%s is an invalid value", pszZ);
4360
            bOK = false;
×
4361
        }
4362

4363
        if (bOK)
2,636✔
4364
        {
4365
            asGCPs.emplace_back(std::move(gcp));
2,636✔
4366
        }
4367
    }
4368
}
91✔
4369

4370
/************************************************************************/
4371
/*                   GDALSerializeOpenOptionsToXML()                    */
4372
/************************************************************************/
4373

4374
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
1,006✔
4375
                                   char **papszOpenOptions)
4376
{
4377
    if (papszOpenOptions != nullptr)
1,006✔
4378
    {
4379
        CPLXMLNode *psOpenOptions =
4380
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
3✔
4381
        CPLXMLNode *psLastChild = nullptr;
3✔
4382

4383
        for (char **papszIter = papszOpenOptions; *papszIter != nullptr;
6✔
4384
             papszIter++)
4385
        {
4386
            const char *pszRawValue;
4387
            char *pszKey = nullptr;
3✔
4388
            CPLXMLNode *psOOI;
4389

4390
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
3✔
4391

4392
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
3✔
4393
            if (psLastChild == nullptr)
3✔
4394
                psOpenOptions->psChild = psOOI;
3✔
4395
            else
4396
                psLastChild->psNext = psOOI;
×
4397
            psLastChild = psOOI;
3✔
4398

4399
            CPLSetXMLValue(psOOI, "#key", pszKey);
3✔
4400
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
3✔
4401

4402
            CPLFree(pszKey);
3✔
4403
        }
4404
    }
4405
}
1,006✔
4406

4407
/************************************************************************/
4408
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4409
/************************************************************************/
4410

4411
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
2,226✔
4412
{
4413
    char **papszOpenOptions = nullptr;
2,226✔
4414
    const CPLXMLNode *psOpenOptions =
4415
        CPLGetXMLNode(psParentNode, "OpenOptions");
2,226✔
4416
    if (psOpenOptions != nullptr)
2,226✔
4417
    {
4418
        const CPLXMLNode *psOOI;
4419
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
22✔
4420
             psOOI = psOOI->psNext)
11✔
4421
        {
4422
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
11✔
4423
                psOOI->psChild == nullptr ||
11✔
4424
                psOOI->psChild->psNext == nullptr ||
11✔
4425
                psOOI->psChild->eType != CXT_Attribute ||
11✔
4426
                psOOI->psChild->psChild == nullptr)
11✔
4427
                continue;
×
4428

4429
            char *pszName = psOOI->psChild->psChild->pszValue;
11✔
4430
            char *pszValue = psOOI->psChild->psNext->pszValue;
11✔
4431
            if (pszName != nullptr && pszValue != nullptr)
11✔
4432
                papszOpenOptions =
4433
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
11✔
4434
        }
4435
    }
4436
    return papszOpenOptions;
2,226✔
4437
}
4438

4439
/************************************************************************/
4440
/*                    GDALRasterIOGetResampleAlg()                      */
4441
/************************************************************************/
4442

4443
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
2,163✔
4444
{
4445
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
2,163✔
4446
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
2,163✔
4447
        eResampleAlg = GRIORA_NearestNeighbour;
18✔
4448
    else if (EQUAL(pszResampling, "BILINEAR"))
2,145✔
4449
        eResampleAlg = GRIORA_Bilinear;
2,016✔
4450
    else if (EQUAL(pszResampling, "CUBIC"))
129✔
4451
        eResampleAlg = GRIORA_Cubic;
78✔
4452
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
51✔
4453
        eResampleAlg = GRIORA_CubicSpline;
1✔
4454
    else if (EQUAL(pszResampling, "LANCZOS"))
50✔
4455
        eResampleAlg = GRIORA_Lanczos;
1✔
4456
    else if (EQUAL(pszResampling, "AVERAGE"))
49✔
4457
        eResampleAlg = GRIORA_Average;
43✔
4458
    else if (EQUAL(pszResampling, "RMS"))
6✔
4459
        eResampleAlg = GRIORA_RMS;
1✔
4460
    else if (EQUAL(pszResampling, "MODE"))
5✔
4461
        eResampleAlg = GRIORA_Mode;
4✔
4462
    else if (EQUAL(pszResampling, "GAUSS"))
1✔
4463
        eResampleAlg = GRIORA_Gauss;
1✔
4464
    else
4465
        CPLError(CE_Warning, CPLE_NotSupported,
×
4466
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
4467
    return eResampleAlg;
2,163✔
4468
}
4469

4470
/************************************************************************/
4471
/*                    GDALRasterIOGetResampleAlgStr()                   */
4472
/************************************************************************/
4473

4474
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
1✔
4475
{
4476
    switch (eResampleAlg)
1✔
4477
    {
4478
        case GRIORA_NearestNeighbour:
×
4479
            return "NearestNeighbour";
×
4480
        case GRIORA_Bilinear:
×
4481
            return "Bilinear";
×
4482
        case GRIORA_Cubic:
1✔
4483
            return "Cubic";
1✔
4484
        case GRIORA_CubicSpline:
×
4485
            return "CubicSpline";
×
4486
        case GRIORA_Lanczos:
×
4487
            return "Lanczos";
×
4488
        case GRIORA_Average:
×
4489
            return "Average";
×
4490
        case GRIORA_RMS:
×
4491
            return "RMS";
×
4492
        case GRIORA_Mode:
×
4493
            return "Mode";
×
4494
        case GRIORA_Gauss:
×
4495
            return "Gauss";
×
4496
        default:
×
4497
            CPLAssert(false);
×
4498
            return "Unknown";
4499
    }
4500
}
4501

4502
/************************************************************************/
4503
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
4504
/************************************************************************/
4505

4506
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
3,462,070✔
4507
                                        int nXSize, int nYSize, int nBufXSize,
4508
                                        int nBufYSize)
4509
{
4510
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
3,462,070✔
4511
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
371,563✔
4512
    {
4513
        const char *pszResampling =
4514
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
368,896✔
4515
        if (pszResampling != nullptr)
368,896✔
4516
        {
4517
            psExtraArg->eResampleAlg =
1✔
4518
                GDALRasterIOGetResampleAlg(pszResampling);
1✔
4519
        }
4520
    }
4521
}
3,462,070✔
4522

4523
/************************************************************************/
4524
/*                     GDALCanFileAcceptSidecarFile()                   */
4525
/************************************************************************/
4526

4527
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
92,813✔
4528
{
4529
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
92,813✔
4530
        return FALSE;
×
4531
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
4532
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
92,813✔
4533
        return FALSE;
321✔
4534
    return TRUE;
92,492✔
4535
}
4536

4537
/************************************************************************/
4538
/*                   GDALCanReliablyUseSiblingFileList()                */
4539
/************************************************************************/
4540

4541
/* Try to address https://github.com/OSGeo/gdal/issues/2903 */
4542
/* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
4543
/*   (normalization form decomposed). The filesystem takes care of converting */
4544
/*   precomposed form as often coming from user interface to this NFD variant */
4545
/*   See
4546
 * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
4547
 */
4548
/*   And readdir() will return such NFD variant encoding. Consequently comparing
4549
 */
4550
/*   the user filename with ones with readdir() is not reliable */
4551
/* - APFS preserves both case and normalization of the filename on disk in all
4552
 */
4553
/*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
4554
 */
4555
/*   the case-insensitive and case-sensitive variants, using a hash-based native
4556
 */
4557
/*   normalization scheme. APFS preserves the normalization of the filename and
4558
 */
4559
/*   uses hashes of the normalized form of the filename to provide normalization
4560
 */
4561
/*   insensitivity. */
4562
/*   From
4563
 * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
4564
 */
4565
/*   Issues might still arise if the file has been created using one of the
4566
 * UTF-8 */
4567
/*   encoding (likely the decomposed one if using MacOS specific API), but the
4568
 */
4569
/*   string passed to GDAL for opening would be with another one (likely the
4570
 * precomposed one) */
4571
bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
102,918✔
4572
{
4573
#ifdef __APPLE__
4574
    for (int i = 0; pszFilename[i] != 0; ++i)
4575
    {
4576
        if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
4577
        {
4578
            // non-ASCII character found
4579

4580
            // if this is a network storage, assume no issue
4581
            if (!VSIIsLocal(pszFilename))
4582
            {
4583
                return true;
4584
            }
4585
            return false;
4586
        }
4587
    }
4588
    return true;
4589
#else
4590
    (void)pszFilename;
4591
    return true;
102,918✔
4592
#endif
4593
}
4594

4595
/************************************************************************/
4596
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
4597
/************************************************************************/
4598

4599
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
1,089✔
4600
{
4601
    const auto kMaxFloat = std::numeric_limits<float>::max();
1,089✔
4602
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
1,089✔
4603
        return -kMaxFloat;
33✔
4604
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
1,056✔
4605
        return kMaxFloat;
7✔
4606
    return dfVal;
1,049✔
4607
}
4608

4609
/************************************************************************/
4610
/*                        GDALCopyNoDataValue()                         */
4611
/************************************************************************/
4612

4613
void GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand)
2,564✔
4614
{
4615

4616
    int bSuccess;
4617
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
2,564✔
4618
    const auto eDstDataType = poDstBand->GetRasterDataType();
2,564✔
4619
    if (eSrcDataType == GDT_Int64)
2,564✔
4620
    {
4621
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
3✔
4622
        if (bSuccess)
3✔
4623
        {
4624
            if (eDstDataType == GDT_Int64)
3✔
4625
            {
4626
                poDstBand->SetNoDataValueAsInt64(nNoData);
3✔
4627
            }
4628
            else if (eDstDataType == GDT_UInt64)
×
4629
            {
4630
                if (nNoData >= 0)
×
4631
                    poDstBand->SetNoDataValueAsUInt64(
×
4632
                        static_cast<uint64_t>(nNoData));
×
4633
            }
4634
            else if (nNoData ==
×
4635
                     static_cast<int64_t>(static_cast<double>(nNoData)))
×
4636
            {
4637
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
4638
            }
4639
        }
4640
    }
4641
    else if (eSrcDataType == GDT_UInt64)
2,561✔
4642
    {
4643
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
3✔
4644
        if (bSuccess)
3✔
4645
        {
4646
            if (eDstDataType == GDT_UInt64)
3✔
4647
            {
4648
                poDstBand->SetNoDataValueAsUInt64(nNoData);
3✔
4649
            }
4650
            else if (eDstDataType == GDT_Int64)
×
4651
            {
4652
                if (nNoData <
×
4653
                    static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
×
4654
                {
4655
                    poDstBand->SetNoDataValueAsInt64(
×
4656
                        static_cast<int64_t>(nNoData));
×
4657
                }
4658
            }
4659
            else if (nNoData ==
×
4660
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
×
4661
            {
4662
                poDstBand->SetNoDataValue(static_cast<double>(nNoData));
×
4663
            }
4664
        }
4665
    }
4666
    else
4667
    {
4668
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2,558✔
4669
        if (bSuccess)
2,558✔
4670
        {
4671
            if (eDstDataType == GDT_Int64)
155✔
4672
            {
4673
                if (dfNoData >= static_cast<double>(
×
4674
                                    std::numeric_limits<int64_t>::min()) &&
×
4675
                    dfNoData <= static_cast<double>(
×
4676
                                    std::numeric_limits<int64_t>::max()) &&
×
4677
                    dfNoData ==
4678
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
×
4679
                {
4680
                    poDstBand->SetNoDataValueAsInt64(
×
4681
                        static_cast<int64_t>(dfNoData));
×
4682
                }
4683
            }
4684
            else if (eDstDataType == GDT_UInt64)
155✔
4685
            {
4686
                if (dfNoData >= static_cast<double>(
×
4687
                                    std::numeric_limits<uint64_t>::min()) &&
×
4688
                    dfNoData <= static_cast<double>(
×
4689
                                    std::numeric_limits<uint64_t>::max()) &&
×
4690
                    dfNoData ==
4691
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
×
4692
                {
4693
                    poDstBand->SetNoDataValueAsInt64(
×
4694
                        static_cast<uint64_t>(dfNoData));
×
4695
                }
4696
            }
4697
            else
4698
            {
4699
                poDstBand->SetNoDataValue(dfNoData);
155✔
4700
            }
4701
        }
4702
    }
4703
}
2,564✔
4704

4705
/************************************************************************/
4706
/*                     GDALGetNoDataValueCastToDouble()                 */
4707
/************************************************************************/
4708

4709
double GDALGetNoDataValueCastToDouble(int64_t nVal)
1✔
4710
{
4711
    const double dfVal = static_cast<double>(nVal);
1✔
4712
    if (static_cast<int64_t>(dfVal) != nVal)
1✔
4713
    {
4714
        CPLError(CE_Warning, CPLE_AppDefined,
×
4715
                 "GetNoDataValue() returns an approximate value of the "
4716
                 "true nodata value = " CPL_FRMT_GIB ". Use "
4717
                 "GetNoDataValueAsInt64() instead",
4718
                 static_cast<GIntBig>(nVal));
4719
    }
4720
    return dfVal;
1✔
4721
}
4722

4723
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
1✔
4724
{
4725
    const double dfVal = static_cast<double>(nVal);
1✔
4726
    if (static_cast<uint64_t>(dfVal) != nVal)
1✔
4727
    {
4728
        CPLError(CE_Warning, CPLE_AppDefined,
×
4729
                 "GetNoDataValue() returns an approximate value of the "
4730
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
4731
                 "GetNoDataValueAsUInt64() instead",
4732
                 static_cast<GUIntBig>(nVal));
4733
    }
4734
    return dfVal;
1✔
4735
}
4736

4737
/************************************************************************/
4738
/*                GDALGetCompressionFormatForJPEG()                     */
4739
/************************************************************************/
4740

4741
//! @cond Doxygen_Suppress
4742
std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
23✔
4743
{
4744
    std::string osRet;
23✔
4745
    const auto nSavedPos = VSIFTellL(fp);
23✔
4746
    GByte abyMarkerHeader[4];
4747
    if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
23✔
4748
        VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
23✔
4749
        abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
46✔
4750
    {
4751
        osRet = "JPEG";
23✔
4752
        bool bHasAPP14Adobe = false;
23✔
4753
        GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
23✔
4754
        int nNumComponents = 0;
23✔
4755
        while (true)
4756
        {
4757
            const auto nCurPos = VSIFTellL(fp);
171✔
4758
            if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
171✔
4759
                break;
×
4760
            if (abyMarkerHeader[0] != 0xFF)
171✔
4761
                break;
×
4762
            const GByte markerType = abyMarkerHeader[1];
171✔
4763
            const size_t nMarkerSize =
171✔
4764
                abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
171✔
4765
            if (nMarkerSize < 2)
171✔
4766
                break;
×
4767
            if (markerType >= 0xC0 && markerType <= 0xCF &&
171✔
4768
                markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
23✔
4769
            {
4770
                switch (markerType)
23✔
4771
                {
4772
                    case 0xC0:
21✔
4773
                        osRet += ";frame_type=SOF0_baseline";
21✔
4774
                        break;
21✔
4775
                    case 0xC1:
2✔
4776
                        osRet += ";frame_type=SOF1_extended_sequential";
2✔
4777
                        break;
2✔
4778
                    case 0xC2:
×
4779
                        osRet += ";frame_type=SOF2_progressive_huffman";
×
4780
                        break;
×
4781
                    case 0xC3:
×
4782
                        osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
4783
                                 "supported=no";
×
4784
                        break;
×
4785
                    case 0xC5:
×
4786
                        osRet += ";frame_type="
4787
                                 "SOF5_differential_sequential_huffman;"
4788
                                 "libjpeg_supported=no";
×
4789
                        break;
×
4790
                    case 0xC6:
×
4791
                        osRet += ";frame_type=SOF6_differential_progressive_"
4792
                                 "huffman;libjpeg_supported=no";
×
4793
                        break;
×
4794
                    case 0xC7:
×
4795
                        osRet += ";frame_type="
4796
                                 "SOF7_differential_lossless_huffman;"
4797
                                 "libjpeg_supported=no";
×
4798
                        break;
×
4799
                    case 0xC9:
×
4800
                        osRet += ";frame_type="
4801
                                 "SOF9_extended_sequential_arithmetic";
×
4802
                        break;
×
4803
                    case 0xCA:
×
4804
                        osRet += ";frame_type=SOF10_progressive_arithmetic";
×
4805
                        break;
×
4806
                    case 0xCB:
×
4807
                        osRet += ";frame_type="
4808
                                 "SOF11_lossless_arithmetic;libjpeg_"
4809
                                 "supported=no";
×
4810
                        break;
×
4811
                    case 0xCD:
×
4812
                        osRet += ";frame_type=SOF13_differential_sequential_"
4813
                                 "arithmetic;libjpeg_supported=no";
×
4814
                        break;
×
4815
                    case 0xCE:
×
4816
                        osRet += ";frame_type=SOF14_differential_progressive_"
4817
                                 "arithmetic;libjpeg_supported=no";
×
4818
                        break;
×
4819
                    case 0xCF:
×
4820
                        osRet += ";frame_type=SOF15_differential_lossless_"
4821
                                 "arithmetic;libjpeg_supported=no";
×
4822
                        break;
×
4823
                    default:
×
4824
                        break;
×
4825
                }
4826
                GByte abySegmentBegin[6];
4827
                if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
23✔
4828
                              fp) != 1)
23✔
4829
                    break;
×
4830
                osRet += ";bit_depth=";
23✔
4831
                osRet += CPLSPrintf("%d", abySegmentBegin[0]);
23✔
4832
                nNumComponents = abySegmentBegin[5];
23✔
4833
                osRet += ";num_components=";
23✔
4834
                osRet += CPLSPrintf("%d", nNumComponents);
23✔
4835
                if (nNumComponents == 3)
23✔
4836
                {
4837
                    GByte abySegmentNext[3 * 3];
4838
                    if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
13✔
4839
                                  fp) != 1)
13✔
4840
                        break;
×
4841
                    if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
13✔
4842
                        abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
×
4843
                        abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
×
4844
                    {
4845
                        // no subsampling
4846
                        osRet += ";subsampling=4:4:4";
×
4847
                    }
4848
                    else if (abySegmentNext[0] == 1 &&
13✔
4849
                             abySegmentNext[1] == 0x22 &&
11✔
4850
                             abySegmentNext[3] == 2 &&
11✔
4851
                             abySegmentNext[4] == 0x11 &&
11✔
4852
                             abySegmentNext[6] == 3 &&
11✔
4853
                             abySegmentNext[7] == 0x11)
11✔
4854
                    {
4855
                        // classic subsampling
4856
                        osRet += ";subsampling=4:2:0";
11✔
4857
                    }
4858
                    else if (abySegmentNext[0] == 1 &&
2✔
4859
                             abySegmentNext[1] == 0x21 &&
×
4860
                             abySegmentNext[3] == 2 &&
×
4861
                             abySegmentNext[4] == 0x11 &&
×
4862
                             abySegmentNext[6] == 3 &&
×
4863
                             abySegmentNext[7] == 0x11)
×
4864
                    {
4865
                        osRet += ";subsampling=4:2:2";
×
4866
                    }
4867
                }
23✔
4868
            }
4869
            else if (markerType == 0xEE && nMarkerSize == 14)
148✔
4870
            {
4871
                if (VSIFReadL(abyAPP14AdobeMarkerData,
1✔
4872
                              sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
2✔
4873
                    memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
1✔
4874
                        0)
4875
                {
4876
                    bHasAPP14Adobe = true;
1✔
4877
                }
4878
            }
4879
            else if (markerType == 0xDA)
147✔
4880
            {
4881
                // Start of scan
4882
                break;
23✔
4883
            }
4884
            VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
148✔
4885
        }
148✔
4886
        std::string osColorspace;
46✔
4887
        if (bHasAPP14Adobe)
23✔
4888
        {
4889
            if (abyAPP14AdobeMarkerData[11] == 0)
1✔
4890
            {
4891
                if (nNumComponents == 3)
1✔
4892
                    osColorspace = "RGB";
×
4893
                else if (nNumComponents == 4)
1✔
4894
                    osColorspace = "CMYK";
1✔
4895
            }
4896
            else if (abyAPP14AdobeMarkerData[11] == 1)
×
4897
            {
4898
                osColorspace = "YCbCr";
×
4899
            }
4900
            else if (abyAPP14AdobeMarkerData[11] == 2)
×
4901
            {
4902
                osColorspace = "YCCK";
×
4903
            }
4904
        }
4905
        else
4906
        {
4907
            if (nNumComponents == 3)
22✔
4908
                osColorspace = "YCbCr";
13✔
4909
            else if (nNumComponents == 4)
9✔
4910
                osColorspace = "CMYK";
1✔
4911
        }
4912
        osRet += ";colorspace=";
23✔
4913
        if (!osColorspace.empty())
23✔
4914
            osRet += osColorspace;
15✔
4915
        else
4916
            osRet += "unknown";
8✔
4917
    }
4918
    if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
23✔
4919
    {
4920
        CPLError(CE_Failure, CPLE_AppDefined,
×
4921
                 "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
4922
    }
4923
    return osRet;
46✔
4924
}
4925

4926
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
16✔
4927
                                            size_t nBufferSize)
4928
{
4929
    VSILFILE *fp = VSIFileFromMemBuffer(
16✔
4930
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
4931
        false);
4932
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
16✔
4933
    VSIFCloseL(fp);
16✔
4934
    return osRet;
16✔
4935
}
4936

4937
//! @endcond
4938

4939
/************************************************************************/
4940
/*                      GDALGetNoDataReplacementValue()                 */
4941
/************************************************************************/
4942

4943
/**
4944
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
4945
 *        is out of range for the specified data type (dt).
4946
 *        For UInt64 and Int64 data type this function cannot reliably trusted
4947
 *        because their nodata values might not always be representable exactly
4948
 *        as a double, in particular the maximum absolute value for those types
4949
 *        is 2^53.
4950
 *
4951
 * The replacement value is a value that can be used in a computation
4952
 * whose result would match by accident the nodata value, whereas it is
4953
 * meant to be valid. For example, for a dataset with a nodata value of 0,
4954
 * when averaging -1 and 1, one would get normally a value of 0. The
4955
 * replacement nodata value can then be substituted to that 0 value to still
4956
 * get a valid value, as close as practical to the true value, while being
4957
 * different from the nodata value.
4958
 *
4959
 * @param dt Data type
4960
 * @param dfNoDataValue The no data value
4961

4962
 * @since GDAL 3.9
4963
 */
4964
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
155✔
4965
{
4966

4967
    // The logic here is to check if the value is out of range for the
4968
    // specified data type and return a replacement value if it is, return
4969
    // 0 otherwise.
4970
    double dfReplacementVal = dfNoDataValue;
155✔
4971
    if (dt == GDT_Byte)
155✔
4972
    {
4973
        if (GDALClampDoubleValue(dfNoDataValue,
59✔
4974
                                 std::numeric_limits<uint8_t>::lowest(),
59✔
4975
                                 std::numeric_limits<uint8_t>::max()))
59✔
4976
        {
4977
            return 0;
2✔
4978
        }
4979
        if (dfNoDataValue == std::numeric_limits<unsigned char>::max())
57✔
4980
            dfReplacementVal = std::numeric_limits<unsigned char>::max() - 1;
3✔
4981
        else
4982
            dfReplacementVal = dfNoDataValue + 1;
54✔
4983
    }
4984
    else if (dt == GDT_Int8)
96✔
4985
    {
4986
        if (GDALClampDoubleValue(dfNoDataValue,
4✔
4987
                                 std::numeric_limits<int8_t>::lowest(),
4✔
4988
                                 std::numeric_limits<int8_t>::max()))
4✔
4989
        {
4990
            return 0;
2✔
4991
        }
4992
        if (dfNoDataValue == std::numeric_limits<GInt8>::max())
2✔
4993
            dfReplacementVal = std::numeric_limits<GInt8>::max() - 1;
1✔
4994
        else
4995
            dfReplacementVal = dfNoDataValue + 1;
1✔
4996
    }
4997
    else if (dt == GDT_UInt16)
92✔
4998
    {
4999
        if (GDALClampDoubleValue(dfNoDataValue,
6✔
5000
                                 std::numeric_limits<uint16_t>::lowest(),
6✔
5001
                                 std::numeric_limits<uint16_t>::max()))
6✔
5002
        {
5003
            return 0;
2✔
5004
        }
5005
        if (dfNoDataValue == std::numeric_limits<GUInt16>::max())
4✔
5006
            dfReplacementVal = std::numeric_limits<GUInt16>::max() - 1;
1✔
5007
        else
5008
            dfReplacementVal = dfNoDataValue + 1;
3✔
5009
    }
5010
    else if (dt == GDT_Int16)
86✔
5011
    {
5012
        if (GDALClampDoubleValue(dfNoDataValue,
4✔
5013
                                 std::numeric_limits<int16_t>::lowest(),
4✔
5014
                                 std::numeric_limits<int16_t>::max()))
4✔
5015
        {
5016
            return 0;
2✔
5017
        }
5018
        if (dfNoDataValue == std::numeric_limits<GInt16>::max())
2✔
5019
            dfReplacementVal = std::numeric_limits<GInt16>::max() - 1;
1✔
5020
        else
5021
            dfReplacementVal = dfNoDataValue + 1;
1✔
5022
    }
5023
    else if (dt == GDT_UInt32)
82✔
5024
    {
5025
        if (GDALClampDoubleValue(dfNoDataValue,
5✔
5026
                                 std::numeric_limits<uint32_t>::lowest(),
5027
                                 std::numeric_limits<uint32_t>::max()))
5028
        {
5029
            return 0;
2✔
5030
        }
5031
        if (dfNoDataValue == std::numeric_limits<GUInt32>::max())
3✔
5032
            dfReplacementVal = std::numeric_limits<GUInt32>::max() - 1;
1✔
5033
        else
5034
            dfReplacementVal = dfNoDataValue + 1;
2✔
5035
    }
5036
    else if (dt == GDT_Int32)
77✔
5037
    {
5038
        if (GDALClampDoubleValue(dfNoDataValue,
6✔
5039
                                 std::numeric_limits<int32_t>::lowest(),
5040
                                 std::numeric_limits<int32_t>::max()))
5041
        {
5042
            return 0;
2✔
5043
        }
5044
        if (dfNoDataValue == std::numeric_limits<int32_t>::max())
4✔
5045
            dfReplacementVal = std::numeric_limits<int32_t>::max() - 1;
1✔
5046
        else
5047
            dfReplacementVal = dfNoDataValue + 1;
3✔
5048
    }
5049
    else if (dt == GDT_UInt64)
71✔
5050
    {
5051
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5052
        // so we take the next lower value representable as a double 18446744073709549567
5053
        static const double dfMaxUInt64Value{
5054
            std::nextafter(
5055
                static_cast<double>(std::numeric_limits<uint64_t>::max()), 0) -
5056
            1};
5057

5058
        if (GDALClampDoubleValue(dfNoDataValue,
4✔
5059
                                 std::numeric_limits<uint64_t>::lowest(),
5060
                                 std::numeric_limits<uint64_t>::max()))
5061
        {
5062
            return 0;
2✔
5063
        }
5064

5065
        if (dfNoDataValue >=
2✔
5066
            static_cast<double>(std::numeric_limits<uint64_t>::max()))
2✔
5067
            dfReplacementVal = dfMaxUInt64Value;
1✔
5068
        else
5069
            dfReplacementVal = dfNoDataValue + 1;
1✔
5070
    }
5071
    else if (dt == GDT_Int64)
67✔
5072
    {
5073
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5074
        // so we take the next lower value representable as a double 9223372036854774784
5075
        static const double dfMaxInt64Value{
5076
            std::nextafter(
5077
                static_cast<double>(std::numeric_limits<int64_t>::max()), 0) -
5078
            1};
5079

5080
        if (GDALClampDoubleValue(dfNoDataValue,
4✔
5081
                                 std::numeric_limits<int64_t>::lowest(),
5082
                                 std::numeric_limits<int64_t>::max()))
5083
        {
5084
            return 0;
2✔
5085
        }
5086

5087
        if (dfNoDataValue >=
2✔
5088
            static_cast<double>(std::numeric_limits<int64_t>::max()))
2✔
5089
            dfReplacementVal = dfMaxInt64Value;
1✔
5090
        else
5091
            dfReplacementVal = dfNoDataValue + 1;
1✔
5092
    }
5093
    else if (dt == GDT_Float32)
63✔
5094
    {
5095

5096
        if (GDALClampDoubleValue(dfNoDataValue,
32✔
5097
                                 std::numeric_limits<float>::lowest(),
5098
                                 std::numeric_limits<float>::max()))
5099
        {
5100
            return 0;
4✔
5101
        }
5102

5103
        if (dfNoDataValue == std::numeric_limits<float>::max())
28✔
5104
        {
5105
            dfReplacementVal =
1✔
5106
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
1✔
5107
        }
5108
        else
5109
        {
5110
            dfReplacementVal =
27✔
5111
                std::nextafter(static_cast<float>(dfNoDataValue),
27✔
5112
                               std::numeric_limits<float>::max());
5113
        }
5114
    }
5115
    else if (dt == GDT_Float64)
31✔
5116
    {
5117
        if (GDALClampDoubleValue(dfNoDataValue,
31✔
5118
                                 std::numeric_limits<double>::lowest(),
5119
                                 std::numeric_limits<double>::max()))
5120
        {
5121
            return 0;
2✔
5122
        }
5123

5124
        if (dfNoDataValue == std::numeric_limits<double>::max())
29✔
5125
        {
5126
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0f);
2✔
5127
        }
5128
        else
5129
        {
5130
            dfReplacementVal = std::nextafter(
27✔
5131
                dfNoDataValue, std::numeric_limits<double>::max());
5132
        }
5133
    }
5134

5135
    return dfReplacementVal;
133✔
5136
}
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