Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdal_misc.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL Core
4
 * Purpose:  Free standing functions for GDAL.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999, Frank Warmerdam
9
 * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
16
#include <cctype>
17
#include <cerrno>
18
#include <clocale>
19
#include <cmath>
20
#include <cstddef>
21
#include <cstdio>
22
#include <cstdlib>
23
#include <cstring>
24
#include <fcntl.h>
25
26
#include <algorithm>
27
#include <iostream>
28
#include <limits>
29
#include <string>
30
31
#include "cpl_conv.h"
32
#include "cpl_error.h"
33
#include "cpl_float.h"
34
#include "cpl_json.h"
35
#include "cpl_minixml.h"
36
#include "cpl_multiproc.h"
37
#include "cpl_string.h"
38
#include "cpl_vsi.h"
39
#ifdef EMBED_RESOURCE_FILES
40
#include "embedded_resources.h"
41
#endif
42
#include "gdal_version_full/gdal_version.h"
43
#include "gdal.h"
44
#include "gdal_mdreader.h"
45
#include "gdal_priv.h"
46
#include "gdal_priv_templates.hpp"
47
#include "ogr_core.h"
48
#include "ogr_spatialref.h"
49
#include "ogr_geos.h"
50
51
#include "proj.h"
52
53
#ifdef HAVE_CURL
54
#include "cpl_curl_priv.h"
55
#endif
56
57
static int GetMinBitsForPair(const bool pabSigned[], const bool pabFloating[],
58
                             const int panBits[])
59
22.1k
{
60
22.1k
    if (pabFloating[0] != pabFloating[1])
61
182
    {
62
182
        const int nNotFloatingTypeIndex = pabFloating[0] ? 1 : 0;
63
182
        const int nFloatingTypeIndex = pabFloating[0] ? 0 : 1;
64
65
182
        return std::max(panBits[nFloatingTypeIndex],
66
182
                        2 * panBits[nNotFloatingTypeIndex]);
67
182
    }
68
69
21.9k
    if (pabSigned[0] != pabSigned[1])
70
776
    {
71
776
        if (!pabSigned[0] && panBits[0] < panBits[1])
72
3
            return panBits[1];
73
773
        if (!pabSigned[1] && panBits[1] < panBits[0])
74
770
            return panBits[0];
75
76
3
        const int nUnsignedTypeIndex = pabSigned[0] ? 1 : 0;
77
3
        const int nSignedTypeIndex = pabSigned[0] ? 0 : 1;
78
79
3
        return std::max(panBits[nSignedTypeIndex],
80
3
                        2 * panBits[nUnsignedTypeIndex]);
81
773
    }
82
83
21.1k
    return std::max(panBits[0], panBits[1]);
84
21.9k
}
85
86
static int GetNonComplexDataTypeElementSizeBits(GDALDataType eDataType)
87
44.2k
{
88
44.2k
    switch (eDataType)
89
44.2k
    {
90
15.2k
        case GDT_UInt8:
91
15.9k
        case GDT_Int8:
92
15.9k
            return 8;
93
94
7.91k
        case GDT_UInt16:
95
15.4k
        case GDT_Int16:
96
15.4k
        case GDT_Float16:
97
15.4k
        case GDT_CInt16:
98
15.4k
        case GDT_CFloat16:
99
15.4k
            return 16;
100
101
8.16k
        case GDT_UInt32:
102
10.8k
        case GDT_Int32:
103
10.8k
        case GDT_Float32:
104
10.8k
        case GDT_CInt32:
105
10.8k
        case GDT_CFloat32:
106
10.8k
            return 32;
107
108
1.89k
        case GDT_Float64:
109
1.89k
        case GDT_CFloat64:
110
1.89k
        case GDT_UInt64:
111
1.89k
        case GDT_Int64:
112
1.89k
            return 64;
113
114
0
        case GDT_Unknown:
115
0
        case GDT_TypeCount:
116
0
            break;
117
44.2k
    }
118
0
    return 0;
119
44.2k
}
120
121
/************************************************************************/
122
/*                         GDALDataTypeUnion()                          */
123
/************************************************************************/
124
125
/**
126
 * \brief Return the smallest data type that can fully express both input data
127
 * types.
128
 *
129
 * @param eType1 first data type.
130
 * @param eType2 second data type.
131
 *
132
 * @return a data type able to express eType1 and eType2.
133
 */
134
135
GDALDataType CPL_STDCALL GDALDataTypeUnion(GDALDataType eType1,
136
                                           GDALDataType eType2)
137
138
22.1k
{
139
22.1k
    if (eType1 == GDT_Unknown)
140
0
        return eType2;
141
22.1k
    if (eType2 == GDT_Unknown)
142
0
        return eType1;
143
144
22.1k
    const int panBits[] = {GetNonComplexDataTypeElementSizeBits(eType1),
145
22.1k
                           GetNonComplexDataTypeElementSizeBits(eType2)};
146
147
22.1k
    if (panBits[0] == 0 || panBits[1] == 0)
148
0
        return GDT_Unknown;
149
150
22.1k
    const bool pabSigned[] = {CPL_TO_BOOL(GDALDataTypeIsSigned(eType1)),
151
22.1k
                              CPL_TO_BOOL(GDALDataTypeIsSigned(eType2))};
152
153
22.1k
    const bool bSigned = pabSigned[0] || pabSigned[1];
154
22.1k
    const bool pabFloating[] = {CPL_TO_BOOL(GDALDataTypeIsFloating(eType1)),
155
22.1k
                                CPL_TO_BOOL(GDALDataTypeIsFloating(eType2))};
156
22.1k
    const bool bFloating = pabFloating[0] || pabFloating[1];
157
22.1k
    const int nBits = GetMinBitsForPair(pabSigned, pabFloating, panBits);
158
22.1k
    const bool bIsComplex = CPL_TO_BOOL(GDALDataTypeIsComplex(eType1)) ||
159
22.1k
                            CPL_TO_BOOL(GDALDataTypeIsComplex(eType2));
160
161
22.1k
    return GDALFindDataType(nBits, bSigned, bFloating, bIsComplex);
162
22.1k
}
163
164
/************************************************************************/
165
/*                        GDALDataTypeUnionWithValue()                  */
166
/************************************************************************/
167
168
/**
169
 * \brief Union a data type with the one found for a value
170
 *
171
 * @param eDT the first data type
172
 * @param dfValue the value for which to find a data type and union with eDT
173
 * @param bComplex if the value is complex
174
 *
175
 * @return a data type able to express eDT and dfValue.
176
 */
177
GDALDataType CPL_STDCALL GDALDataTypeUnionWithValue(GDALDataType eDT,
178
                                                    double dfValue,
179
                                                    int bComplex)
180
4
{
181
4
    if (!bComplex && !GDALDataTypeIsComplex(eDT) && eDT != GDT_Unknown)
182
4
    {
183
        // Do not return `GDT_Float16` because that type is not supported everywhere
184
4
        const auto eDTMod = eDT == GDT_Float16 ? GDT_Float32 : eDT;
185
4
        if (GDALIsValueExactAs(dfValue, eDTMod))
186
4
        {
187
4
            return eDTMod;
188
4
        }
189
4
    }
190
191
0
    const GDALDataType eDT2 = GDALFindDataTypeForValue(dfValue, bComplex);
192
0
    return GDALDataTypeUnion(eDT, eDT2);
193
4
}
194
195
/************************************************************************/
196
/*                        GetMinBitsForValue()                          */
197
/************************************************************************/
198
static int GetMinBitsForValue(double dValue)
199
0
{
200
0
    if (round(dValue) == dValue)
201
0
    {
202
0
        if (dValue <= cpl::NumericLimits<GByte>::max() &&
203
0
            dValue >= cpl::NumericLimits<GByte>::lowest())
204
0
            return 8;
205
206
0
        if (dValue <= cpl::NumericLimits<GInt8>::max() &&
207
0
            dValue >= cpl::NumericLimits<GInt8>::lowest())
208
0
            return 8;
209
210
0
        if (dValue <= cpl::NumericLimits<GInt16>::max() &&
211
0
            dValue >= cpl::NumericLimits<GInt16>::lowest())
212
0
            return 16;
213
214
0
        if (dValue <= cpl::NumericLimits<GUInt16>::max() &&
215
0
            dValue >= cpl::NumericLimits<GUInt16>::lowest())
216
0
            return 16;
217
218
0
        if (dValue <= cpl::NumericLimits<GInt32>::max() &&
219
0
            dValue >= cpl::NumericLimits<GInt32>::lowest())
220
0
            return 32;
221
222
0
        if (dValue <= cpl::NumericLimits<GUInt32>::max() &&
223
0
            dValue >= cpl::NumericLimits<GUInt32>::lowest())
224
0
            return 32;
225
226
0
        if (dValue <=
227
0
                static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) &&
228
0
            dValue >= static_cast<double>(
229
0
                          cpl::NumericLimits<std::uint64_t>::lowest()))
230
0
            return 64;
231
0
    }
232
0
    else if (static_cast<float>(dValue) == dValue)
233
0
    {
234
0
        return 32;
235
0
    }
236
237
0
    return 64;
238
0
}
239
240
/************************************************************************/
241
/*                        GDALFindDataType()                            */
242
/************************************************************************/
243
244
/**
245
 * \brief Finds the smallest data type able to support the given
246
 *  requirements
247
 *
248
 * @param nBits number of bits necessary
249
 * @param bSigned if negative values are necessary
250
 * @param bFloating if non-integer values necessary
251
 * @param bComplex if complex values are necessary
252
 *
253
 * @return a best fit GDALDataType for supporting the requirements
254
 */
255
GDALDataType CPL_STDCALL GDALFindDataType(int nBits, int bSigned, int bFloating,
256
                                          int bComplex)
257
22.1k
{
258
22.1k
    if (!bFloating)
259
21.0k
    {
260
21.0k
        if (!bComplex)
261
21.0k
        {
262
21.0k
            if (!bSigned)
263
15.1k
            {
264
15.1k
                if (nBits <= 8)
265
6.34k
                    return GDT_UInt8;
266
8.82k
                if (nBits <= 16)
267
4.72k
                    return GDT_UInt16;
268
4.10k
                if (nBits <= 32)
269
4.10k
                    return GDT_UInt32;
270
0
                if (nBits <= 64)
271
0
                    return GDT_UInt64;
272
0
                return GDT_Float64;
273
0
            }
274
5.85k
            else  // bSigned
275
5.85k
            {
276
5.85k
                if (nBits <= 8)
277
345
                    return GDT_Int8;
278
5.50k
                if (nBits <= 16)
279
3.83k
                    return GDT_Int16;
280
1.67k
                if (nBits <= 32)
281
1.67k
                    return GDT_Int32;
282
0
                if (nBits <= 64)
283
0
                    return GDT_Int64;
284
0
                return GDT_Float64;
285
0
            }
286
21.0k
        }
287
0
        else  // bComplex
288
0
        {
289
0
            if (!bSigned)
290
0
            {
291
                // We don't have complex unsigned data types, so
292
                // return a large-enough complex signed type
293
294
                // Do not choose CInt16 for backward compatibility
295
                // if (nBits <= 15)
296
                //     return GDT_CInt16;
297
0
                if (nBits <= 31)
298
0
                    return GDT_CInt32;
299
0
                return GDT_CFloat64;
300
0
            }
301
0
            else  // bSigned
302
0
            {
303
0
                if (nBits <= 16)
304
0
                    return GDT_CInt16;
305
0
                if (nBits <= 32)
306
0
                    return GDT_CInt32;
307
0
                return GDT_CFloat64;
308
0
            }
309
0
        }
310
21.0k
    }
311
1.08k
    else  // bFloating
312
1.08k
    {
313
1.08k
        if (!bComplex)
314
1.08k
        {
315
            // Do not choose Float16 since is not supported everywhere
316
            // if (nBits <= 16)
317
            //     return GDT_Float16;
318
1.08k
            if (nBits <= 32)
319
46
                return GDT_Float32;
320
1.03k
            return GDT_Float64;
321
1.08k
        }
322
0
        else  // bComplex
323
0
        {
324
            // Do not choose Float16 since is not supported everywhere
325
            // if (nBits <= 16)
326
            //     return GDT_CFloat16;
327
0
            if (nBits <= 32)
328
0
                return GDT_CFloat32;
329
0
            return GDT_CFloat64;
330
0
        }
331
1.08k
    }
332
22.1k
}
333
334
/************************************************************************/
335
/*                        GDALFindDataTypeForValue()                    */
336
/************************************************************************/
337
338
/**
339
 * \brief Finds the smallest data type able to support the provided value
340
 *
341
 * @param dValue value to support
342
 * @param bComplex is the value complex
343
 *
344
 * @return a best fit GDALDataType for supporting the value
345
 */
346
GDALDataType CPL_STDCALL GDALFindDataTypeForValue(double dValue, int bComplex)
347
0
{
348
0
    const bool bFloating =
349
0
        round(dValue) != dValue ||
350
0
        dValue >
351
0
            static_cast<double>(cpl::NumericLimits<std::uint64_t>::max()) ||
352
0
        dValue <
353
0
            static_cast<double>(cpl::NumericLimits<std::int64_t>::lowest());
354
0
    const bool bSigned = bFloating || dValue < 0;
355
0
    const int nBits = GetMinBitsForValue(dValue);
356
357
0
    return GDALFindDataType(nBits, bSigned, bFloating, bComplex);
358
0
}
359
360
/************************************************************************/
361
/*                        GDALGetDataTypeSizeBytes()                    */
362
/************************************************************************/
363
364
/**
365
 * \brief Get data type size in <b>bytes</b>.
366
 *
367
 * Returns the size of a GDT_* type in bytes.  In contrast,
368
 * GDALGetDataTypeSize() returns the size in <b>bits</b>.
369
 *
370
 * @param eDataType type, such as GDT_UInt8.
371
 * @return the number of bytes or zero if it is not recognised.
372
 */
373
374
int CPL_STDCALL GDALGetDataTypeSizeBytes(GDALDataType eDataType)
375
376
1.09G
{
377
1.09G
    switch (eDataType)
378
1.09G
    {
379
659M
        case GDT_UInt8:
380
661M
        case GDT_Int8:
381
661M
            return 1;
382
383
17.6M
        case GDT_UInt16:
384
30.8M
        case GDT_Int16:
385
33.7M
        case GDT_Float16:
386
33.7M
            return 2;
387
388
12.2M
        case GDT_UInt32:
389
281M
        case GDT_Int32:
390
323M
        case GDT_Float32:
391
323M
        case GDT_CInt16:
392
323M
        case GDT_CFloat16:
393
323M
            return 4;
394
395
72.2M
        case GDT_Float64:
396
72.7M
        case GDT_CInt32:
397
73.2M
        case GDT_CFloat32:
398
74.9M
        case GDT_UInt64:
399
75.9M
        case GDT_Int64:
400
75.9M
            return 8;
401
402
1.03M
        case GDT_CFloat64:
403
1.03M
            return 16;
404
405
52.8k
        case GDT_Unknown:
406
52.8k
        case GDT_TypeCount:
407
52.8k
            break;
408
1.09G
    }
409
52.8k
    return 0;
410
1.09G
}
411
412
/************************************************************************/
413
/*                        GDALGetDataTypeSizeBits()                     */
414
/************************************************************************/
415
416
/**
417
 * \brief Get data type size in <b>bits</b>.
418
 *
419
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!  Use
420
 * GDALGetDataTypeSizeBytes() for bytes.
421
 *
422
 * @param eDataType type, such as GDT_UInt8.
423
 * @return the number of bits or zero if it is not recognised.
424
 */
425
426
int CPL_STDCALL GDALGetDataTypeSizeBits(GDALDataType eDataType)
427
428
1.22M
{
429
1.22M
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
430
1.22M
}
431
432
/************************************************************************/
433
/*                        GDALGetDataTypeSize()                         */
434
/************************************************************************/
435
436
/**
437
 * \brief Get data type size in bits.  <b>Deprecated</b>.
438
 *
439
 * Returns the size of a GDT_* type in bits, <b>not bytes</b>!
440
 *
441
 * Use GDALGetDataTypeSizeBytes() for bytes.
442
 * Use GDALGetDataTypeSizeBits() for bits.
443
 *
444
 * @param eDataType type, such as GDT_UInt8.
445
 * @return the number of bits or zero if it is not recognised.
446
 */
447
448
int CPL_STDCALL GDALGetDataTypeSize(GDALDataType eDataType)
449
450
0
{
451
0
    return GDALGetDataTypeSizeBytes(eDataType) * 8;
452
0
}
453
454
/************************************************************************/
455
/*                       GDALDataTypeIsComplex()                        */
456
/************************************************************************/
457
458
/**
459
 * \brief Is data type complex?
460
 *
461
 * @return TRUE if the passed type is complex (one of GDT_CInt16, GDT_CInt32,
462
 * GDT_CFloat32 or GDT_CFloat64), that is it consists of a real and imaginary
463
 * component.
464
 */
465
466
int CPL_STDCALL GDALDataTypeIsComplex(GDALDataType eDataType)
467
468
2.41M
{
469
2.41M
    switch (eDataType)
470
2.41M
    {
471
2.73k
        case GDT_CInt16:
472
3.71k
        case GDT_CInt32:
473
4.22k
        case GDT_CFloat16:
474
9.02k
        case GDT_CFloat32:
475
17.9k
        case GDT_CFloat64:
476
17.9k
            return TRUE;
477
478
530k
        case GDT_UInt8:
479
557k
        case GDT_Int8:
480
1.35M
        case GDT_Int16:
481
1.59M
        case GDT_UInt16:
482
1.65M
        case GDT_Int32:
483
2.00M
        case GDT_UInt32:
484
2.00M
        case GDT_Int64:
485
2.01M
        case GDT_UInt64:
486
2.03M
        case GDT_Float16:
487
2.18M
        case GDT_Float32:
488
2.39M
        case GDT_Float64:
489
2.39M
            return FALSE;
490
491
0
        case GDT_Unknown:
492
0
        case GDT_TypeCount:
493
0
            break;
494
2.41M
    }
495
0
    return FALSE;
496
2.41M
}
497
498
/************************************************************************/
499
/*                       GDALDataTypeIsFloating()                       */
500
/************************************************************************/
501
502
/**
503
 * \brief Is data type floating? (might be complex)
504
 *
505
 * @return TRUE if the passed type is floating (one of GDT_Float32, GDT_Float16,
506
 * GDT_Float64, GDT_CFloat16, GDT_CFloat32, GDT_CFloat64)
507
 */
508
509
int CPL_STDCALL GDALDataTypeIsFloating(GDALDataType eDataType)
510
486k
{
511
486k
    switch (eDataType)
512
486k
    {
513
0
        case GDT_Float16:
514
40.1k
        case GDT_Float32:
515
114k
        case GDT_Float64:
516
114k
        case GDT_CFloat16:
517
114k
        case GDT_CFloat32:
518
114k
        case GDT_CFloat64:
519
114k
            return TRUE;
520
521
148k
        case GDT_UInt8:
522
152k
        case GDT_Int8:
523
168k
        case GDT_Int16:
524
222k
        case GDT_UInt16:
525
229k
        case GDT_Int32:
526
371k
        case GDT_UInt32:
527
371k
        case GDT_Int64:
528
372k
        case GDT_UInt64:
529
372k
        case GDT_CInt16:
530
372k
        case GDT_CInt32:
531
372k
            return FALSE;
532
533
0
        case GDT_Unknown:
534
0
        case GDT_TypeCount:
535
0
            break;
536
486k
    }
537
0
    return FALSE;
538
486k
}
539
540
/************************************************************************/
541
/*                       GDALDataTypeIsInteger()                        */
542
/************************************************************************/
543
544
/**
545
 * \brief Is data type integer? (might be complex)
546
 *
547
 * @return TRUE if the passed type is integer (one of GDT_UInt8, GDT_Int16,
548
 * GDT_UInt16, GDT_Int32, GDT_UInt32, GDT_CInt16, GDT_CInt32).
549
 */
550
551
int CPL_STDCALL GDALDataTypeIsInteger(GDALDataType eDataType)
552
553
503k
{
554
503k
    switch (eDataType)
555
503k
    {
556
167k
        case GDT_UInt8:
557
171k
        case GDT_Int8:
558
184k
        case GDT_Int16:
559
237k
        case GDT_UInt16:
560
242k
        case GDT_Int32:
561
447k
        case GDT_UInt32:
562
447k
        case GDT_CInt16:
563
447k
        case GDT_CInt32:
564
448k
        case GDT_UInt64:
565
448k
        case GDT_Int64:
566
448k
            return TRUE;
567
568
15.6k
        case GDT_Float16:
569
43.6k
        case GDT_Float32:
570
55.2k
        case GDT_Float64:
571
55.2k
        case GDT_CFloat16:
572
55.2k
        case GDT_CFloat32:
573
55.2k
        case GDT_CFloat64:
574
55.2k
            return FALSE;
575
576
0
        case GDT_Unknown:
577
0
        case GDT_TypeCount:
578
0
            break;
579
503k
    }
580
0
    return FALSE;
581
503k
}
582
583
/************************************************************************/
584
/*                       GDALDataTypeIsSigned()                         */
585
/************************************************************************/
586
587
/**
588
 * \brief Is data type signed?
589
 *
590
 * @return TRUE if the passed type is signed.
591
 */
592
593
int CPL_STDCALL GDALDataTypeIsSigned(GDALDataType eDataType)
594
703k
{
595
703k
    switch (eDataType)
596
703k
    {
597
281k
        case GDT_UInt8:
598
382k
        case GDT_UInt16:
599
657k
        case GDT_UInt32:
600
658k
        case GDT_UInt64:
601
658k
            return FALSE;
602
603
7.60k
        case GDT_Int8:
604
31.8k
        case GDT_Int16:
605
43.3k
        case GDT_Int32:
606
43.3k
        case GDT_Int64:
607
43.3k
        case GDT_Float16:
608
43.4k
        case GDT_Float32:
609
45.3k
        case GDT_Float64:
610
45.3k
        case GDT_CInt16:
611
45.3k
        case GDT_CInt32:
612
45.3k
        case GDT_CFloat16:
613
45.3k
        case GDT_CFloat32:
614
45.3k
        case GDT_CFloat64:
615
45.3k
            return TRUE;
616
617
0
        case GDT_Unknown:
618
0
        case GDT_TypeCount:
619
0
            break;
620
703k
    }
621
0
    return FALSE;
622
703k
}
623
624
/************************************************************************/
625
/*                    GDALDataTypeIsConversionLossy()                   */
626
/************************************************************************/
627
628
/**
629
 * \brief Is conversion from eTypeFrom to eTypeTo potentially lossy
630
 *
631
 * @param eTypeFrom input datatype
632
 * @param eTypeTo output datatype
633
 * @return TRUE if conversion from eTypeFrom to eTypeTo potentially lossy.
634
 */
635
636
int CPL_STDCALL GDALDataTypeIsConversionLossy(GDALDataType eTypeFrom,
637
                                              GDALDataType eTypeTo)
638
497k
{
639
    // E.g cfloat32 -> float32
640
497k
    if (GDALDataTypeIsComplex(eTypeFrom) && !GDALDataTypeIsComplex(eTypeTo))
641
12
        return TRUE;
642
643
497k
    eTypeFrom = GDALGetNonComplexDataType(eTypeFrom);
644
497k
    eTypeTo = GDALGetNonComplexDataType(eTypeTo);
645
646
497k
    if (GDALDataTypeIsInteger(eTypeTo))
647
442k
    {
648
        // E.g. float32 -> int32
649
442k
        if (GDALDataTypeIsFloating(eTypeFrom))
650
112k
            return TRUE;
651
652
        // E.g. Int16 to UInt16
653
329k
        const int bIsFromSigned = GDALDataTypeIsSigned(eTypeFrom);
654
329k
        const int bIsToSigned = GDALDataTypeIsSigned(eTypeTo);
655
329k
        if (bIsFromSigned && !bIsToSigned)
656
251
            return TRUE;
657
658
        // E.g UInt32 to UInt16
659
329k
        const int nFromSize = GDALGetDataTypeSizeBits(eTypeFrom);
660
329k
        const int nToSize = GDALGetDataTypeSizeBits(eTypeTo);
661
329k
        if (nFromSize > nToSize)
662
367
            return TRUE;
663
664
        // E.g UInt16 to Int16
665
329k
        if (nFromSize == nToSize && !bIsFromSigned && bIsToSigned)
666
0
            return TRUE;
667
668
329k
        return FALSE;
669
329k
    }
670
671
54.8k
    if (eTypeTo == GDT_Float16 &&
672
15.6k
        (eTypeFrom == GDT_Int16 || eTypeFrom == GDT_UInt16 ||
673
15.6k
         eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
674
15.6k
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
675
15.6k
         eTypeFrom == GDT_Float32 || eTypeFrom == GDT_Float64))
676
0
    {
677
0
        return TRUE;
678
0
    }
679
680
54.8k
    if (eTypeTo == GDT_Float32 &&
681
27.9k
        (eTypeFrom == GDT_Int32 || eTypeFrom == GDT_UInt32 ||
682
27.9k
         eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64 ||
683
27.9k
         eTypeFrom == GDT_Float64))
684
0
    {
685
0
        return TRUE;
686
0
    }
687
688
54.8k
    if (eTypeTo == GDT_Float64 &&
689
11.2k
        (eTypeFrom == GDT_Int64 || eTypeFrom == GDT_UInt64))
690
0
    {
691
0
        return TRUE;
692
0
    }
693
694
54.8k
    return FALSE;
695
54.8k
}
696
697
/************************************************************************/
698
/*                        GDALGetDataTypeName()                         */
699
/************************************************************************/
700
701
/**
702
 * \brief Get name of data type.
703
 *
704
 * Returns a symbolic name for the data type.  This is essentially the
705
 * the enumerated item name with the GDT_ prefix removed.  So GDT_UInt8 returns
706
 * "Byte".  The returned strings are static strings and should not be modified
707
 * or freed by the application.  These strings are useful for reporting
708
 * datatypes in debug statements, errors and other user output.
709
 *
710
 * @param eDataType type to get name of.
711
 * @return string corresponding to existing data type
712
 *         or NULL pointer if invalid type given.
713
 */
714
715
const char *CPL_STDCALL GDALGetDataTypeName(GDALDataType eDataType)
716
717
2.31M
{
718
2.31M
    switch (eDataType)
719
2.31M
    {
720
8.88k
        case GDT_Unknown:
721
8.88k
            return "Unknown";
722
723
1.56M
        case GDT_UInt8:
724
            // TODO: return UInt8 for GDAL 4 ?
725
1.56M
            return "Byte";
726
727
3.46k
        case GDT_Int8:
728
3.46k
            return "Int8";
729
730
178k
        case GDT_UInt16:
731
178k
            return "UInt16";
732
733
211k
        case GDT_Int16:
734
211k
            return "Int16";
735
736
39.9k
        case GDT_UInt32:
737
39.9k
            return "UInt32";
738
739
14.5k
        case GDT_Int32:
740
14.5k
            return "Int32";
741
742
2.94k
        case GDT_UInt64:
743
2.94k
            return "UInt64";
744
745
1.60k
        case GDT_Int64:
746
1.60k
            return "Int64";
747
748
78
        case GDT_Float16:
749
78
            return "Float16";
750
751
266k
        case GDT_Float32:
752
266k
            return "Float32";
753
754
18.2k
        case GDT_Float64:
755
18.2k
            return "Float64";
756
757
3.34k
        case GDT_CInt16:
758
3.34k
            return "CInt16";
759
760
1.70k
        case GDT_CInt32:
761
1.70k
            return "CInt32";
762
763
76
        case GDT_CFloat16:
764
76
            return "CFloat16";
765
766
3.89k
        case GDT_CFloat32:
767
3.89k
            return "CFloat32";
768
769
1.22k
        case GDT_CFloat64:
770
1.22k
            return "CFloat64";
771
772
0
        case GDT_TypeCount:
773
0
            break;
774
2.31M
    }
775
0
    return nullptr;
776
2.31M
}
777
778
/************************************************************************/
779
/*                        GDALGetDataTypeByName()                       */
780
/************************************************************************/
781
782
/**
783
 * \brief Get data type by symbolic name.
784
 *
785
 * Returns a data type corresponding to the given symbolic name. This
786
 * function is opposite to the GDALGetDataTypeName().
787
 *
788
 * @param pszName string containing the symbolic name of the type.
789
 *
790
 * @return GDAL data type.
791
 */
