Coverage Report

Created: 2026-04-01 06:20

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