792
793
GDALDataType CPL_STDCALL GDALGetDataTypeByName(const char *pszName)
794
795
77.4k
{
796
77.4k
    VALIDATE_POINTER1(pszName, "GDALGetDataTypeByName", GDT_Unknown);
797
798
77.4k
    if (EQUAL(pszName, "UInt8"))
799
0
        return GDT_UInt8;
800
801
161k
    for (int iType = 1; iType < GDT_TypeCount; iType++)
802
161k
    {
803
161k
        const auto eType = static_cast<GDALDataType>(iType);
804
161k
        if (GDALGetDataTypeName(eType) != nullptr &&
805
161k
            EQUAL(GDALGetDataTypeName(eType), pszName))
806
77.3k
        {
807
77.3k
            return eType;
808
77.3k
        }
809
161k
    }
810
811
37
    return GDT_Unknown;
812
77.4k
}
813
814
/************************************************************************/
815
/*                      GDALAdjustValueToDataType()                     */
816
/************************************************************************/
817
818
template <class T>
819
static inline void ClampAndRound(double &dfValue, bool &bClamped,
820
                                 bool &bRounded)
821
0
{
822
0
    if (dfValue < static_cast<double>(cpl::NumericLimits<T>::lowest()))
823
0
    {
824
0
        bClamped = true;
825
0
        dfValue = static_cast<double>(cpl::NumericLimits<T>::lowest());
826
0
    }
827
0
    else if (dfValue > static_cast<double>(cpl::NumericLimits<T>::max()))
828
0
    {
829
0
        bClamped = true;
830
0
        dfValue = static_cast<double>(cpl::NumericLimits<T>::max());
831
0
    }
832
0
    else if (dfValue != static_cast<double>(static_cast<T>(dfValue)))
833
0
    {
834
0
        bRounded = true;
835
0
        dfValue = static_cast<double>(static_cast<T>(floor(dfValue + 0.5)));
836
0
    }
837
0
}
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned char>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<signed char>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<short>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned short>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<int>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned int>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<long>(double&, bool&, bool&)
Unexecuted instantiation: gdal_misc.cpp:void ClampAndRound<unsigned long>(double&, bool&, bool&)
838
839
/**
840
 * \brief Adjust a value to the output data type
841
 *
842
 * Adjustment consist in clamping to minimum/maximum values of the data type
843
 * and rounding for integral types.
844
 *
845
 * @param eDT target data type.
846
 * @param dfValue value to adjust.
847
 * @param pbClamped pointer to a integer(boolean) to indicate if clamping has
848
 * been made, or NULL
849
 * @param pbRounded pointer to a integer(boolean) to indicate if rounding has
850
 * been made, or NULL
851
 *
852
 * @return adjusted value
853
 */
854
855
double GDALAdjustValueToDataType(GDALDataType eDT, double dfValue,
856
                                 int *pbClamped, int *pbRounded)
857
0
{
858
0
    bool bClamped = false;
859
0
    bool bRounded = false;
860
0
    switch (eDT)
861
0
    {
862
0
        case GDT_UInt8:
863
0
            ClampAndRound<GByte>(dfValue, bClamped, bRounded);
864
0
            break;
865
0
        case GDT_Int8:
866
0
            ClampAndRound<GInt8>(dfValue, bClamped, bRounded);
867
0
            break;
868
0
        case GDT_Int16:
869
0
            ClampAndRound<GInt16>(dfValue, bClamped, bRounded);
870
0
            break;
871
0
        case GDT_UInt16:
872
0
            ClampAndRound<GUInt16>(dfValue, bClamped, bRounded);
873
0
            break;
874
0
        case GDT_Int32:
875
0
            ClampAndRound<GInt32>(dfValue, bClamped, bRounded);
876
0
            break;
877
0
        case GDT_UInt32:
878
0
            ClampAndRound<GUInt32>(dfValue, bClamped, bRounded);
879
0
            break;
880
0
        case GDT_Int64:
881
0
            ClampAndRound<std::int64_t>(dfValue, bClamped, bRounded);
882
0
            break;
883
0
        case GDT_UInt64:
884
0
            ClampAndRound<std::uint64_t>(dfValue, bClamped, bRounded);
885
0
            break;
886
0
        case GDT_Float16:
887
0
        {
888
0
            if (!std::isfinite(dfValue))
889
0
                break;
890
891
            // TODO: Use ClampAndRound
892
0
            if (dfValue < cpl::NumericLimits<GFloat16>::lowest())
893
0
            {
894
0
                bClamped = TRUE;
895
0
                dfValue =
896
0
                    static_cast<double>(cpl::NumericLimits<GFloat16>::lowest());
897
0
            }
898
0
            else if (dfValue > cpl::NumericLimits<GFloat16>::max())
899
0
            {
900
0
                bClamped = TRUE;
901
0
                dfValue =
902
0
                    static_cast<double>(cpl::NumericLimits<GFloat16>::max());
903
0
            }
904
0
            else
905
0
            {
906
                // Intentionally lose precision.
907
                // TODO(schwehr): Is the double cast really necessary?
908
                // If so, why?  What will fail?
909
0
                dfValue = static_cast<double>(static_cast<GFloat16>(dfValue));
910
0
            }
911
0
            break;
912
0
        }
913
0
        case GDT_Float32:
914
0
        {
915
0
            if (!std::isfinite(dfValue))
916
0
                break;
917
918
            // TODO: Use ClampAndRound
919
0
            if (dfValue < cpl::NumericLimits<float>::lowest())
920
0
            {
921
0
                bClamped = TRUE;
922
0
                dfValue =
923
0
                    static_cast<double>(cpl::NumericLimits<float>::lowest());
924
0
            }
925
0
            else if (dfValue > cpl::NumericLimits<float>::max())
926
0
            {
927
0
                bClamped = TRUE;
928
0
                dfValue = static_cast<double>(cpl::NumericLimits<float>::max());
929
0
            }
930
0
            else
931
0
            {
932
                // Intentionally lose precision.
933
                // TODO(schwehr): Is the double cast really necessary?
934
                // If so, why?  What will fail?
935
0
                dfValue = static_cast<double>(static_cast<float>(dfValue));
936
0
            }
937
0
            break;
938
0
        }
939
0
        case GDT_Float64:
940
0
        case GDT_CInt16:
941
0
        case GDT_CInt32:
942
0
        case GDT_CFloat16:
943
0
        case GDT_CFloat32:
944
0
        case GDT_CFloat64:
945
0
        case GDT_Unknown:
946
0
        case GDT_TypeCount:
947
0
            break;
948
0
    }
949
0
    if (pbClamped)
950
0
        *pbClamped = bClamped;
951
0
    if (pbRounded)
952
0
        *pbRounded = bRounded;
953
0
    return dfValue;
954
0
}
955
956
/************************************************************************/
957
/*                         GDALIsValueExactAs()                         */
958
/************************************************************************/
959
960
/**
961
 * \brief Check whether the provided value can be exactly represented in a
962
 * data type.
963
 *
964
 * Only implemented for non-complex data types
965
 *
966
 * @param dfValue value to check.
967
 * @param eDT target data type.
968
 *
969
 * @return true if the provided value can be exactly represented in the
970
 * data type.
971
 * @since GDAL 3.10
972
 */
973
bool GDALIsValueExactAs(double dfValue, GDALDataType eDT)
974
15.8k
{
975
15.8k
    switch (eDT)
976
15.8k
    {
977
8.67k
        case GDT_UInt8:
978
8.67k
            return GDALIsValueExactAs<uint8_t>(dfValue);
979
1.63k
        case GDT_Int8:
980
1.63k
            return GDALIsValueExactAs<int8_t>(dfValue);
981
4.08k
        case GDT_UInt16:
982
4.08k
            return GDALIsValueExactAs<uint16_t>(dfValue);
983
237
        case GDT_Int16:
984
237
            return GDALIsValueExactAs<int16_t>(dfValue);
985
545
        case GDT_UInt32:
986
545
            return GDALIsValueExactAs<uint32_t>(dfValue);
987
1
        case GDT_Int32:
988
1
            return GDALIsValueExactAs<int32_t>(dfValue);
989
0
        case GDT_UInt64:
990
0
            return GDALIsValueExactAs<uint64_t>(dfValue);
991
0
        case GDT_Int64:
992
0
            return GDALIsValueExactAs<int64_t>(dfValue);
993
1
        case GDT_Float16:
994
1
            return GDALIsValueExactAs<GFloat16>(dfValue);
995
0
        case GDT_Float32:
996
0
            return GDALIsValueExactAs<float>(dfValue);
997
715
        case GDT_Float64:
998
715
            return true;
999
0
        case GDT_Unknown:
1000
0
        case GDT_CInt16:
1001
0
        case GDT_CInt32:
1002
0
        case GDT_CFloat16:
1003
0
        case GDT_CFloat32:
1004
0
        case GDT_CFloat64:
1005
0
        case GDT_TypeCount:
1006
0
            break;
1007
15.8k
    }
1008
0
    return true;
1009
15.8k
}
1010
1011
/************************************************************************/
1012
/*                         GDALIsValueInRangeOf()                       */
1013
/************************************************************************/
1014
1015
/**
1016
 * \brief Check whether the provided value can be represented in the range
1017
 * of the data type, possibly with rounding.
1018
 *
1019
 * Only implemented for non-complex data types
1020
 *
1021
 * @param dfValue value to check.
1022
 * @param eDT target data type.
1023
 *
1024
 * @return true if the provided value can be represented in the range
1025
 * of the data type, possibly with rounding.
1026
 * @since GDAL 3.11
1027
 */
1028
bool GDALIsValueInRangeOf(double dfValue, GDALDataType eDT)
1029
0
{
1030
0
    switch (eDT)
1031
0
    {
1032
0
        case GDT_UInt8:
1033
0
            return GDALIsValueInRange<uint8_t>(dfValue);
1034
0
        case GDT_Int8:
1035
0
            return GDALIsValueInRange<int8_t>(dfValue);
1036
0
        case GDT_UInt16:
1037
0
            return GDALIsValueInRange<uint16_t>(dfValue);
1038
0
        case GDT_Int16:
1039
0
            return GDALIsValueInRange<int16_t>(dfValue);
1040
0
        case GDT_UInt32:
1041
0
            return GDALIsValueInRange<uint32_t>(dfValue);
1042
0
        case GDT_Int32:
1043
0
            return GDALIsValueInRange<int32_t>(dfValue);
1044
0
        case GDT_UInt64:
1045
0
            return GDALIsValueInRange<uint64_t>(dfValue);
1046
0
        case GDT_Int64:
1047
0
            return GDALIsValueInRange<int64_t>(dfValue);
1048
0
        case GDT_Float16:
1049
0
            return GDALIsValueInRange<GFloat16>(dfValue);
1050
0
        case GDT_Float32:
1051
0
            return GDALIsValueInRange<float>(dfValue);
1052
0
        case GDT_Float64:
1053
0
            return true;
1054
0
        case GDT_Unknown:
1055
0
        case GDT_CInt16:
1056
0
        case GDT_CInt32:
1057
0
        case GDT_CFloat16:
1058
0
        case GDT_CFloat32:
1059
0
        case GDT_CFloat64:
1060
0
        case GDT_TypeCount:
1061
0
            break;
1062
0
    }
1063
0
    return true;
1064
0
}
1065
1066
/************************************************************************/
1067
/*                        GDALGetNonComplexDataType()                   */
1068
/************************************************************************/
1069
/**
1070
 * \brief Return the base data type for the specified input.
1071
 *
1072
 * If the input data type is complex this function returns the base type
1073
 * i.e. the data type of the real and imaginary parts (non-complex).
1074
 * If the input data type is already non-complex, then it is returned
1075
 * unchanged.
1076
 *
1077
 * @param eDataType type, such as GDT_CFloat32.
1078
 *
1079
 * @return GDAL data type.
1080
 */
1081
GDALDataType CPL_STDCALL GDALGetNonComplexDataType(GDALDataType eDataType)
1082
994k
{
1083
994k
    switch (eDataType)
1084
994k
    {
1085
0
        case GDT_CInt16:
1086
0
            return GDT_Int16;
1087
0
        case GDT_CInt32:
1088
0
            return GDT_Int32;
1089
0
        case GDT_CFloat16:
1090
0
            return GDT_Float16;
1091
0
        case GDT_CFloat32:
1092
0
            return GDT_Float32;
1093
0
        case GDT_CFloat64:
1094
0
            return GDT_Float64;
1095
1096
298k
        case GDT_UInt8:
1097
397k
        case GDT_UInt16:
1098
735k
        case GDT_UInt32:
1099
736k
        case GDT_UInt64:
1100
743k
        case GDT_Int8:
1101
762k
        case GDT_Int16:
1102
772k
        case GDT_Int32:
1103
772k
        case GDT_Int64:
1104
803k
        case GDT_Float16:
1105
899k
        case GDT_Float32:
1106
994k
        case GDT_Float64:
1107
994k
            break;
1108
1109
0
        case GDT_Unknown:
1110
0
        case GDT_TypeCount:
1111
0
            break;
1112
994k
    }
1113
994k
    return eDataType;
1114
994k
}
1115
1116
/************************************************************************/
1117
/*                        GDALGetAsyncStatusTypeByName()                */
1118
/************************************************************************/
1119
/**
1120
 * Get AsyncStatusType by symbolic name.
1121
 *
1122
 * Returns a data type corresponding to the given symbolic name. This
1123
 * function is opposite to the GDALGetAsyncStatusTypeName().
1124
 *
1125
 * @param pszName string containing the symbolic name of the type.
1126
 *
1127
 * @return GDAL AsyncStatus type.
1128
 */
1129
GDALAsyncStatusType CPL_DLL CPL_STDCALL
1130
GDALGetAsyncStatusTypeByName(const char *pszName)
1131
0
{
1132
0
    VALIDATE_POINTER1(pszName, "GDALGetAsyncStatusTypeByName", GARIO_ERROR);
1133
1134
0
    for (int iType = 0; iType < GARIO_TypeCount; iType++)
1135
0
    {
1136
0
        const auto eType = static_cast<GDALAsyncStatusType>(iType);
1137
0
        if (GDALGetAsyncStatusTypeName(eType) != nullptr &&
1138
0
            EQUAL(GDALGetAsyncStatusTypeName(eType), pszName))
1139
0
        {
1140
0
            return eType;
1141
0
        }
1142
0
    }
1143
1144
0
    return GARIO_ERROR;
1145
0
}
1146
1147
/************************************************************************/
1148
/*                        GDALGetAsyncStatusTypeName()                 */
1149
/************************************************************************/
1150
1151
/**
1152
 * Get name of AsyncStatus data type.
1153
 *
1154
 * Returns a symbolic name for the AsyncStatus data type.  This is essentially
1155
 * the enumerated item name with the GARIO_ prefix removed.  So
1156
 * GARIO_COMPLETE returns "COMPLETE".  The returned strings are static strings
1157
 * and should not be modified or freed by the application.  These strings are
1158
 * useful for reporting datatypes in debug statements, errors and other user
1159
 * output.
1160
 *
1161
 * @param eAsyncStatusType type to get name of.
1162
 * @return string corresponding to type.
1163
 */
1164
1165
const char *CPL_STDCALL
1166
GDALGetAsyncStatusTypeName(GDALAsyncStatusType eAsyncStatusType)
1167
1168
0
{
1169
0
    switch (eAsyncStatusType)
1170
0
    {
1171
0
        case GARIO_PENDING:
1172
0
            return "PENDING";
1173
1174
0
        case GARIO_UPDATE:
1175
0
            return "UPDATE";
1176
1177
0
        case GARIO_ERROR:
1178
0
            return "ERROR";
1179
1180
0
        case GARIO_COMPLETE:
1181
0
            return "COMPLETE";
1182
1183
0
        default:
1184
0
            return nullptr;
1185
0
    }
1186
0
}
1187
1188
/************************************************************************/
1189
/*                  GDALGetPaletteInterpretationName()                  */
1190
/************************************************************************/
1191
1192
/**
1193
 * \brief Get name of palette interpretation
1194
 *
1195
 * Returns a symbolic name for the palette interpretation.  This is the
1196
 * the enumerated item name with the GPI_ prefix removed.  So GPI_Gray returns
1197
 * "Gray".  The returned strings are static strings and should not be modified
1198
 * or freed by the application.
1199
 *
1200
 * @param eInterp palette interpretation to get name of.
1201
 * @return string corresponding to palette interpretation.
1202
 */
1203
1204
const char *GDALGetPaletteInterpretationName(GDALPaletteInterp eInterp)
1205
1206
0
{
1207
0
    switch (eInterp)
1208
0
    {
1209
0
        case GPI_Gray:
1210
0
            return "Gray";
1211
1212
0
        case GPI_RGB:
1213
0
            return "RGB";
1214
1215
0
        case GPI_CMYK:
1216
0
            return "CMYK";
1217
1218
0
        case GPI_HLS:
1219
0
            return "HLS";
1220
1221
0
        default:
1222
0
            return "Unknown";
1223
0
    }
1224
0
}
1225
1226
/************************************************************************/
1227
/*                   GDALGetColorInterpretationName()                   */
1228
/************************************************************************/
1229
1230
/**
1231
 * \brief Get name of color interpretation
1232
 *
1233
 * Returns a symbolic name for the color interpretation.  This is derived from
1234
 * the enumerated item name with the GCI_ prefix removed, but there are some
1235
 * variations. So GCI_GrayIndex returns "Gray" and GCI_RedBand returns "Red".
1236
 * The returned strings are static strings and should not be modified
1237
 * or freed by the application.
1238
 *
1239
 * @param eInterp color interpretation to get name of.
1240
 * @return string corresponding to color interpretation
1241
 *         or NULL pointer if invalid enumerator given.
1242
 */
1243
1244
const char *GDALGetColorInterpretationName(GDALColorInterp eInterp)
1245
1246
260k
{
1247
260k
    static_assert(GCI_IR_Start == GCI_RedEdgeBand + 1);
1248
260k
    static_assert(GCI_NIRBand == GCI_IR_Start);
1249
260k
    static_assert(GCI_SAR_Start == GCI_IR_End + 1);
1250
260k
    static_assert(GCI_Max == GCI_SAR_End);
1251
1252
260k
    switch (eInterp)
1253
260k
    {
1254
24.0k
        case GCI_Undefined:
1255
24.0k
            break;
1256
1257
75.0k
        case GCI_GrayIndex:
1258
75.0k
            return "Gray";
1259
1260
57.3k
        case GCI_PaletteIndex:
1261
57.3k
            return "Palette";
1262
1263
31.6k
        case GCI_RedBand:
1264
31.6k
            return "Red";
1265
1266
4.41k
        case GCI_GreenBand:
1267
4.41k
            return "Green";
1268
1269
3.79k
        case GCI_BlueBand:
1270
3.79k
            return "Blue";
1271
1272
10.0k
        case GCI_AlphaBand:
1273
10.0k
            return "Alpha";
1274
1275
1.65k
        case GCI_HueBand:
1276
1.65k
            return "Hue";
1277
1278
1.65k
        case GCI_SaturationBand:
1279
1.65k
            return "Saturation";
1280
1281
1.65k
        case GCI_LightnessBand:
1282
1.65k
            return "Lightness";
1283
1284
1.65k
        case GCI_CyanBand:
1285
1.65k
            return "Cyan";
1286
1287
1.65k
        case GCI_MagentaBand:
1288
1.65k
            return "Magenta";
1289
1290
1.65k
        case GCI_YellowBand:
1291
1.65k
            return "Yellow";
1292
1293
1.65k
        case GCI_BlackBand:
1294
1.65k
            return "Black";
1295
1296
1.65k
        case GCI_YCbCr_YBand:
1297
1.65k
            return "YCbCr_Y";
1298
1299
1.65k
        case GCI_YCbCr_CbBand:
1300
1.65k
            return "YCbCr_Cb";
1301
1302
1.65k
        case GCI_YCbCr_CrBand:
1303
1.65k
            return "YCbCr_Cr";
1304
1305
1.65k
        case GCI_PanBand:
1306
1.65k
            return "Pan";
1307
1308
1.65k
        case GCI_CoastalBand:
1309
1.65k
            return "Coastal";
1310
1311
1.65k
        case GCI_RedEdgeBand:
1312
1.65k
            return "RedEdge";
1313
1314
1.65k
        case GCI_NIRBand:
1315
1.65k
            return "NIR";
1316
1317
1.65k
        case GCI_SWIRBand:
1318
1.65k
            return "SWIR";
1319
1320
1.65k
        case GCI_MWIRBand:
1321
1.65k
            return "MWIR";
1322
1323
1.65k
        case GCI_LWIRBand:
1324
1.65k
            return "LWIR";
1325
1326
1.65k
        case GCI_TIRBand:
1327
1.65k
            return "TIR";
1328
1329
1.65k
        case GCI_OtherIRBand:
1330
1.65k
            return "OtherIR";
1331
1332
1.65k
        case GCI_IR_Reserved_1:
1333
1.65k
            return "IR_Reserved_1";
1334
1335
1.65k
        case GCI_IR_Reserved_2:
1336
1.65k
            return "IR_Reserved_2";
1337
1338
1.65k
        case GCI_IR_Reserved_3:
1339
1.65k
            return "IR_Reserved_3";
1340
1341
1.65k
        case GCI_IR_Reserved_4:
1342
1.65k
            return "IR_Reserved_4";
1343
1344
1.65k
        case GCI_SAR_Ka_Band:
1345
1.65k
            return "SAR_Ka";
1346
1347
1.65k
        case GCI_SAR_K_Band:
1348
1.65k
            return "SAR_K";
1349
1350
1.65k
        case GCI_SAR_Ku_Band:
1351
1.65k
            return "SAR_Ku";
1352
1353
1.65k
        case GCI_SAR_X_Band:
1354
1.65k
            return "SAR_X";
1355
1356
1.65k
        case GCI_SAR_C_Band:
1357
1.65k
            return "SAR_C";
1358
1359
1.65k
        case GCI_SAR_S_Band:
1360
1.65k
            return "SAR_S";
1361
1362
1.65k
        case GCI_SAR_L_Band:
1363
1.65k
            return "SAR_L";
1364
1365
1.65k
        case GCI_SAR_P_Band:
1366
1.65k
            return "SAR_P";
1367
1368
1.65k
        case GCI_SAR_Reserved_1:
1369
1.65k
            return "SAR_Reserved_1";
1370
1371
1.65k
        case GCI_SAR_Reserved_2:
1372
1.65k
            return "SAR_Reserved_2";
1373
260k
    }
1374
24.0k
    return "Undefined";
1375
260k
}
1376
1377
/************************************************************************/
1378
/*                GDALGetColorInterpretationByName()                    */
1379
/************************************************************************/
1380
1381
/**
1382
 * \brief Get color interpretation by symbolic name.
1383
 *
1384
 * Returns a color interpretation corresponding to the given symbolic name. This
1385
 * function is opposite to the GDALGetColorInterpretationName().
1386
 *
1387
 * @param pszName string containing the symbolic name of the color
1388
 * interpretation.
1389
 *
1390
 * @return GDAL color interpretation.
1391
 *
1392
 */
1393
1394
GDALColorInterp GDALGetColorInterpretationByName(const char *pszName)
1395
1396
18.8k
{
1397
18.8k
    VALIDATE_POINTER1(pszName, "GDALGetColorInterpretationByName",
1398
18.8k
                      GCI_Undefined);
1399
1400
133k
    for (int iType = 0; iType <= GCI_Max; iType++)
1401
131k
    {
1402
131k
        if (EQUAL(GDALGetColorInterpretationName(
1403
131k
                      static_cast<GDALColorInterp>(iType)),
1404
131k
                  pszName))
1405
17.1k
        {
1406
17.1k
            return static_cast<GDALColorInterp>(iType);
1407
17.1k
        }
1408
131k
    }
1409
1410
    // Accept British English spelling
1411
1.65k
    if (EQUAL(pszName, "grey"))
1412
69
        return GCI_GrayIndex;
1413
1414
1.58k
    return GCI_Undefined;
1415
1.65k
}
1416
1417
/************************************************************************/
1418
/*                  GDALGetColorInterpFromSTACCommonName()              */
1419
/************************************************************************/
1420
1421
static const struct
1422
{
1423
    const char *pszName;
1424
    GDALColorInterp eInterp;
1425
} asSTACCommonNames[] = {
1426
    {"pan", GCI_PanBand},
1427
    {"coastal", GCI_CoastalBand},
1428
    {"blue", GCI_BlueBand},
1429
    {"green", GCI_GreenBand},
1430
    {"green05", GCI_GreenBand},  // no exact match
1431
    {"yellow", GCI_YellowBand},
1432
    {"red", GCI_RedBand},
1433
    {"rededge", GCI_RedEdgeBand},
1434
    {"rededge071", GCI_RedEdgeBand},  // no exact match
1435
    {"rededge075", GCI_RedEdgeBand},  // no exact match
1436
    {"rededge078", GCI_RedEdgeBand},  // no exact match
1437
    {"nir", GCI_NIRBand},
1438
    {"nir08", GCI_NIRBand},   // no exact match
1439
    {"nir09", GCI_NIRBand},   // no exact match
1440
    {"cirrus", GCI_NIRBand},  // no exact match
1441
    {nullptr,
1442
     GCI_SWIRBand},  // so that GDALGetSTACCommonNameFromColorInterp returns null on GCI_SWIRBand
1443
    {"swir16", GCI_SWIRBand},  // no exact match
1444
    {"swir22", GCI_SWIRBand},  // no exact match
1445
    {"lwir", GCI_LWIRBand},
1446
    {"lwir11", GCI_LWIRBand},  // no exact match
1447
    {"lwir12", GCI_LWIRBand},  // no exact match
1448
};
1449
1450
/** Get color interpreetation from STAC eo:common_name
1451
 *
1452
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1453
 *
1454
 * @since GDAL 3.10
1455
 */
1456
GDALColorInterp GDALGetColorInterpFromSTACCommonName(const char *pszName)
1457
0
{
1458
1459
0
    for (const auto &sAssoc : asSTACCommonNames)
1460
0
    {
1461
0
        if (sAssoc.pszName && EQUAL(pszName, sAssoc.pszName))
1462
0
            return sAssoc.eInterp;
1463
0
    }
1464
0
    return GCI_Undefined;
1465
0
}
1466
1467
/************************************************************************/
1468
/*                  GDALGetSTACCommonNameFromColorInterp()              */
1469
/************************************************************************/
1470
1471
/** Get STAC eo:common_name from GDAL color interpretation
1472
 *
1473
 * Cf https://github.com/stac-extensions/eo?tab=readme-ov-file#common-band-names
1474
 *
1475
 * @return nullptr if there is no match
1476
 *
1477
 * @since GDAL 3.10
1478
 */
1479
const char *GDALGetSTACCommonNameFromColorInterp(GDALColorInterp eInterp)
1480
0
{
1481
0
    for (const auto &sAssoc : asSTACCommonNames)
1482
0
    {
1483
0
        if (eInterp == sAssoc.eInterp)
1484
0
            return sAssoc.pszName;
1485
0
    }
1486
0
    return nullptr;
1487
0
}
1488
1489
/************************************************************************/
1490
/*                     GDALGetRandomRasterSample()                      */
1491
/************************************************************************/
1492
1493
/** Undocumented
1494
 * @param hBand undocumented.
1495
 * @param nSamples undocumented.
1496
 * @param pafSampleBuf undocumented.
1497
 * @return undocumented
1498
 */
1499
int CPL_STDCALL GDALGetRandomRasterSample(GDALRasterBandH hBand, int nSamples,
1500
                                          float *pafSampleBuf)
1501
1502
0
{
1503
0
    VALIDATE_POINTER1(hBand, "GDALGetRandomRasterSample", 0);
1504
1505
0
    GDALRasterBand *poBand;
1506
1507
0
    poBand = GDALRasterBand::FromHandle(
1508
0
        GDALGetRasterSampleOverview(hBand, nSamples));
1509
0
    CPLAssert(nullptr != poBand);
1510
1511
    /* -------------------------------------------------------------------- */
1512
    /*      Figure out the ratio of blocks we will read to get an           */
1513
    /*      approximate value.                                              */
1514
    /* -------------------------------------------------------------------- */
1515
0
    int bGotNoDataValue = FALSE;
1516
1517
0
    double dfNoDataValue = poBand->GetNoDataValue(&bGotNoDataValue);
1518
1519
0
    int nBlockXSize = 0;
1520
0
    int nBlockYSize = 0;
1521
0
    poBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
1522
1523
0
    const int nBlocksPerRow = DIV_ROUND_UP(poBand->GetXSize(), nBlockXSize);
1524
0
    const int nBlocksPerColumn = DIV_ROUND_UP(poBand->GetYSize(), nBlockYSize);
1525
1526
0
    const GIntBig nBlockPixels =
1527
0
        static_cast<GIntBig>(nBlockXSize) * nBlockYSize;
1528
0
    const GIntBig nBlockCount =
1529
0
        static_cast<GIntBig>(nBlocksPerRow) * nBlocksPerColumn;
1530
1531
0
    if (nBlocksPerRow == 0 || nBlocksPerColumn == 0 || nBlockPixels == 0 ||
1532
0
        nBlockCount == 0)
1533
0
    {
1534
0
        CPLError(CE_Failure, CPLE_AppDefined,
1535
0
                 "GDALGetRandomRasterSample(): returning because band"
1536
0
                 " appears degenerate.");
1537
1538
0
        return FALSE;
1539
0
    }
1540
1541
0
    int nSampleRate = static_cast<int>(
1542
0
        std::max(1.0, sqrt(static_cast<double>(nBlockCount)) - 2.0));
1543
1544
0
    if (nSampleRate == nBlocksPerRow && nSampleRate > 1)
1545
0
        nSampleRate--;
1546
1547
0
    while (nSampleRate > 1 &&
1548
0
           ((nBlockCount - 1) / nSampleRate + 1) * nBlockPixels < nSamples)
1549
0
        nSampleRate--;
1550
1551
0
    int nBlockSampleRate = 1;
1552
1553
0
    if ((nSamples / ((nBlockCount - 1) / nSampleRate + 1)) != 0)
1554
0
        nBlockSampleRate = static_cast<int>(std::max<GIntBig>(
1555
0
            1,
1556
0
            nBlockPixels / (nSamples / ((nBlockCount - 1) / nSampleRate + 1))));
1557
1558
0
    int nActualSamples = 0;
1559
1560
0
    for (GIntBig iSampleBlock = 0; iSampleBlock < nBlockCount;
1561
0
         iSampleBlock += nSampleRate)
1562
0
    {
1563
1564
0
        const int iYBlock = static_cast<int>(iSampleBlock / nBlocksPerRow);
1565
0
        const int iXBlock = static_cast<int>(iSampleBlock % nBlocksPerRow);
1566
1567
0
        GDALRasterBlock *const poBlock =
1568
0
            poBand->GetLockedBlockRef(iXBlock, iYBlock);
1569
0
        if (poBlock == nullptr)
1570
0
            continue;
1571
0
        void *pDataRef = poBlock->GetDataRef();
1572
1573
0
        int iXValid = nBlockXSize;
1574
0
        if ((iXBlock + 1) * nBlockXSize > poBand->GetXSize())
1575
0
            iXValid = poBand->GetXSize() - iXBlock * nBlockXSize;
1576
1577
0
        int iYValid = nBlockYSize;
1578
0
        if ((iYBlock + 1) * nBlockYSize > poBand->GetYSize())
1579
0
            iYValid = poBand->GetYSize() - iYBlock * nBlockYSize;
1580
1581
0
        int iRemainder = 0;
1582
1583
0
        for (int iY = 0; iY < iYValid; iY++)
1584
0
        {
1585
0
            int iX = iRemainder;  // Used after for.
1586
0
            for (; iX < iXValid; iX += nBlockSampleRate)
1587
0
            {
1588
0
                double dfValue = 0.0;
1589
0
                const int iOffset = iX + iY * nBlockXSize;
1590
1591
0
                switch (poBlock->GetDataType())
1592
0
                {
1593
0
                    case GDT_UInt8:
1594
0
                        dfValue =
1595
0
                            reinterpret_cast<const GByte *>(pDataRef)[iOffset];
1596
0
                        break;
1597
0
                    case GDT_Int8:
1598
0
                        dfValue =
1599
0
                            reinterpret_cast<const GInt8 *>(pDataRef)[iOffset];
1600
0
                        break;
1601
0
                    case GDT_UInt16:
1602
0
                        dfValue = reinterpret_cast<const GUInt16 *>(
1603
0
                            pDataRef)[iOffset];
1604
0
                        break;
1605
0
                    case GDT_Int16:
1606
0
                        dfValue =
1607
0
                            reinterpret_cast<const GInt16 *>(pDataRef)[iOffset];
1608
0
                        break;
1609
0
                    case GDT_UInt32:
1610
0
                        dfValue = reinterpret_cast<const GUInt32 *>(
1611
0
                            pDataRef)[iOffset];
1612
0
                        break;
1613
0
                    case GDT_Int32:
1614
0
                        dfValue =
1615
0
                            reinterpret_cast<const GInt32 *>(pDataRef)[iOffset];
1616
0
                        break;
1617
0
                    case GDT_UInt64:
1618
0
                        dfValue = static_cast<double>(
1619
0
                            reinterpret_cast<const std::uint64_t *>(
1620
0
                                pDataRef)[iOffset]);
1621
0
                        break;
1622
0
                    case GDT_Int64:
1623
0
                        dfValue = static_cast<double>(
1624
0
                            reinterpret_cast<const std::int64_t *>(
1625
0
                                pDataRef)[iOffset]);
1626
0
                        break;
1627
0
                    case GDT_Float16:
1628
0
                        dfValue = reinterpret_cast<const GFloat16 *>(
1629
0
                            pDataRef)[iOffset];
1630
0
                        break;
1631
0
                    case GDT_Float32:
1632
0
                        dfValue =
1633
0
                            reinterpret_cast<const float *>(pDataRef)[iOffset];
1634
0
                        break;
1635
0
                    case GDT_Float64:
1636
0
                        dfValue =
1637
0
                            reinterpret_cast<const double *>(pDataRef)[iOffset];
1638
0
                        break;
1639
0
                    case GDT_CInt16:
1640
0
                    {
1641
                        // TODO(schwehr): Clean up casts.
1642
0
                        const double dfReal = reinterpret_cast<const GInt16 *>(
1643
0
                            pDataRef)[iOffset * 2];
1644
0
                        const double dfImag = reinterpret_cast<const GInt16 *>(
1645
0
                            pDataRef)[iOffset * 2 + 1];
1646
0
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1647
0
                        break;
1648
0
                    }
1649
0
                    case GDT_CInt32:
1650
0
                    {
1651
0
                        const double dfReal = reinterpret_cast<const GInt32 *>(
1652
0
                            pDataRef)[iOffset * 2];
1653
0
                        const double dfImag = reinterpret_cast<const GInt32 *>(
1654
0
                            pDataRef)[iOffset * 2 + 1];
1655
0
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1656
0
                        break;
1657
0
                    }
1658
0
                    case GDT_CFloat16:
1659
0
                    {
1660
0
                        const double dfReal =
1661
0
                            reinterpret_cast<const GFloat16 *>(
1662
0
                                pDataRef)[iOffset * 2];
1663
0
                        const double dfImag =
1664
0
                            reinterpret_cast<const GFloat16 *>(
1665
0
                                pDataRef)[iOffset * 2 + 1];
1666
0
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1667
0
                        break;
1668
0
                    }
1669
0
                    case GDT_CFloat32:
1670
0
                    {
1671
0
                        const double dfReal = reinterpret_cast<const float *>(
1672
0
                            pDataRef)[iOffset * 2];
1673
0
                        const double dfImag = reinterpret_cast<const float *>(
1674
0
                            pDataRef)[iOffset * 2 + 1];
1675
0
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1676
0
                        break;
1677
0
                    }
1678
0
                    case GDT_CFloat64:
1679
0
                    {
1680
0
                        const double dfReal = reinterpret_cast<const double *>(
1681
0
                            pDataRef)[iOffset * 2];
1682
0
                        const double dfImag = reinterpret_cast<const double *>(
1683
0
                            pDataRef)[iOffset * 2 + 1];
1684
0
                        dfValue = sqrt(dfReal * dfReal + dfImag * dfImag);
1685
0
                        break;
1686
0
                    }
1687
0
                    case GDT_Unknown:
1688
0
                    case GDT_TypeCount:
1689
0
                        CPLAssert(false);
1690
0
                }
1691
1692
0
                if (bGotNoDataValue && dfValue == dfNoDataValue)
1693
0
                    continue;
1694
1695
0
                if (nActualSamples < nSamples)
1696
0
                    pafSampleBuf[nActualSamples++] =
1697
0
                        static_cast<float>(dfValue);
1698
0
            }
1699
1700
0
            iRemainder = iX - iXValid;
1701
0
        }
1702
1703
0
        poBlock->DropLock();
1704
0
    }
1705
1706
0
    return nActualSamples;
1707
0
}
1708
1709
/************************************************************************/
1710
/*                             gdal::GCP                                */
1711
/************************************************************************/
1712
1713
namespace gdal
1714
{
1715
/** Constructor. */
1716
GCP::GCP(const char *pszId, const char *pszInfo, double dfPixel, double dfLine,
1717
         double dfX, double dfY, double dfZ)
1718
154k
    : gcp{CPLStrdup(pszId ? pszId : ""),
1719
154k
          CPLStrdup(pszInfo ? pszInfo : ""),
1720
154k
          dfPixel,
1721
154k
          dfLine,
1722
154k
          dfX,
1723
154k
          dfY,
1724
154k
          dfZ}
1725
154k
{
1726
154k
    static_assert(sizeof(GCP) == sizeof(GDAL_GCP));
1727
154k
}
1728
1729
/** Destructor. */
1730
GCP::~GCP()
1731
497k
{
1732
497k
    CPLFree(gcp.pszId);
1733
497k
    CPLFree(gcp.pszInfo);
1734
497k
}
1735
1736
/** Constructor from a C GDAL_GCP instance. */
1737
GCP::GCP(const GDAL_GCP &other)
1738
316k
    : gcp{CPLStrdup(other.pszId),
1739
316k
          CPLStrdup(other.pszInfo),
1740
316k
          other.dfGCPPixel,
1741
316k
          other.dfGCPLine,
1742
316k
          other.dfGCPX,
1743
316k
          other.dfGCPY,
1744
316k
          other.dfGCPZ}
1745
316k
{
1746
316k
}
1747
1748
/** Copy constructor. */
1749
197k
GCP::GCP(const GCP &other) : GCP(other.gcp)
1750
197k
{
1751
197k
}
1752
1753
/** Move constructor. */
1754
GCP::GCP(GCP &&other)
1755
25.5k
    : gcp{other.gcp.pszId,     other.gcp.pszInfo, other.gcp.dfGCPPixel,
1756
25.5k
          other.gcp.dfGCPLine, other.gcp.dfGCPX,  other.gcp.dfGCPY,
1757
25.5k
          other.gcp.dfGCPZ}
1758
25.5k
{
1759
25.5k
    other.gcp.pszId = nullptr;
1760
25.5k
    other.gcp.pszInfo = nullptr;
1761
25.5k
}
1762
1763
/** Copy assignment operator. */
1764
GCP &GCP::operator=(const GCP &other)
1765
0
{
1766
0
    if (this != &other)
1767
0
    {
1768
0
        CPLFree(gcp.pszId);
1769
0
        CPLFree(gcp.pszInfo);
1770
0
        gcp = other.gcp;
1771
0
        gcp.pszId = CPLStrdup(other.gcp.pszId);
1772
0
        gcp.pszInfo = CPLStrdup(other.gcp.pszInfo);
1773
0
    }
1774
0
    return *this;
1775
0
}
1776
1777
/** Move assignment operator. */
1778
GCP &GCP::operator=(GCP &&other)
1779
0
{
1780
0
    if (this != &other)
1781
0
    {
1782
0
        CPLFree(gcp.pszId);
1783
0
        CPLFree(gcp.pszInfo);
1784
0
        gcp = other.gcp;
1785
0
        other.gcp.pszId = nullptr;
1786
0
        other.gcp.pszInfo = nullptr;
1787
0
    }
1788
0
    return *this;
1789
0
}
1790
1791
/** Set the 'id' member of the GCP. */
1792
void GCP::SetId(const char *pszId)
1793
25.1k
{
1794
25.1k
    CPLFree(gcp.pszId);
1795
25.1k
    gcp.pszId = CPLStrdup(pszId ? pszId : "");
1796
25.1k
}
1797
1798
/** Set the 'info' member of the GCP. */
1799
void GCP::SetInfo(const char *pszInfo)
1800
25.1k
{
1801
25.1k
    CPLFree(gcp.pszInfo);
1802
25.1k
    gcp.pszInfo = CPLStrdup(pszInfo ? pszInfo : "");
1803
25.1k
}
1804
1805
/** Cast a vector of gdal::GCP as a C array of GDAL_GCP. */
1806
/*static */
1807
const GDAL_GCP *GCP::c_ptr(const std::vector<GCP> &asGCPs)
1808
75.9k
{
1809
75.9k
    return asGCPs.empty() ? nullptr : asGCPs.front().c_ptr();
1810
75.9k
}
1811
1812
/** Creates a vector of GDAL::GCP from a C array of GDAL_GCP. */
1813
/*static*/
1814
std::vector<GCP> GCP::fromC(const GDAL_GCP *pasGCPList, int nGCPCount)
1815
5.36k
{
1816
5.36k
    return std::vector<GCP>(pasGCPList, pasGCPList + nGCPCount);
1817
5.36k
}
1818
1819
} /* namespace gdal */
1820
1821
/************************************************************************/
1822
/*                            GDALInitGCPs()                            */
1823
/************************************************************************/
1824
1825
/** Initialize an array of GCPs.
1826
 *
1827
 * Numeric values are initialized to 0 and strings to the empty string ""
1828
 * allocated with CPLStrdup()
1829
 * An array initialized with GDALInitGCPs() must be de-initialized with
1830
 * GDALDeinitGCPs().
1831
 *
1832
 * @param nCount number of GCPs in psGCP
1833
 * @param psGCP array of GCPs of size nCount.
1834
 */
1835
void CPL_STDCALL GDALInitGCPs(int nCount, GDAL_GCP *psGCP)
1836
1837
4.22k
{
1838
4.22k
    if (nCount > 0)
1839
4.22k
    {
1840
4.22k
        VALIDATE_POINTER0(psGCP, "GDALInitGCPs");
1841
4.22k
    }
1842
1843
974k
    for (int iGCP = 0; iGCP < nCount; iGCP++)
1844
970k
    {
1845
970k
        memset(psGCP, 0, sizeof(GDAL_GCP));
1846
970k
        psGCP->pszId = CPLStrdup("");
1847
970k
        psGCP->pszInfo = CPLStrdup("");
1848
970k
        psGCP++;
1849
970k
    }
1850
4.22k
}
1851
1852
/************************************************************************/
1853
/*                           GDALDeinitGCPs()                           */
1854
/************************************************************************/
1855
1856
/** De-initialize an array of GCPs (initialized with GDALInitGCPs())
1857
 *
1858
 * @param nCount number of GCPs in psGCP
1859
 * @param psGCP array of GCPs of size nCount.
1860
 */
1861
void CPL_STDCALL GDALDeinitGCPs(int nCount, GDAL_GCP *psGCP)
1862
1863
3.01k
{
1864
3.01k
    if (nCount > 0)
1865
1.73k
    {
1866
1.73k
        VALIDATE_POINTER0(psGCP, "GDALDeinitGCPs");
1867
1.73k
    }
1868
1869
973k
    for (int iGCP = 0; iGCP < nCount; iGCP++)
1870
970k
    {
1871
970k
        CPLFree(psGCP->pszId);
1872
970k
        CPLFree(psGCP->pszInfo);
1873
970k
        psGCP++;
1874
970k
    }
1875
3.01k
}
1876
1877
/************************************************************************/
1878
/*                         GDALDuplicateGCPs()                          */
1879
/************************************************************************/
1880
1881
/** Duplicate an array of GCPs
1882
 *
1883
 * The return must be freed with GDALDeinitGCPs() followed by CPLFree()
1884
 *
1885
 * @param nCount number of GCPs in psGCP
1886
 * @param pasGCPList array of GCPs of size nCount.
1887
 */
1888
GDAL_GCP *CPL_STDCALL GDALDuplicateGCPs(int nCount, const GDAL_GCP *pasGCPList)
1889
1890
75
{
1891
75
    GDAL_GCP *pasReturn =
1892
75
        static_cast<GDAL_GCP *>(CPLMalloc(sizeof(GDAL_GCP) * nCount));
1893
75
    GDALInitGCPs(nCount, pasReturn);
1894
1895
21.8k
    for (int iGCP = 0; iGCP < nCount; iGCP++)
1896
21.7k
    {
1897
21.7k
        CPLFree(pasReturn[iGCP].pszId);
1898
21.7k
        pasReturn[iGCP].pszId = CPLStrdup(pasGCPList[iGCP].pszId);
1899
1900
21.7k
        CPLFree(pasReturn[iGCP].pszInfo);
1901
21.7k
        pasReturn[iGCP].pszInfo = CPLStrdup(pasGCPList[iGCP].pszInfo);
1902
1903
21.7k
        pasReturn[iGCP].dfGCPPixel = pasGCPList[iGCP].dfGCPPixel;
1904
21.7k
        pasReturn[iGCP].dfGCPLine = pasGCPList[iGCP].dfGCPLine;
1905
21.7k
        pasReturn[iGCP].dfGCPX = pasGCPList[iGCP].dfGCPX;
1906
21.7k
        pasReturn[iGCP].dfGCPY = pasGCPList[iGCP].dfGCPY;
1907
21.7k
        pasReturn[iGCP].dfGCPZ = pasGCPList[iGCP].dfGCPZ;
1908
21.7k
    }
1909
1910
75
    return pasReturn;
1911
75
}
1912
1913
/************************************************************************/
1914
/*                       GDALFindAssociatedFile()                       */
1915
/************************************************************************/
1916
1917
/**
1918
 * \brief Find file with alternate extension.
1919
 *
1920
 * Finds the file with the indicated extension, substituting it in place
1921
 * of the extension of the base filename.  Generally used to search for
1922
 * associated files like world files .RPB files, etc.  If necessary, the
1923
 * extension will be tried in both upper and lower case.  If a sibling file
1924
 * list is available it will be used instead of doing VSIStatExL() calls to
1925
 * probe the file system.
1926
 *
1927
 * Note that the result is a dynamic CPLString so this method should not
1928
 * be used in a situation where there could be cross heap issues.  It is
1929
 * generally imprudent for application built on GDAL to use this function
1930
 * unless they are sure they will always use the same runtime heap as GDAL.
1931
 *
1932
 * @param pszBaseFilename the filename relative to which to search.
1933
 * @param pszExt the target extension in either upper or lower case.
1934
 * @param papszSiblingFiles the list of files in the same directory as
1935
 * pszBaseFilename or NULL if they are not known.
1936
 * @param nFlags special options controlling search.  None defined yet, just
1937
 * pass 0.
1938
 *
1939
 * @return an empty string if the target is not found, otherwise the target
1940
 * file with similar path style as the pszBaseFilename.
1941
 */
1942
1943
/**/
1944
/**/
1945
1946
CPLString GDALFindAssociatedFile(const char *pszBaseFilename,
1947
                                 const char *pszExt,
1948
                                 CSLConstList papszSiblingFiles,
1949
                                 CPL_UNUSED int nFlags)
1950
1951
708k
{
1952
708k
    CPLString osTarget = CPLResetExtensionSafe(pszBaseFilename, pszExt);
1953
1954
708k
    if (papszSiblingFiles == nullptr ||
1955
        // cppcheck-suppress knownConditionTrueFalse
1956
707k
        !GDALCanReliablyUseSiblingFileList(osTarget.c_str()))
1957
644
    {
1958
644
        VSIStatBufL sStatBuf;
1959
1960
644
        if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
1961
644
        {
1962
644
            CPLString osAltExt = pszExt;
1963
1964
644
            if (islower(static_cast<unsigned char>(pszExt[0])))
1965
0
                osAltExt = osAltExt.toupper();
1966
644
            else
1967
644
                osAltExt = osAltExt.tolower();
1968
1969
644
            osTarget = CPLResetExtensionSafe(pszBaseFilename, osAltExt);
1970
1971
644
            if (VSIStatExL(osTarget, &sStatBuf, VSI_STAT_EXISTS_FLAG) != 0)
1972
644
                return "";
1973
644
        }
1974
644
    }
1975
707k
    else
1976
707k
    {
1977
707k
        const int iSibling =
1978
707k
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTarget));
1979
707k
        if (iSibling < 0)
1980
706k
            return "";
1981
1982
986
        osTarget.resize(osTarget.size() - strlen(papszSiblingFiles[iSibling]));
1983
986
        osTarget += papszSiblingFiles[iSibling];
1984
986
    }
1985
1986
986
    return osTarget;
1987
708k
}
1988
1989
/************************************************************************/
1990
/*                         GDALLoadOziMapFile()                         */
1991
/************************************************************************/
1992
1993
/** Helper function for translator implementer wanting support for OZI .map
1994
 *
1995
 * @param pszFilename filename of .tab file
1996
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
1997
 * @param ppszWKT output pointer to a string that will be allocated with
1998
 * CPLMalloc().
1999
 * @param pnGCPCount output pointer to GCP count.
2000
 * @param ppasGCPs outputer pointer to an array of GCPs.
2001
 * @return TRUE in case of success, FALSE otherwise.
2002
 */
2003
int CPL_STDCALL GDALLoadOziMapFile(const char *pszFilename,
2004
                                   double *padfGeoTransform, char **ppszWKT,
2005
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
2006
2007
277
{
2008
277
    VALIDATE_POINTER1(pszFilename, "GDALLoadOziMapFile", FALSE);
2009
277
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadOziMapFile", FALSE);
2010
277
    VALIDATE_POINTER1(pnGCPCount, "GDALLoadOziMapFile", FALSE);
2011
277
    VALIDATE_POINTER1(ppasGCPs, "GDALLoadOziMapFile", FALSE);
2012
2013
277
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
2014
2015
277
    if (!papszLines)
2016
5
        return FALSE;
2017
2018
272
    int nLines = CSLCount(papszLines);
2019
2020
    // Check the OziExplorer Map file signature
2021
272
    if (nLines < 5 ||
2022
106
        !STARTS_WITH_CI(papszLines[0], "OziExplorer Map Data File Version "))
2023
272
    {
2024
272
        CPLError(CE_Failure, CPLE_AppDefined,
2025
272
                 "GDALLoadOziMapFile(): file \"%s\" is not in OziExplorer Map "
2026
272
                 "format.",
2027
272
                 pszFilename);
2028
272
        CSLDestroy(papszLines);
2029
272
        return FALSE;
2030
272
    }
2031
2032
0
    OGRSpatialReference oSRS;
2033
0
    OGRErr eErr = OGRERR_NONE;
2034
2035
    /* The Map Scale Factor has been introduced recently on the 6th line */
2036
    /* and is a trick that is used to just change that line without changing */
2037
    /* the rest of the MAP file but providing an imagery that is smaller or
2038
     * larger */
2039
    /* so we have to correct the pixel/line values read in the .MAP file so they
2040
     */
2041
    /* match the actual imagery dimension. Well, this is a bad summary of what
2042
     */
2043
    /* is explained at
2044
     * http://tech.groups.yahoo.com/group/OziUsers-L/message/12484 */
2045
0
    double dfMSF = 1;
2046
2047
0
    for (int iLine = 5; iLine < nLines; iLine++)
2048
0
    {
2049
0
        if (STARTS_WITH_CI(papszLines[iLine], "MSF,"))
2050
0
        {
2051
0
            dfMSF = CPLAtof(papszLines[iLine] + 4);
2052
0
            if (dfMSF <= 0.01) /* Suspicious values */
2053
0
            {
2054
0
                CPLDebug("OZI", "Suspicious MSF value : %s", papszLines[iLine]);
2055
0
                dfMSF = 1;
2056
0
            }
2057
0
        }
2058
0
    }
2059
2060
0
    eErr = oSRS.importFromOzi(papszLines);
2061
0
    if (eErr == OGRERR_NONE)
2062
0
    {
2063
0
        if (ppszWKT != nullptr)
2064
0
            oSRS.exportToWkt(ppszWKT);
2065
0
    }
2066
2067
0
    int nCoordinateCount = 0;
2068
    // TODO(schwehr): Initialize asGCPs.
2069
0
    GDAL_GCP asGCPs[30];
2070
2071
    // Iterate all lines in the MAP-file
2072
0
    for (int iLine = 5; iLine < nLines; iLine++)
2073
0
    {
2074
0
        char **papszTok = CSLTokenizeString2(
2075
0
            papszLines[iLine], ",",
2076
0
            CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
2077
2078
0
        if (CSLCount(papszTok) < 12)
2079
0
        {
2080
0
            CSLDestroy(papszTok);
2081
0
            continue;
2082
0
        }
2083
2084
0
        if (CSLCount(papszTok) >= 17 && STARTS_WITH_CI(papszTok[0], "Point") &&
2085
0
            !EQUAL(papszTok[2], "") && !EQUAL(papszTok[3], "") &&
2086
0
            nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2087
0
        {
2088
0
            bool bReadOk = false;
2089
0
            double dfLon = 0.0;
2090
0
            double dfLat = 0.0;
2091
2092
0
            if (!EQUAL(papszTok[6], "") && !EQUAL(papszTok[7], "") &&
2093
0
                !EQUAL(papszTok[9], "") && !EQUAL(papszTok[10], ""))
2094
0
            {
2095
                // Set geographical coordinates of the pixels
2096
0
                dfLon = CPLAtofM(papszTok[9]) + CPLAtofM(papszTok[10]) / 60.0;
2097
0
                dfLat = CPLAtofM(papszTok[6]) + CPLAtofM(papszTok[7]) / 60.0;
2098
0
                if (EQUAL(papszTok[11], "W"))
2099
0
                    dfLon = -dfLon;
2100
0
                if (EQUAL(papszTok[8], "S"))
2101
0
                    dfLat = -dfLat;
2102
2103
                // Transform from the geographical coordinates into projected
2104
                // coordinates.
2105
0
                if (eErr == OGRERR_NONE)
2106
0
                {
2107
0
                    OGRSpatialReference *poLongLat = oSRS.CloneGeogCS();
2108
2109
0
                    if (poLongLat)
2110
0
                    {
2111
0
                        oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2112
0
                        poLongLat->SetAxisMappingStrategy(
2113
0
                            OAMS_TRADITIONAL_GIS_ORDER);
2114
2115
0
                        OGRCoordinateTransformation *poTransform =
2116
0
                            OGRCreateCoordinateTransformation(poLongLat, &oSRS);
2117
0
                        if (poTransform)
2118
0
                        {
2119
0
                            bReadOk = CPL_TO_BOOL(
2120
0
                                poTransform->Transform(1, &dfLon, &dfLat));
2121
0
                            delete poTransform;
2122
0
                        }
2123
0
                        delete poLongLat;
2124
0
                    }
2125
0
                }
2126
0
            }
2127
0
            else if (!EQUAL(papszTok[14], "") && !EQUAL(papszTok[15], ""))
2128
0
            {
2129
                // Set cartesian coordinates of the pixels.
2130
0
                dfLon = CPLAtofM(papszTok[14]);
2131
0
                dfLat = CPLAtofM(papszTok[15]);
2132
0
                bReadOk = true;
2133
2134
                // if ( EQUAL(papszTok[16], "S") )
2135
                //     dfLat = -dfLat;
2136
0
            }
2137
2138
0
            if (bReadOk)
2139
0
            {
2140
0
                GDALInitGCPs(1, asGCPs + nCoordinateCount);
2141
2142
                // Set pixel/line part
2143
0
                asGCPs[nCoordinateCount].dfGCPPixel =
2144
0
                    CPLAtofM(papszTok[2]) / dfMSF;
2145
0
                asGCPs[nCoordinateCount].dfGCPLine =
2146
0
                    CPLAtofM(papszTok[3]) / dfMSF;
2147
2148
0
                asGCPs[nCoordinateCount].dfGCPX = dfLon;
2149
0
                asGCPs[nCoordinateCount].dfGCPY = dfLat;
2150
2151
0
                nCoordinateCount++;
2152
0
            }
2153
0
        }
2154
2155
0
        CSLDestroy(papszTok);
2156
0
    }
2157
2158
0
    CSLDestroy(papszLines);
2159
2160
0
    if (nCoordinateCount == 0)
2161
0
    {
2162
0
        CPLDebug("GDAL", "GDALLoadOziMapFile(\"%s\") did read no GCPs.",
2163
0
                 pszFilename);
2164
0
        return FALSE;
2165
0
    }
2166
2167
    /* -------------------------------------------------------------------- */
2168
    /*      Try to convert the GCPs into a geotransform definition, if      */
2169
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2170
    /* -------------------------------------------------------------------- */
2171
0
    if (!GDALGCPsToGeoTransform(
2172
0
            nCoordinateCount, asGCPs, padfGeoTransform,
2173
0
            CPLTestBool(CPLGetConfigOption("OZI_APPROX_GEOTRANSFORM", "NO"))))
2174
0
    {
2175
0
        if (pnGCPCount && ppasGCPs)
2176
0
        {
2177
0
            CPLDebug(
2178
0
                "GDAL",
2179
0
                "GDALLoadOziMapFile(%s) found file, was not able to derive a\n"
2180
0
                "first order geotransform.  Using points as GCPs.",
2181
0
                pszFilename);
2182
2183
0
            *ppasGCPs = static_cast<GDAL_GCP *>(
2184
0
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
2185
0
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
2186
0
            *pnGCPCount = nCoordinateCount;
2187
0
        }
2188
0
    }
2189
0
    else
2190
0
    {
2191
0
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
2192
0
    }
2193
2194
0
    return TRUE;
2195
0
}
2196
2197
/************************************************************************/
2198
/*                       GDALReadOziMapFile()                           */
2199
/************************************************************************/
2200
2201
/** Helper function for translator implementer wanting support for OZI .map
2202
 *
2203
 * @param pszBaseFilename filename whose basename will help building the .map
2204
 * filename.
2205
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2206
 * @param ppszWKT output pointer to a string that will be allocated with
2207
 * CPLMalloc().
2208
 * @param pnGCPCount output pointer to GCP count.
2209
 * @param ppasGCPs outputer pointer to an array of GCPs.
2210
 * @return TRUE in case of success, FALSE otherwise.
2211
 */
2212
int CPL_STDCALL GDALReadOziMapFile(const char *pszBaseFilename,
2213
                                   double *padfGeoTransform, char **ppszWKT,
2214
                                   int *pnGCPCount, GDAL_GCP **ppasGCPs)
2215
2216
0
{
2217
    /* -------------------------------------------------------------------- */
2218
    /*      Try lower case, then upper case.                                */
2219
    /* -------------------------------------------------------------------- */
2220
0
    std::string osOzi = CPLResetExtensionSafe(pszBaseFilename, "map");
2221
2222
0
    VSILFILE *fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
2223
2224
0
    if (fpOzi == nullptr && VSIIsCaseSensitiveFS(osOzi.c_str()))
2225
0
    {
2226
0
        osOzi = CPLResetExtensionSafe(pszBaseFilename, "MAP");
2227
0
        fpOzi = VSIFOpenL(osOzi.c_str(), "rt");
2228
0
    }
2229
2230
0
    if (fpOzi == nullptr)
2231
0
        return FALSE;
2232
2233
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpOzi));
2234
2235
    /* -------------------------------------------------------------------- */
2236
    /*      We found the file, now load and parse it.                       */
2237
    /* -------------------------------------------------------------------- */
2238
0
    return GDALLoadOziMapFile(osOzi.c_str(), padfGeoTransform, ppszWKT,
2239
0
                              pnGCPCount, ppasGCPs);
2240
0
}
2241
2242
/************************************************************************/
2243
/*                         GDALLoadTabFile()                            */
2244
/*                                                                      */
2245
/************************************************************************/
2246
2247
/** Helper function for translator implementer wanting support for MapInfo
2248
 * .tab files.
2249
 *
2250
 * @param pszFilename filename of .tab
2251
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2252
 * @param ppszWKT output pointer to a string that will be allocated with
2253
 * CPLMalloc().
2254
 * @param pnGCPCount output pointer to GCP count.
2255
 * @param ppasGCPs outputer pointer to an array of GCPs.
2256
 * @return TRUE in case of success, FALSE otherwise.
2257
 */
2258
int CPL_STDCALL GDALLoadTabFile(const char *pszFilename,
2259
                                double *padfGeoTransform, char **ppszWKT,
2260
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2261
2262
151
{
2263
151
    char **papszLines = CSLLoad2(pszFilename, 1000, 200, nullptr);
2264
2265
151
    if (!papszLines)
2266
2
        return FALSE;
2267
2268
149
    char **papszTok = nullptr;
2269
149
    bool bTypeRasterFound = false;
2270
149
    bool bInsideTableDef = false;
2271
149
    int nCoordinateCount = 0;
2272
149
    GDAL_GCP asGCPs[256];  // TODO(schwehr): Initialize.
2273
149
    const int numLines = CSLCount(papszLines);
2274
2275
    // Iterate all lines in the TAB-file
2276
27.8k
    for (int iLine = 0; iLine < numLines; iLine++)
2277
27.7k
    {
2278
27.7k
        CSLDestroy(papszTok);
2279
27.7k
        papszTok =
2280
27.7k
            CSLTokenizeStringComplex(papszLines[iLine], " \t(),;", TRUE, FALSE);
2281
2282
27.7k
        if (CSLCount(papszTok) < 2)
2283
22.6k
            continue;
2284
2285
        // Did we find table definition
2286
5.04k
        if (EQUAL(papszTok[0], "Definition") && EQUAL(papszTok[1], "Table"))
2287
0
        {
2288
0
            bInsideTableDef = TRUE;
2289
0
        }
2290
5.04k
        else if (bInsideTableDef && (EQUAL(papszTok[0], "Type")))
2291
0
        {
2292
            // Only RASTER-type will be handled
2293
0
            if (EQUAL(papszTok[1], "RASTER"))
2294
0
            {
2295
0
                bTypeRasterFound = true;
2296
0
            }
2297
0
            else
2298
0
            {
2299
0
                CSLDestroy(papszTok);
2300
0
                CSLDestroy(papszLines);
2301
0
                return FALSE;
2302
0
            }
2303
0
        }
2304
5.04k
        else if (bTypeRasterFound && bInsideTableDef &&
2305
0
                 CSLCount(papszTok) > 4 && EQUAL(papszTok[4], "Label") &&
2306
0
                 nCoordinateCount < static_cast<int>(CPL_ARRAYSIZE(asGCPs)))
2307
0
        {
2308
0
            GDALInitGCPs(1, asGCPs + nCoordinateCount);
2309
2310
0
            asGCPs[nCoordinateCount].dfGCPPixel = CPLAtofM(papszTok[2]);
2311
0
            asGCPs[nCoordinateCount].dfGCPLine = CPLAtofM(papszTok[3]);
2312
0
            asGCPs[nCoordinateCount].dfGCPX = CPLAtofM(papszTok[0]);
2313
0
            asGCPs[nCoordinateCount].dfGCPY = CPLAtofM(papszTok[1]);
2314
0
            if (papszTok[5] != nullptr)
2315
0
            {
2316
0
                CPLFree(asGCPs[nCoordinateCount].pszId);
2317
0
                asGCPs[nCoordinateCount].pszId = CPLStrdup(papszTok[5]);
2318
0
            }
2319
2320
0
            nCoordinateCount++;
2321
0
        }
2322
5.04k
        else if (bTypeRasterFound && bInsideTableDef &&
2323
0
                 EQUAL(papszTok[0], "CoordSys") && ppszWKT != nullptr)
2324
0
        {
2325
0
            OGRSpatialReference oSRS;
2326
2327
0
            if (oSRS.importFromMICoordSys(papszLines[iLine]) == OGRERR_NONE)
2328
0
                oSRS.exportToWkt(ppszWKT);
2329
0
        }
2330
5.04k
        else if (EQUAL(papszTok[0], "Units") && CSLCount(papszTok) > 1 &&
2331
234
                 EQUAL(papszTok[1], "degree"))
2332
0
        {
2333
            /*
2334
            ** If we have units of "degree", but a projected coordinate
2335
            ** system we need to convert it to geographic.  See to01_02.TAB.
2336
            */
2337
0
            if (ppszWKT != nullptr && *ppszWKT != nullptr &&
2338
0
                STARTS_WITH_CI(*ppszWKT, "PROJCS"))
2339
0
            {
2340
0
                OGRSpatialReference oSRS;
2341
0
                oSRS.importFromWkt(*ppszWKT);
2342
2343
0
                OGRSpatialReference oSRSGeogCS;
2344
0
                oSRSGeogCS.CopyGeogCSFrom(&oSRS);
2345
0
                CPLFree(*ppszWKT);
2346
2347
0
                oSRSGeogCS.exportToWkt(ppszWKT);
2348
0
            }
2349
0
        }
2350
5.04k
    }
2351
2352
149
    CSLDestroy(papszTok);
2353
149
    CSLDestroy(papszLines);
2354
2355
149
    if (nCoordinateCount == 0)
2356
149
    {
2357
149
        CPLDebug("GDAL", "GDALLoadTabFile(%s) did not get any GCPs.",
2358
149
                 pszFilename);
2359
149
        return FALSE;
2360
149
    }
2361
2362
    /* -------------------------------------------------------------------- */
2363
    /*      Try to convert the GCPs into a geotransform definition, if      */
2364
    /*      possible.  Otherwise we will need to use them as GCPs.          */
2365
    /* -------------------------------------------------------------------- */
2366
0
    if (!GDALGCPsToGeoTransform(
2367
0
            nCoordinateCount, asGCPs, padfGeoTransform,
2368
0
            CPLTestBool(CPLGetConfigOption("TAB_APPROX_GEOTRANSFORM", "NO"))))
2369
0
    {
2370
0
        if (pnGCPCount && ppasGCPs)
2371
0
        {
2372
0
            CPLDebug("GDAL",
2373
0
                     "GDALLoadTabFile(%s) found file, was not able to derive a "
2374
0
                     "first order geotransform.  Using points as GCPs.",
2375
0
                     pszFilename);
2376
2377
0
            *ppasGCPs = static_cast<GDAL_GCP *>(
2378
0
                CPLCalloc(sizeof(GDAL_GCP), nCoordinateCount));
2379
0
            memcpy(*ppasGCPs, asGCPs, sizeof(GDAL_GCP) * nCoordinateCount);
2380
0
            *pnGCPCount = nCoordinateCount;
2381
0
        }
2382
0
    }
2383
0
    else
2384
0
    {
2385
0
        GDALDeinitGCPs(nCoordinateCount, asGCPs);
2386
0
    }
2387
2388
0
    return TRUE;
2389
149
}
2390
2391
/************************************************************************/
2392
/*                         GDALReadTabFile()                            */
2393
/************************************************************************/
2394
2395
/** Helper function for translator implementer wanting support for MapInfo
2396
 * .tab files.
2397
 *
2398
 * @param pszBaseFilename filename whose basename will help building the .tab
2399
 * filename.
2400
 * @param padfGeoTransform output geotransform. Must hold 6 doubles.
2401
 * @param ppszWKT output pointer to a string that will be allocated with
2402
 * CPLMalloc().
2403
 * @param pnGCPCount output pointer to GCP count.
2404
 * @param ppasGCPs outputer pointer to an array of GCPs.
2405
 * @return TRUE in case of success, FALSE otherwise.
2406
 */
2407
int CPL_STDCALL GDALReadTabFile(const char *pszBaseFilename,
2408
                                double *padfGeoTransform, char **ppszWKT,
2409
                                int *pnGCPCount, GDAL_GCP **ppasGCPs)
2410
2411
0
{
2412
0
    return GDALReadTabFile2(pszBaseFilename, padfGeoTransform, ppszWKT,
2413
0
                            pnGCPCount, ppasGCPs, nullptr, nullptr);
2414
0
}
2415
2416
int GDALReadTabFile2(const char *pszBaseFilename, double *padfGeoTransform,
2417
                     char **ppszWKT, int *pnGCPCount, GDAL_GCP **ppasGCPs,
2418
                     CSLConstList papszSiblingFiles, char **ppszTabFileNameOut)
2419
100k
{
2420
100k
    if (ppszTabFileNameOut)
2421
100k
        *ppszTabFileNameOut = nullptr;
2422
2423
100k
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
2424
0
        return FALSE;
2425
2426
100k
    std::string osTAB = CPLResetExtensionSafe(pszBaseFilename, "tab");
2427
2428
100k
    if (papszSiblingFiles &&
2429
        // cppcheck-suppress knownConditionTrueFalse
2430
100k
        GDALCanReliablyUseSiblingFileList(osTAB.c_str()))
2431
100k
    {
2432
100k
        int iSibling =
2433
100k
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTAB.c_str()));
2434
100k
        if (iSibling >= 0)
2435
151
        {
2436
151
            CPLString osTabFilename = pszBaseFilename;
2437
151
            osTabFilename.resize(strlen(pszBaseFilename) -
2438
151
                                 strlen(CPLGetFilename(pszBaseFilename)));
2439
151
            osTabFilename += papszSiblingFiles[iSibling];
2440
151
            if (GDALLoadTabFile(osTabFilename, padfGeoTransform, ppszWKT,
2441
151
                                pnGCPCount, ppasGCPs))
2442
0
            {
2443
0
                if (ppszTabFileNameOut)
2444
0
                    *ppszTabFileNameOut = CPLStrdup(osTabFilename);
2445
0
                return TRUE;
2446
0
            }
2447
151
        }
2448
100k
        return FALSE;
2449
100k
    }
2450
2451
    /* -------------------------------------------------------------------- */
2452
    /*      Try lower case, then upper case.                                */
2453
    /* -------------------------------------------------------------------- */
2454
2455
93
    VSILFILE *fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
2456
2457
93
    if (fpTAB == nullptr && VSIIsCaseSensitiveFS(osTAB.c_str()))
2458
93
    {
2459
93
        osTAB = CPLResetExtensionSafe(pszBaseFilename, "TAB");
2460
93
        fpTAB = VSIFOpenL(osTAB.c_str(), "rt");
2461
93
    }
2462
2463
93
    if (fpTAB == nullptr)
2464
93
        return FALSE;
2465
2466
0
    CPL_IGNORE_RET_VAL(VSIFCloseL(fpTAB));
2467
2468
    /* -------------------------------------------------------------------- */
2469
    /*      We found the file, now load and parse it.                       */
2470
    /* -------------------------------------------------------------------- */
2471
0
    if (GDALLoadTabFile(osTAB.c_str(), padfGeoTransform, ppszWKT, pnGCPCount,
2472
0
                        ppasGCPs))
2473
0
    {
2474
0
        if (ppszTabFileNameOut)
2475
0
            *ppszTabFileNameOut = CPLStrdup(osTAB.c_str());
2476
0
        return TRUE;
2477
0
    }
2478
0
    return FALSE;
2479
0
}
2480
2481
/************************************************************************/
2482
/*                         GDALLoadWorldFile()                          */
2483
/************************************************************************/
2484
2485
/**
2486
 * \brief Read ESRI world file.
2487
 *
2488
 * This function reads an ESRI style world file, and formats a geotransform
2489
 * from its contents.
2490
 *
2491
 * The world file contains an affine transformation with the parameters
2492
 * in a different order than in a geotransform array.
2493
 *
2494
 * <ul>
2495
 * <li> geotransform[1] : width of pixel
2496
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2497
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2498
 * <li> geotransform[5] : height of pixel (but negative)
2499
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2500
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2501
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2502
 * pixel.
2503
 * </ul>
2504
 *
2505
 * @param pszFilename the world file name.
2506
 * @param padfGeoTransform the six double array into which the
2507
 * geotransformation should be placed.
2508
 *
2509
 * @return TRUE on success or FALSE on failure.
2510
 */
2511
2512
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
2513
                                  double *padfGeoTransform)
2514
2515
782
{
2516
782
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
2517
782
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
2518
2519
782
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
2520
2521
782
    if (!papszLines)
2522
124
        return FALSE;
2523
2524
658
    double world[6] = {0.0};
2525
    // reads the first 6 non-empty lines
2526
658
    int nLines = 0;
2527
658
    const int nLinesCount = CSLCount(papszLines);
2528
658
    for (int i = 0;
2529
6.26k
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
2530
5.61k
         ++i)
2531
5.61k
    {
2532
5.61k
        CPLString line(papszLines[i]);
2533
5.61k
        if (line.Trim().empty())
2534
2.37k
            continue;
2535
2536
3.23k
        world[nLines] = CPLAtofM(line);
2537
3.23k
        ++nLines;
2538
3.23k
    }
2539
2540
658
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
2541
169
        (world[3] != 0.0 || world[1] != 0.0))
2542
71
    {
2543
71
        padfGeoTransform[0] = world[4];
2544
71
        padfGeoTransform[1] = world[0];
2545
71
        padfGeoTransform[2] = world[2];
2546
71
        padfGeoTransform[3] = world[5];
2547
71
        padfGeoTransform[4] = world[1];
2548
71
        padfGeoTransform[5] = world[3];
2549
2550
        // correct for center of pixel vs. top left of pixel
2551
71
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
2552
71
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
2553
71
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
2554
71
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
2555
2556
71
        CSLDestroy(papszLines);
2557
2558
71
        return TRUE;
2559
71
    }
2560
587
    else
2561
587
    {
2562
587
        CPLDebug("GDAL",
2563
587
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2564
587
                 pszFilename);
2565
587
        CSLDestroy(papszLines);
2566
587
        return FALSE;
2567
587
    }
2568
658
}
2569
2570
/************************************************************************/
2571
/*                         GDALReadWorldFile()                          */
2572
/************************************************************************/
2573
2574
/**
2575
 * \brief Read ESRI world file.
2576
 *
2577
 * This function reads an ESRI style world file, and formats a geotransform
2578
 * from its contents.  It does the same as GDALLoadWorldFile() function, but
2579
 * it will form the filename for the worldfile from the filename of the raster
2580
 * file referred and the suggested extension.  If no extension is provided,
2581
 * the code will internally try the unix style and windows style world file
2582
 * extensions (eg. for .tif these would be .tfw and .tifw).
2583
 *
2584
 * The world file contains an affine transformation with the parameters
2585
 * in a different order than in a geotransform array.
2586
 *
2587
 * <ul>
2588
 * <li> geotransform[1] : width of pixel
2589
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2590
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2591
 * <li> geotransform[5] : height of pixel (but negative)
2592
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2593
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2594
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2595
 * pixel.
2596
 * </ul>
2597
 *
2598
 * @param pszBaseFilename the target raster file.
2599
 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2600
 * from the pszBaseFilename
2601
 * @param padfGeoTransform the six double array into which the
2602
 * geotransformation should be placed.
2603
 *
2604
 * @return TRUE on success or FALSE on failure.
2605
 */
2606
2607
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
2608
                                  const char *pszExtension,
2609
                                  double *padfGeoTransform)
2610
2611
101k
{
2612
101k
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
2613
101k
                              nullptr, nullptr);
2614
101k
}
2615
2616
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
2617
                       GDALGeoTransform &gt, CSLConstList papszSiblingFiles,
2618
                       char **ppszWorldFileNameOut)
2619
201k
{
2620
201k
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, gt.data(),
2621
201k
                              papszSiblingFiles, ppszWorldFileNameOut);
2622
201k
}
2623
2624
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
2625
                       double *padfGeoTransform, CSLConstList papszSiblingFiles,
2626
                       char **ppszWorldFileNameOut)
2627
532k
{
2628
532k
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
2629
532k
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
2630
2631
532k
    if (ppszWorldFileNameOut)
2632
329k
        *ppszWorldFileNameOut = nullptr;
2633
2634
532k
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
2635
0
        return FALSE;
2636
2637
    /* -------------------------------------------------------------------- */
2638
    /*      If we aren't given an extension, try both the unix and          */
2639
    /*      windows style extensions.                                       */
2640
    /* -------------------------------------------------------------------- */
2641
532k
    if (pszExtension == nullptr)
2642
151k
    {
2643
151k
        const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename);
2644
2645
151k
        if (oBaseExt.length() < 2)
2646
36.8k
            return FALSE;
2647
2648
        // windows version - first + last + 'w'
2649
114k
        char szDerivedExtension[100] = {'\0'};
2650
114k
        szDerivedExtension[0] = oBaseExt[0];
2651
114k
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
2652
114k
        szDerivedExtension[2] = 'w';
2653
114k
        szDerivedExtension[3] = '\0';
2654
2655
114k
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2656
114k
                               padfGeoTransform, papszSiblingFiles,
2657
114k
                               ppszWorldFileNameOut))
2658
25
            return TRUE;
2659
2660
        // unix version - extension + 'w'
2661
114k
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
2662
0
            return FALSE;
2663
2664
114k
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
2665
114k
                 oBaseExt.c_str());
2666
114k
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2667
114k
                                  padfGeoTransform, papszSiblingFiles,
2668
114k
                                  ppszWorldFileNameOut);
2669
114k
    }
2670
2671
    /* -------------------------------------------------------------------- */
2672
    /*      Skip the leading period in the extension if there is one.       */
2673
    /* -------------------------------------------------------------------- */
2674
381k
    if (*pszExtension == '.')
2675
621
        pszExtension++;
2676
2677
    /* -------------------------------------------------------------------- */
2678
    /*      Generate upper and lower case versions of the extension.        */
2679
    /* -------------------------------------------------------------------- */
2680
381k
    char szExtUpper[32] = {'\0'};
2681
381k
    char szExtLower[32] = {'\0'};
2682
381k
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
2683
381k
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
2684
2685
1.63M
    for (int i = 0; szExtUpper[i] != '\0'; i++)
2686
1.25M
    {
2687
1.25M
        szExtUpper[i] = static_cast<char>(
2688
1.25M
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
2689
1.25M
        szExtLower[i] = static_cast<char>(
2690
1.25M
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
2691
1.25M
    }
2692
2693
381k
    std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
2694
2695
381k
    if (papszSiblingFiles &&
2696
        // cppcheck-suppress knownConditionTrueFalse
2697
228k
        GDALCanReliablyUseSiblingFileList(osTFW.c_str()))
2698
228k
    {
2699
228k
        const int iSibling =
2700
228k
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str()));
2701
228k
        if (iSibling >= 0)
2702
141
        {
2703
141
            CPLString osTFWFilename = pszBaseFilename;
2704
141
            osTFWFilename.resize(strlen(pszBaseFilename) -
2705
141
                                 strlen(CPLGetFilename(pszBaseFilename)));
2706
141
            osTFWFilename += papszSiblingFiles[iSibling];
2707
141
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
2708
4
            {
2709
4
                if (ppszWorldFileNameOut)
2710
4
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
2711
4
                return TRUE;
2712
4
            }
2713
141
        }
2714
228k
        return FALSE;
2715
228k
    }
2716
2717
    /* -------------------------------------------------------------------- */
2718
    /*      Try lower case, then upper case.                                */
2719
    /* -------------------------------------------------------------------- */
2720
2721
152k
    VSIStatBufL sStatBuf;
2722
152k
    bool bGotTFW =
2723
152k
        VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2724
2725
152k
    if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str()))
2726
151k
    {
2727
151k
        osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper);
2728
151k
        bGotTFW =
2729
151k
            VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2730
151k
    }
2731
2732
152k
    if (!bGotTFW)
2733
151k
        return FALSE;
2734
2735
    /* -------------------------------------------------------------------- */
2736
    /*      We found the file, now load and parse it.                       */
2737
    /* -------------------------------------------------------------------- */
2738
641
    if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform))
2739
67
    {
2740
67
        if (ppszWorldFileNameOut)
2741
0
            *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str());
2742
67
        return TRUE;
2743
67
    }
2744
574
    return FALSE;
2745
641
}
2746
2747
/************************************************************************/
2748
/*                         GDALWriteWorldFile()                         */
2749
/*                                                                      */
2750
/*      Helper function for translator implementer wanting              */
2751
/*      support for ESRI world files.                                   */
2752
/************************************************************************/
2753
2754
/**
2755
 * \brief Write ESRI world file.
2756
 *
2757
 * This function writes an ESRI style world file from the passed geotransform.
2758
 *
2759
 * The world file contains an affine transformation with the parameters
2760
 * in a different order than in a geotransform array.
2761
 *
2762
 * <ul>
2763
 * <li> geotransform[1] : width of pixel
2764
 * <li> geotransform[4] : rotational coefficient, zero for north up images.
2765
 * <li> geotransform[2] : rotational coefficient, zero for north up images.
2766
 * <li> geotransform[5] : height of pixel (but negative)
2767
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2768
 * offset to center of top left pixel. <li> geotransform[3] + 0.5 *
2769
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2770
 * pixel.
2771
 * </ul>
2772
 *
2773
 * @param pszBaseFilename the target raster file.
2774
 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2775
 * @param padfGeoTransform the six double array from which the
2776
 * geotransformation should be read.
2777
 *
2778
 * @return TRUE on success or FALSE on failure.
2779
 */
2780
2781
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
2782
                                   const char *pszExtension,
2783
                                   double *padfGeoTransform)
2784
2785
0
{
2786
0
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
2787
0
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
2788
0
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
2789
2790
    /* -------------------------------------------------------------------- */
2791
    /*      Prepare the text to write to the file.                          */
2792
    /* -------------------------------------------------------------------- */
2793
0
    CPLString osTFWText;
2794
2795
0
    osTFWText.Printf("%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n%.10f\n",
2796
0
                     padfGeoTransform[1], padfGeoTransform[4],
2797
0
                     padfGeoTransform[2], padfGeoTransform[5],
2798
0
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
2799
0
                         0.5 * padfGeoTransform[2],
2800
0
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
2801
0
                         0.5 * padfGeoTransform[5]);
2802
2803
    /* -------------------------------------------------------------------- */
2804
    /*      Update extension, and write to disk.                            */
2805
    /* -------------------------------------------------------------------- */
2806
0
    const std::string osTFW =
2807
0
        CPLResetExtensionSafe(pszBaseFilename, pszExtension);
2808
0
    VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt");
2809
0
    if (fpTFW == nullptr)
2810
0
        return FALSE;
2811
2812
0
    const int bRet =
2813
0
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
2814
0
    if (VSIFCloseL(fpTFW) != 0)
2815
0
        return FALSE;
2816
2817
0
    return bRet;
2818
0
}
2819
2820
/************************************************************************/
2821
/*                          GDALVersionInfo()                           */
2822
/************************************************************************/
2823
2824
/**
2825
 * \brief Get runtime version information.
2826
 *
2827
 * Available pszRequest values:
2828
 * <ul>
2829
 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
2830
 * "30603000", e.g for GDAL 3.6.3.0</li>
2831
 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2832
 * string. i.e. "20230312".</li>
2833
 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2834
 * <li> "RELEASE_NICKNAME": (>= 3.11) Returns the GDAL_RELEASE_NICKNAME.
2835
 * (may be empty)</li>
2836
 * <li> "\--version": Returns one line version message suitable for
2837
 * use in response to \--version requests.  i.e. "GDAL 3.6.3, released
2838
 * 2023/03/12"</li>
2839
 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2840
 * the GDAL_DATA directory.
2841
 * </li>
2842
 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2843
 * with information on build time options.</li>
2844
 * </ul>
2845
 *
2846
 * @param pszRequest the type of version info desired, as listed above.
2847
 *
2848
 * @return an internal string containing the requested information.
2849
 */
2850
2851
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
2852
2853
138
{
2854
    /* -------------------------------------------------------------------- */
2855
    /*      Try to capture as much build information as practical.          */
2856
    /* -------------------------------------------------------------------- */
2857
138
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
2858
0
    {
2859
0
        CPLString osBuildInfo;
2860
2861
0
#define STRINGIFY_HELPER(x) #x
2862
0
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2863
2864
#ifdef ESRI_BUILD
2865
        osBuildInfo += "ESRI_BUILD=YES\n";
2866
#endif
2867
#ifdef PAM_ENABLED
2868
        osBuildInfo += "PAM_ENABLED=YES\n";
2869
#endif
2870
0
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
2871
0
#ifdef HAVE_CURL
2872
0
        osBuildInfo += "CURL_ENABLED=YES\n";
2873
0
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
2874
0
#endif
2875
#ifdef HAVE_GEOS
2876
        osBuildInfo += "GEOS_ENABLED=YES\n";
2877
#ifdef GEOS_CAPI_VERSION
2878
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
2879
#endif
2880
#endif
2881
0
        osBuildInfo +=
2882
0
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2883
0
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
2884
0
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
2885
0
        osBuildInfo += proj_info().version;
2886
0
        osBuildInfo += '\n';
2887
2888
0
#ifdef __VERSION__
2889
0
#ifdef __clang_version__
2890
0
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2891
#elif defined(__GNUC__)
2892
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
2893
#elif defined(__INTEL_COMPILER)
2894
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2895
#else
2896
        // STRINGIFY() as we're not sure if its a int or a string
2897
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2898
#endif
2899
#elif defined(_MSC_FULL_VER)
2900
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2901
#elif defined(__INTEL_COMPILER)
2902
        osBuildInfo +=
2903
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2904
#endif
2905
#ifdef CMAKE_UNITY_BUILD
2906
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2907
#endif
2908
0
#ifdef EMBED_RESOURCE_FILES
2909
0
        osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2910
0
#endif
2911
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2912
        osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2913
#endif
2914
2915
0
#undef STRINGIFY_HELPER
2916
0
#undef STRINGIFY
2917
2918
0
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
2919
0
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
2920
0
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
2921
0
    }
2922
2923
    /* -------------------------------------------------------------------- */
2924
    /*      LICENSE is a special case. We try to find and read the          */
2925
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2926
    /* -------------------------------------------------------------------- */
2927
138
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
2928
0
    {
2929
#if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2930
        return GDALGetEmbeddedLicense();
2931
#else
2932
0
        char *pszResultLicence =
2933
0
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
2934
0
        if (pszResultLicence != nullptr)
2935
0
        {
2936
0
            return pszResultLicence;
2937
0
        }
2938
2939
0
        VSILFILE *fp = nullptr;
2940
0
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
2941
0
#ifdef EMBED_RESOURCE_FILES
2942
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2943
0
#endif
2944
0
        const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
2945
0
        if (pszFilename != nullptr)
2946
0
            fp = VSIFOpenL(pszFilename, "r");
2947
0
        if (fp != nullptr)
2948
0
        {
2949
0
            if (VSIFSeekL(fp, 0, SEEK_END) == 0)
2950
0
            {
2951
                // TODO(schwehr): Handle if VSITellL returns a value too large
2952
                // for size_t.
2953
0
                const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
2954
0
                if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
2955
0
                {
2956
0
                    pszResultLicence =
2957
0
                        static_cast<char *>(VSICalloc(1, nLength));
2958
0
                    if (pszResultLicence)
2959
0
                        CPL_IGNORE_RET_VAL(
2960
0
                            VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
2961
0
                }
2962
0
            }
2963
2964
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
2965
0
        }
2966
0
#endif
2967
2968
0
#ifdef EMBED_RESOURCE_FILES
2969
0
        if (!fp)
2970
0
        {
2971
0
            return GDALGetEmbeddedLicense();
2972
0
        }
2973
0
#endif
2974
2975
0
        if (!pszResultLicence)
2976
0
        {
2977
0
            pszResultLicence =
2978
0
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
2979
0
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
2980
0
                          "contain additional details.\n");
2981
0
        }
2982
2983
0
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
2984
0
        return pszResultLicence;
2985
0
#endif
2986
0
    }
2987
2988
    /* -------------------------------------------------------------------- */
2989
    /*      All other strings are fairly small.                             */
2990
    /* -------------------------------------------------------------------- */
2991
138
    CPLString osVersionInfo;
2992
2993
138
    if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
2994
0
        osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
2995
138
    else if (EQUAL(pszRequest, "RELEASE_DATE"))
2996
0
        osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
2997
138
    else if (EQUAL(pszRequest, "RELEASE_NAME"))
2998
138
        osVersionInfo.Printf(GDAL_RELEASE_NAME);
2999
0
    else if (EQUAL(pszRequest, "RELEASE_NICKNAME"))
3000
0
        osVersionInfo.Printf("%s", GDAL_RELEASE_NICKNAME);
3001
0
    else  // --version
3002
0
    {
3003
0
        osVersionInfo = "GDAL " GDAL_RELEASE_NAME;
3004
        if constexpr (GDAL_RELEASE_NICKNAME[0] != '\0')
3005
        {
3006
            osVersionInfo += " \"" GDAL_RELEASE_NICKNAME "\"";
3007
        }
3008
0
        osVersionInfo += CPLString().Printf(
3009
0
            ", released %d/%02d/%02d", GDAL_RELEASE_DATE / 10000,
3010
0
            (GDAL_RELEASE_DATE % 10000) / 100, GDAL_RELEASE_DATE % 100);
3011
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
3012
        // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
3013
        // also true for CLang
3014
        osVersionInfo += " (debug build)";
3015
#elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
3016
        // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
3017
        // In release mode, the compiler generates an error if you specify
3018
        // _ITERATOR_DEBUG_LEVEL as 2.
3019
        osVersionInfo += " (debug build)";
3020
#endif
3021
0
    }
3022
3023
138
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
3024
138
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
3025
138
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
3026
138
}
3027
3028
/************************************************************************/
3029
/*                         GDALCheckVersion()                           */
3030
/************************************************************************/
3031
3032
/** Return TRUE if GDAL library version at runtime matches
3033
   nVersionMajor.nVersionMinor.
3034
3035
    The purpose of this method is to ensure that calling code will run
3036
    with the GDAL version it is compiled for. It is primarily intended
3037
    for external plugins.
3038
3039
    @param nVersionMajor Major version to be tested against
3040
    @param nVersionMinor Minor version to be tested against
3041
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
3042
   method will issue a failure mentioning the name of the calling component.
3043
3044
    @return TRUE if GDAL library version at runtime matches
3045
    nVersionMajor.nVersionMinor, FALSE otherwise.
3046
  */
3047
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
3048
                                 const char *pszCallingComponentName)
3049
398
{
3050
398
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
3051
398
        nVersionMinor == GDAL_VERSION_MINOR)
3052
398
        return TRUE;
3053
3054
0
    if (pszCallingComponentName)
3055
0
    {
3056
0
        CPLError(CE_Failure, CPLE_AppDefined,
3057
0
                 "%s was compiled against GDAL %d.%d, but "
3058
0
                 "the current library version is %d.%d",
3059
0
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
3060
0
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
3061
0
    }
3062
0
    return FALSE;
3063
398
}
3064
3065
/************************************************************************/
3066
/*                            GDALDecToDMS()                            */
3067
/************************************************************************/
3068
3069
/** Translate a decimal degrees value to a DMS string with hemisphere.
3070
 */
3071
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
3072
                                     int nPrecision)
3073
3074
0
{
3075
0
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
3076
0
}
3077
3078
/************************************************************************/
3079
/*                         GDALPackedDMSToDec()                         */
3080
/************************************************************************/
3081
3082
/**
3083
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
3084
 *
3085
 * See CPLPackedDMSToDec().
3086
 */
3087
3088
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
3089
3090
0
{
3091
0
    return CPLPackedDMSToDec(dfPacked);
3092
0
}
3093
3094
/************************************************************************/
3095
/*                         GDALDecToPackedDMS()                         */
3096
/************************************************************************/
3097
3098
/**
3099
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
3100
 *
3101
 * See CPLDecToPackedDMS().
3102
 */
3103
3104
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
3105
3106
0
{
3107
0
    return CPLDecToPackedDMS(dfDec);
3108
0
}
3109
3110
/************************************************************************/
3111
/*                       GDALGCPsToGeoTransform()                       */
3112
/************************************************************************/
3113
3114
/**
3115
 * \brief Generate Geotransform from GCPs.
3116
 *
3117
 * Given a set of GCPs perform first order fit as a geotransform.
3118
 *
3119
 * Due to imprecision in the calculations the fit algorithm will often
3120
 * return non-zero rotational coefficients even if given perfectly non-rotated
3121
 * inputs.  A special case has been implemented for corner corner coordinates
3122
 * given in TL, TR, BR, BL order.  So when using this to get a geotransform
3123
 * from 4 corner coordinates, pass them in this order.
3124
 *
3125
 * If bApproxOK = FALSE, the
3126
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
3127
 * set to YES, then bApproxOK will be overridden with TRUE.
3128
 * When exact fit is asked, the
3129
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
3130
 * give the maximum error threshold in pixel. The default is 0.25.
3131
 *
3132
 * @param nGCPCount the number of GCPs being passed in.
3133
 * @param pasGCPs the list of GCP structures.
3134
 * @param padfGeoTransform the six double array in which the affine
3135
 * geotransformation will be returned.
3136
 * @param bApproxOK If FALSE the function will fail if the geotransform is not
3137
 * essentially an exact fit (within 0.25 pixel) for all GCPs.
3138
 *
3139
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
3140
 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
3141
 * and the fit is poor.
3142
 */
3143
3144
// TODO(schwehr): Add consts to args.
3145
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
3146
                                       double *padfGeoTransform, int bApproxOK)
3147
3148
3.35k
{
3149
3.35k
    double dfPixelThreshold = 0.25;
3150
3.35k
    if (!bApproxOK)
3151
2.30k
    {
3152
2.30k
        bApproxOK = CPLTestBool(
3153
2.30k
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3154
2.30k
        if (!bApproxOK)
3155
2.30k
        {
3156
2.30k
            dfPixelThreshold = std::clamp(
3157
2.30k
                CPLAtof(CPLGetConfigOption(
3158
2.30k
                    "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25")),
3159
2.30k
                0.0, std::numeric_limits<double>::max());
3160
2.30k
        }
3161
2.30k
    }
3162
3163
    /* -------------------------------------------------------------------- */
3164
    /*      Recognise a few special cases.                                  */
3165
    /* -------------------------------------------------------------------- */
3166
3.35k
    if (nGCPCount < 2)
3167
38
        return FALSE;
3168
3169
3.31k
    if (nGCPCount == 2)
3170
2
    {
3171
2
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
3172
2
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
3173
0
            return FALSE;
3174
3175
2
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3176
2
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3177
2
        padfGeoTransform[2] = 0.0;
3178
3179
2
        padfGeoTransform[4] = 0.0;
3180
2
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
3181
2
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
3182
3183
2
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
3184
2
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
3185
2
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
3186
3187
2
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
3188
2
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
3189
2
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3190
3191
2
        return TRUE;
3192
2
    }
3193
3194
    /* -------------------------------------------------------------------- */
3195
    /*      Special case of 4 corner coordinates of a non-rotated           */
3196
    /*      image.  The points must be in TL-TR-BR-BL order for now.        */
3197
    /*      This case helps avoid some imprecision in the general           */
3198
    /*      calculations.                                                   */
3199
    /* -------------------------------------------------------------------- */
3200
3.31k
    if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
3201
1.36k
        pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
3202
828
        pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
3203
187
        pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
3204
187
        pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
3205
99
        pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
3206
83
        pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
3207
58
        pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
3208
54
        pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
3209
53
        pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
3210
52
        pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
3211
19
        pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
3212
19
    {
3213
19
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3214
19
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3215
19
        padfGeoTransform[2] = 0.0;
3216
19
        padfGeoTransform[4] = 0.0;
3217
19
        padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
3218
19
                              (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
3219
3220
19
        padfGeoTransform[0] =
3221
19
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
3222
19
        padfGeoTransform[3] =
3223
19
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3224
19
        return TRUE;
3225
19
    }
3226
3227
    /* -------------------------------------------------------------------- */
3228
    /*      Compute source and destination ranges so we can normalize       */
3229
    /*      the values to make the least squares computation more stable.   */
3230
    /* -------------------------------------------------------------------- */
3231
3.29k
    double min_pixel = pasGCPs[0].dfGCPPixel;
3232
3.29k
    double max_pixel = pasGCPs[0].dfGCPPixel;
3233
3.29k
    double min_line = pasGCPs[0].dfGCPLine;
3234
3.29k
    double max_line = pasGCPs[0].dfGCPLine;
3235
3.29k
    double min_geox = pasGCPs[0].dfGCPX;
3236
3.29k
    double max_geox = pasGCPs[0].dfGCPX;
3237
3.29k
    double min_geoy = pasGCPs[0].dfGCPY;
3238
3.29k
    double max_geoy = pasGCPs[0].dfGCPY;
3239
3240
16.1k
    for (int i = 1; i < nGCPCount; ++i)
3241
12.9k
    {
3242
12.9k
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
3243
12.9k
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
3244
12.9k
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
3245
12.9k
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
3246
12.9k
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
3247
12.9k
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
3248
12.9k
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
3249
12.9k
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
3250
12.9k
    }
3251
3252
3.29k
    double EPS = 1.0e-12;
3253
3254
3.29k
    if (std::abs(max_pixel - min_pixel) < EPS ||
3255
3.17k
        std::abs(max_line - min_line) < EPS ||
3256
3.13k
        std::abs(max_geox - min_geox) < EPS ||
3257
3.08k
        std::abs(max_geoy - min_geoy) < EPS)
3258
225
    {
3259
225
        return FALSE;  // degenerate in at least one dimension.
3260
225
    }
3261
3262
3.07k
    double pl_normalize[6], geo_normalize[6];
3263
3264
3.07k
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
3265
3.07k
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
3266
3.07k
    pl_normalize[2] = 0.0;
3267
3.07k
    pl_normalize[3] = -min_line / (max_line - min_line);
3268
3.07k
    pl_normalize[4] = 0.0;
3269
3.07k
    pl_normalize[5] = 1.0 / (max_line - min_line);
3270
3271
3.07k
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
3272
3.07k
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
3273
3.07k
    geo_normalize[2] = 0.0;
3274
3.07k
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
3275
3.07k
    geo_normalize[4] = 0.0;
3276
3.07k
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
3277
3278
    /* -------------------------------------------------------------------- */
3279
    /* In the general case, do a least squares error approximation by       */
3280
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
3281
    /* -------------------------------------------------------------------- */
3282
3283
3.07k
    double sum_x = 0.0;
3284
3.07k
    double sum_y = 0.0;
3285
3.07k
    double sum_xy = 0.0;
3286
3.07k
    double sum_xx = 0.0;
3287
3.07k
    double sum_yy = 0.0;
3288
3.07k
    double sum_Lon = 0.0;
3289
3.07k
    double sum_Lonx = 0.0;
3290
3.07k
    double sum_Lony = 0.0;
3291
3.07k
    double sum_Lat = 0.0;
3292
3.07k
    double sum_Latx = 0.0;
3293
3.07k
    double sum_Laty = 0.0;
3294
3295
18.3k
    for (int i = 0; i < nGCPCount; ++i)
3296
15.3k
    {
3297
15.3k
        double pixel, line, geox, geoy;
3298
3299
15.3k
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
3300
15.3k
                              pasGCPs[i].dfGCPLine, &pixel, &line);
3301
15.3k
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
3302
15.3k
                              pasGCPs[i].dfGCPY, &geox, &geoy);
3303
3304
15.3k
        sum_x += pixel;
3305
15.3k
        sum_y += line;
3306
15.3k
        sum_xy += pixel * line;
3307
15.3k
        sum_xx += pixel * pixel;
3308
15.3k
        sum_yy += line * line;
3309
15.3k
        sum_Lon += geox;
3310
15.3k
        sum_Lonx += geox * pixel;
3311
15.3k
        sum_Lony += geox * line;
3312
15.3k
        sum_Lat += geoy;
3313
15.3k
        sum_Latx += geoy * pixel;
3314
15.3k
        sum_Laty += geoy * line;
3315
15.3k
    }
3316
3317
3.07k
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
3318
3.07k
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
3319
3.07k
                           sum_x * sum_x * sum_yy;
3320
3321
    /* -------------------------------------------------------------------- */
3322
    /*      If the divisor is zero, there is no valid solution.             */
3323
    /* -------------------------------------------------------------------- */
3324
3.07k
    if (divisor == 0.0)
3325
301
        return FALSE;
3326
3327
    /* -------------------------------------------------------------------- */
3328
    /*      Compute top/left origin.                                        */
3329
    /* -------------------------------------------------------------------- */
3330
2.77k
    double gt_normalized[6] = {0.0};
3331
2.77k
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
3332
2.77k
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
3333
2.77k
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
3334
2.77k
                       divisor;
3335
3336
2.77k
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
3337
2.77k
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
3338
2.77k
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
3339
2.77k
                       divisor;
3340
3341
    /* -------------------------------------------------------------------- */
3342
    /*      Compute X related coefficients.                                 */
3343
    /* -------------------------------------------------------------------- */
3344
2.77k
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
3345
2.77k
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
3346
2.77k
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
3347
2.77k
                       divisor;
3348
3349
2.77k
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
3350
2.77k
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
3351
2.77k
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
3352
2.77k
                       divisor;
3353
3354
    /* -------------------------------------------------------------------- */
3355
    /*      Compute Y related coefficients.                                 */
3356
    /* -------------------------------------------------------------------- */
3357
2.77k
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
3358
2.77k
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
3359
2.77k
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
3360
2.77k
                       divisor;
3361
3362
2.77k
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
3363
2.77k
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
3364
2.77k
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
3365
2.77k
                       divisor;
3366
3367
    /* -------------------------------------------------------------------- */
3368
    /*      Compose the resulting transformation with the normalization     */
3369
    /*      geotransformations.                                             */
3370
    /* -------------------------------------------------------------------- */
3371
2.77k
    double gt1p2[6] = {0.0};
3372
2.77k
    double inv_geo_normalize[6] = {0.0};
3373
2.77k
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
3374
2
        return FALSE;
3375
3376
2.76k
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
3377
2.76k
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
3378
3379
    // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
3380
2.76k
    if (std::abs(padfGeoTransform[1]) <= 1e-15 ||
3381
2.74k
        std::abs(padfGeoTransform[5]) <= 1e-15)
3382
603
    {
3383
603
        return FALSE;
3384
603
    }
3385
3386
    /* -------------------------------------------------------------------- */
3387
    /*      Now check if any of the input points fit this poorly.           */
3388
    /* -------------------------------------------------------------------- */
3389
2.16k
    if (!bApproxOK)
3390
1.61k
    {
3391
        // FIXME? Not sure if it is the more accurate way of computing
3392
        // pixel size
3393
1.61k
        double dfPixelSize =
3394
1.61k
            0.5 *
3395
1.61k
            (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
3396
1.61k
             std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
3397
1.61k
        if (dfPixelSize == 0.0)
3398
0
        {
3399
0
            CPLDebug("GDAL", "dfPixelSize = 0");
3400
0
            return FALSE;
3401
0
        }
3402
3403
5.81k
        for (int i = 0; i < nGCPCount; i++)
3404
4.79k
        {
3405
4.79k
            const double dfErrorX =
3406
4.79k
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
3407
4.79k
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
3408
4.79k
                 padfGeoTransform[0]) -
3409
4.79k
                pasGCPs[i].dfGCPX;
3410
4.79k
            const double dfErrorY =
3411
4.79k
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
3412
4.79k
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
3413
4.79k
                 padfGeoTransform[3]) -
3414
4.79k
                pasGCPs[i].dfGCPY;
3415
3416
4.79k
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
3417
4.38k
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
3418
586
            {
3419
586
                CPLDebug("GDAL",
3420
586
                         "dfErrorX/dfPixelSize = %.2f, "
3421
586
                         "dfErrorY/dfPixelSize = %.2f",
3422
586
                         std::abs(dfErrorX) / dfPixelSize,
3423
586
                         std::abs(dfErrorY) / dfPixelSize);
3424
586
                return FALSE;
3425
586
            }
3426
4.79k
        }
3427
1.61k
    }
3428
3429
1.57k
    return TRUE;
3430
2.16k
}
3431
3432
/************************************************************************/
3433
/*                      GDALComposeGeoTransforms()                      */
3434
/************************************************************************/
3435
3436
/**
3437
 * \brief Compose two geotransforms.
3438
 *
3439
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3440
 * being applied to a point.
3441
 *
3442
 * @param padfGT1 the first geotransform, six values.
3443
 * @param padfGT2 the second geotransform, six values.
3444
 * @param padfGTOut the output geotransform, six values, may safely be the same
3445
 * array as padfGT1 or padfGT2.
3446
 */
3447
3448
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
3449
                              double *padfGTOut)
3450
3451
5.53k
{
3452
5.53k
    double gtwrk[6] = {0.0};
3453
    // We need to think of the geotransform in a more normal form to do
3454
    // the matrix multiple:
3455
    //
3456
    //  __                     __
3457
    //  | gt[1]   gt[2]   gt[0] |
3458
    //  | gt[4]   gt[5]   gt[3] |
3459
    //  |  0.0     0.0     1.0  |
3460
    //  --                     --
3461
    //
3462
    // Then we can use normal matrix multiplication to produce the
3463
    // composed transformation.  I don't actually reform the matrix
3464
    // explicitly which is why the following may seem kind of spagettish.
3465
3466
5.53k
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
3467
5.53k
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
3468
5.53k
    gtwrk[0] =
3469
5.53k
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
3470
3471
5.53k
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
3472
5.53k
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
3473
5.53k
    gtwrk[3] =
3474
5.53k
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
3475
5.53k
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
3476
5.53k
}
3477
3478
/************************************************************************/
3479
/*                      StripIrrelevantOptions()                        */
3480
/************************************************************************/
3481
3482
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
3483
0
{
3484
0
    if (psCOL == nullptr)
3485
0
        return;
3486
0
    if (nOptions == 0)
3487
0
        nOptions = GDAL_OF_RASTER;
3488
0
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
3489
0
        return;
3490
3491
0
    CPLXMLNode *psPrev = nullptr;
3492
0
    for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
3493
0
    {
3494
0
        if (psIter->eType == CXT_Element)
3495
0
        {
3496
0
            CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
3497
0
            bool bStrip = false;
3498
0
            if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
3499
0
                psScope->psChild->pszValue &&
3500
0
                EQUAL(psScope->psChild->pszValue, "vector"))
3501
0
            {
3502
0
                bStrip = true;
3503
0
            }
3504
0
            else if (nOptions == GDAL_OF_VECTOR && psScope &&
3505
0
                     psScope->psChild && psScope->psChild->pszValue &&
3506
0
                     EQUAL(psScope->psChild->pszValue, "raster"))
3507
0
            {
3508
0
                bStrip = true;
3509
0
            }
3510
0
            if (psScope)
3511
0
            {
3512
0
                CPLRemoveXMLChild(psIter, psScope);
3513
0
                CPLDestroyXMLNode(psScope);
3514
0
            }
3515
3516
0
            CPLXMLNode *psNext = psIter->psNext;
3517
0
            if (bStrip)
3518
0
            {
3519
0
                if (psPrev)
3520
0
                    psPrev->psNext = psNext;
3521
0
                else if (psCOL->psChild == psIter)
3522
0
                    psCOL->psChild = psNext;
3523
0
                psIter->psNext = nullptr;
3524
0
                CPLDestroyXMLNode(psIter);
3525
0
                psIter = psNext;
3526
0
            }
3527
0
            else
3528
0
            {
3529
0
                psPrev = psIter;
3530
0
                psIter = psNext;
3531
0
            }
3532
0
        }
3533
0
        else
3534
0
        {
3535
0
            psIter = psIter->psNext;
3536
0
        }
3537
0
    }
3538
0
}
3539
3540
/************************************************************************/
3541
/*                         GDALPrintDriverList()                        */
3542
/************************************************************************/
3543
3544
/** Print on stdout the driver list */
3545
std::string GDALPrintDriverList(int nOptions, bool bJSON)
3546
0
{
3547
0
    if (nOptions == 0)
3548
0
        nOptions = GDAL_OF_RASTER;
3549
3550
0
    if (bJSON)
3551
0
    {
3552
0
        auto poDM = GetGDALDriverManager();
3553
0
        CPLJSONArray oArray;
3554
0
        const int nDriverCount = poDM->GetDriverCount();
3555
0
        for (int iDr = 0; iDr < nDriverCount; ++iDr)
3556
0
        {
3557
0
            auto poDriver = poDM->GetDriver(iDr);
3558
0
            CSLConstList papszMD = poDriver->GetMetadata();
3559
3560
0
            if (nOptions == GDAL_OF_RASTER &&
3561
0
                !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3562
0
                continue;
3563
0
            if (nOptions == GDAL_OF_VECTOR &&
3564
0
                !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3565
0
                continue;
3566
0
            if (nOptions == GDAL_OF_GNM &&
3567
0
                !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3568
0
                continue;
3569
0
            if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3570
0
                !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3571
0
                continue;
3572
3573
0
            CPLJSONObject oJDriver;
3574
0
            oJDriver.Set("short_name", poDriver->GetDescription());
3575
0
            if (const char *pszLongName =
3576
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
3577
0
                oJDriver.Set("long_name", pszLongName);
3578
0
            CPLJSONArray oJScopes;
3579
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3580
0
                oJScopes.Add("raster");
3581
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3582
0
                oJScopes.Add("multidimensional_raster");
3583
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3584
0
                oJScopes.Add("vector");
3585
0
            oJDriver.Add("scopes", oJScopes);
3586
0
            CPLJSONArray oJCaps;
3587
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3588
0
                oJCaps.Add("open");
3589
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3590
0
                oJCaps.Add("create");
3591
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3592
0
                oJCaps.Add("create_copy");
3593
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
3594
0
                oJCaps.Add("update");
3595
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3596
0
                oJCaps.Add("virtual_io");
3597
0
            oJDriver.Add("capabilities", oJCaps);
3598
3599
0
            if (const char *pszExtensions = CSLFetchNameValueDef(
3600
0
                    papszMD, GDAL_DMD_EXTENSIONS,
3601
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3602
0
            {
3603
0
                const CPLStringList aosExt(
3604
0
                    CSLTokenizeString2(pszExtensions, " ", 0));
3605
0
                CPLJSONArray oJExts;
3606
0
                for (int i = 0; i < aosExt.size(); ++i)
3607
0
                {
3608
0
                    oJExts.Add(aosExt[i]);
3609
0
                }
3610
0
                oJDriver.Add("file_extensions", oJExts);
3611
0
            }
3612
3613
0
            oArray.Add(oJDriver);
3614
0
        }
3615
3616
0
        return oArray.Format(CPLJSONObject::PrettyFormat::Pretty);
3617
0
    }
3618
3619
0
    std::string ret;
3620
0
    ret = "Supported Formats: (ro:read-only, rw:read-write, "
3621
0
          "+:write from scratch, u:update, "
3622
0
          "v:virtual-I/O s:subdatasets)\n";
3623
0
    for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
3624
0
    {
3625
0
        GDALDriverH hDriver = GDALGetDriver(iDr);
3626
3627
0
        const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets;
3628
0
        CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
3629
3630
0
        if (nOptions == GDAL_OF_RASTER &&
3631
0
            !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3632
0
            continue;
3633
0
        if (nOptions == GDAL_OF_VECTOR &&
3634
0
            !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3635
0
            continue;
3636
0
        if (nOptions == GDAL_OF_GNM &&
3637
0
            !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3638
0
            continue;
3639
0
        if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3640
0
            !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3641
0
            continue;
3642
3643
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3644
0
            pszRFlag = "r";
3645
3646
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3647
0
            pszWFlag = "w+";
3648
0
        else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3649
0
            pszWFlag = "w";
3650
0
        else
3651
0
            pszWFlag = "o";
3652
3653
0
        const char *pszUpdate = "";
3654
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
3655
0
            pszUpdate = "u";
3656
3657
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3658
0
            pszVirtualIO = "v";
3659
0
        else
3660
0
            pszVirtualIO = "";
3661
3662
0
        if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
3663
0
            pszSubdatasets = "s";
3664
0
        else
3665
0
            pszSubdatasets = "";
3666
3667
0
        CPLString osKind;
3668
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3669
0
            osKind = "raster";
3670
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3671
0
        {
3672
0
            if (!osKind.empty())
3673
0
                osKind += ',';
3674
0
            osKind += "multidimensional raster";
3675
0
        }
3676
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3677
0
        {
3678
0
            if (!osKind.empty())
3679
0
                osKind += ',';
3680
0
            osKind += "vector";
3681
0
        }
3682
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3683
0
        {
3684
0
            if (!osKind.empty())
3685
0
                osKind += ',';
3686
0
            osKind += "geography network";
3687
0
        }
3688
0
        if (osKind.empty())
3689
0
            osKind = "unknown kind";
3690
3691
0
        std::string osExtensions;
3692
0
        if (const char *pszExtensions = CSLFetchNameValueDef(
3693
0
                papszMD, GDAL_DMD_EXTENSIONS,
3694
0
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3695
0
        {
3696
0
            const CPLStringList aosExt(
3697
0
                CSLTokenizeString2(pszExtensions, " ", 0));
3698
0
            for (int i = 0; i < aosExt.size(); ++i)
3699
0
            {
3700
0
                if (i == 0)
3701
0
                    osExtensions = " (*.";
3702
0
                else
3703
0
                    osExtensions += ", *.";
3704
0
                osExtensions += aosExt[i];
3705
0
            }
3706
0
            if (!osExtensions.empty())
3707
0
                osExtensions += ')';
3708
0
        }
3709
3710
0
        ret += CPLSPrintf("  %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/
3711
0
                          GDALGetDriverShortName(hDriver), osKind.c_str(),
3712
0
                          pszRFlag, pszWFlag, pszUpdate, pszVirtualIO,
3713
0
                          pszSubdatasets, GDALGetDriverLongName(hDriver),
3714
0
                          osExtensions.c_str());
3715
0
    }
3716
3717
0
    return ret;
3718
0
}
3719
3720
/************************************************************************/
3721
/*                    GDALGeneralCmdLineProcessor()                     */
3722
/************************************************************************/
3723
3724
/**
3725
 * \brief General utility option processing.
3726
 *
3727
 * This function is intended to provide a variety of generic commandline
3728
 * options for all GDAL commandline utilities.  It takes care of the following
3729
 * commandline options:
3730
 *
3731
 *  \--version: report version of GDAL in use.
3732
 *  \--build: report build info about GDAL in use.
3733
 *  \--license: report GDAL license info.
3734
 *  \--formats: report all format drivers configured. Can be used with -json since 3.10
3735
 *  \--format [format]: report details of one format driver.
3736
 *  \--optfile filename: expand an option file into the argument list.
3737
 *  \--config key value: set system configuration option.
3738
 *  \--config key=value: set system configuration option (since GDAL 3.9)
3739
 *  \--debug [on/off/value]: set debug level.
3740
 *  \--mempreload dir: preload directory contents into /vsimem
3741
 *  \--pause: Pause for user input (allows time to attach debugger)
3742
 *  \--locale [locale]: Install a locale using setlocale() (debugging)
3743
 *  \--help-general: report detailed help on general options.
3744
 *
3745
 * The argument array is replaced "in place" and should be freed with
3746
 * CSLDestroy() when no longer needed.  The typical usage looks something
3747
 * like the following.  Note that the formats should be registered so that
3748
 * the \--formats and \--format options will work properly.
3749
 *
3750
 *  int main( int argc, char ** argv )
3751
 *  {
3752
 *    GDALAllRegister();
3753
 *
3754
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3755
 *    if( argc < 1 )
3756
 *        exit( -argc );
3757
 *
3758
 * @param nArgc number of values in the argument list.
3759
 * @param ppapszArgv pointer to the argument list array (will be updated in
3760
 * place).
3761
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3762
 *                 to determine which drivers should be displayed by \--formats.
3763
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3764
 *
3765
 * @return updated nArgc argument count.  Return of 0 requests terminate
3766
 * without error, return of -1 requests exit with error code.
3767
 */
3768
3769
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
3770
                                            int nOptions)
3771
3772
0
{
3773
0
    CPLStringList aosReturn;
3774
0
    int iArg;
3775
0
    char **papszArgv = *ppapszArgv;
3776
3777
    /* -------------------------------------------------------------------- */
3778
    /*      Preserve the program name.                                      */
3779
    /* -------------------------------------------------------------------- */
3780
0
    aosReturn.AddString(papszArgv[0]);
3781
3782
    /* ==================================================================== */
3783
    /*      Loop over all arguments.                                        */
3784
    /* ==================================================================== */
3785
3786
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3787
    // detects and warns about a unknown config option.
3788
0
    for (iArg = 1; iArg < nArgc; iArg++)
3789
0
    {
3790
0
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
3791
0
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3792
0
        {
3793
0
            if (iArg + 1 >= nArgc)
3794
0
            {
3795
0
                CPLError(CE_Failure, CPLE_AppDefined,
3796
0
                         "--config option given without a key=value argument.");
3797
0
                return -1;
3798
0
            }
3799
3800
0
            const char *pszArg = papszArgv[iArg + 1];
3801
0
            if (strchr(pszArg, '=') != nullptr)
3802
0
            {
3803
0
                char *pszKey = nullptr;
3804
0
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3805
0
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3806
0
                {
3807
0
                    CPLSetConfigOption(pszKey, pszValue);
3808
0
                }
3809
0
                CPLFree(pszKey);
3810
0
                ++iArg;
3811
0
            }
3812
0
            else
3813
0
            {
3814
                // cppcheck-suppress knownConditionTrueFalse
3815
0
                if (iArg + 2 >= nArgc)
3816
0
                {
3817
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3818
0
                             "--config option given without a key and value "
3819
0
                             "argument.");
3820
0
                    return -1;
3821
0
                }
3822
3823
0
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3824
0
                    CPLSetConfigOption(papszArgv[iArg + 1],
3825
0
                                       papszArgv[iArg + 2]);
3826
3827
0
                iArg += 2;
3828
0
            }
3829
0
        }
3830
0
        else if (EQUAL(papszArgv[iArg], "--debug"))
3831
0
        {
3832
0
            if (iArg + 1 >= nArgc)
3833
0
            {
3834
0
                CPLError(CE_Failure, CPLE_AppDefined,
3835
0
                         "--debug option given without debug level.");
3836
0
                return -1;
3837
0
            }
3838
3839
0
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
3840
0
            iArg += 1;
3841
0
        }
3842
0
    }
3843
3844
0
    for (iArg = 1; iArg < nArgc; iArg++)
3845
0
    {
3846
        /* --------------------------------------------------------------------
3847
         */
3848
        /*      --version */
3849
        /* --------------------------------------------------------------------
3850
         */
3851
0
        if (EQUAL(papszArgv[iArg], "--version"))
3852
0
        {
3853
0
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
3854
0
            return 0;
3855
0
        }
3856
3857
        /* --------------------------------------------------------------------
3858
         */
3859
        /*      --build */
3860
        /* --------------------------------------------------------------------
3861
         */
3862
0
        else if (EQUAL(papszArgv[iArg], "--build"))
3863
0
        {
3864
0
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
3865
0
            return 0;
3866
0
        }
3867
3868
        /* --------------------------------------------------------------------
3869
         */
3870
        /*      --license */
3871
        /* --------------------------------------------------------------------
3872
         */
3873
0
        else if (EQUAL(papszArgv[iArg], "--license"))
3874
0
        {
3875
0
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
3876
0
            return 0;
3877
0
        }
3878
3879
        /* --------------------------------------------------------------------
3880
         */
3881
        /*      --config */
3882
        /* --------------------------------------------------------------------
3883
         */
3884
0
        else if (EQUAL(papszArgv[iArg], "--config"))
3885
0
        {
3886
0
            if (iArg + 1 >= nArgc)
3887
0
            {
3888
0
                CPLError(CE_Failure, CPLE_AppDefined,
3889
0
                         "--config option given without a key=value argument.");
3890
0
                return -1;
3891
0
            }
3892
3893
0
            const char *pszArg = papszArgv[iArg + 1];
3894
0
            if (strchr(pszArg, '=') != nullptr)
3895
0
            {
3896
0
                char *pszKey = nullptr;
3897
0
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3898
0
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3899
0
                {
3900
0
                    CPLSetConfigOption(pszKey, pszValue);
3901
0
                }
3902
0
                CPLFree(pszKey);
3903
0
                ++iArg;
3904
0
            }
3905
0
            else
3906
0
            {
3907
0
                if (iArg + 2 >= nArgc)
3908
0
                {
3909
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3910
0
                             "--config option given without a key and value "
3911
0
                             "argument.");
3912
0
                    return -1;
3913
0
                }
3914
3915
0
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3916
0
                    CPLSetConfigOption(papszArgv[iArg + 1],
3917
0
                                       papszArgv[iArg + 2]);
3918
3919
0
                iArg += 2;
3920
0
            }
3921
0
        }
3922
3923
        /* --------------------------------------------------------------------
3924
         */
3925
        /*      --mempreload */
3926
        /* --------------------------------------------------------------------
3927
         */
3928
0
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
3929
0
        {
3930
0
            if (iArg + 1 >= nArgc)
3931
0
            {
3932
0
                CPLError(CE_Failure, CPLE_AppDefined,
3933
0
                         "--mempreload option given without directory path.");
3934
0
                return -1;
3935
0
            }
3936
3937
0
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
3938
0
            if (CSLCount(papszFiles) == 0)
3939
0
            {
3940
0
                CPLError(CE_Failure, CPLE_AppDefined,
3941
0
                         "--mempreload given invalid or empty directory.");
3942
0
                return -1;
3943
0
            }
3944
3945
0
            for (int i = 0; papszFiles[i] != nullptr; i++)
3946
0
            {
3947
0
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
3948
0
                    continue;
3949
3950
0
                std::string osOldPath;
3951
0
                CPLString osNewPath;
3952
0
                osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
3953
0
                                                papszFiles[i], nullptr);
3954
0
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
3955
3956
0
                VSIStatBufL sStatBuf;
3957
0
                if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
3958
0
                    VSI_ISDIR(sStatBuf.st_mode))
3959
0
                {
3960
0
                    CPLDebug("VSI", "Skipping preload of %s.",
3961
0
                             osOldPath.c_str());
3962
0
                    continue;
3963
0
                }
3964
3965
0
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
3966
0
                         osNewPath.c_str());
3967
3968
0
                if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
3969
0
                {
3970
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3971
0
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
3972
0
                    return -1;
3973
0
                }
3974
0
            }
3975
3976
0
            CSLDestroy(papszFiles);
3977
0
            iArg += 1;
3978
0
        }
3979
3980
        /* --------------------------------------------------------------------
3981
         */
3982
        /*      --debug */
3983
        /* --------------------------------------------------------------------
3984
         */
3985
0
        else if (EQUAL(papszArgv[iArg], "--debug"))
3986
0
        {
3987
0
            if (iArg + 1 >= nArgc)
3988
0
            {
3989
0
                CPLError(CE_Failure, CPLE_AppDefined,
3990
0
                         "--debug option given without debug level.");
3991
0
                return -1;
3992
0
            }
3993
3994
0
            iArg += 1;
3995
0
        }
3996
3997
        /* --------------------------------------------------------------------
3998
         */
3999
        /*      --optfile */
4000
        /* --------------------------------------------------------------------
4001
         */
4002
0
        else if (EQUAL(papszArgv[iArg], "--optfile"))
4003
0
        {
4004
0
            if (iArg + 1 >= nArgc)
4005
0
            {
4006
0
                CPLError(CE_Failure, CPLE_AppDefined,
4007
0
                         "--optfile option given without filename.");
4008
0
                return -1;
4009
0
            }
4010
4011
0
            VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb");
4012
4013
0
            if (fpOptFile == nullptr)
4014
0
            {
4015
0
                CPLError(CE_Failure, CPLE_AppDefined,
4016
0
                         "Unable to open optfile '%s'.\n%s",
4017
0
                         papszArgv[iArg + 1], VSIStrerror(errno));
4018
0
                return -1;
4019
0
            }
4020
4021
0
            const char *pszLine;
4022
0
            CPLStringList aosArgvOptfile;
4023
            // dummy value as first argument to please
4024
            // GDALGeneralCmdLineProcessor()
4025
0
            aosArgvOptfile.AddString("");
4026
0
            bool bHasOptfile = false;
4027
0
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
4028
0
            {
4029
0
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
4030
0
                    continue;
4031
4032
0
                char **papszTokens = CSLTokenizeString(pszLine);
4033
0
                for (int i = 0;
4034
0
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
4035
0
                {
4036
0
                    if (EQUAL(papszTokens[i], "--optfile"))
4037
0
                    {
4038
                        // To avoid potential recursion
4039
0
                        CPLError(CE_Warning, CPLE_AppDefined,
4040
0
                                 "--optfile not supported in a option file");
4041
0
                        bHasOptfile = true;
4042
0
                    }
4043
0
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
4044
0
                    papszTokens[i] = nullptr;
4045
0
                }
4046
0
                CSLDestroy(papszTokens);
4047
0
            }
4048
4049
0
            VSIFCloseL(fpOptFile);
4050
4051
0
            char **papszArgvOptfile = aosArgvOptfile.StealList();
4052
0
            if (!bHasOptfile)
4053
0
            {
4054
0
                char **papszArgvOptfileBefore = papszArgvOptfile;
4055
0
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
4056
0
                                                &papszArgvOptfile,
4057
0
                                                nOptions) < 0)
4058
0
                {
4059
0
                    CSLDestroy(papszArgvOptfile);
4060
0
                    return -1;
4061
0
                }
4062
0
                CSLDestroy(papszArgvOptfileBefore);
4063
0
            }
4064
4065
0
            char **papszIter = papszArgvOptfile + 1;
4066
0
            while (*papszIter)
4067
0
            {
4068
0
                aosReturn.AddString(*papszIter);
4069
0
                ++papszIter;
4070
0
            }
4071
0
            CSLDestroy(papszArgvOptfile);
4072
4073
0
            iArg += 1;
4074
0
        }
4075
4076
        /* --------------------------------------------------------------------
4077
         */
4078
        /*      --formats */
4079
        /* --------------------------------------------------------------------
4080
         */
4081
0
        else if (EQUAL(papszArgv[iArg], "--formats"))
4082
0
        {
4083
0
            bool bJSON = false;
4084
0
            for (int i = 1; i < nArgc; i++)
4085
0
            {
4086
0
                if (strcmp(papszArgv[i], "-json") == 0 ||
4087
0
                    strcmp(papszArgv[i], "--json") == 0)
4088
0
                {
4089
0
                    bJSON = true;
4090
0
                    break;
4091
0
                }
4092
0
            }
4093
4094
0
            printf("%s", GDALPrintDriverList(nOptions, bJSON).c_str()); /*ok*/
4095
4096
0
            return 0;
4097
0
        }
4098
4099
        /* --------------------------------------------------------------------
4100
         */
4101
        /*      --format */
4102
        /* --------------------------------------------------------------------
4103
         */
4104
0
        else if (EQUAL(papszArgv[iArg], "--format"))
4105
0
        {
4106
0
            GDALDriverH hDriver;
4107
0
            char **papszMD;
4108
4109
0
            if (iArg + 1 >= nArgc)
4110
0
            {
4111
0
                CPLError(CE_Failure, CPLE_AppDefined,
4112
0
                         "--format option given without a format code.");
4113
0
                return -1;
4114
0
            }
4115
4116
0
            hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
4117
0
            if (hDriver == nullptr)
4118
0
            {
4119
0
                CPLError(CE_Failure, CPLE_AppDefined,
4120
0
                         "--format option given with format '%s', but that "
4121
0
                         "format not\nrecognised.  Use the --formats option "
4122
0
                         "to get a list of available formats,\n"
4123
0
                         "and use the short code (i.e. GTiff or HFA) as the "
4124
0
                         "format identifier.\n",
4125
0
                         papszArgv[iArg + 1]);
4126
0
                return -1;
4127
0
            }
4128
4129
0
            printf("Format Details:\n"); /*ok*/
4130
0
            printf(/*ok*/ "  Short Name: %s\n",
4131
0
                   GDALGetDriverShortName(hDriver));
4132
0
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
4133
4134
0
            papszMD = GDALGetMetadata(hDriver, nullptr);
4135
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
4136
0
                printf("  Supports: Raster\n"); /*ok*/
4137
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
4138
0
                printf("  Supports: Multidimensional raster\n"); /*ok*/
4139
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
4140
0
                printf("  Supports: Vector\n"); /*ok*/
4141
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
4142
0
                printf("  Supports: Geography Network\n"); /*ok*/
4143
4144
0
            const char *pszExt =
4145
0
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
4146
0
            if (pszExt != nullptr)
4147
0
                printf("  Extension%s: %s\n", /*ok*/
4148
0
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
4149
4150
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
4151
0
                printf("  Mime Type: %s\n", /*ok*/
4152
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4153
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
4154
0
                printf("  Help Topic: %s\n", /*ok*/
4155
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4156
4157
0
            if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
4158
0
                printf("  Supports: Raster subdatasets\n"); /*ok*/
4159
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
4160
0
                printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
4161
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
4162
0
                printf(/*ok*/
4163
0
                       "  Supports: Create() - Create writable dataset.\n");
4164
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
4165
0
                printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
4166
0
                              "multidimensional dataset.\n");
4167
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
4168
0
                printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
4169
0
                              "copying "
4170
0
                              "another.\n");
4171
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
4172
0
                printf("  Supports: Update\n"); /*ok*/
4173
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
4174
0
                printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
4175
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
4176
0
                printf("  Creation Datatypes: %s\n", /*ok*/
4177
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
4178
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
4179
0
                printf("  Creation Field Datatypes: %s\n", /*ok*/
4180
0
                       CSLFetchNameValue(papszMD,
4181
0
                                         GDAL_DMD_CREATIONFIELDDATATYPES));
4182
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
4183
0
                printf("  Creation Field Data Sub-types: %s\n", /*ok*/
4184
0
                       CSLFetchNameValue(papszMD,
4185
0
                                         GDAL_DMD_CREATIONFIELDDATASUBTYPES));
4186
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
4187
0
                printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
4188
0
                              "constraint.\n");
4189
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
4190
0
                printf(/*ok*/
4191
0
                       "  Supports: Creating fields with UNIQUE constraint.\n");
4192
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
4193
0
                printf(/*ok*/
4194
0
                       "  Supports: Creating fields with DEFAULT values.\n");
4195
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
4196
0
                /*ok*/ printf(
4197
0
                    "  Supports: Creating geometry fields with NOT NULL "
4198
0
                    "constraint.\n");
4199
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
4200
0
                             false))
4201
0
                /*ok*/ printf("  Supports: Writing geometries with given "
4202
0
                              "coordinate precision\n");
4203
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
4204
0
                printf("  Supports: Reading feature styles.\n"); /*ok*/
4205
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
4206
0
                printf("  Supports: Writing feature styles.\n"); /*ok*/
4207
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
4208
0
                printf("  Supports: Coordinate epoch.\n"); /*ok*/
4209
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
4210
0
                printf("  Supports: Multiple vector layers.\n"); /*ok*/
4211
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
4212
0
                printf("  Supports: Reading field domains.\n"); /*ok*/
4213
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPSERT, false))
4214
0
                printf("  Supports: Feature upsert.\n"); /*ok*/
4215
0
            if (CSLFetchNameValue(papszMD,
4216
0
                                  GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
4217
0
                printf("  Creation field domain types: %s\n", /*ok*/
4218
0
                       CSLFetchNameValue(papszMD,
4219
0
                                         GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
4220
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
4221
0
                printf("  Supported SQL dialects: %s\n", /*ok*/
4222
0
                       CSLFetchNameValue(papszMD,
4223
0
                                         GDAL_DMD_SUPPORTED_SQL_DIALECTS));
4224
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS))
4225
0
                printf("  Supported items for update: %s\n", /*ok*/
4226
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS));
4227
4228
0
            for (const char *key :
4229
0
                 {GDAL_DMD_CREATIONOPTIONLIST,
4230
0
                  GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
4231
0
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4232
0
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4233
0
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4234
0
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4235
0
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4236
0
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4237
0
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
4238
0
            {
4239
0
                if (CSLFetchNameValue(papszMD, key))
4240
0
                {
4241
0
                    CPLXMLNode *psCOL =
4242
0
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
4243
0
                    StripIrrelevantOptions(psCOL, nOptions);
4244
0
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4245
4246
0
                    CPLDestroyXMLNode(psCOL);
4247
4248
0
                    printf("\n%s\n", pszFormattedXML); /*ok*/
4249
0
                    CPLFree(pszFormattedXML);
4250
0
                }
4251
0
            }
4252
4253
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
4254
0
                printf("  Connection prefix: %s\n", /*ok*/
4255
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4256
4257
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
4258
0
            {
4259
0
                CPLXMLNode *psCOL = CPLParseXMLString(
4260
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4261
0
                StripIrrelevantOptions(psCOL, nOptions);
4262
0
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4263
4264
0
                CPLDestroyXMLNode(psCOL);
4265
4266
0
                printf("%s\n", pszFormattedXML); /*ok*/
4267
0
                CPLFree(pszFormattedXML);
4268
0
            }
4269
4270
0
            bool bFirstOtherOption = true;
4271
0
            for (char **papszIter = papszMD; papszIter && *papszIter;
4272
0
                 ++papszIter)
4273
0
            {
4274
0
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
4275
0
                    !STARTS_WITH(*papszIter, "DMD_") &&
4276
0
                    !STARTS_WITH(*papszIter, "DS_") &&
4277
0
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
4278
0
                {
4279
0
                    if (bFirstOtherOption)
4280
0
                        printf("  Other metadata items:\n"); /*ok*/
4281
0
                    bFirstOtherOption = false;
4282
0
                    printf("    %s\n", *papszIter); /*ok*/
4283
0
                }
4284
0
            }
4285
4286
0
            return 0;
4287
0
        }
4288
4289
        /* --------------------------------------------------------------------
4290
         */
4291
        /*      --help-general */
4292
        /* --------------------------------------------------------------------
4293
         */
4294
0
        else if (EQUAL(papszArgv[iArg], "--help-general"))
4295
0
        {
4296
0
            printf("Generic GDAL utility command options:\n");       /*ok*/
4297
0
            printf("  --version: report version of GDAL in use.\n"); /*ok*/
4298
0
            /*ok*/ printf(
4299
0
                "  --build: report detailed information about GDAL in "
4300
0
                "use.\n");
4301
0
            printf("  --license: report GDAL license info.\n"); /*ok*/
4302
0
            printf(                                             /*ok*/
4303
0
                   "  --formats: report all configured format drivers.\n"); /*ok*/
4304
0
            printf("  --format [<format>]: details of one format.\n"); /*ok*/
4305
0
            /*ok*/ printf(
4306
0
                "  --optfile filename: expand an option file into the "
4307
0
                "argument list.\n");
4308
0
            printf(/*ok*/
4309
0
                   "  --config <key> <value> or --config <key>=<value>: set "
4310
0
                   "system configuration option.\n");               /*ok*/
4311
0
            printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
4312
0
            /*ok*/ printf(                                          /*ok*/
4313
0
                          "  --pause: wait for user input, time to attach "
4314
0
                          "debugger\n");
4315
0
            printf("  --locale [<locale>]: install locale for debugging " /*ok*/
4316
0
                   "(i.e. en_US.UTF-8)\n");
4317
0
            printf("  --help-general: report detailed help on general " /*ok*/
4318
0
                   "options.\n");
4319
4320
0
            return 0;
4321
0
        }
4322
4323
        /* --------------------------------------------------------------------
4324
         */
4325
        /*      --locale */
4326
        /* --------------------------------------------------------------------
4327
         */
4328
0
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
4329
0
        {
4330
0
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
4331
0
        }
4332
4333
        /* --------------------------------------------------------------------
4334
         */
4335
        /*      --pause */
4336
        /* --------------------------------------------------------------------
4337
         */
4338
0
        else if (EQUAL(papszArgv[iArg], "--pause"))
4339
0
        {
4340
0
            std::cout << "Hit <ENTER> to Continue." << std::endl;
4341
0
            std::cin.clear();
4342
0
            std::cin.ignore(cpl::NumericLimits<std::streamsize>::max(), '\n');
4343
0
        }
4344
4345
        /* --------------------------------------------------------------------
4346
         */
4347
        /*      Carry through unrecognized options. */
4348
        /* --------------------------------------------------------------------
4349
         */
4350
0
        else
4351
0
        {
4352
0
            aosReturn.AddString(papszArgv[iArg]);
4353
0
        }
4354
0
    }
4355
4356
0
    const int nSize = aosReturn.size();
4357
0
    *ppapszArgv = aosReturn.StealList();
4358
4359
0
    return nSize;
4360
0
}
4361
4362
/************************************************************************/
4363
/*                          _FetchDblFromMD()                           */
4364
/************************************************************************/
4365
4366
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
4367
                            double *padfTarget, int nCount, double dfDefault)
4368
4369
0
{
4370
0
    char szFullKey[200];
4371
4372
0
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
4373
4374
0
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
4375
4376
0
    for (int i = 0; i < nCount; i++)
4377
0
        padfTarget[i] = dfDefault;
4378
4379
0
    if (pszValue == nullptr)
4380
0
        return false;
4381
4382
0
    if (nCount == 1)
4383
0
    {
4384
0
        *padfTarget = CPLAtofM(pszValue);
4385
0
        return true;
4386
0
    }
4387
4388
0
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
4389
4390
0
    if (CSLCount(papszTokens) != nCount)
4391
0
    {
4392
0
        CSLDestroy(papszTokens);
4393
0
        return false;
4394
0
    }
4395
4396
0
    for (int i = 0; i < nCount; i++)
4397
0
        padfTarget[i] = CPLAtofM(papszTokens[i]);
4398
4399
0
    CSLDestroy(papszTokens);
4400
4401
0
    return true;
4402
0
}
4403
4404
/************************************************************************/
4405
/*                         GDALExtractRPCInfo()                         */
4406
/************************************************************************/
4407
4408
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4409
 *
4410
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4411
 *
4412
 * @param papszMD Dictionary of metadata representing RPC
4413
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4414
 * @return TRUE in case of success. FALSE in case of failure.
4415
 */
4416
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
4417
4418
0
{
4419
0
    GDALRPCInfoV2 sRPC;
4420
0
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
4421
0
        return FALSE;
4422
0
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
4423
0
    return TRUE;
4424
0
}
4425
4426
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4427
 *
4428
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4429
 *
4430
 * @param papszMD Dictionary of metadata representing RPC
4431
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4432
 * @return TRUE in case of success. FALSE in case of failure.
4433
 */
4434
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
4435
4436
0
{
4437
0
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
4438
0
        return FALSE;
4439
4440
0
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
4441
0
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
4442
0
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
4443
0
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
4444
0
    {
4445
0
        CPLError(CE_Failure, CPLE_AppDefined,
4446
0
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
4447
0
        return FALSE;
4448
0
    }
4449
4450
0
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
4451
0
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
4452
0
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
4453
0
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
4454
0
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
4455
0
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
4456
0
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
4457
0
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
4458
0
                    1.0);
4459
0
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
4460
0
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
4461
0
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
4462
0
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
4463
4464
0
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
4465
0
                    0.0);
4466
0
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
4467
0
                    0.0);
4468
0
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
4469
0
                    0.0);
4470
0
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
4471
0
                    0.0);
4472
4473
0
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
4474
0
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
4475
0
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
4476
0
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
4477
4478
0
    return TRUE;
4479
0
}
4480
4481
/************************************************************************/
4482
/*                     GDALFindAssociatedAuxFile()                      */
4483
/************************************************************************/
4484
4485
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
4486
                                       GDALAccess eAccess,
4487
                                       GDALDataset *poDependentDS)
4488
4489
353k
{
4490
353k
    const char *pszAuxSuffixLC = "aux";
4491
353k
    const char *pszAuxSuffixUC = "AUX";
4492
4493
353k
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
4494
53.0k
        return nullptr;
4495
4496
    /* -------------------------------------------------------------------- */
4497
    /*      Don't even try to look for an .aux file if we don't have a      */
4498
    /*      path of any kind.                                               */
4499
    /* -------------------------------------------------------------------- */
4500
300k
    if (strlen(pszBasename) == 0)
4501
0
        return nullptr;
4502
4503
    /* -------------------------------------------------------------------- */
4504
    /*      We didn't find that, so try and find a corresponding aux        */
4505
    /*      file.  Check that we are the dependent file of the aux          */
4506
    /*      file, or if we aren't verify that the dependent file does       */
4507
    /*      not exist, likely mean it is us but some sort of renaming       */
4508
    /*      has occurred.                                                   */
4509
    /* -------------------------------------------------------------------- */
4510
300k
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
4511
300k
    CPLString osAuxFilename =
4512
300k
        CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC);
4513
300k
    GDALDataset *poODS = nullptr;
4514
300k
    GByte abyHeader[32];
4515
4516
300k
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
4517
4518
300k
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4519
231k
    {
4520
        // Can't found file with lower case suffix. Try the upper case one.
4521
231k
        osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC);
4522
231k
        fp = VSIFOpenL(osAuxFilename, "rb");
4523
231k
    }
4524
4525
300k
    if (fp != nullptr)
4526
70.7k
    {
4527
70.7k
        if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4528
70.4k
            STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4529
70.7k
                           "EHFA_HEADER_TAG"))
4530
67.0k
        {
4531
            /* Avoid causing failure in opening of main file from SWIG bindings
4532
             */
4533
            /* when auxiliary file cannot be opened (#3269) */
4534
67.0k
            CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
4535
67.0k
            if (poDependentDS != nullptr && poDependentDS->GetShared())
4536
0
                poODS = GDALDataset::FromHandle(
4537
0
                    GDALOpenShared(osAuxFilename, eAccess));
4538
67.0k
            else
4539
67.0k
                poODS =
4540
67.0k
                    GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
4541
67.0k
        }
4542
70.7k
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4543
70.7k
    }
4544
4545
    /* -------------------------------------------------------------------- */
4546
    /*      Try replacing extension with .aux                               */
4547
    /* -------------------------------------------------------------------- */
4548
300k
    if (poODS != nullptr)
4549
59.5k
    {
4550
59.5k
        const char *pszDep =
4551
59.5k
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4552
59.5k
        if (pszDep == nullptr)
4553
59.5k
        {
4554
59.5k
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
4555
59.5k
                     osAuxFilename.c_str());
4556
59.5k
            GDALClose(poODS);
4557
59.5k
            poODS = nullptr;
4558
59.5k
        }
4559
0
        else if (!EQUAL(pszDep, osJustFile))
4560
0
        {
4561
0
            VSIStatBufL sStatBuf;
4562
4563
0
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4564
0
            {
4565
0
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4566
0
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4567
0
                GDALClose(poODS);
4568
0
                poODS = nullptr;
4569
0
            }
4570
0
            else
4571
0
            {
4572
0
                CPLDebug("AUX",
4573
0
                         "%s is for file %s, not %s, but since\n"
4574
0
                         "%s does not exist, we will use .aux file as our own.",
4575
0
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4576
0
                         pszDep);
4577
0
            }
4578
0
        }
4579
4580
        /* --------------------------------------------------------------------
4581
         */
4582
        /*      Confirm that the aux file matches the configuration of the */
4583
        /*      dependent dataset. */
4584
        /* --------------------------------------------------------------------
4585
         */
4586
59.5k
        if (poODS != nullptr && poDependentDS != nullptr &&
4587
0
            (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4588
0
             poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4589
0
             poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4590
0
        {
4591
0
            CPLDebug("AUX",
4592
0
                     "Ignoring aux file %s as its raster configuration\n"
4593
0
                     "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4594
0
                     "x %dB)",
4595
0
                     osAuxFilename.c_str(), poODS->GetRasterXSize(),
4596
0
                     poODS->GetRasterYSize(), poODS->GetRasterCount(),
4597
0
                     poDependentDS->GetRasterXSize(),
4598
0
                     poDependentDS->GetRasterYSize(),
4599
0
                     poDependentDS->GetRasterCount());
4600
4601
0
            GDALClose(poODS);
4602
0
            poODS = nullptr;
4603
0
        }
4604
59.5k
    }
4605
4606
    /* -------------------------------------------------------------------- */
4607
    /*      Try appending .aux to the end of the filename.                  */
4608
    /* -------------------------------------------------------------------- */
4609
300k
    if (poODS == nullptr)
4610
300k
    {
4611
300k
        osAuxFilename = pszBasename;
4612
300k
        osAuxFilename += ".";
4613
300k
        osAuxFilename += pszAuxSuffixLC;
4614
300k
        fp = VSIFOpenL(osAuxFilename, "rb");
4615
300k
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4616
242k
        {
4617
            // Can't found file with lower case suffix. Try the upper case one.
4618
242k
            osAuxFilename = pszBasename;
4619
242k
            osAuxFilename += ".";
4620
242k
            osAuxFilename += pszAuxSuffixUC;
4621
242k
            fp = VSIFOpenL(osAuxFilename, "rb");
4622
242k
        }
4623
4624
300k
        if (fp != nullptr)
4625
58.8k
        {
4626
58.8k
            if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4627
58.4k
                STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4628
58.8k
                               "EHFA_HEADER_TAG"))
4629
55.0k
            {
4630
                /* Avoid causing failure in opening of main file from SWIG
4631
                 * bindings */
4632
                /* when auxiliary file cannot be opened (#3269) */
4633
55.0k
                CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
4634
55.0k
                if (poDependentDS != nullptr && poDependentDS->GetShared())
4635
0
                    poODS = GDALDataset::FromHandle(
4636
0
                        GDALOpenShared(osAuxFilename, eAccess));
4637
55.0k
                else
4638
55.0k
                    poODS = GDALDataset::FromHandle(
4639
55.0k
                        GDALOpen(osAuxFilename, eAccess));
4640
55.0k
            }
4641
58.8k
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4642
58.8k
        }
4643
4644
300k
        if (poODS != nullptr)
4645
45.0k
        {
4646
45.0k
            const char *pszDep =
4647
45.0k
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4648
45.0k
            if (pszDep == nullptr)
4649
45.0k
            {
4650
45.0k
                CPLDebug("AUX",
4651
45.0k
                         "Found %s but it has no dependent file, ignoring.",
4652
45.0k
                         osAuxFilename.c_str());
4653
45.0k
                GDALClose(poODS);
4654
45.0k
                poODS = nullptr;
4655
45.0k
            }
4656
0
            else if (!EQUAL(pszDep, osJustFile))
4657
0
            {
4658
0
                VSIStatBufL sStatBuf;
4659
4660
0
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4661
0
                {
4662
0
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4663
0
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4664
0
                    GDALClose(poODS);
4665
0
                    poODS = nullptr;
4666
0
                }
4667
0
                else
4668
0
                {
4669
0
                    CPLDebug(
4670
0
                        "AUX",
4671
0
                        "%s is for file %s, not %s, but since\n"
4672
0
                        "%s does not exist, we will use .aux file as our own.",
4673
0
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4674
0
                        pszDep);
4675
0
                }
4676
0
            }
4677
45.0k
        }
4678
300k
    }
4679
4680
    /* -------------------------------------------------------------------- */
4681
    /*      Confirm that the aux file matches the configuration of the      */
4682
    /*      dependent dataset.                                              */
4683
    /* -------------------------------------------------------------------- */
4684
300k
    if (poODS != nullptr && poDependentDS != nullptr &&
4685
0
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4686
0
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4687
0
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4688
0
    {
4689
0
        CPLDebug(
4690
0
            "AUX",
4691
0
            "Ignoring aux file %s as its raster configuration\n"
4692
0
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4693
0
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4694
0
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4695
0
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4696
0
            poDependentDS->GetRasterCount());
4697
4698
0
        GDALClose(poODS);
4699
0
        poODS = nullptr;
4700
0
    }
4701
4702
300k
    return poODS;
4703
300k
}
4704
4705
/************************************************************************/
4706
/* Infrastructure to check that dataset characteristics are valid       */
4707
/************************************************************************/
4708
4709
CPL_C_START
4710
4711
/**
4712
 * \brief Return TRUE if the dataset dimensions are valid.
4713
 *
4714
 * @param nXSize raster width
4715
 * @param nYSize raster height
4716
 *
4717
 */
4718
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
4719
299k
{
4720
299k
    if (nXSize <= 0 || nYSize <= 0)
4721
6.57k
    {
4722
6.57k
        CPLError(CE_Failure, CPLE_AppDefined,
4723
6.57k
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4724
6.57k
        return FALSE;
4725
6.57k
    }
4726
292k
    return TRUE;
4727
299k
}
4728
4729
/**
4730
 * \brief Return TRUE if the band count is valid.
4731
 *
4732
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4733
 * the band count will be compared to the maximum number of band allowed.
4734
 * If not defined, the maximum number allowed is 65536.
4735
 *
4736
 * @param nBands the band count
4737
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4738
 *
4739
 */
4740
4741
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
4742
293k
{
4743
293k
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
4744
482
    {
4745
482
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
4746
482
                 nBands);
4747
482
        return FALSE;
4748
482
    }
4749
293k
    const char *pszMaxBandCount =
4750
293k
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
4751
293k
    int nMaxBands = std::clamp(atoi(pszMaxBandCount), 0, INT_MAX - 1);
4752
293k
    if (nBands > nMaxBands)
4753
201
    {
4754
201
        CPLError(CE_Failure, CPLE_AppDefined,
4755
201
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4756
201
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4757
201
                 "legitimate number.",
4758
201
                 nBands, nMaxBands);
4759
201
        return FALSE;
4760
201
    }
4761
293k
    return TRUE;
4762
293k
}
4763
4764
CPL_C_END
4765
4766
/************************************************************************/
4767
/*                     GDALSerializeGCPListToXML()                      */
4768
/************************************************************************/
4769
4770
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
4771
                               const std::vector<gdal::GCP> &asGCPs,
4772
                               const OGRSpatialReference *poGCP_SRS)
4773
5.13k
{
4774
5.13k
    CPLString oFmt;
4775
4776
5.13k
    CPLXMLNode *psPamGCPList =
4777
5.13k
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
4778
4779
5.13k
    CPLXMLNode *psLastChild = nullptr;
4780
4781
5.13k
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
4782
4.04k
    {
4783
4.04k
        char *pszWKT = nullptr;
4784
4.04k
        poGCP_SRS->exportToWkt(&pszWKT);
4785
4.04k
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
4786
4.04k
        CPLFree(pszWKT);
4787
4.04k
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
4788
4.04k
        CPLString osMapping;
4789
12.3k
        for (size_t i = 0; i < mapping.size(); ++i)
4790
8.31k
        {
4791
8.31k
            if (!osMapping.empty())
4792
4.27k
                osMapping += ",";
4793
8.31k
            osMapping += CPLSPrintf("%d", mapping[i]);
4794
8.31k
        }
4795
4.04k
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
4796
4.04k
                       osMapping.c_str());
4797
4798
4.04k
        psLastChild = psPamGCPList->psChild->psNext;
4799
4.04k
    }
4800
4801
5.13k
    for (const gdal::GCP &gcp : asGCPs)
4802
97.4k
    {
4803
97.4k
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
4804
4805
97.4k
        if (psLastChild == nullptr)
4806
1.08k
            psPamGCPList->psChild = psXMLGCP;
4807
96.3k
        else
4808
96.3k
            psLastChild->psNext = psXMLGCP;
4809
97.4k
        psLastChild = psXMLGCP;
4810
4811
97.4k
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
4812
4813
97.4k
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
4814
0
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
4815
4816
97.4k
        CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
4817
4818
97.4k
        CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
4819
4820
97.4k
        CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
4821
4822
97.4k
        CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
4823
4824
97.4k
        if (gcp.Z() != 0.0)
4825
83.2k
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
4826
97.4k
    }
4827
5.13k
}
4828
4829
/************************************************************************/
4830
/*                     GDALDeserializeGCPListFromXML()                  */
4831
/************************************************************************/
4832
4833
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
4834
                                   std::vector<gdal::GCP> &asGCPs,
4835
                                   OGRSpatialReference **ppoGCP_SRS)
4836
3.63k
{
4837
3.63k
    if (ppoGCP_SRS)
4838
3.62k
    {
4839
3.62k
        const char *pszRawProj =
4840
3.62k
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
4841
4842
3.62k
        *ppoGCP_SRS = nullptr;
4843
3.62k
        if (pszRawProj && pszRawProj[0])
4844
3.41k
        {
4845
3.41k
            *ppoGCP_SRS = new OGRSpatialReference();
4846
3.41k
            (*ppoGCP_SRS)
4847
3.41k
                ->SetFromUserInput(
4848
3.41k
                    pszRawProj,
4849
3.41k
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4850
4851
3.41k
            const char *pszMapping =
4852
3.41k
                CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
4853
3.41k
            if (pszMapping)
4854
35
            {
4855
35
                char **papszTokens =
4856
35
                    CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
4857
35
                std::vector<int> anMapping;
4858
105
                for (int i = 0; papszTokens && papszTokens[i]; i++)
4859
70
                {
4860
70
                    anMapping.push_back(atoi(papszTokens[i]));
4861
70
                }
4862
35
                CSLDestroy(papszTokens);
4863
35
                (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
4864
35
            }
4865
3.38k
            else
4866
3.38k
            {
4867
3.38k
                (*ppoGCP_SRS)
4868
3.38k
                    ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4869
3.38k
            }
4870
3.41k
        }
4871
3.62k
    }
4872
4873
3.63k
    asGCPs.clear();
4874
37.6k
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
4875
33.9k
         psXMLGCP = psXMLGCP->psNext)
4876
33.9k
    {
4877
33.9k
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
4878
8.83k
            continue;
4879
4880
25.1k
        gdal::GCP gcp;
4881
25.1k
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
4882
25.1k
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
4883
4884
25.1k
        const auto ParseDoubleValue =
4885
25.1k
            [psXMLGCP](const char *pszParameter, double &dfVal)
4886
100k
        {
4887
100k
            const char *pszVal =
4888
100k
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
4889
100k
            if (!pszVal)
4890
78
            {
4891
78
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
4892
78
                         pszParameter);
4893
78
                return false;
4894
78
            }
4895
100k
            char *endptr = nullptr;
4896
100k
            dfVal = CPLStrtod(pszVal, &endptr);
4897
100k
            if (endptr == pszVal)
4898
20
            {
4899
20
                CPLError(CE_Failure, CPLE_AppDefined,
4900
20
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4901
20
                return false;
4902
20
            }
4903
100k
            return true;
4904
100k
        };
4905
4906
25.1k
        bool bOK = true;
4907
25.1k
        if (!ParseDoubleValue("Pixel", gcp.Pixel()))
4908
24
            bOK = false;
4909
25.1k
        if (!ParseDoubleValue("Line", gcp.Line()))
4910
16
            bOK = false;
4911
25.1k
        if (!ParseDoubleValue("X", gcp.X()))
4912
28
            bOK = false;
4913
25.1k
        if (!ParseDoubleValue("Y", gcp.Y()))
4914
30
            bOK = false;
4915
25.1k
        const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
4916
25.1k
        if (pszZ == nullptr)
4917
23.8k
        {
4918
            // Note: GDAL 1.10.1 and older generated #GCPZ,
4919
            // but could not read it back.
4920
23.8k
            pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
4921
23.8k
        }
4922
25.1k
        char *endptr = nullptr;
4923
25.1k
        gcp.Z() = CPLStrtod(pszZ, &endptr);
4924
25.1k
        if (endptr == pszZ)
4925
0
        {
4926
0
            CPLError(CE_Failure, CPLE_AppDefined,
4927
0
                     "GCP#Z=%s is an invalid value", pszZ);
4928
0
            bOK = false;
4929
0
        }
4930
4931
25.1k
        if (bOK)
4932
25.0k
        {
4933
25.0k
            asGCPs.emplace_back(std::move(gcp));
4934
25.0k
        }
4935
25.1k
    }
4936
3.63k
}
4937
4938
/************************************************************************/
4939
/*                   GDALSerializeOpenOptionsToXML()                    */
4940
/************************************************************************/
4941
4942
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
4943
                                   CSLConstList papszOpenOptions)
4944
56.0k
{
4945
56.0k
    if (papszOpenOptions != nullptr)
4946
0
    {
4947
0
        CPLXMLNode *psOpenOptions =
4948
0
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
4949
0
        CPLXMLNode *psLastChild = nullptr;
4950
4951
0
        for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
4952
0
             papszIter++)
4953
0
        {
4954
0
            const char *pszRawValue;
4955
0
            char *pszKey = nullptr;
4956
0
            CPLXMLNode *psOOI;
4957
4958
0
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
4959
4960
0
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
4961
0
            if (psLastChild == nullptr)
4962
0
                psOpenOptions->psChild = psOOI;
4963
0
            else
4964
0
                psLastChild->psNext = psOOI;
4965
0
            psLastChild = psOOI;
4966
4967
0
            CPLSetXMLValue(psOOI, "#key", pszKey);
4968
0
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
4969
4970
0
            CPLFree(pszKey);
4971
0
        }
4972
0
    }
4973
56.0k
}
4974
4975
/************************************************************************/
4976
/*                  GDALDeserializeOpenOptionsFromXML()                 */
4977
/************************************************************************/
4978
4979
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
4980
65.1k
{
4981
65.1k
    char **papszOpenOptions = nullptr;
4982
65.1k
    const CPLXMLNode *psOpenOptions =
4983
65.1k
        CPLGetXMLNode(psParentNode, "OpenOptions");
4984
65.1k
    if (psOpenOptions != nullptr)
4985
0
    {
4986
0
        const CPLXMLNode *psOOI;
4987
0
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
4988
0
             psOOI = psOOI->psNext)
4989
0
        {
4990
0
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
4991
0
                psOOI->psChild == nullptr ||
4992
0
                psOOI->psChild->psNext == nullptr ||
4993
0
                psOOI->psChild->eType != CXT_Attribute ||
4994
0
                psOOI->psChild->psChild == nullptr)
4995
0
                continue;
4996
4997
0
            char *pszName = psOOI->psChild->psChild->pszValue;
4998
0
            char *pszValue = psOOI->psChild->psNext->pszValue;
4999
0
            if (pszName != nullptr && pszValue != nullptr)
5000
0
                papszOpenOptions =
5001
0
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
5002
0
        }
5003
0
    }
5004
65.1k
    return papszOpenOptions;
5005
65.1k
}
5006
5007
/************************************************************************/
5008
/*                    GDALRasterIOGetResampleAlg()                      */
5009
/************************************************************************/
5010
5011
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
5012
34.3k
{
5013
34.3k
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
5014
34.3k
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
5015
2.34k
        eResampleAlg = GRIORA_NearestNeighbour;
5016
31.9k
    else if (EQUAL(pszResampling, "BILINEAR"))
5017
0
        eResampleAlg = GRIORA_Bilinear;
5018
31.9k
    else if (EQUAL(pszResampling, "CUBIC"))
5019
4.56k
        eResampleAlg = GRIORA_Cubic;
5020
27.4k
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
5021
0
        eResampleAlg = GRIORA_CubicSpline;
5022
27.4k
    else if (EQUAL(pszResampling, "LANCZOS"))
5023
6.89k
        eResampleAlg = GRIORA_Lanczos;
5024
20.5k
    else if (EQUAL(pszResampling, "AVERAGE"))
5025
14.5k
        eResampleAlg = GRIORA_Average;
5026
5.93k
    else if (EQUAL(pszResampling, "RMS"))
5027
0
        eResampleAlg = GRIORA_RMS;
5028
5.93k
    else if (EQUAL(pszResampling, "MODE"))
5029
0
        eResampleAlg = GRIORA_Mode;
5030
5.93k
    else if (EQUAL(pszResampling, "GAUSS"))
5031
170
        eResampleAlg = GRIORA_Gauss;
5032
5.76k
    else
5033
5.76k
        CPLError(CE_Warning, CPLE_NotSupported,
5034
5.76k
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
5035
34.3k
    return eResampleAlg;
5036
34.3k
}
5037
5038
/************************************************************************/
5039
/*                    GDALRasterIOGetResampleAlgStr()                   */
5040
/************************************************************************/
5041
5042
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
5043
0
{
5044
0
    switch (eResampleAlg)
5045
0
    {
5046
0
        case GRIORA_NearestNeighbour:
5047
0
            return "NearestNeighbour";
5048
0
        case GRIORA_Bilinear:
5049
0
            return "Bilinear";
5050
0
        case GRIORA_Cubic:
5051
0
            return "Cubic";
5052
0
        case GRIORA_CubicSpline:
5053
0
            return "CubicSpline";
5054
0
        case GRIORA_Lanczos:
5055
0
            return "Lanczos";
5056
0
        case GRIORA_Average:
5057
0
            return "Average";
5058
0
        case GRIORA_RMS:
5059
0
            return "RMS";
5060
0
        case GRIORA_Mode:
5061
0
            return "Mode";
5062
0
        case GRIORA_Gauss:
5063
0
            return "Gauss";
5064
0
        default:
5065
0
            CPLAssert(false);
5066
0
            return "Unknown";
5067
0
    }
5068
0
}
5069
5070
/************************************************************************/
5071
/*                   GDALRasterIOExtraArgSetResampleAlg()               */
5072
/************************************************************************/
5073
5074
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
5075
                                        int nXSize, int nYSize, int nBufXSize,
5076
                                        int nBufYSize)
5077
31.0M
{
5078
31.0M
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
5079
167k
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
5080
144k
    {
5081
144k
        const char *pszResampling =
5082
144k
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
5083
144k
        if (pszResampling != nullptr)
5084
0
        {
5085
0
            psExtraArg->eResampleAlg =
5086
0
                GDALRasterIOGetResampleAlg(pszResampling);
5087
0
        }
5088
144k
    }
5089
31.0M
}
5090
5091
/************************************************************************/
5092
/*                     GDALCanFileAcceptSidecarFile()                   */
5093
/************************************************************************/
5094
5095
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
5096
2.07M
{
5097
2.07M
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
5098
3.08k
        return FALSE;
5099
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
5100
2.07M
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
5101
1
        return FALSE;
5102
2.07M
    return TRUE;
5103
2.07M
}
5104
5105
/************************************************************************/
5106
/*                   GDALCanReliablyUseSiblingFileList()                */
5107
/************************************************************************/
5108
5109
/* Try to address https://github.com/OSGeo/gdal/issues/2903 */
5110
/* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
5111
/*   (normalization form decomposed). The filesystem takes care of converting */
5112
/*   precomposed form as often coming from user interface to this NFD variant */
5113
/*   See
5114
 * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
5115
 */
5116
/*   And readdir() will return such NFD variant encoding. Consequently comparing
5117
 */
5118
/*   the user filename with ones with readdir() is not reliable */
5119
/* - APFS preserves both case and normalization of the filename on disk in all
5120
 */
5121
/*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
5122
 */
5123
/*   the case-insensitive and case-sensitive variants, using a hash-based native
5124
 */
5125
/*   normalization scheme. APFS preserves the normalization of the filename and
5126
 */
5127
/*   uses hashes of the normalized form of the filename to provide normalization
5128
 */
5129
/*   insensitivity. */
5130
/*   From
5131
 * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
5132
 */
5133
/*   Issues might still arise if the file has been created using one of the
5134
 * UTF-8 */
5135
/*   encoding (likely the decomposed one if using MacOS specific API), but the
5136
 */
5137
/*   string passed to GDAL for opening would be with another one (likely the
5138
 * precomposed one) */
5139
bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
5140
1.63M
{
5141
#ifdef __APPLE__
5142
    for (int i = 0; pszFilename[i] != 0; ++i)
5143
    {
5144
        if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
5145
        {
5146
            // non-ASCII character found
5147
5148
            // if this is a network storage, assume no issue
5149
            if (!VSIIsLocal(pszFilename))
5150
            {
5151
                return true;
5152
            }
5153
            return false;
5154
        }
5155
    }
5156
    return true;
5157
#else
5158
1.63M
    (void)pszFilename;
5159
1.63M
    return true;
5160
1.63M
#endif
5161
1.63M
}
5162
5163
/************************************************************************/
5164
/*                    GDALAdjustNoDataCloseToFloatMax()                 */
5165
/************************************************************************/
5166
5167
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
5168
6.24k
{
5169
6.24k
    const auto kMaxFloat = cpl::NumericLimits<float>::max();
5170
6.24k
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
5171
1
        return -kMaxFloat;
5172
6.23k
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
5173
1
        return kMaxFloat;
5174
6.23k
    return dfVal;
5175
6.23k
}
5176
5177
/************************************************************************/
5178
/*                        GDALCopyNoDataValue()                         */
5179
/************************************************************************/
5180
5181
/** Copy the nodata value from the source band to the target band if
5182
 * it can be exactly represented in the output data type.
5183
 *
5184
 * @param poDstBand Destination band.
5185
 * @param poSrcBand Source band band.
5186
 * @param[out] pbCannotBeExactlyRepresented Pointer to a boolean, or nullptr.
5187
 *             If the value cannot be exactly represented on the output data
5188
 *             type, *pbCannotBeExactlyRepresented will be set to true.
5189
 *
5190
 * @return true if the nodata value was successfully set.
5191
 */
5192
bool GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand,
5193
                         bool *pbCannotBeExactlyRepresented)
5194
84.7k
{
5195
84.7k
    if (pbCannotBeExactlyRepresented)
5196
14.4k
        *pbCannotBeExactlyRepresented = false;
5197
84.7k
    int bSuccess;
5198
84.7k
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
5199
84.7k
    const auto eDstDataType = poDstBand->GetRasterDataType();
5200
84.7k
    if (eSrcDataType == GDT_Int64)
5201
1
    {
5202
1
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
5203
1
        if (bSuccess)
5204
0
        {
5205
0
            if (eDstDataType == GDT_Int64)
5206
0
            {
5207
0
                return poDstBand->SetNoDataValueAsInt64(nNoData) == CE_None;
5208
0
            }
5209
0
            else if (eDstDataType == GDT_UInt64)
5210
0
            {
5211
0
                if (nNoData >= 0)
5212
0
                {
5213
0
                    return poDstBand->SetNoDataValueAsUInt64(
5214
0
                               static_cast<uint64_t>(nNoData)) == CE_None;
5215
0
                }
5216
0
            }
5217
0
            else if (nNoData ==
5218
0
                     static_cast<int64_t>(static_cast<double>(nNoData)))
5219
0
            {
5220
0
                const double dfValue = static_cast<double>(nNoData);
5221
0
                if (GDALIsValueExactAs(dfValue, eDstDataType))
5222
0
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
5223
0
            }
5224
0
        }
5225
1
    }
5226
84.7k
    else if (eSrcDataType == GDT_UInt64)
5227
770
    {
5228
770
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
5229
770
        if (bSuccess)
5230
2
        {
5231
2
            if (eDstDataType == GDT_UInt64)
5232
2
            {
5233
2
                return poDstBand->SetNoDataValueAsUInt64(nNoData) == CE_None;
5234
2
            }
5235
0
            else if (eDstDataType == GDT_Int64)
5236
0
            {
5237
0
                if (nNoData <
5238
0
                    static_cast<uint64_t>(cpl::NumericLimits<int64_t>::max()))
5239
0
                {
5240
0
                    return poDstBand->SetNoDataValueAsInt64(
5241
0
                               static_cast<int64_t>(nNoData)) == CE_None;
5242
0
                }
5243
0
            }
5244
0
            else if (nNoData ==
5245
0
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
5246
0
            {
5247
0
                const double dfValue = static_cast<double>(nNoData);
5248
0
                if (GDALIsValueExactAs(dfValue, eDstDataType))
5249
0
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
5250
0
            }
5251
2
        }
5252
770
    }
5253
83.9k
    else
5254
83.9k
    {
5255
83.9k
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
5256
83.9k
        if (bSuccess)
5257
23.0k
        {
5258
23.0k
            if (eDstDataType == GDT_Int64)
5259
0
            {
5260
0
                if (dfNoData >= static_cast<double>(
5261
0
                                    cpl::NumericLimits<int64_t>::lowest()) &&
5262
0
                    dfNoData <= static_cast<double>(
5263
0
                                    cpl::NumericLimits<int64_t>::max()) &&
5264
0
                    dfNoData ==
5265
0
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
5266
0
                {
5267
0
                    return poDstBand->SetNoDataValueAsInt64(
5268
0
                               static_cast<int64_t>(dfNoData)) == CE_None;
5269
0
                }
5270
0
            }
5271
23.0k
            else if (eDstDataType == GDT_UInt64)
5272
0
            {
5273
0
                if (dfNoData >= static_cast<double>(
5274
0
                                    cpl::NumericLimits<uint64_t>::lowest()) &&
5275
0
                    dfNoData <= static_cast<double>(
5276
0
                                    cpl::NumericLimits<uint64_t>::max()) &&
5277
0
                    dfNoData ==
5278
0
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
5279
0
                {
5280
0
                    return poDstBand->SetNoDataValueAsInt64(
5281
0
                               static_cast<uint64_t>(dfNoData)) == CE_None;
5282
0
                }
5283
0
            }
5284
23.0k
            else
5285
23.0k
            {
5286
23.0k
                return poDstBand->SetNoDataValue(dfNoData) == CE_None;
5287
23.0k
            }
5288
23.0k
        }
5289
83.9k
    }
5290
61.6k
    if (pbCannotBeExactlyRepresented)
5291
0
        *pbCannotBeExactlyRepresented = true;
5292
61.6k
    return false;
5293
84.7k
}
5294
5295
/************************************************************************/
5296
/*                     GDALGetNoDataValueCastToDouble()                 */
5297
/************************************************************************/
5298
5299
double GDALGetNoDataValueCastToDouble(int64_t nVal)
5300
117
{
5301
117
    const double dfVal = static_cast<double>(nVal);
5302
117
    if (static_cast<int64_t>(dfVal) != nVal)
5303
3
    {
5304
3
        CPLError(CE_Warning, CPLE_AppDefined,
5305
3
                 "GetNoDataValue() returns an approximate value of the "
5306
3
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5307
3
                 "GetNoDataValueAsInt64() instead",
5308
3
                 static_cast<GIntBig>(nVal));
5309
3
    }
5310
117
    return dfVal;
5311
117
}
5312
5313
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
5314
131
{
5315
131
    const double dfVal = static_cast<double>(nVal);
5316
131
    if (static_cast<uint64_t>(dfVal) != nVal)
5317
12
    {
5318
12
        CPLError(CE_Warning, CPLE_AppDefined,
5319
12
                 "GetNoDataValue() returns an approximate value of the "
5320
12
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5321
12
                 "GetNoDataValueAsUInt64() instead",
5322
12
                 static_cast<GUIntBig>(nVal));
5323
12
    }
5324
131
    return dfVal;
5325
131
}
5326
5327
/************************************************************************/
5328
/*                GDALGetCompressionFormatForJPEG()                     */
5329
/************************************************************************/
5330
5331
//! @cond Doxygen_Suppress
5332
std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
5333
0
{
5334
0
    std::string osRet;
5335
0
    const auto nSavedPos = VSIFTellL(fp);
5336
0
    GByte abyMarkerHeader[4];
5337
0
    if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
5338
0
        VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
5339
0
        abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
5340
0
    {
5341
0
        osRet = "JPEG";
5342
0
        bool bHasAPP14Adobe = false;
5343
0
        GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
5344
0
        int nNumComponents = 0;
5345
0
        while (true)
5346
0
        {
5347
0
            const auto nCurPos = VSIFTellL(fp);
5348
0
            if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
5349
0
                break;
5350
0
            if (abyMarkerHeader[0] != 0xFF)
5351
0
                break;
5352
0
            const GByte markerType = abyMarkerHeader[1];
5353
0
            const size_t nMarkerSize =
5354
0
                abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
5355
0
            if (nMarkerSize < 2)
5356
0
                break;
5357
0
            if (markerType >= 0xC0 && markerType <= 0xCF &&
5358
0
                markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
5359
0
            {
5360
0
                switch (markerType)
5361
0
                {
5362
0
                    case 0xC0:
5363
0
                        osRet += ";frame_type=SOF0_baseline";
5364
0
                        break;
5365
0
                    case 0xC1:
5366
0
                        osRet += ";frame_type=SOF1_extended_sequential";
5367
0
                        break;
5368
0
                    case 0xC2:
5369
0
                        osRet += ";frame_type=SOF2_progressive_huffman";
5370
0
                        break;
5371
0
                    case 0xC3:
5372
0
                        osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
5373
0
                                 "supported=no";
5374
0
                        break;
5375
0
                    case 0xC5:
5376
0
                        osRet += ";frame_type="
5377
0
                                 "SOF5_differential_sequential_huffman;"
5378
0
                                 "libjpeg_supported=no";
5379
0
                        break;
5380
0
                    case 0xC6:
5381
0
                        osRet += ";frame_type=SOF6_differential_progressive_"
5382
0
                                 "huffman;libjpeg_supported=no";
5383
0
                        break;
5384
0
                    case 0xC7:
5385
0
                        osRet += ";frame_type="
5386
0
                                 "SOF7_differential_lossless_huffman;"
5387
0
                                 "libjpeg_supported=no";
5388
0
                        break;
5389
0
                    case 0xC9:
5390
0
                        osRet += ";frame_type="
5391
0
                                 "SOF9_extended_sequential_arithmetic";
5392
0
                        break;
5393
0
                    case 0xCA:
5394
0
                        osRet += ";frame_type=SOF10_progressive_arithmetic";
5395
0
                        break;
5396
0
                    case 0xCB:
5397
0
                        osRet += ";frame_type="
5398
0
                                 "SOF11_lossless_arithmetic;libjpeg_"
5399
0
                                 "supported=no";
5400
0
                        break;
5401
0
                    case 0xCD:
5402
0
                        osRet += ";frame_type=SOF13_differential_sequential_"
5403
0
                                 "arithmetic;libjpeg_supported=no";
5404
0
                        break;
5405
0
                    case 0xCE:
5406
0
                        osRet += ";frame_type=SOF14_differential_progressive_"
5407
0
                                 "arithmetic;libjpeg_supported=no";
5408
0
                        break;
5409
0
                    case 0xCF:
5410
0
                        osRet += ";frame_type=SOF15_differential_lossless_"
5411
0
                                 "arithmetic;libjpeg_supported=no";
5412
0
                        break;
5413
0
                    default:
5414
0
                        break;
5415
0
                }
5416
0
                GByte abySegmentBegin[6];
5417
0
                if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
5418
0
                              fp) != 1)
5419
0
                    break;
5420
0
                osRet += ";bit_depth=";
5421
0
                osRet += CPLSPrintf("%d", abySegmentBegin[0]);
5422
0
                nNumComponents = abySegmentBegin[5];
5423
0
                osRet += ";num_components=";
5424
0
                osRet += CPLSPrintf("%d", nNumComponents);
5425
0
                if (nNumComponents == 3)
5426
0
                {
5427
0
                    GByte abySegmentNext[3 * 3];
5428
0
                    if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
5429
0
                                  fp) != 1)
5430
0
                        break;
5431
0
                    if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
5432
0
                        abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
5433
0
                        abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
5434
0
                    {
5435
                        // no subsampling
5436
0
                        osRet += ";subsampling=4:4:4";
5437
0
                    }
5438
0
                    else if (abySegmentNext[0] == 1 &&
5439
0
                             abySegmentNext[1] == 0x22 &&
5440
0
                             abySegmentNext[3] == 2 &&
5441
0
                             abySegmentNext[4] == 0x11 &&
5442
0
                             abySegmentNext[6] == 3 &&
5443
0
                             abySegmentNext[7] == 0x11)
5444
0
                    {
5445
                        // classic subsampling
5446
0
                        osRet += ";subsampling=4:2:0";
5447
0
                    }
5448
0
                    else if (abySegmentNext[0] == 1 &&
5449
0
                             abySegmentNext[1] == 0x21 &&
5450
0
                             abySegmentNext[3] == 2 &&
5451
0
                             abySegmentNext[4] == 0x11 &&
5452
0
                             abySegmentNext[6] == 3 &&
5453
0
                             abySegmentNext[7] == 0x11)
5454
0
                    {
5455
0
                        osRet += ";subsampling=4:2:2";
5456
0
                    }
5457
0
                }
5458
0
            }
5459
0
            else if (markerType == 0xEE && nMarkerSize == 14)
5460
0
            {
5461
0
                if (VSIFReadL(abyAPP14AdobeMarkerData,
5462
0
                              sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
5463
0
                    memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
5464
0
                        0)
5465
0
                {
5466
0
                    bHasAPP14Adobe = true;
5467
0
                }
5468
0
            }
5469
0
            else if (markerType == 0xDA)
5470
0
            {
5471
                // Start of scan
5472
0
                break;
5473
0
            }
5474
0
            VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
5475
0
        }
5476
0
        std::string osColorspace;
5477
0
        if (bHasAPP14Adobe)
5478
0
        {
5479
0
            if (abyAPP14AdobeMarkerData[11] == 0)
5480
0
            {
5481
0
                if (nNumComponents == 3)
5482
0
                    osColorspace = "RGB";
5483
0
                else if (nNumComponents == 4)
5484
0
                    osColorspace = "CMYK";
5485
0
            }
5486
0
            else if (abyAPP14AdobeMarkerData[11] == 1)
5487
0
            {
5488
0
                osColorspace = "YCbCr";
5489
0
            }
5490
0
            else if (abyAPP14AdobeMarkerData[11] == 2)
5491
0
            {
5492
0
                osColorspace = "YCCK";
5493
0
            }
5494
0
        }
5495
0
        else
5496
0
        {
5497
0
            if (nNumComponents == 3)
5498
0
                osColorspace = "YCbCr";
5499
0
            else if (nNumComponents == 4)
5500
0
                osColorspace = "CMYK";
5501
0
        }
5502
0
        osRet += ";colorspace=";
5503
0
        if (!osColorspace.empty())
5504
0
            osRet += osColorspace;
5505
0
        else
5506
0
            osRet += "unknown";
5507
0
    }
5508
0
    if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
5509
0
    {
5510
0
        CPLError(CE_Failure, CPLE_AppDefined,
5511
0
                 "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
5512
0
    }
5513
0
    return osRet;
5514
0
}
5515
5516
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
5517
                                            size_t nBufferSize)
5518
0
{
5519
0
    VSILFILE *fp = VSIFileFromMemBuffer(
5520
0
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5521
0
        false);
5522
0
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
5523
0
    VSIFCloseL(fp);
5524
0
    return osRet;
5525
0
}
5526
5527
//! @endcond
5528
5529
/************************************************************************/
5530
/*                      GDALGetNoDataReplacementValue()                 */
5531
/************************************************************************/
5532
5533
/**
5534
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5535
 *        is out of range for the specified data type (dt).
5536
 *        For UInt64 and Int64 data type this function cannot reliably trusted
5537
 *        because their nodata values might not always be representable exactly
5538
 *        as a double, in particular the maximum absolute value for those types
5539
 *        is 2^53.
5540
 *
5541
 * The replacement value is a value that can be used in a computation
5542
 * whose result would match by accident the nodata value, whereas it is
5543
 * meant to be valid. For example, for a dataset with a nodata value of 0,
5544
 * when averaging -1 and 1, one would get normally a value of 0. The
5545
 * replacement nodata value can then be substituted to that 0 value to still
5546
 * get a valid value, as close as practical to the true value, while being
5547
 * different from the nodata value.
5548
 *
5549
 * @param dt Data type
5550
 * @param dfNoDataValue The no data value
5551
5552
 * @since GDAL 3.9
5553
 */
5554
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
5555
3.97k
{
5556
5557
    // The logic here is to check if the value is out of range for the
5558
    // specified data type and return a replacement value if it is, return
5559
    // 0 otherwise.
5560
3.97k
    double dfReplacementVal = dfNoDataValue;
5561
3.97k
    if (dt == GDT_UInt8)
5562
929
    {
5563
929
        if (GDALClampDoubleValue(dfNoDataValue,
5564
929
                                 cpl::NumericLimits<uint8_t>::lowest(),
5565
929
                                 cpl::NumericLimits<uint8_t>::max()))
5566
0
        {
5567
0
            return 0;
5568
0
        }
5569
929
        if (dfNoDataValue == cpl::NumericLimits<unsigned char>::max())
5570
0
            dfReplacementVal = cpl::NumericLimits<unsigned char>::max() - 1;
5571
929
        else
5572
929
            dfReplacementVal = dfNoDataValue + 1;
5573
929
    }
5574
3.05k
    else if (dt == GDT_Int8)
5575
48
    {
5576
48
        if (GDALClampDoubleValue(dfNoDataValue,
5577
48
                                 cpl::NumericLimits<int8_t>::lowest(),
5578
48
                                 cpl::NumericLimits<int8_t>::max()))
5579
0
        {
5580
0
            return 0;
5581
0
        }
5582
48
        if (dfNoDataValue == cpl::NumericLimits<GInt8>::max())
5583
0
            dfReplacementVal = cpl::NumericLimits<GInt8>::max() - 1;
5584
48
        else
5585
48
            dfReplacementVal = dfNoDataValue + 1;
5586
48
    }
5587
3.00k
    else if (dt == GDT_UInt16)
5588
3
    {
5589
3
        if (GDALClampDoubleValue(dfNoDataValue,
5590
3
                                 cpl::NumericLimits<uint16_t>::lowest(),
5591
3
                                 cpl::NumericLimits<uint16_t>::max()))
5592
0
        {
5593
0
            return 0;
5594
0
        }
5595
3
        if (dfNoDataValue == cpl::NumericLimits<GUInt16>::max())
5596
0
            dfReplacementVal = cpl::NumericLimits<GUInt16>::max() - 1;
5597
3
        else
5598
3
            dfReplacementVal = dfNoDataValue + 1;
5599
3
    }
5600
2.99k
    else if (dt == GDT_Int16)
5601
2.99k
    {
5602
2.99k
        if (GDALClampDoubleValue(dfNoDataValue,
5603
2.99k
                                 cpl::NumericLimits<int16_t>::lowest(),
5604
2.99k
                                 cpl::NumericLimits<int16_t>::max()))
5605
0
        {
5606
0
            return 0;
5607
0
        }
5608
2.99k
        if (dfNoDataValue == cpl::NumericLimits<GInt16>::max())
5609
2.46k
            dfReplacementVal = cpl::NumericLimits<GInt16>::max() - 1;
5610
536
        else
5611
536
            dfReplacementVal = dfNoDataValue + 1;
5612
2.99k
    }
5613
2
    else if (dt == GDT_UInt32)
5614
0
    {
5615
0
        if (GDALClampDoubleValue(dfNoDataValue,
5616
0
                                 cpl::NumericLimits<uint32_t>::lowest(),
5617
0
                                 cpl::NumericLimits<uint32_t>::max()))
5618
0
        {
5619
0
            return 0;
5620
0
        }
5621
0
        if (dfNoDataValue == cpl::NumericLimits<GUInt32>::max())
5622
0
            dfReplacementVal = cpl::NumericLimits<GUInt32>::max() - 1;
5623
0
        else
5624
0
            dfReplacementVal = dfNoDataValue + 1;
5625
0
    }
5626
2
    else if (dt == GDT_Int32)
5627
0
    {
5628
0
        if (GDALClampDoubleValue(dfNoDataValue,
5629
0
                                 cpl::NumericLimits<int32_t>::lowest(),
5630
0
                                 cpl::NumericLimits<int32_t>::max()))
5631
0
        {
5632
0
            return 0;
5633
0
        }
5634
0
        if (dfNoDataValue == cpl::NumericLimits<int32_t>::max())
5635
0
            dfReplacementVal = cpl::NumericLimits<int32_t>::max() - 1;
5636
0
        else
5637
0
            dfReplacementVal = dfNoDataValue + 1;
5638
0
    }
5639
2
    else if (dt == GDT_UInt64)
5640
0
    {
5641
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5642
        // so we take the next lower value representable as a double 18446744073709549567
5643
0
        static const double dfMaxUInt64Value{
5644
0
            std::nextafter(
5645
0
                static_cast<double>(cpl::NumericLimits<uint64_t>::max()), 0) -
5646
0
            1};
5647
5648
0
        if (GDALClampDoubleValue(dfNoDataValue,
5649
0
                                 cpl::NumericLimits<uint64_t>::lowest(),
5650
0
                                 cpl::NumericLimits<uint64_t>::max()))
5651
0
        {
5652
0
            return 0;
5653
0
        }
5654
5655
0
        if (dfNoDataValue >=
5656
0
            static_cast<double>(cpl::NumericLimits<uint64_t>::max()))
5657
0
            dfReplacementVal = dfMaxUInt64Value;
5658
0
        else
5659
0
            dfReplacementVal = dfNoDataValue + 1;
5660
0
    }
5661
2
    else if (dt == GDT_Int64)
5662
0
    {
5663
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5664
        // so we take the next lower value representable as a double 9223372036854774784
5665
0
        static const double dfMaxInt64Value{
5666
0
            std::nextafter(
5667
0
                static_cast<double>(cpl::NumericLimits<int64_t>::max()), 0) -
5668
0
            1};
5669
5670
0
        if (GDALClampDoubleValue(dfNoDataValue,
5671
0
                                 cpl::NumericLimits<int64_t>::lowest(),
5672
0
                                 cpl::NumericLimits<int64_t>::max()))
5673
0
        {
5674
0
            return 0;
5675
0
        }
5676
5677
0
        if (dfNoDataValue >=
5678
0
            static_cast<double>(cpl::NumericLimits<int64_t>::max()))
5679
0
            dfReplacementVal = dfMaxInt64Value;
5680
0
        else
5681
0
            dfReplacementVal = dfNoDataValue + 1;
5682
0
    }
5683
2
    else if (dt == GDT_Float16)
5684
0
    {
5685
5686
0
        if (GDALClampDoubleValue(dfNoDataValue,
5687
0
                                 cpl::NumericLimits<GFloat16>::lowest(),
5688
0
                                 cpl::NumericLimits<GFloat16>::max()))
5689
0
        {
5690
0
            return 0;
5691
0
        }
5692
5693
0
        if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max())
5694
0
        {
5695
0
            using std::nextafter;
5696
0
            dfReplacementVal =
5697
0
                nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f));
5698
0
        }
5699
0
        else
5700
0
        {
5701
0
            using std::nextafter;
5702
0
            dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue),
5703
0
                                         cpl::NumericLimits<GFloat16>::max());
5704
0
        }
5705
0
    }
5706
2
    else if (dt == GDT_Float32)
5707
2
    {
5708
5709
2
        if (GDALClampDoubleValue(dfNoDataValue,
5710
2
                                 cpl::NumericLimits<float>::lowest(),
5711
2
                                 cpl::NumericLimits<float>::max()))
5712
0
        {
5713
0
            return 0;
5714
0
        }
5715
5716
2
        if (dfNoDataValue == cpl::NumericLimits<float>::max())
5717
0
        {
5718
0
            dfReplacementVal =
5719
0
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
5720
0
        }
5721
2
        else
5722
2
        {
5723
2
            dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue),
5724
2
                                              cpl::NumericLimits<float>::max());
5725
2
        }
5726
2
    }
5727
0
    else if (dt == GDT_Float64)
5728
0
    {
5729
0
        if (GDALClampDoubleValue(dfNoDataValue,
5730
0
                                 cpl::NumericLimits<double>::lowest(),
5731
0
                                 cpl::NumericLimits<double>::max()))
5732
0
        {
5733
0
            return 0;
5734
0
        }
5735
5736
0
        if (dfNoDataValue == cpl::NumericLimits<double>::max())
5737
0
        {
5738
0
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0);
5739
0
        }
5740
0
        else
5741
0
        {
5742
0
            dfReplacementVal = std::nextafter(
5743
0
                dfNoDataValue, cpl::NumericLimits<double>::max());
5744
0
        }
5745
0
    }
5746
5747
3.97k
    return dfReplacementVal;
5748
3.97k
}
5749
5750
/************************************************************************/
5751
/*                        GDALGetCacheDirectory()                       */
5752
/************************************************************************/
5753
5754
/** Return the root path of the GDAL cache.
5755
 *
5756
 * If the GDAL_CACHE_DIRECTORY configuration option is set, its value will
5757
 * be returned.
5758
 * Otherwise if the XDG_CACHE_HOME environment variable is set,
5759
 * ${XDG_CACHE_HOME}/.gdal will be returned.
5760
 * Otherwise ${HOME}/.gdal on Unix or$ ${USERPROFILE}/.gdal on Windows will
5761
 * be returned.
5762
 * Otherwise ${CPL_TMPDIR|TMPDIR|TEMP}/.gdal_${USERNAME|USER} will be returned.
5763
 * Otherwise empty string will be returned.
5764
 *
5765
 * @since GDAL 3.11
5766
 */
5767
std::string GDALGetCacheDirectory()
5768
4.45k
{
5769
4.45k
    if (const char *pszGDAL_CACHE_DIRECTORY =
5770
4.45k
            CPLGetConfigOption("GDAL_CACHE_DIRECTORY", nullptr))
5771
0
    {
5772
0
        return pszGDAL_CACHE_DIRECTORY;
5773
0
    }
5774
5775
4.45k
    if (const char *pszXDG_CACHE_HOME =
5776
4.45k
            CPLGetConfigOption("XDG_CACHE_HOME", nullptr))
5777
0
    {
5778
0
        return CPLFormFilenameSafe(pszXDG_CACHE_HOME, "gdal", nullptr);
5779
0
    }
5780
5781
#ifdef _WIN32
5782
    const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
5783
#else
5784
4.45k
    const char *pszHome = CPLGetConfigOption("HOME", nullptr);
5785
4.45k
#endif
5786
4.45k
    if (pszHome != nullptr)
5787
4.45k
    {
5788
4.45k
        return CPLFormFilenameSafe(pszHome, ".gdal", nullptr);
5789
4.45k
    }
5790
0
    else
5791
0
    {
5792
0
        const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
5793
5794
0
        if (pszDir == nullptr)
5795
0
            pszDir = CPLGetConfigOption("TMPDIR", nullptr);
5796
5797
0
        if (pszDir == nullptr)
5798
0
            pszDir = CPLGetConfigOption("TEMP", nullptr);
5799
5800
0
        const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
5801
0
        if (pszUsername == nullptr)
5802
0
            pszUsername = CPLGetConfigOption("USER", nullptr);
5803
5804
0
        if (pszDir != nullptr && pszUsername != nullptr)
5805
0
        {
5806
0
            return CPLFormFilenameSafe(
5807
0
                pszDir, CPLSPrintf(".gdal_%s", pszUsername), nullptr);
5808
0
        }
5809
0
    }
5810
0
    return std::string();
5811
4.45k
}
5812
5813
/************************************************************************/
5814
/*                      GDALDoesFileOrDatasetExist()                    */
5815
/************************************************************************/
5816
5817
/** Return whether a file already exists.
5818
 */
5819
bool GDALDoesFileOrDatasetExist(const char *pszName, const char **ppszType,
5820
                                GDALDriver **ppDriver)
5821
6
{
5822
6
    {
5823
6
        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5824
6
        GDALDriverH hDriver = GDALIdentifyDriver(pszName, nullptr);
5825
6
        if (hDriver)
5826
0
        {
5827
0
            if (ppszType)
5828
0
                *ppszType = "Dataset";
5829
0
            if (ppDriver)
5830
0
                *ppDriver = GDALDriver::FromHandle(hDriver);
5831
0
            return true;
5832
0
        }
5833
6
    }
5834
5835
6
    VSIStatBufL sStat;
5836
6
    if (VSIStatL(pszName, &sStat) == 0)
5837
0
    {
5838
0
        if (ppszType)
5839
0
            *ppszType = VSI_ISDIR(sStat.st_mode) ? "Directory" : "File";
5840
0
        return true;
5841
0
    }
5842
5843
6
    return false;
5844
6
}
5845
5846
/************************************************************************/
5847
/*                           GDALGeoTransform::Apply                    */
5848
/************************************************************************/
5849
5850
bool GDALGeoTransform::Apply(const OGREnvelope &env,
5851
                             GDALRasterWindow &window) const
5852
0
{
5853
0
    if (!IsAxisAligned())
5854
0
    {
5855
0
        return false;
5856
0
    }
5857
5858
0
    double dfLeft, dfRight, dfTop, dfBottom;
5859
0
    Apply(env.MinX, env.MinY, &dfLeft, &dfBottom);
5860
0
    Apply(env.MaxX, env.MaxY, &dfRight, &dfTop);
5861
5862
0
    if (dfLeft > dfRight)
5863
0
        std::swap(dfLeft, dfRight);
5864
0
    if (dfTop > dfBottom)
5865
0
        std::swap(dfTop, dfBottom);
5866
5867
0
    constexpr double EPSILON = 1e-5;
5868
0
    dfTop = std::floor(dfTop + EPSILON);
5869
0
    dfBottom = std::ceil(dfBottom - EPSILON);
5870
0
    dfLeft = std::floor(dfLeft + EPSILON);
5871
0
    dfRight = std::ceil(dfRight - EPSILON);
5872
5873
0
    if (!(dfLeft >= INT_MIN && dfLeft <= INT_MAX &&
5874
0
          dfRight - dfLeft <= INT_MAX && dfTop >= INT_MIN && dfTop <= INT_MAX &&
5875
0
          dfBottom - dfLeft <= INT_MAX))
5876
0
    {
5877
0
        return false;
5878
0
    }
5879
0
    window.nXOff = static_cast<int>(dfLeft);
5880
0
    window.nXSize = static_cast<int>(dfRight - dfLeft);
5881
0
    window.nYOff = static_cast<int>(dfTop);
5882
0
    window.nYSize = static_cast<int>(dfBottom - dfTop);
5883
5884
0
    return true;
5885
0
}
5886
5887
bool GDALGeoTransform::Apply(const GDALRasterWindow &window,
5888
                             OGREnvelope &env) const
5889
0
{
5890
0
    if (!IsAxisAligned())
5891
0
    {
5892
0
        return false;
5893
0
    }
5894
5895
0
    double dfLeft = window.nXOff;
5896
0
    double dfRight = window.nXOff + window.nXSize;
5897
0
    double dfTop = window.nYOff;
5898
0
    double dfBottom = window.nYOff + window.nYSize;
5899
5900
0
    Apply(dfLeft, dfBottom, &env.MinX, &env.MinY);
5901
0
    Apply(dfRight, dfTop, &env.MaxX, &env.MaxY);
5902
5903
0
    if (env.MaxX < env.MinX)
5904
0
        std::swap(env.MinX, env.MaxX);
5905
0
    if (env.MaxY < env.MinY)
5906
0
        std::swap(env.MinY, env.MaxY);
5907
5908
0
    return true;
5909
0
}